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