Coverage Report

Created: 2021-03-06 07:03

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
Line
Count
Source (jump to first uncovered line)
1
//===--- PlistDiagnostics.cpp - Plist Diagnostics for Paths -----*- C++ -*-===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
//  This file defines the PlistDiagnostics object.
10
//
11
//===----------------------------------------------------------------------===//
12
13
#include "clang/Analysis/IssueHash.h"
14
#include "clang/Analysis/MacroExpansionContext.h"
15
#include "clang/Analysis/PathDiagnostic.h"
16
#include "clang/Basic/FileManager.h"
17
#include "clang/Basic/PlistSupport.h"
18
#include "clang/Basic/SourceManager.h"
19
#include "clang/Basic/Version.h"
20
#include "clang/CrossTU/CrossTranslationUnit.h"
21
#include "clang/Frontend/ASTUnit.h"
22
#include "clang/Lex/Preprocessor.h"
23
#include "clang/Lex/TokenConcatenation.h"
24
#include "clang/Rewrite/Core/HTMLRewrite.h"
25
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
26
#include "llvm/ADT/SmallPtrSet.h"
27
#include "llvm/ADT/SmallVector.h"
28
#include "llvm/ADT/Statistic.h"
29
#include "llvm/Support/Casting.h"
30
#include <memory>
31
32
using namespace clang;
33
using namespace ento;
34
using namespace markup;
35
36
//===----------------------------------------------------------------------===//
37
// Declarations of helper classes and functions for emitting bug reports in
38
// plist format.
39
//===----------------------------------------------------------------------===//
40
41
namespace {
42
  class PlistDiagnostics : public PathDiagnosticConsumer {
43
    PathDiagnosticConsumerOptions DiagOpts;
44
    const std::string OutputFile;
45
    const Preprocessor &PP;
46
    const cross_tu::CrossTranslationUnitContext &CTU;
47
    const MacroExpansionContext &MacroExpansions;
48
    const bool SupportsCrossFileDiagnostics;
49
50
    void printBugPath(llvm::raw_ostream &o, const FIDMap &FM,
51
                      const PathPieces &Path);
52
53
  public:
54
    PlistDiagnostics(PathDiagnosticConsumerOptions DiagOpts,
55
                     const std::string &OutputFile, const Preprocessor &PP,
56
                     const cross_tu::CrossTranslationUnitContext &CTU,
57
                     const MacroExpansionContext &MacroExpansions,
58
                     bool supportsMultipleFiles);
59
60
67
    ~PlistDiagnostics() override {}
61
62
    void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
63
                              FilesMade *filesMade) override;
64
65
0
    StringRef getName() const override {
66
0
      return "PlistDiagnostics";
67
0
    }
68
69
23.8k
    PathGenerationScheme getGenerationScheme() const override {
70
23.8k
      return Extensive;
71
23.8k
    }
72
0
    bool supportsLogicalOpControlFlow() const override { return true; }
73
631
    bool supportsCrossFileDiagnostics() const override {
74
631
      return SupportsCrossFileDiagnostics;
75
631
    }
76
  };
77
} // end anonymous namespace
78
79
namespace {
80
81
/// A helper class for emitting a single report.
82
class PlistPrinter {
83
  const FIDMap& FM;
84
  const Preprocessor &PP;
85
  const cross_tu::CrossTranslationUnitContext &CTU;
86
  const MacroExpansionContext &MacroExpansions;
87
  llvm::SmallVector<const PathDiagnosticMacroPiece *, 0> MacroPieces;
88
89
public:
90
  PlistPrinter(const FIDMap &FM, const Preprocessor &PP,
91
               const cross_tu::CrossTranslationUnitContext &CTU,
92
               const MacroExpansionContext &MacroExpansions)
93
625
      : FM(FM), PP(PP), CTU(CTU), MacroExpansions(MacroExpansions) {}
94
95
3.49k
  void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P) {
96
3.49k
    ReportPiece(o, P, /*indent*/ 4, /*depth*/ 0, /*includeControlFlow*/ true);
97
3.49k
  }
98
99
  /// Print the expansions of the collected macro pieces.
100
  ///
101
  /// Each time ReportDiag is called on a PathDiagnosticMacroPiece (or, if one
102
  /// is found through a call piece, etc), it's subpieces are reported, and the
103
  /// piece itself is collected. Call this function after the entire bugpath
104
  /// was reported.
105
  void ReportMacroExpansions(raw_ostream &o, unsigned indent);
106
107
private:
108
  void ReportPiece(raw_ostream &o, const PathDiagnosticPiece &P,
109
                   unsigned indent, unsigned depth, bool includeControlFlow,
110
4.44k
                   bool isKeyEvent = false) {
111
4.44k
    switch (P.getKind()) {
112
2.22k
      case PathDiagnosticPiece::ControlFlow:
113
2.22k
        if (includeControlFlow)
114
2.17k
          ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), indent);
115
2.22k
        break;
116
148
      case PathDiagnosticPiece::Call:
117
148
        ReportCall(o, cast<PathDiagnosticCallPiece>(P), indent,
118
148
                   depth);
119
148
        break;
120
1.99k
      case PathDiagnosticPiece::Event:
121
1.99k
        ReportEvent(o, cast<PathDiagnosticEventPiece>(P), indent, depth,
122
1.99k
                    isKeyEvent);
123
1.99k
        break;
124
46
      case PathDiagnosticPiece::Macro:
125
46
        ReportMacroSubPieces(o, cast<PathDiagnosticMacroPiece>(P), indent,
126
46
                             depth);
127
46
        break;
128
1
      case PathDiagnosticPiece::Note:
129
1
        ReportNote(o, cast<PathDiagnosticNotePiece>(P), indent);
130
1
        break;
131
33
      case PathDiagnosticPiece::PopUp:
132
33
        ReportPopUp(o, cast<PathDiagnosticPopUpPiece>(P), indent);
133
33
        break;
134
4.44k
    }
