PathDiagnostic.cpp revision 2042fc1f36d471f437023e8899f0c4fadded2341
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/AST/Expr.h"
17#include "clang/AST/Decl.h"
18#include "clang/AST/DeclObjC.h"
19#include "clang/AST/ParentMap.h"
20#include "clang/AST/StmtCXX.h"
21#include "llvm/ADT/SmallString.h"
22
23using namespace clang;
24using namespace ento;
25
26bool PathDiagnosticMacroPiece::containsEvent() const {
27  for (PathPieces::const_iterator I = subPieces.begin(), E = subPieces.end();
28       I!=E; ++I) {
29    if (isa<PathDiagnosticEventPiece>(*I))
30      return true;
31    if (PathDiagnosticMacroPiece *MP = dyn_cast<PathDiagnosticMacroPiece>(*I))
32      if (MP->containsEvent())
33        return true;
34  }
35  return false;
36}
37
38static StringRef StripTrailingDots(StringRef s) {
39  for (StringRef::size_type i = s.size(); i != 0; --i)
40    if (s[i - 1] != '.')
41      return s.substr(0, i);
42  return "";
43}
44
45PathDiagnosticPiece::PathDiagnosticPiece(StringRef s,
46                                         Kind k, DisplayHint hint)
47  : str(StripTrailingDots(s)), kind(k), Hint(hint) {}
48
49PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint)
50  : kind(k), Hint(hint) {}
51
52PathDiagnosticPiece::~PathDiagnosticPiece() {}
53PathDiagnosticEventPiece::~PathDiagnosticEventPiece() {}
54PathDiagnosticCallPiece::~PathDiagnosticCallPiece() {}
55PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {}
56PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {}
57PathDiagnostic::PathDiagnostic() : path(pathImpl) {}
58PathPieces::~PathPieces() {}
59PathDiagnostic::~PathDiagnostic() {}
60
61PathDiagnostic::PathDiagnostic(StringRef bugtype, StringRef desc,
62                               StringRef category)
63  : BugType(StripTrailingDots(bugtype)),
64    Desc(StripTrailingDots(desc)),
65    Category(StripTrailingDots(category)),
66    path(pathImpl) {}
67
68void PathDiagnosticConsumer::anchor() { }
69
70PathDiagnosticConsumer::~PathDiagnosticConsumer() {
71  // Delete the contents of the FoldingSet if it isn't empty already.
72  for (llvm::FoldingSet<PathDiagnostic>::iterator it =
73       Diags.begin(), et = Diags.end() ; it != et ; ++it) {
74    delete &*it;
75  }
76}
77
78void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) {
79  if (!D)
80    return;
81
82  if (D->path.empty()) {
83    delete D;
84    return;
85  }
86
87  // We need to flatten the locations (convert Stmt* to locations) because
88  // the referenced statements may be freed by the time the diagnostics
89  // are emitted.
90  D->flattenLocations();
91
92  // Profile the node to see if we already have something matching it
93  llvm::FoldingSetNodeID profile;
94  D->Profile(profile);
95  void *InsertPos = 0;
96
97  if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) {
98    // Keep the PathDiagnostic with the shorter path.
99    const unsigned orig_size = orig->full_size();
100    const unsigned new_size = D->full_size();
101
102    if (orig_size <= new_size) {
103      bool shouldKeepOriginal = true;
104      if (orig_size == new_size) {
105        // Here we break ties in a fairly arbitrary, but deterministic, way.
106        llvm::FoldingSetNodeID fullProfile, fullProfileOrig;
107        D->FullProfile(fullProfile);
108        orig->FullProfile(fullProfileOrig);
109        if (fullProfile.ComputeHash() < fullProfileOrig.ComputeHash())
110          shouldKeepOriginal = false;
111      }
112
113      if (shouldKeepOriginal) {
114        delete D;
115        return;
116      }
117    }
118    Diags.RemoveNode(orig);
119    delete orig;
120  }
121
122  Diags.InsertNode(D);
123}
124
125
126namespace {
127struct CompareDiagnostics {
128  // Compare if 'X' is "<" than 'Y'.
129  bool operator()(const PathDiagnostic *X, const PathDiagnostic *Y) const {
130    // First compare by location
131    const FullSourceLoc &XLoc = X->getLocation().asLocation();
132    const FullSourceLoc &YLoc = Y->getLocation().asLocation();
133    if (XLoc < YLoc)
134      return true;
135    if (XLoc != YLoc)
136      return false;
137
138    // Next, compare by bug type.
139    StringRef XBugType = X->getBugType();
140    StringRef YBugType = Y->getBugType();
141    if (XBugType < YBugType)
142      return true;
143    if (XBugType != YBugType)
144      return false;
145
146    // Next, compare by bug description.
147    StringRef XDesc = X->getDescription();
148    StringRef YDesc = Y->getDescription();
149    if (XDesc < YDesc)
150      return true;
151    if (XDesc != YDesc)
152      return false;
153
154    // FIXME: Further refine by comparing PathDiagnosticPieces?
155    return false;
156  }
157};
158}
159
160void
161PathDiagnosticConsumer::FlushDiagnostics(SmallVectorImpl<std::string> *Files) {
162  if (flushed)
163    return;
164
165  flushed = true;
166
167  std::vector<const PathDiagnostic *> BatchDiags;
168  for (llvm::FoldingSet<PathDiagnostic>::iterator it = Diags.begin(),
169       et = Diags.end(); it != et; ++it) {
170    BatchDiags.push_back(&*it);
171  }
172
173  // Clear out the FoldingSet.
174  Diags.clear();
175
176  // Sort the diagnostics so that they are always emitted in a deterministic
177  // order.
178  if (!BatchDiags.empty())
179    std::sort(BatchDiags.begin(), BatchDiags.end(), CompareDiagnostics());
180
181  FlushDiagnosticsImpl(BatchDiags, Files);
182
183  // Delete the flushed diagnostics.
184  for (std::vector<const PathDiagnostic *>::iterator it = BatchDiags.begin(),
185       et = BatchDiags.end(); it != et; ++it) {
186    const PathDiagnostic *D = *it;
187    delete D;
188  }
189}
190
191//===----------------------------------------------------------------------===//
192// PathDiagnosticLocation methods.
193//===----------------------------------------------------------------------===//
194
195static SourceLocation getValidSourceLocation(const Stmt* S,
196                                             LocationOrAnalysisDeclContext LAC) {
197  SourceLocation L = S->getLocStart();
198  assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext should "
199                          "be passed to PathDiagnosticLocation upon creation.");
200
201  // S might be a temporary statement that does not have a location in the
202  // source code, so find an enclosing statement and use it's location.
203  if (!L.isValid()) {
204
205    ParentMap *PM = 0;
206    if (LAC.is<const LocationContext*>())
207      PM = &LAC.get<const LocationContext*>()->getParentMap();
208    else
209      PM = &LAC.get<AnalysisDeclContext*>()->getParentMap();
210
211    while (!L.isValid()) {
212      S = PM->getParent(S);
213      L = S->getLocStart();
214    }
215  }
216
217  return L;
218}
219
220PathDiagnosticLocation
221  PathDiagnosticLocation::createBegin(const Decl *D,
222                                      const SourceManager &SM) {
223  return PathDiagnosticLocation(D->getLocStart(), SM, SingleLocK);
224}
225
226PathDiagnosticLocation
227  PathDiagnosticLocation::createBegin(const Stmt *S,
228                                      const SourceManager &SM,
229                                      LocationOrAnalysisDeclContext LAC) {
230  return PathDiagnosticLocation(getValidSourceLocation(S, LAC),
231                                SM, SingleLocK);
232}
233
234PathDiagnosticLocation
235  PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO,
236                                            const SourceManager &SM) {
237  return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK);
238}
239
240PathDiagnosticLocation
241  PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME,
242                                          const SourceManager &SM) {
243  return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK);
244}
245
246PathDiagnosticLocation
247  PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS,
248                                           const SourceManager &SM) {
249  SourceLocation L = CS->getLBracLoc();
250  return PathDiagnosticLocation(L, SM, SingleLocK);
251}
252
253PathDiagnosticLocation
254  PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS,
255                                         const SourceManager &SM) {
256  SourceLocation L = CS->getRBracLoc();
257  return PathDiagnosticLocation(L, SM, SingleLocK);
258}
259
260PathDiagnosticLocation
261  PathDiagnosticLocation::createDeclBegin(const LocationContext *LC,
262                                          const SourceManager &SM) {
263  // FIXME: Should handle CXXTryStmt if analyser starts supporting C++.
264  if (const CompoundStmt *CS =
265        dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody()))
266    if (!CS->body_empty()) {
267      SourceLocation Loc = (*CS->body_begin())->getLocStart();
268      return PathDiagnosticLocation(Loc, SM, SingleLocK);
269    }
270
271  return PathDiagnosticLocation();
272}
273
274PathDiagnosticLocation
275  PathDiagnosticLocation::createDeclEnd(const LocationContext *LC,
276                                        const SourceManager &SM) {
277  SourceLocation L = LC->getDecl()->getBodyRBrace();
278  return PathDiagnosticLocation(L, SM, SingleLocK);
279}
280
281PathDiagnosticLocation
282  PathDiagnosticLocation::create(const ProgramPoint& P,
283                                 const SourceManager &SMng) {
284
285  const Stmt* S = 0;
286  if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
287    const CFGBlock *BSrc = BE->getSrc();
288    S = BSrc->getTerminatorCondition();
289  }
290  else if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) {
291    S = PS->getStmt();
292  }
293
294  return PathDiagnosticLocation(S, SMng, P.getLocationContext());
295}
296
297PathDiagnosticLocation
298  PathDiagnosticLocation::createEndOfPath(const ExplodedNode* N,
299                                          const SourceManager &SM) {
300  assert(N && "Cannot create a location with a null node.");
301
302  const ExplodedNode *NI = N;
303
304  while (NI) {
305    ProgramPoint P = NI->getLocation();
306    const LocationContext *LC = P.getLocationContext();
307    if (const StmtPoint *PS = dyn_cast<StmtPoint>(&P))
308      return PathDiagnosticLocation(PS->getStmt(), SM, LC);
309    else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
310      const Stmt *Term = BE->getSrc()->getTerminator();
311      if (Term) {
312        return PathDiagnosticLocation(Term, SM, LC);
313      }
314    }
315    NI = NI->succ_empty() ? 0 : *(NI->succ_begin());
316  }
317
318  return createDeclEnd(N->getLocationContext(), SM);
319}
320
321PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation(
322                                           const PathDiagnosticLocation &PDL) {
323  FullSourceLoc L = PDL.asLocation();
324  return PathDiagnosticLocation(L, L.getManager(), SingleLocK);
325}
326
327FullSourceLoc
328  PathDiagnosticLocation::genLocation(SourceLocation L,
329                                      LocationOrAnalysisDeclContext LAC) const {
330  assert(isValid());
331  // Note that we want a 'switch' here so that the compiler can warn us in
332  // case we add more cases.
333  switch (K) {
334    case SingleLocK:
335    case RangeK:
336      break;
337    case StmtK:
338      // Defensive checking.
339      if (!S)
340        break;
341      return FullSourceLoc(getValidSourceLocation(S, LAC),
342                           const_cast<SourceManager&>(*SM));
343    case DeclK:
344      // Defensive checking.
345      if (!D)
346        break;
347      return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM));
348  }
349
350  return FullSourceLoc(L, const_cast<SourceManager&>(*SM));
351}
352
353PathDiagnosticRange
354  PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const {
355  assert(isValid());
356  // Note that we want a 'switch' here so that the compiler can warn us in
357  // case we add more cases.
358  switch (K) {
359    case SingleLocK:
360      return PathDiagnosticRange(SourceRange(Loc,Loc), true);
361    case RangeK:
362      break;
363    case StmtK: {
364      const Stmt *S = asStmt();
365      switch (S->getStmtClass()) {
366        default:
367          break;
368        case Stmt::DeclStmtClass: {
369          const DeclStmt *DS = cast<DeclStmt>(S);
370          if (DS->isSingleDecl()) {
371            // Should always be the case, but we'll be defensive.
372            return SourceRange(DS->getLocStart(),
373                               DS->getSingleDecl()->getLocation());
374          }
375          break;
376        }
377          // FIXME: Provide better range information for different
378          //  terminators.
379        case Stmt::IfStmtClass:
380        case Stmt::WhileStmtClass:
381        case Stmt::DoStmtClass:
382        case Stmt::ForStmtClass:
383        case Stmt::ChooseExprClass:
384        case Stmt::IndirectGotoStmtClass:
385        case Stmt::SwitchStmtClass:
386        case Stmt::BinaryConditionalOperatorClass:
387        case Stmt::ConditionalOperatorClass:
388        case Stmt::ObjCForCollectionStmtClass: {
389          SourceLocation L = getValidSourceLocation(S, LAC);
390          return SourceRange(L, L);
391        }
392      }
393      SourceRange R = S->getSourceRange();
394      if (R.isValid())
395        return R;
396      break;
397    }
398    case DeclK:
399      if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
400        return MD->getSourceRange();
401      if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
402        if (Stmt *Body = FD->getBody())
403          return Body->getSourceRange();
404      }
405      else {
406        SourceLocation L = D->getLocation();
407        return PathDiagnosticRange(SourceRange(L, L), true);
408      }
409  }
410
411  return SourceRange(Loc,Loc);
412}
413
414void PathDiagnosticLocation::flatten() {
415  if (K == StmtK) {
416    K = RangeK;
417    S = 0;
418    D = 0;
419  }
420  else if (K == DeclK) {
421    K = SingleLocK;
422    S = 0;
423    D = 0;
424  }
425}
426
427PathDiagnosticLocation PathDiagnostic::getLocation() const {
428  assert(path.size() > 0 &&
429         "getLocation() requires a non-empty PathDiagnostic.");
430
431  PathDiagnosticPiece *p = path.rbegin()->getPtr();
432
433  while (true) {
434    if (PathDiagnosticCallPiece *cp = dyn_cast<PathDiagnosticCallPiece>(p)) {
435      assert(!cp->path.empty());
436      p = cp->path.rbegin()->getPtr();
437      continue;
438    }
439    break;
440  }
441
442  return p->getLocation();
443}
444
445//===----------------------------------------------------------------------===//
446// Manipulation of PathDiagnosticCallPieces.
447//===----------------------------------------------------------------------===//
448
449static PathDiagnosticLocation getLastStmtLoc(const ExplodedNode *N,
450                                             const SourceManager &SM) {
451  while (N) {
452    ProgramPoint PP = N->getLocation();
453    if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP))
454      return PathDiagnosticLocation(SP->getStmt(), SM, PP.getLocationContext());
455    if (N->pred_empty())
456      break;
457    N = *N->pred_begin();
458  }
459  return PathDiagnosticLocation();
460}
461
462PathDiagnosticCallPiece *
463PathDiagnosticCallPiece::construct(const ExplodedNode *N,
464                                   const CallExit &CE,
465                                   const SourceManager &SM) {
466  const Decl *caller = CE.getLocationContext()->getParent()->getDecl();
467  PathDiagnosticLocation pos = getLastStmtLoc(N, SM);
468  return new PathDiagnosticCallPiece(caller, pos);
469}
470
471PathDiagnosticCallPiece *
472PathDiagnosticCallPiece::construct(PathPieces &path) {
473  PathDiagnosticCallPiece *C = new PathDiagnosticCallPiece(path);
474  path.clear();
475  path.push_front(C);
476  return C;
477}
478
479void PathDiagnosticCallPiece::setCallee(const CallEnter &CE,
480                                        const SourceManager &SM) {
481  const Decl *D = CE.getCalleeContext()->getDecl();
482  Callee = D;
483  callEnter = PathDiagnosticLocation(CE.getCallExpr(), SM,
484                                     CE.getLocationContext());
485}
486
487IntrusiveRefCntPtr<PathDiagnosticEventPiece>
488PathDiagnosticCallPiece::getCallEnterEvent() const {
489  if (!Callee)
490    return 0;
491  SmallString<256> buf;
492  llvm::raw_svector_ostream Out(buf);
493  if (isa<BlockDecl>(Callee))
494    Out << "Entering call to block";
495  else if (const NamedDecl *ND = dyn_cast<NamedDecl>(Callee))
496    Out << "Entering call to '" << *ND << "'";
497  StringRef msg = Out.str();
498  if (msg.empty())
499    return 0;
500  return new PathDiagnosticEventPiece(callEnter, msg);
501}
502
503IntrusiveRefCntPtr<PathDiagnosticEventPiece>
504PathDiagnosticCallPiece::getCallExitEvent() const {
505  if (!Caller)
506    return 0;
507  SmallString<256> buf;
508  llvm::raw_svector_ostream Out(buf);
509  if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Caller))
510    Out << "Returning to '" << *ND << "'";
511  else
512    Out << "Returning to caller";
513  return new PathDiagnosticEventPiece(callReturn, Out.str());
514}
515
516static void compute_path_size(const PathPieces &pieces, unsigned &size) {
517  for (PathPieces::const_iterator it = pieces.begin(),
518                                  et = pieces.end(); it != et; ++it) {
519    const PathDiagnosticPiece *piece = it->getPtr();
520    if (const PathDiagnosticCallPiece *cp =
521        dyn_cast<PathDiagnosticCallPiece>(piece)) {
522      compute_path_size(cp->path, size);
523    }
524    else
525      ++size;
526  }
527}
528
529unsigned PathDiagnostic::full_size() {
530  unsigned size = 0;
531  compute_path_size(path, size);
532  return size;
533}
534
535//===----------------------------------------------------------------------===//
536// FoldingSet profiling methods.
537//===----------------------------------------------------------------------===//
538
539void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const {
540  ID.AddInteger(Range.getBegin().getRawEncoding());
541  ID.AddInteger(Range.getEnd().getRawEncoding());
542  ID.AddInteger(Loc.getRawEncoding());
543  return;
544}
545
546void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const {
547  ID.AddInteger((unsigned) getKind());
548  ID.AddString(str);
549  // FIXME: Add profiling support for code hints.
550  ID.AddInteger((unsigned) getDisplayHint());
551  for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) {
552    ID.AddInteger(I->getBegin().getRawEncoding());
553    ID.AddInteger(I->getEnd().getRawEncoding());
554  }
555}
556
557void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const {
558  PathDiagnosticPiece::Profile(ID);
559  for (PathPieces::const_iterator it = path.begin(),
560       et = path.end(); it != et; ++it) {
561    ID.Add(**it);
562  }
563}
564
565void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const {
566  PathDiagnosticPiece::Profile(ID);
567  ID.Add(Pos);
568}
569
570void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const {
571  PathDiagnosticPiece::Profile(ID);
572  for (const_iterator I = begin(), E = end(); I != E; ++I)
573    ID.Add(*I);
574}
575
576void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const {
577  PathDiagnosticSpotPiece::Profile(ID);
578  for (PathPieces::const_iterator I = subPieces.begin(), E = subPieces.end();
579       I != E; ++I)
580    ID.Add(**I);
581}
582
583void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const {
584  if (!path.empty())
585    getLocation().Profile(ID);
586  ID.AddString(BugType);
587  ID.AddString(Desc);
588  ID.AddString(Category);
589}
590
591void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const {
592  Profile(ID);
593  for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I)
594    ID.Add(**I);
595  for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I)
596    ID.AddString(*I);
597}
598