diff options
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp | 105 |
1 files changed, 91 insertions, 14 deletions
diff --git a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp index 31e9150cc15b..2d5cb60edf7d 100644 --- a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -18,25 +18,41 @@ using namespace clang; using namespace ento; namespace { -class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols> { +class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols, + check::EndAnalysis> { mutable std::unique_ptr<BugType> BT; + // These stats are per-analysis, not per-branch, hence they shouldn't + // stay inside the program state. + struct ReachedStat { + ExplodedNode *ExampleNode; + unsigned NumTimesReached; + }; + mutable llvm::DenseMap<const CallExpr *, ReachedStat> ReachedStats; + void analyzerEval(const CallExpr *CE, CheckerContext &C) const; void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const; void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const; + void analyzerNumTimesReached(const CallExpr *CE, CheckerContext &C) const; void analyzerCrash(const CallExpr *CE, CheckerContext &C) const; void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const; + void analyzerDump(const CallExpr *CE, CheckerContext &C) const; void analyzerExplain(const CallExpr *CE, CheckerContext &C) const; + void analyzerPrintState(const CallExpr *CE, CheckerContext &C) const; void analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const; typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *, CheckerContext &C) const; - void reportBug(llvm::StringRef Msg, CheckerContext &C) const; + ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C) const; + ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, + ExplodedNode *N) const; public: bool evalCall(const CallExpr *CE, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; + void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, + ExprEngine &Eng) const; }; } @@ -56,7 +72,12 @@ bool ExprInspectionChecker::evalCall(const CallExpr *CE, .Case("clang_analyzer_warnOnDeadSymbol", &ExprInspectionChecker::analyzerWarnOnDeadSymbol) .Case("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain) + .Case("clang_analyzer_dump", &ExprInspectionChecker::analyzerDump) .Case("clang_analyzer_getExtent", &ExprInspectionChecker::analyzerGetExtent) + .Case("clang_analyzer_printState", + &ExprInspectionChecker::analyzerPrintState) + .Case("clang_analyzer_numTimesReached", + &ExprInspectionChecker::analyzerNumTimesReached) .Default(nullptr); if (!Handler) @@ -98,16 +119,24 @@ static const char *getArgumentValueString(const CallExpr *CE, } } -void ExprInspectionChecker::reportBug(llvm::StringRef Msg, - CheckerContext &C) const { - if (!BT) - BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); - +ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, + CheckerContext &C) const { ExplodedNode *N = C.generateNonFatalErrorNode(); + reportBug(Msg, C.getBugReporter(), N); + return N; +} + +ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, + BugReporter &BR, + ExplodedNode *N) const { if (!N) - return; + return nullptr; + + if (!BT) + BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); - C.emitReport(llvm::make_unique<BugReport>(*BT, Msg, N)); + BR.emitReport(llvm::make_unique<BugReport>(*BT, Msg, N)); + return N; } void ExprInspectionChecker::analyzerEval(const CallExpr *CE, @@ -127,6 +156,15 @@ void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE, reportBug("REACHABLE", C); } +void ExprInspectionChecker::analyzerNumTimesReached(const CallExpr *CE, + CheckerContext &C) const { + ++ReachedStats[CE].NumTimesReached; + if (!ReachedStats[CE].ExampleNode) { + // Later, in checkEndAnalysis, we'd throw a report against it. + ReachedStats[CE].ExampleNode = C.generateNonFatalErrorNode(); + } +} + void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const { const LocationContext *LC = C.getPredecessor()->getLocationContext(); @@ -144,22 +182,43 @@ void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, void ExprInspectionChecker::analyzerExplain(const CallExpr *CE, CheckerContext &C) const { - if (CE->getNumArgs() == 0) + if (CE->getNumArgs() == 0) { reportBug("Missing argument for explaining", C); + return; + } SVal V = C.getSVal(CE->getArg(0)); SValExplainer Ex(C.getASTContext()); reportBug(Ex.Visit(V), C); } +void ExprInspectionChecker::analyzerDump(const CallExpr *CE, + CheckerContext &C) const { + if (CE->getNumArgs() == 0) { + reportBug("Missing argument for dumping", C); + return; + } + + SVal V = C.getSVal(CE->getArg(0)); + + llvm::SmallString<32> Str; + llvm::raw_svector_ostream OS(Str); + V.dumpToStream(OS); + reportBug(OS.str(), C); +} + void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const { - if (CE->getNumArgs() == 0) + if (CE->getNumArgs() == 0) { reportBug("Missing region for obtaining extent", C); + return; + } auto MR = dyn_cast_or_null<SubRegion>(C.getSVal(CE->getArg(0)).getAsRegion()); - if (!MR) + if (!MR) { reportBug("Obtaining extent of a non-region", C); + return; + } ProgramStateRef State = C.getState(); State = State->BindExpr(CE, C.getLocationContext(), @@ -167,6 +226,11 @@ void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE, C.addTransition(State); } +void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE, + CheckerContext &C) const { + C.getState()->dump(); +} + void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const { if (CE->getNumArgs() == 0) @@ -185,15 +249,28 @@ void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { ProgramStateRef State = C.getState(); const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>(); + ExplodedNode *N = C.getPredecessor(); for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) { SymbolRef Sym = *I; if (!SymReaper.isDead(Sym)) continue; - reportBug("SYMBOL DEAD", C); + // The non-fatal error node should be the same for all reports. + if (ExplodedNode *BugNode = reportBug("SYMBOL DEAD", C)) + N = BugNode; State = State->remove<MarkedSymbols>(Sym); } - C.addTransition(State); + C.addTransition(State, N); +} + +void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, + ExprEngine &Eng) const { + for (auto Item: ReachedStats) { + unsigned NumTimesReached = Item.second.NumTimesReached; + ExplodedNode *N = Item.second.ExampleNode; + + reportBug(std::to_string(NumTimesReached), BR, N); + } } void ExprInspectionChecker::analyzerCrash(const CallExpr *CE, |