135
4.44k
  }
136
137
  void EmitRanges(raw_ostream &o, const ArrayRef<SourceRange> Ranges,
138
                  unsigned indent);
139
  void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent);
140
  void EmitFixits(raw_ostream &o, ArrayRef<FixItHint> fixits, unsigned indent);
141
142
  void ReportControlFlow(raw_ostream &o,
143
                         const PathDiagnosticControlFlowPiece& P,
144
                         unsigned indent);
145
  void ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P,
146
                   unsigned indent, unsigned depth, bool isKeyEvent = false);
147
  void ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P,
148
                  unsigned indent, unsigned depth);
149
  void ReportMacroSubPieces(raw_ostream &o, const PathDiagnosticMacroPiece& P,
150
                            unsigned indent, unsigned depth);
151
  void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P,
152
                  unsigned indent);
153
154
  void ReportPopUp(raw_ostream &o, const PathDiagnosticPopUpPiece &P,
155
                   unsigned indent);
156
};
157
158
} // end of anonymous namespace
159
160
/// Print coverage information to output stream {@code o}.
161
/// May modify the used list of files {@code Fids} by inserting new ones.
162
static void printCoverage(const PathDiagnostic *D,
163
                          unsigned InputIndentLevel,
164
                          SmallVectorImpl<FileID> &Fids,
165
                          FIDMap &FM,
166
                          llvm::raw_fd_ostream &o);
167
168
static Optional<StringRef> getExpandedMacro(
169
    SourceLocation MacroLoc, const cross_tu::CrossTranslationUnitContext &CTU,
170
    const MacroExpansionContext &MacroExpansions, const SourceManager &SM);
171
172
//===----------------------------------------------------------------------===//
173
// Methods of PlistPrinter.
174
//===----------------------------------------------------------------------===//
175
176
void PlistPrinter::EmitRanges(raw_ostream &o,
177
                              const ArrayRef<SourceRange> Ranges,
178
2.06k
                              unsigned indent) {
179
180
2.06k
  if (Ranges.empty())
181
288
    return;
182
183
1.77k
  Indent(o, indent) << "<key>ranges</key>\n";
184
1.77k
  Indent(o, indent) << "<array>\n";
185
1.77k
  ++indent;
186
187
1.77k
  const SourceManager &SM = PP.getSourceManager();
188
1.77k
  const LangOptions &LangOpts = PP.getLangOpts();
189
190
1.77k
  for (auto &R : Ranges)
191
1.90k
    EmitRange(o, SM,
192
1.90k
              Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts),
193
1.90k
              FM, indent + 1);
194
1.77k
  --indent;
195
1.77k
  Indent(o, indent) << "</array>\n";
