summaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer')
-rw-r--r--lib/StaticAnalyzer/Checkers/CStringChecker.cpp1
-rw-r--r--lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp188
-rw-r--r--lib/StaticAnalyzer/Checkers/IteratorChecker.cpp25
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocChecker.cpp43
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp18
-rw-r--r--lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp10
-rw-r--r--lib/StaticAnalyzer/Core/BugReporterVisitors.cpp37
-rw-r--r--lib/StaticAnalyzer/Core/CallEvent.cpp63
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp150
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineC.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCXX.cpp43
-rw-r--r--lib/StaticAnalyzer/Core/IssueHash.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/ProgramState.cpp5
-rw-r--r--lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp46
-rw-r--r--lib/StaticAnalyzer/Core/Store.cpp4
16 files changed, 395 insertions, 244 deletions
diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index 278452ec994a6..12a576e5d80d5 100644
--- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -552,6 +552,7 @@ void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State,
BuiltinBug *BT = static_cast<BuiltinBug *>(BT_Null.get());
auto Report = llvm::make_unique<BugReport>(*BT, WarningMsg, N);
+ Report->addRange(S->getSourceRange());
bugreporter::trackNullOrUndefValue(N, S, *Report);
C.emitReport(std::move(Report));
}
diff --git a/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
index ed877ab345189..29677f737f5cf 100644
--- a/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
@@ -91,37 +91,53 @@ public:
ReserveFn("reserve"), ResizeFn("resize"),
ShrinkToFitFn("shrink_to_fit"), SwapFn("swap") {}
- /// Check whether the function called on the container object is a
- /// member function that potentially invalidates pointers referring
- /// to the objects's internal buffer.
- bool mayInvalidateBuffer(const CallEvent &Call) const;
-
- /// Record the connection between the symbol returned by c_str() and the
- /// corresponding string object region in the ProgramState. Mark the symbol
- /// released if the string object is destroyed.
+ /// Check if the object of this member function call is a `basic_string`.
+ bool isCalledOnStringObject(const CXXInstanceCall *ICall) const;
+
+ /// Check whether the called member function potentially invalidates
+ /// pointers referring to the container object's inner buffer.
+ bool isInvalidatingMemberFunction(const CallEvent &Call) const;
+
+ /// Mark pointer symbols associated with the given memory region released
+ /// in the program state.
+ void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State,
+ const MemRegion *ObjRegion,
+ CheckerContext &C) const;
+
+ /// Standard library functions that take a non-const `basic_string` argument by
+ /// reference may invalidate its inner pointers. Check for these cases and
+ /// mark the pointers released.
+ void checkFunctionArguments(const CallEvent &Call, ProgramStateRef State,
+ CheckerContext &C) const;
+
+ /// Record the connection between raw pointers referring to a container
+ /// object's inner buffer and the object's memory region in the program state.
+ /// Mark potentially invalidated pointers released.
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
- /// Clean up the ProgramState map.
+ /// Clean up the program state map.
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
};
} // end anonymous namespace
-// [string.require]
-//
-// "References, pointers, and iterators referring to the elements of a
-// basic_string sequence may be invalidated by the following uses of that
-// basic_string object:
-//
-// -- TODO: As an argument to any standard library function taking a reference
-// to non-const basic_string as an argument. For example, as an argument to
-// non-member functions swap(), operator>>(), and getline(), or as an argument
-// to basic_string::swap().
-//
-// -- Calling non-const member functions, except operator[], at, front, back,
-// begin, rbegin, end, and rend."
-//
-bool InnerPointerChecker::mayInvalidateBuffer(const CallEvent &Call) const {
+bool InnerPointerChecker::isCalledOnStringObject(
+ const CXXInstanceCall *ICall) const {
+ const auto *ObjRegion =
+ dyn_cast_or_null<TypedValueRegion>(ICall->getCXXThisVal().getAsRegion());
+ if (!ObjRegion)
+ return false;
+
+ QualType ObjTy = ObjRegion->getValueType();
+ if (ObjTy.isNull() ||
+ ObjTy->getAsCXXRecordDecl()->getName() != "basic_string")
+ return false;
+
+ return true;
+}
+
+bool InnerPointerChecker::isInvalidatingMemberFunction(
+ const CallEvent &Call) const {
if (const auto *MemOpCall = dyn_cast<CXXMemberOperatorCall>(&Call)) {
OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator();
if (Opc == OO_Equal || Opc == OO_PlusEqual)
@@ -137,53 +153,104 @@ bool InnerPointerChecker::mayInvalidateBuffer(const CallEvent &Call) const {
Call.isCalled(SwapFn));
}
-void InnerPointerChecker::checkPostCall(const CallEvent &Call,
- CheckerContext &C) const {
- const auto *ICall = dyn_cast<CXXInstanceCall>(&Call);
- if (!ICall)
+void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call,
+ ProgramStateRef State,
+ const MemRegion *MR,
+ CheckerContext &C) const {
+ if (const PtrSet *PS = State->get<RawPtrMap>(MR)) {
+ const Expr *Origin = Call.getOriginExpr();
+ for (const auto Symbol : *PS) {
+ // NOTE: `Origin` may be null, and will be stored so in the symbol's
+ // `RefState` in MallocChecker's `RegionState` program state map.
+ State = allocation_state::markReleased(State, Symbol, Origin);
+ }
+ State = State->remove<RawPtrMap>(MR);
+ C.addTransition(State);
return;
+ }
+}
- SVal Obj = ICall->getCXXThisVal();
- const auto *ObjRegion = dyn_cast_or_null<TypedValueRegion>(Obj.getAsRegion());
- if (!ObjRegion)
- return;
+void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call,
+ ProgramStateRef State,
+ CheckerContext &C) const {
+ if (const auto *FC = dyn_cast<AnyFunctionCall>(&Call)) {
+ const FunctionDecl *FD = FC->getDecl();
+ if (!FD || !FD->isInStdNamespace())
+ return;
- auto *TypeDecl = ObjRegion->getValueType()->getAsCXXRecordDecl();
- if (TypeDecl->getName() != "basic_string")
- return;
+ for (unsigned I = 0, E = FD->getNumParams(); I != E; ++I) {
+ QualType ParamTy = FD->getParamDecl(I)->getType();
+ if (!ParamTy->isReferenceType() ||
+ ParamTy->getPointeeType().isConstQualified())
+ continue;
- ProgramStateRef State = C.getState();
+ // In case of member operator calls, `this` is counted as an
+ // argument but not as a parameter.
+ bool isaMemberOpCall = isa<CXXMemberOperatorCall>(FC);
+ unsigned ArgI = isaMemberOpCall ? I+1 : I;
- if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) {
- SVal RawPtr = Call.getReturnValue();
- if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) {
- // Start tracking this raw pointer by adding it to the set of symbols
- // associated with this container object in the program state map.
- PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();
- const PtrSet *SetPtr = State->get<RawPtrMap>(ObjRegion);
- PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet();
- assert(C.wasInlined || !Set.contains(Sym));
- Set = F.add(Set, Sym);
- State = State->set<RawPtrMap>(ObjRegion, Set);
- C.addTransition(State);
+ SVal Arg = FC->getArgSVal(ArgI);
+ const auto *ArgRegion =
+ dyn_cast_or_null<TypedValueRegion>(Arg.getAsRegion());
+ if (!ArgRegion)
+ continue;
+
+ markPtrSymbolsReleased(Call, State, ArgRegion, C);
}
- return;
}
+}
- if (mayInvalidateBuffer(Call)) {
- if (const PtrSet *PS = State->get<RawPtrMap>(ObjRegion)) {
- // Mark all pointer symbols associated with the deleted object released.
- const Expr *Origin = Call.getOriginExpr();
- for (const auto Symbol : *PS) {
- // NOTE: `Origin` may be null, and will be stored so in the symbol's
- // `RefState` in MallocChecker's `RegionState` program state map.
- State = allocation_state::markReleased(State, Symbol, Origin);
+// [string.require]
+//
+// "References, pointers, and iterators referring to the elements of a
+// basic_string sequence may be invalidated by the following uses of that
+// basic_string object:
+//
+// -- As an argument to any standard library function taking a reference
+// to non-const basic_string as an argument. For example, as an argument to
+// non-member functions swap(), operator>>(), and getline(), or as an argument
+// to basic_string::swap().
+//
+// -- Calling non-const member functions, except operator[], at, front, back,
+// begin, rbegin, end, and rend."
+
+void InnerPointerChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) {
+ if (isCalledOnStringObject(ICall)) {
+ const auto *ObjRegion = dyn_cast_or_null<TypedValueRegion>(
+ ICall->getCXXThisVal().getAsRegion());
+
+ if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) {
+ SVal RawPtr = Call.getReturnValue();
+ if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) {
+ // Start tracking this raw pointer by adding it to the set of symbols
+ // associated with this container object in the program state map.
+
+ PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();
+ const PtrSet *SetPtr = State->get<RawPtrMap>(ObjRegion);
+ PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet();
+ assert(C.wasInlined || !Set.contains(Sym));
+ Set = F.add(Set, Sym);
+
+ State = State->set<RawPtrMap>(ObjRegion, Set);
+ C.addTransition(State);
+ }
+ return;
+ }
+
+ // Check [string.require] / second point.
+ if (isInvalidatingMemberFunction(Call)) {
+ markPtrSymbolsReleased(Call, State, ObjRegion, C);
+ return;
}
- State = State->remove<RawPtrMap>(ObjRegion);
- C.addTransition(State);
- return;
}
}
+
+ // Check [string.require] / first point.
+ checkFunctionArguments(Call, State, C);
}
void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper,
@@ -216,7 +283,6 @@ InnerPointerChecker::InnerPointerBRVisitor::VisitNode(const ExplodedNode *N,
const ExplodedNode *PrevN,
BugReporterContext &BRC,
BugReport &BR) {
-
if (!isSymbolTracked(N->getState(), PtrToBuf) ||
isSymbolTracked(PrevN->getState(), PtrToBuf))
return nullptr;
diff --git a/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp b/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
index 56c250cd16783..520c32e1c7703 100644
--- a/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
@@ -291,6 +291,7 @@ const ContainerData *getContainerData(ProgramStateRef State,
const MemRegion *Cont);
ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
const ContainerData &CData);
+bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos);
bool isZero(ProgramStateRef State, const NonLoc &Val);
} // namespace
@@ -536,7 +537,11 @@ void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
auto ContMap = State->get<ContainerMap>();
for (const auto Cont : ContMap) {
if (!SR.isLiveRegion(Cont.first)) {
- State = State->remove<ContainerMap>(Cont.first);
+ // We must keep the container data while it has live iterators to be able
+ // to compare them to the begin and the end of the container.
+ if (!hasLiveIterators(State, Cont.first)) {
+ State = State->remove<ContainerMap>(Cont.first);
+ }
}
}
@@ -546,6 +551,8 @@ void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
State = State->remove<IteratorComparisonMap>(Comp.first);
}
}
+
+ C.addTransition(State);
}
ProgramStateRef IteratorChecker::evalAssume(ProgramStateRef State, SVal Cond,
@@ -1188,6 +1195,22 @@ ProgramStateRef relateIteratorPositions(ProgramStateRef State,
return NewState;
}
+bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
+ auto RegionMap = State->get<IteratorRegionMap>();
+ for (const auto Reg : RegionMap) {
+ if (Reg.second.getContainer() == Cont)
+ return true;
+ }
+
+ auto SymbolMap = State->get<IteratorSymbolMap>();
+ for (const auto Sym : SymbolMap) {
+ if (Sym.second.getContainer() == Cont)
+ return true;
+ }
+
+ return false;
+}
+
bool isZero(ProgramStateRef State, const NonLoc &Val) {
auto &BVF = State->getBasicVals();
return compare(State, Val,
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 8f07f413e81f6..ebaf79a780c07 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -179,11 +179,11 @@ public:
II_strdup(nullptr), II_win_strdup(nullptr), II_kmalloc(nullptr),
II_if_nameindex(nullptr), II_if_freenameindex(nullptr),
II_wcsdup(nullptr), II_win_wcsdup(nullptr), II_g_malloc(nullptr),
- II_g_malloc0(nullptr), II_g_realloc(nullptr), II_g_try_malloc(nullptr),
- II_g_try_malloc0(nullptr), II_g_try_realloc(nullptr),
- II_g_free(nullptr), II_g_memdup(nullptr), II_g_malloc_n(nullptr),
- II_g_malloc0_n(nullptr), II_g_realloc_n(nullptr),
- II_g_try_malloc_n(nullptr), II_g_try_malloc0_n(nullptr),
+ II_g_malloc0(nullptr), II_g_realloc(nullptr), II_g_try_malloc(nullptr),
+ II_g_try_malloc0(nullptr), II_g_try_realloc(nullptr),
+ II_g_free(nullptr), II_g_memdup(nullptr), II_g_malloc_n(nullptr),
+ II_g_malloc0_n(nullptr), II_g_realloc_n(nullptr),
+ II_g_try_malloc_n(nullptr), II_g_try_malloc0_n(nullptr),
II_g_try_realloc_n(nullptr) {}
/// In pessimistic mode, the checker assumes that it does not know which
@@ -248,11 +248,11 @@ private:
*II_realloc, *II_calloc, *II_valloc, *II_reallocf,
*II_strndup, *II_strdup, *II_win_strdup, *II_kmalloc,
*II_if_nameindex, *II_if_freenameindex, *II_wcsdup,
- *II_win_wcsdup, *II_g_malloc, *II_g_malloc0,
- *II_g_realloc, *II_g_try_malloc, *II_g_try_malloc0,
- *II_g_try_realloc, *II_g_free, *II_g_memdup,
- *II_g_malloc_n, *II_g_malloc0_n, *II_g_realloc_n,
- *II_g_try_malloc_n, *II_g_try_malloc0_n,
+ *II_win_wcsdup, *II_g_malloc, *II_g_malloc0,
+ *II_g_realloc, *II_g_try_malloc, *II_g_try_malloc0,
+ *II_g_try_realloc, *II_g_free, *II_g_memdup,
+ *II_g_malloc_n, *II_g_malloc0_n, *II_g_realloc_n,
+ *II_g_try_malloc_n, *II_g_try_malloc0_n,
*II_g_try_realloc_n;
mutable Optional<uint64_t> KernelZeroFlagVal;
@@ -346,7 +346,7 @@ private:
ProgramStateRef ReallocMemAux(CheckerContext &C, const CallExpr *CE,
bool FreesMemOnFailure,
- ProgramStateRef State,
+ ProgramStateRef State,
bool SuffixWithN = false) const;
static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks,
const Expr *BlockBytes);
@@ -652,7 +652,7 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD,
initIdentifierInfo(C);
if (Family == AF_Malloc && CheckFree) {
- if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf ||
+ if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf ||
FunI == II_g_free)
return true;
}
@@ -662,12 +662,12 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD,
FunI == II_calloc || FunI == II_valloc || FunI == II_strdup ||
FunI == II_win_strdup || FunI == II_strndup || FunI == II_wcsdup ||
FunI == II_win_wcsdup || FunI == II_kmalloc ||
- FunI == II_g_malloc || FunI == II_g_malloc0 ||
- FunI == II_g_realloc || FunI == II_g_try_malloc ||
+ FunI == II_g_malloc || FunI == II_g_malloc0 ||
+ FunI == II_g_realloc || FunI == II_g_try_malloc ||
FunI == II_g_try_malloc0 || FunI == II_g_try_realloc ||
- FunI == II_g_memdup || FunI == II_g_malloc_n ||
- FunI == II_g_malloc0_n || FunI == II_g_realloc_n ||
- FunI == II_g_try_malloc_n || FunI == II_g_try_malloc0_n ||
+ FunI == II_g_memdup || FunI == II_g_malloc_n ||
+ FunI == II_g_malloc0_n || FunI == II_g_realloc_n ||
+ FunI == II_g_try_malloc_n || FunI == II_g_try_malloc0_n ||
FunI == II_g_try_realloc_n)
return true;
}
@@ -873,7 +873,7 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
return;
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
State = ProcessZeroAllocation(C, CE, 0, State);
- } else if (FunI == II_realloc || FunI == II_g_realloc ||
+ } else if (FunI == II_realloc || FunI == II_g_realloc ||
FunI == II_g_try_realloc) {
State = ReallocMemAux(C, CE, false, State);
State = ProcessZeroAllocation(C, CE, 1, State);
@@ -936,7 +936,7 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
return;
State = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State);
State = ProcessZeroAllocation(C, CE, 1, State);
- } else if (FunI == II_g_malloc_n || FunI == II_g_try_malloc_n ||
+ } else if (FunI == II_g_malloc_n || FunI == II_g_try_malloc_n ||
FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) {
if (CE->getNumArgs() < 2)
return;
@@ -2930,6 +2930,11 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode(
OS << MemCallE->getMethodDecl()->getNameAsString();
} else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) {
OS << OpCallE->getDirectCallee()->getNameAsString();
+ } else if (const auto *CallE = dyn_cast<CallExpr>(S)) {
+ auto &CEMgr = BRC.getStateManager().getCallEventManager();
+ CallEventRef<> Call = CEMgr.getSimpleCall(CallE, state, CurrentLC);
+ const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl());
+ OS << (D ? D->getNameAsString() : "unknown");
}
OS << "'";
}
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
index 2c1e139330d68..9c85c09837239 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
@@ -4000,7 +4000,7 @@ void RetainCountChecker::checkEndFunction(const ReturnStmt *RS,
// Don't process anything within synthesized bodies.
const LocationContext *LCtx = Pred->getLocationContext();
if (LCtx->getAnalysisDeclContext()->isBodyAutosynthesized()) {
- assert(!LCtx->inTopFrame());
+ assert(!LCtx->inTopFrame());
return;
}
diff --git a/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp b/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp
index 64b61a0213d28..55516a34d1a78 100644
--- a/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp
@@ -45,9 +45,9 @@ const char * RunLoopBind = "NSRunLoopM";
const char * RunLoopRunBind = "RunLoopRunM";
const char * OtherMsgBind = "OtherMessageSentM";
const char * AutoreleasePoolBind = "AutoreleasePoolM";
+const char * OtherStmtAutoreleasePoolBind = "OtherAutoreleasePoolM";
-class RunLoopAutoreleaseLeakChecker : public Checker<
- check::ASTCodeBody> {
+class RunLoopAutoreleaseLeakChecker : public Checker<check::ASTCodeBody> {
public:
void checkASTCodeBody(const Decl *D,
@@ -66,6 +66,8 @@ static TriBoolTy
seenBeforeRec(const Stmt *Parent, const Stmt *A, const Stmt *B,
MemoizationMapTy &Memoization) {
for (const Stmt *C : Parent->children()) {
+ if (!C) continue;
+
if (C == A)
return true;
@@ -110,17 +112,20 @@ static void emitDiagnostics(BoundNodes &Match,
const auto *AP =
Match.getNodeAs<ObjCAutoreleasePoolStmt>(AutoreleasePoolBind);
+ const auto *OAP =
+ Match.getNodeAs<ObjCAutoreleasePoolStmt>(OtherStmtAutoreleasePoolBind);
bool HasAutoreleasePool = (AP != nullptr);
const auto *RL = Match.getNodeAs<ObjCMessageExpr>(RunLoopBind);
const auto *RLR = Match.getNodeAs<Stmt>(RunLoopRunBind);
assert(RLR && "Run loop launch not found");
-
assert(ME != RLR);
- if (HasAutoreleasePool && seenBefore(AP, RLR, ME))
+
+ // Launch of run loop occurs before the message-sent expression is seen.
+ if (seenBefore(DeclBody, RLR, ME))
return;
- if (!HasAutoreleasePool && seenBefore(DeclBody, RLR, ME))
+ if (HasAutoreleasePool && (OAP != AP))
return;
PathDiagnosticLocation Location = PathDiagnosticLocation::createBegin(
@@ -169,7 +174,8 @@ static void
checkTempObjectsInSamePool(const Decl *D, AnalysisManager &AM, BugReporter &BR,
const RunLoopAutoreleaseLeakChecker *Chkr) {
StatementMatcher RunLoopRunM = getRunLoopRunM();
- StatementMatcher OtherMessageSentM = getOtherMessageSentM();
+ StatementMatcher OtherMessageSentM = getOtherMessageSentM(
+ hasAncestor(autoreleasePoolStmt().bind(OtherStmtAutoreleasePoolBind)));
StatementMatcher RunLoopInAutorelease =
autoreleasePoolStmt(
diff --git a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
index feae9e59b343a..e7a20fa03a4ae 100644
--- a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -129,7 +129,7 @@ bool StackAddrEscapeChecker::isSemaphoreCaptured(const BlockDecl &B) const {
for (const auto &C : B.captures()) {
const auto *T = C.getVariable()->getType()->getAs<TypedefType>();
if (T && T->getDecl()->getIdentifier() == dispatch_semaphore_tII)
- return true;
+ return true;
}
return false;
}
@@ -175,9 +175,9 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
// There is a not-too-uncommon idiom
// where a block passed to dispatch_async captures a semaphore
// and then the thread (which called dispatch_async) is blocked on waiting
- // for the completion of the execution of the block
- // via dispatch_semaphore_wait. To avoid false-positives (for now)
- // we ignore all the blocks which have captured
+ // for the completion of the execution of the block
+ // via dispatch_semaphore_wait. To avoid false-positives (for now)
+ // we ignore all the blocks which have captured
// a variable of the type "dispatch_semaphore_t".
if (isSemaphoreCaptured(*B.getDecl()))
return;
@@ -263,7 +263,7 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R))
checkReturnedBlockCaptures(*B, C);
- if (!isa<StackSpaceRegion>(R->getMemorySpace()) ||
+ if (!isa<StackSpaceRegion>(R->getMemorySpace()) ||
isNotInCurrentFrame(R, C) || isArcManagedBlock(R, C))
return;
diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index c87bc685d8b96..d4d33c1746ced 100644
--- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -269,6 +269,8 @@ namespace {
/// pointer dereference outside.
class NoStoreFuncVisitor final : public BugReporterVisitor {
const SubRegion *RegionOfInterest;
+ const SourceManager &SM;
+ const PrintingPolicy &PP;
static constexpr const char *DiagnosticsMsg =
"Returning without writing to '";
@@ -284,7 +286,10 @@ class NoStoreFuncVisitor final : public BugReporterVisitor {
llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingCalculated;
public:
- NoStoreFuncVisitor(const SubRegion *R) : RegionOfInterest(R) {}
+ NoStoreFuncVisitor(const SubRegion *R)
+ : RegionOfInterest(R),
+ SM(R->getMemRegionManager()->getContext().getSourceManager()),
+ PP(R->getMemRegionManager()->getContext().getPrintingPolicy()) {}
void Profile(llvm::FoldingSetNodeID &ID) const override {
static int Tag = 0;
@@ -307,8 +312,6 @@ public:
CallEventRef<> Call =
BRC.getStateManager().getCallEventManager().getCaller(SCtx, State);
- const PrintingPolicy &PP = BRC.getASTContext().getPrintingPolicy();
- const SourceManager &SM = BRC.getSourceManager();
// Region of interest corresponds to an IVar, exiting a method
// which could have written into that IVar, but did not.
@@ -318,16 +321,14 @@ public:
IvarR->getDecl()) &&
!isRegionOfInterestModifiedInFrame(N))
return notModifiedMemberDiagnostics(
- Ctx, SM, PP, *CallExitLoc, Call,
- MC->getReceiverSVal().getAsRegion());
+ Ctx, *CallExitLoc, Call, MC->getReceiverSVal().getAsRegion());
if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) {
- const MemRegion *ThisRegion = CCall->getCXXThisVal().getAsRegion();
- if (RegionOfInterest->isSubRegionOf(ThisRegion)
+ const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion();
+ if (RegionOfInterest->isSubRegionOf(ThisR)
&& !CCall->getDecl()->isImplicit()
&& !isRegionOfInterestModifiedInFrame(N))
- return notModifiedMemberDiagnostics(Ctx, SM, PP, *CallExitLoc,
- CCall, ThisRegion);
+ return notModifiedMemberDiagnostics(Ctx, *CallExitLoc, Call, ThisR);
}
ArrayRef<ParmVarDecl *> parameters = getCallParameters(Call);
@@ -344,7 +345,7 @@ public:
return nullptr;
return notModifiedParameterDiagnostics(
- Ctx, SM, PP, *CallExitLoc, Call, PVD, R, IndirectionLevel);
+ Ctx, *CallExitLoc, Call, PVD, R, IndirectionLevel);
}
QualType PT = T->getPointeeType();
if (PT.isNull() || PT->isVoidType()) break;
@@ -446,8 +447,6 @@ private:
/// in a given function.
std::shared_ptr<PathDiagnosticPiece> notModifiedMemberDiagnostics(
const LocationContext *Ctx,
- const SourceManager &SM,
- const PrintingPolicy &PP,
CallExitBegin &CallExitLoc,
CallEventRef<> Call,
const MemRegion *ArgRegion) {
@@ -474,8 +473,6 @@ private:
/// before we get to the super region of \c RegionOfInterest
std::shared_ptr<PathDiagnosticPiece>
notModifiedParameterDiagnostics(const LocationContext *Ctx,
- const SourceManager &SM,
- const PrintingPolicy &PP,
CallExitBegin &CallExitLoc,
CallEventRef<> Call,
const ParmVarDecl *PVD,
@@ -612,8 +609,7 @@ public:
const ExplodedNode *N, const MemRegion *R,
bool EnableNullFPSuppression, BugReport &BR,
const SVal V) {
- AnalyzerOptions &Options = N->getState()->getStateManager()
- .getOwningEngine()->getAnalysisManager().options;
+ AnalyzerOptions &Options = N->getState()->getAnalysisManager().options;
if (EnableNullFPSuppression && Options.shouldSuppressNullReturnPaths()
&& V.getAs<Loc>())
BR.addVisitor(llvm::make_unique<MacroNullReturnSuppressionVisitor>(
@@ -740,9 +736,7 @@ public:
RetVal = State->getSVal(*LValue);
// See if the return value is NULL. If so, suppress the report.
- SubEngine *Eng = State->getStateManager().getOwningEngine();
- assert(Eng && "Cannot file a bug report without an owning engine");
- AnalyzerOptions &Options = Eng->getAnalysisManager().options;
+ AnalyzerOptions &Options = State->getAnalysisManager().options;
bool EnableNullFPSuppression = false;
if (InEnableNullFPSuppression && Options.shouldSuppressNullReturnPaths())
@@ -1321,9 +1315,7 @@ SuppressInlineDefensiveChecksVisitor::
SuppressInlineDefensiveChecksVisitor(DefinedSVal Value, const ExplodedNode *N)
: V(Value) {
// Check if the visitor is disabled.
- SubEngine *Eng = N->getState()->getStateManager().getOwningEngine();
- assert(Eng && "Cannot file a bug report without an owning engine");
- AnalyzerOptions &Options = Eng->getAnalysisManager().options;
+ AnalyzerOptions &Options = N->getState()->getAnalysisManager().options;
if (!Options.shouldSuppressInlinedDefensiveChecks())
IsSatisfied = true;
@@ -1603,6 +1595,7 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N,
LVNode->getSVal(Inner).getAsRegion();
if (R) {
+
// Mark both the variable region and its contents as interesting.
SVal V = LVState->getRawSVal(loc::MemRegionVal(R));
report.addVisitor(
diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp
index 8db7b06f186da..fe9260e32dd8b 100644
--- a/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -27,6 +27,7 @@
#include "clang/AST/Type.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/CFGStmtMap.h"
#include "clang/Analysis/ProgramPoint.h"
#include "clang/CrossTU/CrossTranslationUnit.h"
#include "clang/Basic/IdentifierTable.h"
@@ -166,6 +167,68 @@ bool CallEvent::isGlobalCFunction(StringRef FunctionName) const {
return CheckerContext::isCLibraryFunction(FD, FunctionName);
}
+AnalysisDeclContext *CallEvent::getCalleeAnalysisDeclContext() const {
+ const Decl *D = getDecl();
+
+ // If the callee is completely unknown, we cannot construct the stack frame.
+ if (!D)
+ return nullptr;
+
+ // FIXME: Skip virtual functions for now. There's no easy procedure to foresee
+ // the exact decl that should be used, especially when it's not a definition.
+ if (const Decl *RD = getRuntimeDefinition().getDecl())
+ if (RD != D)
+ return nullptr;
+
+ return LCtx->getAnalysisDeclContext()->getManager()->getContext(D);
+}
+
+const StackFrameContext *CallEvent::getCalleeStackFrame() const {
+ AnalysisDeclContext *ADC = getCalleeAnalysisDeclContext();
+ if (!ADC)
+ return nullptr;
+
+ const Expr *E = getOriginExpr();
+ if (!E)
+ return nullptr;
+
+ // Recover CFG block via reverse lookup.
+ // TODO: If we were to keep CFG element information as part of the CallEvent
+ // instead of doing this reverse lookup, we would be able to build the stack
+ // frame for non-expression-based calls, and also we wouldn't need the reverse
+ // lookup.
+ CFGStmtMap *Map = LCtx->getAnalysisDeclContext()->getCFGStmtMap();
+ const CFGBlock *B = Map->getBlock(E);
+ assert(B);
+
+ // Also recover CFG index by scanning the CFG block.
+ unsigned Idx = 0, Sz = B->size();
+ for (; Idx < Sz; ++Idx)
+ if (auto StmtElem = (*B)[Idx].getAs<CFGStmt>())
+ if (StmtElem->getStmt() == E)
+ break;
+ assert(Idx < Sz);
+
+ return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, Idx);
+}
+
+const VarRegion *CallEvent::getParameterLocation(unsigned Index) const {
+ const StackFrameContext *SFC = getCalleeStackFrame();
+ // We cannot construct a VarRegion without a stack frame.
+ if (!SFC)
+ return nullptr;
+
+ const ParmVarDecl *PVD = parameters()[Index];
+ const VarRegion *VR =
+ State->getStateManager().getRegionManager().getVarRegion(PVD, SFC);
+
+ // This sanity check would fail if our parameter declaration doesn't
+ // correspond to the stack frame's function declaration.
+ assert(VR->getStackFrame() == SFC);
+
+ return VR;
+}
+
/// Returns true if a type is a pointer-to-const or reference-to-const
/// with no further indirection.
static bool isPointerToConst(QualType Ty) {
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 188316c096e38..2b4bdd754fdbc 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -117,56 +117,42 @@ STATISTIC(NumTimesRetriedWithoutInlining,
/// the construction context was present and contained references to these
/// AST nodes.
class ConstructedObjectKey {
- typedef std::pair<
- llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *>,
- const LocationContext *> ConstructedObjectKeyImpl;
+ typedef std::pair<ConstructionContextItem, const LocationContext *>
+ ConstructedObjectKeyImpl;
- ConstructedObjectKeyImpl Impl;
+ const ConstructedObjectKeyImpl Impl;
const void *getAnyASTNodePtr() const {
- if (const Stmt *S = getStmt())
+ if (const Stmt *S = getItem().getStmtOrNull())
return S;
else
- return getCXXCtorInitializer();
+ return getItem().getCXXCtorInitializer();
}
public:
- ConstructedObjectKey(
- llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P,
- const LocationContext *LC)
- : Impl(P, LC) {
- // This is the full list of statements that require additional actions when
- // encountered. This list may be expanded when new actions are implemented.
- assert(getCXXCtorInitializer() || isa<DeclStmt>(getStmt()) ||
- isa<CXXNewExpr>(getStmt()) || isa<CXXBindTemporaryExpr>(getStmt()) ||
- isa<MaterializeTemporaryExpr>(getStmt()) ||
- isa<CXXConstructExpr>(getStmt()));
- }
-
- const Stmt *getStmt() const {
- return Impl.first.dyn_cast<const Stmt *>();
- }
+ explicit ConstructedObjectKey(const ConstructionContextItem &Item,
+ const LocationContext *LC)
+ : Impl(Item, LC) {}
- const CXXCtorInitializer *getCXXCtorInitializer() const {
- return Impl.first.dyn_cast<const CXXCtorInitializer *>();
- }
-
- const LocationContext *getLocationContext() const {
- return Impl.second;
- }
+ const ConstructionContextItem &getItem() const { return Impl.first; }
+ const LocationContext *getLocationContext() const { return Impl.second; }
void print(llvm::raw_ostream &OS, PrinterHelper *Helper, PrintingPolicy &PP) {
- OS << '(' << getLocationContext() << ',' << getAnyASTNodePtr() << ") ";
- if (const Stmt *S = getStmt()) {
+ OS << '(' << getLocationContext() << ',' << getAnyASTNodePtr() << ','
+ << getItem().getKindAsString();
+ if (getItem().getKind() == ConstructionContextItem::ArgumentKind)
+ OS << " #" << getItem().getIndex();
+ OS << ") ";
+ if (const Stmt *S = getItem().getStmtOrNull()) {
S->printPretty(OS, Helper, PP);
} else {
- const CXXCtorInitializer *I = getCXXCtorInitializer();
+ const CXXCtorInitializer *I = getItem().getCXXCtorInitializer();
OS << I->getAnyMember()->getNameAsString();
}
}
void Profile(llvm::FoldingSetNodeID &ID) const {
- ID.AddPointer(Impl.first.getOpaqueValue());
+ ID.Add(Impl.first);
ID.AddPointer(Impl.second);
}
@@ -184,15 +170,6 @@ typedef llvm::ImmutableMap<ConstructedObjectKey, SVal>
REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction,
ObjectsUnderConstructionMap)
-// Additionally, track a set of destructors that correspond to elided
-// constructors when copy elision occurs.
-typedef std::pair<const CXXBindTemporaryExpr *, const LocationContext *>
- ElidedDestructorItem;
-typedef llvm::ImmutableSet<ElidedDestructorItem>
- ElidedDestructorSet;
-REGISTER_TRAIT_WITH_PROGRAMSTATE(ElidedDestructors,
- ElidedDestructorSet)
-
//===----------------------------------------------------------------------===//
// Engine construction and deletion.
//===----------------------------------------------------------------------===//
@@ -449,31 +426,32 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State,
return State;
}
-ProgramStateRef ExprEngine::addObjectUnderConstruction(
- ProgramStateRef State,
- llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P,
- const LocationContext *LC, SVal V) {
- ConstructedObjectKey Key(P, LC->getStackFrame());
+ProgramStateRef
+ExprEngine::addObjectUnderConstruction(ProgramStateRef State,
+ const ConstructionContextItem &Item,
+ const LocationContext *LC, SVal V) {
+ ConstructedObjectKey Key(Item, LC->getStackFrame());
// FIXME: Currently the state might already contain the marker due to
// incorrect handling of temporaries bound to default parameters.
assert(!State->get<ObjectsUnderConstruction>(Key) ||
- isa<CXXBindTemporaryExpr>(Key.getStmt()));
+ Key.getItem().getKind() ==
+ ConstructionContextItem::TemporaryDestructorKind);
return State->set<ObjectsUnderConstruction>(Key, V);
}
-Optional<SVal> ExprEngine::getObjectUnderConstruction(
- ProgramStateRef State,
- llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P,
- const LocationContext *LC) {
- ConstructedObjectKey Key(P, LC->getStackFrame());
+Optional<SVal>
+ExprEngine::getObjectUnderConstruction(ProgramStateRef State,
+ const ConstructionContextItem &Item,
+ const LocationContext *LC) {
+ ConstructedObjectKey Key(Item, LC->getStackFrame());
return Optional<SVal>::create(State->get<ObjectsUnderConstruction>(Key));
}
-ProgramStateRef ExprEngine::finishObjectConstruction(
- ProgramStateRef State,
- llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P,
- const LocationContext *LC) {
- ConstructedObjectKey Key(P, LC->getStackFrame());
+ProgramStateRef
+ExprEngine::finishObjectConstruction(ProgramStateRef State,
+ const ConstructionContextItem &Item,
+ const LocationContext *LC) {
+ ConstructedObjectKey Key(Item, LC->getStackFrame());
assert(State->contains<ObjectsUnderConstruction>(Key));
return State->remove<ObjectsUnderConstruction>(Key);
}
@@ -481,25 +459,26 @@ ProgramStateRef ExprEngine::finishObjectConstruction(
ProgramStateRef ExprEngine::elideDestructor(ProgramStateRef State,
const CXXBindTemporaryExpr *BTE,
const LocationContext *LC) {
- ElidedDestructorItem I(BTE, LC);
- assert(!State->contains<ElidedDestructors>(I));
- return State->add<ElidedDestructors>(I);
+ ConstructedObjectKey Key({BTE, /*IsElided=*/true}, LC);
+ // FIXME: Currently the state might already contain the marker due to
+ // incorrect handling of temporaries bound to default parameters.
+ return State->set<ObjectsUnderConstruction>(Key, UnknownVal());
}
ProgramStateRef
ExprEngine::cleanupElidedDestructor(ProgramStateRef State,
const CXXBindTemporaryExpr *BTE,
const LocationContext *LC) {
- ElidedDestructorItem I(BTE, LC);
- assert(State->contains<ElidedDestructors>(I));
- return State->remove<ElidedDestructors>(I);
+ ConstructedObjectKey Key({BTE, /*IsElided=*/true}, LC);
+ assert(State->contains<ObjectsUnderConstruction>(Key));
+ return State->remove<ObjectsUnderConstruction>(Key);
}
bool ExprEngine::isDestructorElided(ProgramStateRef State,
const CXXBindTemporaryExpr *BTE,
const LocationContext *LC) {
- ElidedDestructorItem I(BTE, LC);
- return State->contains<ElidedDestructors>(I);
+ ConstructedObjectKey Key({BTE, /*IsElided=*/true}, LC);
+ return State->contains<ObjectsUnderConstruction>(Key);
}
bool ExprEngine::areAllObjectsFullyConstructed(ProgramStateRef State,
@@ -512,10 +491,6 @@ bool ExprEngine::areAllObjectsFullyConstructed(ProgramStateRef State,
if (I.first.getLocationContext() == LC)
return false;
- for (auto I: State->get<ElidedDestructors>())
- if (I.second == LC)
- return false;
-
LC = LC->getParent();
}
return true;
@@ -560,14 +535,6 @@ static void printObjectsUnderConstructionForContext(raw_ostream &Out,
Key.print(Out, nullptr, PP);
Out << " : " << Value << NL;
}
-
- for (auto I : State->get<ElidedDestructors>()) {
- if (I.second != LC)
- continue;
- Out << '(' << I.second << ',' << (const void *)I.first << ") ";
- I.first->printPretty(Out, nullptr, PP);
- Out << " : (constructor elided)" << NL;
- }
}
void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State,
@@ -2252,25 +2219,14 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
for (auto I : State->get<ObjectsUnderConstruction>())
if (I.first.getLocationContext() == LC) {
// The comment above only pardons us for not cleaning up a
- // CXXBindTemporaryExpr. If any other statements are found here,
+ // temporary destructor. If any other statements are found here,
// it must be a separate problem.
- assert(isa<CXXBindTemporaryExpr>(I.first.getStmt()));
+ assert(I.first.getItem().getKind() ==
+ ConstructionContextItem::TemporaryDestructorKind ||
+ I.first.getItem().getKind() ==
+ ConstructionContextItem::ElidedDestructorKind);
State = State->remove<ObjectsUnderConstruction>(I.first);
- // Also cleanup the elided destructor info.
- ElidedDestructorItem Item(
- cast<CXXBindTemporaryExpr>(I.first.getStmt()),
- I.first.getLocationContext());
- State = State->remove<ElidedDestructors>(Item);
}
-
- // Also suppress the assertion for elided destructors when temporary
- // destructors are not provided at all by the CFG, because there's no
- // good place to clean them up.
- if (!AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG())
- for (auto I : State->get<ElidedDestructors>())
- if (I.second == LC)
- State = State->remove<ElidedDestructors>(I);
-
LC = LC->getParent();
}
if (State != Pred->getState()) {
@@ -2338,7 +2294,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
// Evaluate the LHS of the case value.
llvm::APSInt V1 = Case->getLHS()->EvaluateKnownConstInt(getContext());
assert(V1.getBitWidth() == getContext().getIntWidth(CondE->getType()));
-
+
// Get the RHS of the case, if it exists.
llvm::APSInt V2;
if (const Expr *E = Case->getRHS())
@@ -2538,12 +2494,12 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
ExplodedNodeSet CheckedSet;
getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, M, *this);
- ExplodedNodeSet EvalSet;
- ValueDecl *Member = M->getMemberDecl();
+ ExplodedNodeSet EvalSet;
+ ValueDecl *Member = M->getMemberDecl();
// Handle static member variables and enum constants accessed via
// member syntax.
- if (isa<VarDecl>(Member) || isa<EnumConstantDecl>(Member)) {
+ if (isa<VarDecl>(Member) || isa<EnumConstantDecl>(Member)) {
for (const auto I : CheckedSet)
VisitCommonDeclRefExpr(M, Member, I, EvalSet);
} else {
diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index c7b1a9ac82f0b..61b7a290e42a9 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -684,7 +684,7 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred,
// known to be false, 1 if the value is known to be true and a new symbol
// when the assumption is unknown.
nonloc::ConcreteInt Zero(getBasicVals().getValue(0, B->getType()));
- X = evalBinOp(N->getState(), BO_NE,
+ X = evalBinOp(N->getState(), BO_NE,
svalBuilder.evalCast(RHSVal, B->getType(), RHS->getType()),
Zero, B->getType());
}
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index dc124fc3ff2d2..4f1766a813c6d 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -221,25 +221,42 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
// and construct directly into the final object. This call
// also sets the CallOpts flags for us.
SVal V;
+ // If the elided copy/move constructor is not supported, there's still
+ // benefit in trying to model the non-elided constructor.
+ // Stash our state before trying to elide, as it'll get overwritten.
+ ProgramStateRef PreElideState = State;
+ EvalCallOptions PreElideCallOpts = CallOpts;
+
std::tie(State, V) = prepareForObjectConstruction(
CE, State, LCtx, TCC->getConstructionContextAfterElision(), CallOpts);
- // Remember that we've elided the constructor.
- State = addObjectUnderConstruction(State, CE, LCtx, V);
+ // FIXME: This definition of "copy elision has not failed" is unreliable.
+ // It doesn't indicate that the constructor will actually be inlined
+ // later; it is still up to evalCall() to decide.
+ if (!CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion) {
+ // Remember that we've elided the constructor.
+ State = addObjectUnderConstruction(State, CE, LCtx, V);
- // Remember that we've elided the destructor.
- if (BTE)
- State = elideDestructor(State, BTE, LCtx);
+ // Remember that we've elided the destructor.
+ if (BTE)
+ State = elideDestructor(State, BTE, LCtx);
- // Instead of materialization, shamelessly return
- // the final object destination.
- if (MTE)
- State = addObjectUnderConstruction(State, MTE, LCtx, V);
+ // Instead of materialization, shamelessly return
+ // the final object destination.
+ if (MTE)
+ State = addObjectUnderConstruction(State, MTE, LCtx, V);
- return std::make_pair(State, V);
+ return std::make_pair(State, V);
+ } else {
+ // Copy elision failed. Revert the changes and proceed as if we have
+ // a simple temporary.
+ State = PreElideState;
+ CallOpts = PreElideCallOpts;
+ }
+ LLVM_FALLTHROUGH;
}
case ConstructionContext::SimpleTemporaryObjectKind: {
- const auto *TCC = cast<SimpleTemporaryObjectConstructionContext>(CC);
+ const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);
const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();
const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr();
SVal V = UnknownVal();
@@ -274,6 +291,10 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
CallOpts.IsTemporaryCtorOrDtor = true;
return std::make_pair(State, V);
}
+ case ConstructionContext::ArgumentKind: {
+ // Function argument constructors. Not implemented yet.
+ break;
+ }
}
}
// If we couldn't find an existing region to construct into, assume we're
diff --git a/lib/StaticAnalyzer/Core/IssueHash.cpp b/lib/StaticAnalyzer/Core/IssueHash.cpp
index 274ebe7a941bc..6c55c61dd399a 100644
--- a/lib/StaticAnalyzer/Core/IssueHash.cpp
+++ b/lib/StaticAnalyzer/Core/IssueHash.cpp
@@ -26,7 +26,7 @@
using namespace clang;
-// Get a string representation of the parts of the signature that can be
+// Get a string representation of the parts of the signature that can be
// overloaded on.
static std::string GetSignature(const FunctionDecl *Target) {
if (!Target)
diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp
index 2b401607293b7..94e2e00d8bbc5 100644
--- a/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/Analysis/CFG.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
@@ -494,6 +495,10 @@ void ProgramState::dumpTaint() const {
printTaint(llvm::errs());
}
+AnalysisManager& ProgramState::getAnalysisManager() const {
+ return stateMgr->getOwningEngine()->getAnalysisManager();
+}
+
//===----------------------------------------------------------------------===//
// Generic Data Map.
//===----------------------------------------------------------------------===//
diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index beae0dfae2896..62c54fc956a96 100644
--- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -440,7 +440,7 @@ static bool shouldRearrange(ProgramStateRef State, BinaryOperator::Opcode Op,
SymbolRef Sym, llvm::APSInt Int, QualType Ty) {
return Sym->getType() == Ty &&
(!BinaryOperator::isComparisonOp(Op) ||
- (isWithinConstantOverflowBounds(Sym, State) &&
+ (isWithinConstantOverflowBounds(Sym, State) &&
isWithinConstantOverflowBounds(Int)));
}
@@ -1236,11 +1236,21 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) {
return Sym == Val.getAsSymbol();
}
+ SVal cache(SymbolRef Sym, SVal V) {
+ Cached[Sym] = V;
+ return V;
+ }
+
+ SVal skip(SymbolRef Sym) {
+ return cache(Sym, SVB.makeSymbolVal(Sym));
+ }
+
public:
Simplifier(ProgramStateRef State)
: State(State), SVB(State->getStateManager().getSValBuilder()) {}
SVal VisitSymbolData(const SymbolData *S) {
+ // No cache here.
if (const llvm::APSInt *I =
SVB.getKnownValue(State, SVB.makeSymbolVal(S)))
return Loc::isLocType(S->getType()) ? (SVal)SVB.makeIntLocVal(*I)
@@ -1257,11 +1267,9 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) {
return I->second;
SVal LHS = Visit(S->getLHS());
- if (isUnchanged(S->getLHS(), LHS)) {
- SVal V = SVB.makeSymbolVal(S);
- Cached[S] = V;
- return V;
- }
+ if (isUnchanged(S->getLHS(), LHS))
+ return skip(S);
+
SVal RHS;
// By looking at the APSInt in the right-hand side of S, we cannot
// figure out if it should be treated as a Loc or as a NonLoc.
@@ -1281,9 +1289,8 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) {
RHS = SVB.makeIntVal(S->getRHS());
}
- SVal V = SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType());
- Cached[S] = V;
- return V;
+ return cache(
+ S, SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()));
}
SVal VisitSymSymExpr(const SymSymExpr *S) {
@@ -1291,16 +1298,21 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) {
if (I != Cached.end())
return I->second;
+ // For now don't try to simplify mixed Loc/NonLoc expressions
+ // because they often appear from LocAsInteger operations
+ // and we don't know how to combine a LocAsInteger
+ // with a concrete value.
+ if (Loc::isLocType(S->getLHS()->getType()) !=
+ Loc::isLocType(S->getRHS()->getType()))
+ return skip(S);
+
SVal LHS = Visit(S->getLHS());
SVal RHS = Visit(S->getRHS());
- if (isUnchanged(S->getLHS(), LHS) && isUnchanged(S->getRHS(), RHS)) {
- SVal V = SVB.makeSymbolVal(S);
- Cached[S] = V;
- return V;
- }
- SVal V = SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType());
- Cached[S] = V;
- return V;
+ if (isUnchanged(S->getLHS(), LHS) && isUnchanged(S->getRHS(), RHS))
+ return skip(S);
+
+ return cache(
+ S, SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()));
}
SVal VisitSymExpr(SymbolRef S) { return nonloc::SymbolVal(S); }
diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp
index 5ab5c082269ba..94188a9ef698f 100644
--- a/lib/StaticAnalyzer/Core/Store.cpp
+++ b/lib/StaticAnalyzer/Core/Store.cpp
@@ -448,10 +448,10 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
// value. See also the similar FIXME in getLValueFieldOrIvar().
if (Base.isUnknownOrUndef() || Base.getAs<loc::ConcreteInt>())
return Base;
-
+
if (Base.getAs<loc::GotoLabel>())
return UnknownVal();
-
+
const SubRegion *BaseRegion =
Base.castAs<loc::MemRegionVal>().getRegionAs<SubRegion>();