aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Core/ExprEngine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer/Core/ExprEngine.cpp')
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp282
1 files changed, 168 insertions, 114 deletions
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 151eef56fece..1fef5b3c1edd 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1,9 +1,8 @@
//===- ExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ----------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -34,6 +33,7 @@
#include "clang/Analysis/ConstructionContext.h"
#include "clang/Analysis/ProgramPoint.h"
#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/JsonSupport.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/PrettyStackTrace.h"
@@ -142,21 +142,34 @@ public:
return getLocationContext()->getDecl()->getASTContext();
}
- void print(llvm::raw_ostream &OS, PrinterHelper *Helper, PrintingPolicy &PP) {
- OS << "(LC" << getLocationContext()->getID() << ',';
- if (const Stmt *S = getItem().getStmtOrNull())
- OS << 'S' << S->getID(getASTContext());
+ void printJson(llvm::raw_ostream &Out, PrinterHelper *Helper,
+ PrintingPolicy &PP) const {
+ const Stmt *S = getItem().getStmtOrNull();
+ const CXXCtorInitializer *I = nullptr;
+ if (!S)
+ I = getItem().getCXXCtorInitializer();
+
+ if (S)
+ Out << "\"stmt_id\": " << S->getID(getASTContext());
else
- OS << 'I' << getItem().getCXXCtorInitializer()->getID(getASTContext());
- OS << ',' << getItem().getKindAsString();
+ Out << "\"init_id\": " << I->getID(getASTContext());
+
+ // Kind
+ Out << ", \"kind\": \"" << getItem().getKindAsString()
+ << "\", \"argument_index\": ";
+
if (getItem().getKind() == ConstructionContextItem::ArgumentKind)
- OS << " #" << getItem().getIndex();
- OS << ") ";
- if (const Stmt *S = getItem().getStmtOrNull()) {
- S->printPretty(OS, Helper, PP);
+ Out << getItem().getIndex();
+ else
+ Out << "null";
+
+ // Pretty-print
+ Out << ", \"pretty\": ";
+
+ if (S) {
+ S->printJson(Out, Helper, PP, /*AddQuotes=*/true);
} else {
- const CXXCtorInitializer *I = getItem().getCXXCtorInitializer();
- OS << I->getAnyMember()->getNameAsString();
+ Out << '\"' << I->getAnyMember()->getNameAsString() << '\"';
}
}
@@ -198,9 +211,13 @@ ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU,
mgr.getConstraintManagerCreator(), G.getAllocator(),
this),
SymMgr(StateMgr.getSymbolManager()),
- svalBuilder(StateMgr.getSValBuilder()), ObjCNoRet(mgr.getASTContext()),
+ MRMgr(StateMgr.getRegionManager()),
+ svalBuilder(StateMgr.getSValBuilder()),
+ ObjCNoRet(mgr.getASTContext()),
BR(mgr, *this),
- VisitedCallees(VisitedCalleesIn), HowToInline(HowToInlineIn) {
+ VisitedCallees(VisitedCalleesIn),
+ HowToInline(HowToInlineIn)
+ {
unsigned TrimInterval = mgr.options.GraphTrimInterval;
if (TrimInterval != 0) {
// Enable eager node reclamation when constructing the ExplodedGraph.
@@ -208,10 +225,6 @@ ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU,
}
}
-ExprEngine::~ExprEngine() {
- BR.FlushReports();
-}
-
//===----------------------------------------------------------------------===//
// Utility methods.
//===----------------------------------------------------------------------===//
@@ -538,36 +551,73 @@ ExprEngine::processRegionChanges(ProgramStateRef state,
LCtx, Call);
}
-static void printObjectsUnderConstructionForContext(raw_ostream &Out,
- ProgramStateRef State,
- const char *NL,
- const LocationContext *LC) {
+static void
+printObjectsUnderConstructionJson(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const LocationContext *LCtx,
+ unsigned int Space = 0, bool IsDot = false) {
PrintingPolicy PP =
- LC->getAnalysisDeclContext()->getASTContext().getPrintingPolicy();
- for (auto I : State->get<ObjectsUnderConstruction>()) {
- ConstructedObjectKey Key = I.first;
+ LCtx->getAnalysisDeclContext()->getASTContext().getPrintingPolicy();
+
+ ++Space;
+ bool HasItem = false;
+
+ // Store the last key.
+ const ConstructedObjectKey *LastKey = nullptr;
+ for (const auto &I : State->get<ObjectsUnderConstruction>()) {
+ const ConstructedObjectKey &Key = I.first;
+ if (Key.getLocationContext() != LCtx)
+ continue;
+
+ if (!HasItem) {
+ Out << "[" << NL;
+ HasItem = true;
+ }
+
+ LastKey = &Key;
+ }
+
+ for (const auto &I : State->get<ObjectsUnderConstruction>()) {
+ const ConstructedObjectKey &Key = I.first;
SVal Value = I.second;
- if (Key.getLocationContext() != LC)
+ if (Key.getLocationContext() != LCtx)
continue;
- Key.print(Out, nullptr, PP);
- Out << " : " << Value << NL;
+
+ Indent(Out, Space, IsDot) << "{ ";
+ Key.printJson(Out, nullptr, PP);
+ Out << ", \"value\": \"" << Value << "\" }";
+
+ if (&Key != LastKey)
+ Out << ',';
+ Out << NL;
+ }
+
+ if (HasItem)
+ Indent(Out, --Space, IsDot) << ']'; // End of "location_context".
+ else {
+ Out << "null ";
}
}
-void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State,
- const char *NL, const char *Sep,
- const LocationContext *LCtx) {
- if (LCtx) {
- if (!State->get<ObjectsUnderConstruction>().isEmpty()) {
- Out << Sep << "Objects under construction:" << NL;
+void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State,
+ const LocationContext *LCtx, const char *NL,
+ unsigned int Space, bool IsDot) const {
+ Indent(Out, Space, IsDot) << "\"constructing_objects\": ";
- LCtx->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) {
- printObjectsUnderConstructionForContext(Out, State, NL, LC);
- });
- }
+ if (LCtx && !State->get<ObjectsUnderConstruction>().isEmpty()) {
+ ++Space;
+ Out << '[' << NL;
+ LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) {
+ printObjectsUnderConstructionJson(Out, State, NL, LC, Space, IsDot);
+ });
+
+ --Space;
+ Indent(Out, Space, IsDot) << "]," << NL; // End of "constructing_objects".
+ } else {
+ Out << "null," << NL;
}
- getCheckerManager().runCheckersForPrintState(Out, State, NL, Sep);
+ getCheckerManager().runCheckersForPrintStateJson(Out, State, NL, Space,
+ IsDot);
}
void ExprEngine::processEndWorklist() {
@@ -1338,6 +1388,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::NoInitExprClass:
case Stmt::SizeOfPackExprClass:
case Stmt::StringLiteralClass:
+ case Stmt::SourceLocExprClass:
case Stmt::ObjCStringLiteralClass:
case Stmt::CXXPseudoDestructorExprClass:
case Stmt::SubstNonTypeTemplateParmExprClass:
@@ -1517,7 +1568,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
ProgramStateRef NewState =
createTemporaryRegionIfNeeded(State, LCtx, OCE->getArg(0));
if (NewState != State) {
- Pred = Bldr.generateNode(OCE, Pred, NewState, /*Tag=*/nullptr,
+ Pred = Bldr.generateNode(OCE, Pred, NewState, /*tag=*/nullptr,
ProgramPoint::PreStmtKind);
// Did we cache out?
if (!Pred)
@@ -1636,6 +1687,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::CXXReinterpretCastExprClass:
case Stmt::CXXConstCastExprClass:
case Stmt::CXXFunctionalCastExprClass:
+ case Stmt::BuiltinBitCastExprClass:
case Stmt::ObjCBridgedCastExprClass: {
Bldr.takeNodes(Pred);
const auto *C = cast<CastExpr>(S);
@@ -1858,7 +1910,7 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
// other constraints) then consider completely unrolling it.
if(AMgr.options.ShouldUnrollLoops) {
unsigned maxBlockVisitOnPath = AMgr.options.maxBlockVisitOnPath;
- const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator();
+ const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminatorStmt();
if (Term) {
ProgramStateRef NewState = updateLoopStack(Term, AMgr.getASTContext(),
Pred, maxBlockVisitOnPath);
@@ -1879,7 +1931,7 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
unsigned int BlockCount = nodeBuilder.getContext().blockCount();
if (BlockCount == AMgr.options.maxBlockVisitOnPath - 1 &&
AMgr.options.ShouldWidenLoops) {
- const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator();
+ const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminatorStmt();
if (!(Term &&
(isa<ForStmt>(Term) || isa<WhileStmt>(Term) || isa<DoStmt>(Term))))
return;
@@ -2004,8 +2056,8 @@ static const Stmt *ResolveCondition(const Stmt *Condition,
if (!BO || !BO->isLogicalOp())
return Condition;
- assert(!B->getTerminator().isTemporaryDtorsBranch() &&
- "Temporary destructor branches handled by processBindTemporary.");
+ assert(B->getTerminator().isStmtBranch() &&
+ "Other kinds of branches are handled separately!");
// For logical operations, we still have the case where some branches
// use the traditional "merge" approach and others sink the branch
@@ -2258,7 +2310,6 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
Pred->getStackFrame()->getParent()));
PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext());
- StateMgr.EndPath(Pred->getState());
ExplodedNodeSet Dst;
if (Pred->getLocationContext()->inTopFrame()) {
@@ -2620,43 +2671,39 @@ void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred,
getCheckerManager().runCheckersForPostStmt(Dst, AfterInvalidateSet, AE, *this);
}
-// A value escapes in three possible cases:
+// A value escapes in four possible cases:
// (1) We are binding to something that is not a memory region.
-// (2) We are binding to a MemrRegion that does not have stack storage.
-// (3) We are binding to a MemRegion with stack storage that the store
+// (2) We are binding to a MemRegion that does not have stack storage.
+// (3) We are binding to a top-level parameter region with a non-trivial
+// destructor. We won't see the destructor during analysis, but it's there.
+// (4) We are binding to a MemRegion with stack storage that the store
// does not understand.
-ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State,
- SVal Loc,
- SVal Val,
- const LocationContext *LCtx) {
- // Are we storing to something that causes the value to "escape"?
- bool escapes = true;
-
- // TODO: Move to StoreManager.
- if (Optional<loc::MemRegionVal> regionLoc = Loc.getAs<loc::MemRegionVal>()) {
- escapes = !regionLoc->getRegion()->hasStackStorage();
-
- if (!escapes) {
- // To test (3), generate a new state with the binding added. If it is
- // the same state, then it escapes (since the store cannot represent
- // the binding).
- // Do this only if we know that the store is not supposed to generate the
- // same state.
- SVal StoredVal = State->getSVal(regionLoc->getRegion());
- if (StoredVal != Val)
- escapes = (State == (State->bindLoc(*regionLoc, Val, LCtx)));
- }
- }
-
- // If our store can represent the binding and we aren't storing to something
- // that doesn't have local storage then just return and have the simulation
- // state continue as is.
- if (!escapes)
- return State;
+ProgramStateRef
+ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, SVal Loc,
+ SVal Val, const LocationContext *LCtx) {
+
+ // Cases (1) and (2).
+ const MemRegion *MR = Loc.getAsRegion();
+ if (!MR || !MR->hasStackStorage())
+ return escapeValue(State, Val, PSK_EscapeOnBind);
+
+ // Case (3).
+ if (const auto *VR = dyn_cast<VarRegion>(MR->getBaseRegion()))
+ if (VR->hasStackParametersStorage() && VR->getStackFrame()->inTopFrame())
+ if (const auto *RD = VR->getValueType()->getAsCXXRecordDecl())
+ if (!RD->hasTrivialDestructor())
+ return escapeValue(State, Val, PSK_EscapeOnBind);
+
+ // Case (4): in order to test that, generate a new state with the binding
+ // added. If it is the same state, then it escapes (since the store cannot
+ // represent the binding).
+ // Do this only if we know that the store is not supposed to generate the
+ // same state.
+ SVal StoredVal = State->getSVal(MR);
+ if (StoredVal != Val)
+ if (State == (State->bindLoc(loc::MemRegionVal(MR), Val, LCtx)))
+ return escapeValue(State, Val, PSK_EscapeOnBind);
- // Otherwise, find all symbols referenced by 'val' that we are tracking
- // and stop tracking them.
- State = escapeValue(State, Val, PSK_EscapeOnBind);
return State;
}
@@ -2959,7 +3006,8 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
for (const auto &EQ : EQClasses) {
for (const BugReport &Report : EQ) {
- if (Report.getErrorNode() == N)
+ if (Report.getErrorNode()->getState() == N->getState() &&
+ Report.getErrorNode()->getLocation() == N->getLocation())
return true;
}
}
@@ -2995,57 +3043,63 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
return false;
}
- static std::string getNodeAttributes(const ExplodedNode *N,
- ExplodedGraph *) {
- SmallVector<StringRef, 10> Out;
- auto Noop = [](const ExplodedNode*){};
- if (traverseHiddenNodes(N, Noop, Noop, &nodeHasBugReport)) {
- Out.push_back("style=filled");
- Out.push_back("fillcolor=red");
- }
-
- if (traverseHiddenNodes(N, Noop, Noop,
- [](const ExplodedNode *C) { return C->isSink(); }))
- Out.push_back("color=blue");
- return llvm::join(Out, ",");
- }
-
static bool isNodeHidden(const ExplodedNode *N) {
return N->isTrivial();
}
static std::string getNodeLabel(const ExplodedNode *N, ExplodedGraph *G){
- std::string sbuf;
- llvm::raw_string_ostream Out(sbuf);
+ std::string Buf;
+ llvm::raw_string_ostream Out(Buf);
+ const bool IsDot = true;
+ const unsigned int Space = 1;
ProgramStateRef State = N->getState();
+ auto Noop = [](const ExplodedNode*){};
+ bool HasReport = traverseHiddenNodes(
+ N, Noop, Noop, &nodeHasBugReport);
+ bool IsSink = traverseHiddenNodes(
+ N, Noop, Noop, [](const ExplodedNode *N) { return N->isSink(); });
+
+ Out << "{ \"node_id\": " << N->getID(G) << ", \"pointer\": \""
+ << (const void *)N << "\", \"state_id\": " << State->getID()
+ << ", \"has_report\": " << (HasReport ? "true" : "false")
+ << ", \"is_sink\": " << (IsSink ? "true" : "false")
+ << ",\\l";
+
+ Indent(Out, Space, IsDot) << "\"program_points\": [\\l";
+
// Dump program point for all the previously skipped nodes.
traverseHiddenNodes(
N,
[&](const ExplodedNode *OtherNode) {
- OtherNode->getLocation().print(/*CR=*/"\\l", Out);
+ Indent(Out, Space + 1, IsDot) << "{ ";
+ OtherNode->getLocation().printJson(Out, /*NL=*/"\\l");
+ Out << ", \"tag\": ";
if (const ProgramPointTag *Tag = OtherNode->getLocation().getTag())
- Out << "\\lTag:" << Tag->getTagDescription();
- if (N->isSink())
- Out << "\\lNode is sink\\l";
- if (nodeHasBugReport(N))
- Out << "\\lBug report attached\\l";
+ Out << '\"' << Tag->getTagDescription() << "\" }";
+ else
+ Out << "null }";
},
- [&](const ExplodedNode *) { Out << "\\l--------\\l"; },
+ // Adds a comma and a new-line between each program point.
+ [&](const ExplodedNode *) { Out << ",\\l"; },
[&](const ExplodedNode *) { return false; });
- Out << "\\l\\|";
-
- Out << "StateID: ST" << State->getID() << ", NodeID: N" << N->getID(G)
- << " <" << (const void *)N << ">\\|";
+ Out << "\\l"; // Adds a new-line to the last program point.
+ Indent(Out, Space, IsDot) << "],\\l";
bool SameAsAllPredecessors =
std::all_of(N->pred_begin(), N->pred_end(), [&](const ExplodedNode *P) {
return P->getState() == State;
});
- if (!SameAsAllPredecessors)
- State->printDOT(Out, N->getLocationContext());
+
+ if (!SameAsAllPredecessors) {
+ State->printDOT(Out, N->getLocationContext(), Space);
+ } else {
+ Indent(Out, Space, IsDot) << "\"program_state\": null";
+ }
+
+ Out << "\\l}\\l";
return Out.str();
}
};