clang  3.7.0
PlistDiagnostics.cpp
Go to the documentation of this file.
1 //===--- PlistDiagnostics.cpp - Plist Diagnostics for Paths -----*- 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 PlistDiagnostics object.
11 //
12 //===----------------------------------------------------------------------===//
13 
18 #include "clang/Basic/Version.h"
19 #include "clang/Lex/Preprocessor.h"
22 #include "llvm/ADT/DenseMap.h"
23 #include "llvm/ADT/SmallVector.h"
24 #include "llvm/Support/Casting.h"
25 using namespace clang;
26 using namespace ento;
27 using namespace markup;
28 
29 namespace {
30  class PlistDiagnostics : public PathDiagnosticConsumer {
31  const std::string OutputFile;
32  const LangOptions &LangOpts;
33  const bool SupportsCrossFileDiagnostics;
34  public:
35  PlistDiagnostics(AnalyzerOptions &AnalyzerOpts,
36  const std::string& prefix,
37  const LangOptions &LangOpts,
38  bool supportsMultipleFiles);
39 
40  ~PlistDiagnostics() override {}
41 
42  void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
43  FilesMade *filesMade) override;
44 
45  StringRef getName() const override {
46  return "PlistDiagnostics";
47  }
48 
49  PathGenerationScheme getGenerationScheme() const override {
50  return Extensive;
51  }
52  bool supportsLogicalOpControlFlow() const override { return true; }
53  bool supportsCrossFileDiagnostics() const override {
54  return SupportsCrossFileDiagnostics;
55  }
56  };
57 } // end anonymous namespace
58 
59 PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts,
60  const std::string& output,
61  const LangOptions &LO,
62  bool supportsMultipleFiles)
63  : OutputFile(output),
64  LangOpts(LO),
65  SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
66 
67 void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
69  const std::string& s,
70  const Preprocessor &PP) {
71  C.push_back(new PlistDiagnostics(AnalyzerOpts, s,
72  PP.getLangOpts(), false));
73 }
74 
75 void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
77  const std::string &s,
78  const Preprocessor &PP) {
79  C.push_back(new PlistDiagnostics(AnalyzerOpts, s,
80  PP.getLangOpts(), true));
81 }
82 
83 static void ReportControlFlow(raw_ostream &o,
85  const FIDMap& FM,
86  const SourceManager &SM,
87  const LangOptions &LangOpts,
88  unsigned indent) {
89 
90  Indent(o, indent) << "<dict>\n";
91  ++indent;
92 
93  Indent(o, indent) << "<key>kind</key><string>control</string>\n";
94 
95  // Emit edges.
96  Indent(o, indent) << "<key>edges</key>\n";
97  ++indent;
98  Indent(o, indent) << "<array>\n";
99  ++indent;
101  I!=E; ++I) {
102  Indent(o, indent) << "<dict>\n";
103  ++indent;
104 
105  // Make the ranges of the start and end point self-consistent with adjacent edges
106  // by forcing to use only the beginning of the range. This simplifies the layout
107  // logic for clients.
108  Indent(o, indent) << "<key>start</key>\n";
109  SourceRange StartEdge(
110  SM.getExpansionLoc(I->getStart().asRange().getBegin()));
111  EmitRange(o, SM, Lexer::getAsCharRange(StartEdge, SM, LangOpts), FM,
112  indent + 1);
113 
114  Indent(o, indent) << "<key>end</key>\n";
115  SourceRange EndEdge(SM.getExpansionLoc(I->getEnd().asRange().getBegin()));
116  EmitRange(o, SM, Lexer::getAsCharRange(EndEdge, SM, LangOpts), FM,
117  indent + 1);
118 
119  --indent;
120  Indent(o, indent) << "</dict>\n";
121  }
122  --indent;
123  Indent(o, indent) << "</array>\n";
124  --indent;
125 
126  // Output any helper text.
127  const std::string& s = P.getString();
128  if (!s.empty()) {
129  Indent(o, indent) << "<key>alternate</key>";
130  EmitString(o, s) << '\n';
131  }
132 
133  --indent;
134  Indent(o, indent) << "</dict>\n";
135 }
136 
137 static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P,
138  const FIDMap& FM,
139  const SourceManager &SM,
140  const LangOptions &LangOpts,
141  unsigned indent,
142  unsigned depth,
143  bool isKeyEvent = false) {
144 
145  Indent(o, indent) << "<dict>\n";
146  ++indent;
147 
148  Indent(o, indent) << "<key>kind</key><string>event</string>\n";
149 
150  if (isKeyEvent) {
151  Indent(o, indent) << "<key>key_event</key><true/>\n";
152  }
153 
154  // Output the location.
156 
157  Indent(o, indent) << "<key>location</key>\n";
158  EmitLocation(o, SM, L, FM, indent);
159 
160  // Output the ranges (if any).
162 
163  if (!Ranges.empty()) {
164  Indent(o, indent) << "<key>ranges</key>\n";
165  Indent(o, indent) << "<array>\n";
166  ++indent;
167  for (auto &R : Ranges)
168  EmitRange(o, SM,
169  Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts),
170  FM, indent + 1);
171  --indent;
172  Indent(o, indent) << "</array>\n";
173  }
174 
175  // Output the call depth.
176  Indent(o, indent) << "<key>depth</key>";
177  EmitInteger(o, depth) << '\n';
178 
179  // Output the text.
180  assert(!P.getString().empty());
181  Indent(o, indent) << "<key>extended_message</key>\n";
182  Indent(o, indent);
183  EmitString(o, P.getString()) << '\n';
184 
185  // Output the short text.
186  // FIXME: Really use a short string.
187  Indent(o, indent) << "<key>message</key>\n";
188  Indent(o, indent);
189  EmitString(o, P.getString()) << '\n';
190 
191  // Finish up.
192  --indent;
193  Indent(o, indent); o << "</dict>\n";
194 }
195 
196 static void ReportPiece(raw_ostream &o,
197  const PathDiagnosticPiece &P,
198  const FIDMap& FM, const SourceManager &SM,
199  const LangOptions &LangOpts,
200  unsigned indent,
201  unsigned depth,
202  bool includeControlFlow,
203  bool isKeyEvent = false);
204 
205 static void ReportCall(raw_ostream &o,
206  const PathDiagnosticCallPiece &P,
207  const FIDMap& FM, const SourceManager &SM,
208  const LangOptions &LangOpts,
209  unsigned indent,
210  unsigned depth) {
211 
213  P.getCallEnterEvent();
214 
215  if (callEnter)
216  ReportPiece(o, *callEnter, FM, SM, LangOpts, indent, depth, true,
218 
219  IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithinCaller =
221 
222  ++depth;
223 
224  if (callEnterWithinCaller)
225  ReportPiece(o, *callEnterWithinCaller, FM, SM, LangOpts,
226  indent, depth, true);
227 
228  for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I)
229  ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, true);
230 
231  --depth;
232 
234  P.getCallExitEvent();
235 
236  if (callExit)
237  ReportPiece(o, *callExit, FM, SM, LangOpts, indent, depth, true);
238 }
239 
240 static void ReportMacro(raw_ostream &o,
242  const FIDMap& FM, const SourceManager &SM,
243  const LangOptions &LangOpts,
244  unsigned indent,
245  unsigned depth) {
246 
247  for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end();
248  I!=E; ++I) {
249  ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, false);
250  }
251 }
252 
253 static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P,
254  const FIDMap& FM, const SourceManager &SM,
255  const LangOptions &LangOpts) {
256  ReportPiece(o, P, FM, SM, LangOpts, 4, 0, true);
257 }
258 
259 static void ReportPiece(raw_ostream &o,
260  const PathDiagnosticPiece &P,
261  const FIDMap& FM, const SourceManager &SM,
262  const LangOptions &LangOpts,
263  unsigned indent,
264  unsigned depth,
265  bool includeControlFlow,
266  bool isKeyEvent) {
267  switch (P.getKind()) {
268  case PathDiagnosticPiece::ControlFlow:
269  if (includeControlFlow)
270  ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM,
271  LangOpts, indent);
272  break;
273  case PathDiagnosticPiece::Call:
274  ReportCall(o, cast<PathDiagnosticCallPiece>(P), FM, SM, LangOpts,
275  indent, depth);
276  break;
277  case PathDiagnosticPiece::Event:
278  ReportEvent(o, cast<PathDiagnosticSpotPiece>(P), FM, SM, LangOpts,
279  indent, depth, isKeyEvent);
280  break;
281  case PathDiagnosticPiece::Macro:
282  ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts,
283  indent, depth);
284  break;
285  }
286 }
287 
288 void PlistDiagnostics::FlushDiagnosticsImpl(
289  std::vector<const PathDiagnostic *> &Diags,
290  FilesMade *filesMade) {
291  // Build up a set of FIDs that we use by scanning the locations and
292  // ranges of the diagnostics.
293  FIDMap FM;
295  const SourceManager* SM = nullptr;
296 
297  if (!Diags.empty())
298  SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager();
299 
300 
301  for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(),
302  DE = Diags.end(); DI != DE; ++DI) {
303 
304  const PathDiagnostic *D = *DI;
305 
307  WorkList.push_back(&D->path);
308 
309  while (!WorkList.empty()) {
310  const PathPieces &path = *WorkList.pop_back_val();
311 
312  for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E;
313  ++I) {
314  const PathDiagnosticPiece *piece = I->get();
315  AddFID(FM, Fids, *SM, piece->getLocation().asLocation());
317  for (ArrayRef<SourceRange>::iterator I = Ranges.begin(),
318  E = Ranges.end(); I != E; ++I) {
319  AddFID(FM, Fids, *SM, I->getBegin());
320  AddFID(FM, Fids, *SM, I->getEnd());
321  }
322 
323  if (const PathDiagnosticCallPiece *call =
324  dyn_cast<PathDiagnosticCallPiece>(piece)) {
326  callEnterWithin = call->getCallEnterWithinCallerEvent();
327  if (callEnterWithin)
328  AddFID(FM, Fids, *SM, callEnterWithin->getLocation().asLocation());
329 
330  WorkList.push_back(&call->path);
331  }
332  else if (const PathDiagnosticMacroPiece *macro =
333  dyn_cast<PathDiagnosticMacroPiece>(piece)) {
334  WorkList.push_back(&macro->subPieces);
335  }
336  }
337  }
338  }
339 
340  // Open the file.
341  std::error_code EC;
342  llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::F_Text);
343  if (EC) {
344  llvm::errs() << "warning: could not create file: " << EC.message() << '\n';
345  return;
346  }
347 
348  EmitPlistHeader(o);
349 
350  // Write the root object: a <dict> containing...
351  // - "clang_version", the string representation of clang version
352  // - "files", an <array> mapping from FIDs to file names
353  // - "diagnostics", an <array> containing the path diagnostics
354  o << "<dict>\n" <<
355  " <key>clang_version</key>\n";
356  EmitString(o, getClangFullVersion()) << '\n';
357  o << " <key>files</key>\n"
358  " <array>\n";
359 
360  for (FileID FID : Fids)
361  EmitString(o << " ", SM->getFileEntryForID(FID)->getName()) << '\n';
362 
363  o << " </array>\n"
364  " <key>diagnostics</key>\n"
365  " <array>\n";
366 
367  for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(),
368  DE = Diags.end(); DI!=DE; ++DI) {
369 
370  o << " <dict>\n"
371  " <key>path</key>\n";
372 
373  const PathDiagnostic *D = *DI;
374 
375  o << " <array>\n";
376 
377  for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end();
378  I != E; ++I)
379  ReportDiag(o, **I, FM, *SM, LangOpts);
380 
381  o << " </array>\n";
382 
383  // Output the bug type and bug category.
384  o << " <key>description</key>";
385  EmitString(o, D->getShortDescription()) << '\n';
386  o << " <key>category</key>";
387  EmitString(o, D->getCategory()) << '\n';
388  o << " <key>type</key>";
389  EmitString(o, D->getBugType()) << '\n';
390  o << " <key>check_name</key>";
391  EmitString(o, D->getCheckName()) << '\n';
392 
393  // Output information about the semantic context where
394  // the issue occurred.
395  if (const Decl *DeclWithIssue = D->getDeclWithIssue()) {
396  // FIXME: handle blocks, which have no name.
397  if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) {
398  StringRef declKind;
399  switch (ND->getKind()) {
400  case Decl::CXXRecord:
401  declKind = "C++ class";
402  break;
403  case Decl::CXXMethod:
404  declKind = "C++ method";
405  break;
406  case Decl::ObjCMethod:
407  declKind = "Objective-C method";
408  break;
409  case Decl::Function:
410  declKind = "function";
411  break;
412  default:
413  break;
414  }
415  if (!declKind.empty()) {
416  const std::string &declName = ND->getDeclName().getAsString();
417  o << " <key>issue_context_kind</key>";
418  EmitString(o, declKind) << '\n';
419  o << " <key>issue_context</key>";
420  EmitString(o, declName) << '\n';
421  }
422 
423  // Output the bug hash for issue unique-ing. Currently, it's just an
424  // offset from the beginning of the function.
425  if (const Stmt *Body = DeclWithIssue->getBody()) {
426 
427  // If the bug uniqueing location exists, use it for the hash.
428  // For example, this ensures that two leaks reported on the same line
429  // will have different issue_hashes and that the hash will identify
430  // the leak location even after code is added between the allocation
431  // site and the end of scope (leak report location).
433  if (UPDLoc.isValid()) {
434  FullSourceLoc UL(SM->getExpansionLoc(UPDLoc.asLocation()),
435  *SM);
436  FullSourceLoc UFunL(SM->getExpansionLoc(
437  D->getUniqueingDecl()->getBody()->getLocStart()), *SM);
438  o << " <key>issue_hash</key><string>"
439  << UL.getExpansionLineNumber() - UFunL.getExpansionLineNumber()
440  << "</string>\n";
441 
442  // Otherwise, use the location on which the bug is reported.
443  } else {
445  *SM);
446  FullSourceLoc FunL(SM->getExpansionLoc(Body->getLocStart()), *SM);
447  o << " <key>issue_hash</key><string>"
448  << L.getExpansionLineNumber() - FunL.getExpansionLineNumber()
449  << "</string>\n";
450  }
451 
452  }
453  }
454  }
455 
456  // Output the location of the bug.
457  o << " <key>location</key>\n";
458  EmitLocation(o, *SM, D->getLocation().asLocation(), FM, 2);
459 
460  // Output the diagnostic to the sub-diagnostic client, if any.
461  if (!filesMade->empty()) {
462  StringRef lastName;
463  PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D);
464  if (files) {
465  for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(),
466  CE = files->end(); CI != CE; ++CI) {
467  StringRef newName = CI->first;
468  if (newName != lastName) {
469  if (!lastName.empty()) {
470  o << " </array>\n";
471  }
472  lastName = newName;
473  o << " <key>" << lastName << "_files</key>\n";
474  o << " <array>\n";
475  }
476  o << " <string>" << CI->second << "</string>\n";
477  }
478  o << " </array>\n";
479  }
480  }
481 
482  // Close up the entry.
483  o << " </dict>\n";
484  }
485 
486  o << " </array>\n";
487 
488  // Finish.
489  o << "</dict>\n</plist>";
490 }
PathDiagnosticLocation getUniqueingLoc() const
Get the location on which the report should be uniqued.
static void ReportPiece(raw_ostream &o, const PathDiagnosticPiece &P, const FIDMap &FM, const SourceManager &SM, const LangOptions &LangOpts, unsigned indent, unsigned depth, bool includeControlFlow, bool isKeyEvent=false)
void AddFID(FIDMap &FIDs, SmallVectorImpl< FileID > &V, const SourceManager &SM, SourceLocation L)
Definition: PlistSupport.h:21
void EmitLocation(raw_ostream &o, const SourceManager &SM, SourceLocation L, const FIDMap &FM, unsigned indent)
Definition: PlistSupport.h:90
Defines the clang::FileManager interface and associated types.
std::string getClangFullVersion()
Retrieves a string representing the complete clang version, which includes the clang version number...
Definition: Version.cpp:118
Defines the SourceManager interface.
StringRef getCategory() const
ArrayRef< SourceRange > getRanges() const
Return the SourceRanges associated with this PathDiagnosticPiece.
PathDiagnosticLocation getLocation() const
static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece &P, const FIDMap &FM, const SourceManager &SM, const LangOptions &LangOpts, unsigned indent, unsigned depth, bool isKeyEvent=false)
const Decl * getDeclWithIssue() const
raw_ostream & EmitInteger(raw_ostream &o, int64_t value)
Definition: PlistSupport.h:54
FullSourceLoc asLocation() const
std::pair< SourceLocation, SourceLocation > getExpansionRange(SourceLocation Loc) const
Given a SourceLocation object, return the range of tokens covered by the expansion in the ultimate fi...
const LangOptions & getLangOpts() const
Definition: Preprocessor.h:679
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:48
SmallVector< CharSourceRange, 8 > Ranges
Definition: Format.cpp:1554
std::vector< PathDiagnosticLocationPair >::const_iterator const_iterator
IntrusiveRefCntPtr< PathDiagnosticEventPiece > getCallExitEvent() const
virtual PathDiagnosticLocation getLocation() const =0
AnnotatingParser & P
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
IntrusiveRefCntPtr< PathDiagnosticEventPiece > getCallEnterWithinCallerEvent() const
SourceManager & SM
static void ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P, const FIDMap &FM, const SourceManager &SM, const LangOptions &LangOpts, unsigned indent, unsigned depth)
StringRef getShortDescription() const
Defines version macros and version-related utility functions for Clang.
Defines the clang::Preprocessor interface.
IntrusiveRefCntPtr< PathDiagnosticEventPiece > getCallEnterEvent() const
static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece &P, const FIDMap &FM, const SourceManager &SM, const LangOptions &LangOpts)
const char * getName() const
Definition: FileManager.h:84
std::vector< PathDiagnosticConsumer * > PathDiagnosticConsumers
virtual Stmt * getBody() const
Definition: DeclBase.h:840
const Decl * getUniqueingDecl() const
Get the declaration containing the uniqueing location.
llvm::DenseMap< FileID, unsigned > FIDMap
Definition: PlistSupport.h:19
raw_ostream & EmitPlistHeader(raw_ostream &o)
Definition: PlistSupport.h:45
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
void EmitRange(raw_ostream &o, const SourceManager &SM, CharSourceRange R, const FIDMap &FM, unsigned indent)
Definition: PlistSupport.h:106
raw_ostream & EmitString(raw_ostream &o, StringRef s)
Definition: PlistSupport.h:61
static void ReportControlFlow(raw_ostream &o, const PathDiagnosticControlFlowPiece &P, const FIDMap &FM, const SourceManager &SM, const LangOptions &LangOpts, unsigned indent)
static void ReportMacro(raw_ostream &o, const PathDiagnosticMacroPiece &P, const FIDMap &FM, const SourceManager &SM, const LangOptions &LangOpts, unsigned indent, unsigned depth)
A SourceLocation and its associated SourceManager.
StringRef getBugType() const
A trivial tuple used to represent a source range.
SourceLocation getExpansionLoc(SourceLocation Loc) const
Given a SourceLocation object Loc, return the expansion location referenced by the ID...
StringRef getCheckName() const
This class handles loading and caching of source files into memory.
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
Definition: Preprocessor.h:96
unsigned Indent
The current line's indent.