This revision was automatically updated to reflect the committed changes.
Closed by commit rL266870: [include-fixer] Add a prototype for a new include 
fixing tool. (authored by d0k).

Changed prior to commit:
  http://reviews.llvm.org/D19314?vs=54344&id=54350#toc

Repository:
  rL LLVM

http://reviews.llvm.org/D19314

Files:
  clang-tools-extra/trunk/CMakeLists.txt
  clang-tools-extra/trunk/include-fixer/CMakeLists.txt
  clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.cpp
  clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.h
  clang-tools-extra/trunk/include-fixer/IncludeFixer.cpp
  clang-tools-extra/trunk/include-fixer/IncludeFixer.h
  clang-tools-extra/trunk/include-fixer/XrefsDB.h
  clang-tools-extra/trunk/include-fixer/tool/CMakeLists.txt
  clang-tools-extra/trunk/include-fixer/tool/ClangIncludeFixer.cpp
  clang-tools-extra/trunk/unittests/CMakeLists.txt
  clang-tools-extra/trunk/unittests/include-fixer/CMakeLists.txt
  clang-tools-extra/trunk/unittests/include-fixer/IncludeFixerTest.cpp

Index: clang-tools-extra/trunk/unittests/include-fixer/IncludeFixerTest.cpp
===================================================================
--- clang-tools-extra/trunk/unittests/include-fixer/IncludeFixerTest.cpp
+++ clang-tools-extra/trunk/unittests/include-fixer/IncludeFixerTest.cpp
@@ -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
Index: clang-tools-extra/trunk/unittests/include-fixer/CMakeLists.txt
===================================================================
--- clang-tools-extra/trunk/unittests/include-fixer/CMakeLists.txt
+++ clang-tools-extra/trunk/unittests/include-fixer/CMakeLists.txt
@@ -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
+  )
Index: clang-tools-extra/trunk/unittests/CMakeLists.txt
===================================================================
--- clang-tools-extra/trunk/unittests/CMakeLists.txt
+++ clang-tools-extra/trunk/unittests/CMakeLists.txt
@@ -9,3 +9,4 @@
 add_subdirectory(clang-rename)
 add_subdirectory(clang-query)
 add_subdirectory(clang-tidy)
