xref: /llvm-project/clang/lib/Analysis/PathDiagnostic.cpp (revision 4dab76a752f13b0648ee93c75d9a8960076884ce)
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/Analysis/PathDiagnostic.h"
15 #include "clang/AST/Expr.h"
16 #include "clang/AST/Decl.h"
17 #include "clang/AST/DeclObjC.h"
18 #include "clang/AST/StmtCXX.h"
19 #include "llvm/ADT/SmallString.h"
20 #include "llvm/Support/Casting.h"
21 
22 using namespace clang;
23 using llvm::dyn_cast;
24 using llvm::isa;
25 
26 bool PathDiagnosticMacroPiece::containsEvent() const {
27   for (const_iterator I = begin(), E = end(); I!=E; ++I) {
28     if (isa<PathDiagnosticEventPiece>(*I))
29       return true;
30 
31     if (PathDiagnosticMacroPiece *MP = dyn_cast<PathDiagnosticMacroPiece>(*I))
32       if (MP->containsEvent())
33         return true;
34   }
35 
36   return false;
37 }
38 
39 static llvm::StringRef StripTrailingDots(llvm::StringRef s) {
40   for (llvm::StringRef::size_type i = s.size(); i != 0; --i)
41     if (s[i - 1] != '.')
42       return s.substr(0, i);
43   return "";
44 }
45 
46 PathDiagnosticPiece::PathDiagnosticPiece(llvm::StringRef s,
47                                          Kind k, DisplayHint hint)
48   : str(StripTrailingDots(s)), kind(k), Hint(hint) {}
49 
50 PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint)
51   : kind(k), Hint(hint) {}
52 
53 PathDiagnosticPiece::~PathDiagnosticPiece() {}
54 PathDiagnosticEventPiece::~PathDiagnosticEventPiece() {}
55 PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {}
56 
57 PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {
58   for (iterator I = begin(), E = end(); I != E; ++I) delete *I;
59 }
60 
61 PathDiagnostic::PathDiagnostic() : Size(0) {}
62 
63 PathDiagnostic::~PathDiagnostic() {
64   for (iterator I = begin(), E = end(); I != E; ++I) delete &*I;
65 }
66 
67 void PathDiagnostic::resetPath(bool deletePieces) {
68   Size = 0;
69 
70   if (deletePieces)
71     for (iterator I=begin(), E=end(); I!=E; ++I)
72       delete &*I;
73 
74   path.clear();
75 }
76 
77 
78 PathDiagnostic::PathDiagnostic(llvm::StringRef bugtype, llvm::StringRef desc,
79                                llvm::StringRef category)
80   : Size(0),
81     BugType(StripTrailingDots(bugtype)),
82     Desc(StripTrailingDots(desc)),
83     Category(StripTrailingDots(category)) {}
84 
85 void PathDiagnosticClient::HandleDiagnostic(Diagnostic::Level DiagLevel,
86                                             const DiagnosticInfo &Info) {
87 
88   // Create a PathDiagnostic with a single piece.
89 
90   PathDiagnostic* D = new PathDiagnostic();
91 
92   const char *LevelStr;
93   switch (DiagLevel) {
94   default:
95   case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type");
96   case Diagnostic::Note:    LevelStr = "note: "; break;
97   case Diagnostic::Warning: LevelStr = "warning: "; break;
98   case Diagnostic::Error:   LevelStr = "error: "; break;
99   case Diagnostic::Fatal:   LevelStr = "fatal error: "; break;
100   }
101 
102   llvm::SmallString<100> StrC;
103   StrC += LevelStr;
104   Info.FormatDiagnostic(StrC);
105 
106   PathDiagnosticPiece *P =
107     new PathDiagnosticEventPiece(Info.getLocation(), StrC.str());
108 
109   for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i)
110     P->addRange(Info.getRange(i));
111   for (unsigned i = 0, e = Info.getNumCodeModificationHints(); i != e; ++i)
112     P->addCodeModificationHint(Info.getCodeModificationHint(i));
113   D->push_front(P);
114 
115   HandlePathDiagnostic(D);
116 }
117 
118 //===----------------------------------------------------------------------===//
119 // PathDiagnosticLocation methods.
120 //===----------------------------------------------------------------------===//
121 
122 FullSourceLoc PathDiagnosticLocation::asLocation() const {
123   assert(isValid());
124   // Note that we want a 'switch' here so that the compiler can warn us in
125   // case we add more cases.
126   switch (K) {
127     case SingleLocK:
128     case RangeK:
129       break;
130     case StmtK:
131       return FullSourceLoc(S->getLocStart(), const_cast<SourceManager&>(*SM));
132     case DeclK:
133       return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM));
134   }
135 
136   return FullSourceLoc(R.getBegin(), const_cast<SourceManager&>(*SM));
137 }
138 
139 PathDiagnosticRange PathDiagnosticLocation::asRange() const {
140   assert(isValid());
141   // Note that we want a 'switch' here so that the compiler can warn us in
142   // case we add more cases.
143   switch (K) {
144     case SingleLocK:
145       return PathDiagnosticRange(R, true);
146     case RangeK:
147       break;
148     case StmtK: {
149       const Stmt *S = asStmt();
150       switch (S->getStmtClass()) {
151         default:
152           break;
153         case Stmt::DeclStmtClass: {
154           const DeclStmt *DS = cast<DeclStmt>(S);
155           if (DS->isSingleDecl()) {
156             // Should always be the case, but we'll be defensive.
157             return SourceRange(DS->getLocStart(),
158                                DS->getSingleDecl()->getLocation());
159           }
160           break;
161         }
162           // FIXME: Provide better range information for different
163           //  terminators.
164         case Stmt::IfStmtClass:
165         case Stmt::WhileStmtClass:
166         case Stmt::DoStmtClass:
167         case Stmt::ForStmtClass:
168         case Stmt::ChooseExprClass:
169         case Stmt::IndirectGotoStmtClass:
170         case Stmt::SwitchStmtClass:
171         case Stmt::ConditionalOperatorClass:
172         case Stmt::ObjCForCollectionStmtClass: {
173           SourceLocation L = S->getLocStart();
174           return SourceRange(L, L);
175         }
176       }
177 
178       return S->getSourceRange();
179     }
180     case DeclK:
181       if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
182         return MD->getSourceRange();
183       if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
184         // FIXME: We would like to always get the function body, even
185         // when it needs to be de-serialized, but getting the
186         // ASTContext here requires significant changes.
187         if (Stmt *Body = FD->getBody()) {
188           if (CompoundStmt *CS = dyn_cast<CompoundStmt>(Body))
189             return CS->getSourceRange();
190           else
191             return cast<CXXTryStmt>(Body)->getSourceRange();
192         }
193       }
194       else {
195         SourceLocation L = D->getLocation();
196         return PathDiagnosticRange(SourceRange(L, L), true);
197       }
198   }
199 
200   return R;
201 }
202 
203 void PathDiagnosticLocation::flatten() {
204   if (K == StmtK) {
205     R = asRange();
206     K = RangeK;
207     S = 0;
208     D = 0;
209   }
210   else if (K == DeclK) {
211     SourceLocation L = D->getLocation();
212     R = SourceRange(L, L);
213     K = SingleLocK;
214     S = 0;
215     D = 0;
216   }
217 }
218 
219 //===----------------------------------------------------------------------===//
220 // FoldingSet profiling methods.
221 //===----------------------------------------------------------------------===//
222 
223 void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const {
224   ID.AddInteger((unsigned) K);
225   switch (K) {
226     case RangeK:
227       ID.AddInteger(R.getBegin().getRawEncoding());
228       ID.AddInteger(R.getEnd().getRawEncoding());
229       break;
230     case SingleLocK:
231       ID.AddInteger(R.getBegin().getRawEncoding());
232       break;
233     case StmtK:
234       ID.Add(S);
235       break;
236     case DeclK:
237       ID.Add(D);
238       break;
239   }
240   return;
241 }
242 
243 void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const {
244   ID.AddInteger((unsigned) getKind());
245   ID.AddString(str);
246   // FIXME: Add profiling support for code hints.
247   ID.AddInteger((unsigned) getDisplayHint());
248   for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) {
249     ID.AddInteger(I->getBegin().getRawEncoding());
250     ID.AddInteger(I->getEnd().getRawEncoding());
251   }
252 }
253 
254 void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const {
255   PathDiagnosticPiece::Profile(ID);
256   ID.Add(Pos);
257 }
258 
259 void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const {
260   PathDiagnosticPiece::Profile(ID);
261   for (const_iterator I = begin(), E = end(); I != E; ++I)
262     ID.Add(*I);
263 }
264 
265 void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const {
266   PathDiagnosticSpotPiece::Profile(ID);
267   for (const_iterator I = begin(), E = end(); I != E; ++I)
268     ID.Add(**I);
269 }
270 
271 void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const {
272   ID.AddInteger(Size);
273   ID.AddString(BugType);
274   ID.AddString(Desc);
275   ID.AddString(Category);
276   for (const_iterator I = begin(), E = end(); I != E; ++I)
277     ID.Add(*I);
278 
279   for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I)
280     ID.AddString(*I);
281 }
282