PathDiagnostic.cpp revision 07189521a15d9c088216b943649cb9fe231cbb57
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() {}
58
59
60PathPieces::~PathPieces() {}
61PathDiagnostic::~PathDiagnostic() {}
62
63PathDiagnostic::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
72void PathDiagnosticConsumer::anchor() { }
73
74PathDiagnosticConsumer::~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
82void 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
175namespace {
176struct CompareDiagnostics {
177  // Compare if 'X' is "<" than 'Y'.
178  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
209void
210PathDiagnosticConsumer::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
244static 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
269PathDiagnosticLocation
270  PathDiagnosticLocation::createBegin(const Decl *D,
271                                      const SourceManager &SM) {
272  return PathDiagnosticLocation(D->getLocStart(), SM, SingleLocK);
273}
274
275PathDiagnosticLocation
276  PathDiagnosticLocation::createBegin(const Stmt *S,
277                                      const SourceManager &SM,
278                                      LocationOrAnalysisDeclContext LAC) {
279  return PathDiagnosticLocation(getValidSourceLocation(S, LAC),
280                                SM, SingleLocK);
281}
282
283PathDiagnosticLocation
284  PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO,
285                                            const SourceManager &SM) {
286  return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK);
287}
288
289PathDiagnosticLocation
290  PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME,
291                                          const SourceManager &SM) {
292  return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK);
293}
294
295PathDiagnosticLocation
296  PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS,
297                                           const SourceManager &SM) {
298  SourceLocation L = CS->getLBracLoc();
299  return PathDiagnosticLocation(L, SM, SingleLocK);
300}
301
302PathDiagnosticLocation
303  PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS,
304                                         const SourceManager &SM) {
305  SourceLocation L = CS->getRBracLoc();
306  return PathDiagnosticLocation(L, SM, SingleLocK);
307}
308
309PathDiagnosticLocation
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
323PathDiagnosticLocation
324  PathDiagnosticLocation::createDeclEnd(const LocationContext *LC,
325                                        const SourceManager &SM) {
326  SourceLocation L = LC->getDecl()->getBodyRBrace();
327  return PathDiagnosticLocation(L, SM, SingleLocK);
328}
329
330PathDiagnosticLocation
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
346PathDiagnosticLocation
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
370PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation(
371                                           const PathDiagnosticLocation &PDL) {
372  FullSourceLoc L = PDL.asLocation();
373  return PathDiagnosticLocation(L, L.getManager(), SingleLocK);
374}
375
376FullSourceLoc
377  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
402PathDiagnosticRange
403  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
463void 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
476PathDiagnosticLocation 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
498static 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
511PathDiagnosticCallPiece *
512PathDiagnosticCallPiece::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
520PathDiagnosticCallPiece *
521PathDiagnosticCallPiece::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
529void 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
538IntrusiveRefCntPtr<PathDiagnosticEventPiece>
539PathDiagnosticCallPiece::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
554IntrusiveRefCntPtr<PathDiagnosticEventPiece>
555PathDiagnosticCallPiece::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
568IntrusiveRefCntPtr<PathDiagnosticEventPiece>
569PathDiagnosticCallPiece::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
583static 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
596unsigned 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
606void 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
613void 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
624void 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
632void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const {
633  PathDiagnosticPiece::Profile(ID);
634  ID.Add(Pos);
635}
636
637void 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
643void 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
650void 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
658void 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
666StackHintGenerator::~StackHintGenerator() {}
667
668std::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.)
719void 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
744std::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