2009-11-24 12:45:44 +08:00
|
|
|
//===--- CallAndMessageChecker.cpp ------------------------------*- C++ -*--==//
|
2009-11-03 14:46:03 +08:00
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// 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
|
2009-11-03 14:46:03 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
2009-11-24 12:45:44 +08:00
|
|
|
// This defines CallAndMessageChecker, a builtin checker that checks for various
|
|
|
|
// errors of call and objc message expressions.
|
2009-11-03 14:46:03 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
[analyzer][NFC] Move CheckerRegistry from the Core directory to Frontend
ClangCheckerRegistry is a very non-obvious, poorly documented, weird concept.
It derives from CheckerRegistry, and is placed in lib/StaticAnalyzer/Frontend,
whereas it's base is located in lib/StaticAnalyzer/Core. It was, from what I can
imagine, used to circumvent the problem that the registry functions of the
checkers are located in the clangStaticAnalyzerCheckers library, but that
library depends on clangStaticAnalyzerCore. However, clangStaticAnalyzerFrontend
depends on both of those libraries.
One can make the observation however, that CheckerRegistry has no place in Core,
it isn't used there at all! The only place where it is used is Frontend, which
is where it ultimately belongs.
This move implies that since
include/clang/StaticAnalyzer/Checkers/ClangCheckers.h only contained a single function:
class CheckerRegistry;
void registerBuiltinCheckers(CheckerRegistry ®istry);
it had to re purposed, as CheckerRegistry is no longer available to
clangStaticAnalyzerCheckers. It was renamed to BuiltinCheckerRegistration.h,
which actually describes it a lot better -- it does not contain the registration
functions for checkers, but only those generated by the tblgen files.
Differential Revision: https://reviews.llvm.org/D54436
llvm-svn: 349275
2018-12-16 00:23:51 +08:00
|
|
|
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "clang/AST/ParentMap.h"
|
|
|
|
#include "clang/Basic/TargetInfo.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
2011-03-01 09:16:21 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/Checker.h"
|
2011-02-28 09:28:13 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
2012-07-27 05:39:41 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
|
2011-02-28 09:28:13 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
2012-02-04 21:45:25 +08:00
|
|
|
#include "llvm/ADT/SmallString.h"
|
2017-03-08 23:22:24 +08:00
|
|
|
#include "llvm/ADT/StringExtras.h"
|
2012-12-02 01:12:56 +08:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2009-11-03 14:46:03 +08:00
|
|
|
|
|
|
|
using namespace clang;
|
2010-12-23 15:20:52 +08:00
|
|
|
using namespace ento;
|
2009-11-03 14:46:03 +08:00
|
|
|
|
2009-11-11 13:50:44 +08:00
|
|
|
namespace {
|
2014-03-14 01:55:39 +08:00
|
|
|
|
|
|
|
struct ChecksFilter {
|
|
|
|
DefaultBool Check_CallAndMessageUnInitRefArg;
|
|
|
|
DefaultBool Check_CallAndMessageChecker;
|
|
|
|
|
|
|
|
CheckName CheckName_CallAndMessageUnInitRefArg;
|
|
|
|
CheckName CheckName_CallAndMessageChecker;
|
|
|
|
};
|
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class CallAndMessageChecker
|
2013-08-09 08:55:47 +08:00
|
|
|
: public Checker< check::PreStmt<CallExpr>,
|
|
|
|
check::PreStmt<CXXDeleteExpr>,
|
|
|
|
check::PreObjCMessage,
|
2015-09-15 09:13:53 +08:00
|
|
|
check::ObjCMessageNil,
|
2012-07-03 03:28:21 +08:00
|
|
|
check::PreCall > {
|
2014-03-08 04:03:18 +08:00
|
|
|
mutable std::unique_ptr<BugType> BT_call_null;
|
|
|
|
mutable std::unique_ptr<BugType> BT_call_undef;
|
|
|
|
mutable std::unique_ptr<BugType> BT_cxx_call_null;
|
|
|
|
mutable std::unique_ptr<BugType> BT_cxx_call_undef;
|
|
|
|
mutable std::unique_ptr<BugType> BT_call_arg;
|
|
|
|
mutable std::unique_ptr<BugType> BT_cxx_delete_undef;
|
|
|
|
mutable std::unique_ptr<BugType> BT_msg_undef;
|
|
|
|
mutable std::unique_ptr<BugType> BT_objc_prop_undef;
|
|
|
|
mutable std::unique_ptr<BugType> BT_objc_subscript_undef;
|
|
|
|
mutable std::unique_ptr<BugType> BT_msg_arg;
|
|
|
|
mutable std::unique_ptr<BugType> BT_msg_ret;
|
|
|
|
mutable std::unique_ptr<BugType> BT_call_few_args;
|
|
|
|
|
2009-11-11 13:50:44 +08:00
|
|
|
public:
|
2014-03-14 01:55:39 +08:00
|
|
|
ChecksFilter Filter;
|
2009-11-25 05:41:28 +08:00
|
|
|
|
2011-02-28 09:28:13 +08:00
|
|
|
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
|
2013-08-09 08:55:47 +08:00
|
|
|
void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
|
2012-07-03 03:28:04 +08:00
|
|
|
void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
|
2015-09-15 09:13:53 +08:00
|
|
|
|
|
|
|
/// 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;
|
|
|
|
|
2012-07-03 03:28:21 +08:00
|
|
|
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
|
2009-11-25 05:41:28 +08:00
|
|
|
|
2009-11-21 09:25:37 +08:00
|
|
|
private:
|
2014-03-14 01:55:39 +08:00
|
|
|
bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange,
|
2017-03-08 23:22:24 +08:00
|
|
|
const Expr *ArgEx, int ArgumentNumber,
|
2014-03-14 01:55:39 +08:00
|
|
|
bool CheckUninitFields, const CallEvent &Call,
|
|
|
|
std::unique_ptr<BugType> &BT,
|
|
|
|
const ParmVarDecl *ParamDecl) const;
|
2010-03-18 11:22:29 +08:00
|
|
|
|
2012-08-04 07:08:49 +08:00
|
|
|
static void emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE);
|
2012-07-03 03:28:04 +08:00
|
|
|
void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg,
|
2011-02-28 09:28:13 +08:00
|
|
|
ExplodedNode *N) const;
|
2010-03-18 10:17:27 +08:00
|
|
|
|
2011-08-16 06:09:50 +08:00
|
|
|
void HandleNilReceiver(CheckerContext &C,
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state,
|
2012-07-03 03:28:04 +08:00
|
|
|
const ObjCMethodCall &msg) const;
|
2010-03-18 10:17:27 +08:00
|
|
|
|
2014-03-08 04:03:18 +08:00
|
|
|
void LazyInit_BT(const char *desc, std::unique_ptr<BugType> &BT) const {
|
2010-03-18 11:22:29 +08:00
|
|
|
if (!BT)
|
2014-02-12 05:49:21 +08:00
|
|
|
BT.reset(new BuiltinBug(this, desc));
|
2010-03-18 10:17:27 +08:00
|
|
|
}
|
2014-03-14 01:55:39 +08:00
|
|
|
bool uninitRefOrPointer(CheckerContext &C, const SVal &V,
|
2017-03-08 23:22:24 +08:00
|
|
|
SourceRange ArgRange, const Expr *ArgEx,
|
|
|
|
std::unique_ptr<BugType> &BT,
|
|
|
|
const ParmVarDecl *ParamDecl, const char *BD,
|
|
|
|
int ArgumentNumber) const;
|
2009-11-11 13:50:44 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2012-08-04 07:08:49 +08:00
|
|
|
void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C,
|
|
|
|
const Expr *BadE) {
|
2015-09-17 06:03:05 +08:00
|
|
|
ExplodedNode *N = C.generateErrorNode();
|
2009-11-21 09:25:37 +08:00
|
|
|
if (!N)
|
|
|
|
return;
|
2010-03-18 10:17:27 +08:00
|
|
|
|
2015-06-23 21:15:32 +08:00
|
|
|
auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N);
|
2012-08-04 07:08:49 +08:00
|
|
|
if (BadE) {
|
|
|
|
R->addRange(BadE->getSourceRange());
|
2013-01-26 09:28:23 +08:00
|
|
|
if (BadE->isGLValue())
|
|
|
|
BadE = bugreporter::getDerefExpr(BadE);
|
2018-10-24 02:24:53 +08:00
|
|
|
bugreporter::trackExpressionValue(N, BadE, *R);
|
2012-08-04 07:08:49 +08:00
|
|
|
}
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(std::move(R));
|
2009-11-21 09:25:37 +08:00
|
|
|
}
|
|
|
|
|
2017-03-08 23:22:24 +08:00
|
|
|
static void describeUninitializedArgumentInCall(const CallEvent &Call,
|
|
|
|
int ArgumentNumber,
|
|
|
|
llvm::raw_svector_ostream &Os) {
|
2012-07-19 05:59:51 +08:00
|
|
|
switch (Call.getKind()) {
|
|
|
|
case CE_ObjCMessage: {
|
|
|
|
const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call);
|
|
|
|
switch (Msg.getMessageKind()) {
|
|
|
|
case OCM_Message:
|
2017-03-08 23:22:24 +08:00
|
|
|
Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
|
|
|
|
<< " argument in message expression is an uninitialized value";
|
|
|
|
return;
|
2012-07-19 05:59:51 +08:00
|
|
|
case OCM_PropertyAccess:
|
|
|
|
assert(Msg.isSetter() && "Getters have no args");
|
2017-03-08 23:22:24 +08:00
|
|
|
Os << "Argument for property setter is an uninitialized value";
|
|
|
|
return;
|
2012-07-19 05:59:51 +08:00
|
|
|
case OCM_Subscript:
|
2017-03-08 23:22:24 +08:00
|
|
|
if (Msg.isSetter() && (ArgumentNumber == 0))
|
|
|
|
Os << "Argument for subscript setter is an uninitialized value";
|
|
|
|
else
|
|
|
|
Os << "Subscript index is an uninitialized value";
|
|
|
|
return;
|
2012-07-19 05:59:51 +08:00
|
|
|
}
|
|
|
|
llvm_unreachable("Unknown message kind.");
|
|
|
|
}
|
|
|
|
case CE_Block:
|
2017-03-08 23:22:24 +08:00
|
|
|
Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
|
|
|
|
<< " block call argument is an uninitialized value";
|
|
|
|
return;
|
2012-07-19 05:59:51 +08:00
|
|
|
default:
|
2017-03-08 23:22:24 +08:00
|
|
|
Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
|
|
|
|
<< " function call argument is an uninitialized value";
|
|
|
|
return;
|
2012-07-19 05:59:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-08 23:22:24 +08:00
|
|
|
bool CallAndMessageChecker::uninitRefOrPointer(
|
|
|
|
CheckerContext &C, const SVal &V, SourceRange ArgRange, const Expr *ArgEx,
|
|
|
|
std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl, const char *BD,
|
|
|
|
int ArgumentNumber) const {
|
2014-03-14 01:55:39 +08:00
|
|
|
if (!Filter.Check_CallAndMessageUnInitRefArg)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// No parameter declaration available, i.e. variadic function argument.
|
|
|
|
if(!ParamDecl)
|
|
|
|
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).
|
2017-03-08 23:22:24 +08:00
|
|
|
SmallString<200> Buf;
|
|
|
|
llvm::raw_svector_ostream Os(Buf);
|
2014-03-14 01:55:39 +08:00
|
|
|
|
|
|
|
if (ParamDecl->getType()->isPointerType()) {
|
2017-03-08 23:22:24 +08:00
|
|
|
Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
|
|
|
|
<< " function call argument is a pointer to uninitialized value";
|
2014-03-14 01:55:39 +08:00
|
|
|
} else if (ParamDecl->getType()->isReferenceType()) {
|
2017-03-08 23:22:24 +08:00
|
|
|
Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
|
|
|
|
<< " function call argument is an uninitialized value";
|
2014-03-14 01:55:39 +08:00
|
|
|
} else
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if(!ParamDecl->getType()->getPointeeType().isConstQualified())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (const MemRegion *SValMemRegion = V.getAsRegion()) {
|
|
|
|
const ProgramStateRef State = C.getState();
|
2017-12-12 10:27:55 +08:00
|
|
|
const SVal PSV = State->getSVal(SValMemRegion, C.getASTContext().CharTy);
|
2014-03-14 01:55:39 +08:00
|
|
|
if (PSV.isUndef()) {
|
2015-09-17 06:03:05 +08:00
|
|
|
if (ExplodedNode *N = C.generateErrorNode()) {
|
2014-03-14 01:55:39 +08:00
|
|
|
LazyInit_BT(BD, BT);
|
2017-03-08 23:22:24 +08:00
|
|
|
auto R = llvm::make_unique<BugReport>(*BT, Os.str(), N);
|
2014-03-14 01:55:39 +08:00
|
|
|
R->addRange(ArgRange);
|
2018-10-24 02:24:53 +08:00
|
|
|
if (ArgEx)
|
|
|
|
bugreporter::trackExpressionValue(N, ArgEx, *R);
|
|
|
|
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(std::move(R));
|
2014-03-14 01:55:39 +08:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-10-14 06:18:22 +08:00
|
|
|
namespace {
|
2018-08-30 04:29:39 +08:00
|
|
|
class FindUninitializedField {
|
|
|
|
public:
|
|
|
|
SmallVector<const FieldDecl *, 10> FieldChain;
|
|
|
|
|
|
|
|
private:
|
|
|
|
StoreManager &StoreMgr;
|
|
|
|
MemRegionManager &MrMgr;
|
|
|
|
Store store;
|
|
|
|
|
|
|
|
public:
|
|
|
|
FindUninitializedField(StoreManager &storeMgr, MemRegionManager &mrMgr,
|
|
|
|
Store s)
|
|
|
|
: StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {}
|
|
|
|
|
|
|
|
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()) {
|
|
|
|
const FieldRegion *FR = MrMgr.getFieldRegion(I, R);
|
|
|
|
FieldChain.push_back(I);
|
|
|
|
T = I->getType();
|
|
|
|
if (T->getAsStructureType()) {
|
|
|
|
if (Find(FR))
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
const SVal &V = StoreMgr.getBinding(store, loc::MemRegionVal(FR));
|
|
|
|
if (V.isUndef())
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
FieldChain.pop_back();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
2018-10-14 06:18:22 +08:00
|
|
|
} // namespace
|
2018-08-30 04:29:39 +08:00
|
|
|
|
2014-03-14 01:55:39 +08:00
|
|
|
bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
|
|
|
|
SVal V,
|
|
|
|
SourceRange ArgRange,
|
|
|
|
const Expr *ArgEx,
|
2017-03-08 23:22:24 +08:00
|
|
|
int ArgumentNumber,
|
2014-03-14 01:55:39 +08:00
|
|
|
bool CheckUninitFields,
|
|
|
|
const CallEvent &Call,
|
|
|
|
std::unique_ptr<BugType> &BT,
|
|
|
|
const ParmVarDecl *ParamDecl
|
|
|
|
) const {
|
|
|
|
const char *BD = "Uninitialized argument value";
|
|
|
|
|
2017-03-08 23:22:24 +08:00
|
|
|
if (uninitRefOrPointer(C, V, ArgRange, ArgEx, BT, ParamDecl, BD,
|
|
|
|
ArgumentNumber))
|
2014-03-14 01:55:39 +08:00
|
|
|
return true;
|
|
|
|
|
2010-03-18 11:22:29 +08:00
|
|
|
if (V.isUndef()) {
|
2015-09-17 06:03:05 +08:00
|
|
|
if (ExplodedNode *N = C.generateErrorNode()) {
|
2014-03-14 01:55:39 +08:00
|
|
|
LazyInit_BT(BD, BT);
|
2010-03-18 11:22:29 +08:00
|
|
|
// Generate a report for this bug.
|
2017-03-08 23:22:24 +08:00
|
|
|
SmallString<200> Buf;
|
|
|
|
llvm::raw_svector_ostream Os(Buf);
|
|
|
|
describeUninitializedArgumentInCall(Call, ArgumentNumber, Os);
|
|
|
|
auto R = llvm::make_unique<BugReport>(*BT, Os.str(), N);
|
|
|
|
|
2014-03-14 01:55:39 +08:00
|
|
|
R->addRange(ArgRange);
|
|
|
|
if (ArgEx)
|
2018-10-24 02:24:53 +08:00
|
|
|
bugreporter::trackExpressionValue(N, ArgEx, *R);
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(std::move(R));
|
2010-03-18 11:22:29 +08:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-03-14 01:55:39 +08:00
|
|
|
if (!CheckUninitFields)
|
2012-03-06 07:57:14 +08:00
|
|
|
return false;
|
2013-02-20 13:52:05 +08:00
|
|
|
|
2018-08-30 04:29:39 +08:00
|
|
|
if (auto LV = V.getAs<nonloc::LazyCompoundVal>()) {
|
2010-03-18 11:22:29 +08:00
|
|
|
const LazyCompoundValData *D = LV->getCVData();
|
2012-06-07 01:32:50 +08:00
|
|
|
FindUninitializedField F(C.getState()->getStateManager().getStoreManager(),
|
2010-12-02 15:49:45 +08:00
|
|
|
C.getSValBuilder().getRegionManager(),
|
2010-03-18 11:22:29 +08:00
|
|
|
D->getStore());
|
|
|
|
|
|
|
|
if (F.Find(D->getRegion())) {
|
2015-09-17 06:03:05 +08:00
|
|
|
if (ExplodedNode *N = C.generateErrorNode()) {
|
2014-03-14 01:55:39 +08:00
|
|
|
LazyInit_BT(BD, BT);
|
2012-02-05 10:13:05 +08:00
|
|
|
SmallString<512> Str;
|
2010-03-18 11:22:29 +08:00
|
|
|
llvm::raw_svector_ostream os(Str);
|
|
|
|
os << "Passed-by-value struct argument contains uninitialized data";
|
|
|
|
|
|
|
|
if (F.FieldChain.size() == 1)
|
2011-10-15 02:45:37 +08:00
|
|
|
os << " (e.g., field: '" << *F.FieldChain[0] << "')";
|
2010-03-18 11:22:29 +08:00
|
|
|
else {
|
|
|
|
os << " (e.g., via the field chain: '";
|
|
|
|
bool first = true;
|
2011-07-23 18:55:15 +08:00
|
|
|
for (SmallVectorImpl<const FieldDecl *>::iterator
|
2010-03-18 11:22:29 +08:00
|
|
|
DI = F.FieldChain.begin(), DE = F.FieldChain.end(); DI!=DE;++DI){
|
|
|
|
if (first)
|
|
|
|
first = false;
|
|
|
|
else
|
|
|
|
os << '.';
|
2011-10-15 02:45:37 +08:00
|
|
|
os << **DI;
|
2010-03-18 11:22:29 +08:00
|
|
|
}
|
|
|
|
os << "')";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate a report for this bug.
|
2015-06-23 21:15:32 +08:00
|
|
|
auto R = llvm::make_unique<BugReport>(*BT, os.str(), N);
|
2014-03-14 01:55:39 +08:00
|
|
|
R->addRange(ArgRange);
|
2010-03-18 11:22:29 +08:00
|
|
|
|
2018-08-30 06:48:50 +08:00
|
|
|
if (ArgEx)
|
2018-10-24 02:24:53 +08:00
|
|
|
bugreporter::trackExpressionValue(N, ArgEx, *R);
|
2010-03-18 11:22:29 +08:00
|
|
|
// FIXME: enhance track back for uninitialized value for arbitrary
|
|
|
|
// memregions
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(std::move(R));
|
2010-03-18 11:22:29 +08:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-02-28 09:28:13 +08:00
|
|
|
void CallAndMessageChecker::checkPreStmt(const CallExpr *CE,
|
|
|
|
CheckerContext &C) const{
|
2010-03-18 10:17:27 +08:00
|
|
|
|
2009-11-21 09:25:37 +08:00
|
|
|
const Expr *Callee = CE->getCallee()->IgnoreParens();
|
2012-07-03 03:27:46 +08:00
|
|
|
ProgramStateRef State = C.getState();
|
2012-01-07 06:09:28 +08:00
|
|
|
const LocationContext *LCtx = C.getLocationContext();
|
2012-07-03 03:27:46 +08:00
|
|
|
SVal L = State->getSVal(Callee, LCtx);
|
2010-03-18 10:17:27 +08:00
|
|
|
|
2009-11-21 09:25:37 +08:00
|
|
|
if (L.isUndef()) {
|
|
|
|
if (!BT_call_undef)
|
2014-02-12 05:49:21 +08:00
|
|
|
BT_call_undef.reset(new BuiltinBug(
|
2016-03-24 00:14:12 +08:00
|
|
|
this, "Called function pointer is an uninitialized pointer value"));
|
2012-08-04 07:08:49 +08:00
|
|
|
emitBadCall(BT_call_undef.get(), C, Callee);
|
2009-11-21 09:25:37 +08:00
|
|
|
return;
|
|
|
|
}
|
2010-03-18 10:17:27 +08:00
|
|
|
|
2012-08-04 09:04:52 +08:00
|
|
|
ProgramStateRef StNonNull, StNull;
|
2014-03-02 21:01:17 +08:00
|
|
|
std::tie(StNonNull, StNull) = State->assume(L.castAs<DefinedOrUnknownSVal>());
|
2012-08-04 09:04:52 +08:00
|
|
|
|
|
|
|
if (StNull && !StNonNull) {
|
2009-11-21 09:25:37 +08:00
|
|
|
if (!BT_call_null)
|
2014-02-12 05:49:21 +08:00
|
|
|
BT_call_null.reset(new BuiltinBug(
|
|
|
|
this, "Called function pointer is null (null dereference)"));
|
2012-08-04 07:08:49 +08:00
|
|
|
emitBadCall(BT_call_null.get(), C, Callee);
|
2013-10-02 09:20:28 +08:00
|
|
|
return;
|
2010-03-18 10:17:27 +08:00
|
|
|
}
|
2012-08-16 05:56:23 +08:00
|
|
|
|
|
|
|
C.addTransition(StNonNull);
|
2012-07-03 03:28:21 +08:00
|
|
|
}
|
2010-03-18 10:17:27 +08:00
|
|
|
|
2013-08-09 08:55:47 +08:00
|
|
|
void CallAndMessageChecker::checkPreStmt(const CXXDeleteExpr *DE,
|
|
|
|
CheckerContext &C) const {
|
|
|
|
|
|
|
|
SVal Arg = C.getSVal(DE->getArgument());
|
|
|
|
if (Arg.isUndef()) {
|
|
|
|
StringRef Desc;
|
2015-09-17 06:03:05 +08:00
|
|
|
ExplodedNode *N = C.generateErrorNode();
|
2013-08-09 08:55:47 +08:00
|
|
|
if (!N)
|
|
|
|
return;
|
|
|
|
if (!BT_cxx_delete_undef)
|
2014-02-12 05:49:21 +08:00
|
|
|
BT_cxx_delete_undef.reset(
|
|
|
|
new BuiltinBug(this, "Uninitialized argument value"));
|
2013-08-09 08:55:47 +08:00
|
|
|
if (DE->isArrayFormAsWritten())
|
|
|
|
Desc = "Argument to 'delete[]' is uninitialized";
|
|
|
|
else
|
|
|
|
Desc = "Argument to 'delete' is uninitialized";
|
|
|
|
BugType *BT = BT_cxx_delete_undef.get();
|
2015-06-23 21:15:32 +08:00
|
|
|
auto R = llvm::make_unique<BugReport>(*BT, Desc, N);
|
2018-10-24 02:24:53 +08:00
|
|
|
bugreporter::trackExpressionValue(N, DE, *R);
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(std::move(R));
|
2013-08-09 08:55:47 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-03 03:28:21 +08:00
|
|
|
void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
|
|
|
|
CheckerContext &C) const {
|
2012-08-16 05:56:23 +08:00
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
2012-07-26 08:22:32 +08:00
|
|
|
// If this is a call to a C++ method, check if the callee is null or
|
|
|
|
// undefined.
|
2012-08-04 07:08:49 +08:00
|
|
|
if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) {
|
2012-07-26 08:22:32 +08:00
|
|
|
SVal V = CC->getCXXThisVal();
|
|
|
|
if (V.isUndef()) {
|
|
|
|
if (!BT_cxx_call_undef)
|
2014-02-12 05:49:21 +08:00
|
|
|
BT_cxx_call_undef.reset(
|
|
|
|
new BuiltinBug(this, "Called C++ object pointer is uninitialized"));
|
2012-08-04 07:08:49 +08:00
|
|
|
emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr());
|
2012-07-26 08:22:32 +08:00
|
|
|
return;
|
|
|
|
}
|
2012-08-04 09:04:52 +08:00
|
|
|
|
|
|
|
ProgramStateRef StNonNull, StNull;
|
2014-03-02 21:01:17 +08:00
|
|
|
std::tie(StNonNull, StNull) =
|
2013-02-20 13:52:05 +08:00
|
|
|
State->assume(V.castAs<DefinedOrUnknownSVal>());
|
2012-08-04 09:04:52 +08:00
|
|
|
|
|
|
|
if (StNull && !StNonNull) {
|
2012-07-26 08:22:32 +08:00
|
|
|
if (!BT_cxx_call_null)
|
2014-02-12 05:49:21 +08:00
|
|
|
BT_cxx_call_null.reset(
|
|
|
|
new BuiltinBug(this, "Called C++ object pointer is null"));
|
2012-08-04 07:08:49 +08:00
|
|
|
emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr());
|
2012-07-26 08:22:32 +08:00
|
|
|
return;
|
|
|
|
}
|
2012-08-16 05:56:23 +08:00
|
|
|
|
|
|
|
State = StNonNull;
|
2012-07-26 08:22:32 +08:00
|
|
|
}
|
|
|
|
|
2013-06-19 16:19:56 +08:00
|
|
|
const Decl *D = Call.getDecl();
|
2016-11-16 02:40:46 +08:00
|
|
|
if (D && (isa<FunctionDecl>(D) || isa<BlockDecl>(D))) {
|
|
|
|
// If we have a function or block declaration, we can make sure we pass
|
|
|
|
// enough parameters.
|
|
|
|
unsigned Params = Call.parameters().size();
|
2013-06-19 16:19:56 +08:00
|
|
|
if (Call.getNumArgs() < Params) {
|
2015-09-17 06:03:05 +08:00
|
|
|
ExplodedNode *N = C.generateErrorNode();
|
2013-06-19 16:19:56 +08:00
|
|
|
if (!N)
|
|
|
|
return;
|
|
|
|
|
|
|
|
LazyInit_BT("Function call with too few arguments", BT_call_few_args);
|
|
|
|
|
|
|
|
SmallString<512> Str;
|
|
|
|
llvm::raw_svector_ostream os(Str);
|
2016-11-16 02:40:46 +08:00
|
|
|
if (isa<FunctionDecl>(D)) {
|
|
|
|
os << "Function ";
|
|
|
|
} else {
|
|
|
|
assert(isa<BlockDecl>(D));
|
|
|
|
os << "Block ";
|
|
|
|
}
|
|
|
|
os << "taking " << Params << " argument"
|
|
|
|
<< (Params == 1 ? "" : "s") << " is called with fewer ("
|
2013-06-19 16:19:56 +08:00
|
|
|
<< Call.getNumArgs() << ")";
|
|
|
|
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(
|
|
|
|
llvm::make_unique<BugReport>(*BT_call_few_args, os.str(), N));
|
2013-06-19 16:19:56 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-03 03:28:21 +08:00
|
|
|
// 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 =
|
2012-07-26 08:22:32 +08:00
|
|
|
!(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody()));
|
2012-07-03 03:28:21 +08:00
|
|
|
|
2014-03-08 04:03:18 +08:00
|
|
|
std::unique_ptr<BugType> *BT;
|
2012-07-19 05:59:51 +08:00
|
|
|
if (isa<ObjCMethodCall>(Call))
|
2012-07-03 03:28:21 +08:00
|
|
|
BT = &BT_msg_arg;
|
2012-07-19 05:59:51 +08:00
|
|
|
else
|
2012-07-03 03:28:21 +08:00
|
|
|
BT = &BT_call_arg;
|
|
|
|
|
2016-11-16 02:40:46 +08:00
|
|
|
const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
|
2014-03-14 01:55:39 +08:00
|
|
|
for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) {
|
2014-05-27 10:45:47 +08:00
|
|
|
const ParmVarDecl *ParamDecl = nullptr;
|
2014-03-14 01:55:39 +08:00
|
|
|
if(FD && i < FD->getNumParams())
|
|
|
|
ParamDecl = FD->getParamDecl(i);
|
2012-07-19 05:59:51 +08:00
|
|
|
if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i),
|
2017-03-08 23:22:24 +08:00
|
|
|
Call.getArgExpr(i), i,
|
2014-03-14 01:55:39 +08:00
|
|
|
checkUninitFields, Call, *BT, ParamDecl))
|
2012-07-03 03:28:21 +08:00
|
|
|
return;
|
2014-03-14 01:55:39 +08:00
|
|
|
}
|
2012-08-16 05:56:23 +08:00
|
|
|
|
|
|
|
// If we make it here, record our assumptions about the callee.
|
|
|
|
C.addTransition(State);
|
2009-11-21 08:49:41 +08:00
|
|
|
}
|
|
|
|
|
2012-07-03 03:28:04 +08:00
|
|
|
void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
|
2011-02-28 09:28:13 +08:00
|
|
|
CheckerContext &C) const {
|
2012-07-03 03:28:04 +08:00
|
|
|
SVal recVal = msg.getReceiverSVal();
|
|
|
|
if (recVal.isUndef()) {
|
2015-09-17 06:03:05 +08:00
|
|
|
if (ExplodedNode *N = C.generateErrorNode()) {
|
2014-05-27 10:45:47 +08:00
|
|
|
BugType *BT = nullptr;
|
2012-07-19 05:59:51 +08:00
|
|
|
switch (msg.getMessageKind()) {
|
|
|
|
case OCM_Message:
|
2012-07-03 03:28:04 +08:00
|
|
|
if (!BT_msg_undef)
|
2014-02-12 05:49:21 +08:00
|
|
|
BT_msg_undef.reset(new BuiltinBug(this,
|
|
|
|
"Receiver in message expression "
|
2012-07-03 03:28:04 +08:00
|
|
|
"is an uninitialized value"));
|
|
|
|
BT = BT_msg_undef.get();
|
2012-07-19 05:59:51 +08:00
|
|
|
break;
|
|
|
|
case OCM_PropertyAccess:
|
|
|
|
if (!BT_objc_prop_undef)
|
2014-02-12 05:49:21 +08:00
|
|
|
BT_objc_prop_undef.reset(new BuiltinBug(
|
|
|
|
this, "Property access on an uninitialized object pointer"));
|
2012-07-19 05:59:51 +08:00
|
|
|
BT = BT_objc_prop_undef.get();
|
|
|
|
break;
|
|
|
|
case OCM_Subscript:
|
|
|
|
if (!BT_objc_subscript_undef)
|
2014-02-12 05:49:21 +08:00
|
|
|
BT_objc_subscript_undef.reset(new BuiltinBug(
|
|
|
|
this, "Subscript access on an uninitialized object pointer"));
|
2012-07-19 05:59:51 +08:00
|
|
|
BT = BT_objc_subscript_undef.get();
|
|
|
|
break;
|
2012-07-03 03:28:04 +08:00
|
|
|
}
|
2012-07-19 05:59:51 +08:00
|
|
|
assert(BT && "Unknown message kind.");
|
|
|
|
|
2015-06-23 21:15:32 +08:00
|
|
|
auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N);
|
2012-07-19 05:59:51 +08:00
|
|
|
const ObjCMessageExpr *ME = msg.getOriginExpr();
|
|
|
|
R->addRange(ME->getReceiverRange());
|
2009-11-21 08:49:41 +08:00
|
|
|
|
2012-07-03 03:28:04 +08:00
|
|
|
// FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet.
|
2012-07-19 05:59:51 +08:00
|
|
|
if (const Expr *ReceiverE = ME->getInstanceReceiver())
|
2018-10-24 02:24:53 +08:00
|
|
|
bugreporter::trackExpressionValue(N, ReceiverE, *R);
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(std::move(R));
|
2012-07-03 03:28:04 +08:00
|
|
|
}
|
|
|
|
return;
|
2011-02-28 09:28:13 +08:00
|
|
|
}
|
2009-12-02 13:49:12 +08:00
|
|
|
}
|
2009-11-24 15:06:39 +08:00
|
|
|
|
2015-09-15 09:13:53 +08:00
|
|
|
void CallAndMessageChecker::checkObjCMessageNil(const ObjCMethodCall &msg,
|
|
|
|
CheckerContext &C) const {
|
|
|
|
HandleNilReceiver(C, C.getState(), msg);
|
|
|
|
}
|
|
|
|
|
2010-12-02 15:49:45 +08:00
|
|
|
void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
|
2012-07-03 03:28:04 +08:00
|
|
|
const ObjCMethodCall &msg,
|
2011-02-28 09:28:13 +08:00
|
|
|
ExplodedNode *N) const {
|
2010-03-18 10:17:27 +08:00
|
|
|
|
2009-11-25 05:41:28 +08:00
|
|
|
if (!BT_msg_ret)
|
2011-02-28 09:28:13 +08:00
|
|
|
BT_msg_ret.reset(
|
2014-02-12 05:49:21 +08:00
|
|
|
new BuiltinBug(this, "Receiver in message expression is 'nil'"));
|
2010-03-18 10:17:27 +08:00
|
|
|
|
2012-07-19 05:59:51 +08:00
|
|
|
const ObjCMessageExpr *ME = msg.getOriginExpr();
|
|
|
|
|
2013-04-04 03:28:19 +08:00
|
|
|
QualType ResTy = msg.getResultType();
|
|
|
|
|
2012-02-05 10:13:05 +08:00
|
|
|
SmallString<200> buf;
|
2009-11-25 05:41:28 +08:00
|
|
|
llvm::raw_svector_ostream os(buf);
|
2014-01-04 01:59:55 +08:00
|
|
|
os << "The receiver of message '";
|
|
|
|
ME->getSelector().print(os);
|
|
|
|
os << "' is nil";
|
2013-04-04 03:28:19 +08:00
|
|
|
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";
|
|
|
|
}
|
2010-03-18 10:17:27 +08:00
|
|
|
|
2015-06-23 21:15:32 +08:00
|
|
|
auto report = llvm::make_unique<BugReport>(*BT_msg_ret, os.str(), N);
|
2012-07-19 05:59:51 +08:00
|
|
|
report->addRange(ME->getReceiverRange());
|
2012-07-03 03:28:04 +08:00
|
|
|
// FIXME: This won't track "self" in messages to super.
|
2012-07-19 05:59:51 +08:00
|
|
|
if (const Expr *receiver = ME->getInstanceReceiver()) {
|
2018-10-24 02:24:53 +08:00
|
|
|
bugreporter::trackExpressionValue(N, receiver, *report);
|
Overhaul the AST representation of Objective-C message send
expressions, to improve source-location information, clarify the
actual receiver of the message, and pave the way for proper C++
support. The ObjCMessageExpr node represents four different kinds of
message sends in a single AST node:
1) Send to a object instance described by an expression (e.g., [x method:5])
2) Send to a class described by the class name (e.g., [NSString method:5])
3) Send to a superclass class (e.g, [super method:5] in class method)
4) Send to a superclass instance (e.g., [super method:5] in instance method)
Previously these four cases where tangled together. Now, they have
more distinct representations. Specific changes:
1) Unchanged; the object instance is represented by an Expr*.
2) Previously stored the ObjCInterfaceDecl* referring to the class
receiving the message. Now stores a TypeSourceInfo* so that we know
how the class was spelled. This both maintains typedef information
and opens the door for more complicated C++ types (e.g., dependent
types). There was an alternative, unused representation of these
sends by naming the class via an IdentifierInfo *. In practice, we
either had an ObjCInterfaceDecl *, from which we would get the
IdentifierInfo *, or we fell into the case below...
3) Previously represented by a class message whose IdentifierInfo *
referred to "super". Sema and CodeGen would use isStr("super") to
determine if they had a send to super. Now represented as a
"class super" send, where we have both the location of the "super"
keyword and the ObjCInterfaceDecl* of the superclass we're
targetting (statically).
4) Previously represented by an instance message whose receiver is a
an ObjCSuperExpr, which Sema and CodeGen would check for via
isa<ObjCSuperExpr>(). Now represented as an "instance super" send,
where we have both the location of the "super" keyword and the
ObjCInterfaceDecl* of the superclass we're targetting
(statically). Note that ObjCSuperExpr only has one remaining use in
the AST, which is for "super.prop" references.
The new representation of ObjCMessageExpr is 2 pointers smaller than
the old one, since it combines more storage. It also eliminates a leak
when we loaded message-send expressions from a precompiled header. The
representation also feels much cleaner to me; comments welcome!
This patch attempts to maintain the same semantics we previously had
with Objective-C message sends. In several places, there are massive
changes that boil down to simply replacing a nested-if structure such
as:
if (message has a receiver expression) {
// instance message
if (isa<ObjCSuperExpr>(...)) {
// send to super
} else {
// send to an object
}
} else {
// class message
if (name->isStr("super")) {
// class send to super
} else {
// send to class
}
}
with a switch
switch (E->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance: ...
case ObjCMessageExpr::Instance: ...
case ObjCMessageExpr::SuperClass: ...
case ObjCMessageExpr::Class:...
}
There are quite a few places (particularly in the checkers) where
send-to-super is effectively ignored. I've placed FIXMEs in most of
them, and attempted to address send-to-super in a reasonable way. This
could use some review.
llvm-svn: 101972
2010-04-21 08:45:42 +08:00
|
|
|
}
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(std::move(report));
|
2009-11-25 05:41:28 +08:00
|
|
|
}
|
|
|
|
|
2010-12-02 15:49:45 +08:00
|
|
|
static bool supportsNilWithFloatRet(const llvm::Triple &triple) {
|
2012-02-01 07:52:54 +08:00
|
|
|
return (triple.getVendor() == llvm::Triple::Apple &&
|
2015-10-31 00:30:27 +08:00
|
|
|
(triple.isiOS() || triple.isWatchOS() ||
|
|
|
|
!triple.isMacOSXVersionLT(10,5)));
|
2009-11-25 06:48:18 +08:00
|
|
|
}
|
|
|
|
|
2009-11-25 05:41:28 +08:00
|
|
|
void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state,
|
2012-07-03 03:28:04 +08:00
|
|
|
const ObjCMethodCall &Msg) const {
|
2011-01-25 08:03:53 +08:00
|
|
|
ASTContext &Ctx = C.getASTContext();
|
2014-02-18 02:25:34 +08:00
|
|
|
static CheckerProgramPointTag Tag(this, "NilReceiver");
|
2010-03-18 10:17:27 +08:00
|
|
|
|
2009-11-25 05:41:28 +08:00
|
|
|
// Check the return type of the message expression. A message to nil will
|
|
|
|
// return different values depending on the return type and the architecture.
|
2012-07-03 03:28:04 +08:00
|
|
|
QualType RetTy = Msg.getResultType();
|
2009-11-25 06:48:18 +08:00
|
|
|
CanQualType CanRetTy = Ctx.getCanonicalType(RetTy);
|
2012-01-07 06:09:28 +08:00
|
|
|
const LocationContext *LCtx = C.getLocationContext();
|
2009-11-25 05:41:28 +08:00
|
|
|
|
2010-04-27 05:31:17 +08:00
|
|
|
if (CanRetTy->isStructureOrClassType()) {
|
2011-10-29 03:05:10 +08:00
|
|
|
// Structure returns are safe since the compiler zeroes them out.
|
2012-07-03 03:28:04 +08:00
|
|
|
SVal V = C.getSValBuilder().makeZeroVal(RetTy);
|
2013-03-28 01:35:58 +08:00
|
|
|
C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V), &Tag);
|
2009-11-25 05:41:28 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-10-29 03:05:10 +08:00
|
|
|
// Other cases: check if sizeof(return type) > sizeof(void*)
|
2011-11-02 06:41:01 +08:00
|
|
|
if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap()
|
2012-07-03 03:28:04 +08:00
|
|
|
.isConsumedExpr(Msg.getOriginExpr())) {
|
2009-11-25 05:41:28 +08:00
|
|
|
// Compute: sizeof(void *) and sizeof(return type)
|
2010-03-18 10:17:27 +08:00
|
|
|
const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy);
|
2009-11-25 06:48:18 +08:00
|
|
|
const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy);
|
2009-11-25 05:41:28 +08:00
|
|
|
|
2013-04-04 03:28:19 +08:00
|
|
|
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)))) {
|
2015-09-17 06:03:05 +08:00
|
|
|
if (ExplodedNode *N = C.generateErrorNode(state, &Tag))
|
2012-07-03 03:28:04 +08:00
|
|
|
emitNilReceiverBug(C, Msg, N);
|
2009-11-25 05:41:28 +08:00
|
|
|
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.
|
|
|
|
//
|
2012-07-03 03:28:04 +08:00
|
|
|
SVal V = C.getSValBuilder().makeZeroVal(RetTy);
|
2013-03-28 01:35:58 +08:00
|
|
|
C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V), &Tag);
|
2009-11-25 05:41:28 +08:00
|
|
|
return;
|
|
|
|
}
|
2010-03-18 10:17:27 +08:00
|
|
|
|
2011-10-27 05:06:34 +08:00
|
|
|
C.addTransition(state);
|
2009-11-03 14:46:03 +08:00
|
|
|
}
|
2011-02-28 09:28:13 +08:00
|
|
|
|
2014-03-14 01:55:39 +08:00
|
|
|
#define REGISTER_CHECKER(name) \
|
|
|
|
void ento::register##name(CheckerManager &mgr) { \
|
|
|
|
CallAndMessageChecker *Checker = \
|
|
|
|
mgr.registerChecker<CallAndMessageChecker>(); \
|
|
|
|
Checker->Filter.Check_##name = true; \
|
|
|
|
Checker->Filter.CheckName_##name = mgr.getCurrentCheckName(); \
|
|
|
|
}
|
|
|
|
|
|
|
|
REGISTER_CHECKER(CallAndMessageUnInitRefArg)
|
|
|
|
REGISTER_CHECKER(CallAndMessageChecker)
|