196
1.77k
}
197
198
void PlistPrinter::EmitMessage(raw_ostream &o, StringRef Message,
199
2.02k
                               unsigned indent) {
200
  // Output the text.
201
2.02k
  assert(!Message.empty());
202
0
  Indent(o, indent) << "<key>extended_message</key>\n";
203
2.02k
  Indent(o, indent);
204
2.02k
  EmitString(o, Message) << '\n';
205
206
  // Output the short text.
207
  // FIXME: Really use a short string.
208
2.02k
  Indent(o, indent) << "<key>message</key>\n";
209
2.02k
  Indent(o, indent);
210
2.02k
  EmitString(o, Message) << '\n';
211
2.02k
}
212
213
void PlistPrinter::EmitFixits(raw_ostream &o, ArrayRef<FixItHint> fixits,
214
1.99k
                              unsigned indent) {
215
1.99k
  if (fixits.size() == 0)
216
1.99k
    return;
217
218
1
  const SourceManager &SM = PP.getSourceManager();
219
1
  const LangOptions &LangOpts = PP.getLangOpts();
220
221
1
  Indent(o, indent) << "<key>fixits</key>\n";
222
1
  Indent(o, indent) << "<array>\n";
223
1
  for (const auto &fixit : fixits) {
224
1
    assert(!fixit.isNull());
225
    // FIXME: Add support for InsertFromRange and BeforePreviousInsertion.
226
0
    assert(!fixit.InsertFromRange.isValid() && "Not implemented yet!");
227
0
    assert(!fixit.BeforePreviousInsertions && "Not implemented yet!");
228
0
    Indent(o, indent) << " <dict>\n";
229
1
    Indent(o, indent) << "  <key>remove_range</key>\n";
230
1
    EmitRange(o, SM, Lexer::getAsCharRange(fixit.RemoveRange, SM, LangOpts),
231
1
              FM, indent + 2);
232
1
    Indent(o, indent) << "  <key>insert_string</key>";
233
1
    EmitString(o, fixit.CodeToInsert);
234
1
    o << "\n";
235
1
    Indent(o, indent) << " </dict>\n";
236
1
  }
237
1
  Indent(o, indent) << "</array>\n";
238
1
}
239
240
void PlistPrinter::ReportControlFlow(raw_ostream &o,
241
                                     const PathDiagnosticControlFlowPiece& P,
242
2.17k
                                     unsigned indent) {
243
244
2.17k
  const SourceManager &SM = PP.getSourceManager();
245
2.17k
  const LangOptions &LangOpts = PP.getLangOpts();
246
247
2.17k
  Indent(o, indent) << "<dict>\n";
248
2.17k
  ++indent;
249
250
2.17k
  Indent(o, indent) << "<key>kind</key><string>control</string>\n";
251
252
  // Emit edges.
253
2.17k
  Indent(o, indent) << "<key>edges</key>\n";
254
2.17k
  ++indent;
255
2.17k
  Indent(o, indent) << "<array>\n";
256
2.17k
  ++indent;
257
2.17k
  for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end();
258
4.35k
       I!=E; 
++I2.17k
) {
259
2.17k
    Indent(o, indent) << "<dict>\n";
260
2.17k
    ++indent;
261
262
    // Make the ranges of the start and end point self-consistent with adjacent edges
263
    // by forcing to use only the beginning of the range.  This simplifies the layout
264
    // logic for clients.
265
2.17k
    Indent(o, indent) << "<key>start</key>\n";
266
2.17k
    SourceRange StartEdge(
267
2.17k
        SM.getExpansionLoc(I->getStart().asRange().getBegin()));
268
2.17k
    EmitRange(o, SM, Lexer::getAsCharRange(StartEdge, SM, LangOpts), FM,
269
2.17k
              indent + 1);
270
271
2.17k
    Indent(o, indent) << "<key>end</key>\n";
272
2.17k
    SourceRange EndEdge(SM.getExpansionLoc(I->getEnd().asRange().getBegin()));
273
2.17k
    EmitRange(o, SM, Lexer::getAsCharRange(EndEdge, SM, LangOpts), FM,
274
2.17k
              indent + 1);
275
276
2.17k
    --indent;
277
2.17k
    Indent(o, indent) << "</dict>\n";
278
2.17k
  }
279
2.17k
  --indent;
280
2.17k
  Indent(o, indent) << "</array>\n";
281
2.17k
  --indent;
282
283
  // Output any helper text.
284
2.17k
  const auto &s = P.getString();
285
2.17k
  if (!s.empty()) {
286
0
    Indent(o, indent) << "<key>alternate</key>";
287
0
    EmitString(o, s) << '\n';
288
0
  }
289
290
2.17k
  assert(P.getFixits().size() == 0 &&
291
2.17k
         "Fixits on constrol flow pieces are not implemented yet!");
292
293
0
  --indent;
294
2.17k
  Indent(o, indent) << "</dict>\n";
295
2.17k
}
296
297
void PlistPrinter::ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P,
298
                               unsigned indent, unsigned depth,
