Updated to Clang 3.5a.
Change-Id: I8127eb568f674c2e72635b639a3295381fe8af82
diff --git a/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
index 9af0a5a..3becdca 100644
--- a/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
@@ -112,7 +112,7 @@
<< " | Empty WorkList: "
<< (Eng.hasEmptyWorkList() ? "yes" : "no");
- B.EmitBasicReport(D, "Analyzer Statistics", "Internal Statistics",
+ B.EmitBasicReport(D, this, "Analyzer Statistics", "Internal Statistics",
output.str(), PathDiagnosticLocation(D, SM));
// Emit warning for each block we bailed out on.
@@ -129,7 +129,7 @@
outputI << "(" << NameOfRootFunction << ")" <<
": The analyzer generated a sink at this point";
B.EmitBasicReport(
- D, "Sink Point", "Internal Statistics", outputI.str(),
+ D, this, "Sink Point", "Internal Statistics", outputI.str(),
PathDiagnosticLocation::createBegin(CS->getStmt(), SM, LC));
}
}
diff --git a/lib/StaticAnalyzer/Checkers/Android.mk b/lib/StaticAnalyzer/Checkers/Android.mk
index 902fc11..bb2a539 100644
--- a/lib/StaticAnalyzer/Checkers/Android.mk
+++ b/lib/StaticAnalyzer/Checkers/Android.mk
@@ -5,6 +5,7 @@
AttrList.inc \
AttrParsedAttrList.inc \
Attrs.inc \
+ AttrVisitor.inc \
Checkers.inc \
CommentCommandList.inc \
CommentNodes.inc \
@@ -41,7 +42,6 @@
ExprInspectionChecker.cpp \
FixedAddressChecker.cpp \
GenericTaintChecker.cpp \
- IdempotentOperationChecker.cpp \
IdenticalExprChecker.cpp \
IvarInvalidationChecker.cpp \
LLVMConventionsChecker.cpp \
diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
index 312bc74..cb5b010 100644
--- a/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
@@ -25,7 +25,8 @@
namespace {
class ArrayBoundChecker :
public Checker<check::Location> {
- mutable OwningPtr<BuiltinBug> BT;
+ mutable std::unique_ptr<BuiltinBug> BT;
+
public:
void checkLocation(SVal l, bool isLoad, const Stmt* S,
CheckerContext &C) const;
@@ -66,8 +67,9 @@
return;
if (!BT)
- BT.reset(new BuiltinBug("Out-of-bound array access",
- "Access out-of-bound array element (buffer overflow)"));
+ BT.reset(new BuiltinBug(
+ this, "Out-of-bound array access",
+ "Access out-of-bound array element (buffer overflow)"));
// FIXME: It would be nice to eventually make this diagnostic more clear,
// e.g., by referencing the original declaration or by saying *why* this
diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
index 5e4b824..a8d7b3a 100644
--- a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
+++ b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
@@ -28,8 +28,8 @@
namespace {
class ArrayBoundCheckerV2 :
public Checker<check::Location> {
- mutable OwningPtr<BuiltinBug> BT;
-
+ mutable std::unique_ptr<BuiltinBug> BT;
+
enum OOB_Kind { OOB_Precedes, OOB_Excedes, OOB_Tainted };
void reportOOB(CheckerContext &C, ProgramStateRef errorState,
@@ -120,7 +120,7 @@
return;
ProgramStateRef state_precedesLowerBound, state_withinLowerBound;
- llvm::tie(state_precedesLowerBound, state_withinLowerBound) =
+ std::tie(state_precedesLowerBound, state_withinLowerBound) =
state->assume(*lowerBoundToCheck);
// Are we constrained enough to definitely precede the lower bound?
@@ -152,7 +152,7 @@
break;
ProgramStateRef state_exceedsUpperBound, state_withinUpperBound;
- llvm::tie(state_exceedsUpperBound, state_withinUpperBound) =
+ std::tie(state_exceedsUpperBound, state_withinUpperBound) =
state->assume(*upperboundToCheck);
// If we are under constrained and the index variables are tainted, report.
@@ -187,7 +187,7 @@
return;
if (!BT)
- BT.reset(new BuiltinBug("Out-of-bound access"));
+ BT.reset(new BuiltinBug(this, "Out-of-bound access"));
// FIXME: This diagnostics are preliminary. We should get far better
// diagnostics for explaining buffer overruns.
@@ -311,7 +311,6 @@
return RegionRawOffsetV2();
}
-
void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) {
mgr.registerChecker<ArrayBoundCheckerV2>();
}
diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index f66f8b7..4b2ccd4 100644
--- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -39,7 +39,8 @@
namespace {
class APIMisuse : public BugType {
public:
- APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
+ APIMisuse(const CheckerBase *checker, const char *name)
+ : BugType(checker, name, "API Misuse (Apple)") {}
};
} // end anonymous namespace
@@ -94,7 +95,7 @@
class NilArgChecker : public Checker<check::PreObjCMessage,
check::PostStmt<ObjCDictionaryLiteral>,
check::PostStmt<ObjCArrayLiteral> > {
- mutable OwningPtr<APIMisuse> BT;
+ mutable std::unique_ptr<APIMisuse> BT;
void warnIfNilExpr(const Expr *E,
const char *Msg,
@@ -170,10 +171,13 @@
assert(Arg == 1);
os << "Key argument ";
}
- os << "to '" << msg.getSelector().getAsString() << "' cannot be nil";
+ os << "to '";
+ msg.getSelector().print(os);
+ os << "' cannot be nil";
} else {
- os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"
- << msg.getSelector().getAsString() << "' cannot be nil";
+ os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '";
+ msg.getSelector().print(os);
+ os << "' cannot be nil";
}
}
@@ -188,7 +192,7 @@
const Expr *E,
CheckerContext &C) const {
if (!BT)
- BT.reset(new APIMisuse("nil argument"));
+ BT.reset(new APIMisuse(this, "nil argument"));
BugReport *R = new BugReport(*BT, Msg, N);
R->addRange(Range);
@@ -309,7 +313,7 @@
namespace {
class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
- mutable OwningPtr<APIMisuse> BT;
+ mutable std::unique_ptr<APIMisuse> BT;
mutable IdentifierInfo* II;
public:
CFNumberCreateChecker() : II(0) {}
@@ -480,8 +484,8 @@
<< " bits of the input integer will be lost.";
if (!BT)
- BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
-
+ BT.reset(new APIMisuse(this, "Bad use of CFNumberCreate"));
+
BugReport *report = new BugReport(*BT, os.str(), N);
report->addRange(CE->getArg(2)->getSourceRange());
C.emitReport(report);
@@ -494,7 +498,7 @@
namespace {
class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
- mutable OwningPtr<APIMisuse> BT;
+ mutable std::unique_ptr<APIMisuse> BT;
mutable IdentifierInfo *Retain, *Release, *MakeCollectable;
public:
CFRetainReleaseChecker(): Retain(0), Release(0), MakeCollectable(0) {}
@@ -519,8 +523,8 @@
Retain = &Ctx.Idents.get("CFRetain");
Release = &Ctx.Idents.get("CFRelease");
MakeCollectable = &Ctx.Idents.get("CFMakeCollectable");
- BT.reset(
- new APIMisuse("null passed to CFRetain/CFRelease/CFMakeCollectable"));
+ BT.reset(new APIMisuse(
+ this, "null passed to CFRetain/CFRelease/CFMakeCollectable"));
}
// Check if we called CFRetain/CFRelease/CFMakeCollectable.
@@ -548,7 +552,7 @@
// Are they equal?
ProgramStateRef stateTrue, stateFalse;
- llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
+ std::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
if (stateTrue && !stateFalse) {
ExplodedNode *N = C.generateSink(stateTrue);
@@ -586,7 +590,7 @@
mutable Selector retainS;
mutable Selector autoreleaseS;
mutable Selector drainS;
- mutable OwningPtr<BugType> BT;
+ mutable std::unique_ptr<BugType> BT;
public:
void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
@@ -597,9 +601,9 @@
CheckerContext &C) const {
if (!BT) {
- BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
- "instance"));
-
+ BT.reset(new APIMisuse(
+ this, "message incorrectly sent to class instead of class instance"));
+
ASTContext &Ctx = C.getASTContext();
releaseS = GetNullarySelector("release", Ctx);
retainS = GetNullarySelector("retain", Ctx);
@@ -620,7 +624,9 @@
SmallString<200> buf;
llvm::raw_svector_ostream os(buf);
- os << "The '" << S.getAsString() << "' message should be sent to instances "
+ os << "The '";
+ S.print(os);
+ os << "' message should be sent to instances "
"of class '" << Class->getName()
<< "' and not the class directly";
@@ -643,7 +649,7 @@
mutable Selector orderedSetWithObjectsS;
mutable Selector initWithObjectsS;
mutable Selector initWithObjectsAndKeysS;
- mutable OwningPtr<BugType> BT;
+ mutable std::unique_ptr<BugType> BT;
bool isVariadicMessage(const ObjCMethodCall &msg) const;
@@ -703,7 +709,8 @@
void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
CheckerContext &C) const {
if (!BT) {
- BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
+ BT.reset(new APIMisuse(this,
+ "Arguments passed to variadic method aren't all "
"Objective-C pointer types"));
ASTContext &Ctx = C.getASTContext();
@@ -733,8 +740,7 @@
// Verify that all arguments have Objective-C types.
Optional<ExplodedNode*> errorNode;
- ProgramStateRef state = C.getState();
-
+
for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
QualType ArgTy = msg.getArgExpr(I)->getType();
if (ArgTy->isObjCObjectPointerType())
@@ -772,8 +778,8 @@
else
os << "Argument to method '";
- os << msg.getSelector().getAsString()
- << "' should be an Objective-C pointer type, not '";
+ msg.getSelector().print(os);
+ os << "' should be an Objective-C pointer type, not '";
ArgTy.print(os, C.getLangOpts());
os << "'";
@@ -852,7 +858,7 @@
return State;
ProgramStateRef StNonNil, StNil;
- llvm::tie(StNonNil, StNil) = State->assume(*KnownCollection);
+ std::tie(StNonNil, StNil) = State->assume(*KnownCollection);
if (StNil && !StNonNil) {
// The collection is nil. This path is infeasible.
return NULL;
@@ -1135,7 +1141,10 @@
/// \brief The checker restricts the return values of APIs known to
/// never (or almost never) return 'nil'.
class ObjCNonNilReturnValueChecker
- : public Checker<check::PostObjCMessage> {
+ : public Checker<check::PostObjCMessage,
+ check::PostStmt<ObjCArrayLiteral>,
+ check::PostStmt<ObjCDictionaryLiteral>,
+ check::PostStmt<ObjCBoxedExpr> > {
mutable bool Initialized;
mutable Selector ObjectAtIndex;
mutable Selector ObjectAtIndexedSubscript;
@@ -1143,13 +1152,32 @@
public:
ObjCNonNilReturnValueChecker() : Initialized(false) {}
+
+ ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
+ ProgramStateRef State,
+ CheckerContext &C) const;
+ void assumeExprIsNonNull(const Expr *E, CheckerContext &C) const {
+ C.addTransition(assumeExprIsNonNull(E, C.getState(), C));
+ }
+
+ void checkPostStmt(const ObjCArrayLiteral *E, CheckerContext &C) const {
+ assumeExprIsNonNull(E, C);
+ }
+ void checkPostStmt(const ObjCDictionaryLiteral *E, CheckerContext &C) const {
+ assumeExprIsNonNull(E, C);
+ }
+ void checkPostStmt(const ObjCBoxedExpr *E, CheckerContext &C) const {
+ assumeExprIsNonNull(E, C);
+ }
+
void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
};
}
-static ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
- ProgramStateRef State,
- CheckerContext &C) {
+ProgramStateRef
+ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr,
+ ProgramStateRef State,
+ CheckerContext &C) const {
SVal Val = State->getSVal(NonNullExpr, C.getLocationContext());
if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>())
return State->assume(*DV, true);
@@ -1237,6 +1265,7 @@
mgr.registerChecker<ObjCLoopChecker>();
}
-void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
+void
+ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
mgr.registerChecker<ObjCNonNilReturnValueChecker>();
}
diff --git a/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
index 5169244..83a37c9 100644
--- a/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
@@ -23,7 +23,7 @@
namespace {
class BoolAssignmentChecker : public Checker< check::Bind > {
- mutable OwningPtr<BuiltinBug> BT;
+ mutable std::unique_ptr<BuiltinBug> BT;
void emitReport(ProgramStateRef state, CheckerContext &C) const;
public:
void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const;
@@ -34,7 +34,7 @@
CheckerContext &C) const {
if (ExplodedNode *N = C.addTransition(state)) {
if (!BT)
- BT.reset(new BuiltinBug("Assignment of a non-Boolean value"));
+ BT.reset(new BuiltinBug(this, "Assignment of a non-Boolean value"));
C.emitReport(new BugReport(*BT, BT->getDescription(), N));
}
}
@@ -96,7 +96,7 @@
}
ProgramStateRef stateLT, stateGE;
- llvm::tie(stateGE, stateLT) = CM.assumeDual(state, *greaterThanEqualToZero);
+ std::tie(stateGE, stateLT) = CM.assumeDual(state, *greaterThanEqualToZero);
// Is it possible for the value to be less than zero?
if (stateLT) {
@@ -132,7 +132,7 @@
}
ProgramStateRef stateGT, stateLE;
- llvm::tie(stateLE, stateGT) = CM.assumeDual(state, *lessThanEqToOne);
+ std::tie(stateLE, stateGT) = CM.assumeDual(state, *lessThanEqToOne);
// Is it possible for the value to be greater than one?
if (stateGT) {
diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index ebd3377..8e7a839 100644
--- a/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -3,6 +3,10 @@
SOURCE Checkers.td
TARGET ClangSACheckers)
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
add_clang_library(clangStaticAnalyzerCheckers
AllocationDiagnostics.cpp
AnalyzerStatsChecker.cpp
@@ -32,7 +36,6 @@
ExprInspectionChecker.cpp
FixedAddressChecker.cpp
GenericTaintChecker.cpp
- IdempotentOperationChecker.cpp
IdenticalExprChecker.cpp
IvarInvalidationChecker.cpp
LLVMConventionsChecker.cpp
@@ -71,21 +74,13 @@
UnreachableCodeChecker.cpp
VLASizeChecker.cpp
VirtualCallChecker.cpp
- )
-add_dependencies(clangStaticAnalyzerCheckers
- clangStaticAnalyzerCore
- ClangAttrClasses
- ClangAttrList
- ClangCommentNodes
- ClangDeclNodes
- ClangDiagnosticCommon
- ClangStmtNodes
+ DEPENDS
ClangSACheckers
- )
-target_link_libraries(clangStaticAnalyzerCheckers
- clangBasic
+ LINK_LIBS
clangAST
+ clangAnalysis
+ clangBasic
clangStaticAnalyzerCore
)
diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index c3736d7..d7c1d94 100644
--- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -35,11 +35,8 @@
check::DeadSymbols,
check::RegionChanges
> {
- mutable OwningPtr<BugType> BT_Null,
- BT_Bounds,
- BT_Overlap,
- BT_NotCString,
- BT_AdditionOverflow;
+ mutable std::unique_ptr<BugType> BT_Null, BT_Bounds, BT_Overlap,
+ BT_NotCString, BT_AdditionOverflow;
mutable const char *CurrentFunctionDescription;
@@ -51,6 +48,11 @@
DefaultBool CheckCStringOutOfBounds;
DefaultBool CheckCStringBufferOverlap;
DefaultBool CheckCStringNotNullTerm;
+
+ CheckName CheckNameCStringNullArg;
+ CheckName CheckNameCStringOutOfBounds;
+ CheckName CheckNameCStringBufferOverlap;
+ CheckName CheckNameCStringNotNullTerm;
};
CStringChecksFilter Filter;
@@ -221,7 +223,7 @@
return NULL;
ProgramStateRef stateNull, stateNonNull;
- llvm::tie(stateNull, stateNonNull) = assumeZero(C, state, l, S->getType());
+ std::tie(stateNull, stateNonNull) = assumeZero(C, state, l, S->getType());
if (stateNull && !stateNonNull) {
if (!Filter.CheckCStringNullArg)
@@ -232,8 +234,9 @@
return NULL;
if (!BT_Null)
- BT_Null.reset(new BuiltinBug(categories::UnixAPI,
- "Null pointer argument in call to byte string function"));
+ BT_Null.reset(new BuiltinBug(
+ Filter.CheckNameCStringNullArg, categories::UnixAPI,
+ "Null pointer argument in call to byte string function"));
SmallString<80> buf;
llvm::raw_svector_ostream os(buf);
@@ -294,8 +297,9 @@
return NULL;
if (!BT_Bounds) {
- BT_Bounds.reset(new BuiltinBug("Out-of-bound array access",
- "Byte string function accesses out-of-bound array element"));
+ BT_Bounds.reset(new BuiltinBug(
+ Filter.CheckNameCStringOutOfBounds, "Out-of-bound array access",
+ "Byte string function accesses out-of-bound array element"));
}
BuiltinBug *BT = static_cast<BuiltinBug*>(BT_Bounds.get());
@@ -439,7 +443,7 @@
// Are the two values the same?
SValBuilder &svalBuilder = C.getSValBuilder();
- llvm::tie(stateTrue, stateFalse) =
+ std::tie(stateTrue, stateFalse) =
state->assume(svalBuilder.evalEQ(state, *firstLoc, *secondLoc));
if (stateTrue && !stateFalse) {
@@ -461,7 +465,7 @@
if (!reverseTest)
return state;
- llvm::tie(stateTrue, stateFalse) = state->assume(*reverseTest);
+ std::tie(stateTrue, stateFalse) = state->assume(*reverseTest);
if (stateTrue) {
if (stateFalse) {
// If we don't know which one comes first, we can't perform this test.
@@ -506,7 +510,7 @@
if (!OverlapTest)
return state;
- llvm::tie(stateTrue, stateFalse) = state->assume(*OverlapTest);
+ std::tie(stateTrue, stateFalse) = state->assume(*OverlapTest);
if (stateTrue && !stateFalse) {
// Overlap!
@@ -526,7 +530,8 @@
return;
if (!BT_Overlap)
- BT_Overlap.reset(new BugType(categories::UnixAPI, "Improper arguments"));
+ BT_Overlap.reset(new BugType(Filter.CheckNameCStringBufferOverlap,
+ categories::UnixAPI, "Improper arguments"));
// Generate a report for this bug.
BugReport *report =
@@ -576,7 +581,7 @@
*maxMinusRightNL, cmpTy);
ProgramStateRef stateOverflow, stateOkay;
- llvm::tie(stateOverflow, stateOkay) =
+ std::tie(stateOverflow, stateOkay) =
state->assume(willOverflow.castAs<DefinedOrUnknownSVal>());
if (stateOverflow && !stateOkay) {
@@ -586,8 +591,9 @@
return NULL;
if (!BT_AdditionOverflow)
- BT_AdditionOverflow.reset(new BuiltinBug("API",
- "Sum of expressions causes overflow"));
+ BT_AdditionOverflow.reset(
+ new BuiltinBug(Filter.CheckNameCStringOutOfBounds, "API",
+ "Sum of expressions causes overflow"));
// This isn't a great error message, but this should never occur in real
// code anyway -- you'd have to create a buffer longer than a size_t can
@@ -703,8 +709,9 @@
if (ExplodedNode *N = C.addTransition(state)) {
if (!BT_NotCString)
- BT_NotCString.reset(new BuiltinBug(categories::UnixAPI,
- "Argument is not a null-terminated string."));
+ BT_NotCString.reset(new BuiltinBug(
+ Filter.CheckNameCStringNotNullTerm, categories::UnixAPI,
+ "Argument is not a null-terminated string."));
SmallString<120> buf;
llvm::raw_svector_ostream os(buf);
@@ -714,8 +721,7 @@
<< "', which is not a null-terminated string";
// Generate a report for this bug.
- BugReport *report = new BugReport(*BT_NotCString,
- os.str(), N);
+ BugReport *report = new BugReport(*BT_NotCString, os.str(), N);
report->addRange(Ex->getSourceRange());
C.emitReport(report);
@@ -763,8 +769,9 @@
if (ExplodedNode *N = C.addTransition(state)) {
if (!BT_NotCString)
- BT_NotCString.reset(new BuiltinBug(categories::UnixAPI,
- "Argument is not a null-terminated string."));
+ BT_NotCString.reset(new BuiltinBug(
+ Filter.CheckNameCStringNotNullTerm, categories::UnixAPI,
+ "Argument is not a null-terminated string."));
SmallString<120> buf;
llvm::raw_svector_ostream os(buf);
@@ -909,7 +916,7 @@
QualType sizeTy = Size->getType();
ProgramStateRef stateZeroSize, stateNonZeroSize;
- llvm::tie(stateZeroSize, stateNonZeroSize) =
+ std::tie(stateZeroSize, stateNonZeroSize) =
assumeZero(C, state, sizeVal, sizeTy);
// Get the value of the Dest.
@@ -1066,7 +1073,7 @@
QualType sizeTy = Size->getType();
ProgramStateRef stateZeroSize, stateNonZeroSize;
- llvm::tie(stateZeroSize, stateNonZeroSize) =
+ std::tie(stateZeroSize, stateNonZeroSize) =
assumeZero(C, state, sizeVal, sizeTy);
// If the size can be zero, the result will be 0 in that case, and we don't
@@ -1092,7 +1099,7 @@
// See if they are the same.
DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV);
ProgramStateRef StSameBuf, StNotSameBuf;
- llvm::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf);
+ std::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf);
// If the two arguments might be the same buffer, we know the result is 0,
// and we only need to check one size.
@@ -1150,7 +1157,7 @@
SVal maxlenVal = state->getSVal(maxlenExpr, LCtx);
ProgramStateRef stateZeroSize, stateNonZeroSize;
- llvm::tie(stateZeroSize, stateNonZeroSize) =
+ std::tie(stateZeroSize, stateNonZeroSize) =
assumeZero(C, state, maxlenVal, maxlenExpr->getType());
// If the size can be zero, the result will be 0 in that case, and we don't
@@ -1204,10 +1211,10 @@
ProgramStateRef stateStringTooLong, stateStringNotTooLong;
// Check if the strLength is greater than the maxlen.
- llvm::tie(stateStringTooLong, stateStringNotTooLong) =
- state->assume(C.getSValBuilder().evalBinOpNN(
- state, BO_GT, *strLengthNL, *maxlenValNL, cmpTy)
- .castAs<DefinedOrUnknownSVal>());
+ std::tie(stateStringTooLong, stateStringNotTooLong) = state->assume(
+ C.getSValBuilder()
+ .evalBinOpNN(state, BO_GT, *strLengthNL, *maxlenValNL, cmpTy)
+ .castAs<DefinedOrUnknownSVal>());
if (stateStringTooLong && !stateStringNotTooLong) {
// If the string is longer than maxlen, return maxlen.
@@ -1371,7 +1378,7 @@
// Check if the max number to copy is less than the length of the src.
// If the bound is equal to the source length, strncpy won't null-
// terminate the result!
- llvm::tie(stateSourceTooLong, stateSourceNotTooLong) = state->assume(
+ std::tie(stateSourceTooLong, stateSourceNotTooLong) = state->assume(
svalBuilder.evalBinOpNN(state, BO_GE, *strLengthNL, *lenValNL, cmpTy)
.castAs<DefinedOrUnknownSVal>());
@@ -1418,7 +1425,7 @@
// case strncpy will do no work at all. Our bounds check uses n-1
// as the last element accessed, so n == 0 is problematic.
ProgramStateRef StateZeroSize, StateNonZeroSize;
- llvm::tie(StateZeroSize, StateNonZeroSize) =
+ std::tie(StateZeroSize, StateNonZeroSize) =
assumeZero(C, state, *lenValNL, sizeTy);
// If the size is known to be zero, we're done.
@@ -1711,7 +1718,7 @@
SValBuilder &svalBuilder = C.getSValBuilder();
DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV);
ProgramStateRef StSameBuf, StNotSameBuf;
- llvm::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf);
+ std::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf);
// If the two arguments might be the same buffer, we know the result is 0,
// and we only need to check one size.
@@ -1928,9 +1935,8 @@
// Record string length for char a[] = "abc";
ProgramStateRef state = C.getState();
- for (DeclStmt::const_decl_iterator I = DS->decl_begin(), E = DS->decl_end();
- I != E; ++I) {
- const VarDecl *D = dyn_cast<VarDecl>(*I);
+ for (const auto *I : DS->decls()) {
+ const VarDecl *D = dyn_cast<VarDecl>(I);
if (!D)
continue;
@@ -2057,10 +2063,12 @@
C.addTransition(state);
}
-#define REGISTER_CHECKER(name) \
-void ento::register##name(CheckerManager &mgr) {\
- mgr.registerChecker<CStringChecker>()->Filter.Check##name = true; \
-}
+#define REGISTER_CHECKER(name) \
+ void ento::register##name(CheckerManager &mgr) { \
+ CStringChecker *checker = mgr.registerChecker<CStringChecker>(); \
+ checker->Filter.Check##name = true; \
+ checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \
+ }
REGISTER_CHECKER(CStringNullArg)
REGISTER_CHECKER(CStringOutOfBounds)
diff --git a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
index d29a12a..abfb971 100644
--- a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
@@ -31,6 +31,7 @@
namespace {
class WalkAST: public StmtVisitor<WalkAST> {
+ const CheckerBase *Checker;
BugReporter &BR;
AnalysisDeclContext* AC;
@@ -81,9 +82,8 @@
bool containsBadStrncatPattern(const CallExpr *CE);
public:
- WalkAST(BugReporter &br, AnalysisDeclContext* ac) :
- BR(br), AC(ac) {
- }
+ WalkAST(const CheckerBase *checker, BugReporter &br, AnalysisDeclContext *ac)
+ : Checker(checker), BR(br), AC(ac) {}
// Statement visitor methods.
void VisitChildren(Stmt *S);
@@ -157,8 +157,9 @@
os << "U";
os << "se a safer 'strlcat' API";
- BR.EmitBasicReport(FD, "Anti-pattern in the argument", "C String API",
- os.str(), Loc, LenArg->getSourceRange());
+ BR.EmitBasicReport(FD, Checker, "Anti-pattern in the argument",
+ "C String API", os.str(), Loc,
+ LenArg->getSourceRange());
}
}
@@ -179,7 +180,7 @@
void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
BugReporter &BR) const {
- WalkAST walker(BR, Mgr.getAnalysisDeclContext(D));
+ WalkAST walker(this, BR, Mgr.getAnalysisDeclContext(D));
walker.Visit(D->getBody());
}
};
diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
index fefcbe7..907f516 100644
--- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -27,24 +27,35 @@
using namespace ento;
namespace {
+
+struct ChecksFilter {
+ DefaultBool Check_CallAndMessageUnInitRefArg;
+ DefaultBool Check_CallAndMessageChecker;
+
+ CheckName CheckName_CallAndMessageUnInitRefArg;
+ CheckName CheckName_CallAndMessageChecker;
+};
+
class CallAndMessageChecker
: public Checker< check::PreStmt<CallExpr>,
check::PreStmt<CXXDeleteExpr>,
check::PreObjCMessage,
check::PreCall > {
- mutable OwningPtr<BugType> BT_call_null;
- mutable OwningPtr<BugType> BT_call_undef;
- mutable OwningPtr<BugType> BT_cxx_call_null;
- mutable OwningPtr<BugType> BT_cxx_call_undef;
- mutable OwningPtr<BugType> BT_call_arg;
- mutable OwningPtr<BugType> BT_cxx_delete_undef;
- mutable OwningPtr<BugType> BT_msg_undef;
- mutable OwningPtr<BugType> BT_objc_prop_undef;
- mutable OwningPtr<BugType> BT_objc_subscript_undef;
- mutable OwningPtr<BugType> BT_msg_arg;
- mutable OwningPtr<BugType> BT_msg_ret;
- mutable OwningPtr<BugType> BT_call_few_args;
+ 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;
+
public:
+ ChecksFilter Filter;
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
@@ -52,10 +63,11 @@
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
private:
- static bool PreVisitProcessArg(CheckerContext &C, SVal V,
- SourceRange argRange, const Expr *argEx,
- bool IsFirstArgument, bool checkUninitFields,
- const CallEvent &Call, OwningPtr<BugType> &BT);
+ bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange,
+ const Expr *ArgEx, bool IsFirstArgument,
+ bool CheckUninitFields, const CallEvent &Call,
+ std::unique_ptr<BugType> &BT,
+ const ParmVarDecl *ParamDecl) const;
static void emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE);
void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg,
@@ -65,10 +77,14 @@
ProgramStateRef state,
const ObjCMethodCall &msg) const;
- static void LazyInit_BT(const char *desc, OwningPtr<BugType> &BT) {
+ void LazyInit_BT(const char *desc, std::unique_ptr<BugType> &BT) const {
if (!BT)
- BT.reset(new BuiltinBug(desc));
+ BT.reset(new BuiltinBug(this, desc));
}
+ bool uninitRefOrPointer(CheckerContext &C, const SVal &V,
+ const SourceRange &ArgRange,
+ const Expr *ArgEx, std::unique_ptr<BugType> &BT,
+ const ParmVarDecl *ParamDecl, const char *BD) const;
};
} // end anonymous namespace
@@ -113,30 +129,86 @@
}
}
+bool CallAndMessageChecker::uninitRefOrPointer(CheckerContext &C,
+ const SVal &V,
+ const SourceRange &ArgRange,
+ const Expr *ArgEx,
+ std::unique_ptr<BugType> &BT,
+ const ParmVarDecl *ParamDecl,
+ const char *BD) const {
+ 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).
+ StringRef Message;
+
+ if (ParamDecl->getType()->isPointerType()) {
+ Message = "Function call argument is a pointer to uninitialized value";
+ } else if (ParamDecl->getType()->isReferenceType()) {
+ Message = "Function call argument is an uninitialized value";
+ } else
+ return false;
+
+ if(!ParamDecl->getType()->getPointeeType().isConstQualified())
+ return false;
+
+ if (const MemRegion *SValMemRegion = V.getAsRegion()) {
+ const ProgramStateRef State = C.getState();
+ const SVal PSV = State->getSVal(SValMemRegion);
+ if (PSV.isUndef()) {
+ if (ExplodedNode *N = C.generateSink()) {
+ LazyInit_BT(BD, BT);
+ BugReport *R = new BugReport(*BT, Message, N);
+ R->addRange(ArgRange);
+ if (ArgEx) {
+ bugreporter::trackNullOrUndefValue(N, ArgEx, *R);
+ }
+ C.emitReport(R);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
- SVal V, SourceRange argRange,
- const Expr *argEx,
+ SVal V,
+ SourceRange ArgRange,
+ const Expr *ArgEx,
bool IsFirstArgument,
- bool checkUninitFields,
+ bool CheckUninitFields,
const CallEvent &Call,
- OwningPtr<BugType> &BT) {
+ std::unique_ptr<BugType> &BT,
+ const ParmVarDecl *ParamDecl
+ ) const {
+ const char *BD = "Uninitialized argument value";
+
+ if (uninitRefOrPointer(C, V, ArgRange, ArgEx, BT, ParamDecl, BD))
+ return true;
+
if (V.isUndef()) {
if (ExplodedNode *N = C.generateSink()) {
- LazyInit_BT("Uninitialized argument value", BT);
+ LazyInit_BT(BD, BT);
// Generate a report for this bug.
- StringRef Desc = describeUninitializedArgumentInCall(Call,
- IsFirstArgument);
+ StringRef Desc =
+ describeUninitializedArgumentInCall(Call, IsFirstArgument);
BugReport *R = new BugReport(*BT, Desc, N);
- R->addRange(argRange);
- if (argEx)
- bugreporter::trackNullOrUndefValue(N, argEx, *R);
+ R->addRange(ArgRange);
+ if (ArgEx)
+ bugreporter::trackNullOrUndefValue(N, ArgEx, *R);
C.emitReport(R);
}
return true;
}
- if (!checkUninitFields)
+ if (!CheckUninitFields)
return false;
if (Optional<nonloc::LazyCompoundVal> LV =
@@ -159,10 +231,9 @@
if (const RecordType *RT = T->getAsStructureType()) {
const RecordDecl *RD = RT->getDecl()->getDefinition();
assert(RD && "Referred record has no definition");
- for (RecordDecl::field_iterator I =
- RD->field_begin(), E = RD->field_end(); I!=E; ++I) {
- const FieldRegion *FR = MrMgr.getFieldRegion(*I, R);
- FieldChain.push_back(*I);
+ 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))
@@ -188,7 +259,7 @@
if (F.Find(D->getRegion())) {
if (ExplodedNode *N = C.generateSink()) {
- LazyInit_BT("Uninitialized argument value", BT);
+ LazyInit_BT(BD, BT);
SmallString<512> Str;
llvm::raw_svector_ostream os(Str);
os << "Passed-by-value struct argument contains uninitialized data";
@@ -211,7 +282,7 @@
// Generate a report for this bug.
BugReport *R = new BugReport(*BT, os.str(), N);
- R->addRange(argRange);
+ R->addRange(ArgRange);
// FIXME: enhance track back for uninitialized value for arbitrary
// memregions
@@ -234,20 +305,19 @@
if (L.isUndef()) {
if (!BT_call_undef)
- BT_call_undef.reset(new BuiltinBug("Called function pointer is an "
- "uninitalized pointer value"));
+ BT_call_undef.reset(new BuiltinBug(
+ this, "Called function pointer is an uninitalized pointer value"));
emitBadCall(BT_call_undef.get(), C, Callee);
return;
}
ProgramStateRef StNonNull, StNull;
- llvm::tie(StNonNull, StNull) =
- State->assume(L.castAs<DefinedOrUnknownSVal>());
+ std::tie(StNonNull, StNull) = State->assume(L.castAs<DefinedOrUnknownSVal>());
if (StNull && !StNonNull) {
if (!BT_call_null)
- BT_call_null.reset(
- new BuiltinBug("Called function pointer is null (null dereference)"));
+ BT_call_null.reset(new BuiltinBug(
+ this, "Called function pointer is null (null dereference)"));
emitBadCall(BT_call_null.get(), C, Callee);
return;
}
@@ -265,7 +335,8 @@
if (!N)
return;
if (!BT_cxx_delete_undef)
- BT_cxx_delete_undef.reset(new BuiltinBug("Uninitialized argument value"));
+ BT_cxx_delete_undef.reset(
+ new BuiltinBug(this, "Uninitialized argument value"));
if (DE->isArrayFormAsWritten())
Desc = "Argument to 'delete[]' is uninitialized";
else
@@ -289,20 +360,20 @@
SVal V = CC->getCXXThisVal();
if (V.isUndef()) {
if (!BT_cxx_call_undef)
- BT_cxx_call_undef.reset(new BuiltinBug("Called C++ object pointer is "
- "uninitialized"));
+ BT_cxx_call_undef.reset(
+ new BuiltinBug(this, "Called C++ object pointer is uninitialized"));
emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr());
return;
}
ProgramStateRef StNonNull, StNull;
- llvm::tie(StNonNull, StNull) =
+ std::tie(StNonNull, StNull) =
State->assume(V.castAs<DefinedOrUnknownSVal>());
if (StNull && !StNonNull) {
if (!BT_cxx_call_null)
- BT_cxx_call_null.reset(new BuiltinBug("Called C++ object pointer "
- "is null"));
+ BT_cxx_call_null.reset(
+ new BuiltinBug(this, "Called C++ object pointer is null"));
emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr());
return;
}
@@ -311,7 +382,8 @@
}
const Decl *D = Call.getDecl();
- if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) {
+ const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
+ if (FD) {
// If we have a declaration, we can make sure we pass enough parameters to
// the function.
unsigned Params = FD->getNumParams();
@@ -340,17 +412,21 @@
const bool checkUninitFields =
!(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody()));
- OwningPtr<BugType> *BT;
+ std::unique_ptr<BugType> *BT;
if (isa<ObjCMethodCall>(Call))
BT = &BT_msg_arg;
else
BT = &BT_call_arg;
- for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i)
+ for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) {
+ const ParmVarDecl *ParamDecl = NULL;
+ if(FD && i < FD->getNumParams())
+ ParamDecl = FD->getParamDecl(i);
if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i),
Call.getArgExpr(i), /*IsFirstArgument=*/i == 0,
- checkUninitFields, Call, *BT))
+ checkUninitFields, Call, *BT, ParamDecl))
return;
+ }
// If we make it here, record our assumptions about the callee.
C.addTransition(State);
@@ -365,22 +441,21 @@
switch (msg.getMessageKind()) {
case OCM_Message:
if (!BT_msg_undef)
- BT_msg_undef.reset(new BuiltinBug("Receiver in message expression "
+ BT_msg_undef.reset(new BuiltinBug(this,
+ "Receiver in message expression "
"is an uninitialized value"));
BT = BT_msg_undef.get();
break;
case OCM_PropertyAccess:
if (!BT_objc_prop_undef)
- BT_objc_prop_undef.reset(new BuiltinBug("Property access on an "
- "uninitialized object "
- "pointer"));
+ BT_objc_prop_undef.reset(new BuiltinBug(
+ this, "Property access on an uninitialized object pointer"));
BT = BT_objc_prop_undef.get();
break;
case OCM_Subscript:
if (!BT_objc_subscript_undef)
- BT_objc_subscript_undef.reset(new BuiltinBug("Subscript access on an "
- "uninitialized object "
- "pointer"));
+ BT_objc_subscript_undef.reset(new BuiltinBug(
+ this, "Subscript access on an uninitialized object pointer"));
BT = BT_objc_subscript_undef.get();
break;
}
@@ -402,7 +477,7 @@
ProgramStateRef state = C.getState();
ProgramStateRef notNilState, nilState;
- llvm::tie(notNilState, nilState) = state->assume(receiverVal);
+ std::tie(notNilState, nilState) = state->assume(receiverVal);
// Handle receiver must be nil.
if (nilState && !notNilState) {
@@ -418,7 +493,7 @@
if (!BT_msg_ret)
BT_msg_ret.reset(
- new BuiltinBug("Receiver in message expression is 'nil'"));
+ new BuiltinBug(this, "Receiver in message expression is 'nil'"));
const ObjCMessageExpr *ME = msg.getOriginExpr();
@@ -426,8 +501,9 @@
SmallString<200> buf;
llvm::raw_svector_ostream os(buf);
- os << "The receiver of message '" << ME->getSelector().getAsString()
- << "' is nil";
+ os << "The receiver of message '";
+ ME->getSelector().print(os);
+ os << "' is nil";
if (ResTy->isReferenceType()) {
os << ", which results in forming a null reference";
} else {
@@ -454,7 +530,7 @@
ProgramStateRef state,
const ObjCMethodCall &Msg) const {
ASTContext &Ctx = C.getASTContext();
- static SimpleProgramPointTag Tag("CallAndMessageChecker : NilReceiver");
+ static CheckerProgramPointTag Tag(this, "NilReceiver");
// Check the return type of the message expression. A message to nil will
// return different values depending on the return type and the architecture.
@@ -510,6 +586,13 @@
C.addTransition(state);
}
-void ento::registerCallAndMessageChecker(CheckerManager &mgr) {
- mgr.registerChecker<CallAndMessageChecker>();
-}
+#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)
diff --git a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
index 5e6e105..e9adf30 100644
--- a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
@@ -23,12 +23,71 @@
namespace {
class CastSizeChecker : public Checker< check::PreStmt<CastExpr> > {
- mutable OwningPtr<BuiltinBug> BT;
+ mutable std::unique_ptr<BuiltinBug> BT;
+
public:
void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
};
}
+/// Check if we are casting to a struct with a flexible array at the end.
+/// \code
+/// struct foo {
+/// size_t len;
+/// struct bar data[];
+/// };
+/// \endcode
+/// or
+/// \code
+/// struct foo {
+/// size_t len;
+/// struct bar data[0];
+/// }
+/// \endcode
+/// In these cases it is also valid to allocate size of struct foo + a multiple
+/// of struct bar.
+static bool evenFlexibleArraySize(ASTContext &Ctx, CharUnits RegionSize,
+ CharUnits TypeSize, QualType ToPointeeTy) {
+ const RecordType *RT = ToPointeeTy->getAs<RecordType>();
+ if (!RT)
+ return false;
+
+ const RecordDecl *RD = RT->getDecl();
+ RecordDecl::field_iterator Iter(RD->field_begin());
+ RecordDecl::field_iterator End(RD->field_end());
+ const FieldDecl *Last = 0;
+ for (; Iter != End; ++Iter)
+ Last = *Iter;
+ assert(Last && "empty structs should already be handled");
+
+ const Type *ElemType = Last->getType()->getArrayElementTypeNoTypeQual();
+ CharUnits FlexSize;
+ if (const ConstantArrayType *ArrayTy =
+ Ctx.getAsConstantArrayType(Last->getType())) {
+ FlexSize = Ctx.getTypeSizeInChars(ElemType);
+ if (ArrayTy->getSize() == 1 && TypeSize > FlexSize)
+ TypeSize -= FlexSize;
+ else if (ArrayTy->getSize() != 0)
+ return false;
+ } else if (RD->hasFlexibleArrayMember()) {
+ FlexSize = Ctx.getTypeSizeInChars(ElemType);
+ } else {
+ return false;
+ }
+
+ if (FlexSize.isZero())
+ return false;
+
+ CharUnits Left = RegionSize - TypeSize;
+ if (Left.isNegative())
+ return false;
+
+ if (Left % FlexSize == 0)
+ return true;
+
+ return false;
+}
+
void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
const Expr *E = CE->getSubExpr();
ASTContext &Ctx = C.getASTContext();
@@ -66,21 +125,23 @@
if (typeSize.isZero())
return;
- if (regionSize % typeSize != 0) {
- if (ExplodedNode *errorNode = C.generateSink()) {
- if (!BT)
- BT.reset(new BuiltinBug("Cast region with wrong size.",
- "Cast a region whose size is not a multiple of the"
- " destination type size."));
- BugReport *R = new BugReport(*BT, BT->getDescription(),
- errorNode);
- R->addRange(CE->getSourceRange());
- C.emitReport(R);
- }
+ if (regionSize % typeSize == 0)
+ return;
+
+ if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy))
+ return;
+
+ if (ExplodedNode *errorNode = C.generateSink()) {
+ if (!BT)
+ BT.reset(new BuiltinBug(this, "Cast region with wrong size.",
+ "Cast a region whose size is not a multiple"
+ " of the destination type size."));
+ BugReport *R = new BugReport(*BT, BT->getDescription(), errorNode);
+ R->addRange(CE->getSourceRange());
+ C.emitReport(R);
}
}
-
void ento::registerCastSizeChecker(CheckerManager &mgr) {
- mgr.registerChecker<CastSizeChecker>();
+ mgr.registerChecker<CastSizeChecker>();
}
diff --git a/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp
index 60348c7..d765315 100644
--- a/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp
@@ -24,7 +24,7 @@
namespace {
class CastToStructChecker : public Checker< check::PreStmt<CastExpr> > {
- mutable OwningPtr<BuiltinBug> BT;
+ mutable std::unique_ptr<BuiltinBug> BT;
public:
void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
@@ -58,10 +58,11 @@
if (!OrigPointeeTy->isRecordType()) {
if (ExplodedNode *N = C.addTransition()) {
if (!BT)
- BT.reset(new BuiltinBug("Cast from non-struct type to struct type",
- "Casting a non-structure type to a structure type "
- "and accessing a field can lead to memory access "
- "errors or data corruption."));
+ BT.reset(
+ new BuiltinBug(this, "Cast from non-struct type to struct type",
+ "Casting a non-structure type to a structure type "
+ "and accessing a field can lead to memory access "
+ "errors or data corruption."));
BugReport *R = new BugReport(*BT,BT->getDescription(), N);
R->addRange(CE->getSourceRange());
C.emitReport(R);
diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
index 3f9b3cc..827a10a 100644
--- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
@@ -97,8 +97,9 @@
return false;
}
-static void checkObjCDealloc(const ObjCImplementationDecl *D,
- const LangOptions& LOpts, BugReporter& BR) {
+static void checkObjCDealloc(const CheckerBase *Checker,
+ const ObjCImplementationDecl *D,
+ const LangOptions &LOpts, BugReporter &BR) {
assert (LOpts.getGC() != LangOptions::GCOnly);
@@ -112,15 +113,12 @@
bool containsPointerIvar = false;
- for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), E=ID->ivar_end();
- I!=E; ++I) {
-
- ObjCIvarDecl *ID = *I;
- QualType T = ID->getType();
+ for (const auto *Ivar : ID->ivars()) {
+ QualType T = Ivar->getType();
if (!T->isObjCObjectPointerType() ||
- ID->getAttr<IBOutletAttr>() || // Skip IBOutlets.
- ID->getAttr<IBOutletCollectionAttr>()) // Skip IBOutletCollections.
+ Ivar->hasAttr<IBOutletAttr>() || // Skip IBOutlets.
+ Ivar->hasAttr<IBOutletCollectionAttr>()) // Skip IBOutletCollections.
continue;
containsPointerIvar = true;
@@ -155,14 +153,12 @@
// Get the "dealloc" selector.
IdentifierInfo* II = &Ctx.Idents.get("dealloc");
Selector S = Ctx.Selectors.getSelector(0, &II);
- ObjCMethodDecl *MD = 0;
+ const ObjCMethodDecl *MD = 0;
// Scan the instance methods for "dealloc".
- for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(),
- E = D->instmeth_end(); I!=E; ++I) {
-
- if ((*I)->getSelector() == S) {
- MD = *I;
+ for (const auto *I : D->instance_methods()) {
+ if (I->getSelector() == S) {
+ MD = I;
break;
}
}
@@ -180,7 +176,7 @@
llvm::raw_string_ostream os(buf);
os << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method";
- BR.EmitBasicReport(D, name, categories::CoreFoundationObjectiveC,
+ BR.EmitBasicReport(D, Checker, name, categories::CoreFoundationObjectiveC,
os.str(), DLoc);
return;
}
@@ -198,7 +194,7 @@
<< "' does not send a 'dealloc' message to its super class"
" (missing [super dealloc])";
- BR.EmitBasicReport(MD, name, categories::CoreFoundationObjectiveC,
+ BR.EmitBasicReport(MD, Checker, name, categories::CoreFoundationObjectiveC,
os.str(), DLoc);
return;
}
@@ -212,9 +208,7 @@
// Scan for missing and extra releases of ivars used by implementations
// of synthesized properties
- for (ObjCImplementationDecl::propimpl_iterator I = D->propimpl_begin(),
- E = D->propimpl_end(); I!=E; ++I) {
-
+ for (const auto *I : D->property_impls()) {
// We can only check the synthesized properties
if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
continue;
@@ -262,10 +256,10 @@
}
PathDiagnosticLocation SDLoc =
- PathDiagnosticLocation::createBegin(*I, BR.getSourceManager());
+ PathDiagnosticLocation::createBegin(I, BR.getSourceManager());
- BR.EmitBasicReport(MD, name, categories::CoreFoundationObjectiveC,
- os.str(), SDLoc);
+ BR.EmitBasicReport(MD, Checker, name,
+ categories::CoreFoundationObjectiveC, os.str(), SDLoc);
}
}
}
@@ -282,7 +276,8 @@
BugReporter &BR) const {
if (mgr.getLangOpts().getGC() == LangOptions::GCOnly)
return;
- checkObjCDealloc(cast<ObjCImplementationDecl>(D), mgr.getLangOpts(), BR);
+ checkObjCDealloc(this, cast<ObjCImplementationDecl>(D), mgr.getLangOpts(),
+ BR);
}
};
}
diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
index 9cb1d2d..dc53602 100644
--- a/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
@@ -40,10 +40,11 @@
static void CompareReturnTypes(const ObjCMethodDecl *MethDerived,
const ObjCMethodDecl *MethAncestor,
BugReporter &BR, ASTContext &Ctx,
- const ObjCImplementationDecl *ID) {
+ const ObjCImplementationDecl *ID,
+ const CheckerBase *Checker) {
- QualType ResDerived = MethDerived->getResultType();
- QualType ResAncestor = MethAncestor->getResultType();
+ QualType ResDerived = MethDerived->getReturnType();
+ QualType ResAncestor = MethAncestor->getReturnType();
if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) {
std::string sbuf;
@@ -53,9 +54,9 @@
<< *MethDerived->getClassInterface()
<< "', which is derived from class '"
<< *MethAncestor->getClassInterface()
- << "', defines the instance method '"
- << MethDerived->getSelector().getAsString()
- << "' whose return type is '"
+ << "', defines the instance method '";
+ MethDerived->getSelector().print(os);
+ os << "' whose return type is '"
<< ResDerived.getAsString()
<< "'. A method with the same name (same selector) is also defined in "
"class '"
@@ -69,15 +70,15 @@
PathDiagnosticLocation::createBegin(MethDerived,
BR.getSourceManager());
- BR.EmitBasicReport(MethDerived,
- "Incompatible instance method return type",
- categories::CoreFoundationObjectiveC,
- os.str(), MethDLoc);
+ BR.EmitBasicReport(
+ MethDerived, Checker, "Incompatible instance method return type",
+ categories::CoreFoundationObjectiveC, os.str(), MethDLoc);
}
}
static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID,
- BugReporter& BR) {
+ BugReporter &BR,
+ const CheckerBase *Checker) {
const ObjCInterfaceDecl *D = ID->getClassInterface();
const ObjCInterfaceDecl *C = D->getSuperClass();
@@ -92,10 +93,7 @@
MapTy IMeths;
unsigned NumMethods = 0;
- for (ObjCImplementationDecl::instmeth_iterator I=ID->instmeth_begin(),
- E=ID->instmeth_end(); I!=E; ++I) {
-
- ObjCMethodDecl *M = *I;
+ for (auto *M : ID->instance_methods()) {
IMeths[M->getSelector()] = M;
++NumMethods;
}
@@ -103,10 +101,7 @@
// Now recurse the class hierarchy chain looking for methods with the
// same signatures.
while (C && NumMethods) {
- for (ObjCInterfaceDecl::instmeth_iterator I=C->instmeth_begin(),
- E=C->instmeth_end(); I!=E; ++I) {
-
- ObjCMethodDecl *M = *I;
+ for (const auto *M : C->instance_methods()) {
Selector S = M->getSelector();
MapTy::iterator MI = IMeths.find(S);
@@ -118,7 +113,7 @@
ObjCMethodDecl *MethDerived = MI->second;
MI->second = 0;
- CompareReturnTypes(MethDerived, M, BR, Ctx, ID);
+ CompareReturnTypes(MethDerived, M, BR, Ctx, ID, Checker);
}
C = C->getSuperClass();
@@ -135,7 +130,7 @@
public:
void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
BugReporter &BR) const {
- CheckObjCInstMethSignature(D, BR);
+ CheckObjCInstMethSignature(D, BR, this);
}
};
}
diff --git a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
index 415d3ec..5706364 100644
--- a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
@@ -46,8 +46,18 @@
DefaultBool check_vfork;
DefaultBool check_FloatLoopCounter;
DefaultBool check_UncheckedReturn;
+
+ CheckName checkName_gets;
+ CheckName checkName_getpw;
+ CheckName checkName_mktemp;
+ CheckName checkName_mkstemp;
+ CheckName checkName_strcpy;
+ CheckName checkName_rand;
+ CheckName checkName_vfork;
+ CheckName checkName_FloatLoopCounter;
+ CheckName checkName_UncheckedReturn;
};
-
+
class WalkAST : public StmtVisitor<WalkAST> {
BugReporter &BR;
AnalysisDeclContext* AC;
@@ -281,7 +291,7 @@
PathDiagnosticLocation FSLoc =
PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC);
- BR.EmitBasicReport(AC->getDecl(),
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
bugType, "Security", os.str(),
FSLoc, ranges);
}
@@ -302,11 +312,11 @@
return;
// Verify that the function takes a single argument.
- if (FPT->getNumArgs() != 1)
+ if (FPT->getNumParams() != 1)
return;
// Is the argument a 'char*'?
- const PointerType *PT = FPT->getArgType(0)->getAs<PointerType>();
+ const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
if (!PT)
return;
@@ -316,7 +326,7 @@
// Issue a warning.
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
- BR.EmitBasicReport(AC->getDecl(),
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
"Potential buffer overflow in call to 'gets'",
"Security",
"Call to function 'gets' is extremely insecure as it can "
@@ -338,15 +348,15 @@
return;
// Verify that the function takes two arguments.
- if (FPT->getNumArgs() != 2)
+ if (FPT->getNumParams() != 2)
return;
// Verify the first argument type is integer.
- if (!FPT->getArgType(0)->isIntegralOrUnscopedEnumerationType())
+ if (!FPT->getParamType(0)->isIntegralOrUnscopedEnumerationType())
return;
// Verify the second argument type is char*.
- const PointerType *PT = FPT->getArgType(1)->getAs<PointerType>();
+ const PointerType *PT = FPT->getParamType(1)->getAs<PointerType>();
if (!PT)
return;
@@ -356,7 +366,7 @@
// Issue a warning.
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
- BR.EmitBasicReport(AC->getDecl(),
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
"Potential buffer overflow in call to 'getpw'",
"Security",
"The getpw() function is dangerous as it may overflow the "
@@ -382,11 +392,11 @@
return;
// Verify that the function takes a single argument.
- if (FPT->getNumArgs() != 1)
+ if (FPT->getNumParams() != 1)
return;
// Verify that the argument is Pointer Type.
- const PointerType *PT = FPT->getArgType(0)->getAs<PointerType>();
+ const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
if (!PT)
return;
@@ -397,7 +407,7 @@
// Issue a waring.
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
- BR.EmitBasicReport(AC->getDecl(),
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
"Potential insecure temporary file in call 'mktemp'",
"Security",
"Call to function 'mktemp' is insecure as it always "
@@ -483,7 +493,7 @@
out << " used as a suffix";
}
out << ')';
- BR.EmitBasicReport(AC->getDecl(),
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
"Insecure temporary file creation", "Security",
out.str(), CELoc, strArg->getSourceRange());
}
@@ -504,7 +514,7 @@
// Issue a warning.
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
- BR.EmitBasicReport(AC->getDecl(),
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
"Potential insecure memory buffer bounds restriction in "
"call 'strcpy'",
"Security",
@@ -531,7 +541,7 @@
// Issue a warning.
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
- BR.EmitBasicReport(AC->getDecl(),
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
"Potential insecure memory buffer bounds restriction in "
"call 'strcat'",
"Security",
@@ -551,14 +561,14 @@
return false;
// Verify the function takes two arguments, three in the _chk version.
- int numArgs = FPT->getNumArgs();
+ int numArgs = FPT->getNumParams();
if (numArgs != 2 && numArgs != 3)
return false;
// Verify the type for both arguments.
for (int i = 0; i < 2; i++) {
// Verify that the arguments are pointers.
- const PointerType *PT = FPT->getArgType(i)->getAs<PointerType>();
+ const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
if (!PT)
return false;
@@ -584,17 +594,16 @@
if (!FTP)
return;
- if (FTP->getNumArgs() == 1) {
+ if (FTP->getNumParams() == 1) {
// Is the argument an 'unsigned short *'?
// (Actually any integer type is allowed.)
- const PointerType *PT = FTP->getArgType(0)->getAs<PointerType>();
+ const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>();
if (!PT)
return;
if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType())
return;
- }
- else if (FTP->getNumArgs() != 0)
+ } else if (FTP->getNumParams() != 0)
return;
// Issue a warning.
@@ -610,8 +619,9 @@
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
- BR.EmitBasicReport(AC->getDecl(), os1.str(), "Security", os2.str(),
- CELoc, CE->getCallee()->getSourceRange());
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
+ "Security", os2.str(), CELoc,
+ CE->getCallee()->getSourceRange());
}
//===----------------------------------------------------------------------===//
@@ -628,13 +638,13 @@
return;
// Verify that the function takes no argument.
- if (FTP->getNumArgs() != 0)
+ if (FTP->getNumParams() != 0)
return;
// Issue a warning.
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
- BR.EmitBasicReport(AC->getDecl(),
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
"'random' is not a secure random number generator",
"Security",
"The 'random' function produces a sequence of values that "
@@ -654,7 +664,7 @@
// All calls to vfork() are insecure, issue a warning.
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
- BR.EmitBasicReport(AC->getDecl(),
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
"Potential insecure implementation-specific behavior in "
"call 'vfork'",
"Security",
@@ -704,12 +714,12 @@
// Verify that the function takes one or two arguments (depending on
// the function).
- if (FTP->getNumArgs() != (identifierid < 4 ? 1 : 2))
+ if (FTP->getNumParams() != (identifierid < 4 ? 1 : 2))
return;
// The arguments must be integers.
- for (unsigned i = 0; i < FTP->getNumArgs(); i++)
- if (! FTP->getArgType(i)->isIntegralOrUnscopedEnumerationType())
+ for (unsigned i = 0; i < FTP->getNumParams(); i++)
+ if (!FTP->getParamType(i)->isIntegralOrUnscopedEnumerationType())
return;
// Issue a warning.
@@ -725,8 +735,9 @@
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
- BR.EmitBasicReport(AC->getDecl(), os1.str(), "Security", os2.str(),
- CELoc, CE->getCallee()->getSourceRange());
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
+ "Security", os2.str(), CELoc,
+ CE->getCallee()->getSourceRange());
}
//===----------------------------------------------------------------------===//
@@ -746,10 +757,13 @@
};
}
-#define REGISTER_CHECKER(name) \
-void ento::register##name(CheckerManager &mgr) {\
- mgr.registerChecker<SecuritySyntaxChecker>()->filter.check_##name = true;\
-}
+#define REGISTER_CHECKER(name) \
+ void ento::register##name(CheckerManager &mgr) { \
+ SecuritySyntaxChecker *checker = \
+ mgr.registerChecker<SecuritySyntaxChecker>(); \
+ checker->filter.check_##name = true; \
+ checker->filter.checkName_##name = mgr.getCurrentCheckName(); \
+ }
REGISTER_CHECKER(gets)
REGISTER_CHECKER(getpw)
diff --git a/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp b/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp
index 1207b67..5106b06 100644
--- a/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp
@@ -24,10 +24,12 @@
namespace {
class WalkAST : public StmtVisitor<WalkAST> {
BugReporter &BR;
+ const CheckerBase *Checker;
AnalysisDeclContext* AC;
public:
- WalkAST(BugReporter &br, AnalysisDeclContext* ac) : BR(br), AC(ac) {}
+ WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac)
+ : BR(br), Checker(checker), AC(ac) {}
void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E);
void VisitStmt(Stmt *S) { VisitChildren(S); }
void VisitChildren(Stmt *S);
@@ -62,7 +64,7 @@
PathDiagnosticLocation ELoc =
PathDiagnosticLocation::createBegin(E, BR.getSourceManager(), AC);
- BR.EmitBasicReport(AC->getDecl(),
+ BR.EmitBasicReport(AC->getDecl(), Checker,
"Potential unintended use of sizeof() on pointer type",
categories::LogicError,
"The code calls sizeof() on a pointer type. "
@@ -80,7 +82,7 @@
public:
void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
BugReporter &BR) const {
- WalkAST walker(BR, mgr.getAnalysisDeclContext(D));
+ WalkAST walker(BR, this, mgr.getAnalysisDeclContext(D));
walker.Visit(D->getBody());
}
};
diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td
index 862212d..a457b44 100644
--- a/lib/StaticAnalyzer/Checkers/Checkers.td
+++ b/lib/StaticAnalyzer/Checkers/Checkers.td
@@ -120,6 +120,10 @@
HelpText<"Warn about unintended use of sizeof() on pointer expressions">,
DescFile<"CheckSizeofPointer.cpp">;
+def CallAndMessageUnInitRefArg : Checker<"CallAndMessageUnInitRefArg">,
+ HelpText<"Check for logical errors for function calls and Objective-C message expressions (e.g., uninitialized arguments, null function pointers, and pointer to undefined variables)">,
+ DescFile<"CallAndMessageChecker.cpp">;
+
} // end "alpha.core"
//===----------------------------------------------------------------------===//
@@ -203,10 +207,6 @@
let ParentPackage = DeadCodeAlpha in {
-def IdempotentOperationChecker : Checker<"IdempotentOperations">,
- HelpText<"Warn about idempotent operations">,
- DescFile<"IdempotentOperationChecker.cpp">;
-
def UnreachableCodeChecker : Checker<"UnreachableCode">,
HelpText<"Check unreachable code">,
DescFile<"UnreachableCodeChecker.cpp">;
@@ -416,6 +416,10 @@
HelpText<"Model the APIs that are guaranteed to return a non-nil value">,
DescFile<"BasicObjCFoundationChecks.cpp">;
+def ObjCSuperCallChecker : Checker<"MissingSuperCall">,
+ HelpText<"Warn about Objective-C methods that lack a necessary call to super">,
+ DescFile<"ObjCMissingSuperCallChecker.cpp">;
+
def NSErrorChecker : Checker<"NSError">,
HelpText<"Check usage of NSError** parameters">,
DescFile<"NSErrorChecker.cpp">;
@@ -448,10 +452,6 @@
HelpText<"Check for direct assignments to instance variables in the methods annotated with objc_no_direct_instance_variable_assignment">,
DescFile<"DirectIvarAssignment.cpp">;
-def ObjCSuperCallChecker : Checker<"MissingSuperCall">,
- HelpText<"Warn about Objective-C methods that lack a necessary call to super">,
- DescFile<"ObjCMissingSuperCallChecker.cpp">;
-
} // end "alpha.osx.cocoa"
let ParentPackage = CoreFoundation in {
diff --git a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
index 9912965..628cf2c 100644
--- a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
@@ -41,7 +41,7 @@
class ChrootChecker : public Checker<eval::Call, check::PreStmt<CallExpr> > {
mutable IdentifierInfo *II_chroot, *II_chdir;
// This bug refers to possibly break out of a chroot() jail.
- mutable OwningPtr<BuiltinBug> BT_BreakJail;
+ mutable std::unique_ptr<BuiltinBug> BT_BreakJail;
public:
ChrootChecker() : II_chroot(0), II_chdir(0) {}
@@ -142,9 +142,9 @@
if (isRootChanged((intptr_t) *k))
if (ExplodedNode *N = C.addTransition()) {
if (!BT_BreakJail)
- BT_BreakJail.reset(new BuiltinBug("Break out of jail",
- "No call of chdir(\"/\") immediately "
- "after chroot"));
+ BT_BreakJail.reset(new BuiltinBug(
+ this, "Break out of jail", "No call of chdir(\"/\") immediately "
+ "after chroot"));
BugReport *R = new BugReport(*BT_BreakJail,
BT_BreakJail->getDescription(), N);
C.emitReport(R);
diff --git a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
index 9d855ce..6001a3c 100644
--- a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
@@ -124,21 +124,23 @@
const CFG &cfg;
ASTContext &Ctx;
BugReporter& BR;
+ const CheckerBase *Checker;
AnalysisDeclContext* AC;
ParentMap& Parents;
llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
- OwningPtr<ReachableCode> reachableCode;
+ std::unique_ptr<ReachableCode> reachableCode;
const CFGBlock *currentBlock;
- OwningPtr<llvm::DenseSet<const VarDecl *> > InEH;
+ std::unique_ptr<llvm::DenseSet<const VarDecl *>> InEH;
enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit };
public:
- DeadStoreObs(const CFG &cfg, ASTContext &ctx,
- BugReporter& br, AnalysisDeclContext* ac, ParentMap& parents,
- llvm::SmallPtrSet<const VarDecl*, 20> &escaped)
- : cfg(cfg), Ctx(ctx), BR(br), AC(ac), Parents(parents),
- Escaped(escaped), currentBlock(0) {}
+ DeadStoreObs(const CFG &cfg, ASTContext &ctx, BugReporter &br,
+ const CheckerBase *checker, AnalysisDeclContext *ac,
+ ParentMap &parents,
+ llvm::SmallPtrSet<const VarDecl *, 20> &escaped)
+ : cfg(cfg), Ctx(ctx), BR(br), Checker(checker), AC(ac), Parents(parents),
+ Escaped(escaped), currentBlock(0) {}
virtual ~DeadStoreObs() {}
@@ -199,7 +201,8 @@
return;
}
- BR.EmitBasicReport(AC->getDecl(), BugType, "Dead store", os.str(), L, R);
+ BR.EmitBasicReport(AC->getDecl(), Checker, BugType, "Dead store", os.str(),
+ L, R);
}
void CheckVarDecl(const VarDecl *VD, const Expr *Ex, const Expr *Val,
@@ -214,7 +217,8 @@
return;
if (!isLive(Live, VD) &&
- !(VD->getAttr<UnusedAttr>() || VD->getAttr<BlocksAttr>())) {
+ !(VD->hasAttr<UnusedAttr>() || VD->hasAttr<BlocksAttr>() ||
+ VD->hasAttr<ObjCPreciseLifetimeAttr>())) {
PathDiagnosticLocation ExLoc =
PathDiagnosticLocation::createBegin(Ex, BR.getSourceManager(), AC);
@@ -251,8 +255,8 @@
return false;
}
- virtual void observeStmt(const Stmt *S, const CFGBlock *block,
- const LiveVariables::LivenessValues &Live) {
+ void observeStmt(const Stmt *S, const CFGBlock *block,
+ const LiveVariables::LivenessValues &Live) override {
currentBlock = block;
@@ -309,10 +313,8 @@
else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S))
// Iterate through the decls. Warn if any initializers are complex
// expressions that are not live (never used).
- for (DeclStmt::const_decl_iterator DI=DS->decl_begin(), DE=DS->decl_end();
- DI != DE; ++DI) {
-
- VarDecl *V = dyn_cast<VarDecl>(*DI);
+ for (const auto *DI : DS->decls()) {
+ const auto *V = dyn_cast<VarDecl>(DI);
if (!V)
continue;
@@ -339,8 +341,10 @@
// A dead initialization is a variable that is dead after it
// is initialized. We don't flag warnings for those variables
- // marked 'unused'.
- if (!isLive(Live, V) && V->getAttr<UnusedAttr>() == 0) {
+ // marked 'unused' or 'objc_precise_lifetime'.
+ if (!isLive(Live, V) &&
+ !V->hasAttr<UnusedAttr>() &&
+ !V->hasAttr<ObjCPreciseLifetimeAttr>()) {
// Special case: check for initializations with constants.
//
// e.g. : int x = 0;
@@ -436,7 +440,7 @@
ParentMap &pmap = mgr.getParentMap(D);
FindEscaped FS;
cfg.VisitBlockStmts(FS);
- DeadStoreObs A(cfg, BR.getContext(), BR, AC, pmap, FS.Escaped);
+ DeadStoreObs A(cfg, BR.getContext(), BR, this, AC, pmap, FS.Escaped);
L->runOnAllBlocks(A);
}
}
diff --git a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
index a2c8d1f..51e7a3d 100644
--- a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
+++ b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
@@ -95,6 +95,11 @@
public:
void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
BugReporter &BR) const {
+ PrintingPolicy Policy(mgr.getLangOpts());
+ Policy.TerseOutput = true;
+ Policy.PolishForDeclaration = true;
+ D->print(llvm::errs(), Policy);
+
if (CFG *cfg = mgr.getCFG(D)) {
cfg->dump(mgr.getLangOpts(),
llvm::sys::Process::StandardErrHasColors());
diff --git a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
index 72d46c5..efdc213 100644
--- a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
@@ -29,8 +29,8 @@
: public Checker< check::Location,
check::Bind,
EventDispatcher<ImplicitNullDerefEvent> > {
- mutable OwningPtr<BuiltinBug> BT_null;
- mutable OwningPtr<BuiltinBug> BT_undef;
+ mutable std::unique_ptr<BuiltinBug> BT_null;
+ mutable std::unique_ptr<BuiltinBug> BT_undef;
void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C,
bool IsBind = false) const;
@@ -97,7 +97,7 @@
// We know that 'location' cannot be non-null. This is what
// we call an "explicit" null dereference.
if (!BT_null)
- BT_null.reset(new BuiltinBug("Dereference of null pointer"));
+ BT_null.reset(new BuiltinBug(this, "Dereference of null pointer"));
SmallString<100> buf;
llvm::raw_svector_ostream os(buf);
@@ -180,7 +180,8 @@
if (l.isUndef()) {
if (ExplodedNode *N = C.generateSink()) {
if (!BT_undef)
- BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value"));
+ BT_undef.reset(
+ new BuiltinBug(this, "Dereference of undefined pointer value"));
BugReport *report =
new BugReport(*BT_undef, BT_undef->getDescription(), N);
@@ -200,7 +201,7 @@
ProgramStateRef state = C.getState();
ProgramStateRef notNullState, nullState;
- llvm::tie(notNullState, nullState) = state->assume(location);
+ std::tie(notNullState, nullState) = state->assume(location);
// The explicit NULL case.
if (nullState) {
@@ -239,8 +240,7 @@
ProgramStateRef State = C.getState();
ProgramStateRef StNonNull, StNull;
- llvm::tie(StNonNull, StNull) =
- State->assume(V.castAs<DefinedOrUnknownSVal>());
+ std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
if (StNull) {
if (!StNonNull) {
diff --git a/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp b/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
index b43dc18..0bcebf6 100644
--- a/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
+++ b/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
@@ -63,13 +63,15 @@
const ObjCMethodDecl *MD;
const ObjCInterfaceDecl *InterfD;
BugReporter &BR;
+ const CheckerBase *Checker;
LocationOrAnalysisDeclContext DCtx;
public:
MethodCrawler(const IvarToPropertyMapTy &InMap, const ObjCMethodDecl *InMD,
- const ObjCInterfaceDecl *InID,
- BugReporter &InBR, AnalysisDeclContext *InDCtx)
- : IvarToPropMap(InMap), MD(InMD), InterfD(InID), BR(InBR), DCtx(InDCtx) {}
+ const ObjCInterfaceDecl *InID, BugReporter &InBR,
+ const CheckerBase *Checker, AnalysisDeclContext *InDCtx)
+ : IvarToPropMap(InMap), MD(InMD), InterfD(InID), BR(InBR),
+ Checker(Checker), DCtx(InDCtx) {}
void VisitStmt(const Stmt *S) { VisitChildren(S); }
@@ -122,10 +124,7 @@
IvarToPropertyMapTy IvarToPropMap;
// Find all properties for this class.
- for (ObjCInterfaceDecl::prop_iterator I = InterD->prop_begin(),
- E = InterD->prop_end(); I != E; ++I) {
- ObjCPropertyDecl *PD = *I;
-
+ for (const auto *PD : InterD->properties()) {
// Find the corresponding IVar.
const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterD,
Mgr.getASTContext());
@@ -140,10 +139,7 @@
if (IvarToPropMap.empty())
return;
- for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(),
- E = D->instmeth_end(); I != E; ++I) {
-
- ObjCMethodDecl *M = *I;
+ for (const auto *M : D->instance_methods()) {
AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M);
if ((*ShouldSkipMethod)(M))
@@ -152,20 +148,17 @@
const Stmt *Body = M->getBody();
assert(Body);
- MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, DCtx);
+ MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, this,
+ DCtx);
MC.VisitStmt(Body);
}
}
static bool isAnnotatedToAllowDirectAssignment(const Decl *D) {
- for (specific_attr_iterator<AnnotateAttr>
- AI = D->specific_attr_begin<AnnotateAttr>(),
- AE = D->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) {
- const AnnotateAttr *Ann = *AI;
+ for (const auto *Ann : D->specific_attrs<AnnotateAttr>())
if (Ann->getAnnotation() ==
"objc_allow_direct_instance_variable_assignment")
return true;
- }
return false;
}
@@ -204,13 +197,11 @@
if (GetterMethod && GetterMethod->getCanonicalDecl() == MD)
return;
- BR.EmitBasicReport(MD,
- "Property access",
- categories::CoreFoundationObjectiveC,
+ BR.EmitBasicReport(
+ MD, Checker, "Property access", categories::CoreFoundationObjectiveC,
"Direct assignment to an instance variable backing a property; "
- "use the setter instead", PathDiagnosticLocation(IvarRef,
- BR.getSourceManager(),
- DCtx));
+ "use the setter instead",
+ PathDiagnosticLocation(IvarRef, BR.getSourceManager(), DCtx));
}
}
}
@@ -225,14 +216,9 @@
// Register the checker that checks for direct accesses in functions annotated
// with __attribute__((annotate("objc_no_direct_instance_variable_assignment"))).
static bool AttrFilter(const ObjCMethodDecl *M) {
- for (specific_attr_iterator<AnnotateAttr>
- AI = M->specific_attr_begin<AnnotateAttr>(),
- AE = M->specific_attr_end<AnnotateAttr>();
- AI != AE; ++AI) {
- const AnnotateAttr *Ann = *AI;
+ for (const auto *Ann : M->specific_attrs<AnnotateAttr>())
if (Ann->getAnnotation() == "objc_no_direct_instance_variable_assignment")
return false;
- }
return true;
}
diff --git a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
index 93daf94..e060c36 100644
--- a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
@@ -23,7 +23,7 @@
namespace {
class DivZeroChecker : public Checker< check::PreStmt<BinaryOperator> > {
- mutable OwningPtr<BuiltinBug> BT;
+ mutable std::unique_ptr<BuiltinBug> BT;
void reportBug(const char *Msg,
ProgramStateRef StateZero,
CheckerContext &C) const ;
@@ -37,7 +37,7 @@
CheckerContext &C) const {
if (ExplodedNode *N = C.generateSink(StateZero)) {
if (!BT)
- BT.reset(new BuiltinBug("Division by zero"));
+ BT.reset(new BuiltinBug(this, "Division by zero"));
BugReport *R = new BugReport(*BT, Msg, N);
bugreporter::trackNullOrUndefValue(N, bugreporter::GetDenomExpr(N), *R);
@@ -68,7 +68,7 @@
// Check for divide by zero.
ConstraintManager &CM = C.getConstraintManager();
ProgramStateRef stateNotZero, stateZero;
- llvm::tie(stateNotZero, stateZero) = CM.assumeDual(C.getState(), *DV);
+ std::tie(stateNotZero, stateZero) = CM.assumeDual(C.getState(), *DV);
if (!stateNotZero) {
assert(stateZero);
diff --git a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
index 3ed2435..9a0fa09 100644
--- a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
@@ -18,7 +18,7 @@
namespace {
class ExprInspectionChecker : public Checker< eval::Call > {
- mutable OwningPtr<BugType> BT;
+ mutable std::unique_ptr<BugType> BT;
void analyzerEval(const CallExpr *CE, CheckerContext &C) const;
void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const;
@@ -68,7 +68,7 @@
return "UNDEFINED";
ProgramStateRef StTrue, StFalse;
- llvm::tie(StTrue, StFalse) =
+ std::tie(StTrue, StFalse) =
State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>());
if (StTrue) {
@@ -95,7 +95,7 @@
return;
if (!BT)
- BT.reset(new BugType("Checking analyzer assumptions", "debug"));
+ BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N);
C.emitReport(R);
@@ -106,7 +106,7 @@
ExplodedNode *N = C.getPredecessor();
if (!BT)
- BT.reset(new BugType("Checking analyzer assumptions", "debug"));
+ BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
BugReport *R = new BugReport(*BT, "REACHABLE", N);
C.emitReport(R);
@@ -126,7 +126,7 @@
return;
if (!BT)
- BT.reset(new BugType("Checking analyzer assumptions", "debug"));
+ BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N);
C.emitReport(R);
diff --git a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
index 085a991..60bb036 100644
--- a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
@@ -25,7 +25,7 @@
namespace {
class FixedAddressChecker
: public Checker< check::PreStmt<BinaryOperator> > {
- mutable OwningPtr<BuiltinBug> BT;
+ mutable std::unique_ptr<BuiltinBug> BT;
public:
void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
@@ -52,10 +52,11 @@
if (ExplodedNode *N = C.addTransition()) {
if (!BT)
- BT.reset(new BuiltinBug("Use fixed address",
- "Using a fixed address is not portable because that "
- "address will probably not be valid in all "
- "environments or platforms."));
+ BT.reset(
+ new BuiltinBug(this, "Use fixed address",
+ "Using a fixed address is not portable because that "
+ "address will probably not be valid in all "
+ "environments or platforms."));
BugReport *R = new BugReport(*BT, BT->getDescription(), N);
R->addRange(B->getRHS()->getSourceRange());
C.emitReport(R);
diff --git a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
index 1dc60c6..93bc12c 100644
--- a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
@@ -34,7 +34,6 @@
static void *getTag() { static int Tag; return &Tag; }
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
- void checkPostStmt(const DeclRefExpr *DRE, CheckerContext &C) const;
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
@@ -43,10 +42,10 @@
/// Denotes the return vale.
static const unsigned ReturnValueIndex = UINT_MAX - 1;
- mutable OwningPtr<BugType> BT;
+ mutable std::unique_ptr<BugType> BT;
inline void initBugType() const {
if (!BT)
- BT.reset(new BugType("Use of Untrusted Data", "Untrusted Data"));
+ BT.reset(new BugType(this, "Use of Untrusted Data", "Untrusted Data"));
}
/// \brief Catch taint related bugs. Check if tainted data is passed to a
@@ -613,11 +612,7 @@
const FunctionDecl *FDecl = C.getCalleeDecl(CE);
if (!FDecl)
return false;
- for (specific_attr_iterator<FormatAttr>
- i = FDecl->specific_attr_begin<FormatAttr>(),
- e = FDecl->specific_attr_end<FormatAttr>(); i != e ; ++i) {
-
- const FormatAttr *Format = *i;
+ for (const auto *Format : FDecl->specific_attrs<FormatAttr>()) {
ArgNum = Format->getFormatIdx() - 1;
if ((Format->getType()->getName() == "printf") &&
CE->getNumArgs() > ArgNum)
diff --git a/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp
deleted file mode 100644
index 4997f8d..0000000
--- a/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp
+++ /dev/null
@@ -1,737 +0,0 @@
-//==- IdempotentOperationChecker.cpp - Idempotent Operations ----*- C++ -*-==//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines a set of path-sensitive checks for idempotent and/or
-// tautological operations. Each potential operation is checked along all paths
-// to see if every path results in a pointless operation.
-// +-------------------------------------------+
-// |Table of idempotent/tautological operations|
-// +-------------------------------------------+
-//+--------------------------------------------------------------------------+
-//|Operator | x op x | x op 1 | 1 op x | x op 0 | 0 op x | x op ~0 | ~0 op x |
-//+--------------------------------------------------------------------------+
-// +, += | | | | x | x | |
-// -, -= | | | | x | -x | |
-// *, *= | | x | x | 0 | 0 | |
-// /, /= | 1 | x | | N/A | 0 | |
-// &, &= | x | | | 0 | 0 | x | x
-// |, |= | x | | | x | x | ~0 | ~0
-// ^, ^= | 0 | | | x | x | |
-// <<, <<= | | | | x | 0 | |
-// >>, >>= | | | | x | 0 | |
-// || | x | 1 | 1 | x | x | 1 | 1
-// && | x | x | x | 0 | 0 | x | x
-// = | x | | | | | |
-// == | 1 | | | | | |
-// >= | 1 | | | | | |
-// <= | 1 | | | | | |
-// > | 0 | | | | | |
-// < | 0 | | | | | |
-// != | 0 | | | | | |
-//===----------------------------------------------------------------------===//
-//
-// Things TODO:
-// - Improved error messages
-// - Handle mixed assumptions (which assumptions can belong together?)
-// - Finer grained false positive control (levels)
-// - Handling ~0 values
-
-#include "ClangSACheckers.h"
-#include "clang/AST/Stmt.h"
-#include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
-#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h"
-#include "clang/Analysis/CFGStmtMap.h"
-#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.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/CheckerContext.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
-#include "llvm/ADT/BitVector.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/SmallSet.h"
-#include "llvm/ADT/SmallString.h"
-#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/raw_ostream.h"
-
-using namespace clang;
-using namespace ento;
-
-namespace {
-class IdempotentOperationChecker
- : public Checker<check::PreStmt<BinaryOperator>,
- check::PostStmt<BinaryOperator>,
- check::EndAnalysis> {
-public:
- void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
- void checkPostStmt(const BinaryOperator *B, CheckerContext &C) const;
- void checkEndAnalysis(ExplodedGraph &G, BugReporter &B,ExprEngine &Eng) const;
-
-private:
- // Our assumption about a particular operation.
- enum Assumption { Possible = 0, Impossible, Equal, LHSis1, RHSis1, LHSis0,
- RHSis0 };
-
- static void UpdateAssumption(Assumption &A, const Assumption &New);
-
- // False positive reduction methods
- static bool isSelfAssign(const Expr *LHS, const Expr *RHS);
- static bool isUnused(const Expr *E, AnalysisDeclContext *AC);
- static bool isTruncationExtensionAssignment(const Expr *LHS,
- const Expr *RHS);
- static bool pathWasCompletelyAnalyzed(AnalysisDeclContext *AC,
- const CFGBlock *CB,
- const CoreEngine &CE);
- static bool CanVary(const Expr *Ex,
- AnalysisDeclContext *AC);
- static bool isConstantOrPseudoConstant(const DeclRefExpr *DR,
- AnalysisDeclContext *AC);
- static bool containsNonLocalVarDecl(const Stmt *S);
-
- // Hash table and related data structures
- struct BinaryOperatorData {
- BinaryOperatorData() : assumption(Possible) {}
-
- Assumption assumption;
- ExplodedNodeSet explodedNodes; // Set of ExplodedNodes that refer to a
- // BinaryOperator
- };
- typedef llvm::DenseMap<const BinaryOperator *, BinaryOperatorData>
- AssumptionMap;
- mutable AssumptionMap hash;
- mutable OwningPtr<BugType> BT;
-};
-}
-
-void IdempotentOperationChecker::checkPreStmt(const BinaryOperator *B,
- CheckerContext &C) const {
- // Find or create an entry in the hash for this BinaryOperator instance.
- // If we haven't done a lookup before, it will get default initialized to
- // 'Possible'. At this stage we do not store the ExplodedNode, as it has not
- // been created yet.
- BinaryOperatorData &Data = hash[B];
- Assumption &A = Data.assumption;
- AnalysisDeclContext *AC = C.getCurrentAnalysisDeclContext();
-
- // If we already have visited this node on a path that does not contain an
- // idempotent operation, return immediately.
- if (A == Impossible)
- return;
-
- // Retrieve both sides of the operator and determine if they can vary (which
- // may mean this is a false positive.
- const Expr *LHS = B->getLHS();
- const Expr *RHS = B->getRHS();
-
- // At this stage we can calculate whether each side contains a false positive
- // that applies to all operators. We only need to calculate this the first
- // time.
- bool LHSContainsFalsePositive = false, RHSContainsFalsePositive = false;
- if (A == Possible) {
- // An expression contains a false positive if it can't vary, or if it
- // contains a known false positive VarDecl.
- LHSContainsFalsePositive = !CanVary(LHS, AC)
- || containsNonLocalVarDecl(LHS);
- RHSContainsFalsePositive = !CanVary(RHS, AC)
- || containsNonLocalVarDecl(RHS);
- }
-
- ProgramStateRef state = C.getState();
- const LocationContext *LCtx = C.getLocationContext();
- SVal LHSVal = state->getSVal(LHS, LCtx);
- SVal RHSVal = state->getSVal(RHS, LCtx);
-
- // If either value is unknown, we can't be 100% sure of all paths.
- if (LHSVal.isUnknownOrUndef() || RHSVal.isUnknownOrUndef()) {
- A = Impossible;
- return;
- }
- BinaryOperator::Opcode Op = B->getOpcode();
-
- // Dereference the LHS SVal if this is an assign operation
- switch (Op) {
- default:
- break;
-
- // Fall through intentional
- case BO_AddAssign:
- case BO_SubAssign:
- case BO_MulAssign:
- case BO_DivAssign:
- case BO_AndAssign:
- case BO_OrAssign:
- case BO_XorAssign:
- case BO_ShlAssign:
- case BO_ShrAssign:
- case BO_Assign:
- // Assign statements have one extra level of indirection
- if (!LHSVal.getAs<Loc>()) {
- A = Impossible;
- return;
- }
- LHSVal = state->getSVal(LHSVal.castAs<Loc>(), LHS->getType());
- }
-
-
- // We now check for various cases which result in an idempotent operation.
-
- // x op x
- switch (Op) {
- default:
- break; // We don't care about any other operators.
-
- // Fall through intentional
- case BO_Assign:
- // x Assign x can be used to silence unused variable warnings intentionally.
- // If this is a self assignment and the variable is referenced elsewhere,
- // and the assignment is not a truncation or extension, then it is a false
- // positive.
- if (isSelfAssign(LHS, RHS)) {
- if (!isUnused(LHS, AC) && !isTruncationExtensionAssignment(LHS, RHS)) {
- UpdateAssumption(A, Equal);
- return;
- }
- else {
- A = Impossible;
- return;
- }
- }
-
- case BO_SubAssign:
- case BO_DivAssign:
- case BO_AndAssign:
- case BO_OrAssign:
- case BO_XorAssign:
- case BO_Sub:
- case BO_Div:
- case BO_And:
- case BO_Or:
- case BO_Xor:
- case BO_LOr:
- case BO_LAnd:
- case BO_EQ:
- case BO_NE:
- if (LHSVal != RHSVal || LHSContainsFalsePositive
- || RHSContainsFalsePositive)
- break;
- UpdateAssumption(A, Equal);
- return;
- }
-
- // x op 1
- switch (Op) {
- default:
- break; // We don't care about any other operators.
-
- // Fall through intentional
- case BO_MulAssign:
- case BO_DivAssign:
- case BO_Mul:
- case BO_Div:
- case BO_LOr:
- case BO_LAnd:
- if (!RHSVal.isConstant(1) || RHSContainsFalsePositive)
- break;
- UpdateAssumption(A, RHSis1);
- return;
- }
-
- // 1 op x
- switch (Op) {
- default:
- break; // We don't care about any other operators.
-
- // Fall through intentional
- case BO_MulAssign:
- case BO_Mul:
- case BO_LOr:
- case BO_LAnd:
- if (!LHSVal.isConstant(1) || LHSContainsFalsePositive)
- break;
- UpdateAssumption(A, LHSis1);
- return;
- }
-
- // x op 0
- switch (Op) {
- default:
- break; // We don't care about any other operators.
-
- // Fall through intentional
- case BO_AddAssign:
- case BO_SubAssign:
- case BO_MulAssign:
- case BO_AndAssign:
- case BO_OrAssign:
- case BO_XorAssign:
- case BO_Add:
- case BO_Sub:
- case BO_Mul:
- case BO_And:
- case BO_Or:
- case BO_Xor:
- case BO_Shl:
- case BO_Shr:
- case BO_LOr:
- case BO_LAnd:
- if (!RHSVal.isConstant(0) || RHSContainsFalsePositive)
- break;
- UpdateAssumption(A, RHSis0);
- return;
- }
-
- // 0 op x
- switch (Op) {
- default:
- break; // We don't care about any other operators.
-
- // Fall through intentional
- //case BO_AddAssign: // Common false positive
- case BO_SubAssign: // Check only if unsigned
- case BO_MulAssign:
- case BO_DivAssign:
- case BO_AndAssign:
- //case BO_OrAssign: // Common false positive
- //case BO_XorAssign: // Common false positive
- case BO_ShlAssign:
- case BO_ShrAssign:
- case BO_Add:
- case BO_Sub:
- case BO_Mul:
- case BO_Div:
- case BO_And:
- case BO_Or:
- case BO_Xor:
- case BO_Shl:
- case BO_Shr:
- case BO_LOr:
- case BO_LAnd:
- if (!LHSVal.isConstant(0) || LHSContainsFalsePositive)
- break;
- UpdateAssumption(A, LHSis0);
- return;
- }
-
- // If we get to this point, there has been a valid use of this operation.
- A = Impossible;
-}
-
-// At the post visit stage, the predecessor ExplodedNode will be the
-// BinaryOperator that was just created. We use this hook to collect the
-// ExplodedNode.
-void IdempotentOperationChecker::checkPostStmt(const BinaryOperator *B,
- CheckerContext &C) const {
- // Add the ExplodedNode we just visited
- BinaryOperatorData &Data = hash[B];
-
- const Stmt *predStmt =
- C.getPredecessor()->getLocation().castAs<StmtPoint>().getStmt();
-
- // Ignore implicit calls to setters.
- if (!isa<BinaryOperator>(predStmt))
- return;
-
- Data.explodedNodes.Add(C.getPredecessor());
-}
-
-void IdempotentOperationChecker::checkEndAnalysis(ExplodedGraph &G,
- BugReporter &BR,
- ExprEngine &Eng) const {
- if (!BT)
- BT.reset(new BugType("Idempotent operation", "Dead code"));
-
- // Iterate over the hash to see if we have any paths with definite
- // idempotent operations.
- for (AssumptionMap::const_iterator i = hash.begin(); i != hash.end(); ++i) {
- // Unpack the hash contents
- const BinaryOperatorData &Data = i->second;
- const Assumption &A = Data.assumption;
- const ExplodedNodeSet &ES = Data.explodedNodes;
-
- // If there are no nodes accosted with the expression, nothing to report.
- // FIXME: This is possible because the checker does part of processing in
- // checkPreStmt and part in checkPostStmt.
- if (ES.begin() == ES.end())
- continue;
-
- const BinaryOperator *B = i->first;
-
- if (A == Impossible)
- continue;
-
- // If the analyzer did not finish, check to see if we can still emit this
- // warning
- if (Eng.hasWorkRemaining()) {
- // If we can trace back
- AnalysisDeclContext *AC = (*ES.begin())->getLocationContext()
- ->getAnalysisDeclContext();
- if (!pathWasCompletelyAnalyzed(AC,
- AC->getCFGStmtMap()->getBlock(B),
- Eng.getCoreEngine()))
- continue;
- }
-
- // Select the error message and SourceRanges to report.
- SmallString<128> buf;
- llvm::raw_svector_ostream os(buf);
- bool LHSRelevant = false, RHSRelevant = false;
- switch (A) {
- case Equal:
- LHSRelevant = true;
- RHSRelevant = true;
- if (B->getOpcode() == BO_Assign)
- os << "Assigned value is always the same as the existing value";
- else
- os << "Both operands to '" << B->getOpcodeStr()
- << "' always have the same value";
- break;
- case LHSis1:
- LHSRelevant = true;
- os << "The left operand to '" << B->getOpcodeStr() << "' is always 1";
- break;
- case RHSis1:
- RHSRelevant = true;
- os << "The right operand to '" << B->getOpcodeStr() << "' is always 1";
- break;
- case LHSis0:
- LHSRelevant = true;
- os << "The left operand to '" << B->getOpcodeStr() << "' is always 0";
- break;
- case RHSis0:
- RHSRelevant = true;
- os << "The right operand to '" << B->getOpcodeStr() << "' is always 0";
- break;
- case Possible:
- llvm_unreachable("Operation was never marked with an assumption");
- case Impossible:
- llvm_unreachable(0);
- }
-
- // Add a report for each ExplodedNode
- for (ExplodedNodeSet::iterator I = ES.begin(), E = ES.end(); I != E; ++I) {
- BugReport *report = new BugReport(*BT, os.str(), *I);
-
- // Add source ranges and visitor hooks
- if (LHSRelevant) {
- const Expr *LHS = i->first->getLHS();
- report->addRange(LHS->getSourceRange());
- FindLastStoreBRVisitor::registerStatementVarDecls(*report, LHS, false);
- }
- if (RHSRelevant) {
- const Expr *RHS = i->first->getRHS();
- report->addRange(i->first->getRHS()->getSourceRange());
- FindLastStoreBRVisitor::registerStatementVarDecls(*report, RHS, false);
- }
-
- BR.emitReport(report);
- }
- }
-
- hash.clear();
-}
-
-// Updates the current assumption given the new assumption
-inline void IdempotentOperationChecker::UpdateAssumption(Assumption &A,
- const Assumption &New) {
-// If the assumption is the same, there is nothing to do
- if (A == New)
- return;
-
- switch (A) {
- // If we don't currently have an assumption, set it
- case Possible:
- A = New;
- return;
-
- // If we have determined that a valid state happened, ignore the new
- // assumption.
- case Impossible:
- return;
-
- // Any other case means that we had a different assumption last time. We don't
- // currently support mixing assumptions for diagnostic reasons, so we set
- // our assumption to be impossible.
- default:
- A = Impossible;
- return;
- }
-}
-
-// Check for a statement where a variable is self assigned to possibly avoid an
-// unused variable warning.
-bool IdempotentOperationChecker::isSelfAssign(const Expr *LHS, const Expr *RHS) {
- LHS = LHS->IgnoreParenCasts();
- RHS = RHS->IgnoreParenCasts();
-
- const DeclRefExpr *LHS_DR = dyn_cast<DeclRefExpr>(LHS);
- if (!LHS_DR)
- return false;
-
- const VarDecl *VD = dyn_cast<VarDecl>(LHS_DR->getDecl());
- if (!VD)
- return false;
-
- const DeclRefExpr *RHS_DR = dyn_cast<DeclRefExpr>(RHS);
- if (!RHS_DR)
- return false;
-
- if (VD != RHS_DR->getDecl())
- return false;
-
- return true;
-}
-
-// Returns true if the Expr points to a VarDecl that is not read anywhere
-// outside of self-assignments.
-bool IdempotentOperationChecker::isUnused(const Expr *E,
- AnalysisDeclContext *AC) {
- if (!E)
- return false;
-
- const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts());
- if (!DR)
- return false;
-
- const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
- if (!VD)
- return false;
-
- if (AC->getPseudoConstantAnalysis()->wasReferenced(VD))
- return false;
-
- return true;
-}
-
-// Check for self casts truncating/extending a variable
-bool IdempotentOperationChecker::isTruncationExtensionAssignment(
- const Expr *LHS,
- const Expr *RHS) {
-
- const DeclRefExpr *LHS_DR = dyn_cast<DeclRefExpr>(LHS->IgnoreParenCasts());
- if (!LHS_DR)
- return false;
-
- const VarDecl *VD = dyn_cast<VarDecl>(LHS_DR->getDecl());
- if (!VD)
- return false;
-
- const DeclRefExpr *RHS_DR = dyn_cast<DeclRefExpr>(RHS->IgnoreParenCasts());
- if (!RHS_DR)
- return false;
-
- if (VD != RHS_DR->getDecl())
- return false;
-
- return dyn_cast<DeclRefExpr>(RHS->IgnoreParenLValueCasts()) == NULL;
-}
-
-// Returns false if a path to this block was not completely analyzed, or true
-// otherwise.
-bool
-IdempotentOperationChecker::pathWasCompletelyAnalyzed(AnalysisDeclContext *AC,
- const CFGBlock *CB,
- const CoreEngine &CE) {
-
- CFGReverseBlockReachabilityAnalysis *CRA = AC->getCFGReachablityAnalysis();
-
- // Test for reachability from any aborted blocks to this block
- typedef CoreEngine::BlocksExhausted::const_iterator ExhaustedIterator;
- for (ExhaustedIterator I = CE.blocks_exhausted_begin(),
- E = CE.blocks_exhausted_end(); I != E; ++I) {
- const BlockEdge &BE = I->first;
-
- // The destination block on the BlockEdge is the first block that was not
- // analyzed. If we can reach this block from the aborted block, then this
- // block was not completely analyzed.
- //
- // Also explicitly check if the current block is the destination block.
- // While technically reachable, it means we aborted the analysis on
- // a path that included that block.
- const CFGBlock *destBlock = BE.getDst();
- if (destBlock == CB || CRA->isReachable(destBlock, CB))
- return false;
- }
-
- // Test for reachability from blocks we just gave up on.
- typedef CoreEngine::BlocksAborted::const_iterator AbortedIterator;
- for (AbortedIterator I = CE.blocks_aborted_begin(),
- E = CE.blocks_aborted_end(); I != E; ++I) {
- const CFGBlock *destBlock = I->first;
- if (destBlock == CB || CRA->isReachable(destBlock, CB))
- return false;
- }
-
- // For the items still on the worklist, see if they are in blocks that
- // can eventually reach 'CB'.
- class VisitWL : public WorkList::Visitor {
- const CFGStmtMap *CBM;
- const CFGBlock *TargetBlock;
- CFGReverseBlockReachabilityAnalysis &CRA;
- public:
- VisitWL(const CFGStmtMap *cbm, const CFGBlock *targetBlock,
- CFGReverseBlockReachabilityAnalysis &cra)
- : CBM(cbm), TargetBlock(targetBlock), CRA(cra) {}
- virtual bool visit(const WorkListUnit &U) {
- ProgramPoint P = U.getNode()->getLocation();
- const CFGBlock *B = 0;
- if (Optional<StmtPoint> SP = P.getAs<StmtPoint>()) {
- B = CBM->getBlock(SP->getStmt());
- } else if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
- B = BE->getDst();
- } else if (Optional<BlockEntrance> BEnt = P.getAs<BlockEntrance>()) {
- B = BEnt->getBlock();
- } else if (Optional<BlockExit> BExit = P.getAs<BlockExit>()) {
- B = BExit->getBlock();
- }
- if (!B)
- return true;
-
- return B == TargetBlock || CRA.isReachable(B, TargetBlock);
- }
- };
- VisitWL visitWL(AC->getCFGStmtMap(), CB, *CRA);
- // Were there any items in the worklist that could potentially reach
- // this block?
- if (CE.getWorkList()->visitItemsInWorkList(visitWL))
- return false;
-
- // Verify that this block is reachable from the entry block
- if (!CRA->isReachable(&AC->getCFG()->getEntry(), CB))
- return false;
-
- // If we get to this point, there is no connection to the entry block or an
- // aborted block. This path is unreachable and we can report the error.
- return true;
-}
-
-// Recursive function that determines whether an expression contains any element
-// that varies. This could be due to a compile-time constant like sizeof. An
-// expression may also involve a variable that behaves like a constant. The
-// function returns true if the expression varies, and false otherwise.
-bool IdempotentOperationChecker::CanVary(const Expr *Ex,
- AnalysisDeclContext *AC) {
- // Parentheses and casts are irrelevant here
- Ex = Ex->IgnoreParenCasts();
-
- if (Ex->getLocStart().isMacroID())
- return false;
-
- switch (Ex->getStmtClass()) {
- // Trivially true cases
- case Stmt::ArraySubscriptExprClass:
- case Stmt::MemberExprClass:
- case Stmt::StmtExprClass:
- case Stmt::CallExprClass:
- case Stmt::VAArgExprClass:
- case Stmt::ShuffleVectorExprClass:
- return true;
- default:
- return true;
-
- // Trivially false cases
- case Stmt::IntegerLiteralClass:
- case Stmt::CharacterLiteralClass:
- case Stmt::FloatingLiteralClass:
- case Stmt::PredefinedExprClass:
- case Stmt::ImaginaryLiteralClass:
- case Stmt::StringLiteralClass:
- case Stmt::OffsetOfExprClass:
- case Stmt::CompoundLiteralExprClass:
- case Stmt::AddrLabelExprClass:
- case Stmt::BinaryTypeTraitExprClass:
- case Stmt::GNUNullExprClass:
- case Stmt::InitListExprClass:
- case Stmt::DesignatedInitExprClass:
- case Stmt::BlockExprClass:
- return false;
-
- // Cases requiring custom logic
- case Stmt::UnaryExprOrTypeTraitExprClass: {
- const UnaryExprOrTypeTraitExpr *SE =
- cast<const UnaryExprOrTypeTraitExpr>(Ex);
- if (SE->getKind() != UETT_SizeOf)
- return false;
- return SE->getTypeOfArgument()->isVariableArrayType();
- }
- case Stmt::DeclRefExprClass:
- // Check for constants/pseudoconstants
- return !isConstantOrPseudoConstant(cast<DeclRefExpr>(Ex), AC);
-
- // The next cases require recursion for subexpressions
- case Stmt::BinaryOperatorClass: {
- const BinaryOperator *B = cast<const BinaryOperator>(Ex);
-
- // Exclude cases involving pointer arithmetic. These are usually
- // false positives.
- if (B->getOpcode() == BO_Sub || B->getOpcode() == BO_Add)
- if (B->getLHS()->getType()->getAs<PointerType>())
- return false;
-
- return CanVary(B->getRHS(), AC)
- || CanVary(B->getLHS(), AC);
- }
- case Stmt::UnaryOperatorClass:
- return CanVary(cast<UnaryOperator>(Ex)->getSubExpr(), AC);
- case Stmt::ConditionalOperatorClass:
- case Stmt::BinaryConditionalOperatorClass:
- return CanVary(cast<AbstractConditionalOperator>(Ex)->getCond(), AC);
- }
-}
-
-// Returns true if a DeclRefExpr is or behaves like a constant.
-bool IdempotentOperationChecker::isConstantOrPseudoConstant(
- const DeclRefExpr *DR,
- AnalysisDeclContext *AC) {
- // Check if the type of the Decl is const-qualified
- if (DR->getType().isConstQualified())
- return true;
-
- // Check for an enum
- if (isa<EnumConstantDecl>(DR->getDecl()))
- return true;
-
- const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
- if (!VD)
- return true;
-
- // Check if the Decl behaves like a constant. This check also takes care of
- // static variables, which can only change between function calls if they are
- // modified in the AST.
- PseudoConstantAnalysis *PCA = AC->getPseudoConstantAnalysis();
- if (PCA->isPseudoConstant(VD))
- return true;
-
- return false;
-}
-
-// Recursively find any substatements containing VarDecl's with storage other
-// than local
-bool IdempotentOperationChecker::containsNonLocalVarDecl(const Stmt *S) {
- const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S);
-
- if (DR)
- if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
- if (!VD->hasLocalStorage())
- return true;
-
- for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end();
- ++I)
- if (const Stmt *child = *I)
- if (containsNonLocalVarDecl(child))
- return true;
-
- return false;
-}
-
-
-void ento::registerIdempotentOperationChecker(CheckerManager &mgr) {
- mgr.registerChecker<IdempotentOperationChecker>();
-}
diff --git a/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp b/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp
index e696e38..d5c52b4 100644
--- a/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp
@@ -11,22 +11,23 @@
/// \brief This defines IdenticalExprChecker, a check that warns about
/// unintended use of identical expressions.
///
-/// It checks for use of identical expressions with comparison operators.
+/// It checks for use of identical expressions with comparison operators and
+/// inside conditional expressions.
///
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
+#include "clang/AST/RecursiveASTVisitor.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/CheckerContext.h"
-#include "clang/AST/RecursiveASTVisitor.h"
using namespace clang;
using namespace ento;
-static bool isIdenticalExpr(const ASTContext &Ctx, const Expr *Expr1,
- const Expr *Expr2);
+static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,
+ const Stmt *Stmt2, bool IgnoreSideEffects = false);
//===----------------------------------------------------------------------===//
// FindIdenticalExprVisitor - Identify nodes using identical expressions.
//===----------------------------------------------------------------------===//
@@ -34,23 +35,153 @@
namespace {
class FindIdenticalExprVisitor
: public RecursiveASTVisitor<FindIdenticalExprVisitor> {
+ BugReporter &BR;
+ const CheckerBase *Checker;
+ AnalysisDeclContext *AC;
public:
- explicit FindIdenticalExprVisitor(BugReporter &B, AnalysisDeclContext *A)
- : BR(B), AC(A) {}
+ explicit FindIdenticalExprVisitor(BugReporter &B,
+ const CheckerBase *Checker,
+ AnalysisDeclContext *A)
+ : BR(B), Checker(Checker), AC(A) {}
// FindIdenticalExprVisitor only visits nodes
- // that are binary operators.
+ // that are binary operators, if statements or
+ // conditional operators.
bool VisitBinaryOperator(const BinaryOperator *B);
+ bool VisitIfStmt(const IfStmt *I);
+ bool VisitConditionalOperator(const ConditionalOperator *C);
private:
- BugReporter &BR;
- AnalysisDeclContext *AC;
+ void reportIdenticalExpr(const BinaryOperator *B, bool CheckBitwise,
+ ArrayRef<SourceRange> Sr);
+ void checkBitwiseOrLogicalOp(const BinaryOperator *B, bool CheckBitwise);
+ void checkComparisonOp(const BinaryOperator *B);
};
} // end anonymous namespace
+void FindIdenticalExprVisitor::reportIdenticalExpr(const BinaryOperator *B,
+ bool CheckBitwise,
+ ArrayRef<SourceRange> Sr) {
+ StringRef Message;
+ if (CheckBitwise)
+ Message = "identical expressions on both sides of bitwise operator";
+ else
+ Message = "identical expressions on both sides of logical operator";
+
+ PathDiagnosticLocation ELoc =
+ PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());
+ BR.EmitBasicReport(AC->getDecl(), Checker,
+ "Use of identical expressions",
+ categories::LogicError,
+ Message, ELoc, Sr);
+}
+
+void FindIdenticalExprVisitor::checkBitwiseOrLogicalOp(const BinaryOperator *B,
+ bool CheckBitwise) {
+ SourceRange Sr[2];
+
+ const Expr *LHS = B->getLHS();
+ const Expr *RHS = B->getRHS();
+
+ // Split operators as long as we still have operators to split on. We will
+ // get called for every binary operator in an expression so there is no need
+ // to check every one against each other here, just the right most one with
+ // the others.
+ while (const BinaryOperator *B2 = dyn_cast<BinaryOperator>(LHS)) {
+ if (B->getOpcode() != B2->getOpcode())
+ break;
+ if (isIdenticalStmt(AC->getASTContext(), RHS, B2->getRHS())) {
+ Sr[0] = RHS->getSourceRange();
+ Sr[1] = B2->getRHS()->getSourceRange();
+ reportIdenticalExpr(B, CheckBitwise, Sr);
+ }
+ LHS = B2->getLHS();
+ }
+
+ if (isIdenticalStmt(AC->getASTContext(), RHS, LHS)) {
+ Sr[0] = RHS->getSourceRange();
+ Sr[1] = LHS->getSourceRange();
+ reportIdenticalExpr(B, CheckBitwise, Sr);
+ }
+}
+
+bool FindIdenticalExprVisitor::VisitIfStmt(const IfStmt *I) {
+ const Stmt *Stmt1 = I->getThen();
+ const Stmt *Stmt2 = I->getElse();
+
+ // Check for identical conditions:
+ //
+ // if (b) {
+ // foo1();
+ // } else if (b) {
+ // foo2();
+ // }
+ if (Stmt1 && Stmt2) {
+ const Expr *Cond1 = I->getCond();
+ const Stmt *Else = Stmt2;
+ while (const IfStmt *I2 = dyn_cast_or_null<IfStmt>(Else)) {
+ const Expr *Cond2 = I2->getCond();
+ if (isIdenticalStmt(AC->getASTContext(), Cond1, Cond2, false)) {
+ SourceRange Sr = Cond1->getSourceRange();
+ PathDiagnosticLocation ELoc(Cond2, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), Checker, "Identical conditions",
+ categories::LogicError,
+ "expression is identical to previous condition",
+ ELoc, Sr);
+ }
+ Else = I2->getElse();
+ }
+ }
+
+ if (!Stmt1 || !Stmt2)
+ return true;
+
+ // Special handling for code like:
+ //
+ // if (b) {
+ // i = 1;
+ // } else
+ // i = 1;
+ if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt1)) {
+ if (CompStmt->size() == 1)
+ Stmt1 = CompStmt->body_back();
+ }
+ if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt2)) {
+ if (CompStmt->size() == 1)
+ Stmt2 = CompStmt->body_back();
+ }
+
+ if (isIdenticalStmt(AC->getASTContext(), Stmt1, Stmt2, true)) {
+ PathDiagnosticLocation ELoc =
+ PathDiagnosticLocation::createBegin(I, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), Checker,
+ "Identical branches",
+ categories::LogicError,
+ "true and false branches are identical", ELoc);
+ }
+ return true;
+}
+
bool FindIdenticalExprVisitor::VisitBinaryOperator(const BinaryOperator *B) {
BinaryOperator::Opcode Op = B->getOpcode();
- if (!BinaryOperator::isComparisonOp(Op))
- return true;
+
+ if (BinaryOperator::isBitwiseOp(Op))
+ checkBitwiseOrLogicalOp(B, true);
+
+ if (BinaryOperator::isLogicalOp(Op))
+ checkBitwiseOrLogicalOp(B, false);
+
+ if (BinaryOperator::isComparisonOp(Op))
+ checkComparisonOp(B);
+
+ // We want to visit ALL nodes (subexpressions of binary comparison
+ // expressions too) that contains comparison operators.
+ // True is always returned to traverse ALL nodes.
+ return true;
+}
+
+void FindIdenticalExprVisitor::checkComparisonOp(const BinaryOperator *B) {
+ BinaryOperator::Opcode Op = B->getOpcode();
+
//
// Special case for floating-point representation.
//
@@ -83,26 +214,26 @@
(DeclRef2->getType()->hasFloatingRepresentation())) {
if (DeclRef1->getDecl() == DeclRef2->getDecl()) {
if ((Op == BO_EQ) || (Op == BO_NE)) {
- return true;
+ return;
}
}
}
} else if ((FloatLit1) && (FloatLit2)) {
if (FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue())) {
if ((Op == BO_EQ) || (Op == BO_NE)) {
- return true;
+ return;
}
}
} else if (LHS->getType()->hasFloatingRepresentation()) {
// If any side of comparison operator still has floating-point
// representation, then it's an expression. Don't warn.
// Here only LHS is checked since RHS will be implicit casted to float.
- return true;
+ return;
} else {
// No special case with floating-point representation, report as usual.
}
- if (isIdenticalExpr(AC->getASTContext(), B->getLHS(), B->getRHS())) {
+ if (isIdenticalStmt(AC->getASTContext(), B->getLHS(), B->getRHS())) {
PathDiagnosticLocation ELoc =
PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());
StringRef Message;
@@ -110,15 +241,41 @@
Message = "comparison of identical expressions always evaluates to true";
else
Message = "comparison of identical expressions always evaluates to false";
- BR.EmitBasicReport(AC->getDecl(), "Compare of identical expressions",
+ BR.EmitBasicReport(AC->getDecl(), Checker,
+ "Compare of identical expressions",
categories::LogicError, Message, ELoc);
}
- // We want to visit ALL nodes (subexpressions of binary comparison
- // expressions too) that contains comparison operators.
- // True is always returned to traverse ALL nodes.
+}
+
+bool FindIdenticalExprVisitor::VisitConditionalOperator(
+ const ConditionalOperator *C) {
+
+ // Check if expressions in conditional expression are identical
+ // from a symbolic point of view.
+
+ if (isIdenticalStmt(AC->getASTContext(), C->getTrueExpr(),
+ C->getFalseExpr(), true)) {
+ PathDiagnosticLocation ELoc =
+ PathDiagnosticLocation::createConditionalColonLoc(
+ C, BR.getSourceManager());
+
+ SourceRange Sr[2];
+ Sr[0] = C->getTrueExpr()->getSourceRange();
+ Sr[1] = C->getFalseExpr()->getSourceRange();
+ BR.EmitBasicReport(
+ AC->getDecl(), Checker,
+ "Identical expressions in conditional expression",
+ categories::LogicError,
+ "identical expressions on both sides of ':' in conditional expression",
+ ELoc, Sr);
+ }
+ // We want to visit ALL nodes (expressions in conditional
+ // expressions too) that contains conditional operators,
+ // thus always return true to traverse ALL nodes.
return true;
}
-/// \brief Determines whether two expression trees are identical regarding
+
+/// \brief Determines whether two statement trees are identical regarding
/// operators and symbols.
///
/// Exceptions: expressions containing macros or functions with possible side
@@ -126,82 +283,189 @@
/// Limitations: (t + u) and (u + t) are not considered identical.
/// t*(u + t) and t*u + t*t are not considered identical.
///
-static bool isIdenticalExpr(const ASTContext &Ctx, const Expr *Expr1,
- const Expr *Expr2) {
- // If Expr1 & Expr2 are of different class then they are not
- // identical expression.
- if (Expr1->getStmtClass() != Expr2->getStmtClass())
+static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,
+ const Stmt *Stmt2, bool IgnoreSideEffects) {
+
+ if (!Stmt1 || !Stmt2) {
+ if (!Stmt1 && !Stmt2)
+ return true;
return false;
- // If Expr1 has side effects then don't warn even if expressions
- // are identical.
- if (Expr1->HasSideEffects(Ctx))
- return false;
- // Is expression is based on macro then don't warn even if
- // the expressions are identical.
- if ((Expr1->getExprLoc().isMacroID()) || (Expr2->getExprLoc().isMacroID()))
- return false;
- // If all children of two expressions are identical, return true.
- Expr::const_child_iterator I1 = Expr1->child_begin();
- Expr::const_child_iterator I2 = Expr2->child_begin();
- while (I1 != Expr1->child_end() && I2 != Expr2->child_end()) {
- const Expr *Child1 = dyn_cast<Expr>(*I1);
- const Expr *Child2 = dyn_cast<Expr>(*I2);
- if (!Child1 || !Child2 || !isIdenticalExpr(Ctx, Child1, Child2))
- return false;
- ++I1;
- ++I2;
}
- // If there are different number of children in the expressions, return false.
- // (TODO: check if this is a redundant condition.)
- if (I1 != Expr1->child_end())
- return false;
- if (I2 != Expr2->child_end())
+
+ // If Stmt1 & Stmt2 are of different class then they are not
+ // identical statements.
+ if (Stmt1->getStmtClass() != Stmt2->getStmtClass())
return false;
- switch (Expr1->getStmtClass()) {
+ const Expr *Expr1 = dyn_cast<Expr>(Stmt1);
+ const Expr *Expr2 = dyn_cast<Expr>(Stmt2);
+
+ if (Expr1 && Expr2) {
+ // If Stmt1 has side effects then don't warn even if expressions
+ // are identical.
+ if (!IgnoreSideEffects && Expr1->HasSideEffects(Ctx))
+ return false;
+ // If either expression comes from a macro then don't warn even if
+ // the expressions are identical.
+ if ((Expr1->getExprLoc().isMacroID()) || (Expr2->getExprLoc().isMacroID()))
+ return false;
+
+ // If all children of two expressions are identical, return true.
+ Expr::const_child_iterator I1 = Expr1->child_begin();
+ Expr::const_child_iterator I2 = Expr2->child_begin();
+ while (I1 != Expr1->child_end() && I2 != Expr2->child_end()) {
+ if (!*I1 || !*I2 || !isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects))
+ return false;
+ ++I1;
+ ++I2;
+ }
+ // If there are different number of children in the statements, return
+ // false.
+ if (I1 != Expr1->child_end())
+ return false;
+ if (I2 != Expr2->child_end())
+ return false;
+ }
+
+ switch (Stmt1->getStmtClass()) {
default:
return false;
+ case Stmt::CallExprClass:
case Stmt::ArraySubscriptExprClass:
- case Stmt::CStyleCastExprClass:
case Stmt::ImplicitCastExprClass:
case Stmt::ParenExprClass:
+ case Stmt::BreakStmtClass:
+ case Stmt::ContinueStmtClass:
+ case Stmt::NullStmtClass:
return true;
+ case Stmt::CStyleCastExprClass: {
+ const CStyleCastExpr* CastExpr1 = cast<CStyleCastExpr>(Stmt1);
+ const CStyleCastExpr* CastExpr2 = cast<CStyleCastExpr>(Stmt2);
+
+ return CastExpr1->getTypeAsWritten() == CastExpr2->getTypeAsWritten();
+ }
+ case Stmt::ReturnStmtClass: {
+ const ReturnStmt *ReturnStmt1 = cast<ReturnStmt>(Stmt1);
+ const ReturnStmt *ReturnStmt2 = cast<ReturnStmt>(Stmt2);
+
+ return isIdenticalStmt(Ctx, ReturnStmt1->getRetValue(),
+ ReturnStmt2->getRetValue(), IgnoreSideEffects);
+ }
+ case Stmt::ForStmtClass: {
+ const ForStmt *ForStmt1 = cast<ForStmt>(Stmt1);
+ const ForStmt *ForStmt2 = cast<ForStmt>(Stmt2);
+
+ if (!isIdenticalStmt(Ctx, ForStmt1->getInit(), ForStmt2->getInit(),
+ IgnoreSideEffects))
+ return false;
+ if (!isIdenticalStmt(Ctx, ForStmt1->getCond(), ForStmt2->getCond(),
+ IgnoreSideEffects))
+ return false;
+ if (!isIdenticalStmt(Ctx, ForStmt1->getInc(), ForStmt2->getInc(),
+ IgnoreSideEffects))
+ return false;
+ if (!isIdenticalStmt(Ctx, ForStmt1->getBody(), ForStmt2->getBody(),
+ IgnoreSideEffects))
+ return false;
+ return true;
+ }
+ case Stmt::DoStmtClass: {
+ const DoStmt *DStmt1 = cast<DoStmt>(Stmt1);
+ const DoStmt *DStmt2 = cast<DoStmt>(Stmt2);
+
+ if (!isIdenticalStmt(Ctx, DStmt1->getCond(), DStmt2->getCond(),
+ IgnoreSideEffects))
+ return false;
+ if (!isIdenticalStmt(Ctx, DStmt1->getBody(), DStmt2->getBody(),
+ IgnoreSideEffects))
+ return false;
+ return true;
+ }
+ case Stmt::WhileStmtClass: {
+ const WhileStmt *WStmt1 = cast<WhileStmt>(Stmt1);
+ const WhileStmt *WStmt2 = cast<WhileStmt>(Stmt2);
+
+ if (!isIdenticalStmt(Ctx, WStmt1->getCond(), WStmt2->getCond(),
+ IgnoreSideEffects))
+ return false;
+ if (!isIdenticalStmt(Ctx, WStmt1->getBody(), WStmt2->getBody(),
+ IgnoreSideEffects))
+ return false;
+ return true;
+ }
+ case Stmt::IfStmtClass: {
+ const IfStmt *IStmt1 = cast<IfStmt>(Stmt1);
+ const IfStmt *IStmt2 = cast<IfStmt>(Stmt2);
+
+ if (!isIdenticalStmt(Ctx, IStmt1->getCond(), IStmt2->getCond(),
+ IgnoreSideEffects))
+ return false;
+ if (!isIdenticalStmt(Ctx, IStmt1->getThen(), IStmt2->getThen(),
+ IgnoreSideEffects))
+ return false;
+ if (!isIdenticalStmt(Ctx, IStmt1->getElse(), IStmt2->getElse(),
+ IgnoreSideEffects))
+ return false;
+ return true;
+ }
+ case Stmt::CompoundStmtClass: {
+ const CompoundStmt *CompStmt1 = cast<CompoundStmt>(Stmt1);
+ const CompoundStmt *CompStmt2 = cast<CompoundStmt>(Stmt2);
+
+ if (CompStmt1->size() != CompStmt2->size())
+ return false;
+
+ CompoundStmt::const_body_iterator I1 = CompStmt1->body_begin();
+ CompoundStmt::const_body_iterator I2 = CompStmt2->body_begin();
+ while (I1 != CompStmt1->body_end() && I2 != CompStmt2->body_end()) {
+ if (!isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects))
+ return false;
+ ++I1;
+ ++I2;
+ }
+
+ return true;
+ }
+ case Stmt::CompoundAssignOperatorClass:
case Stmt::BinaryOperatorClass: {
- const BinaryOperator *BinOp1 = dyn_cast<BinaryOperator>(Expr1);
- const BinaryOperator *BinOp2 = dyn_cast<BinaryOperator>(Expr2);
+ const BinaryOperator *BinOp1 = cast<BinaryOperator>(Stmt1);
+ const BinaryOperator *BinOp2 = cast<BinaryOperator>(Stmt2);
return BinOp1->getOpcode() == BinOp2->getOpcode();
}
case Stmt::CharacterLiteralClass: {
- const CharacterLiteral *CharLit1 = dyn_cast<CharacterLiteral>(Expr1);
- const CharacterLiteral *CharLit2 = dyn_cast<CharacterLiteral>(Expr2);
+ const CharacterLiteral *CharLit1 = cast<CharacterLiteral>(Stmt1);
+ const CharacterLiteral *CharLit2 = cast<CharacterLiteral>(Stmt2);
return CharLit1->getValue() == CharLit2->getValue();
}
case Stmt::DeclRefExprClass: {
- const DeclRefExpr *DeclRef1 = dyn_cast<DeclRefExpr>(Expr1);
- const DeclRefExpr *DeclRef2 = dyn_cast<DeclRefExpr>(Expr2);
+ const DeclRefExpr *DeclRef1 = cast<DeclRefExpr>(Stmt1);
+ const DeclRefExpr *DeclRef2 = cast<DeclRefExpr>(Stmt2);
return DeclRef1->getDecl() == DeclRef2->getDecl();
}
case Stmt::IntegerLiteralClass: {
- const IntegerLiteral *IntLit1 = dyn_cast<IntegerLiteral>(Expr1);
- const IntegerLiteral *IntLit2 = dyn_cast<IntegerLiteral>(Expr2);
+ const IntegerLiteral *IntLit1 = cast<IntegerLiteral>(Stmt1);
+ const IntegerLiteral *IntLit2 = cast<IntegerLiteral>(Stmt2);
return IntLit1->getValue() == IntLit2->getValue();
}
case Stmt::FloatingLiteralClass: {
- const FloatingLiteral *FloatLit1 = dyn_cast<FloatingLiteral>(Expr1);
- const FloatingLiteral *FloatLit2 = dyn_cast<FloatingLiteral>(Expr2);
+ const FloatingLiteral *FloatLit1 = cast<FloatingLiteral>(Stmt1);
+ const FloatingLiteral *FloatLit2 = cast<FloatingLiteral>(Stmt2);
return FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue());
}
+ case Stmt::StringLiteralClass: {
+ const StringLiteral *StringLit1 = cast<StringLiteral>(Stmt1);
+ const StringLiteral *StringLit2 = cast<StringLiteral>(Stmt2);
+ return StringLit1->getString() == StringLit2->getString();
+ }
case Stmt::MemberExprClass: {
- const MemberExpr *MemberExpr1 = dyn_cast<MemberExpr>(Expr1);
- const MemberExpr *MemberExpr2 = dyn_cast<MemberExpr>(Expr2);
- return MemberExpr1->getMemberDecl() == MemberExpr2->getMemberDecl();
+ const MemberExpr *MemberStmt1 = cast<MemberExpr>(Stmt1);
+ const MemberExpr *MemberStmt2 = cast<MemberExpr>(Stmt2);
+ return MemberStmt1->getMemberDecl() == MemberStmt2->getMemberDecl();
}
case Stmt::UnaryOperatorClass: {
- const UnaryOperator *UnaryOp1 = dyn_cast<UnaryOperator>(Expr1);
- const UnaryOperator *UnaryOp2 = dyn_cast<UnaryOperator>(Expr2);
- if (UnaryOp1->getOpcode() != UnaryOp2->getOpcode())
- return false;
- return !UnaryOp1->isIncrementDecrementOp();
+ const UnaryOperator *UnaryOp1 = cast<UnaryOperator>(Stmt1);
+ const UnaryOperator *UnaryOp2 = cast<UnaryOperator>(Stmt2);
+ return UnaryOp1->getOpcode() == UnaryOp2->getOpcode();
}
}
}
@@ -215,7 +479,7 @@
public:
void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
BugReporter &BR) const {
- FindIdenticalExprVisitor Visitor(BR, Mgr.getAnalysisDeclContext(D));
+ FindIdenticalExprVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D));
Visitor.TraverseDecl(const_cast<Decl *>(D));
}
};
diff --git a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
index cc940be..fdd1fdc 100644
--- a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
@@ -49,6 +49,9 @@
DefaultBool check_MissingInvalidationMethod;
/// Check that all ivars are invalidated.
DefaultBool check_InstanceVariableInvalidation;
+
+ CheckName checkName_MissingInvalidationMethod;
+ CheckName checkName_InstanceVariableInvalidation;
};
class IvarInvalidationCheckerImpl {
@@ -200,7 +203,8 @@
const ObjCIvarDecl *IvarDecl,
const IvarToPropMapTy &IvarToPopertyMap);
- void reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl,
+ void reportNoInvalidationMethod(CheckName CheckName,
+ const ObjCIvarDecl *FirstIvarDecl,
const IvarToPropMapTy &IvarToPopertyMap,
const ObjCInterfaceDecl *InterfaceD,
bool MissingDeclaration) const;
@@ -223,10 +227,7 @@
};
static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) {
- for (specific_attr_iterator<AnnotateAttr>
- AI = M->specific_attr_begin<AnnotateAttr>(),
- AE = M->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) {
- const AnnotateAttr *Ann = *AI;
+ for (const auto *Ann : M->specific_attrs<AnnotateAttr>()) {
if (!LookForPartial &&
Ann->getAnnotation() == "objc_instance_variable_invalidator")
return true;
@@ -247,33 +248,22 @@
// TODO: Cache the results.
// Check all methods.
- for (ObjCContainerDecl::method_iterator
- I = D->meth_begin(),
- E = D->meth_end(); I != E; ++I) {
- const ObjCMethodDecl *MDI = *I;
- if (isInvalidationMethod(MDI, Partial))
- OutInfo.addInvalidationMethod(
- cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
- }
+ for (const auto *MDI : D->methods())
+ if (isInvalidationMethod(MDI, Partial))
+ OutInfo.addInvalidationMethod(
+ cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
// If interface, check all parent protocols and super.
if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) {
// Visit all protocols.
- for (ObjCInterfaceDecl::protocol_iterator
- I = InterfD->protocol_begin(),
- E = InterfD->protocol_end(); I != E; ++I) {
- containsInvalidationMethod((*I)->getDefinition(), OutInfo, Partial);
- }
+ for (const auto *I : InterfD->protocols())
+ containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
// Visit all categories in case the invalidation method is declared in
// a category.
- for (ObjCInterfaceDecl::visible_extensions_iterator
- Ext = InterfD->visible_extensions_begin(),
- ExtEnd = InterfD->visible_extensions_end();
- Ext != ExtEnd; ++Ext) {
- containsInvalidationMethod(*Ext, OutInfo, Partial);
- }
+ for (const auto *Ext : InterfD->visible_extensions())
+ containsInvalidationMethod(Ext, OutInfo, Partial);
containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
return;
@@ -281,10 +271,8 @@
// If protocol, check all parent protocols.
if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
- for (ObjCInterfaceDecl::protocol_iterator
- I = ProtD->protocol_begin(),
- E = ProtD->protocol_end(); I != E; ++I) {
- containsInvalidationMethod((*I)->getDefinition(), OutInfo, Partial);
+ for (const auto *I : ProtD->protocols()) {
+ containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
}
return;
}
@@ -476,7 +464,8 @@
// Report an error in case none of the invalidation methods are declared.
if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) {
if (Filter.check_MissingInvalidationMethod)
- reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD,
+ reportNoInvalidationMethod(Filter.checkName_MissingInvalidationMethod,
+ FirstIvarDecl, IvarToPopertyMap, InterfaceD,
/*MissingDeclaration*/ true);
// If there are no invalidation methods, there is no ivar validation work
// to be done.
@@ -532,17 +521,17 @@
reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, 0);
} else {
// Otherwise, no invalidation methods were implemented.
- reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD,
+ reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation,
+ FirstIvarDecl, IvarToPopertyMap, InterfaceD,
/*MissingDeclaration*/ false);
}
}
}
-void IvarInvalidationCheckerImpl::
-reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl,
- const IvarToPropMapTy &IvarToPopertyMap,
- const ObjCInterfaceDecl *InterfaceD,
- bool MissingDeclaration) const {
+void IvarInvalidationCheckerImpl::reportNoInvalidationMethod(
+ CheckName CheckName, const ObjCIvarDecl *FirstIvarDecl,
+ const IvarToPropMapTy &IvarToPopertyMap,
+ const ObjCInterfaceDecl *InterfaceD, bool MissingDeclaration) const {
SmallString<128> sbuf;
llvm::raw_svector_ostream os(sbuf);
assert(FirstIvarDecl);
@@ -557,7 +546,7 @@
PathDiagnosticLocation IvarDecLocation =
PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager());
- BR.EmitBasicReport(FirstIvarDecl, "Incomplete invalidation",
+ BR.EmitBasicReport(FirstIvarDecl, CheckName, "Incomplete invalidation",
categories::CoreFoundationObjectiveC, os.str(),
IvarDecLocation);
}
@@ -575,15 +564,16 @@
PathDiagnosticLocation::createEnd(MethodD->getBody(),
BR.getSourceManager(),
Mgr.getAnalysisDeclContext(MethodD));
- BR.EmitBasicReport(MethodD, "Incomplete invalidation",
+ BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation,
+ "Incomplete invalidation",
categories::CoreFoundationObjectiveC, os.str(),
MethodDecLocation);
} else {
- BR.EmitBasicReport(IvarD, "Incomplete invalidation",
- categories::CoreFoundationObjectiveC, os.str(),
- PathDiagnosticLocation::createBegin(IvarD,
- BR.getSourceManager()));
-
+ BR.EmitBasicReport(
+ IvarD, Filter.checkName_InstanceVariableInvalidation,
+ "Incomplete invalidation", categories::CoreFoundationObjectiveC,
+ os.str(),
+ PathDiagnosticLocation::createBegin(IvarD, BR.getSourceManager()));
}
}
@@ -750,10 +740,13 @@
};
}
-#define REGISTER_CHECKER(name) \
-void ento::register##name(CheckerManager &mgr) {\
- mgr.registerChecker<IvarInvalidationChecker>()->Filter.check_##name = true;\
-}
+#define REGISTER_CHECKER(name) \
+ void ento::register##name(CheckerManager &mgr) { \
+ IvarInvalidationChecker *checker = \
+ mgr.registerChecker<IvarInvalidationChecker>(); \
+ checker->Filter.check_##name = true; \
+ checker->Filter.checkName_##name = mgr.getCurrentCheckName(); \
+ }
REGISTER_CHECKER(InstanceVariableInvalidation)
REGISTER_CHECKER(MissingInvalidationMethod)
diff --git a/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp b/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp
index 02a7cc3..17ebf9e 100644
--- a/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp
@@ -115,11 +115,14 @@
namespace {
class StringRefCheckerVisitor : public StmtVisitor<StringRefCheckerVisitor> {
- BugReporter &BR;
const Decl *DeclWithIssue;
+ BugReporter &BR;
+ const CheckerBase *Checker;
+
public:
- StringRefCheckerVisitor(const Decl *declWithIssue, BugReporter &br)
- : BR(br), DeclWithIssue(declWithIssue) {}
+ StringRefCheckerVisitor(const Decl *declWithIssue, BugReporter &br,
+ const CheckerBase *checker)
+ : DeclWithIssue(declWithIssue), BR(br), Checker(checker) {}
void VisitChildren(Stmt *S) {
for (Stmt::child_iterator I = S->child_begin(), E = S->child_end() ;
I != E; ++I)
@@ -133,16 +136,17 @@
};
} // end anonymous namespace
-static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR) {
- StringRefCheckerVisitor walker(D, BR);
+static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR,
+ const CheckerBase *Checker) {
+ StringRefCheckerVisitor walker(D, BR, Checker);
walker.Visit(D->getBody());
}
void StringRefCheckerVisitor::VisitDeclStmt(DeclStmt *S) {
VisitChildren(S);
- for (DeclStmt::decl_iterator I = S->decl_begin(), E = S->decl_end();I!=E; ++I)
- if (VarDecl *VD = dyn_cast<VarDecl>(*I))
+ for (auto *I : S->decls())
+ if (VarDecl *VD = dyn_cast<VarDecl>(I))
VisitVarDecl(VD);
}
@@ -179,7 +183,7 @@
"std::string that it outlives";
PathDiagnosticLocation VDLoc =
PathDiagnosticLocation::createBegin(VD, BR.getSourceManager());
- BR.EmitBasicReport(DeclWithIssue, desc, "LLVM Conventions", desc,
+ BR.EmitBasicReport(DeclWithIssue, Checker, desc, "LLVM Conventions", desc,
VDLoc, Init->getSourceRange());
}
@@ -197,9 +201,7 @@
if (IsClangStmt(R) || IsClangType(R) || IsClangDecl(R) || IsClangAttr(R))
return true;
- for (CXXRecordDecl::base_class_const_iterator I = R->bases_begin(),
- E = R->bases_end(); I!=E; ++I) {
- CXXBaseSpecifier BS = *I;
+ for (const auto &BS : R->bases()) {
QualType T = BS.getType();
if (const RecordType *baseT = T->getAs<RecordType>()) {
CXXRecordDecl *baseD = cast<CXXRecordDecl>(baseT->getDecl());
@@ -216,23 +218,26 @@
SmallVector<FieldDecl*, 10> FieldChain;
const CXXRecordDecl *Root;
BugReporter &BR;
+ const CheckerBase *Checker;
+
public:
- ASTFieldVisitor(const CXXRecordDecl *root, BugReporter &br)
- : Root(root), BR(br) {}
+ ASTFieldVisitor(const CXXRecordDecl *root, BugReporter &br,
+ const CheckerBase *checker)
+ : Root(root), BR(br), Checker(checker) {}
void Visit(FieldDecl *D);
void ReportError(QualType T);
};
} // end anonymous namespace
-static void CheckASTMemory(const CXXRecordDecl *R, BugReporter &BR) {
+static void CheckASTMemory(const CXXRecordDecl *R, BugReporter &BR,
+ const CheckerBase *Checker) {
if (!IsPartOfAST(R))
return;
- for (RecordDecl::field_iterator I = R->field_begin(), E = R->field_end();
- I != E; ++I) {
- ASTFieldVisitor walker(R, BR);
- walker.Visit(*I);
+ for (auto *I : R->fields()) {
+ ASTFieldVisitor walker(R, BR, Checker);
+ walker.Visit(I);
}
}
@@ -246,9 +251,8 @@
if (const RecordType *RT = T->getAs<RecordType>()) {
const RecordDecl *RD = RT->getDecl()->getDefinition();
- for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end();
- I != E; ++I)
- Visit(*I);
+ for (auto *I : RD->fields())
+ Visit(I);
}
FieldChain.pop_back();
@@ -284,8 +288,8 @@
// the class may be in the header file, for example).
PathDiagnosticLocation L = PathDiagnosticLocation::createBegin(
FieldChain.front(), BR.getSourceManager());
- BR.EmitBasicReport(Root, "AST node allocates heap memory", "LLVM Conventions",
- os.str(), L);
+ BR.EmitBasicReport(Root, Checker, "AST node allocates heap memory",
+ "LLVM Conventions", os.str(), L);
}
//===----------------------------------------------------------------------===//
@@ -300,12 +304,12 @@
void checkASTDecl(const CXXRecordDecl *R, AnalysisManager& mgr,
BugReporter &BR) const {
if (R->isCompleteDefinition())
- CheckASTMemory(R, BR);
+ CheckASTMemory(R, BR, this);
}
void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
BugReporter &BR) const {
- CheckStringRefAssignedTemporary(D, BR);
+ CheckStringRefAssignedTemporary(D, BR, this);
}
};
}
diff --git a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
index f1f06c7..4a293c4 100644
--- a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
@@ -29,7 +29,7 @@
class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
check::PostStmt<CallExpr>,
check::DeadSymbols> {
- mutable OwningPtr<BugType> BT;
+ mutable std::unique_ptr<BugType> BT;
public:
/// AllocationState is a part of the checker specific state together with the
@@ -91,7 +91,7 @@
inline void initBugType() const {
if (!BT)
- BT.reset(new BugType("Improper use of SecKeychain API",
+ BT.reset(new BugType(this, "Improper use of SecKeychain API",
"API Misuse (Apple)"));
}
@@ -139,7 +139,7 @@
SecKeychainBugVisitor(SymbolRef S) : Sym(S) {}
virtual ~SecKeychainBugVisitor() {}
- void Profile(llvm::FoldingSetNodeID &ID) const {
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
static int X = 0;
ID.AddPointer(&X);
ID.AddPointer(Sym);
@@ -148,7 +148,7 @@
PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
const ExplodedNode *PrevN,
BugReporterContext &BRC,
- BugReport &BR);
+ BugReport &BR) override;
};
};
}
@@ -575,7 +575,7 @@
return;
}
- static SimpleProgramPointTag Tag("MacOSKeychainAPIChecker : DeadSymbolsLeak");
+ static CheckerProgramPointTag Tag(this, "DeadSymbolsLeak");
ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag);
// Generate the error reports.
diff --git a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
index 32ebb51..d9e4699 100644
--- a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
@@ -31,7 +31,7 @@
namespace {
class MacOSXAPIChecker : public Checker< check::PreStmt<CallExpr> > {
- mutable OwningPtr<BugType> BT_dispatchOnce;
+ mutable std::unique_ptr<BugType> BT_dispatchOnce;
public:
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
@@ -67,7 +67,7 @@
return;
if (!BT_dispatchOnce)
- BT_dispatchOnce.reset(new BugType("Improper use of 'dispatch_once'",
+ BT_dispatchOnce.reset(new BugType(this, "Improper use of 'dispatch_once'",
"API Misuse (Apple)"));
// Handle _dispatch_once. In some versions of the OS X SDK we have the case
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index c7aa0fb..f0d1924 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -16,6 +16,7 @@
#include "InterCheckerAPI.h"
#include "clang/AST/Attr.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TargetInfo.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
@@ -48,7 +49,7 @@
Allocated,
// Reference to released/freed memory.
Released,
- // The responsibility for freeing resources has transfered from
+ // The responsibility for freeing resources has transferred from
// this reference. A relinquished symbol should not be freed.
Relinquished,
// We are no longer guaranteed to have observed all manipulations
@@ -100,17 +101,16 @@
}
void dump(raw_ostream &OS) const {
- static const char *const Table[] = {
- "Allocated",
- "Released",
- "Relinquished"
- };
- OS << Table[(unsigned) K];
+ switch (static_cast<Kind>(K)) {
+#define CASE(ID) case ID: OS << #ID; break;
+ CASE(Allocated)
+ CASE(Released)
+ CASE(Relinquished)
+ CASE(Escaped)
+ }
}
- LLVM_ATTRIBUTE_USED void dump() const {
- dump(llvm::errs());
- }
+ LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); }
};
enum ReallocPairKind {
@@ -156,30 +156,24 @@
check::Location,
eval::Assume>
{
- mutable OwningPtr<BugType> BT_DoubleFree;
- mutable OwningPtr<BugType> BT_Leak;
- mutable OwningPtr<BugType> BT_UseFree;
- mutable OwningPtr<BugType> BT_BadFree;
- mutable OwningPtr<BugType> BT_MismatchedDealloc;
- mutable OwningPtr<BugType> BT_OffsetFree;
- mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc,
- *II_valloc, *II_reallocf, *II_strndup, *II_strdup;
-
public:
MallocChecker() : II_malloc(0), II_free(0), II_realloc(0), II_calloc(0),
- II_valloc(0), II_reallocf(0), II_strndup(0), II_strdup(0) {}
+ II_valloc(0), II_reallocf(0), II_strndup(0), II_strdup(0),
+ II_kmalloc(0) {}
/// In pessimistic mode, the checker assumes that it does not know which
/// functions might free the memory.
- struct ChecksFilter {
- DefaultBool CMallocPessimistic;
- DefaultBool CMallocOptimistic;
- DefaultBool CNewDeleteChecker;
- DefaultBool CNewDeleteLeaksChecker;
- DefaultBool CMismatchedDeallocatorChecker;
+ enum CheckKind {
+ CK_MallocPessimistic,
+ CK_MallocOptimistic,
+ CK_NewDeleteChecker,
+ CK_NewDeleteLeaksChecker,
+ CK_MismatchedDeallocatorChecker,
+ CK_NumCheckKinds
};
- ChecksFilter Filter;
+ DefaultBool ChecksEnabled[CK_NumCheckKinds];
+ CheckName CheckNames[CK_NumCheckKinds];
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
@@ -204,9 +198,21 @@
PointerEscapeKind Kind) const;
void printState(raw_ostream &Out, ProgramStateRef State,
- const char *NL, const char *Sep) const;
+ const char *NL, const char *Sep) const override;
private:
+ mutable std::unique_ptr<BugType> BT_DoubleFree[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_DoubleDelete;
+ mutable std::unique_ptr<BugType> BT_Leak[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_UseFree[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_BadFree[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_MismatchedDealloc;
+ mutable std::unique_ptr<BugType> BT_OffsetFree[CK_NumCheckKinds];
+ mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc,
+ *II_valloc, *II_reallocf, *II_strndup, *II_strdup,
+ *II_kmalloc;
+ mutable Optional<uint64_t> KernelZeroFlagVal;
+
void initIdentifierInfo(ASTContext &C) const;
/// \brief Determine family of a deallocation expression.
@@ -234,9 +240,9 @@
bool isAllocationFunction(const FunctionDecl *FD, ASTContext &C) const;
bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const;
///@}
- static ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
- const CallExpr *CE,
- const OwnershipAttr* Att);
+ ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
+ const CallExpr *CE,
+ const OwnershipAttr* Att) const;
static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
const Expr *SizeEx, SVal Init,
ProgramStateRef State,
@@ -251,6 +257,12 @@
ProgramStateRef State,
AllocationFamily Family = AF_Malloc);
+ // Check if this malloc() for special flags. At present that means M_ZERO or
+ // __GFP_ZERO (in which case, treat it like calloc).
+ llvm::Optional<ProgramStateRef>
+ performKernelMalloc(const CallExpr *CE, CheckerContext &C,
+ const ProgramStateRef &State) const;
+
/// Update the RefState to reflect the new memory allocation.
static ProgramStateRef
MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State,
@@ -279,6 +291,8 @@
bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const;
+ bool checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const;
+
/// Check if the function is known free memory, or if it is
/// "interesting" and should be modeled explicitly.
///
@@ -302,10 +316,12 @@
///@{
/// Tells if a given family/call/symbol is tracked by the current checker.
- bool isTrackedByCurrentChecker(AllocationFamily Family) const;
- bool isTrackedByCurrentChecker(CheckerContext &C,
- const Stmt *AllocDeallocStmt) const;
- bool isTrackedByCurrentChecker(CheckerContext &C, SymbolRef Sym) const;
+ /// Sets CheckKind to the kind of the checker responsible for this
+ /// family/call/symbol.
+ Optional<CheckKind> getCheckIfTracked(AllocationFamily Family) const;
+ Optional<CheckKind> getCheckIfTracked(CheckerContext &C,
+ const Stmt *AllocDeallocStmt) const;
+ Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym) const;
///@}
static bool SummarizeValue(raw_ostream &os, SVal V);
static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR);
@@ -322,6 +338,8 @@
void ReportDoubleFree(CheckerContext &C, SourceRange Range, bool Released,
SymbolRef Sym, SymbolRef PrevSym) const;
+ void ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const;
+
/// Find the location of the allocation for Sym on the path leading to the
/// exploded node N.
LeakInfo getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
@@ -356,7 +374,7 @@
virtual ~MallocBugVisitor() {}
- void Profile(llvm::FoldingSetNodeID &ID) const {
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
static int X = 0;
ID.AddPointer(&X);
ID.AddPointer(Sym);
@@ -398,11 +416,11 @@
PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
const ExplodedNode *PrevN,
BugReporterContext &BRC,
- BugReport &BR);
+ BugReport &BR) override;
PathDiagnosticPiece* getEndPath(BugReporterContext &BRC,
const ExplodedNode *EndPathNode,
- BugReport &BR) {
+ BugReport &BR) override {
if (!IsLeak)
return 0;
@@ -420,7 +438,8 @@
StackHintGeneratorForReallocationFailed(SymbolRef S, StringRef M)
: StackHintGeneratorForSymbol(S, M) {}
- virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex) {
+ std::string getMessageForArg(const Expr *ArgE,
+ unsigned ArgIndex) override {
// Printed parameters start at 1, not 0.
++ArgIndex;
@@ -433,7 +452,7 @@
return os.str();
}
- virtual std::string getMessageForReturn(const CallExpr *CallExpr) {
+ std::string getMessageForReturn(const CallExpr *CallExpr) override {
return "Reallocation of returned value failed";
}
};
@@ -455,7 +474,7 @@
StopTrackingCallback(ProgramStateRef st) : state(st) {}
ProgramStateRef getState() const { return state; }
- bool VisitSymbol(SymbolRef sym) {
+ bool VisitSymbol(SymbolRef sym) override {
state = state->remove<RegionState>(sym);
return true;
}
@@ -473,6 +492,7 @@
II_valloc = &Ctx.Idents.get("valloc");
II_strdup = &Ctx.Idents.get("strdup");
II_strndup = &Ctx.Idents.get("strndup");
+ II_kmalloc = &Ctx.Idents.get("kmalloc");
}
bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const {
@@ -499,16 +519,13 @@
if (FunI == II_malloc || FunI == II_realloc ||
FunI == II_reallocf || FunI == II_calloc || FunI == II_valloc ||
- FunI == II_strdup || FunI == II_strndup)
+ FunI == II_strdup || FunI == II_strndup || FunI == II_kmalloc)
return true;
}
- if (Filter.CMallocOptimistic && FD->hasAttrs())
- for (specific_attr_iterator<OwnershipAttr>
- i = FD->specific_attr_begin<OwnershipAttr>(),
- e = FD->specific_attr_end<OwnershipAttr>();
- i != e; ++i)
- if ((*i)->getOwnKind() == OwnershipAttr::Returns)
+ if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs())
+ for (const auto *I : FD->specific_attrs<OwnershipAttr>())
+ if (I->getOwnKind() == OwnershipAttr::Returns)
return true;
return false;
}
@@ -525,13 +542,10 @@
return true;
}
- if (Filter.CMallocOptimistic && FD->hasAttrs())
- for (specific_attr_iterator<OwnershipAttr>
- i = FD->specific_attr_begin<OwnershipAttr>(),
- e = FD->specific_attr_end<OwnershipAttr>();
- i != e; ++i)
- if ((*i)->getOwnKind() == OwnershipAttr::Takes ||
- (*i)->getOwnKind() == OwnershipAttr::Holds)
+ if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs())
+ for (const auto *I : FD->specific_attrs<OwnershipAttr>())
+ if (I->getOwnKind() == OwnershipAttr::Takes ||
+ I->getOwnKind() == OwnershipAttr::Holds)
return true;
return false;
}
@@ -569,10 +583,88 @@
return true;
}
+llvm::Optional<ProgramStateRef> MallocChecker::performKernelMalloc(
+ const CallExpr *CE, CheckerContext &C, const ProgramStateRef &State) const {
+ // 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels:
+ //
+ // void *malloc(unsigned long size, struct malloc_type *mtp, int flags);
+ //
+ // One of the possible flags is M_ZERO, which means 'give me back an
+ // allocation which is already zeroed', like calloc.
+
+ // 2-argument kmalloc(), as used in the Linux kernel:
+ //
+ // void *kmalloc(size_t size, gfp_t flags);
+ //
+ // Has the similar flag value __GFP_ZERO.
+
+ // This logic is largely cloned from O_CREAT in UnixAPIChecker, maybe some
+ // code could be shared.
+
+ ASTContext &Ctx = C.getASTContext();
+ llvm::Triple::OSType OS = Ctx.getTargetInfo().getTriple().getOS();
+
+ if (!KernelZeroFlagVal.hasValue()) {
+ if (OS == llvm::Triple::FreeBSD)
+ KernelZeroFlagVal = 0x0100;
+ else if (OS == llvm::Triple::NetBSD)
+ KernelZeroFlagVal = 0x0002;
+ else if (OS == llvm::Triple::OpenBSD)
+ KernelZeroFlagVal = 0x0008;
+ else if (OS == llvm::Triple::Linux)
+ // __GFP_ZERO
+ KernelZeroFlagVal = 0x8000;
+ else
+ // FIXME: We need a more general way of getting the M_ZERO value.
+ // See also: O_CREAT in UnixAPIChecker.cpp.
+
+ // Fall back to normal malloc behavior on platforms where we don't
+ // know M_ZERO.
+ return None;
+ }
+
+ // We treat the last argument as the flags argument, and callers fall-back to
+ // normal malloc on a None return. This works for the FreeBSD kernel malloc
+ // as well as Linux kmalloc.
+ if (CE->getNumArgs() < 2)
+ return None;
+
+ const Expr *FlagsEx = CE->getArg(CE->getNumArgs() - 1);
+ const SVal V = State->getSVal(FlagsEx, C.getLocationContext());
+ if (!V.getAs<NonLoc>()) {
+ // The case where 'V' can be a location can only be due to a bad header,
+ // so in this case bail out.
+ return None;
+ }
+
+ NonLoc Flags = V.castAs<NonLoc>();
+ NonLoc ZeroFlag = C.getSValBuilder()
+ .makeIntVal(KernelZeroFlagVal.getValue(), FlagsEx->getType())
+ .castAs<NonLoc>();
+ SVal MaskedFlagsUC = C.getSValBuilder().evalBinOpNN(State, BO_And,
+ Flags, ZeroFlag,
+ FlagsEx->getType());
+ if (MaskedFlagsUC.isUnknownOrUndef())
+ return None;
+ DefinedSVal MaskedFlags = MaskedFlagsUC.castAs<DefinedSVal>();
+
+ // Check if maskedFlags is non-zero.
+ ProgramStateRef TrueState, FalseState;
+ std::tie(TrueState, FalseState) = State->assume(MaskedFlags);
+
+ // If M_ZERO is set, treat this like calloc (initialized).
+ if (TrueState && !FalseState) {
+ SVal ZeroVal = C.getSValBuilder().makeZeroVal(Ctx.CharTy);
+ return MallocMemAux(C, CE, CE->getArg(0), ZeroVal, TrueState);
+ }
+
+ return None;
+}
+
void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
if (C.wasInlined)
return;
-
+
const FunctionDecl *FD = C.getCalleeDecl(CE);
if (!FD)
return;
@@ -584,7 +676,27 @@
initIdentifierInfo(C.getASTContext());
IdentifierInfo *FunI = FD->getIdentifier();
- if (FunI == II_malloc || FunI == II_valloc) {
+ if (FunI == II_malloc) {
+ if (CE->getNumArgs() < 1)
+ return;
+ if (CE->getNumArgs() < 3) {
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+ } else if (CE->getNumArgs() == 3) {
+ llvm::Optional<ProgramStateRef> MaybeState =
+ performKernelMalloc(CE, C, State);
+ if (MaybeState.hasValue())
+ State = MaybeState.getValue();
+ else
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+ }
+ } else if (FunI == II_kmalloc) {
+ llvm::Optional<ProgramStateRef> MaybeState =
+ performKernelMalloc(CE, C, State);
+ if (MaybeState.hasValue())
+ State = MaybeState.getValue();
+ else
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+ } else if (FunI == II_valloc) {
if (CE->getNumArgs() < 1)
return;
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
@@ -620,21 +732,19 @@
}
}
- if (Filter.CMallocOptimistic || Filter.CMismatchedDeallocatorChecker) {
+ if (ChecksEnabled[CK_MallocOptimistic] ||
+ ChecksEnabled[CK_MismatchedDeallocatorChecker]) {
// Check all the attributes, if there are any.
// There can be multiple of these attributes.
if (FD->hasAttrs())
- for (specific_attr_iterator<OwnershipAttr>
- i = FD->specific_attr_begin<OwnershipAttr>(),
- e = FD->specific_attr_end<OwnershipAttr>();
- i != e; ++i) {
- switch ((*i)->getOwnKind()) {
+ for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {
+ switch (I->getOwnKind()) {
case OwnershipAttr::Returns:
- State = MallocMemReturnsAttr(C, CE, *i);
+ State = MallocMemReturnsAttr(C, CE, I);
break;
case OwnershipAttr::Takes:
case OwnershipAttr::Holds:
- State = FreeMemAttr(C, CE, *i);
+ State = FreeMemAttr(C, CE, I);
break;
}
}
@@ -667,7 +777,7 @@
void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE,
CheckerContext &C) const {
- if (!Filter.CNewDeleteChecker)
+ if (!ChecksEnabled[CK_NewDeleteChecker])
if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol())
checkUseAfterFree(Sym, C, DE->getArgument());
@@ -729,10 +839,10 @@
C.addTransition(State);
}
-ProgramStateRef MallocChecker::MallocMemReturnsAttr(CheckerContext &C,
- const CallExpr *CE,
- const OwnershipAttr* Att) {
- if (Att->getModule() != "malloc")
+ProgramStateRef
+MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
+ const OwnershipAttr *Att) const {
+ if (Att->getModule() != II_malloc)
return 0;
OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
@@ -804,8 +914,8 @@
ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
const CallExpr *CE,
- const OwnershipAttr* Att) const {
- if (Att->getModule() != "malloc")
+ const OwnershipAttr *Att) const {
+ if (Att->getModule() != II_malloc)
return 0;
ProgramStateRef State = C.getState();
@@ -909,7 +1019,7 @@
os << "-";
else
os << "+";
- os << Msg->getSelector().getAsString();
+ Msg->getSelector().print(os);
return true;
}
@@ -971,7 +1081,7 @@
// The explicit NULL case, no operation is performed.
ProgramStateRef notNullState, nullState;
- llvm::tie(notNullState, nullState) = State->assume(location);
+ std::tie(notNullState, nullState) = State->assume(location);
if (nullState && !notNullState)
return 0;
@@ -1088,18 +1198,23 @@
RefState::getReleased(Family, ParentExpr));
}
-bool MallocChecker::isTrackedByCurrentChecker(AllocationFamily Family) const {
+Optional<MallocChecker::CheckKind>
+MallocChecker::getCheckIfTracked(AllocationFamily Family) const {
switch (Family) {
case AF_Malloc: {
- if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic)
- return false;
- return true;
+ if (ChecksEnabled[CK_MallocOptimistic]) {
+ return CK_MallocOptimistic;
+ } else if (ChecksEnabled[CK_MallocPessimistic]) {
+ return CK_MallocPessimistic;
+ }
+ return Optional<MallocChecker::CheckKind>();
}
case AF_CXXNew:
case AF_CXXNewArray: {
- if (!Filter.CNewDeleteChecker)
- return false;
- return true;
+ if (ChecksEnabled[CK_NewDeleteChecker]) {
+ return CK_NewDeleteChecker;
+ }
+ return Optional<MallocChecker::CheckKind>();
}
case AF_None: {
llvm_unreachable("no family");
@@ -1108,18 +1223,18 @@
llvm_unreachable("unhandled family");
}
-bool
-MallocChecker::isTrackedByCurrentChecker(CheckerContext &C,
- const Stmt *AllocDeallocStmt) const {
- return isTrackedByCurrentChecker(getAllocationFamily(C, AllocDeallocStmt));
+Optional<MallocChecker::CheckKind>
+MallocChecker::getCheckIfTracked(CheckerContext &C,
+ const Stmt *AllocDeallocStmt) const {
+ return getCheckIfTracked(getAllocationFamily(C, AllocDeallocStmt));
}
-bool MallocChecker::isTrackedByCurrentChecker(CheckerContext &C,
- SymbolRef Sym) const {
+Optional<MallocChecker::CheckKind>
+MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym) const {
const RefState *RS = C.getState()->get<RegionState>(Sym);
assert(RS);
- return isTrackedByCurrentChecker(RS->getAllocationFamily());
+ return getCheckIfTracked(RS->getAllocationFamily());
}
bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) {
@@ -1213,17 +1328,21 @@
SourceRange Range,
const Expr *DeallocExpr) const {
- if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic &&
- !Filter.CNewDeleteChecker)
+ if (!ChecksEnabled[CK_MallocOptimistic] &&
+ !ChecksEnabled[CK_MallocPessimistic] &&
+ !ChecksEnabled[CK_NewDeleteChecker])
return;
- if (!isTrackedByCurrentChecker(C, DeallocExpr))
+ Optional<MallocChecker::CheckKind> CheckKind =
+ getCheckIfTracked(C, DeallocExpr);
+ if (!CheckKind.hasValue())
return;
if (ExplodedNode *N = C.generateSink()) {
- if (!BT_BadFree)
- BT_BadFree.reset(new BugType("Bad free", "Memory Error"));
-
+ if (!BT_BadFree[*CheckKind])
+ BT_BadFree[*CheckKind].reset(
+ new BugType(CheckNames[*CheckKind], "Bad free", "Memory Error"));
+
SmallString<100> buf;
llvm::raw_svector_ostream os(buf);
@@ -1249,7 +1368,7 @@
printExpectedAllocName(os, C, DeallocExpr);
}
- BugReport *R = new BugReport(*BT_BadFree, os.str(), N);
+ BugReport *R = new BugReport(*BT_BadFree[*CheckKind], os.str(), N);
R->markInteresting(MR);
R->addRange(Range);
C.emitReport(R);
@@ -1263,14 +1382,15 @@
SymbolRef Sym,
bool OwnershipTransferred) const {
- if (!Filter.CMismatchedDeallocatorChecker)
+ if (!ChecksEnabled[CK_MismatchedDeallocatorChecker])
return;
if (ExplodedNode *N = C.generateSink()) {
if (!BT_MismatchedDealloc)
- BT_MismatchedDealloc.reset(new BugType("Bad deallocator",
- "Memory Error"));
-
+ BT_MismatchedDealloc.reset(
+ new BugType(CheckNames[CK_MismatchedDeallocatorChecker],
+ "Bad deallocator", "Memory Error"));
+
SmallString<100> buf;
llvm::raw_svector_ostream os(buf);
@@ -1314,19 +1434,23 @@
SourceRange Range, const Expr *DeallocExpr,
const Expr *AllocExpr) const {
- if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic &&
- !Filter.CNewDeleteChecker)
+ if (!ChecksEnabled[CK_MallocOptimistic] &&
+ !ChecksEnabled[CK_MallocPessimistic] &&
+ !ChecksEnabled[CK_NewDeleteChecker])
return;
- if (!isTrackedByCurrentChecker(C, AllocExpr))
+ Optional<MallocChecker::CheckKind> CheckKind =
+ getCheckIfTracked(C, AllocExpr);
+ if (!CheckKind.hasValue())
return;
ExplodedNode *N = C.generateSink();
if (N == NULL)
return;
- if (!BT_OffsetFree)
- BT_OffsetFree.reset(new BugType("Offset free", "Memory Error"));
+ if (!BT_OffsetFree[*CheckKind])
+ BT_OffsetFree[*CheckKind].reset(
+ new BugType(CheckNames[*CheckKind], "Offset free", "Memory Error"));
SmallString<100> buf;
llvm::raw_svector_ostream os(buf);
@@ -1357,7 +1481,7 @@
else
os << "allocated memory";
- BugReport *R = new BugReport(*BT_OffsetFree, os.str(), N);
+ BugReport *R = new BugReport(*BT_OffsetFree[*CheckKind], os.str(), N);
R->markInteresting(MR->getBaseRegion());
R->addRange(Range);
C.emitReport(R);
@@ -1366,18 +1490,21 @@
void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range,
SymbolRef Sym) const {
- if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic &&
- !Filter.CNewDeleteChecker)
+ if (!ChecksEnabled[CK_MallocOptimistic] &&
+ !ChecksEnabled[CK_MallocPessimistic] &&
+ !ChecksEnabled[CK_NewDeleteChecker])
return;
- if (!isTrackedByCurrentChecker(C, Sym))
+ Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
+ if (!CheckKind.hasValue())
return;
if (ExplodedNode *N = C.generateSink()) {
- if (!BT_UseFree)
- BT_UseFree.reset(new BugType("Use-after-free", "Memory Error"));
+ if (!BT_UseFree[*CheckKind])
+ BT_UseFree[*CheckKind].reset(new BugType(
+ CheckNames[*CheckKind], "Use-after-free", "Memory Error"));
- BugReport *R = new BugReport(*BT_UseFree,
+ BugReport *R = new BugReport(*BT_UseFree[*CheckKind],
"Use of memory after it is freed", N);
R->markInteresting(Sym);
@@ -1391,21 +1518,25 @@
bool Released, SymbolRef Sym,
SymbolRef PrevSym) const {
- if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic &&
- !Filter.CNewDeleteChecker)
+ if (!ChecksEnabled[CK_MallocOptimistic] &&
+ !ChecksEnabled[CK_MallocPessimistic] &&
+ !ChecksEnabled[CK_NewDeleteChecker])
return;
- if (!isTrackedByCurrentChecker(C, Sym))
+ Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
+ if (!CheckKind.hasValue())
return;
if (ExplodedNode *N = C.generateSink()) {
- if (!BT_DoubleFree)
- BT_DoubleFree.reset(new BugType("Double free", "Memory Error"));
+ if (!BT_DoubleFree[*CheckKind])
+ BT_DoubleFree[*CheckKind].reset(
+ new BugType(CheckNames[*CheckKind], "Double free", "Memory Error"));
- BugReport *R = new BugReport(*BT_DoubleFree,
- (Released ? "Attempt to free released memory"
- : "Attempt to free non-owned memory"),
- N);
+ BugReport *R =
+ new BugReport(*BT_DoubleFree[*CheckKind],
+ (Released ? "Attempt to free released memory"
+ : "Attempt to free non-owned memory"),
+ N);
R->addRange(Range);
R->markInteresting(Sym);
if (PrevSym)
@@ -1415,6 +1546,30 @@
}
}
+void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const {
+
+ if (!ChecksEnabled[CK_NewDeleteChecker])
+ return;
+
+ Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
+ if (!CheckKind.hasValue())
+ return;
+ assert(*CheckKind == CK_NewDeleteChecker && "invalid check kind");
+
+ if (ExplodedNode *N = C.generateSink()) {
+ if (!BT_DoubleDelete)
+ BT_DoubleDelete.reset(new BugType(CheckNames[CK_NewDeleteChecker],
+ "Double delete", "Memory Error"));
+
+ BugReport *R = new BugReport(*BT_DoubleDelete,
+ "Attempt to delete released memory", N);
+
+ R->markInteresting(Sym);
+ R->addVisitor(new MallocBugVisitor(Sym));
+ C.emitReport(R);
+ }
+}
+
ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
const CallExpr *CE,
bool FreesOnFail) const {
@@ -1451,9 +1606,9 @@
svalBuilder.makeIntValWithPtrWidth(0, false));
ProgramStateRef StatePtrIsNull, StatePtrNotNull;
- llvm::tie(StatePtrIsNull, StatePtrNotNull) = state->assume(PtrEQ);
+ std::tie(StatePtrIsNull, StatePtrNotNull) = state->assume(PtrEQ);
ProgramStateRef StateSizeIsZero, StateSizeNotZero;
- llvm::tie(StateSizeIsZero, StateSizeNotZero) = state->assume(SizeZero);
+ std::tie(StateSizeIsZero, StateSizeNotZero) = state->assume(SizeZero);
// We only assume exceptional states if they are definitely true; if the
// state is under-constrained, assume regular realloc behavior.
bool PrtIsNull = StatePtrIsNull && !StatePtrNotNull;
@@ -1576,31 +1731,34 @@
void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,
CheckerContext &C) const {
- if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic &&
- !Filter.CNewDeleteLeaksChecker)
+ if (!ChecksEnabled[CK_MallocOptimistic] &&
+ !ChecksEnabled[CK_MallocPessimistic] &&
+ !ChecksEnabled[CK_NewDeleteLeaksChecker])
return;
const RefState *RS = C.getState()->get<RegionState>(Sym);
assert(RS && "cannot leak an untracked symbol");
AllocationFamily Family = RS->getAllocationFamily();
- if (!isTrackedByCurrentChecker(Family))
+ Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
+ if (!CheckKind.hasValue())
return;
// Special case for new and new[]; these are controlled by a separate checker
// flag so that they can be selectively disabled.
if (Family == AF_CXXNew || Family == AF_CXXNewArray)
- if (!Filter.CNewDeleteLeaksChecker)
+ if (!ChecksEnabled[CK_NewDeleteLeaksChecker])
return;
assert(N);
- if (!BT_Leak) {
- BT_Leak.reset(new BugType("Memory leak", "Memory Error"));
+ if (!BT_Leak[*CheckKind]) {
+ BT_Leak[*CheckKind].reset(
+ new BugType(CheckNames[*CheckKind], "Memory leak", "Memory Error"));
// Leaks should not be reported if they are post-dominated by a sink:
// (1) Sinks are higher importance bugs.
// (2) NoReturnFunctionChecker uses sink nodes to represent paths ending
// with __noreturn functions such as assert() or exit(). We choose not
// to report leaks on such paths.
- BT_Leak->setSuppressOnSink(true);
+ BT_Leak[*CheckKind]->setSuppressOnSink(true);
}
// Most bug reports are cached at the location where they occurred.
@@ -1609,7 +1767,7 @@
PathDiagnosticLocation LocUsedForUniqueing;
const ExplodedNode *AllocNode = 0;
const MemRegion *Region = 0;
- llvm::tie(AllocNode, Region) = getAllocationSite(N, Sym, C);
+ std::tie(AllocNode, Region) = getAllocationSite(N, Sym, C);
ProgramPoint P = AllocNode->getLocation();
const Stmt *AllocationStmt = 0;
@@ -1631,9 +1789,9 @@
os << "Potential memory leak";
}
- BugReport *R = new BugReport(*BT_Leak, os.str(), N,
- LocUsedForUniqueing,
- AllocNode->getLocationContext()->getDecl());
+ BugReport *R =
+ new BugReport(*BT_Leak[*CheckKind], os.str(), N, LocUsedForUniqueing,
+ AllocNode->getLocationContext()->getDecl());
R->markInteresting(Sym);
R->addVisitor(new MallocBugVisitor(Sym, true));
C.emitReport(R);
@@ -1681,7 +1839,7 @@
// Generate leak node.
ExplodedNode *N = C.getPredecessor();
if (!Errors.empty()) {
- static SimpleProgramPointTag Tag("MallocChecker : DeadSymbolsLeak");
+ static CheckerProgramPointTag Tag("MallocChecker", "DeadSymbolsLeak");
N = C.addTransition(C.getState(), C.getPredecessor(), &Tag);
for (SmallVectorImpl<SymbolRef>::iterator
I = Errors.begin(), E = Errors.end(); I != E; ++I) {
@@ -1695,17 +1853,24 @@
void MallocChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
+ if (const CXXDestructorCall *DC = dyn_cast<CXXDestructorCall>(&Call)) {
+ SymbolRef Sym = DC->getCXXThisVal().getAsSymbol();
+ if (!Sym || checkDoubleDelete(Sym, C))
+ return;
+ }
+
// We will check for double free in the post visit.
if (const AnyFunctionCall *FC = dyn_cast<AnyFunctionCall>(&Call)) {
const FunctionDecl *FD = FC->getDecl();
if (!FD)
return;
- if ((Filter.CMallocOptimistic || Filter.CMallocPessimistic) &&
+ if ((ChecksEnabled[CK_MallocOptimistic] ||
+ ChecksEnabled[CK_MallocPessimistic]) &&
isFreeFunction(FD, C.getASTContext()))
return;
- if (Filter.CNewDeleteChecker &&
+ if (ChecksEnabled[CK_NewDeleteChecker] &&
isStandardNewDelete(FD, C.getASTContext()))
return;
}
@@ -1803,8 +1968,7 @@
bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C,
const Stmt *S) const {
- // FIXME: Handle destructor called from delete more precisely.
- if (isReleased(Sym, C) && S) {
+ if (isReleased(Sym, C)) {
ReportUseAfterFree(C, S->getSourceRange(), Sym);
return true;
}
@@ -1812,6 +1976,15 @@
return false;
}
+bool MallocChecker::checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const {
+
+ if (isReleased(Sym, C)) {
+ ReportDoubleDelete(C, Sym);
+ return true;
+ }
+ return false;
+}
+
// Check if the location is a freed symbolic region.
void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S,
CheckerContext &C) const {
@@ -1869,11 +2042,11 @@
assert(Call);
EscapingSymbol = 0;
- // For now, assume that any C++ call can free memory.
+ // For now, assume that any C++ or block call can free memory.
// TODO: If we want to be more optimistic here, we'll need to make sure that
// regions escape to C++ containers. They seem to do that even now, but for
// mysterious reasons.
- if (!(isa<FunctionCall>(Call) || isa<ObjCMethodCall>(Call)))
+ if (!(isa<SimpleFunctionCall>(Call) || isa<ObjCMethodCall>(Call)))
return true;
// Check Objective-C messages by selector name.
@@ -1909,7 +2082,8 @@
// that the pointers get freed by following the container itself.
if (FirstSlot.startswith("addPointer") ||
FirstSlot.startswith("insertPointer") ||
- FirstSlot.startswith("replacePointer")) {
+ FirstSlot.startswith("replacePointer") ||
+ FirstSlot.equals("valueWithPointer")) {
return true;
}
@@ -1927,7 +2101,7 @@
}
// At this point the only thing left to handle is straight function calls.
- const FunctionDecl *FD = cast<FunctionCall>(Call)->getDecl();
+ const FunctionDecl *FD = cast<SimpleFunctionCall>(Call)->getDecl();
if (!FD)
return true;
@@ -2130,7 +2304,7 @@
StackHint = new StackHintGeneratorForSymbol(Sym,
"Returning; memory was released");
} else if (isRelinquished(RS, RSPrev, S)) {
- Msg = "Memory ownership is transfered";
+ Msg = "Memory ownership is transferred";
StackHint = new StackHintGeneratorForSymbol(Sym, "");
} else if (isReallocFailedCheck(RS, RSPrev, S)) {
Mode = ReallocationFailed;
@@ -2178,11 +2352,17 @@
RegionStateTy RS = State->get<RegionState>();
if (!RS.isEmpty()) {
- Out << Sep << "MallocChecker:" << NL;
+ Out << Sep << "MallocChecker :" << NL;
for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) {
+ const RefState *RefS = State->get<RegionState>(I.getKey());
+ AllocationFamily Family = RefS->getAllocationFamily();
+ Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
+
I.getKey()->dumpToStream(Out);
Out << " : ";
I.getData().dump(Out);
+ if (CheckKind.hasValue())
+ Out << " (" << CheckNames[*CheckKind].getName() << ")";
Out << NL;
}
}
@@ -2190,17 +2370,23 @@
void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {
registerCStringCheckerBasic(mgr);
- mgr.registerChecker<MallocChecker>()->Filter.CNewDeleteLeaksChecker = true;
+ MallocChecker *checker = mgr.registerChecker<MallocChecker>();
+ checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true;
+ checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] =
+ mgr.getCurrentCheckName();
// We currently treat NewDeleteLeaks checker as a subchecker of NewDelete
// checker.
- mgr.registerChecker<MallocChecker>()->Filter.CNewDeleteChecker = true;
+ if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker])
+ checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker] = true;
}
-#define REGISTER_CHECKER(name) \
-void ento::register##name(CheckerManager &mgr) {\
- registerCStringCheckerBasic(mgr); \
- mgr.registerChecker<MallocChecker>()->Filter.C##name = true;\
-}
+#define REGISTER_CHECKER(name) \
+ void ento::register##name(CheckerManager &mgr) { \
+ registerCStringCheckerBasic(mgr); \
+ MallocChecker *checker = mgr.registerChecker<MallocChecker>(); \
+ checker->ChecksEnabled[MallocChecker::CK_##name] = true; \
+ checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \
+ }
REGISTER_CHECKER(MallocPessimistic)
REGISTER_CHECKER(MallocOptimistic)
diff --git a/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
index 0cdf911..8097727 100644
--- a/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
@@ -213,11 +213,12 @@
e = PossibleMallocOverflows.end();
i != e;
++i) {
- BR.EmitBasicReport(D, "malloc() size overflow", categories::UnixAPI,
- "the computation of the size of the memory allocation may overflow",
- PathDiagnosticLocation::createOperatorLoc(i->mulop,
- BR.getSourceManager()),
- i->mulop->getSourceRange());
+ BR.EmitBasicReport(
+ D, this, "malloc() size overflow", categories::UnixAPI,
+ "the computation of the size of the memory allocation may overflow",
+ PathDiagnosticLocation::createOperatorLoc(i->mulop,
+ BR.getSourceManager()),
+ i->mulop->getSourceRange());
}
}
@@ -262,6 +263,7 @@
OutputPossibleOverflows(PossibleMallocOverflows, D, BR, mgr);
}
-void ento::registerMallocOverflowSecurityChecker(CheckerManager &mgr) {
+void
+ento::registerMallocOverflowSecurityChecker(CheckerManager &mgr) {
mgr.registerChecker<MallocOverflowSecurityChecker>();
}
diff --git a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
index 6c776eb..5b068c8 100644
--- a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
@@ -101,9 +101,8 @@
}
TypeCallPair VisitDeclStmt(const DeclStmt *S) {
- for (DeclStmt::const_decl_iterator I = S->decl_begin(), E = S->decl_end();
- I!=E; ++I)
- if (const VarDecl *VD = dyn_cast<VarDecl>(*I))
+ for (const auto *I : S->decls())
+ if (const VarDecl *VD = dyn_cast<VarDecl>(I))
if (const Expr *Init = VD->getInit())
VisitChild(VD, Init);
return TypeCallPair();
@@ -236,10 +235,8 @@
PathDiagnosticLocation::createBegin(i->AllocCall->getCallee(),
BR.getSourceManager(), ADC);
- BR.EmitBasicReport(D, "Allocator sizeof operand mismatch",
- categories::UnixAPI,
- OS.str(),
- L, Ranges);
+ BR.EmitBasicReport(D, this, "Allocator sizeof operand mismatch",
+ categories::UnixAPI, OS.str(), L, Ranges);
}
}
}
diff --git a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
index fc28e1f..b180c03 100644
--- a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
@@ -32,7 +32,7 @@
namespace {
class NSAutoreleasePoolChecker
: public Checker<check::PreObjCMessage> {
- mutable OwningPtr<BugType> BT;
+ mutable std::unique_ptr<BugType> BT;
mutable Selector releaseS;
public:
@@ -59,7 +59,7 @@
return;
if (!BT)
- BT.reset(new BugType("Use -drain instead of -release",
+ BT.reset(new BugType(this, "Use -drain instead of -release",
"API Upgrade (Apple)"));
ExplodedNode *N = C.addTransition();
diff --git a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
index 9f01522..5a505fc 100644
--- a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
@@ -54,16 +54,15 @@
BugReporter &BR) const {
if (!D->isThisDeclarationADefinition())
return;
- if (!D->getResultType()->isVoidType())
+ if (!D->getReturnType()->isVoidType())
return;
if (!II)
II = &D->getASTContext().Idents.get("NSError");
bool hasNSError = false;
- for (ObjCMethodDecl::param_const_iterator
- I = D->param_begin(), E = D->param_end(); I != E; ++I) {
- if (IsNSError((*I)->getType(), II)) {
+ for (const auto *I : D->params()) {
+ if (IsNSError(I->getType(), II)) {
hasNSError = true;
break;
}
@@ -75,7 +74,7 @@
"error occurred";
PathDiagnosticLocation L =
PathDiagnosticLocation::create(D, BR.getSourceManager());
- BR.EmitBasicReport(D, "Bad return type when passing NSError**",
+ BR.EmitBasicReport(D, this, "Bad return type when passing NSError**",
"Coding conventions (Apple)", err, L);
}
}
@@ -102,16 +101,15 @@
BugReporter &BR) const {
if (!D->doesThisDeclarationHaveABody())
return;
- if (!D->getResultType()->isVoidType())
+ if (!D->getReturnType()->isVoidType())
return;
if (!II)
II = &D->getASTContext().Idents.get("CFErrorRef");
bool hasCFError = false;
- for (FunctionDecl::param_const_iterator
- I = D->param_begin(), E = D->param_end(); I != E; ++I) {
- if (IsCFError((*I)->getType(), II)) {
+ for (auto I : D->params()) {
+ if (IsCFError(I->getType(), II)) {
hasCFError = true;
break;
}
@@ -123,7 +121,7 @@
"error occurred";
PathDiagnosticLocation L =
PathDiagnosticLocation::create(D, BR.getSourceManager());
- BR.EmitBasicReport(D, "Bad return type when passing CFErrorRef*",
+ BR.EmitBasicReport(D, this, "Bad return type when passing CFErrorRef*",
"Coding conventions (Apple)", err, L);
}
}
@@ -136,14 +134,16 @@
class NSErrorDerefBug : public BugType {
public:
- NSErrorDerefBug() : BugType("NSError** null dereference",
- "Coding conventions (Apple)") {}
+ NSErrorDerefBug(const CheckerBase *Checker)
+ : BugType(Checker, "NSError** null dereference",
+ "Coding conventions (Apple)") {}
};
class CFErrorDerefBug : public BugType {
public:
- CFErrorDerefBug() : BugType("CFErrorRef* null dereference",
- "Coding conventions (Apple)") {}
+ CFErrorDerefBug(const CheckerBase *Checker)
+ : BugType(Checker, "CFErrorRef* null dereference",
+ "Coding conventions (Apple)") {}
};
}
@@ -264,11 +264,10 @@
BugType *bug = 0;
if (isNSError)
- bug = new NSErrorDerefBug();
+ bug = new NSErrorDerefBug(this);
else
- bug = new CFErrorDerefBug();
- BugReport *report = new BugReport(*bug, os.str(),
- event.SinkNode);
+ bug = new CFErrorDerefBug(this);
+ BugReport *report = new BugReport(*bug, os.str(), event.SinkNode);
BR.emitReport(report);
}
@@ -305,14 +304,14 @@
void ento::registerNSErrorChecker(CheckerManager &mgr) {
mgr.registerChecker<NSErrorMethodChecker>();
- NSOrCFErrorDerefChecker *
- checker = mgr.registerChecker<NSOrCFErrorDerefChecker>();
+ NSOrCFErrorDerefChecker *checker =
+ mgr.registerChecker<NSOrCFErrorDerefChecker>();
checker->ShouldCheckNSError = true;
}
void ento::registerCFErrorChecker(CheckerManager &mgr) {
mgr.registerChecker<CFErrorFunctionChecker>();
- NSOrCFErrorDerefChecker *
- checker = mgr.registerChecker<NSOrCFErrorDerefChecker>();
+ NSOrCFErrorDerefChecker *checker =
+ mgr.registerChecker<NSOrCFErrorDerefChecker>();
checker->ShouldCheckCFError = true;
}
diff --git a/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
index 0e1064e..0e9cbba 100644
--- a/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
@@ -37,11 +37,10 @@
void NoReturnFunctionChecker::checkPostCall(const CallEvent &CE,
CheckerContext &C) const {
- ProgramStateRef state = C.getState();
bool BuildSinks = false;
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CE.getDecl()))
- BuildSinks = FD->getAttr<AnalyzerNoReturnAttr>() || FD->isNoReturn();
+ BuildSinks = FD->hasAttr<AnalyzerNoReturnAttr>() || FD->isNoReturn();
const Expr *Callee = CE.getOriginExpr();
if (!BuildSinks && Callee)
@@ -151,7 +150,6 @@
C.generateSink();
}
-
void ento::registerNoReturnFunctionChecker(CheckerManager &mgr) {
mgr.registerChecker<NoReturnFunctionChecker>();
}
diff --git a/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
index 273a7a3..293114f 100644
--- a/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
@@ -29,8 +29,9 @@
namespace {
class NonNullParamChecker
: public Checker< check::PreCall > {
- mutable OwningPtr<BugType> BTAttrNonNull;
- mutable OwningPtr<BugType> BTNullRefArg;
+ mutable std::unique_ptr<BugType> BTAttrNonNull;
+ mutable std::unique_ptr<BugType> BTNullRefArg;
+
public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
@@ -43,7 +44,7 @@
} // end anonymous namespace
void NonNullParamChecker::checkPreCall(const CallEvent &Call,
- CheckerContext &C) const {
+ CheckerContext &C) const {
const Decl *FD = Call.getDecl();
if (!FD)
return;
@@ -66,6 +67,12 @@
}
bool haveAttrNonNull = Att && Att->isNonNull(idx);
+ if (!haveAttrNonNull) {
+ // Check if the parameter is also marked 'nonnull'.
+ ArrayRef<ParmVarDecl*> parms = Call.parameters();
+ if (idx < parms.size())
+ haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>();
+ }
if (!haveRefTypeParam && !haveAttrNonNull)
continue;
@@ -98,7 +105,9 @@
V = *CSV_I;
DV = V.getAs<DefinedSVal>();
assert(++CSV_I == CSV->end());
- if (!DV)
+ // FIXME: Handle (some_union){ some_other_union_val }, which turns into
+ // a LazyCompoundVal inside a CompoundVal.
+ if (!V.getAs<Loc>())
continue;
// Retrieve the corresponding expression.
if (const CompoundLiteralExpr *CE = dyn_cast<CompoundLiteralExpr>(ArgE))
@@ -114,7 +123,7 @@
ConstraintManager &CM = C.getConstraintManager();
ProgramStateRef stateNotNull, stateNull;
- llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
+ std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
if (stateNull && !stateNotNull) {
// Generate an error node. Check for a null node in case
@@ -156,8 +165,7 @@
// the BugReport is passed to 'EmitWarning'.
if (!BTAttrNonNull)
BTAttrNonNull.reset(new BugType(
- "Argument with 'nonnull' attribute passed null",
- "API"));
+ this, "Argument with 'nonnull' attribute passed null", "API"));
BugReport *R = new BugReport(*BTAttrNonNull,
"Null pointer passed as an argument to a 'nonnull' parameter",
@@ -171,7 +179,7 @@
BugReport *NonNullParamChecker::genReportReferenceToNullPointer(
const ExplodedNode *ErrorNode, const Expr *ArgE) const {
if (!BTNullRefArg)
- BTNullRefArg.reset(new BuiltinBug("Dereference of null pointer"));
+ BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer"));
BugReport *R = new BugReport(*BTNullRefArg,
"Forming reference to null pointer",
diff --git a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
index 4018a66..fbf2d73 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
@@ -26,8 +26,8 @@
namespace {
class ObjCAtSyncChecker
: public Checker< check::PreStmt<ObjCAtSynchronizedStmt> > {
- mutable OwningPtr<BuiltinBug> BT_null;
- mutable OwningPtr<BuiltinBug> BT_undef;
+ mutable std::unique_ptr<BuiltinBug> BT_null;
+ mutable std::unique_ptr<BuiltinBug> BT_undef;
public:
void checkPreStmt(const ObjCAtSynchronizedStmt *S, CheckerContext &C) const;
@@ -45,8 +45,8 @@
if (V.getAs<UndefinedVal>()) {
if (ExplodedNode *N = C.generateSink()) {
if (!BT_undef)
- BT_undef.reset(new BuiltinBug("Uninitialized value used as mutex "
- "for @synchronized"));
+ BT_undef.reset(new BuiltinBug(this, "Uninitialized value used as mutex "
+ "for @synchronized"));
BugReport *report =
new BugReport(*BT_undef, BT_undef->getDescription(), N);
bugreporter::trackNullOrUndefValue(N, Ex, *report);
@@ -60,7 +60,7 @@
// Check for null mutexes.
ProgramStateRef notNullState, nullState;
- llvm::tie(notNullState, nullState) = state->assume(V.castAs<DefinedSVal>());
+ std::tie(notNullState, nullState) = state->assume(V.castAs<DefinedSVal>());
if (nullState) {
if (!notNullState) {
@@ -68,8 +68,9 @@
// a null mutex just means no synchronization occurs.
if (ExplodedNode *N = C.addTransition(nullState)) {
if (!BT_null)
- BT_null.reset(new BuiltinBug("Nil value used as mutex for @synchronized() "
- "(no synchronization will occur)"));
+ BT_null.reset(new BuiltinBug(
+ this, "Nil value used as mutex for @synchronized() "
+ "(no synchronization will occur)"));
BugReport *report =
new BugReport(*BT_null, BT_null->getDescription(), N);
bugreporter::trackNullOrUndefValue(N, Ex, *report);
diff --git a/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
index 503b1b5..239860d 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
@@ -27,6 +27,7 @@
namespace {
class WalkAST : public StmtVisitor<WalkAST> {
BugReporter &BR;
+ const CheckerBase *Checker;
AnalysisDeclContext* AC;
ASTContext &ASTC;
uint64_t PtrWidth;
@@ -71,9 +72,9 @@
}
public:
- WalkAST(BugReporter &br, AnalysisDeclContext* ac)
- : BR(br), AC(ac), ASTC(AC->getASTContext()),
- PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {}
+ WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac)
+ : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()),
+ PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {}
// Statement visitor methods.
void VisitChildren(Stmt *S);
@@ -142,9 +143,9 @@
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
- BR.EmitBasicReport(AC->getDecl(),
- OsName.str(), categories::CoreFoundationObjectiveC,
- Os.str(), CELoc, Arg->getSourceRange());
+ BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(),
+ categories::CoreFoundationObjectiveC, Os.str(), CELoc,
+ Arg->getSourceRange());
}
// Recurse and check children.
@@ -163,7 +164,7 @@
void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
BugReporter &BR) const {
- WalkAST walker(BR, Mgr.getAnalysisDeclContext(D));
+ WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D));
walker.Visit(D->getBody());
}
};
diff --git a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
index b9e96ee..8e51154 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
@@ -30,10 +30,10 @@
namespace {
class ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>,
check::PostStmt<CallExpr> > {
- mutable OwningPtr<BugType> BT;
+ mutable std::unique_ptr<BugType> BT;
inline void initBugType() const {
if (!BT)
- BT.reset(new BugType("CFArray API",
+ BT.reset(new BugType(this, "CFArray API",
categories::CoreFoundationObjectiveC));
}
diff --git a/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
index 789b9f4..a2cf8e1 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
@@ -181,16 +181,12 @@
// Iterate over all instance methods.
- for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(),
- E = D->instmeth_end();
- I != E; ++I) {
- Selector S = (*I)->getSelector();
+ for (auto *MD : D->instance_methods()) {
+ Selector S = MD->getSelector();
// Find out whether this is a selector that we want to check.
if (!SelectorsForClass[SuperclassName].count(S))
continue;
- ObjCMethodDecl *MD = *I;
-
// Check if the method calls its superclass implementation.
if (MD->getBody())
{
@@ -212,7 +208,7 @@
<< "' instance method in " << SuperclassName.str() << " subclass '"
<< *D << "' is missing a [super " << S.getAsString() << "] call";
- BR.EmitBasicReport(MD, Name, categories::CoreFoundationObjectiveC,
+ BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC,
os.str(), DLoc);
}
}
diff --git a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
index 8506e08..6c33084 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
@@ -74,17 +74,17 @@
void checkPostCall(const CallEvent &CE, CheckerContext &C) const;
void printState(raw_ostream &Out, ProgramStateRef State,
- const char *NL, const char *Sep) const;
+ const char *NL, const char *Sep) const override;
};
} // end anonymous namespace
namespace {
class InitSelfBug : public BugType {
- const std::string desc;
public:
- InitSelfBug() : BugType("Missing \"self = [(super or self) init...]\"",
- categories::CoreFoundationObjectiveC) {}
+ InitSelfBug(const CheckerBase *Checker)
+ : BugType(Checker, "Missing \"self = [(super or self) init...]\"",
+ categories::CoreFoundationObjectiveC) {}
};
} // end anonymous namespace
@@ -147,7 +147,8 @@
}
static void checkForInvalidSelf(const Expr *E, CheckerContext &C,
- const char *errorStr) {
+ const char *errorStr,
+ const CheckerBase *Checker) {
if (!E)
return;
@@ -162,8 +163,7 @@
if (!N)
return;
- BugReport *report =
- new BugReport(*new InitSelfBug(), errorStr, N);
+ BugReport *report = new BugReport(*new InitSelfBug(Checker), errorStr, N);
C.emitReport(report);
}
@@ -205,9 +205,11 @@
C.getCurrentAnalysisDeclContext()->getDecl())))
return;
- checkForInvalidSelf(E->getBase(), C,
- "Instance variable used while 'self' is not set to the result of "
- "'[(super or self) init...]'");
+ checkForInvalidSelf(
+ E->getBase(), C,
+ "Instance variable used while 'self' is not set to the result of "
+ "'[(super or self) init...]'",
+ this);
}
void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S,
@@ -218,8 +220,9 @@
return;
checkForInvalidSelf(S->getRetValue(), C,
- "Returning 'self' while it is not set to the result of "
- "'[(super or self) init...]'");
+ "Returning 'self' while it is not set to the result of "
+ "'[(super or self) init...]'",
+ this);
}
// When a call receives a reference to 'self', [Pre/Post]Call pass
@@ -347,7 +350,7 @@
if (FlagMap.isEmpty() && !DidCallInit && !PreCallFlags)
return;
- Out << Sep << NL << "ObjCSelfInitChecker:" << NL;
+ Out << Sep << NL << *this << " :" << NL;
if (DidCallInit)
Out << " An init method has been called." << NL;
diff --git a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
index c66c7d0..d3b1753 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
@@ -77,22 +77,17 @@
static void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) {
// Scan the methods for accesses.
- for (ObjCContainerDecl::instmeth_iterator I = D->instmeth_begin(),
- E = D->instmeth_end(); I!=E; ++I)
- Scan(M, (*I)->getBody());
+ for (const auto *I : D->instance_methods())
+ Scan(M, I->getBody());
if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) {
// Scan for @synthesized property methods that act as setters/getters
// to an ivar.
- for (ObjCImplementationDecl::propimpl_iterator I = ID->propimpl_begin(),
- E = ID->propimpl_end(); I!=E; ++I)
- Scan(M, *I);
+ for (const auto *I : ID->property_impls())
+ Scan(M, I);
// Scan the associated categories as well.
- for (ObjCInterfaceDecl::visible_categories_iterator
- Cat = ID->getClassInterface()->visible_categories_begin(),
- CatEnd = ID->getClassInterface()->visible_categories_end();
- Cat != CatEnd; ++Cat) {
+ for (const auto *Cat : ID->getClassInterface()->visible_categories()) {
if (const ObjCCategoryImplDecl *CID = Cat->getImplementation())
Scan(M, CID);
}
@@ -101,9 +96,8 @@
static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID,
SourceManager &SM) {
- for (DeclContext::decl_iterator I=C->decls_begin(), E=C->decls_end();
- I!=E; ++I)
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*I)) {
+ for (const auto *I : C->decls())
+ if (const auto *FD = dyn_cast<FunctionDecl>(I)) {
SourceLocation L = FD->getLocStart();
if (SM.getFileID(L) == FID)
Scan(M, FD->getBody());
@@ -111,29 +105,26 @@
}
static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
- BugReporter &BR) {
+ BugReporter &BR,
+ const CheckerBase *Checker) {
const ObjCInterfaceDecl *ID = D->getClassInterface();
IvarUsageMap M;
// Iterate over the ivars.
- for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(),
- E=ID->ivar_end(); I!=E; ++I) {
-
- const ObjCIvarDecl *ID = *I;
-
+ for (const auto *Ivar : ID->ivars()) {
// Ignore ivars that...
// (a) aren't private
// (b) explicitly marked unused
// (c) are iboutlets
// (d) are unnamed bitfields
- if (ID->getAccessControl() != ObjCIvarDecl::Private ||
- ID->getAttr<UnusedAttr>() || ID->getAttr<IBOutletAttr>() ||
- ID->getAttr<IBOutletCollectionAttr>() ||
- ID->isUnnamedBitfield())
+ if (Ivar->getAccessControl() != ObjCIvarDecl::Private ||
+ Ivar->hasAttr<UnusedAttr>() || Ivar->hasAttr<IBOutletAttr>() ||
+ Ivar->hasAttr<IBOutletCollectionAttr>() ||
+ Ivar->isUnnamedBitfield())
continue;
- M[ID] = Unused;
+ M[Ivar] = Unused;
}
if (M.empty())
@@ -172,7 +163,7 @@
PathDiagnosticLocation L =
PathDiagnosticLocation::create(I->first, BR.getSourceManager());
- BR.EmitBasicReport(D, "Unused instance variable", "Optimization",
+ BR.EmitBasicReport(D, Checker, "Unused instance variable", "Optimization",
os.str(), L);
}
}
@@ -187,7 +178,7 @@
public:
void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
BugReporter &BR) const {
- checkObjCUnusedIvar(D, BR);
+ checkObjCUnusedIvar(D, BR, this);
}
};
}
diff --git a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
index bcbfacd..00480e4 100644
--- a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
@@ -24,7 +24,7 @@
namespace {
class PointerArithChecker
: public Checker< check::PreStmt<BinaryOperator> > {
- mutable OwningPtr<BuiltinBug> BT;
+ mutable std::unique_ptr<BuiltinBug> BT;
public:
void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
@@ -53,10 +53,11 @@
if (ExplodedNode *N = C.addTransition()) {
if (!BT)
- BT.reset(new BuiltinBug("Dangerous pointer arithmetic",
- "Pointer arithmetic done on non-array variables "
- "means reliance on memory layout, which is "
- "dangerous."));
+ BT.reset(
+ new BuiltinBug(this, "Dangerous pointer arithmetic",
+ "Pointer arithmetic done on non-array variables "
+ "means reliance on memory layout, which is "
+ "dangerous."));
BugReport *R = new BugReport(*BT, BT->getDescription(), N);
R->addRange(B->getSourceRange());
C.emitReport(R);
diff --git a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
index 07c82d4..fbb2628 100644
--- a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
@@ -25,7 +25,7 @@
namespace {
class PointerSubChecker
: public Checker< check::PreStmt<BinaryOperator> > {
- mutable OwningPtr<BuiltinBug> BT;
+ mutable std::unique_ptr<BuiltinBug> BT;
public:
void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
@@ -62,9 +62,10 @@
if (ExplodedNode *N = C.addTransition()) {
if (!BT)
- BT.reset(new BuiltinBug("Pointer subtraction",
- "Subtraction of two pointers that do not point to "
- "the same memory chunk may cause incorrect result."));
+ BT.reset(
+ new BuiltinBug(this, "Pointer subtraction",
+ "Subtraction of two pointers that do not point to "
+ "the same memory chunk may cause incorrect result."));
BugReport *R = new BugReport(*BT, BT->getDescription(), N);
R->addRange(B->getSourceRange());
C.emitReport(R);
diff --git a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index ffb8cf2..1ede3a2 100644
--- a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -24,9 +24,37 @@
using namespace ento;
namespace {
+
+struct LockState {
+ enum Kind { Destroyed, Locked, Unlocked } K;
+
+private:
+ LockState(Kind K) : K(K) {}
+
+public:
+ static LockState getLocked(void) { return LockState(Locked); }
+ static LockState getUnlocked(void) { return LockState(Unlocked); }
+ static LockState getDestroyed(void) { return LockState(Destroyed); }
+
+ bool operator==(const LockState &X) const {
+ return K == X.K;
+ }
+
+ bool isLocked() const { return K == Locked; }
+ bool isUnlocked() const { return K == Unlocked; }
+ bool isDestroyed() const { return K == Destroyed; }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(K);
+ }
+};
+
class PthreadLockChecker : public Checker< check::PostStmt<CallExpr> > {
- mutable OwningPtr<BugType> BT_doublelock;
- mutable OwningPtr<BugType> BT_lor;
+ mutable std::unique_ptr<BugType> BT_doublelock;
+ mutable std::unique_ptr<BugType> BT_doubleunlock;
+ mutable std::unique_ptr<BugType> BT_destroylock;
+ mutable std::unique_ptr<BugType> BT_initlock;
+ mutable std::unique_ptr<BugType> BT_lor;
enum LockingSemantics {
NotApplicable = 0,
PthreadSemantics,
@@ -39,12 +67,16 @@
bool isTryLock, enum LockingSemantics semantics) const;
void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const;
+ void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
+ void InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
+ void reportUseDestroyedBug(CheckerContext &C, const CallExpr *CE) const;
};
} // end anonymous namespace
// GDM Entry for tracking lock state.
REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *)
+REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState)
void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
CheckerContext &C) const {
@@ -54,7 +86,7 @@
if (FName.empty())
return;
- if (CE->getNumArgs() != 1)
+ if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
return;
if (FName == "pthread_mutex_lock" ||
@@ -69,7 +101,7 @@
false, XNUSemantics);
else if (FName == "pthread_mutex_trylock" ||
FName == "pthread_rwlock_tryrdlock" ||
- FName == "pthread_rwlock_tryrwlock")
+ FName == "pthread_rwlock_trywrlock")
AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
true, PthreadSemantics);
else if (FName == "lck_mtx_try_lock" ||
@@ -82,6 +114,11 @@
FName == "lck_mtx_unlock" ||
FName == "lck_rw_done")
ReleaseLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
+ else if (FName == "pthread_mutex_destroy" ||
+ FName == "lck_mtx_destroy")
+ DestroyLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
+ else if (FName == "pthread_mutex_init")
+ InitLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
}
void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
@@ -100,18 +137,24 @@
DefinedSVal retVal = X.castAs<DefinedSVal>();
- if (state->contains<LockSet>(lockR)) {
- if (!BT_doublelock)
- BT_doublelock.reset(new BugType("Double locking", "Lock checker"));
- ExplodedNode *N = C.generateSink();
- if (!N)
+ if (const LockState *LState = state->get<LockMap>(lockR)) {
+ if (LState->isLocked()) {
+ if (!BT_doublelock)
+ BT_doublelock.reset(new BugType(this, "Double locking",
+ "Lock checker"));
+ ExplodedNode *N = C.generateSink();
+ if (!N)
+ return;
+ BugReport *report = new BugReport(*BT_doublelock,
+ "This lock has already been acquired",
+ N);
+ report->addRange(CE->getArg(0)->getSourceRange());
+ C.emitReport(report);
return;
- BugReport *report = new BugReport(*BT_doublelock,
- "This lock has already "
- "been acquired", N);
- report->addRange(CE->getArg(0)->getSourceRange());
- C.emitReport(report);
- return;
+ } else if (LState->isDestroyed()) {
+ reportUseDestroyedBug(C, CE);
+ return;
+ }
}
ProgramStateRef lockSucc = state;
@@ -120,10 +163,10 @@
ProgramStateRef lockFail;
switch (semantics) {
case PthreadSemantics:
- llvm::tie(lockFail, lockSucc) = state->assume(retVal);
+ std::tie(lockFail, lockSucc) = state->assume(retVal);
break;
case XNUSemantics:
- llvm::tie(lockSucc, lockFail) = state->assume(retVal);
+ std::tie(lockSucc, lockFail) = state->assume(retVal);
break;
default:
llvm_unreachable("Unknown tryLock locking semantics");
@@ -144,6 +187,7 @@
// Record that the lock was acquired.
lockSucc = lockSucc->add<LockSet>(lockR);
+ lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked());
C.addTransition(lockSucc);
}
@@ -155,35 +199,140 @@
return;
ProgramStateRef state = C.getState();
+
+ if (const LockState *LState = state->get<LockMap>(lockR)) {
+ if (LState->isUnlocked()) {
+ if (!BT_doubleunlock)
+ BT_doubleunlock.reset(new BugType(this, "Double unlocking",
+ "Lock checker"));
+ ExplodedNode *N = C.generateSink();
+ if (!N)
+ return;
+ BugReport *Report = new BugReport(*BT_doubleunlock,
+ "This lock has already been unlocked",
+ N);
+ Report->addRange(CE->getArg(0)->getSourceRange());
+ C.emitReport(Report);
+ return;
+ } else if (LState->isDestroyed()) {
+ reportUseDestroyedBug(C, CE);
+ return;
+ }
+ }
+
LockSetTy LS = state->get<LockSet>();
// FIXME: Better analysis requires IPA for wrappers.
- // FIXME: check for double unlocks
- if (LS.isEmpty())
- return;
-
- const MemRegion *firstLockR = LS.getHead();
- if (firstLockR != lockR) {
- if (!BT_lor)
- BT_lor.reset(new BugType("Lock order reversal", "Lock checker"));
- ExplodedNode *N = C.generateSink();
- if (!N)
+
+ if (!LS.isEmpty()) {
+ const MemRegion *firstLockR = LS.getHead();
+ if (firstLockR != lockR) {
+ if (!BT_lor)
+ BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker"));
+ ExplodedNode *N = C.generateSink();
+ if (!N)
+ return;
+ BugReport *report = new BugReport(*BT_lor,
+ "This was not the most recently "
+ "acquired lock. Possible lock order "
+ "reversal",
+ N);
+ report->addRange(CE->getArg(0)->getSourceRange());
+ C.emitReport(report);
return;
- BugReport *report = new BugReport(*BT_lor,
- "This was not the most "
- "recently acquired lock. "
- "Possible lock order "
- "reversal", N);
- report->addRange(CE->getArg(0)->getSourceRange());
- C.emitReport(report);
- return;
+ }
+ // Record that the lock was released.
+ state = state->set<LockSet>(LS.getTail());
}
- // Record that the lock was released.
- state = state->set<LockSet>(LS.getTail());
+ state = state->set<LockMap>(lockR, LockState::getUnlocked());
C.addTransition(state);
}
+void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE,
+ SVal Lock) const {
+
+ const MemRegion *LockR = Lock.getAsRegion();
+ if (!LockR)
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ const LockState *LState = State->get<LockMap>(LockR);
+ if (!LState || LState->isUnlocked()) {
+ State = State->set<LockMap>(LockR, LockState::getDestroyed());
+ C.addTransition(State);
+ return;
+ }
+
+ StringRef Message;
+
+ if (LState->isLocked()) {
+ Message = "This lock is still locked";
+ } else {
+ Message = "This lock has already been destroyed";
+ }
+
+ if (!BT_destroylock)
+ BT_destroylock.reset(new BugType(this, "Destroy invalid lock",
+ "Lock checker"));
+ ExplodedNode *N = C.generateSink();
+ if (!N)
+ return;
+ BugReport *Report = new BugReport(*BT_destroylock, Message, N);
+ Report->addRange(CE->getArg(0)->getSourceRange());
+ C.emitReport(Report);
+}
+
+void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE,
+ SVal Lock) const {
+
+ const MemRegion *LockR = Lock.getAsRegion();
+ if (!LockR)
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ const struct LockState *LState = State->get<LockMap>(LockR);
+ if (!LState || LState->isDestroyed()) {
+ State = State->set<LockMap>(LockR, LockState::getUnlocked());
+ C.addTransition(State);
+ return;
+ }
+
+ StringRef Message;
+
+ if (LState->isLocked()) {
+ Message = "This lock is still being held";
+ } else {
+ Message = "This lock has already been initialized";
+ }
+
+ if (!BT_initlock)
+ BT_initlock.reset(new BugType(this, "Init invalid lock",
+ "Lock checker"));
+ ExplodedNode *N = C.generateSink();
+ if (!N)
+ return;
+ BugReport *Report = new BugReport(*BT_initlock, Message, N);
+ Report->addRange(CE->getArg(0)->getSourceRange());
+ C.emitReport(Report);
+}
+
+void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C,
+ const CallExpr *CE) const {
+ if (!BT_destroylock)
+ BT_destroylock.reset(new BugType(this, "Use destroyed lock",
+ "Lock checker"));
+ ExplodedNode *N = C.generateSink();
+ if (!N)
+ return;
+ BugReport *Report = new BugReport(*BT_destroylock,
+ "This lock has already been destroyed",
+ N);
+ Report->addRange(CE->getArg(0)->getSourceRange());
+ C.emitReport(Report);
+}
void ento::registerPthreadLockChecker(CheckerManager &mgr) {
mgr.registerChecker<PthreadLockChecker>();
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
index c474e78..9ac9931 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
+#include "AllocationDiagnostics.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
@@ -20,6 +21,7 @@
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/StaticAnalyzer/Checkers/ObjCRetainCount.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
@@ -28,7 +30,6 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
-#include "clang/StaticAnalyzer/Checkers/ObjCRetainCount.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableList.h"
@@ -38,8 +39,6 @@
#include "llvm/ADT/StringExtras.h"
#include <cstdarg>
-#include "AllocationDiagnostics.h"
-
using namespace clang;
using namespace ento;
using namespace objc_retain;
@@ -95,29 +94,70 @@
};
private:
- Kind kind;
- RetEffect::ObjKind okind;
+ /// The number of outstanding retains.
unsigned Cnt;
+ /// The number of outstanding autoreleases.
unsigned ACnt;
+ /// The (static) type of the object at the time we started tracking it.
QualType T;
- RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t)
- : kind(k), okind(o), Cnt(cnt), ACnt(acnt), T(t) {}
+ /// The current state of the object.
+ ///
+ /// See the RefVal::Kind enum for possible values.
+ unsigned RawKind : 5;
+
+ /// The kind of object being tracked (CF or ObjC), if known.
+ ///
+ /// See the RetEffect::ObjKind enum for possible values.
+ unsigned RawObjectKind : 2;
+
+ /// True if the current state and/or retain count may turn out to not be the
+ /// best possible approximation of the reference counting state.
+ ///
+ /// If true, the checker may decide to throw away ("override") this state
+ /// in favor of something else when it sees the object being used in new ways.
+ ///
+ /// This setting should not be propagated to state derived from this state.
+ /// Once we start deriving new states, it would be inconsistent to override
+ /// them.
+ unsigned IsOverridable : 1;
+
+ RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t,
+ bool Overridable = false)
+ : Cnt(cnt), ACnt(acnt), T(t), RawKind(static_cast<unsigned>(k)),
+ RawObjectKind(static_cast<unsigned>(o)), IsOverridable(Overridable) {
+ assert(getKind() == k && "not enough bits for the kind");
+ assert(getObjKind() == o && "not enough bits for the object kind");
+ }
public:
- Kind getKind() const { return kind; }
+ Kind getKind() const { return static_cast<Kind>(RawKind); }
- RetEffect::ObjKind getObjKind() const { return okind; }
+ RetEffect::ObjKind getObjKind() const {
+ return static_cast<RetEffect::ObjKind>(RawObjectKind);
+ }
unsigned getCount() const { return Cnt; }
unsigned getAutoreleaseCount() const { return ACnt; }
unsigned getCombinedCounts() const { return Cnt + ACnt; }
- void clearCounts() { Cnt = 0; ACnt = 0; }
- void setCount(unsigned i) { Cnt = i; }
- void setAutoreleaseCount(unsigned i) { ACnt = i; }
+ void clearCounts() {
+ Cnt = 0;
+ ACnt = 0;
+ IsOverridable = false;
+ }
+ void setCount(unsigned i) {
+ Cnt = i;
+ IsOverridable = false;
+ }
+ void setAutoreleaseCount(unsigned i) {
+ ACnt = i;
+ IsOverridable = false;
+ }
QualType getType() const { return T; }
+ bool isOverridable() const { return IsOverridable; }
+
bool isOwned() const {
return getKind() == Owned;
}
@@ -134,20 +174,31 @@
return getKind() == ReturnedNotOwned;
}
+ /// Create a state for an object whose lifetime is the responsibility of the
+ /// current function, at least partially.
+ ///
+ /// Most commonly, this is an owned object with a retain count of +1.
static RefVal makeOwned(RetEffect::ObjKind o, QualType t,
unsigned Count = 1) {
return RefVal(Owned, o, Count, 0, t);
}
+ /// Create a state for an object whose lifetime is not the responsibility of
+ /// the current function.
+ ///
+ /// Most commonly, this is an unowned object with a retain count of +0.
static RefVal makeNotOwned(RetEffect::ObjKind o, QualType t,
unsigned Count = 0) {
return RefVal(NotOwned, o, Count, 0, t);
}
- // Comparison, profiling, and pretty-printing.
-
- bool operator==(const RefVal& X) const {
- return kind == X.kind && Cnt == X.Cnt && T == X.T && ACnt == X.ACnt;
+ /// Create an "overridable" state for an unowned object at +0.
+ ///
+ /// An overridable state is one that provides a good approximation of the
+ /// reference counting state now, but which may be discarded later if the
+ /// checker sees the object being used in new ways.
+ static RefVal makeOverridableNotOwned(RetEffect::ObjKind o, QualType t) {
+ return RefVal(NotOwned, o, 0, 0, t, /*Overridable=*/true);
}
RefVal operator-(size_t i) const {
@@ -170,11 +221,24 @@
getType());
}
+ // Comparison, profiling, and pretty-printing.
+
+ bool hasSameState(const RefVal &X) const {
+ return getKind() == X.getKind() && Cnt == X.Cnt && ACnt == X.ACnt;
+ }
+
+ bool operator==(const RefVal& X) const {
+ return T == X.T && hasSameState(X) && getObjKind() == X.getObjKind() &&
+ IsOverridable == X.IsOverridable;
+ }
+
void Profile(llvm::FoldingSetNodeID& ID) const {
- ID.AddInteger((unsigned) kind);
+ ID.Add(T);
+ ID.AddInteger(RawKind);
ID.AddInteger(Cnt);
ID.AddInteger(ACnt);
- ID.Add(T);
+ ID.AddInteger(RawObjectKind);
+ ID.AddBoolean(IsOverridable);
}
void print(raw_ostream &Out) const;
@@ -184,6 +248,9 @@
if (!T.isNull())
Out << "Tracked " << T.getAsString() << '/';
+ if (isOverridable())
+ Out << "(overridable) ";
+
switch (getKind()) {
default: llvm_unreachable("Invalid RefVal kind");
case Owned: {
@@ -653,11 +720,11 @@
AF(BPAlloc), ScratchArgs(AF.getEmptyMap()),
ObjCAllocRetE(gcenabled
? RetEffect::MakeGCNotOwned()
- : (usesARC ? RetEffect::MakeARCNotOwned()
+ : (usesARC ? RetEffect::MakeNotOwned(RetEffect::ObjC)
: RetEffect::MakeOwned(RetEffect::ObjC, true))),
ObjCInitRetE(gcenabled
? RetEffect::MakeGCNotOwned()
- : (usesARC ? RetEffect::MakeARCNotOwned()
+ : (usesARC ? RetEffect::MakeNotOwned(RetEffect::ObjC)
: RetEffect::MakeOwnedWhenTrackedReceiver())) {
InitializeClassMethodSummaries();
InitializeMethodSummaries();
@@ -689,7 +756,7 @@
const RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) {
const ObjCInterfaceDecl *ID = MD->getClassInterface();
Selector S = MD->getSelector();
- QualType ResultTy = MD->getResultType();
+ QualType ResultTy = MD->getReturnType();
ObjCMethodSummariesTy *CachedSummaries;
if (MD->isInstanceMethod())
@@ -861,7 +928,7 @@
// Special cases where the callback argument CANNOT free the return value.
// This can generally only happen if we know that the callback will only be
// called when the return value is already being deallocated.
- if (const FunctionCall *FC = dyn_cast<FunctionCall>(&Call)) {
+ if (const SimpleFunctionCall *FC = dyn_cast<SimpleFunctionCall>(&Call)) {
if (IdentifierInfo *Name = FC->getDecl()->getIdentifier()) {
// When the CGBitmapContext is deallocated, the callback here will free
// the associated data buffer.
@@ -909,7 +976,7 @@
const RetainSummary *Summ;
switch (Call.getKind()) {
case CE_Function:
- Summ = getFunctionSummary(cast<FunctionCall>(Call).getDecl());
+ Summ = getFunctionSummary(cast<SimpleFunctionCall>(Call).getDecl());
break;
case CE_CXXMember:
case CE_CXXMemberOperator:
@@ -971,7 +1038,7 @@
FName = FName.substr(FName.find_first_not_of('_'));
// Inspect the result type.
- QualType RetTy = FT->getResultType();
+ QualType RetTy = FT->getReturnType();
// FIXME: This should all be refactored into a chain of "summary lookup"
// filters.
@@ -1102,7 +1169,7 @@
break;
}
- if (FD->getAttr<CFAuditedTransferAttr>()) {
+ if (FD->hasAttr<CFAuditedTransferAttr>()) {
S = getCFCreateGetRuleSummary(FD);
break;
}
@@ -1175,7 +1242,7 @@
// Sanity check that this is *really* a unary function. This can
// happen if people do weird things.
const FunctionProtoType* FTP = dyn_cast<FunctionProtoType>(FT);
- if (!FTP || FTP->getNumArgs() != 1)
+ if (!FTP || FTP->getNumParams() != 1)
return getPersistentStopSummary();
assert (ScratchArgs.isEmpty());
@@ -1214,21 +1281,21 @@
RetainSummaryManager::getRetEffectFromAnnotations(QualType RetTy,
const Decl *D) {
if (cocoa::isCocoaObjectRef(RetTy)) {
- if (D->getAttr<NSReturnsRetainedAttr>())
+ if (D->hasAttr<NSReturnsRetainedAttr>())
return ObjCAllocRetE;
- if (D->getAttr<NSReturnsNotRetainedAttr>() ||
- D->getAttr<NSReturnsAutoreleasedAttr>())
+ if (D->hasAttr<NSReturnsNotRetainedAttr>() ||
+ D->hasAttr<NSReturnsAutoreleasedAttr>())
return RetEffect::MakeNotOwned(RetEffect::ObjC);
} else if (!RetTy->isPointerType()) {
return None;
}
- if (D->getAttr<CFReturnsRetainedAttr>())
+ if (D->hasAttr<CFReturnsRetainedAttr>())
return RetEffect::MakeOwned(RetEffect::CF, true);
- if (D->getAttr<CFReturnsNotRetainedAttr>())
+ if (D->hasAttr<CFReturnsNotRetainedAttr>())
return RetEffect::MakeNotOwned(RetEffect::CF);
return None;
@@ -1248,13 +1315,13 @@
for (FunctionDecl::param_const_iterator pi = FD->param_begin(),
pe = FD->param_end(); pi != pe; ++pi, ++parm_idx) {
const ParmVarDecl *pd = *pi;
- if (pd->getAttr<NSConsumedAttr>())
+ if (pd->hasAttr<NSConsumedAttr>())
Template->addArg(AF, parm_idx, DecRefMsg);
- else if (pd->getAttr<CFConsumedAttr>())
+ else if (pd->hasAttr<CFConsumedAttr>())
Template->addArg(AF, parm_idx, DecRef);
}
-
- QualType RetTy = FD->getResultType();
+
+ QualType RetTy = FD->getReturnType();
if (Optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, FD))
Template->setRetEffect(*RetE);
}
@@ -1269,7 +1336,7 @@
RetainSummaryTemplate Template(Summ, *this);
// Effects on the receiver.
- if (MD->getAttr<NSConsumesSelfAttr>())
+ if (MD->hasAttr<NSConsumesSelfAttr>())
Template->setReceiverEffect(DecRefMsg);
// Effects on the parameters.
@@ -1278,14 +1345,14 @@
pi=MD->param_begin(), pe=MD->param_end();
pi != pe; ++pi, ++parm_idx) {
const ParmVarDecl *pd = *pi;
- if (pd->getAttr<NSConsumedAttr>())
+ if (pd->hasAttr<NSConsumedAttr>())
Template->addArg(AF, parm_idx, DecRefMsg);
- else if (pd->getAttr<CFConsumedAttr>()) {
+ else if (pd->hasAttr<CFConsumedAttr>()) {
Template->addArg(AF, parm_idx, DecRef);
}
}
-
- QualType RetTy = MD->getResultType();
+
+ QualType RetTy = MD->getReturnType();
if (Optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, MD))
Template->setRetEffect(*RetE);
}
@@ -1507,6 +1574,11 @@
// as for NSWindow objects.
addClassMethSummary("NSPanel", "alloc", NoTrackYet);
+ // For NSNull, objects returned by +null are singletons that ignore
+ // retain/release semantics. Just don't track them.
+ // <rdar://problem/12858915>
+ addClassMethSummary("NSNull", "null", NoTrackYet);
+
// Don't track allocated autorelease pools, as it is okay to prematurely
// exit a method.
addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet);
@@ -1543,8 +1615,9 @@
class CFRefBug : public BugType {
protected:
- CFRefBug(StringRef name)
- : BugType(name, categories::MemoryCoreFoundationObjectiveC) {}
+ CFRefBug(const CheckerBase *checker, StringRef name)
+ : BugType(checker, name, categories::MemoryCoreFoundationObjectiveC) {}
+
public:
// FIXME: Eventually remove.
@@ -1555,18 +1628,19 @@
class UseAfterRelease : public CFRefBug {
public:
- UseAfterRelease() : CFRefBug("Use-after-release") {}
+ UseAfterRelease(const CheckerBase *checker)
+ : CFRefBug(checker, "Use-after-release") {}
- const char *getDescription() const {
+ const char *getDescription() const override {
return "Reference-counted object is used after it is released";
}
};
class BadRelease : public CFRefBug {
public:
- BadRelease() : CFRefBug("Bad release") {}
+ BadRelease(const CheckerBase *checker) : CFRefBug(checker, "Bad release") {}
- const char *getDescription() const {
+ const char *getDescription() const override {
return "Incorrect decrement of the reference count of an object that is "
"not owned at this point by the caller";
}
@@ -1574,40 +1648,40 @@
class DeallocGC : public CFRefBug {
public:
- DeallocGC()
- : CFRefBug("-dealloc called while using garbage collection") {}
+ DeallocGC(const CheckerBase *checker)
+ : CFRefBug(checker, "-dealloc called while using garbage collection") {}
- const char *getDescription() const {
+ const char *getDescription() const override {
return "-dealloc called while using garbage collection";
}
};
class DeallocNotOwned : public CFRefBug {
public:
- DeallocNotOwned()
- : CFRefBug("-dealloc sent to non-exclusively owned object") {}
+ DeallocNotOwned(const CheckerBase *checker)
+ : CFRefBug(checker, "-dealloc sent to non-exclusively owned object") {}
- const char *getDescription() const {
+ const char *getDescription() const override {
return "-dealloc sent to object that may be referenced elsewhere";
}
};
class OverAutorelease : public CFRefBug {
public:
- OverAutorelease()
- : CFRefBug("Object autoreleased too many times") {}
+ OverAutorelease(const CheckerBase *checker)
+ : CFRefBug(checker, "Object autoreleased too many times") {}
- const char *getDescription() const {
+ const char *getDescription() const override {
return "Object autoreleased too many times";
}
};
class ReturnedNotOwnedForOwned : public CFRefBug {
public:
- ReturnedNotOwnedForOwned()
- : CFRefBug("Method should return an owned object") {}
+ ReturnedNotOwnedForOwned(const CheckerBase *checker)
+ : CFRefBug(checker, "Method should return an owned object") {}
- const char *getDescription() const {
+ const char *getDescription() const override {
return "Object with a +0 retain count returned to caller where a +1 "
"(owning) retain count is expected";
}
@@ -1615,15 +1689,14 @@
class Leak : public CFRefBug {
public:
- Leak(StringRef name)
- : CFRefBug(name) {
+ Leak(const CheckerBase *checker, StringRef name) : CFRefBug(checker, name) {
// Leaks should not be reported if they are post-dominated by a sink.
setSuppressOnSink(true);
}
- const char *getDescription() const { return ""; }
+ const char *getDescription() const override { return ""; }
- bool isLeak() const { return true; }
+ bool isLeak() const override { return true; }
};
//===---------===//
@@ -1640,20 +1713,20 @@
CFRefReportVisitor(SymbolRef sym, bool gcEnabled, const SummaryLogTy &log)
: Sym(sym), SummaryLog(log), GCEnabled(gcEnabled) {}
- virtual void Profile(llvm::FoldingSetNodeID &ID) const {
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
static int x = 0;
ID.AddPointer(&x);
ID.AddPointer(Sym);
}
- virtual PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
- const ExplodedNode *PrevN,
- BugReporterContext &BRC,
- BugReport &BR);
+ PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
- virtual PathDiagnosticPiece *getEndPath(BugReporterContext &BRC,
- const ExplodedNode *N,
- BugReport &BR);
+ PathDiagnosticPiece *getEndPath(BugReporterContext &BRC,
+ const ExplodedNode *N,
+ BugReport &BR) override;
};
class CFRefLeakReportVisitor : public CFRefReportVisitor {
@@ -1664,9 +1737,9 @@
PathDiagnosticPiece *getEndPath(BugReporterContext &BRC,
const ExplodedNode *N,
- BugReport &BR);
+ BugReport &BR) override;
- virtual BugReporterVisitor *clone() const {
+ BugReporterVisitor *clone() const override {
// The curiously-recurring template pattern only works for one level of
// subclassing. Rather than make a new template base for
// CFRefReportVisitor, we simply override clone() to do the right thing.
@@ -1697,7 +1770,7 @@
addGCModeDescription(LOpts, GCEnabled);
}
- virtual std::pair<ranges_iterator, ranges_iterator> getRanges() {
+ std::pair<ranges_iterator, ranges_iterator> getRanges() override {
const CFRefBug& BugTy = static_cast<CFRefBug&>(getBugType());
if (!BugTy.isLeak())
return BugReport::getRanges();
@@ -1714,7 +1787,7 @@
CheckerContext &Ctx,
bool IncludeAllocationLine);
- PathDiagnosticLocation getLocation(const SourceManager &SM) const {
+ PathDiagnosticLocation getLocation(const SourceManager &SM) const override {
assert(Location.isValid());
return Location;
}
@@ -1918,7 +1991,7 @@
if (!GCEnabled && std::find(AEffects.begin(), AEffects.end(), Dealloc) !=
AEffects.end()) {
// Determine if the object's reference count was pushed to zero.
- assert(!(PrevV == CurrV) && "The typestate *must* have changed.");
+ assert(!PrevV.hasSameState(CurrV) && "The state should have changed.");
// We may not have transitioned to 'release' if we hit an error.
// This case is handled elsewhere.
if (CurrV.getKind() == RefVal::Released) {
@@ -1939,7 +2012,7 @@
if (GCEnabled) {
// Determine if the object's reference count was pushed to zero.
- assert(!(PrevV == CurrV) && "The typestate *must* have changed.");
+ assert(!PrevV.hasSameState(CurrV) && "The state should have changed.");
os << "In GC mode a call to '" << *FD
<< "' decrements an object's retain count and registers the "
@@ -1964,7 +2037,7 @@
}
// Determine if the typestate has changed.
- if (!(PrevV == CurrV))
+ if (!PrevV.hasSameState(CurrV))
switch (CurrV.getKind()) {
case RefVal::Owned:
case RefVal::NotOwned:
@@ -2212,9 +2285,9 @@
os << (isa<ObjCMethodDecl>(D) ? " is returned from a method "
: " is returned from a function ");
- if (D->getAttr<CFReturnsNotRetainedAttr>())
+ if (D->hasAttr<CFReturnsNotRetainedAttr>())
os << "that is annotated as CF_RETURNS_NOT_RETAINED";
- else if (D->getAttr<NSReturnsNotRetainedAttr>())
+ else if (D->hasAttr<NSReturnsNotRetainedAttr>())
os << "that is annotated as NS_RETURNS_NOT_RETAINED";
else {
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
@@ -2328,24 +2401,25 @@
check::PostStmt<ObjCArrayLiteral>,
check::PostStmt<ObjCDictionaryLiteral>,
check::PostStmt<ObjCBoxedExpr>,
+ check::PostStmt<ObjCIvarRefExpr>,
check::PostCall,
check::PreStmt<ReturnStmt>,
check::RegionChanges,
eval::Assume,
eval::Call > {
- mutable OwningPtr<CFRefBug> useAfterRelease, releaseNotOwned;
- mutable OwningPtr<CFRefBug> deallocGC, deallocNotOwned;
- mutable OwningPtr<CFRefBug> overAutorelease, returnNotOwnedForOwned;
- mutable OwningPtr<CFRefBug> leakWithinFunction, leakAtReturn;
- mutable OwningPtr<CFRefBug> leakWithinFunctionGC, leakAtReturnGC;
+ mutable std::unique_ptr<CFRefBug> useAfterRelease, releaseNotOwned;
+ mutable std::unique_ptr<CFRefBug> deallocGC, deallocNotOwned;
+ mutable std::unique_ptr<CFRefBug> overAutorelease, returnNotOwnedForOwned;
+ mutable std::unique_ptr<CFRefBug> leakWithinFunction, leakAtReturn;
+ mutable std::unique_ptr<CFRefBug> leakWithinFunctionGC, leakAtReturnGC;
- typedef llvm::DenseMap<SymbolRef, const SimpleProgramPointTag *> SymbolTagMap;
+ typedef llvm::DenseMap<SymbolRef, const CheckerProgramPointTag *> SymbolTagMap;
// This map is only used to ensure proper deletion of any allocated tags.
mutable SymbolTagMap DeadSymbolTags;
- mutable OwningPtr<RetainSummaryManager> Summaries;
- mutable OwningPtr<RetainSummaryManager> SummariesGC;
+ mutable std::unique_ptr<RetainSummaryManager> Summaries;
+ mutable std::unique_ptr<RetainSummaryManager> SummariesGC;
mutable SummaryLogTy SummaryLog;
mutable bool ShouldResetSummaryLog;
@@ -2402,17 +2476,18 @@
bool GCEnabled) const {
if (GCEnabled) {
if (!leakWithinFunctionGC)
- leakWithinFunctionGC.reset(new Leak("Leak of object when using "
- "garbage collection"));
+ leakWithinFunctionGC.reset(new Leak(this, "Leak of object when using "
+ "garbage collection"));
return leakWithinFunctionGC.get();
} else {
if (!leakWithinFunction) {
if (LOpts.getGC() == LangOptions::HybridGC) {
- leakWithinFunction.reset(new Leak("Leak of object when not using "
+ leakWithinFunction.reset(new Leak(this,
+ "Leak of object when not using "
"garbage collection (GC) in "
"dual GC/non-GC code"));
} else {
- leakWithinFunction.reset(new Leak("Leak"));
+ leakWithinFunction.reset(new Leak(this, "Leak"));
}
}
return leakWithinFunction.get();
@@ -2422,17 +2497,19 @@
CFRefBug *getLeakAtReturnBug(const LangOptions &LOpts, bool GCEnabled) const {
if (GCEnabled) {
if (!leakAtReturnGC)
- leakAtReturnGC.reset(new Leak("Leak of returned object when using "
+ leakAtReturnGC.reset(new Leak(this,
+ "Leak of returned object when using "
"garbage collection"));
return leakAtReturnGC.get();
} else {
if (!leakAtReturn) {
if (LOpts.getGC() == LangOptions::HybridGC) {
- leakAtReturn.reset(new Leak("Leak of returned object when not using "
+ leakAtReturn.reset(new Leak(this,
+ "Leak of returned object when not using "
"garbage collection (GC) in dual "
"GC/non-GC code"));
} else {
- leakAtReturn.reset(new Leak("Leak of returned object"));
+ leakAtReturn.reset(new Leak(this, "Leak of returned object"));
}
}
return leakAtReturn.get();
@@ -2464,7 +2541,7 @@
}
void printState(raw_ostream &Out, ProgramStateRef State,
- const char *NL, const char *Sep) const;
+ const char *NL, const char *Sep) const override;
void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const;
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
@@ -2474,6 +2551,8 @@
void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const;
void checkPostStmt(const ObjCBoxedExpr *BE, CheckerContext &C) const;
+ void checkPostStmt(const ObjCIvarRefExpr *IRE, CheckerContext &C) const;
+
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
void checkSummary(const RetainSummary &Summ, const CallEvent &Call,
@@ -2542,7 +2621,7 @@
StopTrackingCallback(ProgramStateRef st) : state(st) {}
ProgramStateRef getState() const { return state; }
- bool VisitSymbol(SymbolRef sym) {
+ bool VisitSymbol(SymbolRef sym) override {
state = state->remove<RefBindings>(sym);
return true;
}
@@ -2691,6 +2770,20 @@
C.addTransition(State);
}
+void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ // If an instance variable was previously accessed through a property,
+ // it may have a synthesized refcount of +0. Override right now that we're
+ // doing direct access.
+ if (Optional<Loc> IVarLoc = C.getSVal(IRE).getAs<Loc>())
+ if (SymbolRef Sym = State->getSVal(*IVarLoc).getAsSymbol())
+ if (const RefVal *RV = getRefBinding(State, Sym))
+ if (RV->isOverridable())
+ State = removeRefBinding(State, Sym);
+ C.addTransition(State);
+}
+
void RetainCountChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
RetainSummaryManager &Summaries = getSummaryManager(C);
@@ -2731,6 +2824,16 @@
return RetTy;
}
+static bool wasSynthesizedProperty(const ObjCMethodCall *Call,
+ ExplodedNode *N) {
+ if (!Call || !Call->getDecl()->isPropertyAccessor())
+ return false;
+
+ CallExitEnd PP = N->getLocation().castAs<CallExitEnd>();
+ const StackFrameContext *Frame = PP.getCalleeContext();
+ return Frame->getAnalysisDeclContext()->isBodyAutosynthesized();
+}
+
// We don't always get the exact modeling of the function with regards to the
// retain count checker even when the function is inlined. For example, we need
// to stop tracking the symbols which were marked with StopTrackingHard.
@@ -2765,6 +2868,19 @@
SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol();
if (Sym)
state = removeRefBinding(state, Sym);
+ } else if (RE.getKind() == RetEffect::NotOwnedSymbol) {
+ if (wasSynthesizedProperty(MsgInvocation, C.getPredecessor())) {
+ // Believe the summary if we synthesized the body of a property getter
+ // and the return value is currently untracked. If the corresponding
+ // instance variable is later accessed directly, however, we're going to
+ // want to override this state, so that the owning object can perform
+ // reference counting operations on its own ivars.
+ SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol();
+ if (Sym && !getRefBinding(state, Sym))
+ state = setRefBinding(state, Sym,
+ RefVal::makeOverridableNotOwned(RE.getObjKind(),
+ Sym->getType()));
+ }
}
C.addTransition(state);
@@ -2857,7 +2973,6 @@
}
case RetEffect::GCNotOwnedSymbol:
- case RetEffect::ARCNotOwnedSymbol:
case RetEffect::NotOwnedSymbol: {
const Expr *Ex = CallOrMsg.getOriginExpr();
SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol();
@@ -3053,22 +3168,22 @@
llvm_unreachable("Unhandled error.");
case RefVal::ErrorUseAfterRelease:
if (!useAfterRelease)
- useAfterRelease.reset(new UseAfterRelease());
+ useAfterRelease.reset(new UseAfterRelease(this));
BT = &*useAfterRelease;
break;
case RefVal::ErrorReleaseNotOwned:
if (!releaseNotOwned)
- releaseNotOwned.reset(new BadRelease());
+ releaseNotOwned.reset(new BadRelease(this));
BT = &*releaseNotOwned;
break;
case RefVal::ErrorDeallocGC:
if (!deallocGC)
- deallocGC.reset(new DeallocGC());
+ deallocGC.reset(new DeallocGC(this));
BT = &*deallocGC;
break;
case RefVal::ErrorDeallocNotOwned:
if (!deallocNotOwned)
- deallocNotOwned.reset(new DeallocNotOwned());
+ deallocNotOwned.reset(new DeallocNotOwned(this));
BT = &*deallocNotOwned;
break;
}
@@ -3232,8 +3347,7 @@
return;
// Update the autorelease counts.
- static SimpleProgramPointTag
- AutoreleaseTag("RetainCountChecker : Autorelease");
+ static CheckerProgramPointTag AutoreleaseTag(this, "Autorelease");
state = handleAutoreleaseCounts(state, Pred, &AutoreleaseTag, C, Sym, X);
// Did we cache out?
@@ -3294,8 +3408,7 @@
// Generate an error node.
state = setRefBinding(state, Sym, X);
- static SimpleProgramPointTag
- ReturnOwnLeakTag("RetainCountChecker : ReturnsOwnLeak");
+ static CheckerProgramPointTag ReturnOwnLeakTag(this, "ReturnsOwnLeak");
ExplodedNode *N = C.addTransition(state, Pred, &ReturnOwnLeakTag);
if (N) {
const LangOptions &LOpts = C.getASTContext().getLangOpts();
@@ -3315,12 +3428,12 @@
// owned object.
state = setRefBinding(state, Sym, X ^ RefVal::ErrorReturnedNotOwned);
- static SimpleProgramPointTag
- ReturnNotOwnedTag("RetainCountChecker : ReturnNotOwnedForOwned");
+ static CheckerProgramPointTag ReturnNotOwnedTag(this,
+ "ReturnNotOwnedForOwned");
ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag);
if (N) {
if (!returnNotOwnedForOwned)
- returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned());
+ returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned(this));
CFRefReport *report =
new CFRefReport(*returnNotOwnedForOwned,
@@ -3375,7 +3488,7 @@
// false positives.
if (const VarRegion *LVR = dyn_cast_or_null<VarRegion>(loc.getAsRegion())) {
const VarDecl *VD = LVR->getDecl();
- if (VD->getAttr<CleanupAttr>()) {
+ if (VD->hasAttr<CleanupAttr>()) {
escapes = true;
}
}
@@ -3508,7 +3621,7 @@
os << "has a +" << V.getCount() << " retain count";
if (!overAutorelease)
- overAutorelease.reset(new OverAutorelease());
+ overAutorelease.reset(new OverAutorelease(this));
const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
CFRefReport *report =
@@ -3602,13 +3715,13 @@
const ProgramPointTag *
RetainCountChecker::getDeadSymbolTag(SymbolRef sym) const {
- const SimpleProgramPointTag *&tag = DeadSymbolTags[sym];
+ const CheckerProgramPointTag *&tag = DeadSymbolTags[sym];
if (!tag) {
SmallString<64> buf;
llvm::raw_svector_ostream out(buf);
- out << "RetainCountChecker : Dead Symbol : ";
+ out << "Dead Symbol : ";
sym->dumpToStream(out);
- tag = new SimpleProgramPointTag(out.str());
+ tag = new CheckerProgramPointTag(this, out.str());
}
return tag;
}
diff --git a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
index fe253b7..b1cde6b 100644
--- a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
@@ -25,7 +25,8 @@
namespace {
class ReturnPointerRangeChecker :
public Checker< check::PreStmt<ReturnStmt> > {
- mutable OwningPtr<BuiltinBug> BT;
+ mutable std::unique_ptr<BuiltinBug> BT;
+
public:
void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
};
@@ -69,9 +70,10 @@
// FIXME: This bug correspond to CWE-466. Eventually we should have bug
// types explicitly reference such exploit categories (when applicable).
if (!BT)
- BT.reset(new BuiltinBug("Return of pointer value outside of expected range",
- "Returned pointer value points outside the original object "
- "(potential buffer overflow)"));
+ BT.reset(new BuiltinBug(
+ this, "Return of pointer value outside of expected range",
+ "Returned pointer value points outside the original object "
+ "(potential buffer overflow)"));
// FIXME: It would be nice to eventually make this diagnostic more clear,
// e.g., by referencing the original declaration or by saying *why* this
diff --git a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
index ed96c40..b4d92d6 100644
--- a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
@@ -25,8 +25,8 @@
namespace {
class ReturnUndefChecker : public Checker< check::PreStmt<ReturnStmt> > {
- mutable OwningPtr<BuiltinBug> BT_Undef;
- mutable OwningPtr<BuiltinBug> BT_NullReference;
+ mutable std::unique_ptr<BuiltinBug> BT_Undef;
+ mutable std::unique_ptr<BuiltinBug> BT_NullReference;
void emitUndef(CheckerContext &C, const Expr *RetE) const;
void checkReference(CheckerContext &C, const Expr *RetE,
@@ -94,16 +94,16 @@
void ReturnUndefChecker::emitUndef(CheckerContext &C, const Expr *RetE) const {
if (!BT_Undef)
- BT_Undef.reset(new BuiltinBug("Garbage return value",
- "Undefined or garbage value "
- "returned to caller"));
+ BT_Undef.reset(
+ new BuiltinBug(this, "Garbage return value",
+ "Undefined or garbage value returned to caller"));
emitBug(C, *BT_Undef, RetE);
}
void ReturnUndefChecker::checkReference(CheckerContext &C, const Expr *RetE,
DefinedOrUnknownSVal RetVal) const {
ProgramStateRef StNonNull, StNull;
- llvm::tie(StNonNull, StNull) = C.getState()->assume(RetVal);
+ std::tie(StNonNull, StNull) = C.getState()->assume(RetVal);
if (StNonNull) {
// Going forward, assume the location is non-null.
@@ -113,7 +113,7 @@
// The return value is known to be null. Emit a bug report.
if (!BT_NullReference)
- BT_NullReference.reset(new BuiltinBug("Returning null reference"));
+ BT_NullReference.reset(new BuiltinBug(this, "Returning null reference"));
emitBug(C, *BT_NullReference, RetE, bugreporter::getDerefExpr(RetE));
}
diff --git a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
index 9ca0ab5..83b15ec 100644
--- a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
@@ -54,8 +54,8 @@
mutable IdentifierInfo *IIfopen, *IIfclose;
- OwningPtr<BugType> DoubleCloseBugType;
- OwningPtr<BugType> LeakBugType;
+ std::unique_ptr<BugType> DoubleCloseBugType;
+ std::unique_ptr<BugType> LeakBugType;
void initIdentifierInfo(ASTContext &Ctx) const;
@@ -99,7 +99,7 @@
StopTrackingCallback(ProgramStateRef st) : state(st) {}
ProgramStateRef getState() const { return state; }
- bool VisitSymbol(SymbolRef sym) {
+ bool VisitSymbol(SymbolRef sym) override {
state = state->remove<StreamMap>(sym);
return true;
}
@@ -109,11 +109,11 @@
SimpleStreamChecker::SimpleStreamChecker() : IIfopen(0), IIfclose(0) {
// Initialize the bug types.
- DoubleCloseBugType.reset(new BugType("Double fclose",
- "Unix Stream API Error"));
+ DoubleCloseBugType.reset(
+ new BugType(this, "Double fclose", "Unix Stream API Error"));
- LeakBugType.reset(new BugType("Resource Leak",
- "Unix Stream API Error"));
+ LeakBugType.reset(
+ new BugType(this, "Resource Leak", "Unix Stream API Error"));
// Sinks are higher importance bugs as well as calls to assert() or exit(0).
LeakBugType->setSuppressOnSink(true);
}
diff --git a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
index 4fd778e..327a9e0 100644
--- a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -28,8 +28,8 @@
namespace {
class StackAddrEscapeChecker : public Checker< check::PreStmt<ReturnStmt>,
check::EndFunction > {
- mutable OwningPtr<BuiltinBug> BT_stackleak;
- mutable OwningPtr<BuiltinBug> BT_returnstack;
+ mutable std::unique_ptr<BuiltinBug> BT_stackleak;
+ mutable std::unique_ptr<BuiltinBug> BT_returnstack;
public:
void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
@@ -100,8 +100,8 @@
return;
if (!BT_returnstack)
- BT_returnstack.reset(
- new BuiltinBug("Return of address to stack-allocated memory"));
+ BT_returnstack.reset(
+ new BuiltinBug(this, "Return of address to stack-allocated memory"));
// Generate a report for this bug.
SmallString<512> buf;
@@ -177,8 +177,8 @@
{}
bool HandleBinding(StoreManager &SMgr, Store store,
- const MemRegion *region, SVal val) {
-
+ const MemRegion *region, SVal val) override {
+
if (!isa<GlobalsSpaceRegion>(region->getMemorySpace()))
return true;
@@ -217,11 +217,11 @@
if (!BT_stackleak)
BT_stackleak.reset(
- new BuiltinBug("Stack address stored into global variable",
- "Stack address was saved into a global variable. "
- "This is dangerous because the address will become "
- "invalid after returning from the function"));
-
+ new BuiltinBug(this, "Stack address stored into global variable",
+ "Stack address was saved into a global variable. "
+ "This is dangerous because the address will become "
+ "invalid after returning from the function"));
+
for (unsigned i = 0, e = cb.V.size(); i != e; ++i) {
// Generate a report for this bug.
SmallString<512> buf;
diff --git a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index ffdf2d5..6000942 100644
--- a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -62,8 +62,8 @@
*II_fwrite,
*II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos,
*II_clearerr, *II_feof, *II_ferror, *II_fileno;
- mutable OwningPtr<BuiltinBug> BT_nullfp, BT_illegalwhence,
- BT_doubleclose, BT_ResourceLeak;
+ mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence,
+ BT_doubleclose, BT_ResourceLeak;
public:
StreamChecker()
@@ -218,7 +218,7 @@
// Bifurcate the state into two: one with a valid FILE* pointer, the other
// with a NULL.
ProgramStateRef stateNotNull, stateNull;
- llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal);
+ std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal);
if (SymbolRef Sym = RetVal.getAsSymbol()) {
// if RetVal is not NULL, set the symbol's state to Opened.
@@ -270,9 +270,10 @@
if (ExplodedNode *N = C.addTransition(state)) {
if (!BT_illegalwhence)
- BT_illegalwhence.reset(new BuiltinBug("Illegal whence argument",
- "The whence argument to fseek() should be "
- "SEEK_SET, SEEK_END, or SEEK_CUR."));
+ BT_illegalwhence.reset(
+ new BuiltinBug(this, "Illegal whence argument",
+ "The whence argument to fseek() should be "
+ "SEEK_SET, SEEK_END, or SEEK_CUR."));
BugReport *R = new BugReport(*BT_illegalwhence,
BT_illegalwhence->getDescription(), N);
C.emitReport(R);
@@ -343,13 +344,13 @@
ConstraintManager &CM = C.getConstraintManager();
ProgramStateRef stateNotNull, stateNull;
- llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
+ std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
if (!stateNotNull && stateNull) {
if (ExplodedNode *N = C.generateSink(stateNull)) {
if (!BT_nullfp)
- BT_nullfp.reset(new BuiltinBug("NULL stream pointer",
- "Stream pointer might be NULL."));
+ BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer",
+ "Stream pointer might be NULL."));
BugReport *R =new BugReport(*BT_nullfp, BT_nullfp->getDescription(), N);
C.emitReport(R);
}
@@ -378,9 +379,9 @@
ExplodedNode *N = C.generateSink();
if (N) {
if (!BT_doubleclose)
- BT_doubleclose.reset(new BuiltinBug("Double fclose",
- "Try to close a file Descriptor already"
- " closed. Cause undefined behaviour."));
+ BT_doubleclose.reset(new BuiltinBug(
+ this, "Double fclose", "Try to close a file Descriptor already"
+ " closed. Cause undefined behaviour."));
BugReport *R = new BugReport(*BT_doubleclose,
BT_doubleclose->getDescription(), N);
C.emitReport(R);
@@ -407,8 +408,9 @@
ExplodedNode *N = C.generateSink();
if (N) {
if (!BT_ResourceLeak)
- BT_ResourceLeak.reset(new BuiltinBug("Resource Leak",
- "Opened File never closed. Potential Resource leak."));
+ BT_ResourceLeak.reset(new BuiltinBug(
+ this, "Resource Leak",
+ "Opened File never closed. Potential Resource leak."));
BugReport *R = new BugReport(*BT_ResourceLeak,
BT_ResourceLeak->getDescription(), N);
C.emitReport(R);
diff --git a/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp b/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp
index 264f7f9..d33c977 100644
--- a/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp
@@ -22,7 +22,7 @@
namespace {
class TaintTesterChecker : public Checker< check::PostStmt<Expr> > {
- mutable OwningPtr<BugType> BT;
+ mutable std::unique_ptr<BugType> BT;
void initBugType() const;
/// Given a pointer argument, get the symbol of the value it contains
@@ -38,7 +38,7 @@
inline void TaintTesterChecker::initBugType() const {
if (!BT)
- BT.reset(new BugType("Tainted data", "General"));
+ BT.reset(new BugType(this, "Tainted data", "General"));
}
void TaintTesterChecker::checkPostStmt(const Expr *E,
diff --git a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
index 8235e68..22e2155 100644
--- a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
@@ -24,7 +24,7 @@
namespace {
class UndefBranchChecker : public Checker<check::BranchCondition> {
- mutable OwningPtr<BuiltinBug> BT;
+ mutable std::unique_ptr<BuiltinBug> BT;
struct FindUndefExpr {
ProgramStateRef St;
@@ -67,8 +67,8 @@
ExplodedNode *N = Ctx.generateSink();
if (N) {
if (!BT)
- BT.reset(
- new BuiltinBug("Branch condition evaluates to a garbage value"));
+ BT.reset(new BuiltinBug(
+ this, "Branch condition evaluates to a garbage value"));
// What's going on here: we want to highlight the subexpression of the
// condition that is the most likely source of the "uninitialized
diff --git a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
index 93812f7..93fe7d4 100644
--- a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
@@ -27,7 +27,7 @@
namespace {
class UndefCapturedBlockVarChecker
: public Checker< check::PostStmt<BlockExpr> > {
- mutable OwningPtr<BugType> BT;
+ mutable std::unique_ptr<BugType> BT;
public:
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
@@ -71,7 +71,7 @@
const VarRegion *VR = I.getCapturedRegion();
const VarDecl *VD = VR->getDecl();
- if (VD->getAttr<BlocksAttr>() || !VD->hasLocalStorage())
+ if (VD->hasAttr<BlocksAttr>() || !VD->hasLocalStorage())
continue;
// Get the VarRegion associated with VD in the local stack frame.
@@ -79,7 +79,8 @@
state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) {
if (ExplodedNode *N = C.generateSink()) {
if (!BT)
- BT.reset(new BuiltinBug("uninitialized variable captured by block"));
+ BT.reset(
+ new BuiltinBug(this, "uninitialized variable captured by block"));
// Generate a bug report.
SmallString<128> buf;
diff --git a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
index 3f6549d..00fd971 100644
--- a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
@@ -28,8 +28,8 @@
class UndefResultChecker
: public Checker< check::PostStmt<BinaryOperator> > {
- mutable OwningPtr<BugType> BT;
-
+ mutable std::unique_ptr<BugType> BT;
+
public:
void checkPostStmt(const BinaryOperator *B, CheckerContext &C) const;
};
@@ -55,7 +55,8 @@
return;
if (!BT)
- BT.reset(new BuiltinBug("Result of operation is garbage or undefined"));
+ BT.reset(
+ new BuiltinBug(this, "Result of operation is garbage or undefined"));
SmallString<256> sbuf;
llvm::raw_svector_ostream OS(sbuf);
diff --git a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
index 5df8846..e952671 100644
--- a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
@@ -25,7 +25,7 @@
namespace {
class UndefinedArraySubscriptChecker
: public Checker< check::PreStmt<ArraySubscriptExpr> > {
- mutable OwningPtr<BugType> BT;
+ mutable std::unique_ptr<BugType> BT;
public:
void checkPreStmt(const ArraySubscriptExpr *A, CheckerContext &C) const;
@@ -50,7 +50,7 @@
if (!N)
return;
if (!BT)
- BT.reset(new BuiltinBug("Array subscript is undefined"));
+ BT.reset(new BuiltinBug(this, "Array subscript is undefined"));
// Generate a report for this bug.
BugReport *R = new BugReport(*BT, BT->getName(), N);
diff --git a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
index 016e3c8..30775d5 100644
--- a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
@@ -24,7 +24,7 @@
namespace {
class UndefinedAssignmentChecker
: public Checker<check::Bind> {
- mutable OwningPtr<BugType> BT;
+ mutable std::unique_ptr<BugType> BT;
public:
void checkBind(SVal location, SVal val, const Stmt *S,
@@ -54,7 +54,7 @@
const char *str = "Assigned value is garbage or undefined";
if (!BT)
- BT.reset(new BuiltinBug(str));
+ BT.reset(new BuiltinBug(this, str));
// Generate a report for this bug.
const Expr *ex = 0;
diff --git a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
index 4ea07e2..aa7e25bc 100644
--- a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
@@ -30,7 +30,7 @@
namespace {
class UnixAPIChecker : public Checker< check::PreStmt<CallExpr> > {
- mutable OwningPtr<BugType> BT_open, BT_pthreadOnce, BT_mallocZero;
+ mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce, BT_mallocZero;
mutable Optional<uint64_t> Val_O_CREAT;
public:
@@ -57,21 +57,15 @@
const unsigned numArgs,
const unsigned sizeArg,
const char *fn) const;
+ void LazyInitialize(std::unique_ptr<BugType> &BT, const char *name) const {
+ if (BT)
+ return;
+ BT.reset(new BugType(this, name, categories::UnixAPI));
+ }
};
} //end anonymous namespace
//===----------------------------------------------------------------------===//
-// Utility functions.
-//===----------------------------------------------------------------------===//
-
-static inline void LazyInitialize(OwningPtr<BugType> &BT,
- const char *name) {
- if (BT)
- return;
- BT.reset(new BugType(name, categories::UnixAPI));
-}
-
-//===----------------------------------------------------------------------===//
// "open" (man 2 open)
//===----------------------------------------------------------------------===//
@@ -86,6 +80,7 @@
// FIXME: We need a more general way of getting the O_CREAT value.
// We could possibly grovel through the preprocessor state, but
// that would require passing the Preprocessor object to the ExprEngine.
+ // See also: MallocChecker.cpp / M_ZERO.
return;
}
}
@@ -119,7 +114,7 @@
// Check if maskedFlags is non-zero.
ProgramStateRef trueState, falseState;
- llvm::tie(trueState, falseState) = state->assume(maskedFlags);
+ std::tie(trueState, falseState) = state->assume(maskedFlags);
// Only emit an error if the value of 'maskedFlags' is properly
// constrained;
@@ -199,7 +194,7 @@
const SVal argVal,
ProgramStateRef *trueState,
ProgramStateRef *falseState) {
- llvm::tie(*trueState, *falseState) =
+ std::tie(*trueState, *falseState) =
state->assume(argVal.castAs<DefinedSVal>());
return (*falseState && !*trueState);
@@ -207,7 +202,7 @@
// Generates an error report, indicating that the function whose name is given
// will perform a zero byte allocation.
-// Returns false if an error occured, true otherwise.
+// Returns false if an error occurred, true otherwise.
bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C,
ProgramStateRef falseState,
const Expr *arg,
@@ -217,7 +212,7 @@
return false;
LazyInitialize(BT_mallocZero,
- "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");
+ "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");
SmallString<256> S;
llvm::raw_svector_ostream os(S);
diff --git a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
index a40b5a3..66c1acd 100644
--- a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
@@ -136,7 +136,7 @@
ci != ce; ++ci) {
if (Optional<CFGStmt> S = (*ci).getAs<CFGStmt>())
if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) {
- if (CE->isBuiltinCall() == Builtin::BI__builtin_unreachable) {
+ if (CE->getBuiltinCallee() == Builtin::BI__builtin_unreachable) {
foundUnreachable = true;
break;
}
@@ -165,7 +165,7 @@
if (SM.isInSystemHeader(SL) || SM.isInExternCSystemHeader(SL))
continue;
- B.EmitBasicReport(D, "Unreachable code", "Dead code",
+ B.EmitBasicReport(D, this, "Unreachable code", "Dead code",
"This statement is never executed", DL, SR);
}
}
@@ -178,6 +178,9 @@
for (CFGBlock::const_pred_iterator I = CB->pred_begin(), E = CB->pred_end();
I != E; ++I) {
+ if (!*I)
+ continue;
+
if (!reachable.count((*I)->getBlockID())) {
// If we find an unreachable predecessor, mark this block as reachable so
// we don't report this block
@@ -219,6 +222,8 @@
return false;
const CFGBlock *pred = *CB->pred_begin();
+ if (!pred)
+ return false;
// Get the predecessor block's terminator conditon
const Stmt *cond = pred->getTerminatorCondition();
@@ -242,7 +247,7 @@
bool UnreachableCodeChecker::isEmptyCFGBlock(const CFGBlock *CB) {
return CB->getLabel() == 0 // No labels
&& CB->size() == 0 // No statements
- && CB->getTerminator() == 0; // No terminator
+ && !CB->getTerminator(); // No terminator
}
void ento::registerUnreachableCodeChecker(CheckerManager &mgr) {
diff --git a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
index 30aef06..c7b2024 100644
--- a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
@@ -29,7 +29,7 @@
namespace {
class VLASizeChecker : public Checker< check::PreStmt<DeclStmt> > {
- mutable OwningPtr<BugType> BT;
+ mutable std::unique_ptr<BugType> BT;
enum VLASize_Kind { VLA_Garbage, VLA_Zero, VLA_Tainted };
void reportBug(VLASize_Kind Kind,
@@ -51,7 +51,8 @@
return;
if (!BT)
- BT.reset(new BuiltinBug("Dangerous variable-length array (VLA) declaration"));
+ BT.reset(new BuiltinBug(
+ this, "Dangerous variable-length array (VLA) declaration"));
SmallString<256> buf;
llvm::raw_svector_ostream os(buf);
@@ -113,7 +114,7 @@
DefinedSVal sizeD = sizeV.castAs<DefinedSVal>();
ProgramStateRef stateNotZero, stateZero;
- llvm::tie(stateNotZero, stateZero) = state->assume(sizeD);
+ std::tie(stateNotZero, stateZero) = state->assume(sizeD);
if (stateZero && !stateNotZero) {
reportBug(VLA_Zero, SE, stateZero, C);
diff --git a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
index 7b6adbf..9b5c852 100644
--- a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
@@ -28,6 +28,7 @@
namespace {
class WalkAST : public StmtVisitor<WalkAST> {
+ const CheckerBase *Checker;
BugReporter &BR;
AnalysisDeclContext *AC;
@@ -58,11 +59,10 @@
const CallExpr *visitingCallExpr;
public:
- WalkAST(BugReporter &br, AnalysisDeclContext *ac)
- : BR(br),
- AC(ac),
- visitingCallExpr(0) {}
-
+ WalkAST(const CheckerBase *checker, BugReporter &br,
+ AnalysisDeclContext *ac)
+ : Checker(checker), BR(br), AC(ac), visitingCallExpr(0) {}
+
bool hasWork() const { return !WList.empty(); }
/// This method adds a CallExpr to the worklist and marks the callee as
@@ -187,21 +187,19 @@
if (isPure) {
os << "\n" << "Call pure virtual functions during construction or "
<< "destruction may leads undefined behaviour";
- BR.EmitBasicReport(AC->getDecl(),
+ BR.EmitBasicReport(AC->getDecl(), Checker,
"Call pure virtual function during construction or "
"Destruction",
- "Cplusplus",
- os.str(), CELoc, R);
+ "Cplusplus", os.str(), CELoc, R);
return;
}
else {
os << "\n" << "Call virtual functions during construction or "
<< "destruction will never go to a more derived class";
- BR.EmitBasicReport(AC->getDecl(),
+ BR.EmitBasicReport(AC->getDecl(), Checker,
"Call virtual function during construction or "
"Destruction",
- "Cplusplus",
- os.str(), CELoc, R);
+ "Cplusplus", os.str(), CELoc, R);
return;
}
}
@@ -215,11 +213,10 @@
public:
void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr,
BugReporter &BR) const {
- WalkAST walker(BR, mgr.getAnalysisDeclContext(RD));
+ WalkAST walker(this, BR, mgr.getAnalysisDeclContext(RD));
// Check the constructors.
- for (CXXRecordDecl::ctor_iterator I = RD->ctor_begin(), E = RD->ctor_end();
- I != E; ++I) {
+ for (const auto *I : RD->ctors()) {
if (!I->isCopyOrMoveConstructor())
if (Stmt *Body = I->getBody()) {
walker.Visit(Body);