diff options
author | Anna Zaks <ganna@apple.com> | 2012-03-16 23:24:20 +0000 |
---|---|---|
committer | Anna Zaks <ganna@apple.com> | 2012-03-16 23:24:20 +0000 |
commit | 56a938ff85a444eb3d30d2634d92ce5b1f6fae56 (patch) | |
tree | 9d85719ed51903e12e39c5f7d4db93f727f37763 /lib/StaticAnalyzer/Core | |
parent | 076add680e281709cf081052be0dcb822dc8f37d (diff) |
[analyzer] Create symbol-aware stack hints (building upon r152837).
The symbol-aware stack hint combines the checker-provided message
with the information about how the symbol was passed to the callee: as
a parameter or a return value.
For malloc, the generated messages look like this :
"Returning from 'foo'; released memory via 1st parameter"
"Returning from 'foo'; allocated memory via 1st parameter"
"Returning from 'foo'; allocated memory returned"
"Returning from 'foo'; reallocation of 1st parameter failed"
(We are yet to handle cases when the symbol is a field in a struct or
an array element.)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@152962 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/StaticAnalyzer/Core')
-rw-r--r-- | lib/StaticAnalyzer/Core/BugReporter.cpp | 33 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/PathDiagnostic.cpp | 91 |
2 files changed, 111 insertions, 13 deletions
diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index b59405bcd2..363a6dffee 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -380,22 +380,29 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { //===----------------------------------------------------------------------===// // "Minimal" path diagnostic generation algorithm. //===----------------------------------------------------------------------===// +typedef std::pair<PathDiagnosticCallPiece*, const ExplodedNode*> StackDiagPair; +typedef SmallVector<StackDiagPair, 6> StackDiagVector; + static void updateStackPiecesWithMessage(PathDiagnosticPiece *P, - llvm::SmallVector<PathDiagnosticCallPiece*, 6> &CallStack) { + StackDiagVector &CallStack) { // If the piece contains a special message, add it to all the call // pieces on the active stack. if (PathDiagnosticEventPiece *ep = dyn_cast<PathDiagnosticEventPiece>(P)) { - StringRef stackMsg = ep->getCallStackMessage(); - if (!stackMsg.empty()) - for (llvm::SmallVector<PathDiagnosticCallPiece*, 6>::iterator - I = CallStack.begin(), E = CallStack.end(); I != E; ++I) + if (ep->hasCallStackHint()) + for (StackDiagVector::iterator I = CallStack.begin(), + E = CallStack.end(); I != E; ++I) { + PathDiagnosticCallPiece *CP = I->first; + const ExplodedNode *N = I->second; + StringRef stackMsg = ep->getCallStackMessage(N); + // The last message on the path to final bug is the most important // one. Since we traverse the path backwards, do not add the message // if one has been previously added. - if (!(*I)->hasCallStackMessage()) - (*I)->setCallStackMessage(stackMsg); + if (!CP->hasCallStackMessage()) + CP->setCallStackMessage(stackMsg); + } } } @@ -410,7 +417,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin()); - llvm::SmallVector<PathDiagnosticCallPiece*, 6> CallStack; + StackDiagVector CallStack; while (NextNode) { N = NextNode; @@ -424,7 +431,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, PathDiagnosticCallPiece::construct(N, *CE, SMgr); PD.getActivePath().push_front(C); PD.pushActivePath(&C->path); - CallStack.push_back(C); + CallStack.push_back(StackDiagPair(C, N)); continue; } @@ -446,7 +453,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, } C->setCallee(*CE, SMgr); if (!CallStack.empty()) { - assert(CallStack.back() == C); + assert(CallStack.back().first == C); CallStack.pop_back(); } continue; @@ -1047,7 +1054,7 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, const ExplodedNode *N) { EdgeBuilder EB(PD, PDB); const SourceManager& SM = PDB.getSourceManager(); - llvm::SmallVector<PathDiagnosticCallPiece*, 6> CallStack; + StackDiagVector CallStack; const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin()); while (NextNode) { @@ -1068,7 +1075,7 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PathDiagnosticCallPiece::construct(N, *CE, SM); PD.getActivePath().push_front(C); PD.pushActivePath(&C->path); - CallStack.push_back(C); + CallStack.push_back(StackDiagPair(C, N)); break; } @@ -1104,7 +1111,7 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, EB.addContext(CE->getCallExpr()); if (!CallStack.empty()) { - assert(CallStack.back() == C); + assert(CallStack.back().first == C); CallStack.pop_back(); } break; diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index cd08f9ceb7..2a2b9c615e 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -659,3 +659,94 @@ void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const { for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I) ID.AddString(*I); } + +StackHintGenerator::~StackHintGenerator() {} + +std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ + ProgramPoint P = N->getLocation(); + const CallExit *CExit = dyn_cast<CallExit>(&P); + assert(CExit && "Stack Hints should be constructed at CallExit points."); + + const CallExpr *CE = dyn_cast_or_null<CallExpr>(CExit->getStmt()); + if (!CE) + return ""; + + // Get the successor node to make sure the return statement is evaluated and + // CE is set to the result value. + N = *N->succ_begin(); + if (!N) + return getMessageForSymbolNotFound(); + + // Check if one of the parameters are set to the interesting symbol. + ProgramStateRef State = N->getState(); + const LocationContext *LCtx = N->getLocationContext(); + unsigned ArgIndex = 0; + for (CallExpr::const_arg_iterator I = CE->arg_begin(), + E = CE->arg_end(); I != E; ++I, ++ArgIndex){ + SVal SV = State->getSVal(*I, LCtx); + + // Check if the variable corresponding to the symbol is passed by value. + SymbolRef AS = SV.getAsLocSymbol(); + if (AS == Sym) { + return getMessageForArg(*I, ArgIndex); + } + + // Check if the parameter is a pointer to the symbol. + if (const loc::MemRegionVal *Reg = dyn_cast<loc::MemRegionVal>(&SV)) { + SVal PSV = State->getSVal(Reg->getRegion()); + SymbolRef AS = PSV.getAsLocSymbol(); + if (AS == Sym) { + return getMessageForArg(*I, ArgIndex); + } + } + } + + // Check if we are returning the interesting symbol. + SVal SV = State->getSVal(CE, LCtx); + SymbolRef RetSym = SV.getAsLocSymbol(); + if (RetSym == Sym) { + return getMessageForReturn(CE); + } + + return getMessageForSymbolNotFound(); +} + +/// TODO: This is copied from clang diagnostics. Maybe we could just move it to +/// some common place. (Same as HandleOrdinalModifier.) +void StackHintGeneratorForSymbol::printOrdinal(unsigned ValNo, + llvm::raw_svector_ostream &Out) { + assert(ValNo != 0 && "ValNo must be strictly positive!"); + + // We could use text forms for the first N ordinals, but the numeric + // forms are actually nicer in diagnostics because they stand out. + Out << ValNo; + + // It is critically important that we do this perfectly for + // user-written sequences with over 100 elements. + switch (ValNo % 100) { + case 11: + case 12: + case 13: + Out << "th"; return; + default: + switch (ValNo % 10) { + case 1: Out << "st"; return; + case 2: Out << "nd"; return; + case 3: Out << "rd"; return; + default: Out << "th"; return; + } + } +} + +std::string StackHintGeneratorForSymbol::getMessageForArg(const Expr *ArgE, + unsigned ArgIndex) { + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + + os << Msg << " via "; + // Printed parameters start at 1, not 0. + printOrdinal(++ArgIndex, os); + os << " parameter"; + + return os.str(); +} |