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