299
1.99k
                               bool isKeyEvent) {
300
301
1.99k
  const SourceManager &SM = PP.getSourceManager();
302
303
1.99k
  Indent(o, indent) << "<dict>\n";
304
1.99k
  ++indent;
305
306
1.99k
  Indent(o, indent) << "<key>kind</key><string>event</string>\n";
307
308
1.99k
  if (isKeyEvent) {
309
2
    Indent(o, indent) << "<key>key_event</key><true/>\n";
310
2
  }
311
312
  // Output the location.
313
1.99k
  FullSourceLoc L = P.getLocation().asLocation();
314
315
1.99k
  Indent(o, indent) << "<key>location</key>\n";
316
1.99k
  EmitLocation(o, SM, L, FM, indent);
317
318
  // Output the ranges (if any).
319
1.99k
  ArrayRef<SourceRange> Ranges = P.getRanges();
320
1.99k
  EmitRanges(o, Ranges, indent);
321
322
  // Output the call depth.
323
1.99k
  Indent(o, indent) << "<key>depth</key>";
324
1.99k
  EmitInteger(o, depth) << '\n';
325
326
  // Output the text.
327
1.99k
  EmitMessage(o, P.getString(), indent);
328
329
  // Output the fixits.
330
1.99k
  EmitFixits(o, P.getFixits(), indent);
331
332
  // Finish up.
333
1.99k
  --indent;
334
1.99k
  Indent(o, indent); o << "</dict>\n";
335
1.99k
}
336
337
void PlistPrinter::ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P,
338
                              unsigned indent,
339
148
                              unsigned depth) {
340
341
148
  if (auto callEnter = P.getCallEnterEvent())
342
147
    ReportPiece(o, *callEnter, indent, depth, /*includeControlFlow*/ true,
343
147
                P.isLastInMainSourceFile());
344
345
346
148
  ++depth;
347
348
148
  if (auto callEnterWithinCaller = P.getCallEnterWithinCallerEvent())
349
136
    ReportPiece(o, *callEnterWithinCaller, indent, depth,
350
136
                /*includeControlFlow*/ true);
351
352
638
  for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;
++I490
)
353
490
    ReportPiece(o, **I, indent, depth, /*includeControlFlow*/ true);
354
355
148
  --depth;
356
357
148
  if (auto callExit = P.getCallExitEvent())
358
75
    ReportPiece(o, *callExit, indent, depth, /*includeControlFlow*/ true);
359
360
148
  assert(P.getFixits().size() == 0 &&
361
148
         "Fixits on call pieces are not implemented yet!");
