aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Checkers
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2013-06-10 20:45:12 +0000
committerDimitry Andric <dim@FreeBSD.org>2013-06-10 20:45:12 +0000
commit6a0372513edbc473b538d2f724efac50405d6fef (patch)
tree8f7776b7310bebaf415ac5b69e46e9f928c37144 /lib/StaticAnalyzer/Checkers
parent809500fc2c13c8173a16b052304d983864e4a1e1 (diff)
downloadsrc-6a0372513edbc473b538d2f724efac50405d6fef.tar.gz
src-6a0372513edbc473b538d2f724efac50405d6fef.zip
Vendor import of clang tags/RELEASE_33/final r183502 (effectively, 3.3vendor/clang/clang-release_33-r183502
Notes
Notes: svn path=/vendor/clang/dist/; revision=251609 svn path=/vendor/clang/clang-release_33-r183502/; revision=251610; tag=vendor/clang/clang-release_33-r183502
Diffstat (limited to 'lib/StaticAnalyzer/Checkers')
-rw-r--r--lib/StaticAnalyzer/Checkers/AllocationDiagnostics.cpp24
-rw-r--r--lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h31
-rw-r--r--lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp112
-rw-r--r--lib/StaticAnalyzer/Checkers/CMakeLists.txt3
-rw-r--r--lib/StaticAnalyzer/Checkers/CStringChecker.cpp62
-rw-r--r--lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/Checkers.td9
-rw-r--r--lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp14
-rw-r--r--lib/StaticAnalyzer/Checkers/DebugCheckers.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp18
-rw-r--r--lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp47
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocChecker.cpp220
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp158
-rw-r--r--lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp13
16 files changed, 550 insertions, 173 deletions
diff --git a/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.cpp b/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.cpp
new file mode 100644
index 000000000000..3dec8a58c929
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.cpp
@@ -0,0 +1,24 @@
+//=- AllocationDiagnostics.cpp - Config options for allocation diags *- C++ -*-//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Declares the configuration functions for leaks/allocation diagnostics.
+//
+//===--------------------------
+
+#include "AllocationDiagnostics.h"
+
+namespace clang {
+namespace ento {
+
+bool shouldIncludeAllocationSiteInLeakDiagnostics(AnalyzerOptions &AOpts) {
+ return AOpts.getBooleanOption("leak-diagnostics-reference-allocation",
+ false);
+}
+
+}}
diff --git a/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h b/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h
new file mode 100644
index 000000000000..2b314a3b749a
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h
@@ -0,0 +1,31 @@
+//=--- AllocationDiagnostics.h - Config options for allocation diags *- C++ -*-//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Declares the configuration functions for leaks/allocation diagnostics.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SA_LIB_CHECKERS_ALLOC_DIAGS_H
+#define LLVM_CLANG_SA_LIB_CHECKERS_ALLOC_DIAGS_H
+
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+
+namespace clang { namespace ento {
+
+/// \brief Returns true if leak diagnostics should directly reference
+/// the allocatin site (where possible).
+///
+/// The default is false.
+///
+bool shouldIncludeAllocationSiteInLeakDiagnostics(AnalyzerOptions &AOpts);
+
+}}
+
+#endif
+
diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index 533a324e7507..fba14a0fc498 100644
--- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -123,18 +123,29 @@ void NilArgChecker::WarnIfNilArg(CheckerContext &C,
if (Class == FC_NSArray) {
os << "Array element cannot be nil";
} else if (Class == FC_NSDictionary) {
- if (Arg == 0)
- os << "Dictionary object cannot be nil";
- else {
+ if (Arg == 0) {
+ os << "Value stored into '";
+ os << GetReceiverInterfaceName(msg) << "' cannot be nil";
+ } else {
assert(Arg == 1);
- os << "Dictionary key cannot be nil";
+ os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil";
}
} else
llvm_unreachable("Missing foundation class for the subscript expr");
} else {
- os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"
- << msg.getSelector().getAsString() << "' cannot be nil";
+ if (Class == FC_NSDictionary) {
+ if (Arg == 0)
+ os << "Value argument ";
+ else {
+ assert(Arg == 1);
+ os << "Key argument ";
+ }
+ os << "to '" << msg.getSelector().getAsString() << "' cannot be nil";
+ } else {
+ os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"
+ << msg.getSelector().getAsString() << "' cannot be nil";
+ }
}
BugReport *R = new BugReport(*BT, os.str(), N);
@@ -377,7 +388,7 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
// FIXME: If the pointee isn't an integer type, should we flag a warning?
// People can do weird stuff with pointers.
- if (!T->isIntegerType())
+ if (!T->isIntegralOrEnumerationType())
return;
uint64_t SourceSize = Ctx.getTypeSize(T);
@@ -748,38 +759,81 @@ static bool isKnownNonNilCollectionType(QualType T) {
}
}
-void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
- CheckerContext &C) const {
- ProgramStateRef State = C.getState();
-
- // Check if this is the branch for the end of the loop.
- SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext());
- if (CollectionSentinel.isZeroConstant())
- return;
-
+/// Assumes that the collection is non-nil.
+///
+/// If the collection is known to be nil, returns NULL to indicate an infeasible
+/// path.
+static ProgramStateRef checkCollectionNonNil(CheckerContext &C,
+ ProgramStateRef State,
+ const ObjCForCollectionStmt *FCS) {
+ if (!State)
+ return NULL;
+
+ SVal CollectionVal = C.getSVal(FCS->getCollection());
+ Optional<DefinedSVal> KnownCollection = CollectionVal.getAs<DefinedSVal>();
+ if (!KnownCollection)
+ return State;
+
+ ProgramStateRef StNonNil, StNil;
+ llvm::tie(StNonNil, StNil) = State->assume(*KnownCollection);
+ if (StNil && !StNonNil) {
+ // The collection is nil. This path is infeasible.
+ return NULL;
+ }
+
+ return StNonNil;
+}
+
+/// Assumes that the collection elements are non-nil.
+///
+/// This only applies if the collection is one of those known not to contain
+/// nil values.
+static ProgramStateRef checkElementNonNil(CheckerContext &C,
+ ProgramStateRef State,
+ const ObjCForCollectionStmt *FCS) {
+ if (!State)
+ return NULL;
+
// See if the collection is one where we /know/ the elements are non-nil.
- const Expr *Collection = FCS->getCollection();
- if (!isKnownNonNilCollectionType(Collection->getType()))
- return;
-
- // FIXME: Copied from ExprEngineObjC.
+ if (!isKnownNonNilCollectionType(FCS->getCollection()->getType()))
+ return State;
+
+ const LocationContext *LCtx = C.getLocationContext();
const Stmt *Element = FCS->getElement();
- SVal ElementVar;
+
+ // FIXME: Copied from ExprEngineObjC.
+ Optional<Loc> ElementLoc;
if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
assert(ElemDecl->getInit() == 0);
- ElementVar = State->getLValue(ElemDecl, C.getLocationContext());
+ ElementLoc = State->getLValue(ElemDecl, LCtx);
} else {
- ElementVar = State->getSVal(Element, C.getLocationContext());
+ ElementLoc = State->getSVal(Element, LCtx).getAs<Loc>();
}
- if (!ElementVar.getAs<Loc>())
- return;
+ if (!ElementLoc)
+ return State;
// Go ahead and assume the value is non-nil.
- SVal Val = State->getSVal(ElementVar.castAs<Loc>());
- State = State->assume(Val.castAs<DefinedOrUnknownSVal>(), true);
- C.addTransition(State);
+ SVal Val = State->getSVal(*ElementLoc);
+ return State->assume(Val.castAs<DefinedOrUnknownSVal>(), true);
+}
+
+void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
+ CheckerContext &C) const {
+ // Check if this is the branch for the end of the loop.
+ SVal CollectionSentinel = C.getSVal(FCS);
+ if (CollectionSentinel.isZeroConstant())
+ return;
+
+ ProgramStateRef State = C.getState();
+ State = checkCollectionNonNil(C, State, FCS);
+ State = checkElementNonNil(C, State, FCS);
+
+ if (!State)
+ C.generateSink();
+ else if (State != C.getState())
+ C.addTransition(State);
}
namespace {
diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index b7df10e7ffbe..7da68251063b 100644
--- a/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -4,6 +4,7 @@ clang_tablegen(Checkers.inc -gen-clang-sa-checkers
TARGET ClangSACheckers)
add_clang_library(clangStaticAnalyzerCheckers
+ AllocationDiagnostics.cpp
AnalyzerStatsChecker.cpp
ArrayBoundChecker.cpp
ArrayBoundCheckerV2.cpp
@@ -42,8 +43,8 @@ add_clang_library(clangStaticAnalyzerCheckers
MallocSizeofChecker.cpp
NSAutoreleasePoolChecker.cpp
NSErrorChecker.cpp
- NonNullParamChecker.cpp
NoReturnFunctionChecker.cpp
+ NonNullParamChecker.cpp
ObjCAtSyncChecker.cpp
ObjCContainersASTChecker.cpp
ObjCContainersChecker.cpp
diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index cc55e9f6ecf0..aa1ca6f2f809 100644
--- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -114,6 +114,8 @@ public:
bool isBounded = false,
bool ignoreCase = false) const;
+ void evalStrsep(CheckerContext &C, const CallExpr *CE) const;
+
// Utility methods
std::pair<ProgramStateRef , ProgramStateRef >
static assumeZero(CheckerContext &C,
@@ -1752,6 +1754,63 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
C.addTransition(state);
}
+void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const {
+ //char *strsep(char **stringp, const char *delim);
+ if (CE->getNumArgs() < 2)
+ return;
+
+ // Sanity: does the search string parameter match the return type?
+ const Expr *SearchStrPtr = CE->getArg(0);
+ QualType CharPtrTy = SearchStrPtr->getType()->getPointeeType();
+ if (CharPtrTy.isNull() ||
+ CE->getType().getUnqualifiedType() != CharPtrTy.getUnqualifiedType())
+ return;
+
+ CurrentFunctionDescription = "strsep()";
+ ProgramStateRef State = C.getState();
+ const LocationContext *LCtx = C.getLocationContext();
+
+ // Check that the search string pointer is non-null (though it may point to
+ // a null string).
+ SVal SearchStrVal = State->getSVal(SearchStrPtr, LCtx);
+ State = checkNonNull(C, State, SearchStrPtr, SearchStrVal);
+ if (!State)
+ return;
+
+ // Check that the delimiter string is non-null.
+ const Expr *DelimStr = CE->getArg(1);
+ SVal DelimStrVal = State->getSVal(DelimStr, LCtx);
+ State = checkNonNull(C, State, DelimStr, DelimStrVal);
+ if (!State)
+ return;
+
+ SValBuilder &SVB = C.getSValBuilder();
+ SVal Result;
+ if (Optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) {
+ // Get the current value of the search string pointer, as a char*.
+ Result = State->getSVal(*SearchStrLoc, CharPtrTy);
+
+ // Invalidate the search string, representing the change of one delimiter
+ // character to NUL.
+ State = InvalidateBuffer(C, State, SearchStrPtr, Result);
+
+ // Overwrite the search string pointer. The new value is either an address
+ // further along in the same string, or NULL if there are no more tokens.
+ State = State->bindLoc(*SearchStrLoc,
+ SVB.conjureSymbolVal(getTag(), CE, LCtx, CharPtrTy,
+ C.blockCount()));
+ } else {
+ assert(SearchStrVal.isUnknown());
+ // Conjure a symbolic value. It's the best we can do.
+ Result = SVB.conjureSymbolVal(0, CE, LCtx, C.blockCount());
+ }
+
+ // Set the return value, and finish.
+ State = State->BindExpr(CE, LCtx, Result);
+ C.addTransition(State);
+}
+
+
//===----------------------------------------------------------------------===//
// The driver method, and other Checker callbacks.
//===----------------------------------------------------------------------===//
@@ -1762,6 +1821,7 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
if (!FDecl)
return false;
+ // FIXME: Poorly-factored string switches are slow.
FnCheck evalFunction = 0;
if (C.isCLibraryFunction(FDecl, "memcpy"))
evalFunction = &CStringChecker::evalMemcpy;
@@ -1793,6 +1853,8 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
evalFunction = &CStringChecker::evalStrcasecmp;
else if (C.isCLibraryFunction(FDecl, "strncasecmp"))
evalFunction = &CStringChecker::evalStrncasecmp;
+ else if (C.isCLibraryFunction(FDecl, "strsep"))
+ evalFunction = &CStringChecker::evalStrsep;
else if (C.isCLibraryFunction(FDecl, "bcopy"))
evalFunction = &CStringChecker::evalBcopy;
else if (C.isCLibraryFunction(FDecl, "bcmp"))
diff --git a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
index 3a57a56aea6e..92c0eef3e880 100644
--- a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
@@ -101,6 +101,8 @@ public:
// - strncat(dst, src, sizeof(dst) - 1);
// - strncat(dst, src, sizeof(dst));
bool WalkAST::containsBadStrncatPattern(const CallExpr *CE) {
+ if (CE->getNumArgs() != 3)
+ return false;
const Expr *DstArg = CE->getArg(0);
const Expr *SrcArg = CE->getArg(1);
const Expr *LenArg = CE->getArg(2);
diff --git a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
index 7ef13ab53865..63080ea230f8 100644
--- a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
@@ -345,7 +345,7 @@ void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) {
return;
// Verify the first argument type is integer.
- if (!FPT->getArgType(0)->isIntegerType())
+ if (!FPT->getArgType(0)->isIntegralOrUnscopedEnumerationType())
return;
// Verify the second argument type is char*.
@@ -602,7 +602,7 @@ void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {
if (!PT)
return;
- if (! PT->getPointeeType()->isIntegerType())
+ if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType())
return;
}
else if (FTP->getNumArgs() != 0)
@@ -725,7 +725,7 @@ void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
// The arguments must be integers.
for (unsigned i = 0; i < FTP->getNumArgs(); i++)
- if (! FTP->getArgType(i)->isIntegerType())
+ if (! FTP->getArgType(i)->isIntegralOrUnscopedEnumerationType())
return;
// Issue a warning.
diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td
index 3db3fb9962a5..fc35b223ee7f 100644
--- a/lib/StaticAnalyzer/Checkers/Checkers.td
+++ b/lib/StaticAnalyzer/Checkers/Checkers.td
@@ -167,6 +167,11 @@ def ReturnUndefChecker : Checker<"UndefReturn">,
//===----------------------------------------------------------------------===//
let ParentPackage = Cplusplus in {
+
+def NewDeleteChecker : Checker<"NewDelete">,
+ HelpText<"Check for double-free and use-after-free problems. Traces memory managed by new/delete.">,
+ DescFile<"MallocChecker.cpp">;
+
} // end: "cplusplus"
let ParentPackage = CplusplusAlpha in {
@@ -175,8 +180,8 @@ def VirtualCallChecker : Checker<"VirtualCall">,
HelpText<"Check virtual function calls during construction or destruction">,
DescFile<"VirtualCallChecker.cpp">;
-def NewDeleteChecker : Checker<"NewDelete">,
- HelpText<"Check for memory leaks, double free, and use-after-free problems. Traces memory managed by new/delete.">,
+def NewDeleteLeaksChecker : Checker<"NewDeleteLeaks">,
+ HelpText<"Check for memory leaks. Traces memory managed by new/delete.">,
DescFile<"MallocChecker.cpp">;
} // end: "alpha.cplusplus"
diff --git a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
index f2e3e6d7815e..f336a6e68051 100644
--- a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
@@ -101,7 +101,8 @@ void ReachableCode::computeReachableBlocks() {
}
}
-static const Expr *LookThroughTransitiveAssignments(const Expr *Ex) {
+static const Expr *
+LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex) {
while (Ex) {
const BinaryOperator *BO =
dyn_cast<BinaryOperator>(Ex->IgnoreParenCasts());
@@ -111,6 +112,10 @@ static const Expr *LookThroughTransitiveAssignments(const Expr *Ex) {
Ex = BO->getRHS();
continue;
}
+ if (BO->getOpcode() == BO_Comma) {
+ Ex = BO->getRHS();
+ continue;
+ }
break;
}
return Ex;
@@ -266,7 +271,9 @@ public:
if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
// Special case: check for assigning null to a pointer.
// This is a common form of defensive programming.
- const Expr *RHS = LookThroughTransitiveAssignments(B->getRHS());
+ const Expr *RHS =
+ LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS());
+ RHS = RHS->IgnoreParenCasts();
QualType T = VD->getType();
if (T->isPointerType() || T->isObjCObjectPointerType()) {
@@ -274,7 +281,6 @@ public:
return;
}
- RHS = RHS->IgnoreParenCasts();
// Special case: self-assignments. These are often used to shut up
// "unused variable" compiler warnings.
if (const DeclRefExpr *RhsDR = dyn_cast<DeclRefExpr>(RHS))
@@ -326,7 +332,7 @@ public:
// Look through transitive assignments, e.g.:
// int x = y = 0;
- E = LookThroughTransitiveAssignments(E);
+ E = LookThroughTransitiveAssignmentsAndCommaOperators(E);
// Don't warn on C++ objects (yet) until we can show that their
// constructors/destructors don't have side effects.
diff --git a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
index 29b4a637cda4..fe12866e51c3 100644
--- a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
+++ b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
@@ -7,7 +7,7 @@
//
//===----------------------------------------------------------------------===//
//
-// This file defines a checkers that display debugging information.
+// This file defines checkers that display debugging information.
//
//===----------------------------------------------------------------------===//
diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
index 9f176a4b5bf7..759aa6605ee7 100644
--- a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
+++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
@@ -27,7 +27,8 @@ namespace {
class DynamicTypePropagation:
public Checker< check::PreCall,
check::PostCall,
- check::PostStmt<ImplicitCastExpr> > {
+ check::PostStmt<ImplicitCastExpr>,
+ check::PostStmt<CXXNewExpr> > {
const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
CheckerContext &C) const;
@@ -38,6 +39,7 @@ public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostStmt(const ImplicitCastExpr *CastE, CheckerContext &C) const;
+ void checkPostStmt(const CXXNewExpr *NewE, CheckerContext &C) const;
};
}
@@ -190,6 +192,20 @@ void DynamicTypePropagation::checkPostStmt(const ImplicitCastExpr *CastE,
return;
}
+void DynamicTypePropagation::checkPostStmt(const CXXNewExpr *NewE,
+ CheckerContext &C) const {
+ if (NewE->isArray())
+ return;
+
+ // We only track dynamic type info for regions.
+ const MemRegion *MR = C.getSVal(NewE).getAsRegion();
+ if (!MR)
+ return;
+
+ C.addTransition(C.getState()->setDynamicTypeInfo(MR, NewE->getType(),
+ /*CanBeSubclass=*/false));
+}
+
const ObjCObjectType *
DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
CheckerContext &C) const {
diff --git a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
index 5ed28e955d4e..cc940be7b187 100644
--- a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
@@ -437,6 +437,7 @@ visit(const ObjCImplementationDecl *ImplD) const {
// Remove ivars invalidated by the partial invalidation methods. They do not
// need to be invalidated in the regular invalidation methods.
+ bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false;
for (MethodSet::iterator
I = PartialInfo.InvalidationMethods.begin(),
E = PartialInfo.InvalidationMethods.end(); I != E; ++I) {
@@ -446,6 +447,8 @@ visit(const ObjCImplementationDecl *ImplD) const {
const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
InterfD->isInstanceMethod());
if (D && D->hasBody()) {
+ AtImplementationContainsAtLeastOnePartialInvalidationMethod = true;
+
bool CalledAnotherInvalidationMethod = false;
// The MethodCrowler is going to remove the invalidated ivars.
MethodCrawler(Ivars,
@@ -471,7 +474,7 @@ visit(const ObjCImplementationDecl *ImplD) const {
containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false);
// Report an error in case none of the invalidation methods are declared.
- if (!Info.needsInvalidation()) {
+ if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) {
if (Filter.check_MissingInvalidationMethod)
reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD,
/*MissingDeclaration*/ true);
@@ -520,9 +523,19 @@ visit(const ObjCImplementationDecl *ImplD) const {
}
// Report an error in case none of the invalidation methods are implemented.
- if (!AtImplementationContainsAtLeastOneInvalidationMethod)
- reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD,
- /*MissingDeclaration*/ false);
+ if (!AtImplementationContainsAtLeastOneInvalidationMethod) {
+ if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) {
+ // Warn on the ivars that were not invalidated by the prrtial
+ // invalidation methods.
+ for (IvarSet::const_iterator
+ I = Ivars.begin(), E = Ivars.end(); I != E; ++I)
+ reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, 0);
+ } else {
+ // Otherwise, no invalidation methods were implemented.
+ reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD,
+ /*MissingDeclaration*/ false);
+ }
+ }
}
void IvarInvalidationCheckerImpl::
@@ -551,19 +564,27 @@ reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl,
void IvarInvalidationCheckerImpl::
reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
- const IvarToPropMapTy &IvarToPopertyMap,
- const ObjCMethodDecl *MethodD) const {
+ const IvarToPropMapTy &IvarToPopertyMap,
+ const ObjCMethodDecl *MethodD) const {
SmallString<128> sbuf;
llvm::raw_svector_ostream os(sbuf);
printIvar(os, IvarD, IvarToPopertyMap);
os << "needs to be invalidated or set to nil";
- PathDiagnosticLocation MethodDecLocation =
- PathDiagnosticLocation::createEnd(MethodD->getBody(),
- BR.getSourceManager(),
- Mgr.getAnalysisDeclContext(MethodD));
- BR.EmitBasicReport(MethodD, "Incomplete invalidation",
- categories::CoreFoundationObjectiveC, os.str(),
- MethodDecLocation);
+ if (MethodD) {
+ PathDiagnosticLocation MethodDecLocation =
+ PathDiagnosticLocation::createEnd(MethodD->getBody(),
+ BR.getSourceManager(),
+ Mgr.getAnalysisDeclContext(MethodD));
+ BR.EmitBasicReport(MethodD, "Incomplete invalidation",
+ categories::CoreFoundationObjectiveC, os.str(),
+ MethodDecLocation);
+ } else {
+ BR.EmitBasicReport(IvarD, "Incomplete invalidation",
+ categories::CoreFoundationObjectiveC, os.str(),
+ PathDiagnosticLocation::createBegin(IvarD,
+ BR.getSourceManager()));
+
+ }
}
void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated(
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 4b0e7661d8da..5d3eb65148dc 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -50,7 +50,12 @@ class RefState {
Released,
// The responsibility for freeing resources has transfered from
// this reference. A relinquished symbol should not be freed.
- Relinquished };
+ Relinquished,
+ // We are no longer guaranteed to have observed all manipulations
+ // of this pointer/memory. For example, it could have been
+ // passed as a parameter to an opaque function.
+ Escaped
+ };
const Stmt *S;
unsigned K : 2; // Kind enum, but stored as a bitfield.
@@ -58,12 +63,15 @@ class RefState {
// family.
RefState(Kind k, const Stmt *s, unsigned family)
- : S(s), K(k), Family(family) {}
+ : S(s), K(k), Family(family) {
+ assert(family != AF_None);
+ }
public:
bool isAllocated() const { return K == Allocated; }
bool isReleased() const { return K == Released; }
bool isRelinquished() const { return K == Relinquished; }
- AllocationFamily getAllocationFamily() const {
+ bool isEscaped() const { return K == Escaped; }
+ AllocationFamily getAllocationFamily() const {
return (AllocationFamily)Family;
}
const Stmt *getStmt() const { return S; }
@@ -81,6 +89,9 @@ public:
static RefState getRelinquished(unsigned family, const Stmt *s) {
return RefState(Relinquished, s, family);
}
+ static RefState getEscaped(const RefState *RS) {
+ return RefState(Escaped, RS->getStmt(), RS->getAllocationFamily());
+ }
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddInteger(K);
@@ -136,7 +147,7 @@ class MallocChecker : public Checker<check::DeadSymbols,
check::PointerEscape,
check::ConstPointerEscape,
check::PreStmt<ReturnStmt>,
- check::PreStmt<CallExpr>,
+ check::PreCall,
check::PostStmt<CallExpr>,
check::PostStmt<CXXNewExpr>,
check::PreStmt<CXXDeleteExpr>,
@@ -164,12 +175,13 @@ public:
DefaultBool CMallocPessimistic;
DefaultBool CMallocOptimistic;
DefaultBool CNewDeleteChecker;
+ DefaultBool CNewDeleteLeaksChecker;
DefaultBool CMismatchedDeallocatorChecker;
};
ChecksFilter Filter;
- void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
@@ -282,19 +294,20 @@ private:
PointerEscapeKind Kind,
bool(*CheckRefState)(const RefState*)) const;
- // Used to suppress warnings if they are not related to the tracked family
- // (derived from AllocDeallocStmt).
- bool isTrackedFamily(AllocationFamily Family) const;
- bool isTrackedFamily(CheckerContext &C, const Stmt *AllocDeallocStmt) const;
- bool isTrackedFamily(CheckerContext &C, SymbolRef Sym) const;
-
+ ///@{
+ /// 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;
+ ///@}
static bool SummarizeValue(raw_ostream &os, SVal V);
static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR);
void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range,
const Expr *DeallocExpr) const;
void ReportMismatchedDealloc(CheckerContext &C, SourceRange Range,
- const Expr *DeallocExpr,
- const RefState *RS) const;
+ const Expr *DeallocExpr, const RefState *RS,
+ SymbolRef Sym) const;
void ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range,
const Expr *DeallocExpr,
const Expr *AllocExpr = 0) const;
@@ -1007,36 +1020,37 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
if (RsBase) {
- bool DeallocMatchesAlloc =
- RsBase->getAllocationFamily() == AF_None ||
- RsBase->getAllocationFamily() == getAllocationFamily(C, ParentExpr);
-
- // Check if an expected deallocation function matches the real one.
- if (!DeallocMatchesAlloc && RsBase->isAllocated()) {
- ReportMismatchedDealloc(C, ArgExpr->getSourceRange(), ParentExpr, RsBase);
- return 0;
- }
-
- // Check double free.
- if (DeallocMatchesAlloc &&
- (RsBase->isReleased() || RsBase->isRelinquished()) &&
+ // Check for double free first.
+ if ((RsBase->isReleased() || RsBase->isRelinquished()) &&
!didPreviousFreeFail(State, SymBase, PreviousRetStatusSymbol)) {
ReportDoubleFree(C, ParentExpr->getSourceRange(), RsBase->isReleased(),
SymBase, PreviousRetStatusSymbol);
return 0;
- }
- // Check if the memory location being freed is the actual location
- // allocated, or an offset.
- RegionOffset Offset = R->getAsOffset();
- if (RsBase->isAllocated() &&
- Offset.isValid() &&
- !Offset.hasSymbolicOffset() &&
- Offset.getOffset() != 0) {
- const Expr *AllocExpr = cast<Expr>(RsBase->getStmt());
- ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,
- AllocExpr);
- return 0;
+ // If the pointer is allocated or escaped, but we are now trying to free it,
+ // check that the call to free is proper.
+ } else if (RsBase->isAllocated() || RsBase->isEscaped()) {
+
+ // Check if an expected deallocation function matches the real one.
+ bool DeallocMatchesAlloc =
+ RsBase->getAllocationFamily() == getAllocationFamily(C, ParentExpr);
+ if (!DeallocMatchesAlloc) {
+ ReportMismatchedDealloc(C, ArgExpr->getSourceRange(),
+ ParentExpr, RsBase, SymBase);
+ return 0;
+ }
+
+ // Check if the memory location being freed is the actual location
+ // allocated, or an offset.
+ RegionOffset Offset = R->getAsOffset();
+ if (Offset.isValid() &&
+ !Offset.hasSymbolicOffset() &&
+ Offset.getOffset() != 0) {
+ const Expr *AllocExpr = cast<Expr>(RsBase->getStmt());
+ ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,
+ AllocExpr);
+ return 0;
+ }
}
}
@@ -1056,7 +1070,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
}
}
- AllocationFamily Family = RsBase ? RsBase->getAllocationFamily() : AF_None;
+ AllocationFamily Family = RsBase ? RsBase->getAllocationFamily()
+ : getAllocationFamily(C, ParentExpr);
// Normal free.
if (Hold)
return State->set<RegionState>(SymBase,
@@ -1067,7 +1082,7 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
RefState::getReleased(Family, ParentExpr));
}
-bool MallocChecker::isTrackedFamily(AllocationFamily Family) const {
+bool MallocChecker::isTrackedByCurrentChecker(AllocationFamily Family) const {
switch (Family) {
case AF_Malloc: {
if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic)
@@ -1081,22 +1096,24 @@ bool MallocChecker::isTrackedFamily(AllocationFamily Family) const {
return true;
}
case AF_None: {
- return true;
+ llvm_unreachable("no family");
}
}
llvm_unreachable("unhandled family");
}
-bool MallocChecker::isTrackedFamily(CheckerContext &C,
- const Stmt *AllocDeallocStmt) const {
- return isTrackedFamily(getAllocationFamily(C, AllocDeallocStmt));
+bool
+MallocChecker::isTrackedByCurrentChecker(CheckerContext &C,
+ const Stmt *AllocDeallocStmt) const {
+ return isTrackedByCurrentChecker(getAllocationFamily(C, AllocDeallocStmt));
}
-bool MallocChecker::isTrackedFamily(CheckerContext &C, SymbolRef Sym) const {
- const RefState *RS = C.getState()->get<RegionState>(Sym);
+bool MallocChecker::isTrackedByCurrentChecker(CheckerContext &C,
+ SymbolRef Sym) const {
- return RS ? isTrackedFamily(RS->getAllocationFamily())
- : isTrackedFamily(AF_None);
+ const RefState *RS = C.getState()->get<RegionState>(Sym);
+ assert(RS);
+ return isTrackedByCurrentChecker(RS->getAllocationFamily());
}
bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) {
@@ -1194,7 +1211,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
!Filter.CNewDeleteChecker)
return;
- if (!isTrackedFamily(C, DeallocExpr))
+ if (!isTrackedByCurrentChecker(C, DeallocExpr))
return;
if (ExplodedNode *N = C.generateSink()) {
@@ -1236,7 +1253,8 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
void MallocChecker::ReportMismatchedDealloc(CheckerContext &C,
SourceRange Range,
const Expr *DeallocExpr,
- const RefState *RS) const {
+ const RefState *RS,
+ SymbolRef Sym) const {
if (!Filter.CMismatchedDeallocatorChecker)
return;
@@ -1266,7 +1284,9 @@ void MallocChecker::ReportMismatchedDealloc(CheckerContext &C,
os << ", not " << DeallocOs.str();
BugReport *R = new BugReport(*BT_MismatchedDealloc, os.str(), N);
+ R->markInteresting(Sym);
R->addRange(Range);
+ R->addVisitor(new MallocBugVisitor(Sym));
C.emitReport(R);
}
}
@@ -1279,7 +1299,7 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,
!Filter.CNewDeleteChecker)
return;
- if (!isTrackedFamily(C, AllocExpr))
+ if (!isTrackedByCurrentChecker(C, AllocExpr))
return;
ExplodedNode *N = C.generateSink();
@@ -1331,7 +1351,7 @@ void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range,
!Filter.CNewDeleteChecker)
return;
- if (!isTrackedFamily(C, Sym))
+ if (!isTrackedByCurrentChecker(C, Sym))
return;
if (ExplodedNode *N = C.generateSink()) {
@@ -1356,7 +1376,7 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range,
!Filter.CNewDeleteChecker)
return;
- if (!isTrackedFamily(C, Sym))
+ if (!isTrackedByCurrentChecker(C, Sym))
return;
if (ExplodedNode *N = C.generateSink()) {
@@ -1510,13 +1530,19 @@ MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
// Find the most recent expression bound to the symbol in the current
// context.
- if (!ReferenceRegion) {
- if (const MemRegion *MR = C.getLocationRegionIfPostStore(N)) {
- SVal Val = State->getSVal(MR);
- if (Val.getAsLocSymbol() == Sym)
- ReferenceRegion = MR;
+ if (!ReferenceRegion) {
+ if (const MemRegion *MR = C.getLocationRegionIfPostStore(N)) {
+ SVal Val = State->getSVal(MR);
+ if (Val.getAsLocSymbol() == Sym) {
+ const VarRegion* VR = MR->getBaseRegion()->getAs<VarRegion>();
+ // Do not show local variables belonging to a function other than
+ // where the error is reported.
+ if (!VR ||
+ (VR->getStackFrame() == LeakContext->getCurrentStackFrame()))
+ ReferenceRegion = MR;
+ }
+ }
}
- }
// Allocation node, is the last node in the current context in which the
// symbol was tracked.
@@ -1532,12 +1558,21 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,
CheckerContext &C) const {
if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic &&
- !Filter.CNewDeleteChecker)
+ !Filter.CNewDeleteLeaksChecker)
return;
- if (!isTrackedFamily(C, Sym))
+ const RefState *RS = C.getState()->get<RegionState>(Sym);
+ assert(RS && "cannot leak an untracked symbol");
+ AllocationFamily Family = RS->getAllocationFamily();
+ if (!isTrackedByCurrentChecker(Family))
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)
+ return;
+
assert(N);
if (!BT_Leak) {
BT_Leak.reset(new BugType("Memory leak", "Memory Error"));
@@ -1570,11 +1605,11 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,
SmallString<200> buf;
llvm::raw_svector_ostream os(buf);
- os << "Memory is never released; potential leak";
if (Region && Region->canPrintPretty()) {
- os << " of memory pointed to by '";
+ os << "Potential leak of memory pointed to by ";
Region->printPretty(os);
- os << '\'';
+ } else {
+ os << "Potential memory leak";
}
BugReport *R = new BugReport(*BT_Leak, os.str(), N,
@@ -1638,26 +1673,39 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
C.addTransition(state->set<RegionState>(RS), N);
}
-void MallocChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
+void MallocChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+
// We will check for double free in the post visit.
- if ((Filter.CMallocOptimistic || Filter.CMallocPessimistic) &&
- isFreeFunction(C.getCalleeDecl(CE), C.getASTContext()))
- return;
+ if (const AnyFunctionCall *FC = dyn_cast<AnyFunctionCall>(&Call)) {
+ const FunctionDecl *FD = FC->getDecl();
+ if (!FD)
+ return;
- if (Filter.CNewDeleteChecker &&
- isStandardNewDelete(C.getCalleeDecl(CE), C.getASTContext()))
- return;
+ if ((Filter.CMallocOptimistic || Filter.CMallocPessimistic) &&
+ isFreeFunction(FD, C.getASTContext()))
+ return;
- // Check use after free, when a freed pointer is passed to a call.
- ProgramStateRef State = C.getState();
- for (CallExpr::const_arg_iterator I = CE->arg_begin(),
- E = CE->arg_end(); I != E; ++I) {
- const Expr *A = *I;
- if (A->getType().getTypePtr()->isAnyPointerType()) {
- SymbolRef Sym = C.getSVal(A).getAsSymbol();
+ if (Filter.CNewDeleteChecker &&
+ isStandardNewDelete(FD, C.getASTContext()))
+ return;
+ }
+
+ // Check if the callee of a method is deleted.
+ if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) {
+ SymbolRef Sym = CC->getCXXThisVal().getAsSymbol();
+ if (!Sym || checkUseAfterFree(Sym, C, CC->getCXXThisExpr()))
+ return;
+ }
+
+ // Check arguments for being used after free.
+ for (unsigned I = 0, E = Call.getNumArgs(); I != E; ++I) {
+ SVal ArgSVal = Call.getArgSVal(I);
+ if (ArgSVal.getAs<Loc>()) {
+ SymbolRef Sym = ArgSVal.getAsSymbol();
if (!Sym)
continue;
- if (checkUseAfterFree(Sym, C, A))
+ if (checkUseAfterFree(Sym, C, Call.getArgExpr(I)))
return;
}
}
@@ -1976,8 +2024,10 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State,
SymbolRef sym = *I;
if (const RefState *RS = State->get<RegionState>(sym)) {
- if (RS->isAllocated() && CheckRefState(RS))
+ if (RS->isAllocated() && CheckRefState(RS)) {
State = State->remove<RegionState>(sym);
+ State = State->set<RegionState>(sym, RefState::getEscaped(RS));
+ }
}
}
return State;
@@ -2042,7 +2092,7 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N,
} else if (isReleased(RS, RSPrev, S)) {
Msg = "Memory is released";
StackHint = new StackHintGeneratorForSymbol(Sym,
- "Returned released memory");
+ "Returning; memory was released");
} else if (isRelinquished(RS, RSPrev, S)) {
Msg = "Memory ownership is transfered";
StackHint = new StackHintGeneratorForSymbol(Sym, "");
@@ -2102,6 +2152,14 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State,
}
}
+void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {
+ registerCStringCheckerBasic(mgr);
+ mgr.registerChecker<MallocChecker>()->Filter.CNewDeleteLeaksChecker = true;
+ // We currently treat NewDeleteLeaks checker as a subchecker of NewDelete
+ // checker.
+ mgr.registerChecker<MallocChecker>()->Filter.CNewDeleteChecker = true;
+}
+
#define REGISTER_CHECKER(name) \
void ento::register##name(CheckerManager &mgr) {\
registerCStringCheckerBasic(mgr); \
diff --git a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
index ce7d4ccf7a0a..d29f34fb03e2 100644
--- a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
@@ -188,7 +188,7 @@ public:
for (CallExpr::const_arg_iterator ai = i->AllocCall->arg_begin(),
ae = i->AllocCall->arg_end(); ai != ae; ++ai) {
- if (!(*ai)->getType()->isIntegerType())
+ if (!(*ai)->getType()->isIntegralOrUnscopedEnumerationType())
continue;
SizeofFinder SFinder;
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
index 79409e85bda4..0f456ea8d785 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
@@ -37,6 +37,8 @@
#include "llvm/ADT/StringExtras.h"
#include <cstdarg>
+#include "AllocationDiagnostics.h"
+
using namespace clang;
using namespace ento;
using llvm::StrInStrNoCase;
@@ -324,7 +326,7 @@ void RefVal::print(raw_ostream &Out) const {
break;
case RefVal::ErrorOverAutorelease:
- Out << "Over autoreleased";
+ Out << "Over-autoreleased";
break;
case RefVal::ErrorReturnedNotOwned:
@@ -1114,12 +1116,14 @@ RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) {
// correctly.
ScratchArgs = AF.add(ScratchArgs, 12, StopTracking);
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
- } else if (FName == "dispatch_set_context") {
+ } else if (FName == "dispatch_set_context" ||
+ FName == "xpc_connection_set_context") {
// <rdar://problem/11059275> - The analyzer currently doesn't have
// a good way to reason about the finalizer function for libdispatch.
// If we pass a context object that is memory managed, stop tracking it.
+ // <rdar://problem/13783514> - Same problem, but for XPC.
// FIXME: this hack should possibly go away once we can handle
- // libdispatch finalizers.
+ // libdispatch and XPC finalizers.
ScratchArgs = AF.add(ScratchArgs, 1, StopTracking);
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
} else if (FName.startswith("NSLog")) {
@@ -1660,10 +1664,10 @@ namespace {
class OverAutorelease : public CFRefBug {
public:
OverAutorelease()
- : CFRefBug("Object sent -autorelease too many times") {}
+ : CFRefBug("Object autoreleased too many times") {}
const char *getDescription() const {
- return "Object sent -autorelease too many times";
+ return "Object autoreleased too many times";
}
};
@@ -1773,11 +1777,11 @@ namespace {
class CFRefLeakReport : public CFRefReport {
const MemRegion* AllocBinding;
-
public:
CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled,
const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym,
- CheckerContext &Ctx);
+ CheckerContext &Ctx,
+ bool IncludeAllocationLine);
PathDiagnosticLocation getLocation(const SourceManager &SM) const {
assert(Location.isValid());
@@ -2048,7 +2052,7 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
return 0;
assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount());
- os << "Object sent -autorelease message";
+ os << "Object autoreleased";
break;
}
@@ -2134,32 +2138,86 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
// Find the first node in the current function context that referred to the
// tracked symbol and the memory location that value was stored to. Note, the
// value is only reported if the allocation occurred in the same function as
-// the leak.
-static std::pair<const ExplodedNode*,const MemRegion*>
+// the leak. The function can also return a location context, which should be
+// treated as interesting.
+struct AllocationInfo {
+ const ExplodedNode* N;
+ const MemRegion *R;
+ const LocationContext *InterestingMethodContext;
+ AllocationInfo(const ExplodedNode *InN,
+ const MemRegion *InR,
+ const LocationContext *InInterestingMethodContext) :
+ N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {}
+};
+
+static AllocationInfo
GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N,
SymbolRef Sym) {
- const ExplodedNode *Last = N;
+ const ExplodedNode *AllocationNode = N;
+ const ExplodedNode *AllocationNodeInCurrentContext = N;
const MemRegion* FirstBinding = 0;
const LocationContext *LeakContext = N->getLocationContext();
+ // The location context of the init method called on the leaked object, if
+ // available.
+ const LocationContext *InitMethodContext = 0;
+
while (N) {
ProgramStateRef St = N->getState();
+ const LocationContext *NContext = N->getLocationContext();
if (!getRefBinding(St, Sym))
break;
StoreManager::FindUniqueBinding FB(Sym);
StateMgr.iterBindings(St, FB);
- if (FB) FirstBinding = FB.getRegion();
+
+ if (FB) {
+ const MemRegion *R = FB.getRegion();
+ const VarRegion *VR = R->getBaseRegion()->getAs<VarRegion>();
+ // Do not show local variables belonging to a function other than
+ // where the error is reported.
+ if (!VR || VR->getStackFrame() == LeakContext->getCurrentStackFrame())
+ FirstBinding = R;
+ }
- // Allocation node, is the last node in the current context in which the
- // symbol was tracked.
- if (N->getLocationContext() == LeakContext)
- Last = N;
+ // AllocationNode is the last node in which the symbol was tracked.
+ AllocationNode = N;
+
+ // AllocationNodeInCurrentContext, is the last node in the current context
+ // in which the symbol was tracked.
+ if (NContext == LeakContext)
+ AllocationNodeInCurrentContext = N;
+
+ // Find the last init that was called on the given symbol and store the
+ // init method's location context.
+ if (!InitMethodContext)
+ if (Optional<CallEnter> CEP = N->getLocation().getAs<CallEnter>()) {
+ const Stmt *CE = CEP->getCallExpr();
+ if (const ObjCMessageExpr *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) {
+ const Stmt *RecExpr = ME->getInstanceReceiver();
+ if (RecExpr) {
+ SVal RecV = St->getSVal(RecExpr, NContext);
+ if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym)
+ InitMethodContext = CEP->getCalleeContext();
+ }
+ }
+ }
N = N->pred_empty() ? NULL : *(N->pred_begin());
}
+ // If we are reporting a leak of the object that was allocated with alloc,
+ // mark its init method as interesting.
+ const LocationContext *InterestingMethodContext = 0;
+ if (InitMethodContext) {
+ const ProgramPoint AllocPP = AllocationNode->getLocation();
+ if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
+ if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>())
+ if (ME->getMethodFamily() == OMF_alloc)
+ InterestingMethodContext = InitMethodContext;
+ }
+
// If allocation happened in a function different from the leak node context,
// do not report the binding.
assert(N && "Could not find allocation node");
@@ -2167,7 +2225,9 @@ GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N,
FirstBinding = 0;
}
- return std::make_pair(Last, FirstBinding);
+ return AllocationInfo(AllocationNodeInCurrentContext,
+ FirstBinding,
+ InterestingMethodContext);
}
PathDiagnosticPiece*
@@ -2190,12 +2250,12 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
// We are reporting a leak. Walk up the graph to get to the first node where
// the symbol appeared, and also get the first VarDecl that tracked object
// is stored to.
- const ExplodedNode *AllocNode = 0;
- const MemRegion* FirstBinding = 0;
-
- llvm::tie(AllocNode, FirstBinding) =
+ AllocationInfo AllocI =
GetAllocationSite(BRC.getStateManager(), EndN, Sym);
+ const MemRegion* FirstBinding = AllocI.R;
+ BR.markInteresting(AllocI.InterestingMethodContext);
+
SourceManager& SM = BRC.getSourceManager();
// Compute an actual location for the leak. Sometimes a leak doesn't
@@ -2267,8 +2327,9 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
bool GCEnabled, const SummaryLogTy &Log,
ExplodedNode *n, SymbolRef sym,
- CheckerContext &Ctx)
-: CFRefReport(D, LOpts, GCEnabled, Log, n, sym, false) {
+ CheckerContext &Ctx,
+ bool IncludeAllocationLine)
+ : CFRefReport(D, LOpts, GCEnabled, Log, n, sym, false) {
// Most bug reports are cached at the location where they occurred.
// With leaks, we want to unique them by the location where they were
@@ -2282,9 +2343,13 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
const SourceManager& SMgr = Ctx.getSourceManager();
- llvm::tie(AllocNode, AllocBinding) = // Set AllocBinding.
+ AllocationInfo AllocI =
GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym);
+ AllocNode = AllocI.N;
+ AllocBinding = AllocI.R;
+ markInteresting(AllocI.InterestingMethodContext);
+
// Get the SourceLocation for the allocation site.
// FIXME: This will crash the analyzer if an allocation comes from an
// implicit call. (Currently there are no such allocations in Cocoa, though.)
@@ -2295,8 +2360,17 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
else
AllocStmt = P.castAs<PostStmt>().getStmt();
assert(AllocStmt && "All allocations must come from explicit calls");
- Location = PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
- n->getLocationContext());
+
+ PathDiagnosticLocation AllocLocation =
+ PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
+ AllocNode->getLocationContext());
+ Location = AllocLocation;
+
+ // Set uniqieing info, which will be used for unique the bug reports. The
+ // leaks should be uniqued on the allocation site.
+ UniqueingLocation = AllocLocation;
+ UniqueingDecl = AllocNode->getLocationContext()->getDecl();
+
// Fill in the description of the bug.
Description.clear();
llvm::raw_string_ostream os(Description);
@@ -2305,9 +2379,13 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
os << "(when using garbage collection) ";
os << "of an object";
- // FIXME: AllocBinding doesn't get populated for RegionStore yet.
- if (AllocBinding)
+ if (AllocBinding) {
os << " stored into '" << AllocBinding->getString() << '\'';
+ if (IncludeAllocationLine) {
+ FullSourceLoc SL(AllocStmt->getLocStart(), Ctx.getSourceManager());
+ os << " (allocated on line " << SL.getSpellingLineNumber() << ")";
+ }
+ }
addVisitor(new CFRefLeakReportVisitor(sym, GCEnabled, Log));
}
@@ -2348,8 +2426,14 @@ class RetainCountChecker
mutable SummaryLogTy SummaryLog;
mutable bool ShouldResetSummaryLog;
+ /// Optional setting to indicate if leak reports should include
+ /// the allocation line.
+ mutable bool IncludeAllocationLine;
+
public:
- RetainCountChecker() : ShouldResetSummaryLog(false) {}
+ RetainCountChecker(AnalyzerOptions &AO)
+ : ShouldResetSummaryLog(false),
+ IncludeAllocationLine(shouldIncludeAllocationSiteInLeakDiagnostics(AO)) {}
virtual ~RetainCountChecker() {
DeleteContainerSeconds(DeadSymbolTags);
@@ -3294,7 +3378,8 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
CFRefReport *report =
new CFRefLeakReport(*getLeakAtReturnBug(LOpts, GCEnabled),
LOpts, GCEnabled, SummaryLog,
- N, Sym, C);
+ N, Sym, C, IncludeAllocationLine);
+
C.emitReport(report);
}
}
@@ -3480,10 +3565,12 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
if (N) {
SmallString<128> sbuf;
llvm::raw_svector_ostream os(sbuf);
- os << "Object over-autoreleased: object was sent -autorelease ";
+ os << "Object was autoreleased ";
if (V.getAutoreleaseCount() > 1)
- os << V.getAutoreleaseCount() << " times ";
- os << "but the object has a +" << V.getCount() << " retain count";
+ os << V.getAutoreleaseCount() << " times but the object ";
+ else
+ os << "but ";
+ os << "has a +" << V.getCount() << " retain count";
if (!overAutorelease)
overAutorelease.reset(new OverAutorelease());
@@ -3534,7 +3621,8 @@ RetainCountChecker::processLeaks(ProgramStateRef state,
assert(BT && "BugType not initialized.");
CFRefLeakReport *report = new CFRefLeakReport(*BT, LOpts, GCEnabled,
- SummaryLog, N, *I, Ctx);
+ SummaryLog, N, *I, Ctx,
+ IncludeAllocationLine);
Ctx.emitReport(report);
}
}
@@ -3656,6 +3744,6 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State,
//===----------------------------------------------------------------------===//
void ento::registerRetainCountChecker(CheckerManager &Mgr) {
- Mgr.registerChecker<RetainCountChecker>();
+ Mgr.registerChecker<RetainCountChecker>(Mgr.getAnalyzerOptions());
}
diff --git a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
index 7a5d99360108..ed96c401a7aa 100644
--- a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
@@ -55,8 +55,17 @@ void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS,
// void test() {
// return foo();
// }
- if (RT.isNull() || !RT->isVoidType())
- emitUndef(C, RetE);
+ if (!RT.isNull() && RT->isVoidType())
+ return;
+
+ // Not all blocks have explicitly-specified return types; if the return type
+ // is not available, but the return value expression has 'void' type, assume
+ // Sema already checked it.
+ if (RT.isNull() && isa<BlockDecl>(SFC->getDecl()) &&
+ RetE->getType()->isVoidType())
+ return;
+
+ emitUndef(C, RetE);
return;
}