clang  3.9.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 
17 #include "clang/Basic/Version.h"
18 #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 auto &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).
161  ArrayRef<SourceRange> Ranges = P.getRanges();
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.front()->path.front()->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());
316  ArrayRef<SourceRange> Ranges = piece->getRanges();
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  o << " <!-- This hash is experimental and going to change! -->\n";
394  o << " <key>issue_hash_content_of_line_in_context</key>";
396  FullSourceLoc L(SM->getExpansionLoc(UPDLoc.isValid()
397  ? UPDLoc.asLocation()
398  : D->getLocation().asLocation()),
399  *SM);
400  const Decl *DeclWithIssue = D->getDeclWithIssue();
401  EmitString(o, GetIssueHash(*SM, L, D->getCheckName(), D->getBugType(),
402  DeclWithIssue, LangOpts))
403  << '\n';
404 
405  // Output information about the semantic context where
406  // the issue occurred.
407  if (const Decl *DeclWithIssue = D->getDeclWithIssue()) {
408  // FIXME: handle blocks, which have no name.
409  if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) {
410  StringRef declKind;
411  switch (ND->getKind()) {
412  case Decl::CXXRecord:
413  declKind = "C++ class";
414  break;
415  case Decl::CXXMethod:
416  declKind = "C++ method";
417  break;
418  case Decl::ObjCMethod:
419  declKind = "Objective-C method";
420  break;
421  case Decl::Function:
422  declKind = "function";
423  break;
424  default:
425  break;
426  }
427  if (!declKind.empty()) {
428  const std::string &declName = ND->getDeclName().getAsString();
429  o << " <key>issue_context_kind</key>";
430  EmitString(o, declKind) << '\n';
431  o << " <key>issue_context</key>";
432  EmitString(o, declName) << '\n';
433  }
434 
435  // Output the bug hash for issue unique-ing. Currently, it's just an
436  // offset from the beginning of the function.
437  if (const Stmt *Body = DeclWithIssue->getBody()) {
438 
439  // If the bug uniqueing location exists, use it for the hash.
440  // For example, this ensures that two leaks reported on the same line
441  // will have different issue_hashes and that the hash will identify
442  // the leak location even after code is added between the allocation
443  // site and the end of scope (leak report location).
444  if (UPDLoc.isValid()) {
445  FullSourceLoc UFunL(SM->getExpansionLoc(
446  D->getUniqueingDecl()->getBody()->getLocStart()), *SM);
447  o << " <key>issue_hash_function_offset</key><string>"
448  << L.getExpansionLineNumber() - UFunL.getExpansionLineNumber()
449  << "</string>\n";
450 
451  // Otherwise, use the location on which the bug is reported.
452  } else {
453  FullSourceLoc FunL(SM->getExpansionLoc(Body->getLocStart()), *SM);
454  o << " <key>issue_hash_function_offset</key><string>"
455  << L.getExpansionLineNumber() - FunL.getExpansionLineNumber()
456  << "</string>\n";
457  }
458 
459  }
460  }
461  }
462 
463  // Output the location of the bug.
464  o << " <key>location</key>\n";
465  EmitLocation(o, *SM, D->getLocation().asLocation(), FM, 2);
466 
467  // Output the diagnostic to the sub-diagnostic client, if any.
468  if (!filesMade->empty()) {
469  StringRef lastName;
470  PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D);
471  if (files) {
472  for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(),
473  CE = files->end(); CI != CE; ++CI) {
474  StringRef newName = CI->first;
475  if (newName != lastName) {
476  if (!lastName.empty()) {
477  o << " </array>\n";
478  }
479  lastName = newName;
480  o << " <key>" << lastName << "_files</key>\n";
481  o << " <array>\n";
482  }
483  o << " <string>" << CI->second << "</string>\n";
484  }
485  o << " </array>\n";
486  }
487  }
488 
489  // Close up the entry.
490  o << " </dict>\n";
491  }
492 
493  o << " </array>\n";
494 
495  // Finish.
496  o << "</dict>\n</plist>";
497 }
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
StringRef P
llvm::SmallString< 32 > GetIssueHash(const SourceManager &SM, FullSourceLoc &IssueLoc, llvm::StringRef CheckerName, llvm::StringRef BugType, const Decl *D, const LangOptions &LangOpts)
Get an MD5 hash to help identify bugs.
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
Return the semantic context where an issue occurred.
raw_ostream & EmitInteger(raw_ostream &o, int64_t value)
Definition: PlistSupport.h:54
FullSourceLoc asLocation() const
PathDiagnostic - PathDiagnostic objects represent a single path-sensitive diagnostic.
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:690
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:48
std::vector< PathDiagnosticLocationPair >::const_iterator const_iterator
IntrusiveRefCntPtr< PathDiagnosticEventPiece > getCallExitEvent() const
detail::InMemoryDirectory::const_iterator I
virtual PathDiagnosticLocation getLocation() const =0
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
IntrusiveRefCntPtr< PathDiagnosticEventPiece > getCallEnterWithinCallerEvent() const
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 SourceManager & SM
Definition: Format.cpp:1184
const char * getName() const
Definition: FileManager.h:85
const TemplateArgument * iterator
Definition: Type.h:4233
std::vector< PathDiagnosticConsumer * > PathDiagnosticConsumers
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...
detail::InMemoryDirectory::const_iterator E
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.
NamedDecl - This represents a decl with a name.
Definition: Decl.h:213
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:97
unsigned Indent
The current line's indent.