+add_subdirectory(include-fixer)
Index: clang-tools-extra/trunk/include-fixer/tool/CMakeLists.txt
===================================================================
--- clang-tools-extra/trunk/include-fixer/tool/CMakeLists.txt
+++ clang-tools-extra/trunk/include-fixer/tool/CMakeLists.txt
@@ -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
+  )
Index: clang-tools-extra/trunk/include-fixer/tool/ClangIncludeFixer.cpp
===================================================================
--- clang-tools-extra/trunk/include-fixer/tool/ClangIncludeFixer.cpp
+++ clang-tools-extra/trunk/include-fixer/tool/ClangIncludeFixer.cpp
@@ -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();
+}
Index: clang-tools-extra/trunk/include-fixer/CMakeLists.txt
===================================================================
--- clang-tools-extra/trunk/include-fixer/CMakeLists.txt
+++ clang-tools-extra/trunk/include-fixer/CMakeLists.txt
@@ -0,0 +1,20 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+add_clang_library(clangIncludeFixer
+  IncludeFixer.cpp
+  InMemoryXrefsDB.cpp
+
+  LINK_LIBS
+  clangAST
+  clangBasic
+  clangFrontend
+  clangLex
+  clangParse
+  clangSema
+  clangTooling
+  clangToolingCore
+  )
+
+add_subdirectory(tool)
Index: clang-tools-extra/trunk/include-fixer/IncludeFixer.h
===================================================================
--- clang-tools-extra/trunk/include-fixer/IncludeFixer.h
+++ clang-tools-extra/trunk/include-fixer/IncludeFixer.h
@@ -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
Index: clang-tools-extra/trunk/include-fixer/XrefsDB.h
===================================================================
--- clang-tools-extra/trunk/include-fixer/XrefsDB.h
+++ clang-tools-extra/trunk/include-fixer/XrefsDB.h
@@ -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
Index: clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.h
===================================================================
--- clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.h
+++ clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.h
@@ -0,0 +1,36 @@
+//===-- InMemoryXrefsDB.h ---------------------------------------*- 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_INMEMORYXREFSDB_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INMEMORYXREFSDB_H
+
+#include "XrefsDB.h"
+#include <map>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace include_fixer {
+
+/// Xref database with fixed content.
+class InMemoryXrefsDB : public XrefsDB {
+public:
+  InMemoryXrefsDB(std::map<std::string, std::vector<std::string>> LookupTable)
+      : LookupTable(std::move(LookupTable)) {}
+
+  std::vector<std::string> search(llvm::StringRef Identifier) override;
+
+private:
+  std::map<std::string, std::vector<std::string>> LookupTable;
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INMEMORYXREFSDB_H
Index: clang-tools-extra/trunk/include-fixer/IncludeFixer.cpp
===================================================================
--- clang-tools-extra/trunk/include-fixer/IncludeFixer.cpp
+++ clang-tools-extra/trunk/include-fixer/IncludeFixer.cpp
@@ -0,0 +1,309 @@
+//===-- IncludeFixer.cpp - Include inserter based on sema callbacks -------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IncludeFixer.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Parse/ParseAST.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Sema/ExternalSemaSource.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+
+#define DEBUG_TYPE "include-fixer"
+
+using namespace clang;
+
+namespace clang {
+namespace include_fixer {
+namespace {
+
+class Action;
+
+class PreprocessorHooks : public clang::PPCallbacks {
+public:
+  explicit PreprocessorHooks(Action *EnclosingPass)
+      : EnclosingPass(EnclosingPass), TrackedFile(nullptr) {}
+
+  void FileChanged(clang::SourceLocation loc,
+                   clang::PPCallbacks::FileChangeReason reason,
+                   clang::SrcMgr::CharacteristicKind file_type,
+                   clang::FileID prev_fid) override;
+
+  void InclusionDirective(clang::SourceLocation HashLocation,
+                          const clang::Token &IncludeToken,
+                          llvm::StringRef FileName, bool IsAngled,
+                          clang::CharSourceRange FileNameRange,
+                          const clang::FileEntry *IncludeFile,
+                          llvm::StringRef SearchPath,
+                          llvm::StringRef relative_path,
+                          const clang::Module *imported) override;
+
+private:
+  /// The current Action.
+  Action *EnclosingPass;
+
+  /// The current FileEntry.
+  const clang::FileEntry *TrackedFile;
+};
+
+/// Manages the parse, gathers include suggestions.
+class Action : public clang::ASTFrontendAction,
+               public clang::ExternalSemaSource {
+public:
+  explicit Action(XrefsDB &Xrefs) : Xrefs(Xrefs) {}
+
+  std::unique_ptr<clang::ASTConsumer>
+  CreateASTConsumer(clang::CompilerInstance &Compiler,
+                    StringRef InFile) override {
+    Filename = InFile;
+    Compiler.getPreprocessor().addPPCallbacks(
+        llvm::make_unique<PreprocessorHooks>(this));
+    return llvm::make_unique<clang::ASTConsumer>();
+  }
+
+  void ExecuteAction() override {
+    clang::CompilerInstance *Compiler = &getCompilerInstance();
+    assert(!Compiler->hasSema() && "CI already has Sema");
+
+    // Set up our hooks into sema and parse the AST.
+    if (hasCodeCompletionSupport() &&
+        !Compiler->getFrontendOpts().CodeCompletionAt.FileName.empty())
+      Compiler->createCodeCompletionConsumer();
+
+    clang::CodeCompleteConsumer *CompletionConsumer = nullptr;
+    if (Compiler->hasCodeCompletionConsumer())
+      CompletionConsumer = &Compiler->getCodeCompletionConsumer();
+
+    Compiler->createSema(getTranslationUnitKind(), CompletionConsumer);
+    Compiler->getSema().addExternalSource(this);
+
+    clang::ParseAST(Compiler->getSema(), Compiler->getFrontendOpts().ShowStats,
+                    Compiler->getFrontendOpts().SkipFunctionBodies);
+  }
+
+  /// Callback for incomplete types. If we encounter a forward declaration we
+  /// have the fully qualified name ready. Just query that.
+  bool MaybeDiagnoseMissingCompleteType(clang::SourceLocation Loc,
+                                        clang::QualType T) override {
+    clang::ASTContext &context = getCompilerInstance().getASTContext();
+    query(T.getUnqualifiedType().getAsString(context.getPrintingPolicy()));
+    return false;
+  }
+
+  /// Callback for unknown identifiers. Try to piece together as much
+  /// qualification as we can get and do a query.
+  clang::TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo,
+                                    int LookupKind, Scope *S, CXXScopeSpec *SS,
+                                    CorrectionCandidateCallback &CCC,
+                                    DeclContext *MemberContext,
+                                    bool EnteringContext,
+                                    const ObjCObjectPointerType *OPT) override {
+    // We don't want to look up inner parts of nested name specifies. Looking up
+    // the header where a namespace is defined in is rarely useful.
+    if (LookupKind == clang::Sema::LookupNestedNameSpecifierName) {
+      DEBUG(llvm::dbgs() << "ignoring " << Typo.getAsString() << "\n");
+      return clang::TypoCorrection();
+    }
+
+    /// If we have a scope specification, use that to get more precise results.
+    std::string QueryString;
+    if (SS && SS->getRange().isValid()) {
+      auto Range = CharSourceRange::getTokenRange(SS->getRange().getBegin(),
+                                                  Typo.getLoc());
+      QueryString =
+          Lexer::getSourceText(Range, getCompilerInstance().getSourceManager(),
+                               getCompilerInstance().getLangOpts());
+    } else {
+      QueryString = Typo.getAsString();
+    }
+
+    return query(QueryString);
+  }
+
+  StringRef filename() const { return Filename; }
+
+  /// Called for each include file we discover is in the file.
+  /// \param SourceManager the active SourceManager
+  /// \param canonical_path the canonical path to the include file
+  /// \param uttered_path the path as it appeared in the program
+  /// \param IsAngled whether angle brackets were used
+  /// \param HashLocation the source location of the include's \#
+  /// \param EndLocation the source location following the include
+  void NextInclude(clang::SourceManager *SourceManager,
+                   llvm::StringRef canonical_path, llvm::StringRef uttered_path,
+                   bool IsAngled, clang::SourceLocation HashLocation,
+                   clang::SourceLocation EndLocation) {
+    unsigned Offset = SourceManager->getFileOffset(HashLocation);
+    if (FirstIncludeOffset == -1U)
+      FirstIncludeOffset = Offset;
+  }
+
+  /// Generate replacements for the suggested includes.
+  /// \return true if changes will be made, false otherwise.
+  bool Rewrite(clang::SourceManager &SourceManager,
+               std::vector<clang::tooling::Replacement> &replacements) {
+    for (const auto &ToTry : Untried) {
+      DEBUG(llvm::dbgs() << "Adding include " << ToTry << "\n");
+      std::string ToAdd = "#include " + ToTry + "\n";
+
+      if (FirstIncludeOffset == -1U)
+        FirstIncludeOffset = 0;
+
+      replacements.push_back(clang::tooling::Replacement(
+          SourceManager, FileBegin.getLocWithOffset(FirstIncludeOffset), 0,
+          ToAdd));
+
+      // We currently abort after the first inserted include. The more
+      // includes we have the less safe this becomes due to error recovery
+      // changing the results.
+      // FIXME: Handle multiple includes at once.
+      return true;
+    }
+    return false;
+  }
+
+  /// Gets the location at the very top of the file.
+  clang::SourceLocation file_begin() const { return FileBegin; }
+
+  /// Sets the location at the very top of the file.
+  void setFileBegin(clang::SourceLocation Location) { FileBegin = Location; }
+
+  /// Add an include to the set of includes to try.
+  /// \param include_path The include path to try.
+  void TryInclude(const std::string &query, const std::string &include_path) {
+    Untried.insert(include_path);
+  }
+
+private:
+  /// Query the database for a given identifier.
+  clang::TypoCorrection query(StringRef Query) {
+    assert(!Query.empty() && "Empty query!");
+
+    // Save database lookups by not looking up identifiers multiple times.
+    if (!SeenQueries.insert(Query).second)
+      return clang::TypoCorrection();
+
+    DEBUG(llvm::dbgs() << "Looking up " << Query << " ... ");
+
+    std::string error_text;
+    auto SearchReply = Xrefs.search(Query);
+    DEBUG(llvm::dbgs() << SearchReply.size() << " replies\n");
+    if (SearchReply.empty())
+      return clang::TypoCorrection();
+
+    // Add those files to the set of includes to try out.
+    // FIXME: Rank the results and pick the best one instead of the first one.
+    TryInclude(Query, SearchReply[0]);
+
+    // FIXME: We should just return the name we got as input here and prevent
+    // clang from trying to correct the typo by itself. That may change the
+    // identifier to something that's not wanted by the user.
+    return clang::TypoCorrection();
+  }
+
+  /// The client to use to find cross-references.
+  XrefsDB &Xrefs;
+
+  // Remeber things we looked up to avoid querying things twice.
+  llvm::StringSet<> SeenQueries;
+
+  /// The absolute path to the file being processed.
+  std::string Filename;
+
+  /// The location of the beginning of the tracked file.
+  clang::SourceLocation FileBegin;
+
+  /// The offset of the last include in the original source file. This will
+  /// be used as the insertion point for new include directives.
+  unsigned FirstIncludeOffset = -1U;
+
+  /// Includes we have left to try.
+  std::set<std::string> Untried;
+};
+
+void PreprocessorHooks::FileChanged(clang::SourceLocation Loc,
+                                    clang::PPCallbacks::FileChangeReason Reason,
+                                    clang::SrcMgr::CharacteristicKind FileType,
+                                    clang::FileID PrevFID) {
+  // Remember where the main file starts.
+  if (Reason == clang::PPCallbacks::EnterFile) {
+    clang::SourceManager *SourceManager =
+        &EnclosingPass->getCompilerInstance().getSourceManager();
+    clang::FileID loc_id = SourceManager->getFileID(Loc);
+    if (const clang::FileEntry *FileEntry =
+            SourceManager->getFileEntryForID(loc_id)) {
+      if (FileEntry->getName() == EnclosingPass->filename()) {
+        EnclosingPass->setFileBegin(Loc);
+        TrackedFile = FileEntry;
+      }
+    }
+  }
+}
+
+void PreprocessorHooks::InclusionDirective(
+    clang::SourceLocation HashLocation, const clang::Token &IncludeToken,
+    llvm::StringRef FileName, bool IsAngled,
+    clang::CharSourceRange FileNameRange, const clang::FileEntry *IncludeFile,
+    llvm::StringRef SearchPath, llvm::StringRef relative_path,
+    const clang::Module *imported) {
+  // Remember include locations so we can insert our new include at the end of
+  // the include block.
+  clang::SourceManager *SourceManager =
+      &EnclosingPass->getCompilerInstance().getSourceManager();
+  auto IDPosition = SourceManager->getDecomposedExpansionLoc(HashLocation);
+  const FileEntry *SourceFile =
+      SourceManager->getFileEntryForID(IDPosition.first);
+  if (!IncludeFile || TrackedFile != SourceFile)
+    return;
+  EnclosingPass->NextInclude(SourceManager, IncludeFile->getName(), FileName,
+                             IsAngled, HashLocation, FileNameRange.getEnd());
+}
+
+} // namespace
+
+IncludeFixerActionFactory::IncludeFixerActionFactory(
+    XrefsDB &Xrefs, std::vector<clang::tooling::Replacement> &Replacements)
+    : Xrefs(Xrefs), Replacements(Replacements) {}
+
+IncludeFixerActionFactory::~IncludeFixerActionFactory() = default;
+
+bool IncludeFixerActionFactory::runInvocation(
+    clang::CompilerInvocation *Invocation, clang::FileManager *Files,
+    std::shared_ptr<clang::PCHContainerOperations> PCHContainerOps,
+    clang::DiagnosticConsumer *Diagnostics) {
+  assert(Invocation->getFrontendOpts().Inputs.size() == 1);
+
+  // Set up Clang.
+  clang::CompilerInstance Compiler(PCHContainerOps);
+  Compiler.setInvocation(Invocation);
+  Compiler.setFileManager(Files);
+
+  // Create the compiler's actual diagnostics engine. We want to drop all
+  // diagnostics here.
+  Compiler.createDiagnostics(new clang::IgnoringDiagConsumer,
+                             /*ShouldOwnClient=*/true);
+  Compiler.createSourceManager(*Files);
+
+  // Run the parser, gather missing includes.
+  auto ScopedToolAction = llvm::make_unique<Action>(Xrefs);
+  Compiler.ExecuteAction(*ScopedToolAction);
+
+  // Generate replacements.
+  ScopedToolAction->Rewrite(Compiler.getSourceManager(), Replacements);
+
+  // Technically this should only return true if we're sure that we have a
+  // parseable file. We don't know that though.
+  return true;
+}
+
+} // namespace include_fixer
+} // namespace clang
Index: clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.cpp
===================================================================
--- clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.cpp
+++ clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.cpp
@@ -0,0 +1,23 @@
+//===-- InMemoryXrefsDB.cpp -----------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InMemoryXrefsDB.h"
+
+namespace clang {
+namespace include_fixer {
+
+std::vector<std::string> InMemoryXrefsDB::search(llvm::StringRef Identifier) {
+  auto I = LookupTable.find(Identifier);
+  if (I != LookupTable.end())
+    return I->second;
+  return {};
+}
+
+} // namespace include_fixer
+} // namespace clang
Index: clang-tools-extra/trunk/CMakeLists.txt
===================================================================
--- clang-tools-extra/trunk/CMakeLists.txt
+++ clang-tools-extra/trunk/CMakeLists.txt
@@ -6,6 +6,7 @@
 endif()
 
 add_subdirectory(clang-query)
+add_subdirectory(include-fixer)
 add_subdirectory(pp-trace)
 add_subdirectory(tool-template)
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to