aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm/tools/clang/lib/StaticAnalyzer
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2015-05-27 20:44:45 +0000
committerDimitry Andric <dim@FreeBSD.org>2015-05-27 20:44:45 +0000
commit33956c43007dfb106f401e3c14abc011a4b1d4ca (patch)
tree50a603f7e1932cd42f58e26687ce907933014db0 /contrib/llvm/tools/clang/lib/StaticAnalyzer
parentff0cc061ecf297f1556e906d229826fd709f37d6 (diff)
parent5e20cdd81c44a443562a09007668ffdf76c455af (diff)
Merge clang trunk r238337 from ^/vendor/clang/dist, resolve conflicts,
and preserve our customizations, where necessary.
Notes
Notes: svn path=/projects/clang-trunk/; revision=283633
Diffstat (limited to 'contrib/llvm/tools/clang/lib/StaticAnalyzer')
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp4
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp1
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td6
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp2
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp2
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h2
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp2
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp22
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp507
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp8
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp351
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp59
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp41
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp11
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp8
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp15
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp2
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp8
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp15
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp22
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp28
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp2
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.h2
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp2
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp13
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp2
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h4
27 files changed, 772 insertions, 369 deletions
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index e91a7e16802e..0f5741bf9e68 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -1922,10 +1922,6 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
if (!evalFunction)
return false;
- // Make sure each function sets its own description.
- // (But don't bother in a release build.)
- assert(!(CurrentFunctionDescription = nullptr));
-
// Check and evaluate the call.
(this->*evalFunction)(C, CE);
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
index 45768b2cdf76..0beb917833d6 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
@@ -28,6 +28,7 @@ using namespace ento;
static bool isArc4RandomAvailable(const ASTContext &Ctx) {
const llvm::Triple &T = Ctx.getTargetInfo().getTriple();
return T.getVendor() == llvm::Triple::Apple ||
+ T.getOS() == llvm::Triple::CloudABI ||
T.getOS() == llvm::Triple::FreeBSD ||
T.getOS() == llvm::Triple::NetBSD ||
T.getOS() == llvm::Triple::OpenBSD ||
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td
index ba5b4fa3c66c..d1d6ac277ffe 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td
@@ -295,7 +295,7 @@ def UnixAPIChecker : Checker<"API">,
HelpText<"Check calls to various UNIX/Posix functions">,
DescFile<"UnixAPIChecker.cpp">;
-def MallocPessimistic : Checker<"Malloc">,
+def MallocChecker: Checker<"Malloc">,
HelpText<"Check for memory leaks, double free, and use-after-free problems. Traces memory managed by malloc()/free().">,
DescFile<"MallocChecker.cpp">;
@@ -315,10 +315,6 @@ def ChrootChecker : Checker<"Chroot">,
HelpText<"Check improper use of chroot">,
DescFile<"ChrootChecker.cpp">;
-def MallocOptimistic : Checker<"MallocWithAnnotations">,
- HelpText<"Check for memory leaks, double free, and use-after-free problems. Traces memory managed by malloc()/free(). Assumes that all user-defined functions which might free a pointer are annotated.">,
- DescFile<"MallocChecker.cpp">;
-
def PthreadLockChecker : Checker<"PthreadLock">,
HelpText<"Simple lock -> unlock checker">,
DescFile<"PthreadLockChecker.cpp">;
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
index c1ea76735c41..f4be5b3e82f4 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
@@ -142,7 +142,7 @@ public:
: cfg(cfg), Ctx(ctx), BR(br), Checker(checker), AC(ac), Parents(parents),
Escaped(escaped), currentBlock(nullptr) {}
- virtual ~DeadStoreObs() {}
+ ~DeadStoreObs() override {}
bool isLive(const LiveVariables::LivenessValues &Live, const VarDecl *D) {
if (Live.isLive(D))
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
index 4ee02230649d..2e442c7d875c 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
@@ -162,7 +162,7 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
os.flush();
BugReport *report =
new BugReport(*BT_null,
- buf.empty() ? BT_null->getDescription() : buf.str(),
+ buf.empty() ? BT_null->getDescription() : StringRef(buf),
N);
bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S), *report);
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
index b7549fd17da9..d38d63cd05ce 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
@@ -13,6 +13,8 @@
#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_INTERCHECKERAPI_H
#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_INTERCHECKERAPI_H
namespace clang {
+class CheckerManager;
+
namespace ento {
/// Register the checker which evaluates CString API calls.
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
index 1926600b6d6e..02c12095b5a5 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
@@ -336,7 +336,7 @@ const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
llvm::raw_svector_ostream os(PropNameWithUnderscore);
os << '_' << PropName;
}
- if (IvarName == PropNameWithUnderscore.str())
+ if (IvarName == PropNameWithUnderscore)
return Iv;
}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
index 13ea4d3ed090..52e29368cea3 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
@@ -137,7 +137,7 @@ private:
public:
SecKeychainBugVisitor(SymbolRef S) : Sym(S) {}
- virtual ~SecKeychainBugVisitor() {}
+ ~SecKeychainBugVisitor() override {}
void Profile(llvm::FoldingSetNodeID &ID) const override {
static int X = 0;
@@ -292,7 +292,11 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
// If it is a call to an allocator function, it could be a double allocation.
idx = getTrackedFunctionIndex(funName, true);
if (idx != InvalidIdx) {
- const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
+ unsigned paramIdx = FunctionsToTrack[idx].Param;
+ if (CE->getNumArgs() <= paramIdx)
+ return;
+
+ const Expr *ArgExpr = CE->getArg(paramIdx);
if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C))
if (const AllocationState *AS = State->get<AllocatedData>(V)) {
if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) {
@@ -325,8 +329,12 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
if (idx == InvalidIdx)
return;
+ unsigned paramIdx = FunctionsToTrack[idx].Param;
+ if (CE->getNumArgs() <= paramIdx)
+ return;
+
// Check the argument to the deallocator.
- const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
+ const Expr *ArgExpr = CE->getArg(paramIdx);
SVal ArgSVal = State->getSVal(ArgExpr, C.getLocationContext());
// Undef is reported by another checker.
@@ -499,9 +507,11 @@ MacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N,
while (N) {
if (!N->getState()->get<AllocatedData>(Sym))
break;
- // Allocation node, is the last node in the current context in which the
- // symbol was tracked.
- if (N->getLocationContext() == LeakContext)
+ // Allocation node, is the last node in the current or parent context in
+ // which the symbol was tracked.
+ const LocationContext *NContext = N->getLocationContext();
+ if (NContext == LeakContext ||
+ NContext->isParentOf(LeakContext))
AllocNode = N;
N = N->pred_empty() ? nullptr : *(N->pred_begin());
}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index aee5a43048b9..0cf009407349 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -43,12 +43,15 @@ enum AllocationFamily {
AF_Malloc,
AF_CXXNew,
AF_CXXNewArray,
- AF_IfNameIndex
+ AF_IfNameIndex,
+ AF_Alloca
};
class RefState {
enum Kind { // Reference to allocated memory.
Allocated,
+ // Reference to zero-allocated memory.
+ AllocatedOfSizeZero,
// Reference to released/freed memory.
Released,
// The responsibility for freeing resources has transferred from
@@ -61,8 +64,8 @@ class RefState {
};
const Stmt *S;
- unsigned K : 2; // Kind enum, but stored as a bitfield.
- unsigned Family : 30; // Rest of 32-bit word, currently just an allocation
+ unsigned K : 3; // Kind enum, but stored as a bitfield.
+ unsigned Family : 29; // Rest of 32-bit word, currently just an allocation
// family.
RefState(Kind k, const Stmt *s, unsigned family)
@@ -71,6 +74,7 @@ class RefState {
}
public:
bool isAllocated() const { return K == Allocated; }
+ bool isAllocatedOfSizeZero() const { return K == AllocatedOfSizeZero; }
bool isReleased() const { return K == Released; }
bool isRelinquished() const { return K == Relinquished; }
bool isEscaped() const { return K == Escaped; }
@@ -86,6 +90,10 @@ public:
static RefState getAllocated(unsigned family, const Stmt *s) {
return RefState(Allocated, s, family);
}
+ static RefState getAllocatedOfSizeZero(const RefState *RS) {
+ return RefState(AllocatedOfSizeZero, RS->getStmt(),
+ RS->getAllocationFamily());
+ }
static RefState getReleased(unsigned family, const Stmt *s) {
return RefState(Released, s, family);
}
@@ -106,6 +114,7 @@ public:
switch (static_cast<Kind>(K)) {
#define CASE(ID) case ID: OS << #ID; break;
CASE(Allocated)
+ CASE(AllocatedOfSizeZero)
CASE(Released)
CASE(Relinquished)
CASE(Escaped)
@@ -160,16 +169,16 @@ class MallocChecker : public Checker<check::DeadSymbols,
{
public:
MallocChecker()
- : II_malloc(nullptr), II_free(nullptr), II_realloc(nullptr),
- II_calloc(nullptr), II_valloc(nullptr), II_reallocf(nullptr),
- II_strndup(nullptr), II_strdup(nullptr), II_kmalloc(nullptr),
- II_if_nameindex(nullptr), II_if_freenameindex(nullptr) {}
+ : II_alloca(nullptr), II_malloc(nullptr), II_free(nullptr),
+ II_realloc(nullptr), II_calloc(nullptr), II_valloc(nullptr),
+ II_reallocf(nullptr), II_strndup(nullptr), II_strdup(nullptr),
+ II_kmalloc(nullptr), II_if_nameindex(nullptr),
+ II_if_freenameindex(nullptr) {}
/// In pessimistic mode, the checker assumes that it does not know which
/// functions might free the memory.
enum CheckKind {
- CK_MallocPessimistic,
- CK_MallocOptimistic,
+ CK_MallocChecker,
CK_NewDeleteChecker,
CK_NewDeleteLeaksChecker,
CK_MismatchedDeallocatorChecker,
@@ -182,6 +191,8 @@ public:
MOK_Any
};
+ DefaultBool IsOptimistic;
+
DefaultBool ChecksEnabled[CK_NumCheckKinds];
CheckName CheckNames[CK_NumCheckKinds];
@@ -216,11 +227,14 @@ private:
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_FreeAlloca[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, *II_if_nameindex, *II_if_freenameindex;
+ mutable std::unique_ptr<BugType> BT_UseZerroAllocated[CK_NumCheckKinds];
+ mutable IdentifierInfo *II_alloca, *II_malloc, *II_free, *II_realloc,
+ *II_calloc, *II_valloc, *II_reallocf, *II_strndup,
+ *II_strdup, *II_kmalloc, *II_if_nameindex,
+ *II_if_freenameindex;
mutable Optional<uint64_t> KernelZeroFlagVal;
void initIdentifierInfo(ASTContext &C) const;
@@ -252,22 +266,24 @@ private:
MemoryOperationKind MemKind) const;
bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const;
///@}
+
+ /// \brief Perform a zero-allocation check.
+ ProgramStateRef ProcessZeroAllocation(CheckerContext &C, const Expr *E,
+ const unsigned AllocationSizeArg,
+ ProgramStateRef State) const;
+
ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
const CallExpr *CE,
- const OwnershipAttr* Att) const;
+ const OwnershipAttr* Att,
+ ProgramStateRef State) const;
static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
- const Expr *SizeEx, SVal Init,
- ProgramStateRef State,
- AllocationFamily Family = AF_Malloc) {
- return MallocMemAux(C, CE,
- State->getSVal(SizeEx, C.getLocationContext()),
- Init, State, Family);
- }
-
+ const Expr *SizeEx, SVal Init,
+ ProgramStateRef State,
+ AllocationFamily Family = AF_Malloc);
static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
- SVal SizeEx, SVal Init,
- ProgramStateRef State,
- AllocationFamily Family = AF_Malloc);
+ SVal SizeEx, SVal Init,
+ 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).
@@ -281,7 +297,8 @@ private:
AllocationFamily Family = AF_Malloc);
ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE,
- const OwnershipAttr* Att) const;
+ const OwnershipAttr* Att,
+ ProgramStateRef State) const;
ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE,
ProgramStateRef state, unsigned Num,
bool Hold,
@@ -295,14 +312,19 @@ private:
bool ReturnsNullOnFailure = false) const;
ProgramStateRef ReallocMem(CheckerContext &C, const CallExpr *CE,
- bool FreesMemOnFailure) const;
- static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE);
+ bool FreesMemOnFailure,
+ ProgramStateRef State) const;
+ static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State);
///\brief Check if the memory associated with this symbol was released.
bool isReleased(SymbolRef Sym, CheckerContext &C) const;
bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const;
+ void checkUseZeroAllocated(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
@@ -330,15 +352,20 @@ private:
/// Tells if a given family/call/symbol is tracked by the current checker.
/// Sets CheckKind to the kind of the checker responsible for this
/// family/call/symbol.
- Optional<CheckKind> getCheckIfTracked(AllocationFamily Family) const;
+ Optional<CheckKind> getCheckIfTracked(AllocationFamily Family,
+ bool IsALeakCheck = false) const;
Optional<CheckKind> getCheckIfTracked(CheckerContext &C,
- const Stmt *AllocDeallocStmt) const;
- Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym) const;
+ const Stmt *AllocDeallocStmt,
+ bool IsALeakCheck = false) const;
+ Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym,
+ bool IsALeakCheck = false) 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 ReportFreeAlloca(CheckerContext &C, SVal ArgVal,
+ SourceRange Range) const;
void ReportMismatchedDealloc(CheckerContext &C, SourceRange Range,
const Expr *DeallocExpr, const RefState *RS,
SymbolRef Sym, bool OwnershipTransferred) const;
@@ -352,6 +379,9 @@ private:
void ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const;
+ void ReportUseZeroAllocated(CheckerContext &C, SourceRange Range,
+ 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,
@@ -384,7 +414,7 @@ private:
MallocBugVisitor(SymbolRef S, bool isLeak = false)
: Sym(S), Mode(Normal), FailedReallocSymbol(nullptr), IsLeak(isLeak) {}
- virtual ~MallocBugVisitor() {}
+ ~MallocBugVisitor() override {}
void Profile(llvm::FoldingSetNodeID &ID) const override {
static int X = 0;
@@ -396,7 +426,9 @@ private:
const Stmt *Stmt) {
// Did not track -> allocated. Other state (released) -> allocated.
return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) &&
- (S && S->isAllocated()) && (!SPrev || !SPrev->isAllocated()));
+ (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) &&
+ (!SPrev || !(SPrev->isAllocated() ||
+ SPrev->isAllocatedOfSizeZero())));
}
inline bool isReleased(const RefState *S, const RefState *SPrev,
@@ -422,7 +454,9 @@ private:
// check. If we have to handle more cases here, it might be cleaner just
// to track this extra bit in the state itself.
return ((!Stmt || !isa<CallExpr>(Stmt)) &&
- (S && S->isAllocated()) && (SPrev && !SPrev->isAllocated()));
+ (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) &&
+ (SPrev && !(SPrev->isAllocated() ||
+ SPrev->isAllocatedOfSizeZero())));
}
PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
@@ -497,6 +531,7 @@ public:
void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const {
if (II_malloc)
return;
+ II_alloca = &Ctx.Idents.get("alloca");
II_malloc = &Ctx.Idents.get("malloc");
II_free = &Ctx.Idents.get("free");
II_realloc = &Ctx.Idents.get("realloc");
@@ -517,6 +552,9 @@ bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const {
if (isCMemFunction(FD, C, AF_IfNameIndex, MemoryOperationKind::MOK_Any))
return true;
+ if (isCMemFunction(FD, C, AF_Alloca, MemoryOperationKind::MOK_Any))
+ return true;
+
if (isStandardNewDelete(FD, C))
return true;
@@ -560,12 +598,17 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD,
if (FunI == II_if_nameindex)
return true;
}
+
+ if (Family == AF_Alloca && CheckAlloc) {
+ if (FunI == II_alloca)
+ return true;
+ }
}
if (Family != AF_Malloc)
return false;
- if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs()) {
+ if (IsOptimistic && FD->hasAttrs()) {
for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {
OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind();
if(OwnKind == OwnershipAttr::Takes || OwnKind == OwnershipAttr::Holds) {
@@ -712,6 +755,8 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
return;
if (CE->getNumArgs() < 3) {
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+ if (CE->getNumArgs() == 1)
+ State = ProcessZeroAllocation(C, CE, 0, State);
} else if (CE->getNumArgs() == 3) {
llvm::Optional<ProgramStateRef> MaybeState =
performKernelMalloc(CE, C, State);
@@ -731,31 +776,43 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
if (CE->getNumArgs() < 1)
return;
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+ State = ProcessZeroAllocation(C, CE, 0, State);
} else if (FunI == II_realloc) {
- State = ReallocMem(C, CE, false);
+ State = ReallocMem(C, CE, false, State);
+ State = ProcessZeroAllocation(C, CE, 1, State);
} else if (FunI == II_reallocf) {
- State = ReallocMem(C, CE, true);
+ State = ReallocMem(C, CE, true, State);
+ State = ProcessZeroAllocation(C, CE, 1, State);
} else if (FunI == II_calloc) {
- State = CallocMem(C, CE);
+ State = CallocMem(C, CE, State);
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ State = ProcessZeroAllocation(C, CE, 1, State);
} else if (FunI == II_free) {
State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
} else if (FunI == II_strdup) {
State = MallocUpdateRefState(C, CE, State);
} else if (FunI == II_strndup) {
State = MallocUpdateRefState(C, CE, State);
- }
- else if (isStandardNewDelete(FD, C.getASTContext())) {
+ } else if (FunI == II_alloca) {
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
+ AF_Alloca);
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ } else if (isStandardNewDelete(FD, C.getASTContext())) {
// Process direct calls to operator new/new[]/delete/delete[] functions
// as distinct from new/new[]/delete/delete[] expressions that are
// processed by the checkPostStmt callbacks for CXXNewExpr and
// CXXDeleteExpr.
OverloadedOperatorKind K = FD->getOverloadedOperator();
- if (K == OO_New)
+ if (K == OO_New) {
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
AF_CXXNew);
- else if (K == OO_Array_New)
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ }
+ else if (K == OO_Array_New) {
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
AF_CXXNewArray);
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ }
else if (K == OO_Delete || K == OO_Array_Delete)
State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
else
@@ -770,19 +827,18 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
}
}
- if (ChecksEnabled[CK_MallocOptimistic] ||
- ChecksEnabled[CK_MismatchedDeallocatorChecker]) {
+ if (IsOptimistic || ChecksEnabled[CK_MismatchedDeallocatorChecker]) {
// Check all the attributes, if there are any.
// There can be multiple of these attributes.
if (FD->hasAttrs())
for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {
switch (I->getOwnKind()) {
case OwnershipAttr::Returns:
- State = MallocMemReturnsAttr(C, CE, I);
+ State = MallocMemReturnsAttr(C, CE, I, State);
break;
case OwnershipAttr::Takes:
case OwnershipAttr::Holds:
- State = FreeMemAttr(C, CE, I);
+ State = FreeMemAttr(C, CE, I, State);
break;
}
}
@@ -790,6 +846,68 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
C.addTransition(State);
}
+// Performs a 0-sized allocations check.
+ProgramStateRef MallocChecker::ProcessZeroAllocation(CheckerContext &C,
+ const Expr *E,
+ const unsigned AllocationSizeArg,
+ ProgramStateRef State) const {
+ if (!State)
+ return nullptr;
+
+ const Expr *Arg = nullptr;
+
+ if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
+ Arg = CE->getArg(AllocationSizeArg);
+ }
+ else if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) {
+ if (NE->isArray())
+ Arg = NE->getArraySize();
+ else
+ return State;
+ }
+ else
+ llvm_unreachable("not a CallExpr or CXXNewExpr");
+
+ assert(Arg);
+
+ Optional<DefinedSVal> DefArgVal =
+ State->getSVal(Arg, C.getLocationContext()).getAs<DefinedSVal>();
+
+ if (!DefArgVal)
+ return State;
+
+ // Check if the allocation size is 0.
+ ProgramStateRef TrueState, FalseState;
+ SValBuilder &SvalBuilder = C.getSValBuilder();
+ DefinedSVal Zero =
+ SvalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>();
+
+ std::tie(TrueState, FalseState) =
+ State->assume(SvalBuilder.evalEQ(State, *DefArgVal, Zero));
+
+ if (TrueState && !FalseState) {
+ SVal retVal = State->getSVal(E, C.getLocationContext());
+ SymbolRef Sym = retVal.getAsLocSymbol();
+ if (!Sym)
+ return State;
+
+ const RefState *RS = State->get<RegionState>(Sym);
+ if (!RS)
+ return State; // TODO: change to assert(RS); after realloc() will
+ // guarantee have a RegionState attached.
+
+ if (!RS->isAllocated())
+ return State;
+
+ return TrueState->set<RegionState>(Sym,
+ RefState::getAllocatedOfSizeZero(RS));
+ }
+
+ // Assume the value is non-zero going forward.
+ assert(FalseState);
+ return FalseState;
+}
+
static QualType getDeepPointeeType(QualType T) {
QualType Result = T, PointeeType = T->getPointeeType();
while (!PointeeType.isNull()) {
@@ -849,6 +967,7 @@ void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
// existing binding.
State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray
: AF_CXXNew);
+ State = ProcessZeroAllocation(C, NE, 0, State);
C.addTransition(State);
}
@@ -919,15 +1038,31 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
ProgramStateRef
MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
- const OwnershipAttr *Att) const {
+ const OwnershipAttr *Att,
+ ProgramStateRef State) const {
+ if (!State)
+ return nullptr;
+
if (Att->getModule() != II_malloc)
return nullptr;
OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
if (I != E) {
- return MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), C.getState());
+ return MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), State);
}
- return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), C.getState());
+ return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), State);
+}
+
+ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
+ const CallExpr *CE,
+ const Expr *SizeEx, SVal Init,
+ ProgramStateRef State,
+ AllocationFamily Family) {
+ if (!State)
+ return nullptr;
+
+ return MallocMemAux(C, CE, State->getSVal(SizeEx, C.getLocationContext()),
+ Init, State, Family);
}
ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
@@ -935,6 +1070,8 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
SVal Size, SVal Init,
ProgramStateRef State,
AllocationFamily Family) {
+ if (!State)
+ return nullptr;
// We expect the malloc functions to return a pointer.
if (!Loc::isLocType(CE->getType()))
@@ -976,6 +1113,9 @@ ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
const Expr *E,
ProgramStateRef State,
AllocationFamily Family) {
+ if (!State)
+ return nullptr;
+
// Get the return value.
SVal retVal = State->getSVal(E, C.getLocationContext());
@@ -992,11 +1132,14 @@ ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
const CallExpr *CE,
- const OwnershipAttr *Att) const {
+ const OwnershipAttr *Att,
+ ProgramStateRef State) const {
+ if (!State)
+ return nullptr;
+
if (Att->getModule() != II_malloc)
return nullptr;
- ProgramStateRef State = C.getState();
bool ReleasedAllocated = false;
for (const auto &Arg : Att->args()) {
@@ -1011,15 +1154,18 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
const CallExpr *CE,
- ProgramStateRef state,
+ ProgramStateRef State,
unsigned Num,
bool Hold,
bool &ReleasedAllocated,
bool ReturnsNullOnFailure) const {
+ if (!State)
+ return nullptr;
+
if (CE->getNumArgs() < (Num + 1))
return nullptr;
- return FreeMemAux(C, CE->getArg(Num), CE, state, Hold,
+ return FreeMemAux(C, CE->getArg(Num), CE, State, Hold,
ReleasedAllocated, ReturnsNullOnFailure);
}
@@ -1065,6 +1211,9 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C,
if (isCMemFunction(FD, Ctx, AF_IfNameIndex, MemoryOperationKind::MOK_Any))
return AF_IfNameIndex;
+ if (isCMemFunction(FD, Ctx, AF_Alloca, MemoryOperationKind::MOK_Any))
+ return AF_Alloca;
+
return AF_None;
}
@@ -1129,6 +1278,7 @@ void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C,
case AF_CXXNew: os << "'new'"; return;
case AF_CXXNewArray: os << "'new[]'"; return;
case AF_IfNameIndex: os << "'if_nameindex()'"; return;
+ case AF_Alloca:
case AF_None: llvm_unreachable("not a deallocation expression");
}
}
@@ -1140,7 +1290,8 @@ void MallocChecker::printExpectedDeallocName(raw_ostream &os,
case AF_CXXNew: os << "'delete'"; return;
case AF_CXXNewArray: os << "'delete[]'"; return;
case AF_IfNameIndex: os << "'if_freenameindex()'"; return;
- case AF_None: llvm_unreachable("suspicious AF_None argument");
+ case AF_Alloca:
+ case AF_None: llvm_unreachable("suspicious argument");
}
}
@@ -1152,6 +1303,9 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
bool &ReleasedAllocated,
bool ReturnsNullOnFailure) const {
+ if (!State)
+ return nullptr;
+
SVal ArgVal = State->getSVal(ArgExpr, C.getLocationContext());
if (!ArgVal.getAs<DefinedOrUnknownSVal>())
return nullptr;
@@ -1191,8 +1345,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
const MemSpaceRegion *MS = R->getMemorySpace();
- // Parameters, locals, statics, globals, and memory returned by alloca()
- // shouldn't be freed.
+ // Parameters, locals, statics, globals, and memory returned by
+ // __builtin_alloca() shouldn't be freed.
if (!(isa<UnknownSpaceRegion>(MS) || isa<HeapSpaceRegion>(MS))) {
// FIXME: at the time this code was written, malloc() regions were
// represented by conjured symbols, which are all in UnknownSpaceRegion.
@@ -1201,8 +1355,12 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
// Of course, free() can work on memory allocated outside the current
// function, so UnknownSpaceRegion is always a possibility.
// False negatives are better than false positives.
-
- ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
+
+ if (isa<AllocaRegion>(R))
+ ReportFreeAlloca(C, ArgVal, ArgExpr->getSourceRange());
+ else
+ ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
+
return nullptr;
}
@@ -1218,6 +1376,12 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
if (RsBase) {
+ // Memory returned by alloca() shouldn't be freed.
+ if (RsBase->getAllocationFamily() == AF_Alloca) {
+ ReportFreeAlloca(C, ArgVal, ArgExpr->getSourceRange());
+ return nullptr;
+ }
+
// Check for double free first.
if ((RsBase->isReleased() || RsBase->isRelinquished()) &&
!didPreviousFreeFail(State, SymBase, PreviousRetStatusSymbol)) {
@@ -1227,7 +1391,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
// 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()) {
+ } else if (RsBase->isAllocated() || RsBase->isAllocatedOfSizeZero() ||
+ RsBase->isEscaped()) {
// Check if an expected deallocation function matches the real one.
bool DeallocMatchesAlloc =
@@ -1252,7 +1417,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
}
}
- ReleasedAllocated = (RsBase != nullptr) && RsBase->isAllocated();
+ ReleasedAllocated = (RsBase != nullptr) && (RsBase->isAllocated() ||
+ RsBase->isAllocatedOfSizeZero());
// Clean out the info on previous call to free return info.
State = State->remove<FreeReturnValue>(SymBase);
@@ -1281,21 +1447,26 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
}
Optional<MallocChecker::CheckKind>
-MallocChecker::getCheckIfTracked(AllocationFamily Family) const {
+MallocChecker::getCheckIfTracked(AllocationFamily Family,
+ bool IsALeakCheck) const {
switch (Family) {
case AF_Malloc:
+ case AF_Alloca:
case AF_IfNameIndex: {
- if (ChecksEnabled[CK_MallocOptimistic]) {
- return CK_MallocOptimistic;
- } else if (ChecksEnabled[CK_MallocPessimistic]) {
- return CK_MallocPessimistic;
- }
+ if (ChecksEnabled[CK_MallocChecker])
+ return CK_MallocChecker;
+
return Optional<MallocChecker::CheckKind>();
}
case AF_CXXNew:
case AF_CXXNewArray: {
- if (ChecksEnabled[CK_NewDeleteChecker]) {
- return CK_NewDeleteChecker;
+ if (IsALeakCheck) {
+ if (ChecksEnabled[CK_NewDeleteLeaksChecker])
+ return CK_NewDeleteLeaksChecker;
+ }
+ else {
+ if (ChecksEnabled[CK_NewDeleteChecker])
+ return CK_NewDeleteChecker;
}
return Optional<MallocChecker::CheckKind>();
}
@@ -1308,16 +1479,18 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family) const {
Optional<MallocChecker::CheckKind>
MallocChecker::getCheckIfTracked(CheckerContext &C,
- const Stmt *AllocDeallocStmt) const {
- return getCheckIfTracked(getAllocationFamily(C, AllocDeallocStmt));
+ const Stmt *AllocDeallocStmt,
+ bool IsALeakCheck) const {
+ return getCheckIfTracked(getAllocationFamily(C, AllocDeallocStmt),
+ IsALeakCheck);
}
Optional<MallocChecker::CheckKind>
-MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym) const {
-
+MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym,
+ bool IsALeakCheck) const {
const RefState *RS = C.getState()->get<RegionState>(Sym);
assert(RS);
- return getCheckIfTracked(RS->getAllocationFamily());
+ return getCheckIfTracked(RS->getAllocationFamily(), IsALeakCheck);
}
bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) {
@@ -1411,8 +1584,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
SourceRange Range,
const Expr *DeallocExpr) const {
- if (!ChecksEnabled[CK_MallocOptimistic] &&
- !ChecksEnabled[CK_MallocPessimistic] &&
+ if (!ChecksEnabled[CK_MallocChecker] &&
!ChecksEnabled[CK_NewDeleteChecker])
return;
@@ -1433,23 +1605,19 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
while (const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(MR))
MR = ER->getSuperRegion();
- if (MR && isa<AllocaRegion>(MR))
- os << "Memory allocated by alloca() should not be deallocated";
- else {
- os << "Argument to ";
- if (!printAllocDeallocName(os, C, DeallocExpr))
- os << "deallocator";
-
- os << " is ";
- bool Summarized = MR ? SummarizeRegion(os, MR)
- : SummarizeValue(os, ArgVal);
- if (Summarized)
- os << ", which is not memory allocated by ";
- else
- os << "not memory allocated by ";
+ os << "Argument to ";
+ if (!printAllocDeallocName(os, C, DeallocExpr))
+ os << "deallocator";
- printExpectedAllocName(os, C, DeallocExpr);
- }
+ os << " is ";
+ bool Summarized = MR ? SummarizeRegion(os, MR)
+ : SummarizeValue(os, ArgVal);
+ if (Summarized)
+ os << ", which is not memory allocated by ";
+ else
+ os << "not memory allocated by ";
+
+ printExpectedAllocName(os, C, DeallocExpr);
BugReport *R = new BugReport(*BT_BadFree[*CheckKind], os.str(), N);
R->markInteresting(MR);
@@ -1458,6 +1626,31 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
}
}
+void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal,
+ SourceRange Range) const {
+
+ Optional<MallocChecker::CheckKind> CheckKind;
+
+ if (ChecksEnabled[CK_MallocChecker])
+ CheckKind = CK_MallocChecker;
+ else if (ChecksEnabled[CK_MismatchedDeallocatorChecker])
+ CheckKind = CK_MismatchedDeallocatorChecker;
+ else
+ return;
+
+ if (ExplodedNode *N = C.generateSink()) {
+ if (!BT_FreeAlloca[*CheckKind])
+ BT_FreeAlloca[*CheckKind].reset(
+ new BugType(CheckNames[*CheckKind], "Free alloca()", "Memory Error"));
+
+ BugReport *R = new BugReport(*BT_FreeAlloca[*CheckKind],
+ "Memory allocated by alloca() should not be deallocated", N);
+ R->markInteresting(ArgVal.getAsRegion());
+ R->addRange(Range);
+ C.emitReport(R);
+ }
+}
+
void MallocChecker::ReportMismatchedDealloc(CheckerContext &C,
SourceRange Range,
const Expr *DeallocExpr,
@@ -1517,8 +1710,8 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,
SourceRange Range, const Expr *DeallocExpr,
const Expr *AllocExpr) const {
- if (!ChecksEnabled[CK_MallocOptimistic] &&
- !ChecksEnabled[CK_MallocPessimistic] &&
+
+ if (!ChecksEnabled[CK_MallocChecker] &&
!ChecksEnabled[CK_NewDeleteChecker])
return;
@@ -1573,8 +1766,7 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,
void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range,
SymbolRef Sym) const {
- if (!ChecksEnabled[CK_MallocOptimistic] &&
- !ChecksEnabled[CK_MallocPessimistic] &&
+ if (!ChecksEnabled[CK_MallocChecker] &&
!ChecksEnabled[CK_NewDeleteChecker])
return;
@@ -1601,8 +1793,7 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range,
bool Released, SymbolRef Sym,
SymbolRef PrevSym) const {
- if (!ChecksEnabled[CK_MallocOptimistic] &&
- !ChecksEnabled[CK_MallocPessimistic] &&
+ if (!ChecksEnabled[CK_MallocChecker] &&
!ChecksEnabled[CK_NewDeleteChecker])
return;
@@ -1637,7 +1828,6 @@ void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const {
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)
@@ -1653,16 +1843,49 @@ void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const {
}
}
+void MallocChecker::ReportUseZeroAllocated(CheckerContext &C,
+ SourceRange Range,
+ SymbolRef Sym) const {
+
+ if (!ChecksEnabled[CK_MallocChecker] &&
+ !ChecksEnabled[CK_NewDeleteChecker])
+ return;
+
+ Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
+
+ if (!CheckKind.hasValue())
+ return;
+
+ if (ExplodedNode *N = C.generateSink()) {
+ if (!BT_UseZerroAllocated[*CheckKind])
+ BT_UseZerroAllocated[*CheckKind].reset(new BugType(
+ CheckNames[*CheckKind], "Use of zero allocated", "Memory Error"));
+
+ BugReport *R = new BugReport(*BT_UseZerroAllocated[*CheckKind],
+ "Use of zero-allocated memory", N);
+
+ R->addRange(Range);
+ if (Sym) {
+ R->markInteresting(Sym);
+ R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym));
+ }
+ C.emitReport(R);
+ }
+}
+
ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
const CallExpr *CE,
- bool FreesOnFail) const {
+ bool FreesOnFail,
+ ProgramStateRef State) const {
+ if (!State)
+ return nullptr;
+
if (CE->getNumArgs() < 2)
return nullptr;
- ProgramStateRef state = C.getState();
const Expr *arg0Expr = CE->getArg(0);
const LocationContext *LCtx = C.getLocationContext();
- SVal Arg0Val = state->getSVal(arg0Expr, LCtx);
+ SVal Arg0Val = State->getSVal(arg0Expr, LCtx);
if (!Arg0Val.getAs<DefinedOrUnknownSVal>())
return nullptr;
DefinedOrUnknownSVal arg0Val = Arg0Val.castAs<DefinedOrUnknownSVal>();
@@ -1670,7 +1893,7 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
SValBuilder &svalBuilder = C.getSValBuilder();
DefinedOrUnknownSVal PtrEQ =
- svalBuilder.evalEQ(state, arg0Val, svalBuilder.makeNull());
+ svalBuilder.evalEQ(State, arg0Val, svalBuilder.makeNull());
// Get the size argument. If there is no size arg then give up.
const Expr *Arg1 = CE->getArg(1);
@@ -1678,20 +1901,20 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
return nullptr;
// Get the value of the size argument.
- SVal Arg1ValG = state->getSVal(Arg1, LCtx);
+ SVal Arg1ValG = State->getSVal(Arg1, LCtx);
if (!Arg1ValG.getAs<DefinedOrUnknownSVal>())
return nullptr;
DefinedOrUnknownSVal Arg1Val = Arg1ValG.castAs<DefinedOrUnknownSVal>();
// Compare the size argument to 0.
DefinedOrUnknownSVal SizeZero =
- svalBuilder.evalEQ(state, Arg1Val,
+ svalBuilder.evalEQ(State, Arg1Val,
svalBuilder.makeIntValWithPtrWidth(0, false));
ProgramStateRef StatePtrIsNull, StatePtrNotNull;
- std::tie(StatePtrIsNull, StatePtrNotNull) = state->assume(PtrEQ);
+ std::tie(StatePtrIsNull, StatePtrNotNull) = State->assume(PtrEQ);
ProgramStateRef StateSizeIsZero, StateSizeNotZero;
- std::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;
@@ -1711,7 +1934,7 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
// Get the from and to pointer symbols as in toPtr = realloc(fromPtr, size).
assert(!PrtIsNull);
SymbolRef FromPtr = arg0Val.getAsSymbol();
- SVal RetVal = state->getSVal(CE, LCtx);
+ SVal RetVal = State->getSVal(CE, LCtx);
SymbolRef ToPtr = RetVal.getAsSymbol();
if (!FromPtr || !ToPtr)
return nullptr;
@@ -1731,7 +1954,7 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
// Default behavior.
if (ProgramStateRef stateFree =
- FreeMemAux(C, CE, state, 0, false, ReleasedAllocated)) {
+ FreeMemAux(C, CE, State, 0, false, ReleasedAllocated)) {
ProgramStateRef stateRealloc = MallocMemAux(C, CE, CE->getArg(1),
UnknownVal(), stateFree);
@@ -1755,20 +1978,23 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
return nullptr;
}
-ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE){
+ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State) {
+ if (!State)
+ return nullptr;
+
if (CE->getNumArgs() < 2)
return nullptr;
- ProgramStateRef state = C.getState();
SValBuilder &svalBuilder = C.getSValBuilder();
const LocationContext *LCtx = C.getLocationContext();
- SVal count = state->getSVal(CE->getArg(0), LCtx);
- SVal elementSize = state->getSVal(CE->getArg(1), LCtx);
- SVal TotalSize = svalBuilder.evalBinOp(state, BO_Mul, count, elementSize,
+ SVal count = State->getSVal(CE->getArg(0), LCtx);
+ SVal elementSize = State->getSVal(CE->getArg(1), LCtx);
+ SVal TotalSize = svalBuilder.evalBinOp(State, BO_Mul, count, elementSize,
svalBuilder.getContext().getSizeType());
SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);
- return MallocMemAux(C, CE, TotalSize, zeroVal, state);
+ return MallocMemAux(C, CE, TotalSize, zeroVal, State);
}
LeakInfo
@@ -1801,9 +2027,11 @@ MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
}
}
- // Allocation node, is the last node in the current context in which the
- // symbol was tracked.
- if (N->getLocationContext() == LeakContext)
+ // Allocation node, is the last node in the current or parent context in
+ // which the symbol was tracked.
+ const LocationContext *NContext = N->getLocationContext();
+ if (NContext == LeakContext ||
+ NContext->isParentOf(LeakContext))
AllocNode = N;
N = N->pred_empty() ? nullptr : *(N->pred_begin());
}
@@ -1814,23 +2042,22 @@ MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,
CheckerContext &C) const {
- if (!ChecksEnabled[CK_MallocOptimistic] &&
- !ChecksEnabled[CK_MallocPessimistic] &&
+ if (!ChecksEnabled[CK_MallocChecker] &&
!ChecksEnabled[CK_NewDeleteLeaksChecker])
return;
const RefState *RS = C.getState()->get<RegionState>(Sym);
assert(RS && "cannot leak an untracked symbol");
AllocationFamily Family = RS->getAllocationFamily();
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
- if (!CheckKind.hasValue())
+
+ if (Family == AF_Alloca)
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 (!ChecksEnabled[CK_NewDeleteLeaksChecker])
- return;
+ Optional<MallocChecker::CheckKind>
+ CheckKind = getCheckIfTracked(Family, true);
+
+ if (!CheckKind.hasValue())
+ return;
assert(N);
if (!BT_Leak[*CheckKind]) {
@@ -1893,7 +2120,7 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
SmallVector<SymbolRef, 2> Errors;
for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) {
if (SymReaper.isDead(I->first)) {
- if (I->second.isAllocated())
+ if (I->second.isAllocated() || I->second.isAllocatedOfSizeZero())
Errors.push_back(I->first);
// Remove the dead symbol from the map.
RS = F.remove(RS, I->first);
@@ -1949,8 +2176,7 @@ void MallocChecker::checkPreCall(const CallEvent &Call,
return;
ASTContext &Ctx = C.getASTContext();
- if ((ChecksEnabled[CK_MallocOptimistic] ||
- ChecksEnabled[CK_MallocPessimistic]) &&
+ if (ChecksEnabled[CK_MallocChecker] &&
(isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Free) ||
isCMemFunction(FD, Ctx, AF_IfNameIndex,
MemoryOperationKind::MOK_Free)))
@@ -2062,6 +2288,15 @@ bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C,
return false;
}
+void MallocChecker::checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C,
+ const Stmt *S) const {
+ assert(Sym);
+ const RefState *RS = C.getState()->get<RegionState>(Sym);
+
+ if (RS && RS->isAllocatedOfSizeZero())
+ ReportUseZeroAllocated(C, RS->getStmt()->getSourceRange(), Sym);
+}
+
bool MallocChecker::checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const {
if (isReleased(Sym, C)) {
@@ -2075,8 +2310,10 @@ bool MallocChecker::checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const {
void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S,
CheckerContext &C) const {
SymbolRef Sym = l.getLocSymbolInBase();
- if (Sym)
+ if (Sym) {
checkUseAfterFree(Sym, C, S);
+ checkUseZeroAllocated(Sym, C, S);
+ }
}
// If a symbolic region is assumed to NULL (or another constant), stop tracking
@@ -2320,7 +2557,8 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State,
continue;
if (const RefState *RS = State->get<RegionState>(sym)) {
- if (RS->isAllocated() && CheckRefState(RS)) {
+ if ((RS->isAllocated() || RS->isAllocatedOfSizeZero()) &&
+ CheckRefState(RS)) {
State = State->remove<RegionState>(sym);
State = State->set<RegionState>(sym, RefState::getEscaped(RS));
}
@@ -2443,6 +2681,8 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State,
const RefState *RefS = State->get<RegionState>(I.getKey());
AllocationFamily Family = RefS->getAllocationFamily();
Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
+ if (!CheckKind.hasValue())
+ CheckKind = getCheckIfTracked(Family, true);
I.getKey()->dumpToStream(Out);
Out << " : ";
@@ -2457,6 +2697,8 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State,
void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {
registerCStringCheckerBasic(mgr);
MallocChecker *checker = mgr.registerChecker<MallocChecker>();
+ checker->IsOptimistic = mgr.getAnalyzerOptions().getBooleanOption(
+ "Optimistic", false, checker);
checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true;
checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] =
mgr.getCurrentCheckName();
@@ -2470,11 +2712,12 @@ void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {
void ento::register##name(CheckerManager &mgr) { \
registerCStringCheckerBasic(mgr); \
MallocChecker *checker = mgr.registerChecker<MallocChecker>(); \
+ checker->IsOptimistic = mgr.getAnalyzerOptions().getBooleanOption( \
+ "Optimistic", false, checker); \
checker->ChecksEnabled[MallocChecker::CK_##name] = true; \
checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \
}
-REGISTER_CHECKER(MallocPessimistic)
-REGISTER_CHECKER(MallocOptimistic)
+REGISTER_CHECKER(MallocChecker)
REGISTER_CHECKER(NewDeleteChecker)
REGISTER_CHECKER(MismatchedDeallocatorChecker)
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
index f38ce77dc6b6..e91347999dc1 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
@@ -142,13 +142,13 @@ private:
}
}
}
- else if (isa<MemberExpr>(E)) {
+ else if (const auto *ME = dyn_cast<MemberExpr>(E)) {
// No points-to analysis, just look at the member
- const Decl * EmeMD = dyn_cast<MemberExpr>(E)->getMemberDecl();
+ const Decl *EmeMD = ME->getMemberDecl();
while (i != e) {
--i;
- if (isa<MemberExpr>(i->variable)) {
- if (dyn_cast<MemberExpr>(i->variable)->getMemberDecl() == EmeMD)
+ if (const auto *ME_i = dyn_cast<MemberExpr>(i->variable)) {
+ if (ME_i->getMemberDecl() == EmeMD)
i = toScanFor.erase (i);
}
}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
index 9a460ba50ddd..58c27d49acce 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
@@ -94,6 +94,16 @@ public:
ErrorReturnedNotOwned
};
+ /// Tracks how an object referenced by an ivar has been used.
+ ///
+ /// This accounts for us not knowing if an arbitrary ivar is supposed to be
+ /// stored at +0 or +1.
+ enum class IvarAccessHistory {
+ None,
+ AccessedDirectly,
+ ReleasedAfterDirectAccess
+ };
+
private:
/// The number of outstanding retains.
unsigned Cnt;
@@ -121,14 +131,16 @@ private:
/// 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;
+ unsigned RawIvarAccessHistory : 2;
RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t,
- bool Overridable = false)
+ IvarAccessHistory IvarAccess)
: Cnt(cnt), ACnt(acnt), T(t), RawKind(static_cast<unsigned>(k)),
- RawObjectKind(static_cast<unsigned>(o)), IsOverridable(Overridable) {
+ RawObjectKind(static_cast<unsigned>(o)),
+ RawIvarAccessHistory(static_cast<unsigned>(IvarAccess)) {
assert(getKind() == k && "not enough bits for the kind");
assert(getObjKind() == o && "not enough bits for the object kind");
+ assert(getIvarAccessHistory() == IvarAccess && "not enough bits");
}
public:
@@ -144,20 +156,24 @@ public:
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; }
+ /// Returns what the analyzer knows about direct accesses to a particular
+ /// instance variable.
+ ///
+ /// If the object with this refcount wasn't originally from an Objective-C
+ /// ivar region, this should always return IvarAccessHistory::None.
+ IvarAccessHistory getIvarAccessHistory() const {
+ return static_cast<IvarAccessHistory>(RawIvarAccessHistory);
+ }
bool isOwned() const {
return getKind() == Owned;
@@ -181,7 +197,7 @@ public:
/// 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);
+ return RefVal(Owned, o, Count, 0, t, IvarAccessHistory::None);
}
/// Create a state for an object whose lifetime is not the responsibility of
@@ -190,47 +206,49 @@ public:
/// 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);
- }
-
- /// 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);
+ return RefVal(NotOwned, o, Count, 0, t, IvarAccessHistory::None);
}
RefVal operator-(size_t i) const {
return RefVal(getKind(), getObjKind(), getCount() - i,
- getAutoreleaseCount(), getType());
+ getAutoreleaseCount(), getType(), getIvarAccessHistory());
}
RefVal operator+(size_t i) const {
return RefVal(getKind(), getObjKind(), getCount() + i,
- getAutoreleaseCount(), getType());
+ getAutoreleaseCount(), getType(), getIvarAccessHistory());
}
RefVal operator^(Kind k) const {
return RefVal(k, getObjKind(), getCount(), getAutoreleaseCount(),
- getType());
+ getType(), getIvarAccessHistory());
}
RefVal autorelease() const {
return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount()+1,
- getType());
+ getType(), getIvarAccessHistory());
+ }
+
+ RefVal withIvarAccess() const {
+ assert(getIvarAccessHistory() == IvarAccessHistory::None);
+ return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount(),
+ getType(), IvarAccessHistory::AccessedDirectly);
+ }
+ RefVal releaseViaIvar() const {
+ assert(getIvarAccessHistory() == IvarAccessHistory::AccessedDirectly);
+ return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount(),
+ getType(), IvarAccessHistory::ReleasedAfterDirectAccess);
}
// Comparison, profiling, and pretty-printing.
bool hasSameState(const RefVal &X) const {
- return getKind() == X.getKind() && Cnt == X.Cnt && ACnt == X.ACnt;
+ return getKind() == X.getKind() && Cnt == X.Cnt && ACnt == X.ACnt &&
+ getIvarAccessHistory() == X.getIvarAccessHistory();
}
bool operator==(const RefVal& X) const {
- return T == X.T && hasSameState(X) && getObjKind() == X.getObjKind() &&
- IsOverridable == X.IsOverridable;
+ return T == X.T && hasSameState(X) && getObjKind() == X.getObjKind();
}
void Profile(llvm::FoldingSetNodeID& ID) const {
@@ -239,7 +257,7 @@ public:
ID.AddInteger(Cnt);
ID.AddInteger(ACnt);
ID.AddInteger(RawObjectKind);
- ID.AddBoolean(IsOverridable);
+ ID.AddInteger(RawIvarAccessHistory);
}
void print(raw_ostream &Out) const;
@@ -249,9 +267,6 @@ void RefVal::print(raw_ostream &Out) const {
if (!T.isNull())
Out << "Tracked " << T.getAsString() << '/';
- if (isOverridable())
- Out << "(overridable) ";
-
switch (getKind()) {
default: llvm_unreachable("Invalid RefVal kind");
case Owned: {
@@ -323,8 +338,18 @@ void RefVal::print(raw_ostream &Out) const {
break;
}
+ switch (getIvarAccessHistory()) {
+ case IvarAccessHistory::None:
+ break;
+ case IvarAccessHistory::AccessedDirectly:
+ Out << " [direct ivar access]";
+ break;
+ case IvarAccessHistory::ReleasedAfterDirectAccess:
+ Out << " [released after direct ivar access]";
+ }
+
if (ACnt) {
- Out << " [ARC +" << ACnt << ']';
+ Out << " [autorelease -" << ACnt << ']';
}
}
} //end anonymous namespace
@@ -1763,12 +1788,11 @@ namespace {
addGCModeDescription(LOpts, GCEnabled);
}
- std::pair<ranges_iterator, ranges_iterator> getRanges() override {
+ llvm::iterator_range<ranges_iterator> getRanges() override {
const CFRefBug& BugTy = static_cast<CFRefBug&>(getBugType());
if (!BugTy.isLeak())
return BugReport::getRanges();
- else
- return std::make_pair(ranges_iterator(), ranges_iterator());
+ return llvm::make_range(ranges_iterator(), ranges_iterator());
}
};
@@ -1829,6 +1853,16 @@ static bool isNumericLiteralExpression(const Expr *E) {
isa<CXXBoolLiteralExpr>(E);
}
+/// Returns true if this stack frame is for an Objective-C method that is a
+/// property getter or setter whose body has been synthesized by the analyzer.
+static bool isSynthesizedAccessor(const StackFrameContext *SFC) {
+ auto Method = dyn_cast_or_null<ObjCMethodDecl>(SFC->getDecl());
+ if (!Method || !Method->isPropertyAccessor())
+ return false;
+
+ return SFC->getAnalysisDeclContext()->isBodyAutosynthesized();
+}
+
PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
const ExplodedNode *PrevN,
BugReporterContext &BRC,
@@ -1859,6 +1893,11 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
if (!PrevT) {
const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
+ if (isa<ObjCIvarRefExpr>(S) &&
+ isSynthesizedAccessor(LCtx->getCurrentStackFrame())) {
+ S = LCtx->getCurrentStackFrame()->getCallSite();
+ }
+
if (isa<ObjCArrayLiteral>(S)) {
os << "NSArray literal is an object with a +0 retain count";
}
@@ -1883,6 +1922,9 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
os << "oxed expression produces an object with a +0 retain count";
}
}
+ else if (isa<ObjCIvarRefExpr>(S)) {
+ os << "Object loaded from instance variable";
+ }
else {
if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
// Get the name of the callee (if it is available).
@@ -2034,7 +2076,6 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
switch (CurrV.getKind()) {
case RefVal::Owned:
case RefVal::NotOwned:
-
if (PrevV.getCount() == CurrV.getCount()) {
// Did an autorelease message get sent?
if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount())
@@ -2062,6 +2103,11 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
break;
case RefVal::Released:
+ if (CurrV.getIvarAccessHistory() ==
+ RefVal::IvarAccessHistory::ReleasedAfterDirectAccess &&
+ CurrV.getIvarAccessHistory() != PrevV.getIvarAccessHistory()) {
+ os << "Strong instance variable relinquished. ";
+ }
os << "Object released.";
break;
@@ -2143,7 +2189,7 @@ static AllocationInfo
GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N,
SymbolRef Sym) {
const ExplodedNode *AllocationNode = N;
- const ExplodedNode *AllocationNodeInCurrentContext = N;
+ const ExplodedNode *AllocationNodeInCurrentOrParentContext = N;
const MemRegion *FirstBinding = nullptr;
const LocationContext *LeakContext = N->getLocationContext();
@@ -2173,10 +2219,15 @@ GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *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;
+ // AllocationNodeInCurrentContext, is the last node in the current or
+ // parent context in which the symbol was tracked.
+ //
+ // Note that the allocation site might be in the parent conext. For example,
+ // the case where an allocation happens in a block that captures a reference
+ // to it and that reference is overwritten/dropped by another call to
+ // the block.
+ if (NContext == LeakContext || NContext->isParentOf(LeakContext))
+ AllocationNodeInCurrentOrParentContext = N;
// Find the last init that was called on the given symbol and store the
// init method's location context.
@@ -2214,7 +2265,7 @@ GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N,
FirstBinding = nullptr;
}
- return AllocationInfo(AllocationNodeInCurrentContext,
+ return AllocationInfo(AllocationNodeInCurrentOrParentContext,
FirstBinding,
InterestingMethodContext);
}
@@ -2345,20 +2396,8 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
ProgramPoint P = AllocNode->getLocation();
if (Optional<CallExitEnd> Exit = P.getAs<CallExitEnd>())
AllocStmt = Exit->getCalleeContext()->getCallSite();
- else {
- // We are going to get a BlockEdge when the leak and allocation happen in
- // different, non-nested frames (contexts). For example, the case where an
- // allocation happens in a block that captures a reference to it and
- // that reference is overwritten/dropped by another call to the block.
- if (Optional<BlockEdge> Edge = P.getAs<BlockEdge>()) {
- if (Optional<CFGStmt> St = Edge->getDst()->front().getAs<CFGStmt>()) {
- AllocStmt = St->getStmt();
- }
- }
- else {
- AllocStmt = P.castAs<PostStmt>().getStmt();
- }
- }
+ else
+ AllocStmt = P.castAs<PostStmt>().getStmt();
assert(AllocStmt && "Cannot find allocation statement");
PathDiagnosticLocation AllocLocation =
@@ -2436,9 +2475,7 @@ public:
: ShouldResetSummaryLog(false),
IncludeAllocationLine(shouldIncludeAllocationSiteInLeakDiagnostics(AO)) {}
- virtual ~RetainCountChecker() {
- DeleteContainerSeconds(DeadSymbolTags);
- }
+ ~RetainCountChecker() override { DeleteContainerSeconds(DeadSymbolTags); }
void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
ExprEngine &Eng) const {
@@ -2774,17 +2811,64 @@ void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex,
C.addTransition(State);
}
+static bool wasLoadedFromIvar(SymbolRef Sym) {
+ if (auto DerivedVal = dyn_cast<SymbolDerived>(Sym))
+ return isa<ObjCIvarRegion>(DerivedVal->getRegion());
+ if (auto RegionVal = dyn_cast<SymbolRegionValue>(Sym))
+ return isa<ObjCIvarRegion>(RegionVal->getRegion());
+ return false;
+}
+
void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE,
CheckerContext &C) const {
+ Optional<Loc> IVarLoc = C.getSVal(IRE).getAs<Loc>();
+ if (!IVarLoc)
+ return;
+
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);
+ SymbolRef Sym = State->getSVal(*IVarLoc).getAsSymbol();
+ if (!Sym || !wasLoadedFromIvar(Sym))
+ return;
+
+ // Accessing an ivar directly is unusual. If we've done that, be more
+ // forgiving about what the surrounding code is allowed to do.
+
+ QualType Ty = Sym->getType();
+ RetEffect::ObjKind Kind;
+ if (Ty->isObjCRetainableType())
+ Kind = RetEffect::ObjC;
+ else if (coreFoundation::isCFObjectRef(Ty))
+ Kind = RetEffect::CF;
+ else
+ return;
+
+ // If the value is already known to be nil, don't bother tracking it.
+ ConstraintManager &CMgr = State->getConstraintManager();
+ if (CMgr.isNull(State, Sym).isConstrainedTrue())
+ return;
+
+ if (const RefVal *RV = getRefBinding(State, Sym)) {
+ // If we've seen this symbol before, or we're only seeing it now because
+ // of something the analyzer has synthesized, don't do anything.
+ if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None ||
+ isSynthesizedAccessor(C.getStackFrame())) {
+ return;
+ }
+
+ // Note that this value has been loaded from an ivar.
+ C.addTransition(setRefBinding(State, Sym, RV->withIvarAccess()));
+ return;
+ }
+
+ RefVal PlusZero = RefVal::makeNotOwned(Kind, Ty);
+
+ // In a synthesized accessor, the effective retain count is +0.
+ if (isSynthesizedAccessor(C.getStackFrame())) {
+ C.addTransition(setRefBinding(State, Sym, PlusZero));
+ return;
+ }
+
+ State = setRefBinding(State, Sym, PlusZero.withIvarAccess());
C.addTransition(State);
}
@@ -2828,16 +2912,6 @@ static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) {
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.
@@ -2872,19 +2946,6 @@ void RetainCountChecker::processSummaryOfInlined(const RetainSummary &Summ,
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);
@@ -3125,11 +3186,16 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
case RefVal::Owned:
assert(V.getCount() > 0);
- if (V.getCount() == 1)
- V = V ^ (E == DecRefBridgedTransferred ? RefVal::NotOwned
- : RefVal::Released);
- else if (E == DecRefAndStopTrackingHard)
+ if (V.getCount() == 1) {
+ if (E == DecRefBridgedTransferred ||
+ V.getIvarAccessHistory() ==
+ RefVal::IvarAccessHistory::AccessedDirectly)
+ V = V ^ RefVal::NotOwned;
+ else
+ V = V ^ RefVal::Released;
+ } else if (E == DecRefAndStopTrackingHard) {
return removeRefBinding(state, sym);
+ }
V = V - 1;
break;
@@ -3139,6 +3205,13 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
if (E == DecRefAndStopTrackingHard)
return removeRefBinding(state, sym);
V = V - 1;
+ } else if (V.getIvarAccessHistory() ==
+ RefVal::IvarAccessHistory::AccessedDirectly) {
+ // Assume that the instance variable was holding on the object at
+ // +1, and we just didn't know.
+ if (E == DecRefAndStopTrackingHard)
+ return removeRefBinding(state, sym);
+ V = V.releaseViaIvar() ^ RefVal::Released;
} else {
V = V ^ RefVal::ErrorReleaseNotOwned;
hasErr = V.getKind();
@@ -3162,6 +3235,16 @@ void RetainCountChecker::processNonLeakError(ProgramStateRef St,
RefVal::Kind ErrorKind,
SymbolRef Sym,
CheckerContext &C) const {
+ // HACK: Ignore retain-count issues on values accessed through ivars,
+ // because of cases like this:
+ // [_contentView retain];
+ // [_contentView removeFromSuperview];
+ // [self addSubview:_contentView]; // invalidates 'self'
+ // [_contentView release];
+ if (const RefVal *RV = getRefBinding(St, Sym))
+ if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
+ return;
+
ExplodedNode *N = C.generateSink(St);
if (!N)
return;
@@ -3229,7 +3312,7 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
// See if it's one of the specific functions we know how to eval.
bool canEval = false;
- QualType ResultTy = CE->getCallReturnType();
+ QualType ResultTy = CE->getCallReturnType(C.getASTContext());
if (ResultTy->isObjCIdType()) {
// Handle: id NSMakeCollectable(CFTypeRef)
canEval = II->isStr("NSMakeCollectable");
@@ -3388,6 +3471,15 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
RetEffect RE, RefVal X,
SymbolRef Sym,
ProgramStateRef state) const {
+ // HACK: Ignore retain-count issues on values accessed through ivars,
+ // because of cases like this:
+ // [_contentView retain];
+ // [_contentView removeFromSuperview];
+ // [self addSubview:_contentView]; // invalidates 'self'
+ // [_contentView release];
+ if (X.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
+ return;
+
// Any leaks or other errors?
if (X.isReturnedOwned() && X.getCount() == 0) {
if (RE.getKind() != RetEffect::NoRet) {
@@ -3428,22 +3520,31 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
}
} else if (X.isReturnedNotOwned()) {
if (RE.isOwned()) {
- // Trying to return a not owned object to a caller expecting an
- // owned object.
- state = setRefBinding(state, Sym, X ^ RefVal::ErrorReturnedNotOwned);
-
- static CheckerProgramPointTag ReturnNotOwnedTag(this,
- "ReturnNotOwnedForOwned");
- ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag);
- if (N) {
- if (!returnNotOwnedForOwned)
- returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned(this));
-
- CFRefReport *report =
- new CFRefReport(*returnNotOwnedForOwned,
- C.getASTContext().getLangOpts(),
- C.isObjCGCEnabled(), SummaryLog, N, Sym);
- C.emitReport(report);
+ if (X.getIvarAccessHistory() ==
+ RefVal::IvarAccessHistory::AccessedDirectly) {
+ // Assume the method was trying to transfer a +1 reference from a
+ // strong ivar to the caller.
+ state = setRefBinding(state, Sym,
+ X.releaseViaIvar() ^ RefVal::ReturnedOwned);
+ } else {
+ // Trying to return a not owned object to a caller expecting an
+ // owned object.
+ state = setRefBinding(state, Sym, X ^ RefVal::ErrorReturnedNotOwned);
+
+ static CheckerProgramPointTag
+ ReturnNotOwnedTag(this, "ReturnNotOwnedForOwned");
+
+ ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag);
+ if (N) {
+ if (!returnNotOwnedForOwned)
+ returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned(this));
+
+ CFRefReport *report =
+ new CFRefReport(*returnNotOwnedForOwned,
+ C.getASTContext().getLangOpts(),
+ C.isObjCGCEnabled(), SummaryLog, N, Sym);
+ C.emitReport(report);
+ }
}
}
}
@@ -3594,6 +3695,14 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
if (V.getKind() == RefVal::ReturnedOwned)
++Cnt;
+ // If we would over-release here, but we know the value came from an ivar,
+ // assume it was a strong ivar that's just been relinquished.
+ if (ACnt > Cnt &&
+ V.getIvarAccessHistory() == RefVal::IvarAccessHistory::AccessedDirectly) {
+ V = V.releaseViaIvar();
+ --ACnt;
+ }
+
if (ACnt <= Cnt) {
if (ACnt == Cnt) {
V.clearCounts();
@@ -3608,6 +3717,15 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
return setRefBinding(state, Sym, V);
}
+ // HACK: Ignore retain-count issues on values accessed through ivars,
+ // because of cases like this:
+ // [_contentView retain];
+ // [_contentView removeFromSuperview];
+ // [self addSubview:_contentView]; // invalidates 'self'
+ // [_contentView release];
+ if (V.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
+ return state;
+
// Woah! More autorelease counts then retain counts left.
// Emit hard error.
V = V ^ RefVal::ErrorOverAutorelease;
@@ -3641,11 +3759,22 @@ ProgramStateRef
RetainCountChecker::handleSymbolDeath(ProgramStateRef state,
SymbolRef sid, RefVal V,
SmallVectorImpl<SymbolRef> &Leaked) const {
- bool hasLeak = false;
- if (V.isOwned())
+ bool hasLeak;
+
+ // HACK: Ignore retain-count issues on values accessed through ivars,
+ // because of cases like this:
+ // [_contentView retain];
+ // [_contentView removeFromSuperview];
+ // [self addSubview:_contentView]; // invalidates 'self'
+ // [_contentView release];
+ if (V.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
+ hasLeak = false;
+ else if (V.isOwned())
hasLeak = true;
else if (V.isNotOwned() || V.isReturnedOwned())
hasLeak = (V.getCount() > 0);
+ else
+ hasLeak = false;
if (!hasLeak)
return removeRefBinding(state, sid);
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
index d717e3fe86d2..1696bcfac9c1 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -13,12 +13,14 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
+using namespace ento;
using namespace llvm;
AnalyzerOptions::UserModeKind AnalyzerOptions::getUserMode() {
@@ -100,12 +102,37 @@ AnalyzerOptions::mayInlineCXXMemberFunction(CXXInlineableMemberKind K) {
static StringRef toString(bool b) { return b ? "true" : "false"; }
-bool AnalyzerOptions::getBooleanOption(StringRef Name, bool DefaultVal) {
+StringRef AnalyzerOptions::getCheckerOption(StringRef CheckerName,
+ StringRef OptionName,
+ StringRef Default,
+ bool SearchInParents) {
+ // Search for a package option if the option for the checker is not specified
+ // and search in parents is enabled.
+ ConfigTable::const_iterator E = Config.end();
+ do {
+ ConfigTable::const_iterator I =
+ Config.find((Twine(CheckerName) + ":" + OptionName).str());
+ if (I != E)
+ return StringRef(I->getValue());
+ size_t Pos = CheckerName.rfind('.');
+ if (Pos == StringRef::npos)
+ return Default;
+ CheckerName = CheckerName.substr(0, Pos);
+ } while (!CheckerName.empty() && SearchInParents);
+ return Default;
+}
+
+bool AnalyzerOptions::getBooleanOption(StringRef Name, bool DefaultVal,
+ const CheckerBase *C,
+ bool SearchInParents) {
// FIXME: We should emit a warning here if the value is something other than
// "true", "false", or the empty string (meaning the default value),
// but the AnalyzerOptions doesn't have access to a diagnostic engine.
+ StringRef Default = toString(DefaultVal);
StringRef V =
- Config.insert(std::make_pair(Name, toString(DefaultVal))).first->second;
+ C ? getCheckerOption(C->getTagDescription(), Name, Default,
+ SearchInParents)
+ : StringRef(Config.insert(std::make_pair(Name, Default)).first->second);
return llvm::StringSwitch<bool>(V)
.Case("true", true)
.Case("false", false)
@@ -113,9 +140,10 @@ bool AnalyzerOptions::getBooleanOption(StringRef Name, bool DefaultVal) {
}
bool AnalyzerOptions::getBooleanOption(Optional<bool> &V, StringRef Name,
- bool DefaultVal) {
+ bool DefaultVal, const CheckerBase *C,
+ bool SearchInParents) {
if (!V.hasValue())
- V = getBooleanOption(Name, DefaultVal);
+ V = getBooleanOption(Name, DefaultVal, C, SearchInParents);
return V.getValue();
}
@@ -199,19 +227,35 @@ bool AnalyzerOptions::shouldWriteStableReportFilename() {
/* Default = */ false);
}
-int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal) {
+int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal,
+ const CheckerBase *C,
+ bool SearchInParents) {
SmallString<10> StrBuf;
llvm::raw_svector_ostream OS(StrBuf);
OS << DefaultVal;
- StringRef V = Config.insert(std::make_pair(Name, OS.str())).first->second;
+ StringRef V = C ? getCheckerOption(C->getTagDescription(), Name, OS.str(),
+ SearchInParents)
+ : StringRef(Config.insert(std::make_pair(Name, OS.str()))
+ .first->second);
+
int Res = DefaultVal;
bool b = V.getAsInteger(10, Res);
assert(!b && "analyzer-config option should be numeric");
- (void) b;
+ (void)b;
return Res;
}
+StringRef AnalyzerOptions::getOptionAsString(StringRef Name,
+ StringRef DefaultVal,
+ const CheckerBase *C,
+ bool SearchInParents) {
+ return C ? getCheckerOption(C->getTagDescription(), Name, DefaultVal,
+ SearchInParents)
+ : StringRef(
+ Config.insert(std::make_pair(Name, DefaultVal)).first->second);
+}
+
unsigned AnalyzerOptions::getAlwaysInlineSize() {
if (!AlwaysInlineSize.hasValue())
AlwaysInlineSize = getOptionAsInteger("ipa-always-inline-size", 3);
@@ -281,4 +325,3 @@ bool AnalyzerOptions::shouldPrunePaths() {
bool AnalyzerOptions::shouldConditionalizeStaticInitializers() {
return getBooleanOption("cfg-conditional-static-initializers", true);
}
-
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
index dff81e383ea6..97e97ef8c4d6 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -2702,22 +2702,22 @@ const Stmt *BugReport::getStmt() const {
return S;
}
-std::pair<BugReport::ranges_iterator, BugReport::ranges_iterator>
-BugReport::getRanges() {
- // If no custom ranges, add the range of the statement corresponding to
- // the error node.
- if (Ranges.empty()) {
- if (const Expr *E = dyn_cast_or_null<Expr>(getStmt()))
- addRange(E->getSourceRange());
- else
- return std::make_pair(ranges_iterator(), ranges_iterator());
- }
+llvm::iterator_range<BugReport::ranges_iterator> BugReport::getRanges() {
+ // If no custom ranges, add the range of the statement corresponding to
+ // the error node.
+ if (Ranges.empty()) {
+ if (const Expr *E = dyn_cast_or_null<Expr>(getStmt()))
+ addRange(E->getSourceRange());
+ else
+ return llvm::make_range(ranges_iterator(), ranges_iterator());
+ }
- // User-specified absence of range info.
- if (Ranges.size() == 1 && !Ranges.begin()->isValid())
- return std::make_pair(ranges_iterator(), ranges_iterator());
+ // User-specified absence of range info.
+ if (Ranges.size() == 1 && !Ranges.begin()->isValid())
+ return llvm::make_range(ranges_iterator(), ranges_iterator());
- return std::make_pair(Ranges.begin(), Ranges.end());
+ return llvm::iterator_range<BugReport::ranges_iterator>(Ranges.begin(),
+ Ranges.end());
}
PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const {
@@ -2763,9 +2763,7 @@ void BugReporter::FlushReports() {
// warnings and new BugTypes.
// FIXME: Only NSErrorChecker needs BugType's FlushReports.
// Turn NSErrorChecker into a proper checker and remove this.
- SmallVector<const BugType*, 16> bugTypes;
- for (BugTypesTy::iterator I=BugTypes.begin(), E=BugTypes.end(); I!=E; ++I)
- bugTypes.push_back(*I);
+ SmallVector<const BugType *, 16> bugTypes(BugTypes.begin(), BugTypes.end());
for (SmallVectorImpl<const BugType *>::iterator
I = bugTypes.begin(), E = bugTypes.end(); I != E; ++I)
const_cast<BugType*>(*I)->FlushReports(*this);
@@ -3055,8 +3053,7 @@ static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM) {
// Now take the pieces and construct a new PathDiagnostic.
path.clear();
- for (PiecesTy::iterator I=Pieces.begin(), E=Pieces.end(); I!=E; ++I)
- path.push_back(*I);
+ path.insert(path.end(), Pieces.begin(), Pieces.end());
}
bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD,
@@ -3434,10 +3431,8 @@ void BugReporter::FlushReport(BugReport *exampleReport,
PathDiagnosticLocation L = exampleReport->getLocation(getSourceManager());
auto piece = llvm::make_unique<PathDiagnosticEventPiece>(
L, exampleReport->getDescription());
- BugReport::ranges_iterator Beg, End;
- std::tie(Beg, End) = exampleReport->getRanges();
- for ( ; Beg != End; ++Beg)
- piece->addRange(*Beg);
+ for (const SourceRange &Range : exampleReport->getRanges())
+ piece->addRange(Range);
D->setEndOfPath(std::move(piece));
}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 2d56bd088497..b906cc9e7624 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -111,15 +111,14 @@ std::unique_ptr<PathDiagnosticPiece> BugReporterVisitor::getDefaultEndPath(
PathDiagnosticLocation L =
PathDiagnosticLocation::createEndOfPath(EndPathNode,BRC.getSourceManager());
- BugReport::ranges_iterator Beg, End;
- std::tie(Beg, End) = BR.getRanges();
+ const auto &Ranges = BR.getRanges();
// Only add the statement itself as a range if we didn't specify any
// special ranges for this report.
- auto P = llvm::make_unique<PathDiagnosticEventPiece>(L, BR.getDescription(),
- Beg == End);
- for (; Beg != End; ++Beg)
- P->addRange(*Beg);
+ auto P = llvm::make_unique<PathDiagnosticEventPiece>(
+ L, BR.getDescription(), Ranges.begin() == Ranges.end());
+ for (const SourceRange &Range : Ranges)
+ P->addRange(Range);
return std::move(P);
}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp
index 1a3965acaf42..22352111d14a 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp
@@ -36,11 +36,3 @@ raw_ostream& clang::ento::operator<<(raw_ostream &Out,
Out << Checker.getCheckName().getName();
return Out;
}
-
-void Checker<check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
- check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
- check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
- check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
- check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
- check::_VoidCheck, check::_VoidCheck, check::_VoidCheck
- >::anchor() { }
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 4699df8819bd..8b7f18fdf6ac 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1901,6 +1901,9 @@ void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *A,
getCheckerManager().runCheckersForPreStmt(checkerPreStmt, Pred, A, *this);
StmtNodeBuilder Bldr(checkerPreStmt, Dst, *currBldrCtx);
+ assert(A->isGLValue() ||
+ (!AMgr.getLangOpts().CPlusPlus &&
+ A->getType().isCForbiddenLValueType()));
for (ExplodedNodeSet::iterator it = checkerPreStmt.begin(),
ei = checkerPreStmt.end(); it != ei; ++it) {
@@ -1909,7 +1912,6 @@ void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *A,
SVal V = state->getLValue(A->getType(),
state->getSVal(Idx, LCtx),
state->getSVal(Base, LCtx));
- assert(A->isGLValue());
Bldr.generateNode(A, *it, state->BindExpr(A, LCtx, V), nullptr,
ProgramPoint::PostLValueKind);
}
@@ -2646,17 +2648,6 @@ struct DOTGraphTraits<ExplodedNode*> :
} // end llvm namespace
#endif
-#ifndef NDEBUG
-template <typename ITERATOR>
-ExplodedNode *GetGraphNode(ITERATOR I) { return *I; }
-
-template <> ExplodedNode*
-GetGraphNode<llvm::DenseMap<ExplodedNode*, Expr*>::iterator>
- (llvm::DenseMap<ExplodedNode*, Expr*>::iterator I) {
- return I->first;
-}
-#endif
-
void ExprEngine::ViewGraph(bool trim) {
#ifndef NDEBUG
if (trim) {
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index ffda52709dc2..1777ea97a402 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -31,7 +31,7 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
ExplodedNodeSet Tmp2;
getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, B, *this);
- // With both the LHS and RHS evaluated, process the operation itself.
+ // With both the LHS and RHS evaluated, process the operation itself.
for (ExplodedNodeSet::iterator it=CheckedSet.begin(), ei=CheckedSet.end();
it != ei; ++it) {
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
index 88b5464d44be..3c1a3b4111d1 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
@@ -45,7 +45,7 @@ class HTMLDiagnostics : public PathDiagnosticConsumer {
public:
HTMLDiagnostics(AnalyzerOptions &AnalyzerOpts, const std::string& prefix, const Preprocessor &pp);
- virtual ~HTMLDiagnostics() { FlushDiagnostics(nullptr); }
+ ~HTMLDiagnostics() override { FlushDiagnostics(nullptr); }
void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
FilesMade *filesMade) override;
@@ -282,7 +282,7 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
llvm::sys::path::append(Model, Directory, "report-%%%%%%.html");
if (std::error_code EC =
- llvm::sys::fs::createUniqueFile(Model.str(), FD, ResultPath)) {
+ llvm::sys::fs::createUniqueFile(Model, FD, ResultPath)) {
llvm::errs() << "warning: could not create file in '" << Directory
<< "': " << EC.message() << '\n';
return;
@@ -302,12 +302,12 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
<< "-" << i << ".html";
llvm::sys::path::append(Model, Directory,
filename.str());
- EC = llvm::sys::fs::openFileForWrite(Model.str(),
+ EC = llvm::sys::fs::openFileForWrite(Model,
FD,
llvm::sys::fs::F_RW |
llvm::sys::fs::F_Excl);
if (EC && EC != std::errc::file_exists) {
- llvm::errs() << "warning: could not create file '" << Model.str()
+ llvm::errs() << "warning: could not create file '" << Model
<< "': " << EC.message() << '\n';
return;
}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
index 76cead623298..1fa675433b56 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -1372,10 +1372,11 @@ void BlockDataRegion::LazyInitializeReferencedVars() {
return;
AnalysisDeclContext *AC = getCodeRegion()->getAnalysisDeclContext();
- AnalysisDeclContext::referenced_decls_iterator I, E;
- std::tie(I, E) = AC->getReferencedBlockVars(BC->getDecl());
+ const auto &ReferencedBlockVars = AC->getReferencedBlockVars(BC->getDecl());
+ auto NumBlockVars =
+ std::distance(ReferencedBlockVars.begin(), ReferencedBlockVars.end());
- if (I == E) {
+ if (NumBlockVars == 0) {
ReferencedVars = (void*) 0x1;
return;
}
@@ -1386,14 +1387,14 @@ void BlockDataRegion::LazyInitializeReferencedVars() {
typedef BumpVector<const MemRegion*> VarVec;
VarVec *BV = (VarVec*) A.Allocate<VarVec>();
- new (BV) VarVec(BC, E - I);
+ new (BV) VarVec(BC, NumBlockVars);
VarVec *BVOriginal = (VarVec*) A.Allocate<VarVec>();
- new (BVOriginal) VarVec(BC, E - I);
+ new (BVOriginal) VarVec(BC, NumBlockVars);
- for ( ; I != E; ++I) {
+ for (const VarDecl *VD : ReferencedBlockVars) {
const VarRegion *VR = nullptr;
const VarRegion *OriginalVR = nullptr;
- std::tie(VR, OriginalVR) = getCaptureRegions(*I);
+ std::tie(VR, OriginalVR) = getCaptureRegions(VD);
assert(VR);
assert(OriginalVR);
BV->push_back(VR, BC);
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
index b971fff642d4..c4900313cad4 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
@@ -432,11 +432,15 @@ void PathDiagnosticConsumer::FlushDiagnostics(
// Sort the diagnostics so that they are always emitted in a deterministic
// order.
- if (!BatchDiags.empty())
- std::sort(BatchDiags.begin(), BatchDiags.end(),
- [](const PathDiagnostic *X, const PathDiagnostic *Y) {
- return X != Y && compare(*X, *Y);
- });
+ int (*Comp)(const PathDiagnostic *const *, const PathDiagnostic *const *) =
+ [](const PathDiagnostic *const *X, const PathDiagnostic *const *Y) {
+ assert(*X != *Y && "PathDiagnostics not uniqued!");
+ if (compare(**X, **Y))
+ return -1;
+ assert(compare(**Y, **X) && "Not a total order!");
+ return 1;
+ };
+ array_pod_sort(BatchDiags.begin(), BatchDiags.end(), Comp);
FlushDiagnosticsImpl(BatchDiags, Files);
@@ -452,7 +456,7 @@ void PathDiagnosticConsumer::FlushDiagnostics(
}
PathDiagnosticConsumer::FilesMade::~FilesMade() {
- for (PDFileEntry &Entry : *this)
+ for (PDFileEntry &Entry : Set)
Entry.~PDFileEntry();
}
@@ -462,11 +466,11 @@ void PathDiagnosticConsumer::FilesMade::addDiagnostic(const PathDiagnostic &PD,
llvm::FoldingSetNodeID NodeID;
NodeID.Add(PD);
void *InsertPos;
- PDFileEntry *Entry = FindNodeOrInsertPos(NodeID, InsertPos);
+ PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos);
if (!Entry) {
Entry = Alloc.Allocate<PDFileEntry>();
Entry = new (Entry) PDFileEntry(NodeID);
- InsertNode(Entry, InsertPos);
+ Set.InsertNode(Entry, InsertPos);
}
// Allocate persistent storage for the file name.
@@ -483,7 +487,7 @@ PathDiagnosticConsumer::FilesMade::getFiles(const PathDiagnostic &PD) {
llvm::FoldingSetNodeID NodeID;
NodeID.Add(PD);
void *InsertPos;
- PDFileEntry *Entry = FindNodeOrInsertPos(NodeID, InsertPos);
+ PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos);
if (!Entry)
return nullptr;
return &Entry->files;
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
index a2c66f881482..e0aff589e053 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -37,7 +37,7 @@ namespace {
const LangOptions &LangOpts,
bool supportsMultipleFiles);
- virtual ~PlistDiagnostics() {}
+ ~PlistDiagnostics() override {}
void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
FilesMade *filesMade) override;
@@ -106,13 +106,14 @@ static void ReportControlFlow(raw_ostream &o,
// by forcing to use only the beginning of the range. This simplifies the layout
// logic for clients.
Indent(o, indent) << "<key>start</key>\n";
- SourceLocation StartEdge = I->getStart().asRange().getBegin();
- EmitRange(o, SM, LangOpts, CharSourceRange::getTokenRange(StartEdge), FM,
+ SourceRange StartEdge(
+ SM.getExpansionLoc(I->getStart().asRange().getBegin()));
+ EmitRange(o, SM, Lexer::getAsCharRange(StartEdge, SM, LangOpts), FM,
indent + 1);
Indent(o, indent) << "<key>end</key>\n";
- SourceLocation EndEdge = I->getEnd().asRange().getBegin();
- EmitRange(o, SM, LangOpts, CharSourceRange::getTokenRange(EndEdge), FM,
+ SourceRange EndEdge(SM.getExpansionLoc(I->getEnd().asRange().getBegin()));
+ EmitRange(o, SM, Lexer::getAsCharRange(EndEdge, SM, LangOpts), FM,
indent + 1);
--indent;
@@ -154,7 +155,7 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P,
FullSourceLoc L = P.getLocation().asLocation();
Indent(o, indent) << "<key>location</key>\n";
- EmitLocation(o, SM, LangOpts, L, FM, indent);
+ EmitLocation(o, SM, L, FM, indent);
// Output the ranges (if any).
ArrayRef<SourceRange> Ranges = P.getRanges();
@@ -163,11 +164,10 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P,
Indent(o, indent) << "<key>ranges</key>\n";
Indent(o, indent) << "<array>\n";
++indent;
- for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end();
- I != E; ++I) {
- EmitRange(o, SM, LangOpts, CharSourceRange::getTokenRange(*I), FM,
- indent + 1);
- }
+ for (auto &R : Ranges)
+ EmitRange(o, SM,
+ Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts),
+ FM, indent + 1);
--indent;
Indent(o, indent) << "</array>\n";
}
@@ -387,7 +387,9 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
EmitString(o, D->getCategory()) << '\n';
o << " <key>type</key>";
EmitString(o, D->getBugType()) << '\n';
-
+ o << " <key>check_name</key>";
+ EmitString(o, D->getCheckName()) << '\n';
+
// Output information about the semantic context where
// the issue occurred.
if (const Decl *DeclWithIssue = D->getDeclWithIssue()) {
@@ -453,7 +455,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
// Output the location of the bug.
o << " <key>location</key>\n";
- EmitLocation(o, *SM, LangOpts, D->getLocation().asLocation(), FM, 2);
+ EmitLocation(o, *SM, D->getLocation().asLocation(), FM, 2);
// Output the diagnostic to the sub-diagnostic client, if any.
if (!filesMade->empty()) {
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 45056226c9e0..6d41fc2146fe 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -767,7 +767,7 @@ static inline bool isUnionField(const FieldRegion *FR) {
typedef SmallVector<const FieldDecl *, 8> FieldVector;
-void getSymbolicOffsetFields(BindingKey K, FieldVector &Fields) {
+static void getSymbolicOffsetFields(BindingKey K, FieldVector &Fields) {
assert(K.hasSymbolicOffset() && "Not implemented for concrete offset keys");
const MemRegion *Base = K.getConcreteOffsetRegion();
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.h
index a72d1d4f9218..135cd4ef8649 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.h
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.h
@@ -27,7 +27,7 @@ class SimpleConstraintManager : public ConstraintManager {
public:
SimpleConstraintManager(SubEngine *subengine, SValBuilder &SB)
: SU(subengine), SVB(SB) {}
- virtual ~SimpleConstraintManager();
+ ~SimpleConstraintManager() override;
//===------------------------------------------------------------------===//
// Common implementation for the interface provided by ConstraintManager.
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index df9e4d6f9199..b3cab87c8080 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -29,7 +29,7 @@ public:
SimpleSValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context,
ProgramStateManager &stateMgr)
: SValBuilder(alloc, context, stateMgr) {}
- virtual ~SimpleSValBuilder() {}
+ ~SimpleSValBuilder() override {}
SVal evalMinus(NonLoc val) override;
SVal evalComplement(NonLoc val) override;
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index 183ef358df63..fbeffb8aac80 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -92,7 +92,7 @@ class ClangDiagPathDiagConsumer : public PathDiagnosticConsumer {
public:
ClangDiagPathDiagConsumer(DiagnosticsEngine &Diag)
: Diag(Diag), IncludePath(false) {}
- virtual ~ClangDiagPathDiagConsumer() {}
+ ~ClangDiagPathDiagConsumer() override {}
StringRef getName() const override { return "ClangDiags"; }
bool supportsLogicalOpControlFlow() const override { return true; }
@@ -199,7 +199,7 @@ public:
}
}
- ~AnalysisConsumer() {
+ ~AnalysisConsumer() override {
if (Opts->PrintStats)
delete TUTotalTimer;
}
@@ -373,8 +373,7 @@ public:
return true;
}
- virtual void
- AddDiagnosticConsumer(PathDiagnosticConsumer *Consumer) override {
+ void AddDiagnosticConsumer(PathDiagnosticConsumer *Consumer) override {
PathConsumers.push_back(Consumer);
}
@@ -590,7 +589,7 @@ AnalysisConsumer::getModeForDecl(Decl *D, AnalysisMode Mode) {
// - System headers: don't run any checks.
SourceManager &SM = Ctx->getSourceManager();
SourceLocation SL = SM.getExpansionLoc(D->getLocation());
- if (!Opts->AnalyzeAll && !SM.isInMainFile(SL)) {
+ if (!Opts->AnalyzeAll && !SM.isWrittenInMainFile(SL)) {
if (SL.isInvalid() || SM.isInSystemHeader(SL))
return AM_None;
return Mode & ~AM_Path;
@@ -724,7 +723,7 @@ class UbigraphViz : public ExplodedNode::Auditor {
public:
UbigraphViz(std::unique_ptr<raw_ostream> Out, StringRef Filename);
- ~UbigraphViz();
+ ~UbigraphViz() override;
void AddEdge(ExplodedNode *Src, ExplodedNode *Dst) override;
};
@@ -735,7 +734,7 @@ static std::unique_ptr<ExplodedNode::Auditor> CreateUbiViz() {
SmallString<128> P;
int FD;
llvm::sys::fs::createTemporaryFile("llvm_ubi", "", FD, P);
- llvm::errs() << "Writing '" << P.str() << "'.\n";
+ llvm::errs() << "Writing '" << P << "'.\n";
auto Stream = llvm::make_unique<llvm::raw_fd_ostream>(FD, true);
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
index 36565cb6e2ca..b3ff79750b49 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
@@ -125,7 +125,7 @@ ento::createCheckerManager(AnalyzerOptions &opts, const LangOptions &langOpts,
}
- return std::move(checkerMgr);
+ return checkerMgr;
}
void ento::printCheckerHelp(raw_ostream &out, ArrayRef<std::string> plugins) {
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h
index fd24e32f3a3d..e23bf8abf384 100644
--- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h
@@ -43,8 +43,8 @@ namespace ento {
class ModelInjector : public CodeInjector {
public:
ModelInjector(CompilerInstance &CI);
- Stmt *getBody(const FunctionDecl *D);
- Stmt *getBody(const ObjCMethodDecl *D);
+ Stmt *getBody(const FunctionDecl *D) override;
+ Stmt *getBody(const ObjCMethodDecl *D) override;
private:
/// \brief Synthesize a body for a declaration