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