NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a_sidorin, rnkovacs, Szelethus,
baloghadamsoftware.
Herald added subscribers: cfe-commits, Charusso, dkrupp, donat.nagy,
mikhail.ramalho, a.sidorin, szepet, kristof.beyls, javed.absar.
Herald added a project: clang.
That's a pretty wonky experiment, even if a fairly easy one. I'm trying to add
support for fix-it hints to the bug reporter. In the current shape the patch
does the following:
- Allow attaching fixit hints to Static Analyzer `BugReport`s.
- Add support for fixits in text output (including the default text output that
goes without notes, as the fixit "belongs" to the warning).
- Add support for fixits in the plist output mode (not sure if i'm fully
supporting all kinds of fixits).
- Implement a fixit for the path-insensitive DeadStores checker (only one of
the cases, and i'm still not sure it's a good fixit, but it was an obvious
first thing to experiment with).
- Implement a fixit for the path-sensitive VirtualCall checker when the virtual
method is not pure virtual (in this case the "fix" is to suppress the warning
by qualifying the call).
More TODOs:
- We don't have a good way to test removal fixits (such as the one in the dead
stores checker). Testing plist files is not a good way to test them (though we
definitely need a few such tests). We can't test them via text output because
clang itself generally doesn't display removal fixits in text output (it's
kinda obvious if you look at how it prints them). In clang-tidy they use a more
sophisticated facility for these tests, testing the fixed file instead, but
it's deep within their custom testing scripts. We might need something similar.
- HTML output still doesn't support fixits. Not sure if they are useful in
there because fixits are generally not very useful when there's no button to
apply them. But there should be no harm in displaying them anyway, so i hope
i'll have time to take a look.
- Need more tests with macros and such stuff.
Repository:
rC Clang
https://reviews.llvm.org/D65182
Files:
clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h
clang/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
clang/lib/StaticAnalyzer/Core/BugReporter.cpp
clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
clang/test/Analysis/Inputs/expected-plists/edges-new.mm.plist
clang/test/Analysis/Inputs/expected-plists/objc-arc.m.plist
clang/test/Analysis/Inputs/expected-plists/plist-output.m.plist
Index: clang/test/Analysis/Inputs/expected-plists/plist-output.m.plist
===================================================================
--- clang/test/Analysis/Inputs/expected-plists/plist-output.m.plist
+++ clang/test/Analysis/Inputs/expected-plists/plist-output.m.plist
@@ -2190,6 +2190,26 @@
<integer>86</integer>
</array>
</dict>
+ <key>fixits</key>
+ <array>
+ <dict>
+ <key>remove_range</key>
+ <array>
+ <dict>
+ <key>line</key><integer>86</integer>
+ <key>col</key><integer>11</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <dict>
+ <key>line</key><integer>86</integer>
+ <key>col</key><integer>40</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ </array>
+ <key>insert_string</key>
+ <string></string>
+ </dict>
+ </array>
</dict>
<dict>
<key>path</key>
Index: clang/test/Analysis/Inputs/expected-plists/objc-arc.m.plist
===================================================================
--- clang/test/Analysis/Inputs/expected-plists/objc-arc.m.plist
+++ clang/test/Analysis/Inputs/expected-plists/objc-arc.m.plist
@@ -403,6 +403,26 @@
<integer>119</integer>
</array>
</dict>
+ <key>fixits</key>
+ <array>
+ <dict>
+ <key>remove_range</key>
+ <array>
+ <dict>
+ <key>line</key><integer>119</integer>
+ <key>col</key><integer>7</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <dict>
+ <key>line</key><integer>119</integer>
+ <key>col</key><integer>25</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ </array>
+ <key>insert_string</key>
+ <string></string>
+ </dict>
+ </array>
</dict>
<dict>
<key>path</key>
@@ -471,6 +491,26 @@
<integer>139</integer>
</array>
</dict>
+ <key>fixits</key>
+ <array>
+ <dict>
+ <key>remove_range</key>
+ <array>
+ <dict>
+ <key>line</key><integer>139</integer>
+ <key>col</key><integer>10</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <dict>
+ <key>line</key><integer>139</integer>
+ <key>col</key><integer>53</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ </array>
+ <key>insert_string</key>
+ <string></string>
+ </dict>
+ </array>
</dict>
<dict>
<key>path</key>
@@ -539,6 +579,26 @@
<integer>144</integer>
</array>
</dict>
+ <key>fixits</key>
+ <array>
+ <dict>
+ <key>remove_range</key>
+ <array>
+ <dict>
+ <key>line</key><integer>144</integer>
+ <key>col</key><integer>10</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <dict>
+ <key>line</key><integer>144</integer>
+ <key>col</key><integer>45</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ </array>
+ <key>insert_string</key>
+ <string></string>
+ </dict>
+ </array>
</dict>
<dict>
<key>path</key>
@@ -607,6 +667,26 @@
<integer>145</integer>
</array>
</dict>
+ <key>fixits</key>
+ <array>
+ <dict>
+ <key>remove_range</key>
+ <array>
+ <dict>
+ <key>line</key><integer>145</integer>
+ <key>col</key><integer>10</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <dict>
+ <key>line</key><integer>145</integer>
+ <key>col</key><integer>44</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ </array>
+ <key>insert_string</key>
+ <string></string>
+ </dict>
+ </array>
</dict>
<dict>
<key>path</key>
@@ -675,6 +755,26 @@
<integer>146</integer>
</array>
</dict>
+ <key>fixits</key>
+ <array>
+ <dict>
+ <key>remove_range</key>
+ <array>
+ <dict>
+ <key>line</key><integer>146</integer>
+ <key>col</key><integer>10</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <dict>
+ <key>line</key><integer>146</integer>
+ <key>col</key><integer>48</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ </array>
+ <key>insert_string</key>
+ <string></string>
+ </dict>
+ </array>
</dict>
<dict>
<key>path</key>
@@ -1085,6 +1185,26 @@
<integer>150</integer>
</array>
</dict>
+ <key>fixits</key>
+ <array>
+ <dict>
+ <key>remove_range</key>
+ <array>
+ <dict>
+ <key>line</key><integer>150</integer>
+ <key>col</key><integer>16</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <dict>
+ <key>line</key><integer>150</integer>
+ <key>col</key><integer>64</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ </array>
+ <key>insert_string</key>
+ <string></string>
+ </dict>
+ </array>
</dict>
<dict>
<key>path</key>
@@ -1153,6 +1273,26 @@
<integer>151</integer>
</array>
</dict>
+ <key>fixits</key>
+ <array>
+ <dict>
+ <key>remove_range</key>
+ <array>
+ <dict>
+ <key>line</key><integer>151</integer>
+ <key>col</key><integer>18</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <dict>
+ <key>line</key><integer>151</integer>
+ <key>col</key><integer>67</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ </array>
+ <key>insert_string</key>
+ <string></string>
+ </dict>
+ </array>
</dict>
<dict>
<key>path</key>
@@ -1221,6 +1361,26 @@
<integer>152</integer>
</array>
</dict>
+ <key>fixits</key>
+ <array>
+ <dict>
+ <key>remove_range</key>
+ <array>
+ <dict>
+ <key>line</key><integer>152</integer>
+ <key>col</key><integer>16</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <dict>
+ <key>line</key><integer>152</integer>
+ <key>col</key><integer>55</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ </array>
+ <key>insert_string</key>
+ <string></string>
+ </dict>
+ </array>
</dict>
<dict>
<key>path</key>
@@ -1289,6 +1449,26 @@
<integer>153</integer>
</array>
</dict>
+ <key>fixits</key>
+ <array>
+ <dict>
+ <key>remove_range</key>
+ <array>
+ <dict>
+ <key>line</key><integer>153</integer>
+ <key>col</key><integer>18</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <dict>
+ <key>line</key><integer>153</integer>
+ <key>col</key><integer>58</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ </array>
+ <key>insert_string</key>
+ <string></string>
+ </dict>
+ </array>
</dict>
<dict>
<key>path</key>
Index: clang/test/Analysis/Inputs/expected-plists/edges-new.mm.plist
===================================================================
--- clang/test/Analysis/Inputs/expected-plists/edges-new.mm.plist
+++ clang/test/Analysis/Inputs/expected-plists/edges-new.mm.plist
@@ -11430,6 +11430,26 @@
<integer>431</integer>
</array>
</dict>
+ <key>fixits</key>
+ <array>
+ <dict>
+ <key>remove_range</key>
+ <array>
+ <dict>
+ <key>line</key><integer>431</integer>
+ <key>col</key><integer>11</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <dict>
+ <key>line</key><integer>431</integer>
+ <key>col</key><integer>40</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ </array>
+ <key>insert_string</key>
+ <string></string>
+ </dict>
+ </array>
</dict>
<dict>
<key>path</key>
Index: clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -116,8 +116,9 @@
E = Diags.end(); I != E; ++I) {
const PathDiagnostic *PD = *I;
SourceLocation WarnLoc = PD->getLocation().asLocation();
- Diag.Report(WarnLoc, WarnID) << PD->getShortDescription()
- << PD->path.back()->getRanges();
+ Diag.Report(WarnLoc, WarnID)
+ << PD->getShortDescription() << PD->path.back()->getRanges()
+ << PD->getFixits();
// First, add extra notes, even if paths should not be included.
for (const auto &Piece : PD->path) {
Index: clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
+++ clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -726,6 +726,22 @@
printCoverage(D, /*IndentLevel=*/2, Fids, FM, o);
+ if (!D->getFixits().empty()) {
+ o << " <key>fixits</key>\n";
+ o << " <array>\n";
+ for (auto Hint : D->getFixits()) {
+ assert(!Hint.isNull());
+ o << " <dict>\n";
+ o << " <key>remove_range</key>\n";
+ EmitRange(o, SM, Lexer::getAsCharRange(Hint.RemoveRange, SM, LangOpts),
+ FM, 4);
+ o << " <key>insert_string</key>\n";
+ o << " <string>" << Hint.CodeToInsert << "</string>\n";
+ o << " </dict>\n";
+ }
+ o << " </array>\n";
+ }
+
// Close up the entry.
o << " </dict>\n";
}
Index: clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
+++ clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
@@ -134,14 +134,15 @@
StringRef CheckName, const Decl *declWithIssue, StringRef bugtype,
StringRef verboseDesc, StringRef shortDesc, StringRef category,
PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique,
- std::unique_ptr<FilesToLineNumsMap> ExecutedLines)
+ std::unique_ptr<FilesToLineNumsMap> ExecutedLines,
+ ArrayRef<FixItHint> Fixits)
: CheckName(CheckName), DeclWithIssue(declWithIssue),
BugType(StripTrailingDots(bugtype)),
VerboseDesc(StripTrailingDots(verboseDesc)),
ShortDesc(StripTrailingDots(shortDesc)),
Category(StripTrailingDots(category)), UniqueingLoc(LocationToUnique),
UniqueingDecl(DeclToUnique), ExecutedLines(std::move(ExecutedLines)),
- path(pathImpl) {}
+ Fixits(Fixits.begin(), Fixits.end()), path(pathImpl) {}
static PathDiagnosticCallPiece *
getFirstStackedCallToHeaderFile(PathDiagnosticCallPiece *CP,
Index: clang/lib/StaticAnalyzer/Core/BugReporter.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ clang/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -1261,7 +1261,7 @@
R->getBugType().getName(), R->getDescription(),
R->getShortDescription(/*Fallback=*/false), BT.getCategory(),
R->getUniqueingLocation(), R->getUniqueingDecl(),
- findExecutedLines(SM, R->getErrorNode()));
+ findExecutedLines(SM, R->getErrorNode()), R->getFixits());
}
static const Stmt *getStmtParent(const Stmt *S, const ParentMap &PM) {
@@ -3121,23 +3121,26 @@
const CheckerBase *Checker,
StringRef Name, StringRef Category,
StringRef Str, PathDiagnosticLocation Loc,
- ArrayRef<SourceRange> Ranges) {
+ ArrayRef<SourceRange> Ranges,
+ ArrayRef<FixItHint> Fixits) {
EmitBasicReport(DeclWithIssue, Checker->getCheckName(), Name, Category, Str,
- Loc, Ranges);
+ Loc, Ranges, Fixits);
}
void BugReporter::EmitBasicReport(const Decl *DeclWithIssue,
- CheckName CheckName,
- StringRef name, StringRef category,
- StringRef str, PathDiagnosticLocation Loc,
- ArrayRef<SourceRange> Ranges) {
+ CheckName CheckName, StringRef name,
+ StringRef category, StringRef str,
+ PathDiagnosticLocation Loc,
+ ArrayRef<SourceRange> Ranges,
+ ArrayRef<FixItHint> Fixits) {
// 'BT' is owned by BugReporter.
BugType *BT = getBugTypeForName(CheckName, name, category);
auto R = llvm::make_unique<BugReport>(*BT, str, Loc);
R->setDeclWithIssue(DeclWithIssue);
- for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end();
- I != E; ++I)
- R->addRange(*I);
+ for (auto SR : Ranges)
+ R->addRange(SR);
+ for (auto FH : Fixits)
+ R->addFixItHint(FH);
emitReport(std::move(R));
}
Index: clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
@@ -141,6 +141,11 @@
return;
auto Report = llvm::make_unique<BugReport>(BT, OS.str(), N);
+ if (!IsPure) {
+ FixItHint Fixit = FixItHint::CreateInsertion(
+ CE->getBeginLoc(), MD->getParent()->getNameAsString() + "::");
+ Report->addFixItHint(Fixit);
+ }
C.emitReport(std::move(Report));
}
Index: clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Lex/Lexer.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
@@ -202,12 +203,23 @@
llvm::raw_svector_ostream os(buf);
const char *BugType = nullptr;
+ SmallVector<FixItHint, 1> Fixits;
+
switch (dsk) {
- case DeadInit:
+ case DeadInit: {
BugType = "Dead initialization";
os << "Value stored to '" << *V
<< "' during its initialization is never read";
+ SourceLocation L1 = Lexer::findNextToken(
+ V->getTypeSourceInfo()->getTypeLoc().getEndLoc(),
+ V->getASTContext().getSourceManager(),
+ V->getASTContext().getLangOpts())->getEndLoc();
+ SourceLocation L2 = Lexer::getLocForEndOfToken(
+ V->getInit()->getEndLoc(), 1, V->getASTContext().getSourceManager(),
+ V->getASTContext().getLangOpts());
+ Fixits.push_back(FixItHint::CreateRemoval({L1, L2}));
break;
+ }
case DeadIncrement:
BugType = "Dead increment";
@@ -225,7 +237,7 @@
}
BR.EmitBasicReport(AC->getDecl(), Checker, BugType, "Dead store", os.str(),
- L, R);
+ L, R, Fixits);
}
void CheckVarDecl(const VarDecl *VD, const Expr *Ex, const Expr *Val,
Index: clang/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
+++ clang/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
@@ -804,13 +804,16 @@
/// Lines executed in the path.
std::unique_ptr<FilesToLineNumsMap> ExecutedLines;
+ SmallVector<FixItHint, 4> Fixits;
+
public:
PathDiagnostic() = delete;
PathDiagnostic(StringRef CheckName, const Decl *DeclWithIssue,
StringRef bugtype, StringRef verboseDesc, StringRef shortDesc,
StringRef category, PathDiagnosticLocation LocationToUnique,
const Decl *DeclToUnique,
- std::unique_ptr<FilesToLineNumsMap> ExecutedLines);
+ std::unique_ptr<FilesToLineNumsMap> ExecutedLines,
+ ArrayRef<FixItHint> Fixits);
~PathDiagnostic();
const PathPieces &path;
@@ -864,6 +867,8 @@
StringRef getBugType() const { return BugType; }
StringRef getCategory() const { return Category; }
+ ArrayRef<FixItHint> getFixits() const { return Fixits; }
+
/// Return the semantic context where an issue occurred. If the
/// issue occurs along a path, this represents the "central" area
/// where the bug manifests.
Index: clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h
+++ clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h
@@ -107,6 +107,8 @@
ExtraTextList ExtraText;
NoteList Notes;
+ SmallVector<FixItHint, 4> Fixits;
+
using Symbols = llvm::DenseSet<SymbolRef>;
using Regions = llvm::DenseSet<const MemRegion *>;
@@ -333,9 +335,17 @@
Ranges.push_back(R);
}
+ void addFixItHint(FixItHint F) {
+ Fixits.push_back(F);
+ }
+
/// Get the SourceRanges associated with the report.
virtual llvm::iterator_range<ranges_iterator> getRanges();
+ virtual llvm::ArrayRef<FixItHint> getFixits() {
+ return Fixits;
+ }
+
/// Add custom or predefined bug report visitors to this report.
///
/// The visitors should be used when the default trace is not sufficient.
@@ -502,12 +512,14 @@
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker,
StringRef BugName, StringRef BugCategory,
StringRef BugStr, PathDiagnosticLocation Loc,
- ArrayRef<SourceRange> Ranges = None);
+ ArrayRef<SourceRange> Ranges = None,
+ ArrayRef<FixItHint> Fixits = None);
void EmitBasicReport(const Decl *DeclWithIssue, CheckName CheckName,
StringRef BugName, StringRef BugCategory,
StringRef BugStr, PathDiagnosticLocation Loc,
- ArrayRef<SourceRange> Ranges = None);
+ ArrayRef<SourceRange> Ranges = None,
+ ArrayRef<FixItHint> Fixits = None);
private:
llvm::StringMap<BugType *> StrBugTypes;
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits