| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| //===-- IncludeFixer.h - Include inserter -----------------------*- C++ -*-===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXER_H | ||
| #define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXER_H | ||
|
|
||
| #include "XrefsDB.h" | ||
| #include "clang/Lex/PreprocessorOptions.h" | ||
| #include "clang/Tooling/Core/Replacement.h" | ||
| #include "clang/Tooling/Tooling.h" | ||
|
|
||
| namespace clang { | ||
| namespace include_fixer { | ||
|
|
||
| class IncludeFixerActionFactory : public clang::tooling::ToolAction { | ||
| public: | ||
| /// \param Xrefs A source for matching symbols to header files. | ||
| /// \param Replacements Storage for the output of the fixer. | ||
| IncludeFixerActionFactory( | ||
| XrefsDB &Xrefs, std::vector<clang::tooling::Replacement> &Replacements); | ||
| ~IncludeFixerActionFactory(); | ||
|
|
||
| bool | ||
| runInvocation(clang::CompilerInvocation *Invocation, | ||
| clang::FileManager *Files, | ||
| std::shared_ptr<clang::PCHContainerOperations> PCHContainerOps, | ||
| clang::DiagnosticConsumer *Diagnostics) override; | ||
|
|
||
| private: | ||
| /// The client to use to find cross-references. | ||
| XrefsDB &Xrefs; | ||
|
|
||
| /// Replacements are written here. | ||
| std::vector<clang::tooling::Replacement> &Replacements; | ||
| }; | ||
|
|
||
| } // namespace include_fixer | ||
| } // namespace clang | ||
|
|
||
| #endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXER_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| //===-- XrefsDB.h - Interface for symbol-header matching --------*- C++ -*-===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_XREFSDB_H | ||
| #define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_XREFSDB_H | ||
|
|
||
| #include "llvm/ADT/StringRef.h" | ||
| #include <vector> | ||
|
|
||
| namespace clang { | ||
| namespace include_fixer { | ||
|
|
||
| /// This class provides an interface for finding the header files corresponding | ||
| /// to an indentifier in the source code. | ||
| class XrefsDB { | ||
| public: | ||
| virtual ~XrefsDB() = default; | ||
|
|
||
| /// Search for header files to be included for an identifier. | ||
| /// \param Identifier The identifier being searched for. May or may not be | ||
| /// fully qualified. | ||
| /// \returns A list of inclusion candidates, in a format ready for being | ||
| /// pasted after an #include token. | ||
| // FIXME: Expose the type name so we can also insert using declarations (or | ||
| // fix the usage) | ||
| virtual std::vector<std::string> search(llvm::StringRef Identifier) = 0; | ||
| }; | ||
|
|
||
| } // namespace include_fixer | ||
| } // namespace clang | ||
|
|
||
| #endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_XREFSDB_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) | ||
|
|
||
| add_clang_executable(clang-include-fixer ClangIncludeFixer.cpp) | ||
| target_link_libraries(clang-include-fixer | ||
| clangBasic | ||
| clangFrontend | ||
| clangIncludeFixer | ||
| clangRewrite | ||
| clangTooling | ||
| clangToolingCore | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| //===-- ClangIncludeFixer.cpp - Standalone include fixer ------------------===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "InMemoryXrefsDB.h" | ||
| #include "IncludeFixer.h" | ||
| #include "clang/Frontend/TextDiagnosticPrinter.h" | ||
| #include "clang/Rewrite/Core/Rewriter.h" | ||
| #include "clang/Tooling/CommonOptionsParser.h" | ||
| #include "clang/Tooling/Tooling.h" | ||
| #include "llvm/Support/CommandLine.h" | ||
| using namespace clang; | ||
|
|
||
| static llvm::cl::OptionCategory tool_options("Tool options"); | ||
|
|
||
| int main(int argc, char **argv) { | ||
| clang::tooling::CommonOptionsParser options(argc, (const char **)argv, | ||
| tool_options); | ||
| clang::tooling::ClangTool tool(options.getCompilations(), | ||
| options.getSourcePathList()); | ||
| // Set up the data source. | ||
| std::map<std::string, std::vector<std::string>> XrefsMap = { | ||
| {"std::string", {"<string>"}}}; | ||
| auto XrefsDB = | ||
| llvm::make_unique<include_fixer::InMemoryXrefsDB>(std::move(XrefsMap)); | ||
|
|
||
| // Now run our tool. | ||
| std::vector<clang::tooling::Replacement> Replacements; | ||
| include_fixer::IncludeFixerActionFactory Factory(*XrefsDB, Replacements); | ||
|
|
||
| tool.run(&Factory); // Always succeeds. | ||
|
|
||
| // Set up a new source manager for applying the resulting replacements. | ||
| llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts( | ||
| new clang::DiagnosticOptions); | ||
| clang::DiagnosticsEngine Diagnostics(new clang::DiagnosticIDs, &*DiagOpts); | ||
| clang::TextDiagnosticPrinter DiagnosticPrinter(llvm::outs(), &*DiagOpts); | ||
| clang::SourceManager source_manager(Diagnostics, tool.getFiles()); | ||
| Diagnostics.setClient(&DiagnosticPrinter, false); | ||
|
|
||
| // Write replacements to disk. | ||
| clang::Rewriter Rewrites(source_manager, clang::LangOptions()); | ||
| clang::tooling::applyAllReplacements(Replacements, Rewrites); | ||
| return Rewrites.overwriteChangedFiles(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| set(LLVM_LINK_COMPONENTS | ||
| support | ||
| ) | ||
|
|
||
| get_filename_component(INCLUDE_FIXER_SOURCE_DIR | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../../include-fixer REALPATH) | ||
| include_directories( | ||
| ${INCLUDE_FIXER_SOURCE_DIR} | ||
| ) | ||
|
|
||
| add_extra_unittest(IncludeFixerTests | ||
| IncludeFixerTest.cpp | ||
| ) | ||
|
|
||
| target_link_libraries(IncludeFixerTests | ||
| clangBasic | ||
| clangFrontend | ||
| clangIncludeFixer | ||
| clangRewrite | ||
| clangTooling | ||
| clangToolingCore | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| //===-- IncludeFixerTest.cpp - Include fixer unit tests -------------------===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "../../../../unittests/Tooling/RewriterTestContext.h" | ||
| #include "InMemoryXrefsDB.h" | ||
| #include "IncludeFixer.h" | ||
| #include "clang/Tooling/Tooling.h" | ||
| #include "gtest/gtest.h" | ||
| using namespace clang; | ||
|
|
||
| namespace clang { | ||
| namespace include_fixer { | ||
| namespace { | ||
|
|
||
| static bool runOnCode(tooling::ToolAction *ToolAction, StringRef Code, | ||
| StringRef FileName) { | ||
| llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem( | ||
| new vfs::InMemoryFileSystem); | ||
| llvm::IntrusiveRefCntPtr<FileManager> Files( | ||
| new FileManager(FileSystemOptions(), InMemoryFileSystem)); | ||
| tooling::ToolInvocation Invocation( | ||
| {std::string("include_fixer"), std::string("-fsyntax-only"), | ||
| FileName.str()}, | ||
| ToolAction, Files.get(), std::make_shared<PCHContainerOperations>()); | ||
|
|
||
| InMemoryFileSystem->addFile(FileName, 0, | ||
| llvm::MemoryBuffer::getMemBuffer(Code)); | ||
|
|
||
| InMemoryFileSystem->addFile("foo.h", 0, | ||
| llvm::MemoryBuffer::getMemBuffer("\n")); | ||
| InMemoryFileSystem->addFile("bar.h", 0, | ||
| llvm::MemoryBuffer::getMemBuffer("\n")); | ||
| return Invocation.run(); | ||
| } | ||
|
|
||
| static std::string runIncludeFixer(StringRef Code) { | ||
| std::map<std::string, std::vector<std::string>> XrefsMap = { | ||
| {"std::string", {"<string>"}}, {"std::string::size_type", {"<string>"}}}; | ||
| auto XrefsDB = | ||
| llvm::make_unique<include_fixer::InMemoryXrefsDB>(std::move(XrefsMap)); | ||
| std::vector<clang::tooling::Replacement> Replacements; | ||
| IncludeFixerActionFactory Factory(*XrefsDB, Replacements); | ||
| runOnCode(&Factory, Code, "input.cc"); | ||
| clang::RewriterTestContext Context; | ||
| clang::FileID ID = Context.createInMemoryFile("input.cc", Code); | ||
| clang::tooling::applyAllReplacements(Replacements, Context.Rewrite); | ||
| return Context.getRewrittenText(ID); | ||
| } | ||
|
|
||
| TEST(IncludeFixer, Typo) { | ||
| EXPECT_EQ("#include <string>\nstd::string foo;\n", | ||
| runIncludeFixer("std::string foo;\n")); | ||
|
|
||
| EXPECT_EQ( | ||
| "// comment\n#include <string>\n#include \"foo.h\"\nstd::string foo;\n" | ||
| "#include \"bar.h\"\n", | ||
| runIncludeFixer("// comment\n#include \"foo.h\"\nstd::string foo;\n" | ||
| "#include \"bar.h\"\n")); | ||
|
|
||
| EXPECT_EQ("#include <string>\n#include \"foo.h\"\nstd::string foo;\n", | ||
| runIncludeFixer("#include \"foo.h\"\nstd::string foo;\n")); | ||
|
|
||
| EXPECT_EQ( | ||
| "#include <string>\n#include \"foo.h\"\nstd::string::size_type foo;\n", | ||
| runIncludeFixer("#include \"foo.h\"\nstd::string::size_type foo;\n")); | ||
|
|
||
| // The fixed xrefs db doesn't know how to handle string without std::. | ||
| EXPECT_EQ("string foo;\n", runIncludeFixer("string foo;\n")); | ||
| } | ||
|
|
||
| TEST(IncludeFixer, IncompleteType) { | ||
| EXPECT_EQ( | ||
| "#include <string>\n#include \"foo.h\"\n" | ||
| "namespace std {\nclass string;\n}\nstring foo;\n", | ||
| runIncludeFixer("#include \"foo.h\"\n" | ||
| "namespace std {\nclass string;\n}\nstring foo;\n")); | ||
| } | ||
|
|
||
| } // namespace | ||
| } // namespace include_fixer | ||
| } // namespace clang |