Index: clang-tools-extra/trunk/clang-tidy/ClangTidy.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/ClangTidy.h +++ clang-tools-extra/trunk/clang-tidy/ClangTidy.h @@ -133,6 +133,11 @@ /// Errors containing fixes are automatically applied. void handleErrors(const std::vector &Errors, bool Fix); +/// \brief Serializes replacements into YAML and writes them to the specified +/// output stream. +void exportReplacements(const std::vector &Errors, + raw_ostream &OS); + } // end namespace tidy } // end namespace clang Index: clang-tools-extra/trunk/clang-tidy/ClangTidy.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/ClangTidy.cpp +++ clang-tools-extra/trunk/clang-tidy/ClangTidy.cpp @@ -34,6 +34,7 @@ #include "clang/Rewrite/Frontend/FrontendActions.h" #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" #include "clang/Tooling/Refactoring.h" +#include "clang/Tooling/ReplacementsYaml.h" #include "clang/Tooling/Tooling.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" @@ -362,5 +363,16 @@ Reporter.Finish(); } +void exportReplacements(const std::vector &Errors, + raw_ostream &OS) { + tooling::TranslationUnitReplacements TUR; + for (const ClangTidyError &Error : Errors) + TUR.Replacements.insert(TUR.Replacements.end(), Error.Fix.begin(), + Error.Fix.end()); + + yaml::Output YAML(OS); + YAML << TUR; +} + } // namespace tidy } // namespace clang Index: clang-tools-extra/trunk/clang-tidy/tool/ClangTidyMain.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/tool/ClangTidyMain.cpp +++ clang-tools-extra/trunk/clang-tidy/tool/ClangTidyMain.cpp @@ -78,6 +78,13 @@ "clang-analyzer- checks."), cl::init(false), cl::cat(ClangTidyCategory)); +static cl::opt ExportFixes( + "export-fixes", + cl::desc("YAML file to store suggested fixes in. The\n" + "stored fixes can be applied to the input source\n" + "code with clang-apply-replacements."), + cl::value_desc("filename"), cl::cat(ClangTidyCategory)); + static void printStats(const clang::tidy::ClangTidyStats &Stats) { if (Stats.errorsIgnored()) { llvm::errs() << "Suppressed " << Stats.errorsIgnored() << " warnings ("; @@ -147,6 +154,16 @@ OptionsParser.getSourcePathList(), &Errors); clang::tidy::handleErrors(Errors, Fix); + if (!ExportFixes.empty()) { + std::error_code EC; + llvm::raw_fd_ostream OS(ExportFixes, EC, llvm::sys::fs::F_None); + if (EC) { + llvm::errs() << "Error opening output file: " << EC.message() << '\n'; + return 1; + } + clang::tidy::exportReplacements(Errors, OS); + } + printStats(Stats); return 0; } Index: clang-tools-extra/trunk/test/clang-tidy/fix.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/fix.cpp +++ clang-tools-extra/trunk/test/clang-tidy/fix.cpp @@ -1,14 +1,17 @@ // RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp -// RUN: clang-tidy %t.cpp -checks='-*,google-explicit-constructor,llvm-namespace-comment' -fix -- > %t.msg 2>&1 +// RUN: clang-tidy %t.cpp -checks='-*,google-explicit-constructor,llvm-namespace-comment' -fix -export-fixes=%t.yaml -- > %t.msg 2>&1 // RUN: FileCheck -input-file=%t.cpp %s // RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MESSAGES %s +// RUN: FileCheck -input-file=%t.yaml -check-prefix=CHECK-YAML %s namespace i { } // CHECK: } // namespace i // CHECK-MESSAGES: note: FIX-IT applied suggested code changes +// CHECK-YAML: ReplacementText: ' // namespace i' 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. +// CHECK-YAML: ReplacementText: 'explicit '