llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
Balázs Kéri 5bfd57e7c9
[clang][analyzer] CallAndMessage warnings at pointer to uninitialized struct (#164600)
`CallAndMessageChecker` did have a warning for the case when pointer to
uninitialized data is passed to a function when the argument type is
pointer to const. This did not work for struct types. The check is
improved to handle cases when struct with uninitialized data is passed
to a function.
2025-12-17 09:46:24 +01:00

738 lines
26 KiB
C++

//===--- CallAndMessageChecker.cpp ------------------------------*- C++ -*--==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This defines CallAndMessageChecker, a builtin checker that checks for various
// errors of call and objc message expressions.
//
//===----------------------------------------------------------------------===//
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ParentMap.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
using namespace ento;
namespace {
class CallAndMessageChecker
: public Checker<check::PreObjCMessage, check::ObjCMessageNil,
check::PreCall> {
const BugType CallNullBug{
this, "Called function pointer is null (null dereference)"};
const BugType CallUndefBug{
this, "Called function pointer is an uninitialized pointer value"};
const BugType CXXCallNullBug{this, "Called C++ object pointer is null"};
const BugType CXXCallUndefBug{this,
"Called C++ object pointer is uninitialized"};
const BugType CallArgBug{this, "Uninitialized argument value"};
const BugType CXXDeleteUndefBug{this, "Uninitialized argument value"};
const BugType MsgUndefBug{
this, "Receiver in message expression is an uninitialized value"};
const BugType ObjCPropUndefBug{
this, "Property access on an uninitialized object pointer"};
const BugType ObjCSubscriptUndefBug{
this, "Subscript access on an uninitialized object pointer"};
const BugType MsgArgBug{this, "Uninitialized argument value"};
const BugType MsgRetBug{this, "Receiver in message expression is 'nil'"};
const BugType CallFewArgsBug{this, "Function call with too few arguments"};
public:
// Like a checker family, CallAndMessageChecker can produce many kinds of
// warnings which can be separately enabled or disabled. However, for
// historical reasons these warning kinds are represented by checker options
// (and not separate checker frontends with their own names) because
// CallAndMessage is among the oldest checkers out there, and can
// be responsible for the majority of the reports on any given project. This
// is obviously not ideal, but changing checker name has the consequence of
// changing the issue hashes associated with the reports, and databases
// relying on this (CodeChecker, for instance) would suffer greatly.
// If we ever end up making changes to the issue hash generation algorithm, or
// the warning messages here, we should totally jump on the opportunity to
// convert these to actual checker frontends.
enum CheckKind {
CK_FunctionPointer,
CK_ParameterCount,
CK_CXXThisMethodCall,
CK_CXXDeallocationArg,
CK_ArgInitializedness,
CK_ArgPointeeInitializedness,
CK_NilReceiver,
CK_UndefReceiver,
CK_NumCheckKinds
};
bool ChecksEnabled[CK_NumCheckKinds] = {false};
/// When checking a struct value for uninitialized data, should all the fields
/// be un-initialized or only find one uninitialized field.
bool StructInitializednessComplete = true;
void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
/// Fill in the return value that results from messaging nil based on the
/// return type and architecture and diagnose if the return value will be
/// garbage.
void checkObjCMessageNil(const ObjCMethodCall &msg, CheckerContext &C) const;
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
ProgramStateRef checkFunctionPointerCall(const CallExpr *CE,
CheckerContext &C,
ProgramStateRef State) const;
ProgramStateRef checkCXXMethodCall(const CXXInstanceCall *CC,
CheckerContext &C,
ProgramStateRef State) const;
ProgramStateRef checkParameterCount(const CallEvent &Call, CheckerContext &C,
ProgramStateRef State) const;
ProgramStateRef checkCXXDeallocation(const CXXDeallocatorCall *DC,
CheckerContext &C,
ProgramStateRef State) const;
ProgramStateRef checkArgInitializedness(const CallEvent &Call,
CheckerContext &C,
ProgramStateRef State) const;
private:
bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange,
const Expr *ArgEx, int ArgumentNumber,
bool CheckUninitFields, const CallEvent &Call,
const BugType &BT,
const ParmVarDecl *ParamDecl) const;
static void emitBadCall(const BugType &BT, CheckerContext &C,
const Expr *BadE);
void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg,
ExplodedNode *N) const;
void HandleNilReceiver(CheckerContext &C,
ProgramStateRef state,
const ObjCMethodCall &msg) const;
bool uninitRefOrPointer(CheckerContext &C, SVal V, SourceRange ArgRange,
const Expr *ArgEx, const BugType &BT,
const ParmVarDecl *ParamDecl,
int ArgumentNumber) const;
};
} // end anonymous namespace
void CallAndMessageChecker::emitBadCall(const BugType &BT, CheckerContext &C,
const Expr *BadE) {
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
auto R = std::make_unique<PathSensitiveBugReport>(BT, BT.getDescription(), N);
if (BadE) {
R->addRange(BadE->getSourceRange());
if (BadE->isGLValue())
BadE = bugreporter::getDerefExpr(BadE);
bugreporter::trackExpressionValue(N, BadE, *R);
}
C.emitReport(std::move(R));
}
static void describeUninitializedArgumentInCall(const CallEvent &Call,
int ArgumentNumber,
llvm::raw_svector_ostream &Os) {
switch (Call.getKind()) {
case CE_ObjCMessage: {
const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call);
switch (Msg.getMessageKind()) {
case OCM_Message:
Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
<< " argument in message expression is an uninitialized value";
return;
case OCM_PropertyAccess:
assert(Msg.isSetter() && "Getters have no args");
Os << "Argument for property setter is an uninitialized value";
return;
case OCM_Subscript:
if (Msg.isSetter() && (ArgumentNumber == 0))
Os << "Argument for subscript setter is an uninitialized value";
else
Os << "Subscript index is an uninitialized value";
return;
}
llvm_unreachable("Unknown message kind.");
}
case CE_Block:
Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
<< " block call argument is an uninitialized value";
return;
default:
Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
<< " function call argument is an uninitialized value";
return;
}
}
namespace {
class FindUninitializedField {
public:
using FieldChainTy = SmallVector<const FieldDecl *, 10>;
FieldChainTy FieldChain;
private:
StoreManager &StoreMgr;
MemRegionManager &MrMgr;
Store store;
bool FindNotUninitialized;
public:
FindUninitializedField(StoreManager &storeMgr, MemRegionManager &mrMgr,
Store s, bool FindNotUninitialized = false)
: StoreMgr(storeMgr), MrMgr(mrMgr), store(s),
FindNotUninitialized(FindNotUninitialized) {}
bool Find(const TypedValueRegion *R) {
QualType T = R->getValueType();
if (const RecordType *RT = T->getAsStructureType()) {
const RecordDecl *RD = RT->getDecl()->getDefinition();
assert(RD && "Referred record has no definition");
for (const auto *I : RD->fields()) {
if (I->isUnnamedBitField())
continue;
const FieldRegion *FR = MrMgr.getFieldRegion(I, R);
FieldChain.push_back(I);
T = I->getType();
if (T->isStructureType()) {
if (FindNotUninitialized ? !Find(FR) : Find(FR))
return !FindNotUninitialized;
} else {
SVal V = StoreMgr.getBinding(store, loc::MemRegionVal(FR));
if (FindNotUninitialized ? !V.isUndef() : V.isUndef())
return !FindNotUninitialized;
}
FieldChain.pop_back();
}
}
return FindNotUninitialized;
}
};
} // namespace
namespace llvm {
template <> struct format_provider<FindUninitializedField::FieldChainTy> {
static void format(const FindUninitializedField::FieldChainTy &V,
raw_ostream &Stream, StringRef Style) {
if (V.size() == 0)
return;
else if (V.size() == 1)
Stream << " (e.g., field: '" << *V[0] << "')";
else {
Stream << " (e.g., via the field chain: '";
interleave(
V, Stream, [&Stream](const FieldDecl *FD) { Stream << *FD; }, ".");
Stream << "')";
}
}
};
} // namespace llvm
bool CallAndMessageChecker::uninitRefOrPointer(
CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx,
const BugType &BT, const ParmVarDecl *ParamDecl, int ArgumentNumber) const {
if (!ChecksEnabled[CK_ArgPointeeInitializedness])
return false;
// No parameter declaration available, i.e. variadic function argument.
if (!ParamDecl)
return false;
QualType ParamT = ParamDecl->getType();
if (!ParamT->isPointerOrReferenceType())
return false;
QualType PointeeT = ParamT->getPointeeType();
if (!PointeeT.isConstQualified())
return false;
const MemRegion *SValMemRegion = V.getAsRegion();
if (!SValMemRegion)
return false;
// If parameter is declared as pointer to const in function declaration,
// then check if corresponding argument in function call is
// pointing to undefined symbol value (uninitialized memory).
const ProgramStateRef State = C.getState();
if (PointeeT->isVoidType())
PointeeT = C.getASTContext().CharTy;
const SVal PointeeV = State->getSVal(SValMemRegion, PointeeT);
if (PointeeV.isUndef()) {
if (ExplodedNode *N = C.generateErrorNode()) {
std::string Msg = llvm::formatv(
"{0}{1} function call argument is {2} uninitialized value",
ArgumentNumber + 1, llvm::getOrdinalSuffix(ArgumentNumber + 1),
ParamT->isPointerType() ? "a pointer to" : "an");
auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
R->addRange(ArgRange);
if (ArgEx)
bugreporter::trackExpressionValue(N, ArgEx, *R);
C.emitReport(std::move(R));
}
return true;
}
if (auto LV = PointeeV.getAs<nonloc::LazyCompoundVal>()) {
const LazyCompoundValData *D = LV->getCVData();
FindUninitializedField F(C.getState()->getStateManager().getStoreManager(),
C.getSValBuilder().getRegionManager(),
D->getStore(), StructInitializednessComplete);
if (F.Find(D->getRegion())) {
if (ExplodedNode *N = C.generateErrorNode()) {
std::string Msg = llvm::formatv(
"{0}{1} function call argument {2} an uninitialized value{3}",
(ArgumentNumber + 1), llvm::getOrdinalSuffix(ArgumentNumber + 1),
ParamT->isPointerType() ? "points to" : "references", F.FieldChain);
auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
R->addRange(ArgRange);
if (ArgEx)
bugreporter::trackExpressionValue(N, ArgEx, *R);
C.emitReport(std::move(R));
}
return true;
}
}
return false;
}
bool CallAndMessageChecker::PreVisitProcessArg(
CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx,
int ArgumentNumber, bool CheckUninitFields, const CallEvent &Call,
const BugType &BT, const ParmVarDecl *ParamDecl) const {
if (uninitRefOrPointer(C, V, ArgRange, ArgEx, BT, ParamDecl, ArgumentNumber))
return true;
if (V.isUndef()) {
if (!ChecksEnabled[CK_ArgInitializedness]) {
C.addSink();
return true;
}
if (ExplodedNode *N = C.generateErrorNode()) {
// Generate a report for this bug.
SmallString<200> Buf;
llvm::raw_svector_ostream Os(Buf);
describeUninitializedArgumentInCall(Call, ArgumentNumber, Os);
auto R = std::make_unique<PathSensitiveBugReport>(BT, Os.str(), N);
R->addRange(ArgRange);
if (ArgEx)
bugreporter::trackExpressionValue(N, ArgEx, *R);
C.emitReport(std::move(R));
}
return true;
}
if (!CheckUninitFields)
return false;
if (auto LV = V.getAs<nonloc::LazyCompoundVal>()) {
const LazyCompoundValData *D = LV->getCVData();
FindUninitializedField F(C.getState()->getStateManager().getStoreManager(),
C.getSValBuilder().getRegionManager(),
D->getStore());
if (F.Find(D->getRegion())) {
if (!ChecksEnabled[CK_ArgInitializedness]) {
C.addSink();
return true;
}
if (ExplodedNode *N = C.generateErrorNode()) {
std::string Msg = llvm::formatv(
"Passed-by-value struct argument contains uninitialized data{0}",
F.FieldChain);
// Generate a report for this bug.
auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
R->addRange(ArgRange);
if (ArgEx)
bugreporter::trackExpressionValue(N, ArgEx, *R);
// FIXME: enhance track back for uninitialized value for arbitrary
// memregions
C.emitReport(std::move(R));
}
return true;
}
}
return false;
}
ProgramStateRef CallAndMessageChecker::checkFunctionPointerCall(
const CallExpr *CE, CheckerContext &C, ProgramStateRef State) const {
const Expr *Callee = CE->getCallee()->IgnoreParens();
const LocationContext *LCtx = C.getLocationContext();
SVal L = State->getSVal(Callee, LCtx);
if (L.isUndef()) {
if (!ChecksEnabled[CK_FunctionPointer]) {
C.addSink(State);
return nullptr;
}
emitBadCall(CallUndefBug, C, Callee);
return nullptr;
}
ProgramStateRef StNonNull, StNull;
std::tie(StNonNull, StNull) = State->assume(L.castAs<DefinedOrUnknownSVal>());
if (StNull && !StNonNull) {
if (!ChecksEnabled[CK_FunctionPointer]) {
C.addSink(StNull);
return nullptr;
}
emitBadCall(CallNullBug, C, Callee);
return nullptr;
}
return StNonNull;
}
ProgramStateRef CallAndMessageChecker::checkParameterCount(
const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const {
// If we have a function or block declaration, we can make sure we pass
// enough parameters.
unsigned Params = Call.parameters().size();
if (Call.getNumArgs() >= Params)
return State;
if (!ChecksEnabled[CK_ParameterCount]) {
C.addSink(State);
return nullptr;
}
ExplodedNode *N = C.generateErrorNode();
if (!N)
return nullptr;
SmallString<512> Str;
llvm::raw_svector_ostream os(Str);
if (isa<AnyFunctionCall>(Call)) {
os << "Function ";
} else {
assert(isa<BlockCall>(Call));
os << "Block ";
}
os << "taking " << Params << " argument" << (Params == 1 ? "" : "s")
<< " is called with fewer (" << Call.getNumArgs() << ")";
C.emitReport(
std::make_unique<PathSensitiveBugReport>(CallFewArgsBug, os.str(), N));
return nullptr;
}
ProgramStateRef CallAndMessageChecker::checkCXXMethodCall(
const CXXInstanceCall *CC, CheckerContext &C, ProgramStateRef State) const {
SVal V = CC->getCXXThisVal();
if (V.isUndef()) {
if (!ChecksEnabled[CK_CXXThisMethodCall]) {
C.addSink(State);
return nullptr;
}
emitBadCall(CXXCallUndefBug, C, CC->getCXXThisExpr());
return nullptr;
}
ProgramStateRef StNonNull, StNull;
std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
if (StNull && !StNonNull) {
if (!ChecksEnabled[CK_CXXThisMethodCall]) {
C.addSink(StNull);
return nullptr;
}
emitBadCall(CXXCallNullBug, C, CC->getCXXThisExpr());
return nullptr;
}
return StNonNull;
}
ProgramStateRef
CallAndMessageChecker::checkCXXDeallocation(const CXXDeallocatorCall *DC,
CheckerContext &C,
ProgramStateRef State) const {
const CXXDeleteExpr *DE = DC->getOriginExpr();
assert(DE);
SVal Arg = C.getSVal(DE->getArgument());
if (!Arg.isUndef())
return State;
if (!ChecksEnabled[CK_CXXDeallocationArg]) {
C.addSink(State);
return nullptr;
}
StringRef Desc;
ExplodedNode *N = C.generateErrorNode();
if (!N)
return nullptr;
if (DE->isArrayFormAsWritten())
Desc = "Argument to 'delete[]' is uninitialized";
else
Desc = "Argument to 'delete' is uninitialized";
auto R = std::make_unique<PathSensitiveBugReport>(CXXDeleteUndefBug, Desc, N);
bugreporter::trackExpressionValue(N, DE, *R);
C.emitReport(std::move(R));
return nullptr;
}
ProgramStateRef CallAndMessageChecker::checkArgInitializedness(
const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const {
const Decl *D = Call.getDecl();
// Don't check for uninitialized field values in arguments if the
// caller has a body that is available and we have the chance to inline it.
// This is a hack, but is a reasonable compromise betweens sometimes warning
// and sometimes not depending on if we decide to inline a function.
const bool checkUninitFields =
!(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody()));
const BugType &BT = isa<ObjCMethodCall>(Call) ? MsgArgBug : CallArgBug;
const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) {
const ParmVarDecl *ParamDecl = nullptr;
if (FD && i < FD->getNumParams())
ParamDecl = FD->getParamDecl(i);
if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i),
Call.getArgExpr(i), i, checkUninitFields, Call, BT,
ParamDecl))
return nullptr;
}
return State;
}
void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()))
State = checkFunctionPointerCall(CE, C, State);
if (!State)
return;
if (Call.getDecl())
State = checkParameterCount(Call, C, State);
if (!State)
return;
if (const auto *CC = dyn_cast<CXXInstanceCall>(&Call))
State = checkCXXMethodCall(CC, C, State);
if (!State)
return;
if (const auto *DC = dyn_cast<CXXDeallocatorCall>(&Call))
State = checkCXXDeallocation(DC, C, State);
if (!State)
return;
State = checkArgInitializedness(Call, C, State);
// If we make it here, record our assumptions about the callee.
C.addTransition(State);
}
void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
CheckerContext &C) const {
SVal recVal = msg.getReceiverSVal();
if (recVal.isUndef()) {
if (!ChecksEnabled[CK_UndefReceiver]) {
C.addSink();
return;
}
if (ExplodedNode *N = C.generateErrorNode()) {
const BugType *BT = nullptr;
switch (msg.getMessageKind()) {
case OCM_Message:
BT = &MsgUndefBug;
break;
case OCM_PropertyAccess:
BT = &ObjCPropUndefBug;
break;
case OCM_Subscript:
BT = &ObjCSubscriptUndefBug;
break;
}
assert(BT && "Unknown message kind.");
auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
const ObjCMessageExpr *ME = msg.getOriginExpr();
R->addRange(ME->getReceiverRange());
// FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet.
if (const Expr *ReceiverE = ME->getInstanceReceiver())
bugreporter::trackExpressionValue(N, ReceiverE, *R);
C.emitReport(std::move(R));
}
return;
}
}
void CallAndMessageChecker::checkObjCMessageNil(const ObjCMethodCall &msg,
CheckerContext &C) const {
HandleNilReceiver(C, C.getState(), msg);
}
void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
const ObjCMethodCall &msg,
ExplodedNode *N) const {
if (!ChecksEnabled[CK_NilReceiver]) {
C.addSink();
return;
}
const ObjCMessageExpr *ME = msg.getOriginExpr();
QualType ResTy = msg.getResultType();
SmallString<200> buf;
llvm::raw_svector_ostream os(buf);
os << "The receiver of message '";
ME->getSelector().print(os);
os << "' is nil";
if (ResTy->isReferenceType()) {
os << ", which results in forming a null reference";
} else {
os << " and returns a value of type '";
msg.getResultType().print(os, C.getLangOpts());
os << "' that will be garbage";
}
auto report =
std::make_unique<PathSensitiveBugReport>(MsgRetBug, os.str(), N);
report->addRange(ME->getReceiverRange());
// FIXME: This won't track "self" in messages to super.
if (const Expr *receiver = ME->getInstanceReceiver()) {
bugreporter::trackExpressionValue(N, receiver, *report);
}
C.emitReport(std::move(report));
}
static bool supportsNilWithFloatRet(const llvm::Triple &triple) {
return (triple.getVendor() == llvm::Triple::Apple &&
(triple.isiOS() || triple.isWatchOS() ||
!triple.isMacOSXVersionLT(10,5)));
}
void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
ProgramStateRef state,
const ObjCMethodCall &Msg) const {
ASTContext &Ctx = C.getASTContext();
// Check the return type of the message expression. A message to nil will
// return different values depending on the return type and the architecture.
QualType RetTy = Msg.getResultType();
CanQualType CanRetTy = Ctx.getCanonicalType(RetTy);
const LocationContext *LCtx = C.getLocationContext();
if (CanRetTy->isStructureOrClassType()) {
// Structure returns are safe since the compiler zeroes them out.
SVal V = C.getSValBuilder().makeZeroVal(RetTy);
C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V));
return;
}
// Other cases: check if sizeof(return type) > sizeof(void*)
if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap()
.isConsumedExpr(Msg.getOriginExpr())) {
// Compute: sizeof(void *) and sizeof(return type)
const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy);
const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy);
if (CanRetTy.getTypePtr()->isReferenceType()||
(voidPtrSize < returnTypeSize &&
!(supportsNilWithFloatRet(Ctx.getTargetInfo().getTriple()) &&
(Ctx.FloatTy == CanRetTy ||
Ctx.DoubleTy == CanRetTy ||
Ctx.LongDoubleTy == CanRetTy ||
Ctx.LongLongTy == CanRetTy ||
Ctx.UnsignedLongLongTy == CanRetTy)))) {
if (ExplodedNode *N = C.generateErrorNode(state))
emitNilReceiverBug(C, Msg, N);
return;
}
// Handle the safe cases where the return value is 0 if the
// receiver is nil.
//
// FIXME: For now take the conservative approach that we only
// return null values if we *know* that the receiver is nil.
// This is because we can have surprises like:
//
// ... = [[NSScreens screens] objectAtIndex:0];
//
// What can happen is that [... screens] could return nil, but
// it most likely isn't nil. We should assume the semantics
// of this case unless we have *a lot* more knowledge.
//
SVal V = C.getSValBuilder().makeZeroVal(RetTy);
C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V));
return;
}
C.addTransition(state);
}
void ento::registerCallAndMessageChecker(CheckerManager &Mgr) {
CallAndMessageChecker *Chk = Mgr.registerChecker<CallAndMessageChecker>();
#define QUERY_CHECKER_OPTION(OPTION) \
Chk->ChecksEnabled[CallAndMessageChecker::CK_##OPTION] = \
Mgr.getAnalyzerOptions().getCheckerBooleanOption( \
Mgr.getCurrentCheckerName(), #OPTION);
QUERY_CHECKER_OPTION(FunctionPointer)
QUERY_CHECKER_OPTION(ParameterCount)
QUERY_CHECKER_OPTION(CXXThisMethodCall)
QUERY_CHECKER_OPTION(CXXDeallocationArg)
QUERY_CHECKER_OPTION(ArgInitializedness)
QUERY_CHECKER_OPTION(ArgPointeeInitializedness)
QUERY_CHECKER_OPTION(NilReceiver)
QUERY_CHECKER_OPTION(UndefReceiver)
Chk->StructInitializednessComplete =
Mgr.getAnalyzerOptions().getCheckerBooleanOption(
Mgr.getCurrentCheckerName(), "ArgPointeeInitializednessComplete");
}
bool ento::shouldRegisterCallAndMessageChecker(const CheckerManager &) {
return true;
}