Output messages when clang-tidy applies fix-its.

Reviewers: djasper, klimek

Reviewed By: djasper

CC: cfe-commits

Differential Revision: http://llvm-reviews.chandlerc.com/D3192

llvm-svn: 204909
This commit is contained in:
Alexander Kornienko 2014-03-27 10:24:11 +00:00
parent f17da19f34
commit 38d81b41eb
2 changed files with 90 additions and 50 deletions

View File

@ -25,6 +25,7 @@
#include "clang/Frontend/ASTConsumers.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/MultiplexConsumer.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Lex/PPCallbacks.h"
@ -38,6 +39,7 @@
#include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h"
#include <algorithm>
#include <utility>
#include <vector>
using namespace clang::ast_matchers;
@ -90,6 +92,84 @@ private:
ClangTidyContext &Context;
};
class ErrorReporter {
public:
ErrorReporter(bool ApplyFixes)
: Files(FileSystemOptions()), DiagOpts(new DiagnosticOptions()),
DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)),
Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
DiagPrinter),
SourceMgr(Diags, Files), Rewrite(SourceMgr, LangOpts),
ApplyFixes(ApplyFixes) {
DiagOpts->ShowColors = llvm::sys::Process::StandardOutHasColors();
DiagPrinter->BeginSourceFile(LangOpts);
}
void reportDiagnostic(const ClangTidyMessage &Message,
DiagnosticsEngine::Level Level,
const tooling::Replacements *Fixes = nullptr) {
SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
// Contains a pair for each attempted fix: location and whether the fix was
// applied successfully.
SmallVector<std::pair<SourceLocation, bool>, 4> FixLocations;
{
DiagnosticBuilder Diag =
Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0"))
<< Message.Message;
if (Fixes != NULL) {
for (const tooling::Replacement &Fix : *Fixes) {
SourceLocation FixLoc =
getLocation(Fix.getFilePath(), Fix.getOffset());
SourceLocation FixEndLoc = FixLoc.getLocWithOffset(Fix.getLength());
Diag << FixItHint::CreateReplacement(SourceRange(FixLoc, FixEndLoc),
Fix.getReplacementText());
++TotalFixes;
if (ApplyFixes) {
bool Success = Fix.isApplicable() && Fix.apply(Rewrite);
if (Success)
++AppliedFixes;
FixLocations.push_back(std::make_pair(FixLoc, Success));
}
}
}
}
for (auto Fix : FixLocations) {
Diags.Report(Fix.first, Fix.second ? diag::note_fixit_applied
: diag::note_fixit_failed);
}
}
void Finish() {
// FIXME: Run clang-format on changes.
if (ApplyFixes && TotalFixes > 0) {
llvm::errs() << "clang-tidy applied " << AppliedFixes << " of "
<< TotalFixes << " suggested fixes.\n";
Rewrite.overwriteChangedFiles();
}
}
private:
SourceLocation getLocation(StringRef FilePath, unsigned Offset) {
if (FilePath.empty())
return SourceLocation();
const FileEntry *File = SourceMgr.getFileManager().getFile(FilePath);
FileID ID = SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User);
return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
}
FileManager Files;
LangOptions LangOpts; // FIXME: use langopts from each original file
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
DiagnosticConsumer *DiagPrinter;
DiagnosticsEngine Diags;
SourceManager SourceMgr;
Rewriter Rewrite;
bool ApplyFixes;
unsigned TotalFixes = 0;
unsigned AppliedFixes = 0;
};
} // namespace
ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
@ -252,59 +332,15 @@ void runClangTidy(StringRef EnableChecksRegex, StringRef DisableChecksRegex,
Tool.run(new ActionFactory(new ClangTidyASTConsumerFactory(Context)));
}
static SourceLocation getLocation(SourceManager &SourceMgr, StringRef FilePath,
unsigned Offset) {
if (FilePath.empty())
return SourceLocation();
const FileEntry *File = SourceMgr.getFileManager().getFile(FilePath);
FileID ID = SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User);
return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
}
static void reportDiagnostic(const ClangTidyMessage &Message,
SourceManager &SourceMgr,
DiagnosticsEngine::Level Level,
DiagnosticsEngine &Diags,
const tooling::Replacements *Fixes = NULL) {
SourceLocation Loc =
getLocation(SourceMgr, Message.FilePath, Message.FileOffset);
DiagnosticBuilder Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0"))
<< Message.Message;
if (Fixes != NULL) {
for (const tooling::Replacement &Fix : *Fixes) {
SourceLocation FixLoc =
getLocation(SourceMgr, Fix.getFilePath(), Fix.getOffset());
Diag << FixItHint::CreateReplacement(
SourceRange(FixLoc, FixLoc.getLocWithOffset(Fix.getLength())),
Fix.getReplacementText());
}
}
}
void handleErrors(SmallVectorImpl<ClangTidyError> &Errors, bool Fix) {
FileManager Files((FileSystemOptions()));
LangOptions LangOpts; // FIXME: use langopts from each original file
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
DiagOpts->ShowColors = llvm::sys::Process::StandardOutHasColors();
DiagnosticConsumer *DiagPrinter =
new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts);
DiagnosticsEngine Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
&*DiagOpts, DiagPrinter);
DiagPrinter->BeginSourceFile(LangOpts);
SourceManager SourceMgr(Diags, Files);
Rewriter Rewrite(SourceMgr, LangOpts);
ErrorReporter Reporter(Fix);
for (const ClangTidyError &Error : Errors) {
reportDiagnostic(Error.Message, SourceMgr, DiagnosticsEngine::Warning, Diags,
&Error.Fix);
Reporter.reportDiagnostic(Error.Message, DiagnosticsEngine::Warning,
&Error.Fix);
for (const ClangTidyMessage &Note : Error.Notes)
reportDiagnostic(Note, SourceMgr, DiagnosticsEngine::Note, Diags);
tooling::applyAllReplacements(Error.Fix, Rewrite);
Reporter.reportDiagnostic(Note, DiagnosticsEngine::Note);
}
// FIXME: Run clang-format on changes.
if (Fix)
Rewrite.overwriteChangedFiles();
Reporter.Finish();
}
} // namespace tidy

View File

@ -1,10 +1,14 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: clang-tidy %t.cpp -fix --
// RUN: clang-tidy %t.cpp -fix -- > %t.msg 2>&1
// RUN: FileCheck -input-file=%t.cpp %s
// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MESSAGES %s
namespace i {
}
// CHECK: } // namespace i
// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
class A { A(int i); };
// CHECK: class A { explicit A(int i); };
// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
// CHECK-MESSAGES: clang-tidy applied 2 of 2 suggested fixes.