Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit 096aec0

Browse files
committed
[analyzer] Extend bug reports with extra notes
These diagnostics are separate from the path-sensitive engine's path notes, and can be added manually on top of path-sensitive or path-insensitive reports. The new note diagnostics would appear as note:-diagnostic on console and as blue bubbles in scan-build. In plist files they currently do not appear, because format needs to be discussed with plist file users. The analyzer option "-analyzer-config notes-as-events=true" would convert notes to normal path notes, and put them at the beginning of the path. This is a temporary hack to show the new notes in plist files. A few checkers would be updated in subsequent commits, including tests for this new feature. Differential Revision: https://reviews.llvm.org/D24278 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@283092 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 1482eca commit 096aec0

File tree

10 files changed

+232
-58
lines changed

10 files changed

+232
-58
lines changed

Diff for: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h

+11
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 'notes-as-events' option, which defaults
545+
/// to false when unset.
546+
bool shouldDisplayNotesAsEvents();
547+
537548
public:
538549
AnalyzerOptions() :
539550
AnalysisStoreOpt(RegionStoreModel),

Diff for: include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h

+37-2
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ class BugReport : public llvm::ilist_node<BugReport> {
6666
typedef SmallVector<std::unique_ptr<BugReporterVisitor>, 8> VisitorList;
6767
typedef VisitorList::iterator visitor_iterator;
6868
typedef SmallVector<StringRef, 2> ExtraTextList;
69+
typedef SmallVector<llvm::IntrusiveRefCntPtr<PathDiagnosticNotePiece>, 4>
70+
NoteList;
6971

7072
protected:
7173
friend class BugReporter;
@@ -82,7 +84,8 @@ class BugReport : public llvm::ilist_node<BugReport> {
8284
const ExplodedNode *ErrorNode;
8385
SmallVector<SourceRange, 4> Ranges;
8486
ExtraTextList ExtraText;
85-
87+
NoteList Notes;
88+
8689
typedef llvm::DenseSet<SymbolRef> Symbols;
8790
typedef llvm::DenseSet<const MemRegion *> Regions;
8891

@@ -177,6 +180,18 @@ class BugReport : public llvm::ilist_node<BugReport> {
177180
const BugType& getBugType() const { return BT; }
178181
BugType& getBugType() { return BT; }
179182

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

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

Diff for: include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h

+20-2
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.

Diff for: lib/Rewrite/HTMLRewrite.cpp

+7-2
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

Diff for: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp

+7
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+
}

Diff for: lib/StaticAnalyzer/Core/BugReporter.cpp

+54-18
Original file line numberDiff line numberDiff line change
@@ -112,15 +112,15 @@ static void removeRedundantMsgs(PathPieces &path) {
112112
path.pop_front();
113113

114114
switch (piece->getKind()) {
115-
case clang::ento::PathDiagnosticPiece::Call:
115+
case PathDiagnosticPiece::Call:
116116
removeRedundantMsgs(cast<PathDiagnosticCallPiece>(piece)->path);
117117
break;
118-
case clang::ento::PathDiagnosticPiece::Macro:
118+
case PathDiagnosticPiece::Macro:
119119
removeRedundantMsgs(cast<PathDiagnosticMacroPiece>(piece)->subPieces);
120120
break;
121-
case clang::ento::PathDiagnosticPiece::ControlFlow:
121+
case PathDiagnosticPiece::ControlFlow:
122122
break;
123-
case clang::ento::PathDiagnosticPiece::Event: {
123+
case PathDiagnosticPiece::Event: {
124124
if (i == N-1)
125125
break;
126126

@@ -140,6 +140,8 @@ static void removeRedundantMsgs(PathPieces &path) {
140140
}
141141
break;
142142
}
143+
case PathDiagnosticPiece::Note:
144+
break;
143145
}
144146
path.push_back(piece);
145147
}
@@ -197,6 +199,9 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R,
197199
}
198200
case PathDiagnosticPiece::ControlFlow:
199201
break;
202+
203+
case PathDiagnosticPiece::Note:
204+
break;
200205
}
201206

202207
pieces.push_back(piece);
@@ -3403,25 +3408,28 @@ void BugReporter::FlushReport(BugReport *exampleReport,
34033408
exampleReport->getUniqueingLocation(),
34043409
exampleReport->getUniqueingDecl()));
34053410

