(I->get())) {
+ int LineNumber =
+ P->getLocation().asLocation().getExpansionLineNumber();
+ int ColumnNumber =
+ P->getLocation().asLocation().getExpansionColumnNumber();
+ os << "Note: | "
+ << "line "
+ << LineNumber << ", column " << ColumnNumber << " "
+ << P->getString() << " |
";
+ ++NumExtraPieces;
+ }
+ }
// Output any other meta data.
@@ -385,21 +415,30 @@
// Create the html for the message.
const char *Kind = nullptr;
+ bool IsExtraNote = false;
+ bool SuppressIndex = (max == 1);
switch (P.getKind()) {
case PathDiagnosticPiece::Call:
- llvm_unreachable("Calls should already be handled");
+ llvm_unreachable("Calls and extra notes should already be handled");
case PathDiagnosticPiece::Event: Kind = "Event"; break;
case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break;
// Setting Kind to "Control" is intentional.
case PathDiagnosticPiece::Macro: Kind = "Control"; break;
+ case PathDiagnosticPiece::ExtraNote:
+ Kind = "ExtraNote";
+ IsExtraNote = true;
+ SuppressIndex = true;
+ break;
}
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
os << "\n | ";
- if (max > 1) {
+ if (!SuppressIndex) {
os << " ";
os << " 1) {
+ if (!SuppressIndex) {
os << " | ";
if (num < max) {
os << " | ";
if (num < max) {
os << "getKind()) {
- case clang::ento::PathDiagnosticPiece::Call:
+ case PathDiagnosticPiece::Call:
removeRedundantMsgs(cast(piece)->path);
break;
- case clang::ento::PathDiagnosticPiece::Macro:
+ case PathDiagnosticPiece::Macro:
removeRedundantMsgs(cast(piece)->subPieces);
break;
- case clang::ento::PathDiagnosticPiece::ControlFlow:
+ case PathDiagnosticPiece::ControlFlow:
break;
- case clang::ento::PathDiagnosticPiece::Event: {
+ case PathDiagnosticPiece::Event: {
if (i == N-1)
break;
@@ -140,6 +140,8 @@
}
break;
}
+ case PathDiagnosticPiece::ExtraNote:
+ break;
}
path.push_back(piece);
}
@@ -197,6 +199,9 @@
}
case PathDiagnosticPiece::ControlFlow:
break;
+
+ case PathDiagnosticPiece::ExtraNote:
+ break;
}
pieces.push_back(piece);
@@ -3403,25 +3408,28 @@
exampleReport->getUniqueingLocation(),
exampleReport->getUniqueingDecl()));
- MaxBugClassSize = std::max(bugReports.size(),
- static_cast(MaxBugClassSize));
+ if (exampleReport->isPathSensitive()) {
+ // Generate the full path diagnostic, using the generation scheme
+ // specified by the PathDiagnosticConsumer. Note that we have to generate
+ // path diagnostics even for consumers which do not support paths, because
+ // the BugReporterVisitors may mark this bug as a false positive.
+ assert(!bugReports.empty());
+
+ MaxBugClassSize =
+ std::max(bugReports.size(), static_cast(MaxBugClassSize));
- // Generate the full path diagnostic, using the generation scheme
- // specified by the PathDiagnosticConsumer. Note that we have to generate
- // path diagnostics even for consumers which do not support paths, because
- // the BugReporterVisitors may mark this bug as a false positive.
- if (!bugReports.empty())
if (!generatePathDiagnostic(*D.get(), PD, bugReports))
return;
- MaxValidBugClassSize = std::max(bugReports.size(),
- static_cast(MaxValidBugClassSize));
+ MaxValidBugClassSize =
+ std::max(bugReports.size(), static_cast(MaxValidBugClassSize));
- // Examine the report and see if the last piece is in a header. Reset the
- // report location to the last piece in the main source file.
- AnalyzerOptions& Opts = getAnalyzerOptions();
- if (Opts.shouldReportIssuesInMainSourceFile() && !Opts.AnalyzeAll)
- D->resetDiagnosticLocationToMainFile();
+ // Examine the report and see if the last piece is in a header. Reset the
+ // report location to the last piece in the main source file.
+ AnalyzerOptions &Opts = getAnalyzerOptions();
+ if (Opts.shouldReportIssuesInMainSourceFile() && !Opts.AnalyzeAll)
+ D->resetDiagnosticLocationToMainFile();
+ }
// If the path is empty, generate a single step path with the location
// of the issue.
@@ -3434,6 +3442,27 @@
D->setEndOfPath(std::move(piece));
}
+ PathPieces &Pieces = D->getMutablePieces();
+ if (getAnalyzerOptions().shouldDisplayExtraNotesAsEvents()) {
+ // For path diagnostic consumers that don't support extra notes,
+ // we may optionally convert those to path notes.
+ for (auto I = exampleReport->getExtraNotes().rbegin(),
+ E = exampleReport->getExtraNotes().rend(); I != E; ++I) {
+ PathDiagnosticExtraNotePiece *Piece = I->get();
+ PathDiagnosticEventPiece *ConvertedPiece =
+ new PathDiagnosticEventPiece(Piece->getLocation(),
+ Piece->getString());
+ for (const auto &R: Piece->getRanges())
+ ConvertedPiece->addRange(R);
+
+ Pieces.push_front(ConvertedPiece);
+ }
+ } else {
+ for (auto I = exampleReport->getExtraNotes().rbegin(),
+ E = exampleReport->getExtraNotes().rend(); I != E; ++I)
+ Pieces.push_front(*I);
+ }
+
// Get the meta data.
const BugReport::ExtraTextList &Meta = exampleReport->getExtraText();
for (BugReport::ExtraTextList::const_iterator i = Meta.begin(),
@@ -3518,6 +3547,13 @@
// FIXME: Print which macro is being invoked.
}
+LLVM_DUMP_METHOD void PathDiagnosticExtraNotePiece::dump() const {
+ llvm::errs() << "EXTRANOTE\n--------------\n";
+ llvm::errs() << getString() << "\n";
+ llvm::errs() << " ---- at ----\n";
+ getLocation().dump();
+}
+
LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const {
if (!isValid()) {
llvm::errs() << "\n";
Index: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
===================================================================
--- lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -344,3 +344,11 @@
WidenLoops = getBooleanOption("widen-loops", /*Default=*/false);
return WidenLoops.getValue();
}
+
+
+bool AnalyzerOptions::shouldDisplayExtraNotesAsEvents() {
+ if (!DisplayExtraNotesAsEvents.hasValue())
+ DisplayExtraNotesAsEvents =
+ getBooleanOption("extra-notes-as-events", /*Default=*/false);
+ return DisplayExtraNotesAsEvents.getValue();
+}
Index: lib/StaticAnalyzer/Checkers/CloneChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/CloneChecker.cpp
+++ lib/StaticAnalyzer/Checkers/CloneChecker.cpp
@@ -16,8 +16,10 @@
#include "ClangSACheckers.h"
#include "clang/Analysis/CloneDetection.h"
#include "clang/Basic/Diagnostic.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
using namespace clang;
@@ -27,6 +29,7 @@
class CloneChecker
: public Checker {
mutable CloneDetector Detector;
+ mutable std::unique_ptr BT_Exact, BT_Suspicious;
public:
void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
@@ -36,12 +39,12 @@
AnalysisManager &Mgr, BugReporter &BR) const;
/// \brief Reports all clones to the user.
- void reportClones(SourceManager &SM, AnalysisManager &Mgr,
+ void reportClones(BugReporter &BR, AnalysisManager &Mgr,
int MinComplexity) const;
/// \brief Reports only suspicious clones to the user along with informaton
/// that explain why they are suspicious.
- void reportSuspiciousClones(SourceManager &SM, AnalysisManager &Mgr,
+ void reportSuspiciousClones(BugReporter &BR, AnalysisManager &Mgr,
int MinComplexity) const;
};
} // end anonymous namespace
@@ -70,79 +73,93 @@
"ReportNormalClones", true, this);
if (ReportSuspiciousClones)
- reportSuspiciousClones(BR.getSourceManager(), Mgr, MinComplexity);
+ reportSuspiciousClones(BR, Mgr, MinComplexity);
if (ReportNormalClones)
- reportClones(BR.getSourceManager(), Mgr, MinComplexity);
+ reportClones(BR, Mgr, MinComplexity);
}
-void CloneChecker::reportClones(SourceManager &SM, AnalysisManager &Mgr,
+static PathDiagnosticLocation makeLocation(const StmtSequence &S,
+ AnalysisManager &Mgr) {
+ ASTContext &ACtx = Mgr.getASTContext();
+ return PathDiagnosticLocation::createBegin(
+ S.front(), ACtx.getSourceManager(),
+ Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl()));
+}
+
+void CloneChecker::reportClones(BugReporter &BR, AnalysisManager &Mgr,
int MinComplexity) const {
std::vector CloneGroups;
Detector.findClones(CloneGroups, MinComplexity);
- DiagnosticsEngine &DiagEngine = Mgr.getDiagnostic();
-
- unsigned WarnID = DiagEngine.getCustomDiagID(DiagnosticsEngine::Warning,
- "Detected code clone.");
-
- unsigned NoteID = DiagEngine.getCustomDiagID(DiagnosticsEngine::Note,
- "Related code clone is here.");
+ if (!BT_Exact)
+ BT_Exact.reset(new BugType(this, "Exact code clone", "Code clone"));
for (CloneDetector::CloneGroup &Group : CloneGroups) {
// We group the clones by printing the first as a warning and all others
// as a note.
- DiagEngine.Report(Group.Sequences.front().getStartLoc(), WarnID);
- for (unsigned i = 1; i < Group.Sequences.size(); ++i) {
- DiagEngine.Report(Group.Sequences[i].getStartLoc(), NoteID);
- }
+ auto R = llvm::make_unique(
+ *BT_Exact, "Detected code clone",
+ makeLocation(Group.Sequences.front(), Mgr));
+ R->addRange(Group.Sequences.front().getSourceRange());
+
+ for (unsigned i = 1; i < Group.Sequences.size(); ++i)
+ R->addExtraNote("Related code clone is here",
+ makeLocation(Group.Sequences[i], Mgr),
+ Group.Sequences[i].getSourceRange());
+ BR.emitReport(std::move(R));
}
}
-void CloneChecker::reportSuspiciousClones(SourceManager &SM,
+void CloneChecker::reportSuspiciousClones(BugReporter &BR,
AnalysisManager &Mgr,
int MinComplexity) const {
std::vector Clones;
Detector.findSuspiciousClones(Clones, MinComplexity);
- DiagnosticsEngine &DiagEngine = Mgr.getDiagnostic();
-
- auto SuspiciousCloneWarning = DiagEngine.getCustomDiagID(
- DiagnosticsEngine::Warning, "suspicious code clone detected; did you "
- "mean to use %0?");
-
- auto RelatedCloneNote = DiagEngine.getCustomDiagID(
- DiagnosticsEngine::Note, "suggestion is based on the usage of this "
- "variable in a similar piece of code");
+ if (!BT_Suspicious)
+ BT_Suspicious.reset(
+ new BugType(this, "Suspicious code clone", "Code clone"));
- auto RelatedSuspiciousCloneNote = DiagEngine.getCustomDiagID(
- DiagnosticsEngine::Note, "suggestion is based on the usage of this "
- "variable in a similar piece of code; did you "
- "mean to use %0?");
+ ASTContext &ACtx = BR.getContext();
+ SourceManager &SM = ACtx.getSourceManager();
+ AnalysisDeclContext *ADC =
+ Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl());
for (CloneDetector::SuspiciousClonePair &Pair : Clones) {
// The first clone always has a suggestion and we report it to the user
// along with the place where the suggestion should be used.
- DiagEngine.Report(Pair.FirstCloneInfo.VarRange.getBegin(),
- SuspiciousCloneWarning)
- << Pair.FirstCloneInfo.VarRange << Pair.FirstCloneInfo.Suggestion;
+ auto R = llvm::make_unique(
+ *BT_Suspicious,
+ "Suspicious code clone detected; did you mean to use '" +
+ Pair.FirstCloneInfo.Suggestion->getNameAsString() + "'?",
+ PathDiagnosticLocation::createBegin(Pair.FirstCloneInfo.Mention, SM,
+ ADC));
+ R->addRange(Pair.FirstCloneInfo.Mention->getSourceRange());
- // The second clone can have a suggestion and if there is one, we report
- // that suggestion to the user.
if (Pair.SecondCloneInfo.Suggestion) {
- DiagEngine.Report(Pair.SecondCloneInfo.VarRange.getBegin(),
- RelatedSuspiciousCloneNote)
- << Pair.SecondCloneInfo.VarRange << Pair.SecondCloneInfo.Suggestion;
- continue;
+ // The second clone can have a suggestion and if there is one, we report
+ // that suggestion to the user.
+ R->addExtraNote("Suggestion is based on the usage of this variable in a "
+ "similar piece of code; did you mean to use '" +
+ Pair.SecondCloneInfo.Suggestion->getNameAsString() +
+ "'?",
+ PathDiagnosticLocation::createBegin(
+ Pair.SecondCloneInfo.Mention, SM, ADC),
+ Pair.SecondCloneInfo.Mention->getSourceRange());
+ } else {
+ // If there isn't a suggestion in the second clone, we only inform the
+ // user where we got the idea that his code could contain an error.
+ R->addExtraNote("Suggestion is based on the usage of this variable in a "
+ "similar piece of code",
+ PathDiagnosticLocation::createBegin(
+ Pair.SecondCloneInfo.Mention, SM, ADC),
+ Pair.SecondCloneInfo.Mention->getSourceRange());
}
- // If there isn't a suggestion in the second clone, we only inform the
- // user where we got the idea that his code could contain an error.
- DiagEngine.Report(Pair.SecondCloneInfo.VarRange.getBegin(),
- RelatedCloneNote)
- << Pair.SecondCloneInfo.VarRange;
+ BR.emitReport(std::move(R));
}
}
Index: lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
+++ lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
@@ -128,6 +128,9 @@
void checkEndFunction(CheckerContext &Ctx) const;
private:
+ void addExtraNoteForDecl(std::unique_ptr &BR, StringRef Msg,
+ const Decl *D) const;
+
void diagnoseMissingReleases(CheckerContext &C) const;
bool diagnoseExtraRelease(SymbolRef ReleasedValue, const ObjCMethodCall &M,
@@ -489,6 +492,18 @@
return State;
}
+/// Add an extra note piece describing a declaration that is important
+/// for understanding the bug report.
+void ObjCDeallocChecker::addExtraNoteForDecl(std::unique_ptr &BR,
+ StringRef Msg,
+ const Decl *D) const {
+ ASTContext &ACtx = D->getASTContext();
+ SourceManager &SM = ACtx.getSourceManager();
+ PathDiagnosticLocation Pos = PathDiagnosticLocation::createBegin(D, SM);
+ if (Pos.isValid() && Pos.asLocation().isValid())
+ BR->addExtraNote(Msg, Pos, D->getSourceRange());
+}
+
/// Report any unreleased instance variables for the current instance being
/// dealloced.
void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const {
@@ -586,6 +601,10 @@
std::unique_ptr BR(
new BugReport(*MissingReleaseBugType, OS.str(), ErrNode));
+ addExtraNoteForDecl(BR, "The property is declared here", PropDecl);
+
+ addExtraNoteForDecl(BR, "The property is synthesized here", PropImpl);
+
C.emitReport(std::move(BR));
}
@@ -689,11 +708,12 @@
);
const ObjCImplDecl *Container = getContainingObjCImpl(C.getLocationContext());
- OS << "The '" << *PropImpl->getPropertyIvarDecl()
- << "' ivar in '" << *Container;
+ const ObjCIvarDecl *IvarDecl = PropImpl->getPropertyIvarDecl();
+ OS << "The '" << *IvarDecl << "' ivar in '" << *Container;
+ bool ReleasedByCIFilterDealloc = isReleasedByCIFilterDealloc(PropImpl);
- if (isReleasedByCIFilterDealloc(PropImpl)) {
+ if (ReleasedByCIFilterDealloc) {
OS << "' will be released by '-[CIFilter dealloc]' but also released here";
} else {
OS << "' was synthesized for ";
@@ -710,6 +730,11 @@
new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode));
BR->addRange(M.getOriginExpr()->getSourceRange());
+ addExtraNoteForDecl(BR, "The property is declared here", PropDecl);
+
+ if (!ReleasedByCIFilterDealloc)
+ addExtraNoteForDecl(BR, "The property is synthesized here", PropImpl);
+
C.emitReport(std::move(BR));
return true;
Index: lib/Rewrite/HTMLRewrite.cpp
===================================================================
--- lib/Rewrite/HTMLRewrite.cpp
+++ lib/Rewrite/HTMLRewrite.cpp
@@ -321,6 +321,7 @@
" .msgT { padding:0x; spacing:0x }\n"
" .msgEvent { background-color:#fff8b4; color:#000000 }\n"
" .msgControl { background-color:#bbbbbb; color:#000000 }\n"
+ " .msgExtraNote { background-color:#ddeeff; color:#000000 }\n"
" .mrange { background-color:#dfddf3 }\n"
" .mrange { border-bottom:1px solid #6F9DBE }\n"
" .PathIndex { font-weight: bold; padding:0px 5px; "
@@ -339,8 +340,12 @@
" border-collapse: collapse; border-spacing: 0px;\n"
" }\n"
" td.rowname {\n"
- " text-align:right; font-weight:bold; color:#444444;\n"
- " padding-right:2ex; }\n"
+ " text-align: right;\n"
+ " vertical-align: top;\n"
+ " font-weight: bold;\n"
+ " color:#444444;\n"
+ " padding-right:2ex;\n"
+ " }\n"
"\n\n";
// Generate header
Index: lib/Analysis/CloneDetection.cpp
===================================================================
--- lib/Analysis/CloneDetection.cpp
+++ lib/Analysis/CloneDetection.cpp
@@ -82,6 +82,10 @@
SourceLocation StmtSequence::getEndLoc() const { return back()->getLocEnd(); }
+SourceRange StmtSequence::getSourceRange() const {
+ return SourceRange(getStartLoc(), getEndLoc());
+}
+
namespace {
/// \brief Analyzes the pattern of the referenced variables in a statement.
@@ -91,11 +95,11 @@
struct VariableOccurence {
/// The index of the associated VarDecl in the Variables vector.
size_t KindID;
- /// The source range in the code where the variable was referenced.
- SourceRange Range;
+ /// The statement in the code where the variable was referenced.
+ const Stmt *Mention;
- VariableOccurence(size_t KindID, SourceRange Range)
- : KindID(KindID), Range(Range) {}
+ VariableOccurence(size_t KindID, const Stmt *Mention)
+ : KindID(KindID), Mention(Mention) {}
};
/// All occurences of referenced variables in the order of appearance.
@@ -107,19 +111,19 @@
/// \brief Adds a new variable referenced to this pattern.
/// \param VarDecl The declaration of the variable that is referenced.
/// \param Range The SourceRange where this variable is referenced.
- void addVariableOccurence(const VarDecl *VarDecl, SourceRange Range) {
+ void addVariableOccurence(const VarDecl *VarDecl, const Stmt *Mention) {
// First check if we already reference this variable
for (size_t KindIndex = 0; KindIndex < Variables.size(); ++KindIndex) {
if (Variables[KindIndex] == VarDecl) {
// If yes, add a new occurence that points to the existing entry in
// the Variables vector.
- Occurences.emplace_back(KindIndex, Range);
+ Occurences.emplace_back(KindIndex, Mention);
return;
}
}
// If this variable wasn't already referenced, add it to the list of
// referenced variables and add a occurence that points to this new entry.
- Occurences.emplace_back(Variables.size(), Range);
+ Occurences.emplace_back(Variables.size(), Mention);
Variables.push_back(VarDecl);
}
@@ -134,7 +138,7 @@
// Check if S is a reference to a variable. If yes, add it to the pattern.
if (auto D = dyn_cast(S)) {
if (auto VD = dyn_cast(D->getDecl()->getCanonicalDecl()))
- addVariableOccurence(VD, D->getSourceRange());
+ addVariableOccurence(VD, D);
}
// Recursively check all children of the given statement.
@@ -208,7 +212,7 @@
// Store information about the first clone.
FirstMismatch->FirstCloneInfo =
CloneDetector::SuspiciousClonePair::SuspiciousCloneInfo(
- Variables[ThisOccurence.KindID], ThisOccurence.Range,
+ Variables[ThisOccurence.KindID], ThisOccurence.Mention,
FirstSuggestion);
// Same as above but with the other clone. We do this for both clones as
@@ -221,7 +225,7 @@
// Store information about the second clone.
FirstMismatch->SecondCloneInfo =
CloneDetector::SuspiciousClonePair::SuspiciousCloneInfo(
- Variables[ThisOccurence.KindID], OtherOccurence.Range,
+ Variables[ThisOccurence.KindID], OtherOccurence.Mention,
SecondSuggestion);
// SuspiciousClonePair guarantees that the first clone always has a
Index: include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
===================================================================
--- include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
+++ include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
@@ -336,7 +336,7 @@
class PathDiagnosticPiece : public RefCountedBaseVPTR {
public:
- enum Kind { ControlFlow, Event, Macro, Call };
+ enum Kind { ControlFlow, Event, Macro, Call, ExtraNote };
enum DisplayHint { Above, Below };
private:
@@ -452,7 +452,8 @@
void Profile(llvm::FoldingSetNodeID &ID) const override;
static bool classof(const PathDiagnosticPiece *P) {
- return P->getKind() == Event || P->getKind() == Macro;
+ return P->getKind() == Event || P->getKind() == Macro ||
+ P->getKind() == ExtraNote;
}
};
@@ -710,6 +711,23 @@
void Profile(llvm::FoldingSetNodeID &ID) const override;
};
+class PathDiagnosticExtraNotePiece: public PathDiagnosticSpotPiece {
+public:
+ PathDiagnosticExtraNotePiece(const PathDiagnosticLocation &Pos, StringRef S,
+ bool AddPosRange = true)
+ : PathDiagnosticSpotPiece(Pos, S, ExtraNote, AddPosRange) {}
+
+ ~PathDiagnosticExtraNotePiece() override;
+
+ static inline bool classof(const PathDiagnosticPiece *P) {
+ return P->getKind() == ExtraNote;
+ }
+
+ void dump() const override;
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override;
+};
+
/// PathDiagnostic - PathDiagnostic objects represent a single path-sensitive
/// diagnostic. It represents an ordered-collection of PathDiagnosticPieces,
/// each which represent the pieces of the path.
Index: include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h
===================================================================
--- include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h
+++ include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h
@@ -66,6 +66,8 @@
typedef SmallVector, 8> VisitorList;
typedef VisitorList::iterator visitor_iterator;
typedef SmallVector ExtraTextList;
+ typedef SmallVector, 4>
+ ExtraNoteList;
protected:
friend class BugReporter;
@@ -82,7 +84,8 @@
const ExplodedNode *ErrorNode;
SmallVector Ranges;
ExtraTextList ExtraText;
-
+ ExtraNoteList ExtraNotes;
+
typedef llvm::DenseSet Symbols;
typedef llvm::DenseSet Regions;
@@ -177,6 +180,18 @@
const BugType& getBugType() const { return BT; }
BugType& getBugType() { return BT; }
+ /// \brief True when the report has an execution path associated with it.
+ ///
+ /// A report is said to be path-sensitive if it was thrown against a
+ /// particular exploded node in the path-sensitive analysis graph.
+ /// Path-sensitive reports have their intermediate path diagnostics
+ /// auto-generated, perhaps with the help of checker-defined visitors,
+ /// and may contain extra notes.
+ /// Path-insensitive reports consist only of a single warning message
+ /// in a specific location, and perhaps extra notes.
+ /// Path-sensitive checkers are allowed to throw path-insensitive reports.
+ bool isPathSensitive() const { return ErrorNode != nullptr; }
+
const ExplodedNode *getErrorNode() const { return ErrorNode; }
StringRef getDescription() const { return Description; }
@@ -245,7 +260,27 @@
void setDeclWithIssue(const Decl *declWithIssue) {
DeclWithIssue = declWithIssue;
}
-
+
+ /// Add new item to the list of additional notes that need to be attached to
+ /// this path-insensitive report. If you want to add extra notes to a
+ /// path-sensitive report, you need to use a BugReporterVisitor because it
+ /// allows you to specify where exactly in the auto-generated path diagnostic
+ /// the extra note should appear.
+ void addExtraNote(StringRef Msg, const PathDiagnosticLocation &Pos,
+ ArrayRef Ranges = {}) {
+ PathDiagnosticExtraNotePiece *P =
+ new PathDiagnosticExtraNotePiece(Pos, Msg);
+
+ for (const auto &R : Ranges)
+ P->addRange(R);
+
+ ExtraNotes.push_back(P);
+ }
+
+ virtual const ExtraNoteList &getExtraNotes() {
+ return ExtraNotes;
+ }
+
/// \brief This allows for addition of meta data to the diagnostic.
///
/// Currently, only the HTMLDiagnosticClient knows how to display it.
Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
===================================================================
--- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
+++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
@@ -266,6 +266,9 @@
/// \sa shouldWidenLoops
Optional WidenLoops;
+ /// \sa shouldDisplayExtraNotesAsEvents
+ Optional DisplayExtraNotesAsEvents;
+
/// A helper function that retrieves option for a given full-qualified
/// checker name.
/// Options for checkers can be specified via 'analyzer-config' command-line
@@ -534,6 +537,14 @@
/// This is controlled by the 'widen-loops' config option.
bool shouldWidenLoops();
+ /// Returns true if the bug reporter should transparently treat extra note
+ /// diagnostic pieces as event diagnostic pieces. Useful when the diagnostic
+ /// consumer doesn't support the extra note pieces.
+ ///
+ /// This is controlled by the 'extra-notes-as-events' option, which defaults
+ /// to false when unset.
+ bool shouldDisplayExtraNotesAsEvents();
+
public:
AnalyzerOptions() :
AnalysisStoreOpt(RegionStoreModel),
Index: include/clang/Analysis/CloneDetection.h
===================================================================
--- include/clang/Analysis/CloneDetection.h
+++ include/clang/Analysis/CloneDetection.h
@@ -128,6 +128,10 @@
/// This method should only be called on a non-empty StmtSequence object.
SourceLocation getEndLoc() const;
+ /// Returns the source range of the whole sequence - from the beginning
+ /// of the first statement to the end of the last statement.
+ SourceRange getSourceRange() const;
+
bool operator==(const StmtSequence &Other) const {
return std::tie(S, StartIndex, EndIndex) ==
std::tie(Other.S, Other.StartIndex, Other.EndIndex);
@@ -250,14 +254,14 @@
/// The variable which referencing in this clone was against the pattern.
const VarDecl *Variable;
/// Where the variable was referenced.
- SourceRange VarRange;
+ const Stmt *Mention;
/// The variable that should have been referenced to follow the pattern.
/// If Suggestion is a nullptr then it's not possible to fix the pattern
/// by referencing a different variable in this clone.
const VarDecl *Suggestion;
- SuspiciousCloneInfo(const VarDecl *Variable, SourceRange Range,
+ SuspiciousCloneInfo(const VarDecl *Variable, const Stmt *Mention,
const VarDecl *Suggestion)
- : Variable(Variable), VarRange(Range), Suggestion(Suggestion) {}
+ : Variable(Variable), Mention(Mention), Suggestion(Suggestion) {}
SuspiciousCloneInfo() {}
};
/// The first clone in the pair which always has a suggested variable.
|
|