Coverage Report

Created: 2017-10-03 07:32

/Users/buildslave/jenkins/sharedspace/clang-stage2-coverage-R@2/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
Line
Count
Source (jump to first uncovered line)
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
14
#include "clang/Basic/FileManager.h"
15
#include "clang/Basic/PlistSupport.h"
16
#include "clang/Basic/SourceManager.h"
17
#include "clang/Basic/Version.h"
18
#include "clang/Lex/Preprocessor.h"
19
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
20
#include "clang/StaticAnalyzer/Core/IssueHash.h"
21
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.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
41
    ~PlistDiagnostics() override {}
40
41
    void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
42
                              FilesMade *filesMade) override;
43
44
0
    StringRef getName() const override {
45
0
      return "PlistDiagnostics";
46
0
    }
47
48
555
    PathGenerationScheme getGenerationScheme() const override {
49
555
      return Extensive;
50
555
    }
51
0
    bool supportsLogicalOpControlFlow() const override { return true; }
52
570
    bool supportsCrossFileDiagnostics() const override {
53
570
      return SupportsCrossFileDiagnostics;
54
570
    }
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
41
    SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
65
66
void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
67
                                         PathDiagnosticConsumers &C,
68
                                         const std::string& s,
69
22
                                         const Preprocessor &PP) {
70
22
  C.push_back(new PlistDiagnostics(AnalyzerOpts, s,
71
22
                                   PP.getLangOpts(), false));
72
22
}
73
74
void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
75
                                                  PathDiagnosticConsumers &C,
76
                                                  const std::string &s,
77
19
                                                  const Preprocessor &PP) {
78
19
  C.push_back(new PlistDiagnostics(AnalyzerOpts, s,
79
19
                                   PP.getLangOpts(), true));
80
19
}
81
82
static void ReportControlFlow(raw_ostream &o,
83
                              const PathDiagnosticControlFlowPiece& P,
84
                              const FIDMap& FM,
85
                              const SourceManager &SM,
86
                              const LangOptions &LangOpts,
87
2.01k
                              unsigned indent) {
88
2.01k
89
2.01k
  Indent(o, indent) << "<dict>\n";
90
2.01k
  ++indent;
91
2.01k
92
2.01k
  Indent(o, indent) << "<key>kind</key><string>control</string>\n";
93
2.01k
94
2.01k
  // Emit edges.
95
2.01k
  Indent(o, indent) << "<key>edges</key>\n";
96
2.01k
  ++indent;
97
2.01k
  Indent(o, indent) << "<array>\n";
98
2.01k
  ++indent;
99
2.01k
  for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end();
100
4.02k
       
I!=E4.02k
;
++I2.01k
) {
101
2.01k
    Indent(o, indent) << "<dict>\n";
102
2.01k
    ++indent;
103
2.01k
104
2.01k
    // Make the ranges of the start and end point self-consistent with adjacent edges
105
2.01k
    // by forcing to use only the beginning of the range.  This simplifies the layout
106
2.01k
    // logic for clients.
107
2.01k
    Indent(o, indent) << "<key>start</key>\n";
108
2.01k
    SourceRange StartEdge(
109
2.01k
        SM.getExpansionLoc(I->getStart().asRange().getBegin()));
110
2.01k
    EmitRange(o, SM, Lexer::getAsCharRange(StartEdge, SM, LangOpts), FM,
111
2.01k
              indent + 1);
112
2.01k
113
2.01k
    Indent(o, indent) << "<key>end</key>\n";
114
2.01k
    SourceRange EndEdge(SM.getExpansionLoc(I->getEnd().asRange().getBegin()));
115
2.01k
    EmitRange(o, SM, Lexer::getAsCharRange(EndEdge, SM, LangOpts), FM,
116
2.01k
              indent + 1);
117
2.01k
118
2.01k
    --indent;
119
2.01k
    Indent(o, indent) << "</dict>\n";
120
2.01k
  }
121
2.01k
  --indent;
122
2.01k
  Indent(o, indent) << "</array>\n";
123
2.01k
  --indent;
124
2.01k
125
2.01k
  // Output any helper text.
126
2.01k
  const auto &s = P.getString();
127
2.01k
  if (
!s.empty()2.01k
) {
128
0
    Indent(o, indent) << "<key>alternate</key>";
129
0
    EmitString(o, s) << '\n';
130
0
  }
131
2.01k
132
2.01k
  --indent;
133
2.01k
  Indent(o, indent) << "</dict>\n";
134
2.01k
}
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
1.79k
                        bool isKeyEvent = false) {
143
1.79k
144
1.79k
  Indent(o, indent) << "<dict>\n";
145
1.79k
  ++indent;
146
1.79k
147
1.79k
  Indent(o, indent) << "<key>kind</key><string>event</string>\n";
148
1.79k
149
1.79k
  if (
isKeyEvent1.79k
) {
150
2
    Indent(o, indent) << "<key>key_event</key><true/>\n";
151
2
  }
152
1.79k
153
1.79k
  // Output the location.
154
1.79k
  FullSourceLoc L = P.getLocation().asLocation();
155
1.79k
156
1.79k
  Indent(o, indent) << "<key>location</key>\n";
157
1.79k
  EmitLocation(o, SM, L, FM, indent);
158
1.79k
159
1.79k
  // Output the ranges (if any).
160
1.79k
  ArrayRef<SourceRange> Ranges = P.getRanges();
161
1.79k
162
1.79k
  if (
!Ranges.empty()1.79k
) {
163
1.57k
    Indent(o, indent) << "<key>ranges</key>\n";
164
1.57k
    Indent(o, indent) << "<array>\n";
165
1.57k
    ++indent;
166
1.57k
    for (auto &R : Ranges)
167
1.74k
      EmitRange(o, SM,
168
1.74k
                Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts),
169
1.74k
                FM, indent + 1);
170
1.57k
    --indent;
171
1.57k
    Indent(o, indent) << "</array>\n";
172
1.57k
  }
173
1.79k
174
1.79k
  // Output the call depth.
175
1.79k
  Indent(o, indent) << "<key>depth</key>";
176
1.79k
  EmitInteger(o, depth) << '\n';
177
1.79k
178
1.79k
  // Output the text.
179
1.79k
  assert(!P.getString().empty());
180
1.79k
  Indent(o, indent) << "<key>extended_message</key>\n";
181
1.79k
  Indent(o, indent);
182
1.79k
  EmitString(o, P.getString()) << '\n';
183
1.79k
184
1.79k
  // Output the short text.
185
1.79k
  // FIXME: Really use a short string.
186
1.79k
  Indent(o, indent) << "<key>message</key>\n";
187
1.79k
  Indent(o, indent);
188
1.79k
  EmitString(o, P.getString()) << '\n';
189
1.79k
190
1.79k
  // Finish up.
191
1.79k
  --indent;
192
1.79k
  Indent(o, indent); o << "</dict>\n";
193
1.79k
}
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
121
                       unsigned depth) {
210
121
211
121
  if (auto callEnter = P.getCallEnterEvent())
212
120
    ReportPiece(o, *callEnter, FM, SM, LangOpts, indent, depth, true,
213
120
                P.isLastInMainSourceFile());
214
121
215
121
216
121
  ++depth;
217
121
218
121
  if (auto callEnterWithinCaller = P.getCallEnterWithinCallerEvent())
219
110
    ReportPiece(o, *callEnterWithinCaller, FM, SM, LangOpts,
220
110
                indent, depth, true);
221
121
222
526
  for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();
I!=E526
;
++I405
)
223
405
    ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, true);
224
121
225
121
  --depth;
226
121
227
121
  if (auto callExit = P.getCallExitEvent())
228
53
    ReportPiece(o, *callExit, FM, SM, LangOpts, indent, depth, true);
229
121
}
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
0
                        unsigned depth) {
237
0
238
0
  for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end();
239
0
       
I!=E0
;
++I0
) {
240
0
    ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, false);
241
0
  }
242
0
}
243
244
static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P,
245
                       const FIDMap& FM, const SourceManager &SM,
246
3.24k
                       const LangOptions &LangOpts) {
247
3.24k
  ReportPiece(o, P, FM, SM, LangOpts, 4, 0, true);
248
3.24k
}
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
3.92k
                        bool isKeyEvent) {
258
3.92k
  switch (P.getKind()) {
259
2.01k
    case PathDiagnosticPiece::ControlFlow:
260
2.01k
      if (includeControlFlow)
261
2.01k
        ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM,
262
2.01k
                          LangOpts, indent);
263
2.01k
      break;
264
121
    case PathDiagnosticPiece::Call:
265
121
      ReportCall(o, cast<PathDiagnosticCallPiece>(P), FM, SM, LangOpts,
266
121
                 indent, depth);
267
121
      break;
268
1.79k
    case PathDiagnosticPiece::Event:
269
1.79k
      ReportEvent(o, cast<PathDiagnosticSpotPiece>(P), FM, SM, LangOpts,
270
1.79k
                  indent, depth, isKeyEvent);
271
1.79k
      break;
272
0
    case PathDiagnosticPiece::Macro:
273
0
      ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts,
274
0
                  indent, depth);
275
0
      break;
276
1
    case PathDiagnosticPiece::Note:
277
1
      // FIXME: Extend the plist format to support those.
278
1
      break;
279
3.92k
  }
