#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Format/Format.h"
#include "clang/Frontend/ASTConsumers.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)),
Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
DiagPrinter),
- SourceMgr(Diags, Files), Rewrite(SourceMgr, LangOpts),
- ApplyFixes(ApplyFixes), TotalFixes(0), AppliedFixes(0),
- WarningsAsErrors(0) {
+ SourceMgr(Diags, Files), ApplyFixes(ApplyFixes), TotalFixes(0),
+ AppliedFixes(0), WarningsAsErrors(0) {
DiagOpts->ShowColors = llvm::sys::Process::StandardOutHasColors();
DiagPrinter->BeginSourceFile(LangOpts);
}
auto Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0 [%1]"))
<< Message.Message << Name;
for (const auto &FileAndReplacements : Error.Fix) {
- for (const auto &Replacement : FileAndReplacements.second) {
+ for (const auto &Repl : FileAndReplacements.second) {
// Retrieve the source range for applicable fixes. Macro definitions
// on the command line have locations in a virtual buffer and don't
// have valid file paths and are therefore not applicable.
SourceRange Range;
SourceLocation FixLoc;
- if (Replacement.isApplicable()) {
- SmallString<128> FixAbsoluteFilePath = Replacement.getFilePath();
+ ++TotalFixes;
+ bool CanBeApplied = false;
+ if (Repl.isApplicable()) {
+ SmallString<128> FixAbsoluteFilePath = Repl.getFilePath();
Files.makeAbsolutePath(FixAbsoluteFilePath);
- FixLoc = getLocation(FixAbsoluteFilePath, Replacement.getOffset());
+ if (ApplyFixes) {
+ tooling::Replacement R(FixAbsoluteFilePath, Repl.getOffset(),
+ Repl.getLength(),
+ Repl.getReplacementText());
+ Replacements &Replacements = FileReplacements[R.getFilePath()];
+ llvm::Error Err = Replacements.add(R);
+ if (Err) {
+ // FIXME: Implement better conflict handling.
+ llvm::errs() << "Trying to resolve conflict: "
+ << llvm::toString(std::move(Err)) << "\n";
+ unsigned NewOffset =
+ Replacements.getShiftedCodePosition(R.getOffset());
+ unsigned NewLength = Replacements.getShiftedCodePosition(
+ R.getOffset() + R.getLength()) -
+ NewOffset;
+ if (NewLength == R.getLength()) {
+ R = Replacement(R.getFilePath(), NewOffset, NewLength,
+ R.getReplacementText());
+ Replacements = Replacements.merge(tooling::Replacements(R));
+ CanBeApplied = true;
+ ++AppliedFixes;
+ } else {
+ llvm::errs()
+ << "Can't resolve conflict, skipping the replacement.\n";
+ }
+
+ } else {
+ CanBeApplied = true;
+ ++AppliedFixes;
+ }
+ }
+ FixLoc = getLocation(FixAbsoluteFilePath, Repl.getOffset());
SourceLocation FixEndLoc =
- FixLoc.getLocWithOffset(Replacement.getLength());
+ FixLoc.getLocWithOffset(Repl.getLength());
Range = SourceRange(FixLoc, FixEndLoc);
- Diag << FixItHint::CreateReplacement(
- Range, Replacement.getReplacementText());
+ Diag << FixItHint::CreateReplacement(Range,
+ Repl.getReplacementText());
}
- ++TotalFixes;
- if (ApplyFixes) {
- bool Success =
- Replacement.isApplicable() && Replacement.apply(Rewrite);
- if (Success)
- ++AppliedFixes;
- FixLocations.push_back(std::make_pair(FixLoc, Success));
- }
+ if (ApplyFixes)
+ FixLocations.push_back(std::make_pair(FixLoc, CanBeApplied));
}
}
}
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();
+ Rewriter Rewrite(SourceMgr, LangOpts);
+ for (const auto &FileAndReplacements : FileReplacements) {
+ StringRef File = FileAndReplacements.first();
+ llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
+ SourceMgr.getFileManager().getBufferForFile(File);
+ if (!Buffer) {
+ llvm::errs() << "Can't get buffer for file " << File << ": "
+ << Buffer.getError().message() << "\n";
+ // FIXME: Maybe don't apply fixes for other files as well.
+ continue;
+ }
+ StringRef Code = Buffer.get()->getBuffer();
+ // FIXME: Make the style customizable.
+ format::FormatStyle Style = format::getStyle("file", File, "LLVM");
+ llvm::Expected<Replacements> CleanReplacements =
+ format::cleanupAroundReplacements(Code, FileAndReplacements.second,
+ Style);
+ if (!CleanReplacements) {
+ llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
+ continue;
+ }
+ if (!tooling::applyAllReplacements(CleanReplacements.get(), Rewrite)) {
+ llvm::errs() << "Can't apply replacements for file " << File << "\n";
+ }
+ }
+ if (Rewrite.overwriteChangedFiles()) {
+ llvm::errs() << "clang-tidy failed to apply suggested fixes.\n";
+ } else {
+ llvm::errs() << "clang-tidy applied " << AppliedFixes << " of "
+ << TotalFixes << " suggested fixes.\n";
+ }
}
}
DiagnosticConsumer *DiagPrinter;
DiagnosticsEngine Diags;
SourceManager SourceMgr;
- Rewriter Rewrite;
+ llvm::StringMap<Replacements> FileReplacements;
bool ApplyFixes;
unsigned TotalFixes;
unsigned AppliedFixes;
ClangTidyStats
runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
- const tooling::CompilationDatabase &Compilations,
+ const CompilationDatabase &Compilations,
ArrayRef<std::string> InputFiles,
std::vector<ClangTidyError> *Errors, ProfileData *Profile) {
ClangTool Tool(Compilations, InputFiles);
void exportReplacements(const std::vector<ClangTidyError> &Errors,
raw_ostream &OS) {
- tooling::TranslationUnitReplacements TUR;
+ TranslationUnitReplacements TUR;
for (const ClangTidyError &Error : Errors) {
for (const auto &FileAndFixes : Error.Fix)
TUR.Replacements.insert(TUR.Replacements.end(),