#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/Calls.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
namespace {
class AttrNonNullChecker
- : public Checker< check::PreStmt<CallExpr> > {
+ : public Checker< check::PreCall > {
mutable OwningPtr<BugType> BT;
public:
- void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
};
} // end anonymous namespace
-void AttrNonNullChecker::checkPreStmt(const CallExpr *CE,
+void AttrNonNullChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
- ProgramStateRef state = C.getState();
- const LocationContext *LCtx = C.getLocationContext();
-
- // Check if the callee has a 'nonnull' attribute.
- SVal X = state->getSVal(CE->getCallee(), LCtx);
-
- const FunctionDecl *FD = X.getAsFunctionDecl();
+ const Decl *FD = Call.getDecl();
if (!FD)
return;
- const NonNullAttr* Att = FD->getAttr<NonNullAttr>();
+ const NonNullAttr *Att = FD->getAttr<NonNullAttr>();
if (!Att)
return;
- // Iterate through the arguments of CE and check them for null.
- unsigned idx = 0;
-
- for (CallExpr::const_arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E;
- ++I, ++idx) {
+ ProgramStateRef state = C.getState();
+ // Iterate through the arguments of CE and check them for null.
+ for (unsigned idx = 0, count = Call.getNumArgs(); idx != count; ++idx) {
if (!Att->isNonNull(idx))
continue;
- SVal V = state->getSVal(*I, LCtx);
+ SVal V = Call.getArgSVal(idx);
DefinedSVal *DV = dyn_cast<DefinedSVal>(&V);
// If the value is unknown or undefined, we can't perform this check.
if (!isa<Loc>(*DV)) {
// If the argument is a union type, we want to handle a potential
- // transparent_unoin GCC extension.
- QualType T = (*I)->getType();
+ // transparent_union GCC extension.
+ const Expr *ArgE = Call.getArgExpr(idx);
+ if (!ArgE)
+ continue;
+
+ QualType T = ArgE->getType();
const RecordType *UT = T->getAsUnionType();
if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>())
continue;
+
if (nonloc::CompoundVal *CSV = dyn_cast<nonloc::CompoundVal>(DV)) {
nonloc::CompoundVal::iterator CSV_I = CSV->begin();
assert(CSV_I != CSV->end());
assert(++CSV_I == CSV->end());
if (!DV)
continue;
- }
- else {
+ } else {
// FIXME: Handle LazyCompoundVals?
continue;
}
"'nonnull' parameter", errorNode);
// Highlight the range of the argument that was null.
- const Expr *arg = *I;
- R->addRange(arg->getSourceRange());
- R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(errorNode,
- arg, R));
+ R->addRange(Call.getArgSourceRange(idx));
+ if (const Expr *ArgE = Call.getArgExpr(idx))
+ R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(errorNode,
+ ArgE, R));
// Emit the bug report.
C.EmitReport(R);
}
namespace {
class CallAndMessageChecker
- : public Checker< check::PreStmt<CallExpr>, check::PreObjCMessage > {
+ : public Checker< check::PreStmt<CallExpr>, check::PreObjCMessage,
+ check::PreCall > {
mutable OwningPtr<BugType> BT_call_null;
mutable OwningPtr<BugType> BT_call_undef;
mutable OwningPtr<BugType> BT_call_arg;
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
private:
- static void PreVisitProcessArgs(CheckerContext &C, const CallEvent &Call,
- const char *BT_desc, OwningPtr<BugType> &BT);
- static bool PreVisitProcessArg(CheckerContext &C, SVal V,SourceRange argRange,
- const Expr *argEx,
+ static bool PreVisitProcessArg(CheckerContext &C, SVal V,
+ SourceRange argRange, const Expr *argEx,
const bool checkUninitFields,
- const char *BT_desc,
- OwningPtr<BugType> &BT);
+ const char *BT_desc, OwningPtr<BugType> &BT);
static void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE);
void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg,
C.EmitReport(R);
}
-void CallAndMessageChecker::PreVisitProcessArgs(CheckerContext &C,
- const CallEvent &Call,
- const char *BT_desc,
- OwningPtr<BugType> &BT) {
- // 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 Decl *D = Call.getDecl();
- const bool checkUninitFields =
- !(C.getAnalysisManager().shouldInlineCall() &&
- (D && D->getBody()));
-
- for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i)
- if (PreVisitProcessArg(C, Call.getArgSVal(i),
- Call.getArgSourceRange(i), Call.getArgExpr(i),
- checkUninitFields, BT_desc, BT))
- return;
-}
-
bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
SVal V, SourceRange argRange,
const Expr *argEx,
OwningPtr<BugType> &BT) {
if (V.isUndef()) {
if (ExplodedNode *N = C.generateSink()) {
- LazyInit_BT(BT_desc, BT);
+ LazyInit_BT("Uninitialized argument value", BT);
// Generate a report for this bug.
- BugReport *R = new BugReport(*BT, BT->getName(), N);
+ BugReport *R = new BugReport(*BT, BT_desc, N);
R->addRange(argRange);
if (argEx)
R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, argEx,
new BuiltinBug("Called function pointer is null (null dereference)"));
EmitBadCall(BT_call_null.get(), C, CE);
}
+}
- // FIXME: This tree of switching can go away if/when we add a check::postCall.
- if (dyn_cast_or_null<BlockDataRegion>(L.getAsRegion())) {
- BlockCall Call(CE, State, LCtx);
- PreVisitProcessArgs(C, Call,
- "Block call argument is an uninitialized value",
- BT_call_arg);
- } else if (const CXXMemberCallExpr *me = dyn_cast<CXXMemberCallExpr>(CE)) {
- CXXMemberCall Call(me, State, LCtx);
- PreVisitProcessArgs(C, Call,
- "Function call argument is an uninitialized value",
- BT_call_arg);
- } else {
- FunctionCall Call(CE, State, LCtx);
- PreVisitProcessArgs(C, Call,
- "Function call argument is an uninitialized value",
- BT_call_arg);
+void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ // 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 Decl *D = Call.getDecl();
+ const bool checkUninitFields =
+ !(C.getAnalysisManager().shouldInlineCall() &&
+ (D && D->getBody()));
+
+ OwningPtr<BugType> *BT;
+ const char *Desc;
+
+ switch (Call.getKind()) {
+ case CE_ObjCPropertyAccess:
+ BT = &BT_msg_arg;
+ // Getters do not have arguments, so we don't need to worry about this.
+ Desc = "Argument for property setter is an uninitialized value";
+ break;
+ case CE_ObjCMessage:
+ BT = &BT_msg_arg;
+ Desc = "Argument in message expression is an uninitialized value";
+ break;
+ case CE_Block:
+ BT = &BT_call_arg;
+ Desc = "Block call argument is an uninitialized value";
+ break;
+ default:
+ BT = &BT_call_arg;
+ Desc = "Function call argument is an uninitialized value";
+ break;
}
+
+ for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i)
+ if (PreVisitProcessArg(C, Call.getArgSVal(i),
+ Call.getArgSourceRange(i), Call.getArgExpr(i),
+ checkUninitFields, Desc, *BT))
+ return;
}
void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
return;
}
}
-
- const char *bugDesc = "Argument in message expression is an "
- "uninitialized value";
- if (const ObjCPropertyAccess *Prop = dyn_cast<ObjCPropertyAccess>(&msg))
- if (Prop->isSetter())
- bugDesc = "Argument for property setter is an uninitialized value";
-
- // Check for any arguments that are uninitialized/undefined.
- PreVisitProcessArgs(C, msg, bugDesc, BT_msg_arg);
}
void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
static bool isSelfVar(SVal location, CheckerContext &C);
namespace {
-class ObjCSelfInitChecker : public Checker< check::PreObjCMessage,
- check::PostObjCMessage,
+class ObjCSelfInitChecker : public Checker< check::PostObjCMessage,
check::PostStmt<ObjCIvarRefExpr>,
check::PreStmt<ReturnStmt>,
- check::PreStmt<CallExpr>,
- check::PostStmt<CallExpr>,
+ check::PreCall,
+ check::PostCall,
check::Location,
check::Bind > {
public:
- void checkPreObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const;
void checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const;
void checkPostStmt(const ObjCIvarRefExpr *E, CheckerContext &C) const;
void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
- void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
- void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
void checkLocation(SVal location, bool isLoad, const Stmt *S,
CheckerContext &C) const;
void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const;
- void checkPreStmt(const CallEvent &CE, CheckerContext &C) const;
- void checkPostStmt(const CallEvent &CE, CheckerContext &C) const;
+ void checkPreCall(const CallEvent &CE, CheckerContext &C) const;
+ void checkPostCall(const CallEvent &CE, CheckerContext &C) const;
};
} // end anonymous namespace
return;
}
- checkPostStmt(Msg, C);
-
// We don't check for an invalid 'self' in an obj-c message expression to cut
// down false positives where logging functions get information from self
// (like its class) or doing "invalidation" on self when the initialization
"'[(super or self) init...]'");
}
-// When a call receives a reference to 'self', [Pre/Post]VisitGenericCall pass
-// the SelfFlags from the object 'self' point to before the call, to the new
+// When a call receives a reference to 'self', [Pre/Post]Call pass
+// the SelfFlags from the object 'self' points to before the call to the new
// object after the call. This is to avoid invalidation of 'self' by logging
// functions.
// Another common pattern in classes with multiple initializers is to put the
// Until we can use inter-procedural analysis, in such a call, transfer the
// SelfFlags to the result of the call.
-void ObjCSelfInitChecker::checkPreStmt(const CallExpr *CE,
+void ObjCSelfInitChecker::checkPreCall(const CallEvent &CE,
CheckerContext &C) const {
- // FIXME: This tree of switching can go away if/when we add a check::postCall.
- const Expr *Callee = CE->getCallee()->IgnoreParens();
- ProgramStateRef State = C.getState();
- const LocationContext *LCtx = C.getLocationContext();
- SVal L = State->getSVal(Callee, LCtx);
-
- if (dyn_cast_or_null<BlockDataRegion>(L.getAsRegion())) {
- BlockCall Call(CE, State, LCtx);
- checkPreStmt(Call, C);
- } else if (const CXXMemberCallExpr *me = dyn_cast<CXXMemberCallExpr>(CE)) {
- CXXMemberCall Call(me, State, LCtx);
- checkPreStmt(Call, C);
- } else {
- FunctionCall Call(CE, State, LCtx);
- checkPreStmt(Call, C);
- }
-}
-
-void ObjCSelfInitChecker::checkPostStmt(const CallExpr *CE,
- CheckerContext &C) const {
- // FIXME: This tree of switching can go away if/when we add a check::postCall.
- const Expr *Callee = CE->getCallee()->IgnoreParens();
- ProgramStateRef State = C.getState();
- const LocationContext *LCtx = C.getLocationContext();
- SVal L = State->getSVal(Callee, LCtx);
-
- if (dyn_cast_or_null<BlockDataRegion>(L.getAsRegion())) {
- BlockCall Call(CE, State, LCtx);
- checkPostStmt(Call, C);
- } else if (const CXXMemberCallExpr *me = dyn_cast<CXXMemberCallExpr>(CE)) {
- CXXMemberCall Call(me, State, LCtx);
- checkPostStmt(Call, C);
- } else {
- FunctionCall Call(CE, State, LCtx);
- checkPostStmt(Call, C);
- }
-}
-
-void ObjCSelfInitChecker::checkPreObjCMessage(const ObjCMethodCall &Msg,
- CheckerContext &C) const {
- checkPreStmt(Msg, C);
-}
+ // FIXME: A callback should disable checkers at the start of functions.
+ if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
+ C.getCurrentAnalysisDeclContext()->getDecl())))
+ return;
-void ObjCSelfInitChecker::checkPreStmt(const CallEvent &CE,
- CheckerContext &C) const {
ProgramStateRef state = C.getState();
unsigned NumArgs = CE.getNumArgs();
// If we passed 'self' as and argument to the call, record it in the state
}
}
-void ObjCSelfInitChecker::checkPostStmt(const CallEvent &CE,
+void ObjCSelfInitChecker::checkPostCall(const CallEvent &CE,
CheckerContext &C) const {
+ // FIXME: A callback should disable checkers at the start of functions.
+ if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
+ C.getCurrentAnalysisDeclContext()->getDecl())))
+ return;
+
ProgramStateRef state = C.getState();
+ SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>();
+ if (!prevFlags)
+ return;
+ state = state->remove<PreCallSelfFlags>();
+
unsigned NumArgs = CE.getNumArgs();
for (unsigned i = 0; i < NumArgs; ++i) {
SVal argV = CE.getArgSVal(i);
// If the address of 'self' is being passed to the call, assume that the
// 'self' after the call will have the same flags.
// EX: log(&self)
- SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>();
- state = state->remove<PreCallSelfFlags>();
addSelfFlag(state, state->getSVal(cast<Loc>(argV)), prevFlags, C);
return;
} else if (hasSelfFlag(argV, SelfFlag_Self, C)) {
// returns 'self'. So assign the flags, which were set on 'self' to the
// return value.
// EX: self = performMoreInitialization(self)
- SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>();
- state = state->remove<PreCallSelfFlags>();
const Expr *CallExpr = CE.getOriginExpr();
if (CallExpr)
addSelfFlag(state, state->getSVal(CallExpr, C.getLocationContext()),
check::EndPath,
check::PostStmt<BlockExpr>,
check::PostStmt<CastExpr>,
- check::PostStmt<CallExpr>,
- check::PostStmt<CXXConstructExpr>,
check::PostStmt<ObjCArrayLiteral>,
check::PostStmt<ObjCDictionaryLiteral>,
check::PostStmt<ObjCBoxedExpr>,
- check::PostObjCMessage,
+ check::PostCall,
check::PreStmt<ReturnStmt>,
check::RegionChanges,
eval::Assume,
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
- void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
- void checkPostStmt(const CXXConstructExpr *CE, CheckerContext &C) const;
void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const;
void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const;
void checkPostStmt(const ObjCBoxedExpr *BE, CheckerContext &C) const;
- void checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const;
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
void checkSummary(const RetainSummary &Summ, const CallEvent &Call,
CheckerContext &C) const;
C.addTransition(state);
}
-void RetainCountChecker::checkPostStmt(const CallExpr *CE,
- CheckerContext &C) const {
- if (C.wasInlined)
- return;
-
- // Get the callee.
- ProgramStateRef state = C.getState();
- const Expr *Callee = CE->getCallee();
- SVal L = state->getSVal(Callee, C.getLocationContext());
-
- RetainSummaryManager &Summaries = getSummaryManager(C);
-
- // FIXME: This tree of switching can go away if/when we add a check::postCall.
- if (dyn_cast_or_null<BlockDataRegion>(L.getAsRegion())) {
- // FIXME: Better support for blocks. For now we stop tracking anything
- // that is passed to blocks.
- // FIXME: Need to handle variables that are "captured" by the block.
- BlockCall Call(CE, state, C.getLocationContext());
- const RetainSummary *Summ = Summaries.getSummary(Call, state);
- checkSummary(*Summ, Call, C);
-
- } else if (const CXXMemberCallExpr *me = dyn_cast<CXXMemberCallExpr>(CE)) {
- CXXMemberCall Call(me, state, C.getLocationContext());
- const RetainSummary *Summ = Summaries.getSummary(Call, state);
- checkSummary(*Summ, Call, C);
-
- } else {
- FunctionCall Call(CE, state, C.getLocationContext());
- const RetainSummary *Summ = Summaries.getSummary(Call, state);
- checkSummary(*Summ, Call, C);
- }
-}
-
-void RetainCountChecker::checkPostStmt(const CXXConstructExpr *CE,
- CheckerContext &C) const {
- const CXXConstructorDecl *Ctor = CE->getConstructor();
- if (!Ctor)
- return;
-
- RetainSummaryManager &Summaries = getSummaryManager(C);
- ProgramStateRef state = C.getState();
- CXXConstructorCall Call(CE, state, C.getLocationContext());
-
- const RetainSummary *Summ = Summaries.getSummary(Call, state);
-
- checkSummary(*Summ, Call, C);
-}
-
void RetainCountChecker::processObjCLiterals(CheckerContext &C,
const Expr *Ex) const {
ProgramStateRef state = C.getState();
C.addTransition(State);
}
-void RetainCountChecker::checkPostObjCMessage(const ObjCMethodCall &Msg,
- CheckerContext &C) const {
- RetainSummaryManager &Summaries = getSummaryManager(C);
- const RetainSummary *Summ = Summaries.getSummary(Msg, C.getState());
+void RetainCountChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (C.wasInlined)
+ return;
- checkSummary(*Summ, Msg, C);
+ RetainSummaryManager &Summaries = getSummaryManager(C);
+ const RetainSummary *Summ = Summaries.getSummary(Call, C.getState());
+ checkSummary(*Summ, Call, C);
}
/// GetReturnType - Used to get the return type of a message expression or
builder = ^(id object) {
id x;
if (object) {
- builder(x); // expected-warning{{Function call argument is an uninitialized value}}
+ builder(x); // expected-warning{{Block call argument is an uninitialized value}}
}
};
builder(target);
--- /dev/null
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -verify %s
+
+@interface MyObject
+- (void)takePointer:(void *)ptr __attribute__((nonnull(1)));
+@end
+
+void testNonNullMethod(int *p, MyObject *obj) {
+ if (p)
+ return;
+ [obj takePointer:p]; // expected-warning{{nonnull}}
+}
+
+
+@interface Subclass : MyObject
+// [[nonnull]] is an inherited attribute.
+- (void)takePointer:(void *)ptr;
+@end
+
+void testSubclass(int *p, Subclass *obj) {
+ if (p)
+ return;
+ [obj takePointer:p]; // expected-warning{{nonnull}}
+}