[clang-tools-extra] r266870 - [include-fixer] Add a prototype for a new include fixing tool.

Benjamin Kramer via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 20 05:43:52 PDT 2016


Author: d0k
Date: Wed Apr 20 07:43:43 2016
New Revision: 266870

URL: http://llvm.org/viewvc/llvm-project?rev=266870&view=rev
Log:
[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

Added:
    clang-tools-extra/trunk/include-fixer/
    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/
    clang-tools-extra/trunk/include-fixer/tool/CMakeLists.txt
    clang-tools-extra/trunk/include-fixer/tool/ClangIncludeFixer.cpp
    clang-tools-extra/trunk/unittests/include-fixer/
    clang-tools-extra/trunk/unittests/include-fixer/CMakeLists.txt
    clang-tools-extra/trunk/unittests/include-fixer/IncludeFixerTest.cpp
Modified:
    clang-tools-extra/trunk/CMakeLists.txt
    clang-tools-extra/trunk/unittests/CMakeLists.txt

Modified: clang-tools-extra/trunk/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/CMakeLists.txt?rev=266870&r1=266869&r2=266870&view=diff
==============================================================================
--- clang-tools-extra/trunk/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/CMakeLists.txt Wed Apr 20 07:43:43 2016
@@ -6,6 +6,7 @@ add_subdirectory(clang-tidy)
 endif()
 
 add_subdirectory(clang-query)
+add_subdirectory(include-fixer)
 add_subdirectory(pp-trace)
 add_subdirectory(tool-template)
 

Added: clang-tools-extra/trunk/include-fixer/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/include-fixer/CMakeLists.txt?rev=266870&view=auto
==============================================================================
--- clang-tools-extra/trunk/include-fixer/CMakeLists.txt (added)
+++ clang-tools-extra/trunk/include-fixer/CMakeLists.txt Wed Apr 20 07:43:43 2016
@@ -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)

Added: clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.cpp?rev=266870&view=auto
==============================================================================
--- clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.cpp (added)
+++ clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.cpp Wed Apr 20 07:43:43 2016
@@ -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

Added: clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.h?rev=266870&view=auto
==============================================================================
--- clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.h (added)
+++ clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.h Wed Apr 20 07:43:43 2016
@@ -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

Added: clang-tools-extra/trunk/include-fixer/IncludeFixer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/include-fixer/IncludeFixer.cpp?rev=266870&view=auto
==============================================================================
--- clang-tools-extra/trunk/include-fixer/IncludeFixer.cpp (added)
+++ clang-tools-extra/trunk/include-fixer/IncludeFixer.cpp Wed Apr 20 07:43:43 2016
@@ -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

Added: clang-tools-extra/trunk/include-fixer/IncludeFixer.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/include-fixer/IncludeFixer.h?rev=266870&view=auto
==============================================================================
--- clang-tools-extra/trunk/include-fixer/IncludeFixer.h (added)
+++ clang-tools-extra/trunk/include-fixer/IncludeFixer.h Wed Apr 20 07:43:43 2016
@@ -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

Added: clang-tools-extra/trunk/include-fixer/XrefsDB.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/include-fixer/XrefsDB.h?rev=266870&view=auto
==============================================================================
--- clang-tools-extra/trunk/include-fixer/XrefsDB.h (added)
+++ clang-tools-extra/trunk/include-fixer/XrefsDB.h Wed Apr 20 07:43:43 2016
@@ -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

Added: clang-tools-extra/trunk/include-fixer/tool/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/include-fixer/tool/CMakeLists.txt?rev=266870&view=auto
==============================================================================
--- clang-tools-extra/trunk/include-fixer/tool/CMakeLists.txt (added)
+++ clang-tools-extra/trunk/include-fixer/tool/CMakeLists.txt Wed Apr 20 07:43:43 2016
@@ -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
+  )

Added: clang-tools-extra/trunk/include-fixer/tool/ClangIncludeFixer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/include-fixer/tool/ClangIncludeFixer.cpp?rev=266870&view=auto
==============================================================================
--- clang-tools-extra/trunk/include-fixer/tool/ClangIncludeFixer.cpp (added)
+++ clang-tools-extra/trunk/include-fixer/tool/ClangIncludeFixer.cpp Wed Apr 20 07:43:43 2016
@@ -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();
+}

Modified: clang-tools-extra/trunk/unittests/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/CMakeLists.txt?rev=266870&r1=266869&r2=266870&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/unittests/CMakeLists.txt Wed Apr 20 07:43:43 2016
@@ -9,3 +9,4 @@ add_subdirectory(clang-apply-replacement
 add_subdirectory(clang-rename)
 add_subdirectory(clang-query)
 add_subdirectory(clang-tidy)
+add_subdirectory(include-fixer)

Added: clang-tools-extra/trunk/unittests/include-fixer/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/include-fixer/CMakeLists.txt?rev=266870&view=auto
==============================================================================
--- clang-tools-extra/trunk/unittests/include-fixer/CMakeLists.txt (added)
+++ clang-tools-extra/trunk/unittests/include-fixer/CMakeLists.txt Wed Apr 20 07:43:43 2016
@@ -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
+  )

Added: clang-tools-extra/trunk/unittests/include-fixer/IncludeFixerTest.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/include-fixer/IncludeFixerTest.cpp?rev=266870&view=auto
==============================================================================
--- clang-tools-extra/trunk/unittests/include-fixer/IncludeFixerTest.cpp (added)
+++ clang-tools-extra/trunk/unittests/include-fixer/IncludeFixerTest.cpp Wed Apr 20 07:43:43 2016
@@ -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




More information about the cfe-commits mailing list