[include-fixer] Add a prototype for a new include fixing tool.

Summary:
The goal of this tool is fairly simple, look up unknown identifiers in a
global database and add the corresponding #include line. It accomplishes
this by hooking into Sema as an ExternalSemaSource and responding to typo
correction callbacks. This means we can see the unknown identifier before
it's being munged by error recovery.

This doesn't work perfectly yet as some typo corrections don't emit
callbacks (delayed typos), but I think this is fixable. We also handle
only one include at a time as this is meant to be run directly from
the editing environment eventually. Adding multiple includes at the same
time is tricky because of error recovery.

This version only has a a dummy database, so all you can do is fixing
missing includes of <string>, but the indexer to build a database will
follow soon.

Reviewers: djasper

Subscribers: ioeric, hokein, cfe-commits

Differential Revision: http://reviews.llvm.org/D19314

llvm-svn: 266870
diff --git a/clang-tools-extra/unittests/include-fixer/IncludeFixerTest.cpp b/clang-tools-extra/unittests/include-fixer/IncludeFixerTest.cpp
new file mode 100644
index 0000000..75ee8a3
--- /dev/null
+++ b/clang-tools-extra/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