362
148
}
363
364
void PlistPrinter::ReportMacroSubPieces(raw_ostream &o,
365
                                        const PathDiagnosticMacroPiece& P,
366
46
                                        unsigned indent, unsigned depth) {
367
46
  MacroPieces.push_back(&P);
368
369
46
  for (PathPieces::const_iterator I = P.subPieces.begin(),
370
46
                                  E = P.subPieces.end();
371
140
       I != E; 
++I94
) {
372
94
    ReportPiece(o, **I, indent, depth, /*includeControlFlow*/ false);
373
94
  }
374
375
46
  assert(P.getFixits().size() == 0 &&
376
46
         "Fixits on constrol flow pieces are not implemented yet!");
377
46
}
378
379
42
void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) {
380
381
46
  for (const PathDiagnosticMacroPiece *P : MacroPieces) {
382
46
    const SourceManager &SM = PP.getSourceManager();
383
384
46
    SourceLocation MacroExpansionLoc =
385
46
        P->getLocation().asLocation().getExpansionLoc();
386
387
46
    const Optional<StringRef> MacroName =
388
46
        MacroExpansions.getOriginalText(MacroExpansionLoc);
389
46
    const Optional<StringRef> ExpansionText =
390
46
        getExpandedMacro(MacroExpansionLoc, CTU, MacroExpansions, SM);
391
392
46
    if (!MacroName.hasValue() || 
!ExpansionText.hasValue()40
)
393
6
      continue;
394
395
40
    Indent(o, indent) << "<dict>\n";
396
40
    ++indent;
397
398
    // Output the location.
399
40
    FullSourceLoc L = P->getLocation().asLocation();
400
401
40
    Indent(o, indent) << "<key>location</key>\n";
402
40
    EmitLocation(o, SM, L, FM, indent);
403
404
    // Output the ranges (if any).
405
40
    ArrayRef<SourceRange> Ranges = P->getRanges();
406
40
    EmitRanges(o, Ranges, indent);
407
408
    // Output the macro name.
409
40
    Indent(o, indent) << "<key>name</key>";
410
40
    EmitString(o, MacroName.getValue()) << '\n';
411
412
    // Output what it expands into.
413
40
    Indent(o, indent) << "<key>expansion</key>";
414
40
    EmitString(o, ExpansionText.getValue()) << '\n';
415
416
    // Finish up.
417
40
    --indent;
418
40
    Indent(o, indent);
419
40
    o << "</dict>\n";
420
40
  }
421
42
}
422
423
void PlistPrinter::ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P,
424
1
                              unsigned indent) {
425
426
1
  const SourceManager &SM = PP.getSourceManager();
427
428
1
  Indent(o, indent) << "<dict>\n";
429
1
  ++indent;
430
431
  // Output the location.
432
1
  FullSourceLoc L = P.getLocation().asLocation();
433
434
1
  Indent(o, indent) << "<key>location</key>\n";
435
1
  EmitLocation(o, SM, L, FM, indent);
436
437
  // Output the ranges (if any).
438
1
  ArrayRef<SourceRange> Ranges = P.getRanges();
439
1
  EmitRanges(o, Ranges, indent);
440
441
  // Output the text.
442
1
  EmitMessage(o, P.getString(), indent);
443
444
  // Output the fixits.
445
1
  EmitFixits(o, P.getFixits(), indent);
446
447
  // Finish up.
448
1
  --indent;
449
1
  Indent(o, indent); o << "</dict>\n";
450
1
}
451
452
void PlistPrinter::ReportPopUp(raw_ostream &o,
453
                               const PathDiagnosticPopUpPiece &P,
454
33
                               unsigned indent) {
455
33
  const SourceManager &SM = PP.getSourceManager();
456
457
33
  Indent(o, indent) << "<dict>\n";
458
33
  ++indent;
459
460
33
  Indent(o, indent) << "<key>kind</key><string>pop-up</string>\n";
461
462
  // Output the location.
463
33
  FullSourceLoc L = P.getLocation().asLocation();
464
465
33
  Indent(o, indent) << "<key>location</key>\n";
466
33
  EmitLocation(o, SM, L, FM, indent);
467
468
  // Output the ranges (if any).
469
33
  ArrayRef<SourceRange> Ranges = P.getRanges();
470
33
  EmitRanges(o, Ranges, indent);
471
472
  // Output the text.
473
33
  EmitMessage(o, P.getString(), indent);
474
475
33
  assert(P.getFixits().size() == 0 &&
476
33
         "Fixits on pop-up pieces are not implemented yet!");
477
478
  // Finish up.
479
0
  --indent;
480
33
  Indent(o, indent) << "</dict>\n";
481
33
}
482
483
//===----------------------------------------------------------------------===//
484
// Static function definitions.
485
//===----------------------------------------------------------------------===//
486
487
/// Print coverage information to output stream {@code o}.
488
/// May modify the used list of files {@code Fids} by inserting new ones.
489
static void printCoverage(const PathDiagnostic *D,
490
                          unsigned InputIndentLevel,
491
                          SmallVectorImpl<FileID> &Fids,
492
                          FIDMap &FM,
493
625
                          llvm::raw_fd_ostream &o) {
494
625
  unsigned IndentLevel = InputIndentLevel;
495
496
625
  Indent(o, IndentLevel) << "<key>ExecutedLines</key>\n";
497
625
  Indent(o, IndentLevel) << "<dict>\n";
498
625
  IndentLevel++;
499
500
  // Mapping from file IDs to executed lines.
501
625
  const FilesToLineNumsMap &ExecutedLines = D->getExecutedLines();
502
1.26k
  for (auto I = ExecutedLines.begin(), E = ExecutedLines.end(); I != E; 
++I641
) {
503
641
    unsigned FileKey = AddFID(FM, Fids, I->first);
504
641
    Indent(o, IndentLevel) << "<key>" << FileKey << "</key>\n";
505
641
    Indent(o, IndentLevel) << "<array>\n";
506
641
    IndentLevel++;
507
3.37k
    for (unsigned LineNo : I->second) {
508
3.37k
      Indent(o, IndentLevel);
509
3.37k
      EmitInteger(o, LineNo) << "\n";
510
3.37k
    }
511
641
    IndentLevel--;
512
641
    Indent(o, IndentLevel) << "</array>\n";
513
641
  }
514
625
  IndentLevel--;
515
625
  Indent(o, IndentLevel) << "</dict>\n";
516
517
625
  assert(IndentLevel == InputIndentLevel);
518
625
}
519
520
//===----------------------------------------------------------------------===//
521
// Methods of PlistDiagnostics.
522
//===----------------------------------------------------------------------===//
523
524
PlistDiagnostics::PlistDiagnostics(
525
    PathDiagnosticConsumerOptions DiagOpts, const std::string &output,
526
    const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU,
527
    const MacroExpansionContext &MacroExpansions, bool supportsMultipleFiles)