3406-
MaxBugClassSize = std::max(bugReports.size(),
3407-
static_cast<size_t>(MaxBugClassSize));
3411+
if (exampleReport->isPathSensitive()) {
3412+
// Generate the full path diagnostic, using the generation scheme
3413+
// specified by the PathDiagnosticConsumer. Note that we have to generate
3414+
// path diagnostics even for consumers which do not support paths, because
3415+
// the BugReporterVisitors may mark this bug as a false positive.
3416+
assert(!bugReports.empty());
3417+
3418+
MaxBugClassSize =
3419+
std::max(bugReports.size(), static_cast<size_t>(MaxBugClassSize));
34083420

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

3417-
MaxValidBugClassSize = std::max(bugReports.size(),
3418-
static_cast<size_t>(MaxValidBugClassSize));
3424+
MaxValidBugClassSize =
3425+
std::max(bugReports.size(), static_cast<size_t>(MaxValidBugClassSize));
34193426

3420-
// Examine the report and see if the last piece is in a header. Reset the
3421-
// report location to the last piece in the main source file.
3422-
AnalyzerOptions& Opts = getAnalyzerOptions();
3423-
if (Opts.shouldReportIssuesInMainSourceFile() && !Opts.AnalyzeAll)
3424-
D->resetDiagnosticLocationToMainFile();
3427+
// Examine the report and see if the last piece is in a header. Reset the
3428+
// report location to the last piece in the main source file.
3429+
AnalyzerOptions &Opts = getAnalyzerOptions();
3430+
if (Opts.shouldReportIssuesInMainSourceFile() && !Opts.AnalyzeAll)
3431+
D->resetDiagnosticLocationToMainFile();
3432+
}
34253433

34263434
// If the path is empty, generate a single step path with the location
34273435
// of the issue.
@@ -3434,6 +3442,27 @@ void BugReporter::FlushReport(BugReport *exampleReport,
34343442
D->setEndOfPath(std::move(piece));
34353443
}
34363444

3445+
PathPieces &Pieces = D->getMutablePieces();
3446+
if (getAnalyzerOptions().shouldDisplayNotesAsEvents()) {
3447+
// For path diagnostic consumers that don't support extra notes,
3448+
// we may optionally convert those to path notes.
3449+
for (auto I = exampleReport->getNotes().rbegin(),
3450+
E = exampleReport->getNotes().rend(); I != E; ++I) {
3451+
PathDiagnosticNotePiece *Piece = I->get();
3452+
PathDiagnosticEventPiece *ConvertedPiece =
3453+
new PathDiagnosticEventPiece(Piece->getLocation(),
3454+
Piece->getString());
3455+
for (const auto &R: Piece->getRanges())
3456+
ConvertedPiece->addRange(R);
3457+
3458+
Pieces.push_front(ConvertedPiece);
3459+
}
3460+
} else {
3461+
for (auto I = exampleReport->getNotes().rbegin(),
3462+
E = exampleReport->getNotes().rend(); I != E; ++I)
3463+
Pieces.push_front(*I);
3464+
}
3465+
34373466
// Get the meta data.
34383467
const BugReport::ExtraTextList &Meta = exampleReport->getExtraText();
34393468
for (BugReport::ExtraTextList::const_iterator i = Meta.begin(),
@@ -3518,6 +3547,13 @@ LLVM_DUMP_METHOD void PathDiagnosticMacroPiece::dump() const {
35183547
// FIXME: Print which macro is being invoked.
35193548
}
35203549

3550+
LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const {
3551+
llvm::errs() << "NOTE\n--------------\n";
3552+
llvm::errs() << getString() << "\n";
3553+
llvm::errs() << " ---- at ----\n";
3554+
getLocation().dump();
3555+
}
3556+
35213557
LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const {
35223558
if (!isValid()) {
35233559
llvm::errs() << "<INVALID>\n";

0 commit comments

Comments
 (0)