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 Fixits; + using Symbols = llvm::DenseSet; using Regions = llvm::DenseSet; @@ -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 getRanges(); + virtual llvm::ArrayRef 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 Ranges = None); + ArrayRef Ranges = None, + ArrayRef Fixits = None); void EmitBasicReport(const Decl *DeclWithIssue, CheckName CheckName, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, - ArrayRef Ranges = None); + ArrayRef Ranges = None, + ArrayRef Fixits = None); private: llvm::StringMap StrBugTypes; 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 ExecutedLines; + SmallVector 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 ExecutedLines); + std::unique_ptr ExecutedLines, + ArrayRef Fixits); ~PathDiagnostic(); const PathPieces &path; @@ -864,6 +867,8 @@ StringRef getBugType() const { return BugType; } StringRef getCategory() const { return Category; } + ArrayRef 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/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 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/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp @@ -146,6 +146,11 @@ return; auto Report = llvm::make_unique(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/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(/*UseFallback=*/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 Ranges) { + ArrayRef Ranges, + ArrayRef 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 Ranges) { + CheckName CheckName, StringRef name, + StringRef category, StringRef str, + PathDiagnosticLocation Loc, + ArrayRef Ranges, + ArrayRef Fixits) { // 'BT' is owned by BugReporter. BugType *BT = getBugTypeForName(CheckName, name, category); auto R = llvm::make_unique(*BT, str, Loc); R->setDeclWithIssue(DeclWithIssue); - for (ArrayRef::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/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 ExecutedLines) + std::unique_ptr ExecutedLines, + ArrayRef 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/PlistDiagnostics.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -731,6 +731,22 @@ printCoverage(D, /*IndentLevel=*/2, Fids, FM, o); + if (!D->getFixits().empty()) { + o << " fixits\n"; + o << " \n"; + for (auto Hint : D->getFixits()) { + assert(!Hint.isNull()); + o << " \n"; + o << " remove_range\n"; + EmitRange(o, SM, Lexer::getAsCharRange(Hint.RemoveRange, SM, LangOpts), + FM, 4); + o << " insert_string\n"; + o << " " << Hint.CodeToInsert << "\n"; + o << " \n"; + } + o << " \n"; + } + // Close up the entry. o << " \n"; } 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/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 @@ 431 + fixits + + + remove_range + + + line431 + col11 + file0 + + + line431 + col40 + file0 + + + insert_string + + + path 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 @@ 119 + fixits + + + remove_range + + + line119 + col7 + file0 + + + line119 + col25 + file0 + + + insert_string + + + path @@ -471,6 +491,26 @@ 139 + fixits + + + remove_range + + + line139 + col10 + file0 + + + line139 + col53 + file0 + + + insert_string + + + path @@ -539,6 +579,26 @@ 144 + fixits + + + remove_range + + + line144 + col10 + file0 + + + line144 + col45 + file0 + + + insert_string + + + path @@ -607,6 +667,26 @@ 145 + fixits + + + remove_range + + + line145 + col10 + file0 + + + line145 + col44 + file0 + + + insert_string + + + path @@ -675,6 +755,26 @@ 146 + fixits + + + remove_range + + + line146 + col10 + file0 + + + line146 + col48 + file0 + + + insert_string + + + path @@ -1085,6 +1185,26 @@ 150 + fixits + + + remove_range + + + line150 + col16 + file0 + + + line150 + col64 + file0 + + + insert_string + + + path @@ -1153,6 +1273,26 @@ 151 + fixits + + + remove_range + + + line151 + col18 + file0 + + + line151 + col67 + file0 + + + insert_string + + + path @@ -1221,6 +1361,26 @@ 152 + fixits + + + remove_range + + + line152 + col16 + file0 + + + line152 + col55 + file0 + + + insert_string + + + path @@ -1289,6 +1449,26 @@ 153 + fixits + + + remove_range + + + line153 + col18 + file0 + + + line153 + col58 + file0 + + + insert_string + + + path 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 @@ 86 + fixits + + + remove_range + + + line86 + col11 + file0 + + + line86 + col40 + file0 + + + insert_string + + + path Index: clang/test/Analysis/virtualcall-fixits.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/virtualcall-fixits.cpp @@ -0,0 +1,17 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,optin.cplusplus.VirtualCall \ +// RUN: %s 2>&1 | FileCheck %s + +struct S { + virtual void foo(); + S() { + foo(); + } +}; + +// CHECK: warning: Call to virtual method 'S::foo' during construction bypasses +// CHECK-SAME: virtual dispatch (qualify the call explicitly to suppress +// CHECK-SAME: the warning) +// CHECK-NEXT: foo(); +// CHECK-NEXT: ^~~~~ +// CHECK-NEXT: S:: +// CHECK-NEXT: 1 warning generated.