Skip to content

Commit fc36b58

Browse files
committed
[analyzer] Try to re-apply r283092 "Extend bug reports with extra notes"
Replace SmallVector<IntrusiveRefCntPtr> with a vector of plain pointers. Would insignificantly increase memory usage. llvm-svn: 283536
1 parent 6ad5da7 commit fc36b58

File tree

10 files changed

+241
-58
lines changed

10 files changed

+241
-58
lines changed

clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,9 @@ class AnalyzerOptions : public RefCountedBase<AnalyzerOptions> {
266266
/// \sa shouldWidenLoops
267267
Optional<bool> WidenLoops;
268268

269+
/// \sa shouldDisplayNotesAsEvents
270+
Optional<bool> DisplayNotesAsEvents;
271+
269272
/// A helper function that retrieves option for a given full-qualified
270273
/// checker name.
271274
/// Options for checkers can be specified via 'analyzer-config' command-line
@@ -534,6 +537,14 @@ class AnalyzerOptions : public RefCountedBase<AnalyzerOptions> {
534537
/// This is controlled by the 'widen-loops' config option.
535538
bool shouldWidenLoops();
536539

540+
/// Returns true if the bug reporter should transparently treat extra note
541+
/// diagnostic pieces as event diagnostic pieces. Useful when the diagnostic
542+
/// consumer doesn't support the extra note pieces.
543+
///
544+
/// This is controlled by the 'extra-notes-as-events' option, which defaults
545+
/// to false when unset.
546+
bool shouldDisplayNotesAsEvents();
547+
537548
public:
538549
AnalyzerOptions() :
539550
AnalysisStoreOpt(RegionStoreModel),

clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ class BugReport : public llvm::ilist_node<BugReport> {
6767
typedef VisitorList::iterator visitor_iterator;
6868
typedef SmallVector<StringRef, 2> ExtraTextList;
6969

70+
// FIXME: We could have used a
71+
// SmallVector<llvm::IntrusiveRefCntPtr<PathDiagnosticNotePiece, 4>>
72+
// as NoteList, however MSVC 2013 crashes on reading this.
73+
typedef SmallVector<const PathDiagnosticNotePiece *, 4> NoteList;
74+
7075
protected:
7176
friend class BugReporter;
7277
friend class BugReportEquivClass;
@@ -82,7 +87,8 @@ class BugReport : public llvm::ilist_node<BugReport> {
8287
const ExplodedNode *ErrorNode;
8388
SmallVector<SourceRange, 4> Ranges;
8489
ExtraTextList ExtraText;
85-
90+
NoteList Notes;
91+
8692
typedef llvm::DenseSet<SymbolRef> Symbols;
8793
typedef llvm::DenseSet<const MemRegion *> Regions;
8894

@@ -177,6 +183,18 @@ class BugReport : public llvm::ilist_node<BugReport> {
177183
const BugType& getBugType() const { return BT; }
178184
BugType& getBugType() { return BT; }
179185

186+
/// \brief True when the report has an execution path associated with it.
187+
///
188+
/// A report is said to be path-sensitive if it was thrown against a
189+
/// particular exploded node in the path-sensitive analysis graph.
190+
/// Path-sensitive reports have their intermediate path diagnostics
191+
/// auto-generated, perhaps with the help of checker-defined visitors,
192+
/// and may contain extra notes.
193+
/// Path-insensitive reports consist only of a single warning message
194+
/// in a specific location, and perhaps extra notes.
195+
/// Path-sensitive checkers are allowed to throw path-insensitive reports.
196+
bool isPathSensitive() const { return ErrorNode != nullptr; }
197+
180198
const ExplodedNode *getErrorNode() const { return ErrorNode; }
181199

182200
StringRef getDescription() const { return Description; }
@@ -245,7 +263,26 @@ class BugReport : public llvm::ilist_node<BugReport> {
245263
void setDeclWithIssue(const Decl *declWithIssue) {
246264
DeclWithIssue = declWithIssue;
247265
}
248-
266+
267+
/// Add new item to the list of additional notes that need to be attached to
268+
/// this path-insensitive report. If you want to add extra notes to a
269+
/// path-sensitive report, you need to use a BugReporterVisitor because it
270+
/// allows you to specify where exactly in the auto-generated path diagnostic
271+
/// the extra note should appear.
272+
void addNote(StringRef Msg, const PathDiagnosticLocation &Pos,
273+
ArrayRef<SourceRange> Ranges = {}) {
274+
auto *P = new PathDiagnosticNotePiece(Pos, Msg);
275+
276+
for (const auto &R : Ranges)
277+
P->addRange(R);
278+
279+
Notes.push_back(P);
280+
}
281+
282+
virtual const NoteList &getNotes() {
283+
return Notes;
284+
}
285+
249286
/// \brief This allows for addition of meta data to the diagnostic.
250287
///
251288
/// Currently, only the HTMLDiagnosticClient knows how to display it.

clang/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ class PathDiagnosticLocationPair {
336336

337337
class PathDiagnosticPiece : public RefCountedBaseVPTR {
338338
public:
339-
enum Kind { ControlFlow, Event, Macro, Call };
339+
enum Kind { ControlFlow, Event, Macro, Call, Note };
340340
enum DisplayHint { Above, Below };
341341

342342
private:
@@ -452,7 +452,8 @@ class PathDiagnosticSpotPiece : public PathDiagnosticPiece {
452452
void Profile(llvm::FoldingSetNodeID &ID) const override;
453453

454454
static bool classof(const PathDiagnosticPiece *P) {
455-
return P->getKind() == Event || P->getKind() == Macro;
455+
return P->getKind() == Event || P->getKind() == Macro ||
456+
P->getKind() == Note;
456457
}
457458
};
458459

@@ -710,6 +711,23 @@ class PathDiagnosticMacroPiece : public PathDiagnosticSpotPiece {
710711
void Profile(llvm::FoldingSetNodeID &ID) const override;
711712
};
712713

714+
class PathDiagnosticNotePiece: public PathDiagnosticSpotPiece {
715+
public:
716+
PathDiagnosticNotePiece(const PathDiagnosticLocation &Pos, StringRef S,
717+
bool AddPosRange = true)
718+
: PathDiagnosticSpotPiece(Pos, S, Note, AddPosRange) {}
719+
720+
~PathDiagnosticNotePiece() override;
721+
722+
static inline bool classof(const PathDiagnosticPiece *P) {
723+
return P->getKind() == Note;
724+
}
725+
726+
void dump() const override;
727+
728+
void Profile(llvm::FoldingSetNodeID &ID) const override;
729+
};
730+
713731
/// PathDiagnostic - PathDiagnostic objects represent a single path-sensitive
714732
/// diagnostic. It represents an ordered-collection of PathDiagnosticPieces,
715733
/// each which represent the pieces of the path.

clang/lib/Rewrite/HTMLRewrite.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ void html::AddHeaderFooterInternalBuiltinCSS(Rewriter &R, FileID FID,
324324
" .msgT { padding:0x; spacing:0x }\n"
325325
" .msgEvent { background-color:#fff8b4; color:#000000 }\n"
326326
" .msgControl { background-color:#bbbbbb; color:#000000 }\n"
327+
" .msgNote { background-color:#ddeeff; color:#000000 }\n"
327328
" .mrange { background-color:#dfddf3 }\n"
328329
" .mrange { border-bottom:1px solid #6F9DBE }\n"
329330
" .PathIndex { font-weight: bold; padding:0px 5px; "
@@ -343,8 +344,12 @@ void html::AddHeaderFooterInternalBuiltinCSS(Rewriter &R, FileID FID,
343344
" border-collapse: collapse; border-spacing: 0px;\n"
344345
" }\n"
345346
" td.rowname {\n"
346-
" text-align:right; font-weight:bold; color:#444444;\n"
347-
" padding-right:2ex; }\n"
347+
" text-align: right;\n"
348+
" vertical-align: top;\n"
349+
" font-weight: bold;\n"
350+
" color:#444444;\n"
351+
" padding-right:2ex;\n"
352+
" }\n"
348353
"</style>\n</head>\n<body>";
349354

350355
// Generate header

clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,3 +344,10 @@ bool AnalyzerOptions::shouldWidenLoops() {
344344
WidenLoops = getBooleanOption("widen-loops", /*Default=*/false);
345345
return WidenLoops.getValue();
346346
}
347+
348+
bool AnalyzerOptions::shouldDisplayNotesAsEvents() {
349+
if (!DisplayNotesAsEvents.hasValue())
350+
DisplayNotesAsEvents =
351+
getBooleanOption("notes-as-events", /*Default=*/false);
352+
return DisplayNotesAsEvents.getValue();
353+
}

clang/lib/StaticAnalyzer/Core/BugReporter.cpp

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,15 @@ static void removeRedundantMsgs(PathPieces &path) {
114114
path.pop_front();
115115

116116
switch (piece->getKind()) {
117-
case clang::ento::PathDiagnosticPiece::Call:
117+
case PathDiagnosticPiece::Call:
118118
removeRedundantMsgs(cast<PathDiagnosticCallPiece>(piece)->path);
119119
break;
120-
case clang::ento::PathDiagnosticPiece::Macro:
120+
case PathDiagnosticPiece::Macro:
121121
removeRedundantMsgs(cast<PathDiagnosticMacroPiece>(piece)->subPieces);
122122
break;
123-
case clang::ento::PathDiagnosticPiece::ControlFlow:
123+
case PathDiagnosticPiece::ControlFlow:
124124
break;
125-
case clang::ento::PathDiagnosticPiece::Event: {
125+
case PathDiagnosticPiece::Event: {
126126
if (i == N-1)
127127
break;
128128

@@ -142,6 +142,8 @@ static void removeRedundantMsgs(PathPieces &path) {
142142
}
143143
break;
144144
}
145+
case PathDiagnosticPiece::Note:
146+
break;
145147
}
146148
path.push_back(piece);
147149
}
@@ -199,6 +201,9 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R,
199201
}
200202
case PathDiagnosticPiece::ControlFlow:
201203
break;
204+
205+
case PathDiagnosticPiece::Note:
206+
break;
202207
}
203208

204209
pieces.push_back(piece);
@@ -2554,6 +2559,12 @@ BugReport::~BugReport() {
25542559
while (!interestingSymbols.empty()) {
25552560
popInterestingSymbolsAndRegions();
25562561
}
2562+
2563+
// FIXME: Replace Notes with a list of shared pointers.
2564+
for (const auto *P: Notes) {
2565+
delete P;
2566+
}
2567+
Notes.clear();
25572568
}
25582569

25592570
const Decl *BugReport::getDeclWithIssue() const {
@@ -3405,25 +3416,28 @@ void BugReporter::FlushReport(BugReport *exampleReport,
34053416
exampleReport->getUniqueingLocation(),
34063417
exampleReport->getUniqueingDecl()));
34073418

3408-
MaxBugClassSize = std::max(bugReports.size(),
3409-
static_cast<size_t>(MaxBugClassSize));
3419+
if (exampleReport->isPathSensitive()) {
3420+
// Generate the full path diagnostic, using the generation scheme
3421+
// specified by the PathDiagnosticConsumer. Note that we have to generate
3422+
// path diagnostics even for consumers which do not support paths, because
3423+
// the BugReporterVisitors may mark this bug as a false positive.
3424+
assert(!bugReports.empty());
3425+
3426+
MaxBugClassSize =
3427+
std::max(bugReports.size(), static_cast<size_t>(MaxBugClassSize));
34103428

3411-
// Generate the full path diagnostic, using the generation scheme
3412-
// specified by the PathDiagnosticConsumer. Note that we have to generate
3413-
// path diagnostics even for consumers which do not support paths, because
3414-
// the BugReporterVisitors may mark this bug as a false positive.
3415-
if (!bugReports.empty())
34163429
if (!generatePathDiagnostic(*D.get(), PD, bugReports))
34173430
return;
34183431

3419-
MaxValidBugClassSize = std::max(bugReports.size(),
3420-
static_cast<size_t>(MaxValidBugClassSize));
3432+
MaxValidBugClassSize =
3433+
std::max(bugReports.size(), static_cast<size_t>(MaxValidBugClassSize));
34213434

3422-
// Examine the report and see if the last piece is in a header. Reset the
3423-
// report location to the last piece in the main source file.
3424-
AnalyzerOptions& Opts = getAnalyzerOptions();
3425-
if (Opts.shouldReportIssuesInMainSourceFile() && !Opts.AnalyzeAll)
3426-
D->resetDiagnosticLocationToMainFile();
3435+
// Examine the report and see if the last piece is in a header. Reset the
3436+
// report location to the last piece in the main source file.
3437+
AnalyzerOptions &Opts = getAnalyzerOptions();
3438+
if (Opts.shouldReportIssuesInMainSourceFile() && !Opts.AnalyzeAll)
3439+
D->resetDiagnosticLocationToMainFile();
3440+
}
34273441

34283442
// If the path is empty, generate a single step path with the location
34293443
// of the issue.
@@ -3436,6 +3450,28 @@ void BugReporter::FlushReport(BugReport *exampleReport,
34363450
D->setEndOfPath(std::move(piece));
34373451
}
34383452

3453+
PathPieces &Pieces = D->getMutablePieces();
3454+
bool ShouldConvert = getAnalyzerOptions().shouldDisplayNotesAsEvents();
3455+
// For path diagnostic consumers that don't support extra notes,
3456+
// we may optionally convert those to path notes.
3457+
for (auto I = exampleReport->getNotes().rbegin(),
3458+
E = exampleReport->getNotes().rend(); I != E; ++I) {
3459+
const PathDiagnosticNotePiece *Piece = *I;
3460+
// FIXME: getNotes() was supposed to return a list of shared pointers,
3461+
// and then we wouldn't normally create a new piece here,
3462+
// unless ShouldConvert is set.
3463+
PathDiagnosticPiece *ConvertedPiece =
3464+
ShouldConvert
3465+
? static_cast<PathDiagnosticPiece *>(new PathDiagnosticEventPiece(
3466+
Piece->getLocation(), Piece->getString()))
3467+
: static_cast<PathDiagnosticPiece *>(new PathDiagnosticNotePiece(
3468+
Piece->getLocation(), Piece->getString()));
3469+
for (const auto &R : Piece->getRanges())
3470+
ConvertedPiece->addRange(R);
3471+
3472+
Pieces.push_front(ConvertedPiece);
3473+
}
3474+
34393475
// Get the meta data.
34403476
const BugReport::ExtraTextList &Meta = exampleReport->getExtraText();
34413477
for (BugReport::ExtraTextList::const_iterator i = Meta.begin(),
@@ -3520,6 +3556,13 @@ LLVM_DUMP_METHOD void PathDiagnosticMacroPiece::dump() const {
35203556
// FIXME: Print which macro is being invoked.
35213557
}
35223558

3559+
LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const {
3560+
llvm::errs() << "NOTE\n--------------\n";
3561+
llvm::errs() << getString() << "\n";
3562+
llvm::errs() << " ---- at ----\n";
3563+
getLocation().dump();
3564+
}
3565+
35233566
LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const {
35243567
if (!isValid()) {
35253568
llvm::errs() << "<INVALID>\n";

0 commit comments

Comments
 (0)