280
3.92k
}
281
282
void PlistDiagnostics::FlushDiagnosticsImpl(
283
                                    std::vector<const PathDiagnostic *> &Diags,
284
41
                                    FilesMade *filesMade) {
285
41
  // Build up a set of FIDs that we use by scanning the locations and
286
41
  // ranges of the diagnostics.
287
41
  FIDMap FM;
288
41
  SmallVector<FileID, 10> Fids;
289
41
  const SourceManager* SM = nullptr;
290
41
291
41
  if (!Diags.empty())
292
41
    SM = &Diags.front()->path.front()->getLocation().getManager();
293
41
294
3.87k
  auto AddPieceFID = [&FM, &Fids, SM](const PathDiagnosticPiece &Piece) {
295
3.87k
    AddFID(FM, Fids, *SM, Piece.getLocation().asLocation());
296
3.87k
    ArrayRef<SourceRange> Ranges = Piece.getRanges();
297
1.69k
    for (const SourceRange &Range : Ranges) {
298
1.69k
      AddFID(FM, Fids, *SM, Range.getBegin());
299
1.69k
      AddFID(FM, Fids, *SM, Range.getEnd());
300
1.69k
    }
301
3.87k
  };
302
41
303
565
  for (const PathDiagnostic *D : Diags) {
304
565
305
565
    SmallVector<const PathPieces *, 5> WorkList;
306
565
    WorkList.push_back(&D->path);
307
565
308
1.25k
    while (
!WorkList.empty()1.25k
) {
309
686
      const PathPieces &Path = *WorkList.pop_back_val();
310
686
311
3.64k
      for (const auto &Iter : Path) {
312
3.64k
        const PathDiagnosticPiece &Piece = *Iter;
313
3.64k
        AddPieceFID(Piece);
314
3.64k
315
3.64k
        if (const PathDiagnosticCallPiece *Call =
316
121
                dyn_cast<PathDiagnosticCallPiece>(&Piece)) {
317
121
          if (auto CallEnterWithin = Call->getCallEnterWithinCallerEvent())
318
110
            AddPieceFID(*CallEnterWithin);
319
121
320
121
          if (auto CallEnterEvent = Call->getCallEnterEvent())
321
120
            AddPieceFID(*CallEnterEvent);
322
121
323
121
          WorkList.push_back(&Call->path);
324
3.64k
        } else 
if (const PathDiagnosticMacroPiece *3.52k
Macro3.52k
=
325
0
                       dyn_cast<PathDiagnosticMacroPiece>(&Piece)) {
326
0
          WorkList.push_back(&Macro->subPieces);
327
0
        }
328
3.64k
      }
329
686
    }
330
565
  }
331
41
332
41
  // Open the file.
333
41
  std::error_code EC;
334
41
  llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::F_Text);
335
41
  if (
EC41
) {
336
0
    llvm::errs() << "warning: could not create file: " << EC.message() << '\n';
337
0
    return;
338
0
  }
339
41
340
41
  EmitPlistHeader(o);
341
41
342
41
  // Write the root object: a <dict> containing...
343
41
  //  - "clang_version", the string representation of clang version
344
41
  //  - "files", an <array> mapping from FIDs to file names
345
41
  //  - "diagnostics", an <array> containing the path diagnostics
346
41
  o << "<dict>\n" <<
347
41
       " <key>clang_version</key>\n";
348
41
  EmitString(o, getClangFullVersion()) << '\n';
349
41
  o << " <key>files</key>\n"
350
41
       " <array>\n";
351
41
352
41
  for (FileID FID : Fids)
353
44
    EmitString(o << "  ", SM->getFileEntryForID(FID)->getName()) << '\n';
354
41
355
41
  o << " </array>\n"
356
41
       " <key>diagnostics</key>\n"
357
41
       " <array>\n";
358
41
359
41
  for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(),
360
606
       DE = Diags.end(); 
DI!=DE606
;
++DI565
) {
361
565
362
565
    o << "  <dict>\n"
363
565
         "   <key>path</key>\n";
364
565
365
565
    const PathDiagnostic *D = *DI;
366
565
367
565
    o << "   <array>\n";
368
565
369
565
    for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end();
370
3.80k
         
I != E3.80k
;
++I3.24k
)
371
3.24k
      ReportDiag(o, **I, FM, *SM, LangOpts);
372
565
373
565
    o << "   </array>\n";
374
565
375
565
    // Output the bug type and bug category.
376
565
    o << "   <key>description</key>";
377
565
    EmitString(o, D->getShortDescription()) << '\n';
378
565
    o << "   <key>category</key>";
379
565
    EmitString(o, D->getCategory()) << '\n';
380
565
    o << "   <key>type</key>";
381
565
    EmitString(o, D->getBugType()) << '\n';
382
565
    o << "   <key>check_name</key>";
383
565
    EmitString(o, D->getCheckName()) << '\n';