528
    : DiagOpts(std::move(DiagOpts)), OutputFile(output), PP(PP), CTU(CTU),
529
      MacroExpansions(MacroExpansions),
530
67
      SupportsCrossFileDiagnostics(supportsMultipleFiles) {
531
  // FIXME: Will be used by a later planned change.
532
67
  (void)this->CTU;
533
67
}
534
535
void ento::createPlistDiagnosticConsumer(
536
    PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
537
    const std::string &OutputFile, const Preprocessor &PP,
538
    const cross_tu::CrossTranslationUnitContext &CTU,
539
45
    const MacroExpansionContext &MacroExpansions) {
540
541
  // TODO: Emit an error here.
542
45
  if (OutputFile.empty())
543
0
    return;
544
545
45
  C.push_back(new PlistDiagnostics(DiagOpts, OutputFile, PP, CTU,
546
45
                                   MacroExpansions,
547
45
                                   /*supportsMultipleFiles=*/false));
548
45
  createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, OutputFile,
549
45
                                          PP, CTU, MacroExpansions);
550
45
}
551
552
void ento::createPlistMultiFileDiagnosticConsumer(
553
    PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
554
    const std::string &OutputFile, const Preprocessor &PP,
555
    const cross_tu::CrossTranslationUnitContext &CTU,
556
22
    const MacroExpansionContext &MacroExpansions) {
557
558
  // TODO: Emit an error here.
559
22
  if (OutputFile.empty())
560
0
    return;
561
562
22
  C.push_back(new PlistDiagnostics(DiagOpts, OutputFile, PP, CTU,
563
22
                                   MacroExpansions,
564
22
                                   /*supportsMultipleFiles=*/true));
565
22
  createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, OutputFile,
566
22
                                          PP, CTU, MacroExpansions);
567
22
}
568
569
void PlistDiagnostics::printBugPath(llvm::raw_ostream &o, const FIDMap &FM,
570
625
                                    const PathPieces &Path) {
571
625
  PlistPrinter Printer(FM, PP, CTU, MacroExpansions);
572
625
  assert(std::is_partitioned(Path.begin(), Path.end(),
573
625
                             [](const PathDiagnosticPieceRef &E) {
574
625
                               return E->getKind() == PathDiagnosticPiece::Note;
575
625
                             }) &&
576
625
         "PathDiagnostic is not partitioned so that notes precede the rest");
577
578
0
  PathPieces::const_iterator FirstNonNote = std::partition_point(
579
1.76k
      Path.begin(), Path.end(), [](const PathDiagnosticPieceRef &E) {
580
1.76k
        return E->getKind() == PathDiagnosticPiece::Note;
581
1.76k
      });
582
583
625
  PathPieces::const_iterator I = Path.begin();
584
585
625
  if (FirstNonNote != Path.begin()) {
586
1
    o << "   <key>notes</key>\n"
587
1
         "   <array>\n";
588
589
2
    for (; I != FirstNonNote; 
++I1
)
590
1
      Printer.ReportDiag(o, **I);
591
592
1
    o << "   </array>\n";
593
1
  }
594
595
625
  o << "   <key>path</key>\n";
596
597
625
  o << "   <array>\n";
598
599
4.12k
  for (PathPieces::const_iterator E = Path.end(); I != E; 
++I3.49k
)
600
3.49k
    Printer.ReportDiag(o, **I);
601
602
625
  o << "   </array>\n";
603
604
625
  if (!DiagOpts.ShouldDisplayMacroExpansions)
605
583
    return;
606
607
42
  o << "   <key>macro_expansions</key>\n"
608
42
       "   <array>\n";
609
42
  Printer.ReportMacroExpansions(o, /* indent */ 4);
610
42
  o << "   </array>\n";
611
42
}
612
613
void PlistDiagnostics::FlushDiagnosticsImpl(
614
                                    std::vector<const PathDiagnostic *> &Diags,
615
67
                                    FilesMade *filesMade) {
616
  // Build up a set of FIDs that we use by scanning the locations and
617
  // ranges of the diagnostics.
618
67
  FIDMap FM;
619
67
  SmallVector<FileID, 10> Fids;
620
67
  const SourceManager& SM = PP.getSourceManager();
621
67
  const LangOptions &LangOpts = PP.getLangOpts();
622
623
4.36k
  auto AddPieceFID = [&FM, &Fids, &SM](const PathDiagnosticPiece &Piece) {
624
4.36k
    AddFID(FM, Fids, SM, Piece.getLocation().asLocation());
625
4.36k
    ArrayRef<SourceRange> Ranges = Piece.getRanges();
626
1.82k
    for (const SourceRange &Range : Ranges) {
627
1.82k
      AddFID(FM, Fids, SM, Range.getBegin());
628
1.82k
      AddFID(FM, Fids, SM, Range.getEnd());
629
1.82k
    }
630
4.36k
  };
631
632
625
  for (const PathDiagnostic *D : Diags) {
633
634
625
    SmallVector<const PathPieces *, 5> WorkList;
635
625
    WorkList.push_back(&D->path);
636
637
1.44k
    while (!WorkList.empty()) {
638
819
      const PathPieces &Path = *WorkList.pop_back_val();
639
640
4.08k
      for (const auto &Iter : Path) {
641
4.08k
        const PathDiagnosticPiece &Piece = *Iter;
642
4.08k
        AddPieceFID(Piece);
643
644
4.08k
        if (const PathDiagnosticCallPiece *Call =
645
148
                dyn_cast<PathDiagnosticCallPiece>(&Piece)) {
646
148
          if (auto CallEnterWithin = Call->getCallEnterWithinCallerEvent())
647
136
            AddPieceFID(*CallEnterWithin);
648
649
148
          if (auto CallEnterEvent = Call->getCallEnterEvent())
650
147
            AddPieceFID(*CallEnterEvent);
651
652
148
          WorkList.push_back(&Call->path);
653
3.93k
        } else if (const PathDiagnosticMacroPiece *Macro =
654
46
                       dyn_cast<PathDiagnosticMacroPiece>(&Piece)) {
655
46
          WorkList.push_back(&Macro->subPieces);
656
46
        }
657
4.08k
      }
658
819
    }
659
625
  }
660
661
  // Open the file.
662
67
  std::error_code EC;
663
67
  llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::OF_Text);
