• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===--- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //  This file defines the PathDiagnostic-related interfaces.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
15 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
16 #include "clang/Basic/SourceManager.h"
17 #include "clang/AST/Expr.h"
18 #include "clang/AST/Decl.h"
19 #include "clang/AST/DeclObjC.h"
20 #include "clang/AST/ParentMap.h"
21 #include "clang/AST/StmtCXX.h"
22 #include "llvm/ADT/SmallString.h"
23 
24 using namespace clang;
25 using namespace ento;
26 
containsEvent() const27 bool PathDiagnosticMacroPiece::containsEvent() const {
28   for (PathPieces::const_iterator I = subPieces.begin(), E = subPieces.end();
29        I!=E; ++I) {
30     if (isa<PathDiagnosticEventPiece>(*I))
31       return true;
32     if (PathDiagnosticMacroPiece *MP = dyn_cast<PathDiagnosticMacroPiece>(*I))
33       if (MP->containsEvent())
34         return true;
35   }
36   return false;
37 }
38 
StripTrailingDots(StringRef s)39 static StringRef StripTrailingDots(StringRef s) {
40   for (StringRef::size_type i = s.size(); i != 0; --i)
41     if (s[i - 1] != '.')
42       return s.substr(0, i);
43   return "";
44 }
45 
PathDiagnosticPiece(StringRef s,Kind k,DisplayHint hint)46 PathDiagnosticPiece::PathDiagnosticPiece(StringRef s,
47                                          Kind k, DisplayHint hint)
48   : str(StripTrailingDots(s)), kind(k), Hint(hint) {}
49 
PathDiagnosticPiece(Kind k,DisplayHint hint)50 PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint)
51   : kind(k), Hint(hint) {}
52 
~PathDiagnosticPiece()53 PathDiagnosticPiece::~PathDiagnosticPiece() {}
~PathDiagnosticEventPiece()54 PathDiagnosticEventPiece::~PathDiagnosticEventPiece() {}
~PathDiagnosticCallPiece()55 PathDiagnosticCallPiece::~PathDiagnosticCallPiece() {}
~PathDiagnosticControlFlowPiece()56 PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {}
~PathDiagnosticMacroPiece()57 PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {}
58 
59 
~PathPieces()60 PathPieces::~PathPieces() {}
~PathDiagnostic()61 PathDiagnostic::~PathDiagnostic() {}
62 
PathDiagnostic(const Decl * declWithIssue,StringRef bugtype,StringRef desc,StringRef category)63 PathDiagnostic::PathDiagnostic(const Decl *declWithIssue,
64                                StringRef bugtype, StringRef desc,
65                                StringRef category)
66   : DeclWithIssue(declWithIssue),
67     BugType(StripTrailingDots(bugtype)),
68     Desc(StripTrailingDots(desc)),
69     Category(StripTrailingDots(category)),
70     path(pathImpl) {}
71 
anchor()72 void PathDiagnosticConsumer::anchor() { }
73 
~PathDiagnosticConsumer()74 PathDiagnosticConsumer::~PathDiagnosticConsumer() {
75   // Delete the contents of the FoldingSet if it isn't empty already.
76   for (llvm::FoldingSet<PathDiagnostic>::iterator it =
77        Diags.begin(), et = Diags.end() ; it != et ; ++it) {
78     delete &*it;
79   }
80 }
81 
HandlePathDiagnostic(PathDiagnostic * D)82 void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) {
83   llvm::OwningPtr<PathDiagnostic> OwningD(D);
84 
85   if (!D || D->path.empty())
86     return;
87 
88   // We need to flatten the locations (convert Stmt* to locations) because
89   // the referenced statements may be freed by the time the diagnostics
90   // are emitted.
91   D->flattenLocations();
92 
93   // If the PathDiagnosticConsumer does not support diagnostics that
94   // cross file boundaries, prune out such diagnostics now.
95   if (!supportsCrossFileDiagnostics()) {
96     // Verify that the entire path is from the same FileID.
97     FileID FID;
98     const SourceManager &SMgr = (*D->path.begin())->getLocation().getManager();
99     llvm::SmallVector<const PathPieces *, 5> WorkList;
100     WorkList.push_back(&D->path);
101 
102     while (!WorkList.empty()) {
103       const PathPieces &path = *WorkList.back();
104       WorkList.pop_back();
105 
106       for (PathPieces::const_iterator I = path.begin(), E = path.end();
107            I != E; ++I) {
108         const PathDiagnosticPiece *piece = I->getPtr();
109         FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc();
110 
111         if (FID.isInvalid()) {
112           FID = SMgr.getFileID(L);
113         } else if (SMgr.getFileID(L) != FID)
114           return; // FIXME: Emit a warning?
115 
116         // Check the source ranges.
117         for (PathDiagnosticPiece::range_iterator RI = piece->ranges_begin(),
118              RE = piece->ranges_end();
119              RI != RE; ++RI) {
120           SourceLocation L = SMgr.getExpansionLoc(RI->getBegin());
121           if (!L.isFileID() || SMgr.getFileID(L) != FID)
122             return; // FIXME: Emit a warning?
123           L = SMgr.getExpansionLoc(RI->getEnd());
124           if (!L.isFileID() || SMgr.getFileID(L) != FID)
125             return; // FIXME: Emit a warning?
126         }
127 
128         if (const PathDiagnosticCallPiece *call =
129             dyn_cast<PathDiagnosticCallPiece>(piece)) {
130           WorkList.push_back(&call->path);
131         }
132         else if (const PathDiagnosticMacroPiece *macro =
133                  dyn_cast<PathDiagnosticMacroPiece>(piece)) {
134           WorkList.push_back(&macro->subPieces);
135         }
136       }
137     }
138 
139     if (FID.isInvalid())
140       return; // FIXME: Emit a warning?
141   }
142 
143   // Profile the node to see if we already have something matching it
144   llvm::FoldingSetNodeID profile;
145   D->Profile(profile);
146   void *InsertPos = 0;
147 
148   if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) {
149     // Keep the PathDiagnostic with the shorter path.
150     const unsigned orig_size = orig->full_size();
151     const unsigned new_size = D->full_size();
152 
153     if (orig_size <= new_size) {
154       bool shouldKeepOriginal = true;
155       if (orig_size == new_size) {
156         // Here we break ties in a fairly arbitrary, but deterministic, way.
157         llvm::FoldingSetNodeID fullProfile, fullProfileOrig;
158         D->FullProfile(fullProfile);
159         orig->FullProfile(fullProfileOrig);
160         if (fullProfile.ComputeHash() < fullProfileOrig.ComputeHash())
161           shouldKeepOriginal = false;
162       }
163 
164       if (shouldKeepOriginal)
165         return;
166     }
167     Diags.RemoveNode(orig);
168     delete orig;
169   }
170 
171   Diags.InsertNode(OwningD.take());
172 }
173 
174 
175 namespace {
176 struct CompareDiagnostics {
177   // Compare if 'X' is "<" than 'Y'.
operator ()__anone89849870111::CompareDiagnostics178   bool operator()(const PathDiagnostic *X, const PathDiagnostic *Y) const {
179     // First compare by location
180     const FullSourceLoc &XLoc = X->getLocation().asLocation();
181     const FullSourceLoc &YLoc = Y->getLocation().asLocation();
182     if (XLoc < YLoc)
183       return true;
184     if (XLoc != YLoc)
185       return false;
186 
187     // Next, compare by bug type.
188     StringRef XBugType = X->getBugType();
189     StringRef YBugType = Y->getBugType();
190     if (XBugType < YBugType)
191       return true;
192     if (XBugType != YBugType)
193       return false;
194 
195     // Next, compare by bug description.
196     StringRef XDesc = X->getDescription();
197     StringRef YDesc = Y->getDescription();
198     if (XDesc < YDesc)
199       return true;
200     if (XDesc != YDesc)
201       return false;
202 
203     // FIXME: Further refine by comparing PathDiagnosticPieces?
204     return false;
205   }
206 };
207 }
208 
209 void
FlushDiagnostics(SmallVectorImpl<std::string> * Files)210 PathDiagnosticConsumer::FlushDiagnostics(SmallVectorImpl<std::string> *Files) {
211   if (flushed)
212     return;
213 
214   flushed = true;
215 
216   std::vector<const PathDiagnostic *> BatchDiags;
217   for (llvm::FoldingSet<PathDiagnostic>::iterator it = Diags.begin(),
218        et = Diags.end(); it != et; ++it) {
219     BatchDiags.push_back(&*it);
220   }
221 
222   // Clear out the FoldingSet.
223   Diags.clear();
224 
225   // Sort the diagnostics so that they are always emitted in a deterministic
226   // order.
227   if (!BatchDiags.empty())
228     std::sort(BatchDiags.begin(), BatchDiags.end(), CompareDiagnostics());
229 
230   FlushDiagnosticsImpl(BatchDiags, Files);
231 
232   // Delete the flushed diagnostics.
233   for (std::vector<const PathDiagnostic *>::iterator it = BatchDiags.begin(),
234        et = BatchDiags.end(); it != et; ++it) {
235     const PathDiagnostic *D = *it;
236     delete D;
237   }
238 }
239 
240 //===----------------------------------------------------------------------===//
241 // PathDiagnosticLocation methods.
242 //===----------------------------------------------------------------------===//
243 
getValidSourceLocation(const Stmt * S,LocationOrAnalysisDeclContext LAC)244 static SourceLocation getValidSourceLocation(const Stmt* S,
245                                              LocationOrAnalysisDeclContext LAC) {
246   SourceLocation L = S->getLocStart();
247   assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext should "
248                           "be passed to PathDiagnosticLocation upon creation.");
249 
250   // S might be a temporary statement that does not have a location in the
251   // source code, so find an enclosing statement and use it's location.
252   if (!L.isValid()) {
253 
254     ParentMap *PM = 0;
255     if (LAC.is<const LocationContext*>())
256       PM = &LAC.get<const LocationContext*>()->getParentMap();
257     else
258       PM = &LAC.get<AnalysisDeclContext*>()->getParentMap();
259 
260     while (!L.isValid()) {
261       S = PM->getParent(S);
262       L = S->getLocStart();
263     }
264   }
265 
266   return L;
267 }
268 
269 PathDiagnosticLocation
createBegin(const Decl * D,const SourceManager & SM)270   PathDiagnosticLocation::createBegin(const Decl *D,
271                                       const SourceManager &SM) {
272   return PathDiagnosticLocation(D->getLocStart(), SM, SingleLocK);
273 }
274 
275 PathDiagnosticLocation
createBegin(const Stmt * S,const SourceManager & SM,LocationOrAnalysisDeclContext LAC)276   PathDiagnosticLocation::createBegin(const Stmt *S,
277                                       const SourceManager &SM,
278                                       LocationOrAnalysisDeclContext LAC) {
279   return PathDiagnosticLocation(getValidSourceLocation(S, LAC),
280                                 SM, SingleLocK);
281 }
282 
283 PathDiagnosticLocation
createOperatorLoc(const BinaryOperator * BO,const SourceManager & SM)284   PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO,
285                                             const SourceManager &SM) {
286   return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK);
287 }
288 
289 PathDiagnosticLocation
createMemberLoc(const MemberExpr * ME,const SourceManager & SM)290   PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME,
291                                           const SourceManager &SM) {
292   return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK);
293 }
294 
295 PathDiagnosticLocation
createBeginBrace(const CompoundStmt * CS,const SourceManager & SM)296   PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS,
297                                            const SourceManager &SM) {
298   SourceLocation L = CS->getLBracLoc();
299   return PathDiagnosticLocation(L, SM, SingleLocK);
300 }
301 
302 PathDiagnosticLocation
createEndBrace(const CompoundStmt * CS,const SourceManager & SM)303   PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS,
304                                          const SourceManager &SM) {
305   SourceLocation L = CS->getRBracLoc();
306   return PathDiagnosticLocation(L, SM, SingleLocK);
307 }
308 
309 PathDiagnosticLocation
createDeclBegin(const LocationContext * LC,const SourceManager & SM)310   PathDiagnosticLocation::createDeclBegin(const LocationContext *LC,
311                                           const SourceManager &SM) {
312   // FIXME: Should handle CXXTryStmt if analyser starts supporting C++.
313   if (const CompoundStmt *CS =
314         dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody()))
315     if (!CS->body_empty()) {
316       SourceLocation Loc = (*CS->body_begin())->getLocStart();
317       return PathDiagnosticLocation(Loc, SM, SingleLocK);
318     }
319 
320   return PathDiagnosticLocation();
321 }
322 
323 PathDiagnosticLocation
createDeclEnd(const LocationContext * LC,const SourceManager & SM)324   PathDiagnosticLocation::createDeclEnd(const LocationContext *LC,
325                                         const SourceManager &SM) {
326   SourceLocation L = LC->getDecl()->getBodyRBrace();
327   return PathDiagnosticLocation(L, SM, SingleLocK);
328 }
329 
330 PathDiagnosticLocation
create(const ProgramPoint & P,const SourceManager & SMng)331   PathDiagnosticLocation::create(const ProgramPoint& P,
332                                  const SourceManager &SMng) {
333 
334   const Stmt* S = 0;
335   if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
336     const CFGBlock *BSrc = BE->getSrc();
337     S = BSrc->getTerminatorCondition();
338   }
339   else if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) {
340     S = PS->getStmt();
341   }
342 
343   return PathDiagnosticLocation(S, SMng, P.getLocationContext());
344 }
345 
346 PathDiagnosticLocation
createEndOfPath(const ExplodedNode * N,const SourceManager & SM)347   PathDiagnosticLocation::createEndOfPath(const ExplodedNode* N,
348                                           const SourceManager &SM) {
349   assert(N && "Cannot create a location with a null node.");
350 
351   const ExplodedNode *NI = N;
352 
353   while (NI) {
354     ProgramPoint P = NI->getLocation();
355     const LocationContext *LC = P.getLocationContext();
356     if (const StmtPoint *PS = dyn_cast<StmtPoint>(&P))
357       return PathDiagnosticLocation(PS->getStmt(), SM, LC);
358     else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
359       const Stmt *Term = BE->getSrc()->getTerminator();
360       if (Term) {
361         return PathDiagnosticLocation(Term, SM, LC);
362       }
363     }
364     NI = NI->succ_empty() ? 0 : *(NI->succ_begin());
365   }
366 
367   return createDeclEnd(N->getLocationContext(), SM);
368 }
369 
createSingleLocation(const PathDiagnosticLocation & PDL)370 PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation(
371                                            const PathDiagnosticLocation &PDL) {
372   FullSourceLoc L = PDL.asLocation();
373   return PathDiagnosticLocation(L, L.getManager(), SingleLocK);
374 }
375 
376 FullSourceLoc
genLocation(SourceLocation L,LocationOrAnalysisDeclContext LAC) const377   PathDiagnosticLocation::genLocation(SourceLocation L,
378                                       LocationOrAnalysisDeclContext LAC) const {
379   assert(isValid());
380   // Note that we want a 'switch' here so that the compiler can warn us in
381   // case we add more cases.
382   switch (K) {
383     case SingleLocK:
384     case RangeK:
385       break;
386     case StmtK:
387       // Defensive checking.
388       if (!S)
389         break;
390       return FullSourceLoc(getValidSourceLocation(S, LAC),
391                            const_cast<SourceManager&>(*SM));
392     case DeclK:
393       // Defensive checking.
394       if (!D)
395         break;
396       return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM));
397   }
398 
399   return FullSourceLoc(L, const_cast<SourceManager&>(*SM));
400 }
401 
402 PathDiagnosticRange
genRange(LocationOrAnalysisDeclContext LAC) const403   PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const {
404   assert(isValid());
405   // Note that we want a 'switch' here so that the compiler can warn us in
406   // case we add more cases.
407   switch (K) {
408     case SingleLocK:
409       return PathDiagnosticRange(SourceRange(Loc,Loc), true);
410     case RangeK:
411       break;
412     case StmtK: {
413       const Stmt *S = asStmt();
414       switch (S->getStmtClass()) {
415         default:
416           break;
417         case Stmt::DeclStmtClass: {
418           const DeclStmt *DS = cast<DeclStmt>(S);
419           if (DS->isSingleDecl()) {
420             // Should always be the case, but we'll be defensive.
421             return SourceRange(DS->getLocStart(),
422                                DS->getSingleDecl()->getLocation());
423           }
424           break;
425         }
426           // FIXME: Provide better range information for different
427           //  terminators.
428         case Stmt::IfStmtClass:
429         case Stmt::WhileStmtClass:
430         case Stmt::DoStmtClass:
431         case Stmt::ForStmtClass:
432         case Stmt::ChooseExprClass:
433         case Stmt::IndirectGotoStmtClass:
434         case Stmt::SwitchStmtClass:
435         case Stmt::BinaryConditionalOperatorClass:
436         case Stmt::ConditionalOperatorClass:
437         case Stmt::ObjCForCollectionStmtClass: {
438           SourceLocation L = getValidSourceLocation(S, LAC);
439           return SourceRange(L, L);
440         }
441       }
442       SourceRange R = S->getSourceRange();
443       if (R.isValid())
444         return R;
445       break;
446     }
447     case DeclK:
448       if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
449         return MD->getSourceRange();
450       if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
451         if (Stmt *Body = FD->getBody())
452           return Body->getSourceRange();
453       }
454       else {
455         SourceLocation L = D->getLocation();
456         return PathDiagnosticRange(SourceRange(L, L), true);
457       }
458   }
459 
460   return SourceRange(Loc,Loc);
461 }
462 
flatten()463 void PathDiagnosticLocation::flatten() {
464   if (K == StmtK) {
465     K = RangeK;
466     S = 0;
467     D = 0;
468   }
469   else if (K == DeclK) {
470     K = SingleLocK;
471     S = 0;
472     D = 0;
473   }
474 }
475 
getLocation() const476 PathDiagnosticLocation PathDiagnostic::getLocation() const {
477   assert(path.size() > 0 &&
478          "getLocation() requires a non-empty PathDiagnostic.");
479 
480   PathDiagnosticPiece *p = path.rbegin()->getPtr();
481 
482   while (true) {
483     if (PathDiagnosticCallPiece *cp = dyn_cast<PathDiagnosticCallPiece>(p)) {
484       assert(!cp->path.empty());
485       p = cp->path.rbegin()->getPtr();
486       continue;
487     }
488     break;
489   }
490 
491   return p->getLocation();
492 }
493 
494 //===----------------------------------------------------------------------===//
495 // Manipulation of PathDiagnosticCallPieces.
496 //===----------------------------------------------------------------------===//
497 
getLastStmtLoc(const ExplodedNode * N,const SourceManager & SM)498 static PathDiagnosticLocation getLastStmtLoc(const ExplodedNode *N,
499                                              const SourceManager &SM) {
500   while (N) {
501     ProgramPoint PP = N->getLocation();
502     if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP))
503       return PathDiagnosticLocation(SP->getStmt(), SM, PP.getLocationContext());
504     if (N->pred_empty())
505       break;
506     N = *N->pred_begin();
507   }
508   return PathDiagnosticLocation();
509 }
510 
511 PathDiagnosticCallPiece *
construct(const ExplodedNode * N,const CallExit & CE,const SourceManager & SM)512 PathDiagnosticCallPiece::construct(const ExplodedNode *N,
513                                    const CallExit &CE,
514                                    const SourceManager &SM) {
515   const Decl *caller = CE.getLocationContext()->getParent()->getDecl();
516   PathDiagnosticLocation pos = getLastStmtLoc(N, SM);
517   return new PathDiagnosticCallPiece(caller, pos);
518 }
519 
520 PathDiagnosticCallPiece *
construct(PathPieces & path,const Decl * caller)521 PathDiagnosticCallPiece::construct(PathPieces &path,
522                                    const Decl *caller) {
523   PathDiagnosticCallPiece *C = new PathDiagnosticCallPiece(path, caller);
524   path.clear();
525   path.push_front(C);
526   return C;
527 }
528 
setCallee(const CallEnter & CE,const SourceManager & SM)529 void PathDiagnosticCallPiece::setCallee(const CallEnter &CE,
530                                         const SourceManager &SM) {
531   const Decl *D = CE.getCalleeContext()->getDecl();
532   Callee = D;
533   callEnter = PathDiagnosticLocation(CE.getCallExpr(), SM,
534                                      CE.getLocationContext());
535   callEnterWithin = PathDiagnosticLocation::createBegin(D, SM);
536 }
537 
538 IntrusiveRefCntPtr<PathDiagnosticEventPiece>
getCallEnterEvent() const539 PathDiagnosticCallPiece::getCallEnterEvent() const {
540   if (!Callee)
541     return 0;
542   SmallString<256> buf;
543   llvm::raw_svector_ostream Out(buf);
544   if (isa<BlockDecl>(Callee))
545     Out << "Calling anonymous block";
546   else if (const NamedDecl *ND = dyn_cast<NamedDecl>(Callee))
547     Out << "Calling '" << *ND << "'";
548   StringRef msg = Out.str();
549   if (msg.empty())
550     return 0;
551   return new PathDiagnosticEventPiece(callEnter, msg);
552 }
553 
554 IntrusiveRefCntPtr<PathDiagnosticEventPiece>
getCallEnterWithinCallerEvent() const555 PathDiagnosticCallPiece::getCallEnterWithinCallerEvent() const {
556   SmallString<256> buf;
557   llvm::raw_svector_ostream Out(buf);
558   if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Caller))
559     Out << "Entered call from '" << *ND << "'";
560   else
561     Out << "Entered call";
562   StringRef msg = Out.str();
563   if (msg.empty())
564     return 0;
565   return new PathDiagnosticEventPiece(callEnterWithin, msg);
566 }
567 
568 IntrusiveRefCntPtr<PathDiagnosticEventPiece>
getCallExitEvent() const569 PathDiagnosticCallPiece::getCallExitEvent() const {
570   if (NoExit)
571     return 0;
572   SmallString<256> buf;
573   llvm::raw_svector_ostream Out(buf);
574   if (!CallStackMessage.empty())
575     Out << CallStackMessage;
576   else if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Callee))
577     Out << "Returning from '" << *ND << "'";
578   else
579     Out << "Returning to caller";
580   return new PathDiagnosticEventPiece(callReturn, Out.str());
581 }
582 
compute_path_size(const PathPieces & pieces,unsigned & size)583 static void compute_path_size(const PathPieces &pieces, unsigned &size) {
584   for (PathPieces::const_iterator it = pieces.begin(),
585                                   et = pieces.end(); it != et; ++it) {
586     const PathDiagnosticPiece *piece = it->getPtr();
587     if (const PathDiagnosticCallPiece *cp =
588         dyn_cast<PathDiagnosticCallPiece>(piece)) {
589       compute_path_size(cp->path, size);
590     }
591     else
592       ++size;
593   }
594 }
595 
full_size()596 unsigned PathDiagnostic::full_size() {
597   unsigned size = 0;
598   compute_path_size(path, size);
599   return size;
600 }
601 
602 //===----------------------------------------------------------------------===//
603 // FoldingSet profiling methods.
604 //===----------------------------------------------------------------------===//
605 
Profile(llvm::FoldingSetNodeID & ID) const606 void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const {
607   ID.AddInteger(Range.getBegin().getRawEncoding());
608   ID.AddInteger(Range.getEnd().getRawEncoding());
609   ID.AddInteger(Loc.getRawEncoding());
610   return;
611 }
612 
Profile(llvm::FoldingSetNodeID & ID) const613 void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const {
614   ID.AddInteger((unsigned) getKind());
615   ID.AddString(str);
616   // FIXME: Add profiling support for code hints.
617   ID.AddInteger((unsigned) getDisplayHint());
618   for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) {
619     ID.AddInteger(I->getBegin().getRawEncoding());
620     ID.AddInteger(I->getEnd().getRawEncoding());
621   }
622 }
623 
Profile(llvm::FoldingSetNodeID & ID) const624 void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const {
625   PathDiagnosticPiece::Profile(ID);
626   for (PathPieces::const_iterator it = path.begin(),
627        et = path.end(); it != et; ++it) {
628     ID.Add(**it);
629   }
630 }
631 
Profile(llvm::FoldingSetNodeID & ID) const632 void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const {
633   PathDiagnosticPiece::Profile(ID);
634   ID.Add(Pos);
635 }
636 
Profile(llvm::FoldingSetNodeID & ID) const637 void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const {
638   PathDiagnosticPiece::Profile(ID);
639   for (const_iterator I = begin(), E = end(); I != E; ++I)
640     ID.Add(*I);
641 }
642 
Profile(llvm::FoldingSetNodeID & ID) const643 void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const {
644   PathDiagnosticSpotPiece::Profile(ID);
645   for (PathPieces::const_iterator I = subPieces.begin(), E = subPieces.end();
646        I != E; ++I)
647     ID.Add(**I);
648 }
649 
Profile(llvm::FoldingSetNodeID & ID) const650 void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const {
651   if (!path.empty())
652     getLocation().Profile(ID);
653   ID.AddString(BugType);
654   ID.AddString(Desc);
655   ID.AddString(Category);
656 }
657 
FullProfile(llvm::FoldingSetNodeID & ID) const658 void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const {
659   Profile(ID);
660   for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I)
661     ID.Add(**I);
662   for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I)
663     ID.AddString(*I);
664 }
665 
~StackHintGenerator()666 StackHintGenerator::~StackHintGenerator() {}
667 
getMessage(const ExplodedNode * N)668 std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){
669   ProgramPoint P = N->getLocation();
670   const CallExit *CExit = dyn_cast<CallExit>(&P);
671   assert(CExit && "Stack Hints should be constructed at CallExit points.");
672 
673   const CallExpr *CE = dyn_cast_or_null<CallExpr>(CExit->getStmt());
674   if (!CE)
675     return "";
676 
677   // Get the successor node to make sure the return statement is evaluated and
678   // CE is set to the result value.
679   N = *N->succ_begin();
680   if (!N)
681     return getMessageForSymbolNotFound();
682 
683   // Check if one of the parameters are set to the interesting symbol.
684   ProgramStateRef State = N->getState();
685   const LocationContext *LCtx = N->getLocationContext();
686   unsigned ArgIndex = 0;
687   for (CallExpr::const_arg_iterator I = CE->arg_begin(),
688                                     E = CE->arg_end(); I != E; ++I, ++ArgIndex){
689     SVal SV = State->getSVal(*I, LCtx);
690 
691     // Check if the variable corresponding to the symbol is passed by value.
692     SymbolRef AS = SV.getAsLocSymbol();
693     if (AS == Sym) {
694       return getMessageForArg(*I, ArgIndex);
695     }
696 
697     // Check if the parameter is a pointer to the symbol.
698     if (const loc::MemRegionVal *Reg = dyn_cast<loc::MemRegionVal>(&SV)) {
699       SVal PSV = State->getSVal(Reg->getRegion());
700       SymbolRef AS = PSV.getAsLocSymbol();
701       if (AS == Sym) {
702         return getMessageForArg(*I, ArgIndex);
703       }
704     }
705   }
706 
707   // Check if we are returning the interesting symbol.
708   SVal SV = State->getSVal(CE, LCtx);
709   SymbolRef RetSym = SV.getAsLocSymbol();
710   if (RetSym == Sym) {
711     return getMessageForReturn(CE);
712   }
713 
714   return getMessageForSymbolNotFound();
715 }
716 
717 /// TODO: This is copied from clang diagnostics. Maybe we could just move it to
718 /// some common place. (Same as HandleOrdinalModifier.)
printOrdinal(unsigned ValNo,llvm::raw_svector_ostream & Out)719 void StackHintGeneratorForSymbol::printOrdinal(unsigned ValNo,
720                                                llvm::raw_svector_ostream &Out) {
721   assert(ValNo != 0 && "ValNo must be strictly positive!");
722 
723   // We could use text forms for the first N ordinals, but the numeric
724   // forms are actually nicer in diagnostics because they stand out.
725   Out << ValNo;
726 
727   // It is critically important that we do this perfectly for
728   // user-written sequences with over 100 elements.
729   switch (ValNo % 100) {
730   case 11:
731   case 12:
732   case 13:
733     Out << "th"; return;
734   default:
735     switch (ValNo % 10) {
736     case 1: Out << "st"; return;
737     case 2: Out << "nd"; return;
738     case 3: Out << "rd"; return;
739     default: Out << "th"; return;
740     }
741   }
742 }
743 
getMessageForArg(const Expr * ArgE,unsigned ArgIndex)744 std::string StackHintGeneratorForSymbol::getMessageForArg(const Expr *ArgE,
745                                                         unsigned ArgIndex) {
746   SmallString<200> buf;
747   llvm::raw_svector_ostream os(buf);
748 
749   os << Msg << " via ";
750   // Printed parameters start at 1, not 0.
751   printOrdinal(++ArgIndex, os);
752   os << " parameter";
753 
754   return os.str();
755 }
756