PathDiagnostic.cpp revision 5a0917d1367115d5fddfe7551f8634759217b54b
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() {}
54PathDiagnosticCallEnterPiece::~PathDiagnosticCallEnterPiece() {}
55PathDiagnosticCallExitPiece::~PathDiagnosticCallExitPiece() {}
56PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {}
57PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {}
58PathDiagnostic::PathDiagnostic() {}
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
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    if (orig->path.size() <= D->path.size()) {
100      bool shouldKeepOriginal = true;
101      if (orig->path.size() == D->path.size()) {
102        // Here we break ties in a fairly arbitrary, but deterministic, way.
103        llvm::FoldingSetNodeID fullProfile, fullProfileOrig;
104        D->FullProfile(fullProfile);
105        orig->FullProfile(fullProfileOrig);
106        if (fullProfile.ComputeHash() < fullProfileOrig.ComputeHash())
107          shouldKeepOriginal = false;
108      }
109
110      if (shouldKeepOriginal) {
111        delete D;
112        return;
113      }
114    }
115    Diags.RemoveNode(orig);
116    delete orig;
117  }
118
119  Diags.InsertNode(D);
120}
121
122
123namespace {
124struct CompareDiagnostics {
125  // Compare if 'X' is "<" than 'Y'.
126  bool operator()(const PathDiagnostic *X, const PathDiagnostic *Y) const {
127    // First compare by location
128    const FullSourceLoc &XLoc = X->getLocation().asLocation();
129    const FullSourceLoc &YLoc = Y->getLocation().asLocation();
130    if (XLoc < YLoc)
131      return true;
132    if (XLoc != YLoc)
133      return false;
134
135    // Next, compare by bug type.
136    StringRef XBugType = X->getBugType();
137    StringRef YBugType = Y->getBugType();
138    if (XBugType < YBugType)
139      return true;
140    if (XBugType != YBugType)
141      return false;
142
143    // Next, compare by bug description.
144    StringRef XDesc = X->getDescription();
145    StringRef YDesc = Y->getDescription();
146    if (XDesc < YDesc)
147      return true;
148    if (XDesc != YDesc)
149      return false;
150
151    // FIXME: Further refine by comparing PathDiagnosticPieces?
152    return false;
153  }
154};
155}
156
157void
158PathDiagnosticConsumer::FlushDiagnostics(SmallVectorImpl<std::string> *Files) {
159  if (flushed)
160    return;
161
162  flushed = true;
163
164  std::vector<const PathDiagnostic *> BatchDiags;
165  for (llvm::FoldingSet<PathDiagnostic>::iterator it = Diags.begin(),
166       et = Diags.end(); it != et; ++it) {
167    BatchDiags.push_back(&*it);
168  }
169
170  // Clear out the FoldingSet.
171  Diags.clear();
172
173  // Sort the diagnostics so that they are always emitted in a deterministic
174  // order.
175  if (!BatchDiags.empty())
176    std::sort(BatchDiags.begin(), BatchDiags.end(), CompareDiagnostics());
177
178  FlushDiagnosticsImpl(BatchDiags, Files);
179
180  // Delete the flushed diagnostics.
181  for (std::vector<const PathDiagnostic *>::iterator it = BatchDiags.begin(),
182       et = BatchDiags.end(); it != et; ++it) {
183    const PathDiagnostic *D = *it;
184    delete D;
185  }
186}
187
188//===----------------------------------------------------------------------===//
189// PathDiagnosticLocation methods.
190//===----------------------------------------------------------------------===//
191
192static SourceLocation getValidSourceLocation(const Stmt* S,
193                                             LocationOrAnalysisDeclContext LAC) {
194  SourceLocation L = S->getLocStart();
195  assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext should "
196                          "be passed to PathDiagnosticLocation upon creation.");
197
198  // S might be a temporary statement that does not have a location in the
199  // source code, so find an enclosing statement and use it's location.
200  if (!L.isValid()) {
201
202    ParentMap *PM = 0;
203    if (LAC.is<const LocationContext*>())
204      PM = &LAC.get<const LocationContext*>()->getParentMap();
205    else
206      PM = &LAC.get<AnalysisDeclContext*>()->getParentMap();
207
208    while (!L.isValid()) {
209      S = PM->getParent(S);
210      L = S->getLocStart();
211    }
212  }
213
214  return L;
215}
216
217PathDiagnosticLocation
218  PathDiagnosticLocation::createBegin(const Decl *D,
219                                      const SourceManager &SM) {
220  return PathDiagnosticLocation(D->getLocStart(), SM, SingleLocK);
221}
222
223PathDiagnosticLocation
224  PathDiagnosticLocation::createBegin(const Stmt *S,
225                                      const SourceManager &SM,
226                                      LocationOrAnalysisDeclContext LAC) {
227  return PathDiagnosticLocation(getValidSourceLocation(S, LAC),
228                                SM, SingleLocK);
229}
230
231PathDiagnosticLocation
232  PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO,
233                                            const SourceManager &SM) {
234  return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK);
235}
236
237PathDiagnosticLocation
238  PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME,
239                                          const SourceManager &SM) {
240  return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK);
241}
242
243PathDiagnosticLocation
244  PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS,
245                                           const SourceManager &SM) {
246  SourceLocation L = CS->getLBracLoc();
247  return PathDiagnosticLocation(L, SM, SingleLocK);
248}
249
250PathDiagnosticLocation
251  PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS,
252                                         const SourceManager &SM) {
253  SourceLocation L = CS->getRBracLoc();
254  return PathDiagnosticLocation(L, SM, SingleLocK);
255}
256
257PathDiagnosticLocation
258  PathDiagnosticLocation::createDeclBegin(const LocationContext *LC,
259                                          const SourceManager &SM) {
260  // FIXME: Should handle CXXTryStmt if analyser starts supporting C++.
261  if (const CompoundStmt *CS =
262        dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody()))
263    if (!CS->body_empty()) {
264      SourceLocation Loc = (*CS->body_begin())->getLocStart();
265      return PathDiagnosticLocation(Loc, SM, SingleLocK);
266    }
267
268  return PathDiagnosticLocation();
269}
270
271PathDiagnosticLocation
272  PathDiagnosticLocation::createDeclEnd(const LocationContext *LC,
273                                        const SourceManager &SM) {
274  SourceLocation L = LC->getDecl()->getBodyRBrace();
275  return PathDiagnosticLocation(L, SM, SingleLocK);
276}
277
278PathDiagnosticLocation
279  PathDiagnosticLocation::create(const ProgramPoint& P,
280                                 const SourceManager &SMng) {
281
282  const Stmt* S = 0;
283  if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
284    const CFGBlock *BSrc = BE->getSrc();
285    S = BSrc->getTerminatorCondition();
286  }
287  else if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) {
288    S = PS->getStmt();
289  }
290
291  return PathDiagnosticLocation(S, SMng, P.getLocationContext());
292}
293
294PathDiagnosticLocation
295  PathDiagnosticLocation::createEndOfPath(const ExplodedNode* N,
296                                          const SourceManager &SM) {
297  assert(N && "Cannot create a location with a null node.");
298
299  const ExplodedNode *NI = N;
300
301  while (NI) {
302    ProgramPoint P = NI->getLocation();
303    const LocationContext *LC = P.getLocationContext();
304    if (const StmtPoint *PS = dyn_cast<StmtPoint>(&P))
305      return PathDiagnosticLocation(PS->getStmt(), SM, LC);
306    else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
307      const Stmt *Term = BE->getSrc()->getTerminator();
308      if (Term) {
309        return PathDiagnosticLocation(Term, SM, LC);
310      }
311    }
312    NI = NI->succ_empty() ? 0 : *(NI->succ_begin());
313  }
314
315  return createDeclEnd(N->getLocationContext(), SM);
316}
317
318PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation(
319                                           const PathDiagnosticLocation &PDL) {
320  FullSourceLoc L = PDL.asLocation();
321  return PathDiagnosticLocation(L, L.getManager(), SingleLocK);
322}
323
324FullSourceLoc
325  PathDiagnosticLocation::genLocation(SourceLocation L,
326                                      LocationOrAnalysisDeclContext LAC) const {
327  assert(isValid());
328  // Note that we want a 'switch' here so that the compiler can warn us in
329  // case we add more cases.
330  switch (K) {
331    case SingleLocK:
332    case RangeK:
333      break;
334    case StmtK:
335      // Defensive checking.
336      if (!S)
337        break;
338      return FullSourceLoc(getValidSourceLocation(S, LAC),
339                           const_cast<SourceManager&>(*SM));
340    case DeclK:
341      // Defensive checking.
342      if (!D)
343        break;
344      return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM));
345  }
346
347  return FullSourceLoc(L, const_cast<SourceManager&>(*SM));
348}
349
350PathDiagnosticRange
351  PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const {
352  assert(isValid());
353  // Note that we want a 'switch' here so that the compiler can warn us in
354  // case we add more cases.
355  switch (K) {
356    case SingleLocK:
357      return PathDiagnosticRange(SourceRange(Loc,Loc), true);
358    case RangeK:
359      break;
360    case StmtK: {
361      const Stmt *S = asStmt();
362      switch (S->getStmtClass()) {
363        default:
364          break;
365        case Stmt::DeclStmtClass: {
366          const DeclStmt *DS = cast<DeclStmt>(S);
367          if (DS->isSingleDecl()) {
368            // Should always be the case, but we'll be defensive.
369            return SourceRange(DS->getLocStart(),
370                               DS->getSingleDecl()->getLocation());
371          }
372          break;
373        }
374          // FIXME: Provide better range information for different
375          //  terminators.
376        case Stmt::IfStmtClass:
377        case Stmt::WhileStmtClass:
378        case Stmt::DoStmtClass:
379        case Stmt::ForStmtClass:
380        case Stmt::ChooseExprClass:
381        case Stmt::IndirectGotoStmtClass:
382        case Stmt::SwitchStmtClass:
383        case Stmt::BinaryConditionalOperatorClass:
384        case Stmt::ConditionalOperatorClass:
385        case Stmt::ObjCForCollectionStmtClass: {
386          SourceLocation L = getValidSourceLocation(S, LAC);
387          return SourceRange(L, L);
388        }
389      }
390      SourceRange R = S->getSourceRange();
391      if (R.isValid())
392        return R;
393      break;
394    }
395    case DeclK:
396      if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
397        return MD->getSourceRange();
398      if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
399        if (Stmt *Body = FD->getBody())
400          return Body->getSourceRange();
401      }
402      else {
403        SourceLocation L = D->getLocation();
404        return PathDiagnosticRange(SourceRange(L, L), true);
405      }
406  }
407
408  return SourceRange(Loc,Loc);
409}
410
411void PathDiagnosticLocation::flatten() {
412  if (K == StmtK) {
413    K = RangeK;
414    S = 0;
415    D = 0;
416  }
417  else if (K == DeclK) {
418    K = SingleLocK;
419    S = 0;
420    D = 0;
421  }
422}
423
424//===----------------------------------------------------------------------===//
425// FoldingSet profiling methods.
426//===----------------------------------------------------------------------===//
427
428void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const {
429  ID.AddInteger(Range.getBegin().getRawEncoding());
430  ID.AddInteger(Range.getEnd().getRawEncoding());
431  ID.AddInteger(Loc.getRawEncoding());
432  return;
433}
434
435void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const {
436  ID.AddInteger((unsigned) getKind());
437  ID.AddString(str);
438  // FIXME: Add profiling support for code hints.
439  ID.AddInteger((unsigned) getDisplayHint());
440  for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) {
441    ID.AddInteger(I->getBegin().getRawEncoding());
442    ID.AddInteger(I->getEnd().getRawEncoding());
443  }
444}
445
446void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const {
447  PathDiagnosticPiece::Profile(ID);
448  ID.Add(Pos);
449}
450
451void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const {
452  PathDiagnosticPiece::Profile(ID);
453  for (const_iterator I = begin(), E = end(); I != E; ++I)
454    ID.Add(*I);
455}
456
457void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const {
458  PathDiagnosticSpotPiece::Profile(ID);
459  for (PathPieces::const_iterator I = subPieces.begin(), E = subPieces.end();
460       I != E; ++I)
461    ID.Add(**I);
462}
463
464void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const {
465  if (!path.empty())
466    getLocation().Profile(ID);
467  ID.AddString(BugType);
468  ID.AddString(Desc);
469  ID.AddString(Category);
470}
471
472void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const {
473  Profile(ID);
474  for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I)
475    ID.Add(**I);
476  for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I)
477    ID.AddString(*I);
478}
479