664
67
  if (EC) {
665
0
    llvm::errs() << "warning: could not create file: " << EC.message() << '\n';
666
0
    return;
667
0
  }
668
669
67
  EmitPlistHeader(o);
670
671
  // Write the root object: a <dict> containing...
672
  //  - "clang_version", the string representation of clang version
673
  //  - "files", an <array> mapping from FIDs to file names
674
  //  - "diagnostics", an <array> containing the path diagnostics
675
67
  o << "<dict>\n" <<
676
67
       " <key>clang_version</key>\n";
677
67
  EmitString(o, getClangFullVersion()) << '\n';
678
67
  o << " <key>diagnostics</key>\n"
679
67
       " <array>\n";
680
681
67
  for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(),
682
692
       DE = Diags.end(); DI!=DE; 
++DI625
) {
683
684
625
    o << "  <dict>\n";
685
686
625
    const PathDiagnostic *D = *DI;
687
625
    printBugPath(o, FM, D->path);
688
689
    // Output the bug type and bug category.
690
625
    o << "   <key>description</key>";
691
625
    EmitString(o, D->getShortDescription()) << '\n';
692
625
    o << "   <key>category</key>";
693
625
    EmitString(o, D->getCategory()) << '\n';
694
625
    o << "   <key>type</key>";
695
625
    EmitString(o, D->getBugType()) << '\n';
696
625
    o << "   <key>check_name</key>";
697
625
    EmitString(o, D->getCheckerName()) << '\n';
698
699
625
    o << "   <!-- This hash is experimental and going to change! -->\n";
700
625
    o << "   <key>issue_hash_content_of_line_in_context</key>";
701
625
    PathDiagnosticLocation UPDLoc = D->getUniqueingLoc();
702
625
    FullSourceLoc L(SM.getExpansionLoc(UPDLoc.isValid()
703
207
                                            ? UPDLoc.asLocation()
704
418
                                            : D->getLocation().asLocation()),
705
625
                    SM);
706
625
    const Decl *DeclWithIssue = D->getDeclWithIssue();
707
625
    EmitString(o, getIssueHash(L, D->getCheckerName(), D->getBugType(),
708
625
                               DeclWithIssue, LangOpts))
709
625
        << '\n';
710
711
    // Output information about the semantic context where
712
    // the issue occurred.
713
625
    if (const Decl *DeclWithIssue = D->getDeclWithIssue()) {
714
      // FIXME: handle blocks, which have no name.
715
623
      if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) {
716
618
        StringRef declKind;
717
618
        switch (ND->getKind()) {
718
0
          case Decl::CXXRecord:
719
0
            declKind = "C++ class";
720
0
            break;
721
7
          case Decl::CXXMethod:
722
7
            declKind = "C++ method";
723
7
            break;
724
50
          case Decl::ObjCMethod:
725
50
            declKind = "Objective-C method";
726
50
            break;
727
545
          case Decl::Function:
728
545
            declKind = "function";
729
545
            break;
730
16
          default:
731
16
            break;
732
618
        }
733
618
        if (!declKind.empty()) {
734
602
          const std::string &declName = ND->getDeclName().getAsString();
735
602
          o << "  <key>issue_context_kind</key>";
736
602
          EmitString(o, declKind) << '\n';
737
602
          o << "  <key>issue_context</key>";
738
602
          EmitString(o, declName) << '\n';
739
602
        }
740
741
        // Output the bug hash for issue unique-ing. Currently, it's just an
742
        // offset from the beginning of the function.
743
618
        if (const Stmt *Body = DeclWithIssue->getBody()) {
744
745
          // If the bug uniqueing location exists, use it for the hash.
746
          // For example, this ensures that two leaks reported on the same line
747
          // will have different issue_hashes and that the hash will identify
748
          // the leak location even after code is added between the allocation
749
          // site and the end of scope (leak report location).
750
618
          if (UPDLoc.isValid()) {
751
204
            FullSourceLoc UFunL(
752
204
                SM.getExpansionLoc(
753
204
                    D->getUniqueingDecl()->getBody()->getBeginLoc()),
754
204
                SM);
755
204
            o << "  <key>issue_hash_function_offset</key><string>"
756
204
              << L.getExpansionLineNumber() - UFunL.getExpansionLineNumber()
757
204
              << "</string>\n";
758
759
          // Otherwise, use the location on which the bug is reported.
760
414
          } else {
761
414
            FullSourceLoc FunL(SM.getExpansionLoc(Body->getBeginLoc()), SM);
762
414
            o << "  <key>issue_hash_function_offset</key><string>"
763
414
              << L.getExpansionLineNumber() - FunL.getExpansionLineNumber()
764
414
              << "</string>\n";
765
414
          }
766
767
618
        }
768
618
      }
769
623
    }