384
565
385
565
    o << "   <!-- This hash is experimental and going to change! -->\n";
386
565
    o << "   <key>issue_hash_content_of_line_in_context</key>";
387
565
    PathDiagnosticLocation UPDLoc = D->getUniqueingLoc();
388
565
    FullSourceLoc L(SM->getExpansionLoc(UPDLoc.isValid()
389
192
                                            ? UPDLoc.asLocation()
390
373
                                            : D->getLocation().asLocation()),
391
565
                    *SM);
392
565
    const Decl *DeclWithIssue = D->getDeclWithIssue();
393
565
    EmitString(o, GetIssueHash(*SM, L, D->getCheckName(), D->getBugType(),
394
565
                               DeclWithIssue, LangOpts))
395
565
        << '\n';
396
565
397
565
    // Output information about the semantic context where
398
565
    // the issue occurred.
399
565
    if (const Decl *
DeclWithIssue565
= D->getDeclWithIssue()) {
400
563
      // FIXME: handle blocks, which have no name.
401
563
      if (const NamedDecl *
ND563
= dyn_cast<NamedDecl>(DeclWithIssue)) {
402
554
        StringRef declKind;
403
554
        switch (ND->getKind()) {
404
0
          case Decl::CXXRecord:
405
0
            declKind = "C++ class";
406
0
            break;
407
11
          case Decl::CXXMethod:
408
11
            declKind = "C++ method";
409
11
            break;
410
50
          case Decl::ObjCMethod:
411
50
            declKind = "Objective-C method";
412
50
            break;
413
479
          case Decl::Function:
414
479
            declKind = "function";
415
479
            break;
416
14
          default:
417
14
            break;
418
554
        }
419
554
        
if (554
!declKind.empty()554
) {
420
540
          const std::string &declName = ND->getDeclName().getAsString();
421
540
          o << "  <key>issue_context_kind</key>";
422
540
          EmitString(o, declKind) << '\n';
423
540
          o << "  <key>issue_context</key>";
424
540
          EmitString(o, declName) << '\n';
425
540
        }
426
554
427
554
        // Output the bug hash for issue unique-ing. Currently, it's just an
428
554
        // offset from the beginning of the function.
429
554
        if (const Stmt *
Body554
= DeclWithIssue->getBody()) {
430
554
431
554
          // If the bug uniqueing location exists, use it for the hash.
432
554
          // For example, this ensures that two leaks reported on the same line
433
554
          // will have different issue_hashes and that the hash will identify
434
554
          // the leak location even after code is added between the allocation
435
554
          // site and the end of scope (leak report location).
436
554
          if (
UPDLoc.isValid()554
) {
437
191
            FullSourceLoc UFunL(SM->getExpansionLoc(
438
191
              D->getUniqueingDecl()->getBody()->getLocStart()), *SM);
439
191
            o << "  <key>issue_hash_function_offset</key><string>"
440
191
              << L.getExpansionLineNumber() - UFunL.getExpansionLineNumber()
441
191
              << "</string>\n";
442
191
443
191
          // Otherwise, use the location on which the bug is reported.
444
554
          } else {
445
363
            FullSourceLoc FunL(SM->getExpansionLoc(Body->getLocStart()), *SM);
446
363
            o << "  <key>issue_hash_function_offset</key><string>"
447
363
              << L.getExpansionLineNumber() - FunL.getExpansionLineNumber()
448
363
              << "</string>\n";
449
363
          }
450
554
451
554
        }
452
554
      }
453
563
    }
454
565
455
565
    // Output the location of the bug.
456
565
    o << "  <key>location</key>\n";
457
565
    EmitLocation(o, *SM, D->getLocation().asLocation(), FM, 2);
458
565
459
565
    // Output the diagnostic to the sub-diagnostic client, if any.
460
565
    if (
!filesMade->empty()565
) {
461
1
      StringRef lastName;
462
1
      PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D);
463
1
      if (
files1
) {
464
1
        for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(),
465
2
                CE = files->end(); 
CI != CE2
;
++CI1
) {
466
1
          StringRef newName = CI->first;
467
1
          if (
newName != lastName1
) {
468
1
            if (
!lastName.empty()1
) {
469
0
              o << "  </array>\n";
470
0
            }
471
1
            lastName = newName;
472
1
            o <<  "  <key>" << lastName << "_files</key>\n";
473
1
            o << "  <array>\n";
474
1
          }
475
1
          o << "   <string>" << CI->second << "</string>\n";
476
1
        }
477
1
        o << "  </array>\n";
478
1
      }
479
1
    }
480
565
481
565
    // Close up the entry.
482
565
    o << "  </dict>\n";
483
565
  }
484
41
485
41
  o << " </array>\n";
486
41
487
41
  // Finish.
488
41
  o << "</dict>\n</plist>";
489
41
}