770
771
    // Output the location of the bug.
772
625
    o << "  <key>location</key>\n";
773
625
    EmitLocation(o, SM, D->getLocation().asLocation(), FM, 2);
774
775
    // Output the diagnostic to the sub-diagnostic client, if any.
776
625
    if (!filesMade->empty()) {
777
2
      StringRef lastName;
778
2
      PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D);
779
2
      if (files) {
780
2
        for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(),
781
4
                CE = files->end(); CI != CE; 
++CI2
) {
782
2
          StringRef newName = CI->first;
783
2
          if (newName != lastName) {
784
2
            if (!lastName.empty()) {
785
0
              o << "  </array>\n";
786
0
            }
787
2
            lastName = newName;
788
2
            o <<  "  <key>" << lastName << "_files</key>\n";
789
2
            o << "  <array>\n";
790
2
          }
791
2
          o << "   <string>" << CI->second << "</string>\n";
792
2
        }
793
2
        o << "  </array>\n";
794
2
      }
795
2
    }
796
797
625
    printCoverage(D, /*IndentLevel=*/2, Fids, FM, o);
798
799
    // Close up the entry.
800
625
    o << "  </dict>\n";
801
625
  }
802
803
67
  o << " </array>\n";
804
805
67
  o << " <key>files</key>\n"
806
67
       " <array>\n";
807
67
  for (FileID FID : Fids)
808
67
    EmitString(o << "  ", SM.getFileEntryForID(FID)->getName()) << '\n';
809
67
  o << " </array>\n";
810
811
67
  if (llvm::AreStatisticsEnabled() && 
DiagOpts.ShouldSerializeStats1
) {
812
1
    o << " <key>statistics</key>\n";
813
1
    std::string stats;
814
1
    llvm::raw_string_ostream os(stats);
815
1
    llvm::PrintStatisticsJSON(os);
816
1
    os.flush();
817
1
    EmitString(o, html::EscapeText(stats)) << '\n';
818
1
  }
819
820
  // Finish.
821
67
  o << "</dict>\n</plist>\n";
822
67
}
823
824
//===----------------------------------------------------------------------===//
825
// Definitions of helper functions and methods for expanding macros.
826
//===----------------------------------------------------------------------===//
827
828
static Optional<StringRef>
829
getExpandedMacro(SourceLocation MacroExpansionLoc,
830
                 const cross_tu::CrossTranslationUnitContext &CTU,
831
                 const MacroExpansionContext &MacroExpansions,
832
46
                 const SourceManager &SM) {
833
46
  if (auto CTUMacroExpCtx =
834
0
          CTU.getMacroExpansionContextForSourceLocation(MacroExpansionLoc)) {
835
0
    return CTUMacroExpCtx->getExpandedText(MacroExpansionLoc);
836
0
  }
837
46
  return MacroExpansions.getExpandedText(MacroExpansionLoc);
838
46
}