summaryrefslogtreecommitdiffstats
path: root/clang-include-fixer
diff options
context:
space:
mode:
Diffstat (limited to 'clang-include-fixer')
-rw-r--r--clang-include-fixer/CMakeLists.txt29
-rw-r--r--clang-include-fixer/FuzzySymbolIndex.cpp142
-rw-r--r--clang-include-fixer/FuzzySymbolIndex.h54
-rw-r--r--clang-include-fixer/InMemorySymbolIndex.cpp31
-rw-r--r--clang-include-fixer/InMemorySymbolIndex.h37
-rw-r--r--clang-include-fixer/IncludeFixer.cpp444
-rw-r--r--clang-include-fixer/IncludeFixer.h157
-rw-r--r--clang-include-fixer/IncludeFixerContext.cpp115
-rw-r--r--clang-include-fixer/IncludeFixerContext.h94
-rw-r--r--clang-include-fixer/SymbolIndex.h37
-rw-r--r--clang-include-fixer/SymbolIndexManager.cpp158
-rw-r--r--clang-include-fixer/SymbolIndexManager.h65
-rw-r--r--clang-include-fixer/YamlSymbolIndex.cpp60
-rw-r--r--clang-include-fixer/YamlSymbolIndex.h45
-rw-r--r--clang-include-fixer/find-all-symbols/CMakeLists.txt24
-rw-r--r--clang-include-fixer/find-all-symbols/FindAllMacros.cpp69
-rw-r--r--clang-include-fixer/find-all-symbols/FindAllMacros.h64
-rw-r--r--clang-include-fixer/find-all-symbols/FindAllSymbols.cpp268
-rw-r--r--clang-include-fixer/find-all-symbols/FindAllSymbols.h62
-rw-r--r--clang-include-fixer/find-all-symbols/FindAllSymbolsAction.cpp36
-rw-r--r--clang-include-fixer/find-all-symbols/FindAllSymbolsAction.h62
-rw-r--r--clang-include-fixer/find-all-symbols/HeaderMapCollector.cpp44
-rw-r--r--clang-include-fixer/find-all-symbols/HeaderMapCollector.h56
-rw-r--r--clang-include-fixer/find-all-symbols/PathConfig.cpp41
-rw-r--r--clang-include-fixer/find-all-symbols/PathConfig.h36
-rw-r--r--clang-include-fixer/find-all-symbols/PragmaCommentHandler.cpp36
-rw-r--r--clang-include-fixer/find-all-symbols/PragmaCommentHandler.h40
-rw-r--r--clang-include-fixer/find-all-symbols/STLPostfixHeaderMap.cpp653
-rw-r--r--clang-include-fixer/find-all-symbols/STLPostfixHeaderMap.h22
-rw-r--r--clang-include-fixer/find-all-symbols/SymbolInfo.cpp136
-rw-r--r--clang-include-fixer/find-all-symbols/SymbolInfo.h142
-rw-r--r--clang-include-fixer/find-all-symbols/SymbolReporter.h29
-rw-r--r--clang-include-fixer/find-all-symbols/tool/CMakeLists.txt24
-rw-r--r--clang-include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp151
-rwxr-xr-xclang-include-fixer/find-all-symbols/tool/run-find-all-symbols.py123
-rw-r--r--clang-include-fixer/plugin/CMakeLists.txt13
-rw-r--r--clang-include-fixer/plugin/IncludeFixerPlugin.cpp99
-rw-r--r--clang-include-fixer/tool/CMakeLists.txt28
-rw-r--r--clang-include-fixer/tool/ClangIncludeFixer.cpp473
-rw-r--r--clang-include-fixer/tool/clang-include-fixer-test.el65
-rw-r--r--clang-include-fixer/tool/clang-include-fixer.el460
-rw-r--r--clang-include-fixer/tool/clang-include-fixer.py210
42 files changed, 4934 insertions, 0 deletions
diff --git a/clang-include-fixer/CMakeLists.txt b/clang-include-fixer/CMakeLists.txt
new file mode 100644
index 00000000..f27f7403
--- /dev/null
+++ b/clang-include-fixer/CMakeLists.txt
@@ -0,0 +1,29 @@
+set(LLVM_LINK_COMPONENTS
+ support
+ )
+
+add_clang_library(clangIncludeFixer
+ IncludeFixer.cpp
+ IncludeFixerContext.cpp
+ InMemorySymbolIndex.cpp
+ FuzzySymbolIndex.cpp
+ SymbolIndexManager.cpp
+ YamlSymbolIndex.cpp
+
+ LINK_LIBS
+ clangAST
+ clangBasic
+ clangFormat
+ clangFrontend
+ clangLex
+ clangParse
+ clangSema
+ clangSerialization
+ clangTooling
+ clangToolingCore
+ findAllSymbols
+ )
+
+add_subdirectory(plugin)
+add_subdirectory(tool)
+add_subdirectory(find-all-symbols)
diff --git a/clang-include-fixer/FuzzySymbolIndex.cpp b/clang-include-fixer/FuzzySymbolIndex.cpp
new file mode 100644
index 00000000..099d7389
--- /dev/null
+++ b/clang-include-fixer/FuzzySymbolIndex.cpp
@@ -0,0 +1,142 @@
+//===--- FuzzySymbolIndex.cpp - Lookup symbols for autocomplete -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "FuzzySymbolIndex.h"
+#include "llvm/Support/Regex.h"
+
+using clang::find_all_symbols::SymbolAndSignals;
+using llvm::StringRef;
+
+namespace clang {
+namespace include_fixer {
+namespace {
+
+class MemSymbolIndex : public FuzzySymbolIndex {
+public:
+ MemSymbolIndex(std::vector<SymbolAndSignals> Symbols) {
+ for (auto &Symbol : Symbols) {
+ auto Tokens = tokenize(Symbol.Symbol.getName());
+ this->Symbols.emplace_back(
+ StringRef(llvm::join(Tokens.begin(), Tokens.end(), " ")),
+ std::move(Symbol));
+ }
+ }
+
+ std::vector<SymbolAndSignals> search(StringRef Query) override {
+ auto Tokens = tokenize(Query);
+ llvm::Regex Pattern("^" + queryRegexp(Tokens));
+ std::vector<SymbolAndSignals> Results;
+ for (const Entry &E : Symbols)
+ if (Pattern.match(E.first))
+ Results.push_back(E.second);
+ return Results;
+ }
+
+private:
+ using Entry = std::pair<llvm::SmallString<32>, SymbolAndSignals>;
+ std::vector<Entry> Symbols;
+};
+
+// Helpers for tokenize state machine.
+enum TokenizeState {
+ EMPTY, // No pending characters.
+ ONE_BIG, // Read one uppercase letter, could be WORD or Word.
+ BIG_WORD, // Reading an uppercase WORD.
+ SMALL_WORD, // Reading a lowercase word.
+ NUMBER // Reading a number.
+};
+
+enum CharType { UPPER, LOWER, DIGIT, MISC };
+CharType classify(char c) {
+ if (isupper(c))
+ return UPPER;
+ if (islower(c))
+ return LOWER;
+ if (isdigit(c))
+ return DIGIT;
+ return MISC;
+}
+
+} // namespace
+
+std::vector<std::string> FuzzySymbolIndex::tokenize(StringRef Text) {
+ std::vector<std::string> Result;
+ // State describes the treatment of text from Start to I.
+ // Once text is Flush()ed into Result, we're done with it and advance Start.
+ TokenizeState State = EMPTY;
+ size_t Start = 0;
+ auto Flush = [&](size_t End) {
+ if (State != EMPTY) {
+ Result.push_back(Text.substr(Start, End - Start).lower());
+ State = EMPTY;
+ }
+ Start = End;
+ };
+ for (size_t I = 0; I < Text.size(); ++I) {
+ CharType Type = classify(Text[I]);
+ if (Type == MISC)
+ Flush(I);
+ else if (Type == LOWER)
+ switch (State) {
+ case BIG_WORD:
+ Flush(I - 1); // FOOBar: first token is FOO, not FOOB.
+ LLVM_FALLTHROUGH;
+ case ONE_BIG:
+ State = SMALL_WORD;
+ LLVM_FALLTHROUGH;
+ case SMALL_WORD:
+ break;
+ default:
+ Flush(I);
+ State = SMALL_WORD;
+ }
+ else if (Type == UPPER)
+ switch (State) {
+ case ONE_BIG:
+ State = BIG_WORD;
+ LLVM_FALLTHROUGH;
+ case BIG_WORD:
+ break;
+ default:
+ Flush(I);
+ State = ONE_BIG;
+ }
+ else if (Type == DIGIT && State != NUMBER) {
+ Flush(I);
+ State = NUMBER;
+ }
+ }
+ Flush(Text.size());
+ return Result;
+}
+
+std::string
+FuzzySymbolIndex::queryRegexp(const std::vector<std::string> &Tokens) {
+ std::string Result;
+ for (size_t I = 0; I < Tokens.size(); ++I) {
+ if (I)
+ Result.append("[[:alnum:]]* ");
+ for (size_t J = 0; J < Tokens[I].size(); ++J) {
+ if (J)
+ Result.append("([[:alnum:]]* )?");
+ Result.push_back(Tokens[I][J]);
+ }
+ }
+ return Result;
+}
+
+llvm::Expected<std::unique_ptr<FuzzySymbolIndex>>
+FuzzySymbolIndex::createFromYAML(StringRef FilePath) {
+ auto Buffer = llvm::MemoryBuffer::getFile(FilePath);
+ if (!Buffer)
+ return llvm::errorCodeToError(Buffer.getError());
+ return llvm::make_unique<MemSymbolIndex>(
+ find_all_symbols::ReadSymbolInfosFromYAML(Buffer.get()->getBuffer()));
+}
+
+} // namespace include_fixer
+} // namespace clang
diff --git a/clang-include-fixer/FuzzySymbolIndex.h b/clang-include-fixer/FuzzySymbolIndex.h
new file mode 100644
index 00000000..27bfadf1
--- /dev/null
+++ b/clang-include-fixer/FuzzySymbolIndex.h
@@ -0,0 +1,54 @@
+//===--- FuzzySymbolIndex.h - Lookup symbols for autocomplete ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FUZZY_SYMBOL_INDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FUZZY_SYMBOL_INDEX_H
+
+#include "SymbolIndex.h"
+#include "find-all-symbols/SymbolInfo.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace include_fixer {
+
+// A FuzzySymbolIndex retrieves top-level symbols matching a query string.
+//
+// It refines the contract of SymbolIndex::search to do fuzzy matching:
+// - symbol names are tokenized: "unique ptr", "string ref".
+// - query must match prefixes of symbol tokens: [upt]
+// - if the query has multiple tokens, splits must match: [StR], not [STr].
+// Helpers for tokenization and regex matching are provided.
+//
+// Implementations may choose to truncate results, refuse short queries, etc.
+class FuzzySymbolIndex : public SymbolIndex {
+public:
+ // Loads the specified clang-include-fixer database and returns an index serving it.
+ static llvm::Expected<std::unique_ptr<FuzzySymbolIndex>>
+ createFromYAML(llvm::StringRef File);
+
+ // Helpers for implementing indexes:
+
+ // Transforms a symbol name or query into a sequence of tokens.
+ // - URLHandlerCallback --> [url, handler, callback]
+ // - snake_case11 --> [snake, case, 11]
+ // - _WTF$ --> [wtf]
+ static std::vector<std::string> tokenize(llvm::StringRef Text);
+
+ // Transforms query tokens into an unanchored regexp to match symbol tokens.
+ // - [fe f] --> /f(\w* )?e\w* f/, matches [fee fie foe].
+ static std::string queryRegexp(const std::vector<std::string> &Tokens);
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FUZZY_SYMBOL_INDEX_H
diff --git a/clang-include-fixer/InMemorySymbolIndex.cpp b/clang-include-fixer/InMemorySymbolIndex.cpp
new file mode 100644
index 00000000..e7858939
--- /dev/null
+++ b/clang-include-fixer/InMemorySymbolIndex.cpp
@@ -0,0 +1,31 @@
+//===-- InMemorySymbolIndex.cpp--------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "InMemorySymbolIndex.h"
+
+using clang::find_all_symbols::SymbolAndSignals;
+
+namespace clang {
+namespace include_fixer {
+
+InMemorySymbolIndex::InMemorySymbolIndex(
+ const std::vector<SymbolAndSignals> &Symbols) {
+ for (const auto &Symbol : Symbols)
+ LookupTable[Symbol.Symbol.getName()].push_back(Symbol);
+}
+
+std::vector<SymbolAndSignals>
+InMemorySymbolIndex::search(llvm::StringRef Identifier) {
+ auto I = LookupTable.find(Identifier);
+ if (I != LookupTable.end())
+ return I->second;
+ return {};
+}
+
+} // namespace include_fixer
+} // namespace clang
diff --git a/clang-include-fixer/InMemorySymbolIndex.h b/clang-include-fixer/InMemorySymbolIndex.h
new file mode 100644
index 00000000..bea8be91
--- /dev/null
+++ b/clang-include-fixer/InMemorySymbolIndex.h
@@ -0,0 +1,37 @@
+//===-- InMemorySymbolIndex.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INMEMORYSYMBOLINDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INMEMORYSYMBOLINDEX_H
+
+#include "SymbolIndex.h"
+#include <map>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace include_fixer {
+
+/// Xref database with fixed content.
+class InMemorySymbolIndex : public SymbolIndex {
+public:
+ InMemorySymbolIndex(
+ const std::vector<find_all_symbols::SymbolAndSignals> &Symbols);
+
+ std::vector<find_all_symbols::SymbolAndSignals>
+ search(llvm::StringRef Identifier) override;
+
+private:
+ std::map<std::string, std::vector<find_all_symbols::SymbolAndSignals>>
+ LookupTable;
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INMEMORYSYMBOLINDEX_H
diff --git a/clang-include-fixer/IncludeFixer.cpp b/clang-include-fixer/IncludeFixer.cpp
new file mode 100644
index 00000000..d364021f
--- /dev/null
+++ b/clang-include-fixer/IncludeFixer.cpp
@@ -0,0 +1,444 @@
+//===-- IncludeFixer.cpp - Include inserter based on sema callbacks -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "IncludeFixer.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/HeaderSearch.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Parse/ParseAST.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+
+#define DEBUG_TYPE "clang-include-fixer"
+
+using namespace clang;
+
+namespace clang {
+namespace include_fixer {
+namespace {
+/// Manages the parse, gathers include suggestions.
+class Action : public clang::ASTFrontendAction {
+public:
+ explicit Action(SymbolIndexManager &SymbolIndexMgr, bool MinimizeIncludePaths)
+ : SemaSource(SymbolIndexMgr, MinimizeIncludePaths,
+ /*GenerateDiagnostics=*/false) {}
+
+ std::unique_ptr<clang::ASTConsumer>
+ CreateASTConsumer(clang::CompilerInstance &Compiler,
+ StringRef InFile) override {
+ SemaSource.setFilePath(InFile);
+ 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);
+ SemaSource.setCompilerInstance(Compiler);
+ Compiler->getSema().addExternalSource(&SemaSource);
+
+ clang::ParseAST(Compiler->getSema(), Compiler->getFrontendOpts().ShowStats,
+ Compiler->getFrontendOpts().SkipFunctionBodies);
+ }
+
+ IncludeFixerContext
+ getIncludeFixerContext(const clang::SourceManager &SourceManager,
+ clang::HeaderSearch &HeaderSearch) const {
+ return SemaSource.getIncludeFixerContext(SourceManager, HeaderSearch,
+ SemaSource.getMatchedSymbols());
+ }
+
+private:
+ IncludeFixerSemaSource SemaSource;
+};
+
+} // namespace
+
+IncludeFixerActionFactory::IncludeFixerActionFactory(
+ SymbolIndexManager &SymbolIndexMgr,
+ std::vector<IncludeFixerContext> &Contexts, StringRef StyleName,
+ bool MinimizeIncludePaths)
+ : SymbolIndexMgr(SymbolIndexMgr), Contexts(Contexts),
+ MinimizeIncludePaths(MinimizeIncludePaths) {}
+
+IncludeFixerActionFactory::~IncludeFixerActionFactory() = default;
+
+bool IncludeFixerActionFactory::runInvocation(
+ std::shared_ptr<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(std::move(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);
+
+ // We abort on fatal errors so don't let a large number of errors become
+ // fatal. A missing #include can cause thousands of errors.
+ Compiler.getDiagnostics().setErrorLimit(0);
+
+ // Run the parser, gather missing includes.
+ auto ScopedToolAction =
+ llvm::make_unique<Action>(SymbolIndexMgr, MinimizeIncludePaths);
+ Compiler.ExecuteAction(*ScopedToolAction);
+
+ Contexts.push_back(ScopedToolAction->getIncludeFixerContext(
+ Compiler.getSourceManager(),
+ Compiler.getPreprocessor().getHeaderSearchInfo()));
+
+ // Technically this should only return true if we're sure that we have a
+ // parseable file. We don't know that though. Only inform users of fatal
+ // errors.
+ return !Compiler.getDiagnostics().hasFatalErrorOccurred();
+}
+
+static bool addDiagnosticsForContext(TypoCorrection &Correction,
+ const IncludeFixerContext &Context,
+ StringRef Code, SourceLocation StartOfFile,
+ ASTContext &Ctx) {
+ auto Reps = createIncludeFixerReplacements(
+ Code, Context, format::getLLVMStyle(), /*AddQualifiers=*/false);
+ if (!Reps || Reps->size() != 1)
+ return false;
+
+ unsigned DiagID = Ctx.getDiagnostics().getCustomDiagID(
+ DiagnosticsEngine::Note, "Add '#include %0' to provide the missing "
+ "declaration [clang-include-fixer]");
+
+ // FIXME: Currently we only generate a diagnostic for the first header. Give
+ // the user choices.
+ const tooling::Replacement &Placed = *Reps->begin();
+
+ auto Begin = StartOfFile.getLocWithOffset(Placed.getOffset());
+ auto End = Begin.getLocWithOffset(std::max(0, (int)Placed.getLength() - 1));
+ PartialDiagnostic PD(DiagID, Ctx.getDiagAllocator());
+ PD << Context.getHeaderInfos().front().Header
+ << FixItHint::CreateReplacement(CharSourceRange::getCharRange(Begin, End),
+ Placed.getReplacementText());
+ Correction.addExtraDiagnostic(std::move(PD));
+ return true;
+}
+
+/// Callback for incomplete types. If we encounter a forward declaration we
+/// have the fully qualified name ready. Just query that.
+bool IncludeFixerSemaSource::MaybeDiagnoseMissingCompleteType(
+ clang::SourceLocation Loc, clang::QualType T) {
+ // Ignore spurious callbacks from SFINAE contexts.
+ if (CI->getSema().isSFINAEContext())
+ return false;
+
+ clang::ASTContext &context = CI->getASTContext();
+ std::string QueryString = QualType(T->getUnqualifiedDesugaredType(), 0)
+ .getAsString(context.getPrintingPolicy());
+ LLVM_DEBUG(llvm::dbgs() << "Query missing complete type '" << QueryString
+ << "'");
+ // Pass an empty range here since we don't add qualifier in this case.
+ std::vector<find_all_symbols::SymbolInfo> MatchedSymbols =
+ query(QueryString, "", tooling::Range());
+
+ if (!MatchedSymbols.empty() && GenerateDiagnostics) {
+ TypoCorrection Correction;
+ FileID FID = CI->getSourceManager().getFileID(Loc);
+ StringRef Code = CI->getSourceManager().getBufferData(FID);
+ SourceLocation StartOfFile =
+ CI->getSourceManager().getLocForStartOfFile(FID);
+ addDiagnosticsForContext(
+ Correction,
+ getIncludeFixerContext(CI->getSourceManager(),
+ CI->getPreprocessor().getHeaderSearchInfo(),
+ MatchedSymbols),
+ Code, StartOfFile, CI->getASTContext());
+ for (const PartialDiagnostic &PD : Correction.getExtraDiagnostics())
+ CI->getSema().Diag(Loc, PD);
+ }
+ return true;
+}
+
+/// Callback for unknown identifiers. Try to piece together as much
+/// qualification as we can get and do a query.
+clang::TypoCorrection IncludeFixerSemaSource::CorrectTypo(
+ const DeclarationNameInfo &Typo, int LookupKind, Scope *S, CXXScopeSpec *SS,
+ CorrectionCandidateCallback &CCC, DeclContext *MemberContext,
+ bool EnteringContext, const ObjCObjectPointerType *OPT) {
+ // Ignore spurious callbacks from SFINAE contexts.
+ if (CI->getSema().isSFINAEContext())
+ return clang::TypoCorrection();
+
+ // We currently ignore the unidentified symbol which is not from the
+ // main file.
+ //
+ // However, this is not always true due to templates in a non-self contained
+ // header, consider the case:
+ //
+ // // header.h
+ // template <typename T>
+ // class Foo {
+ // T t;
+ // };
+ //
+ // // test.cc
+ // // We need to add <bar.h> in test.cc instead of header.h.
+ // class Bar;
+ // Foo<Bar> foo;
+ //
+ // FIXME: Add the missing header to the header file where the symbol comes
+ // from.
+ if (!CI->getSourceManager().isWrittenInMainFile(Typo.getLoc()))
+ return clang::TypoCorrection();
+
+ std::string TypoScopeString;
+ if (S) {
+ // FIXME: Currently we only use namespace contexts. Use other context
+ // types for query.
+ for (const auto *Context = S->getEntity(); Context;
+ Context = Context->getParent()) {
+ if (const auto *ND = dyn_cast<NamespaceDecl>(Context)) {
+ if (!ND->getName().empty())
+ TypoScopeString = ND->getNameAsString() + "::" + TypoScopeString;
+ }
+ }
+ }
+
+ auto ExtendNestedNameSpecifier = [this](CharSourceRange Range) {
+ StringRef Source =
+ Lexer::getSourceText(Range, CI->getSourceManager(), CI->getLangOpts());
+
+ // Skip forward until we find a character that's neither identifier nor
+ // colon. This is a bit of a hack around the fact that we will only get a
+ // single callback for a long nested name if a part of the beginning is
+ // unknown. For example:
+ //
+ // llvm::sys::path::parent_path(...)
+ // ^~~~ ^~~
+ // known
+ // ^~~~
+ // unknown, last callback
+ // ^~~~~~~~~~~
+ // no callback
+ //
+ // With the extension we get the full nested name specifier including
+ // parent_path.
+ // FIXME: Don't rely on source text.
+ const char *End = Source.end();
+ while (isIdentifierBody(*End) || *End == ':')
+ ++End;
+
+ return std::string(Source.begin(), End);
+ };
+
+ /// If we have a scope specification, use that to get more precise results.
+ std::string QueryString;
+ tooling::Range SymbolRange;
+ const auto &SM = CI->getSourceManager();
+ auto CreateToolingRange = [&QueryString, &SM](SourceLocation BeginLoc) {
+ return tooling::Range(SM.getDecomposedLoc(BeginLoc).second,
+ QueryString.size());
+ };
+ if (SS && SS->getRange().isValid()) {
+ auto Range = CharSourceRange::getTokenRange(SS->getRange().getBegin(),
+ Typo.getLoc());
+
+ QueryString = ExtendNestedNameSpecifier(Range);
+ SymbolRange = CreateToolingRange(Range.getBegin());
+ } else if (Typo.getName().isIdentifier() && !Typo.getLoc().isMacroID()) {
+ auto Range =
+ CharSourceRange::getTokenRange(Typo.getBeginLoc(), Typo.getEndLoc());
+
+ QueryString = ExtendNestedNameSpecifier(Range);
+ SymbolRange = CreateToolingRange(Range.getBegin());
+ } else {
+ QueryString = Typo.getAsString();
+ SymbolRange = CreateToolingRange(Typo.getLoc());
+ }
+
+ LLVM_DEBUG(llvm::dbgs() << "TypoScopeQualifiers: " << TypoScopeString
+ << "\n");
+ std::vector<find_all_symbols::SymbolInfo> MatchedSymbols =
+ query(QueryString, TypoScopeString, SymbolRange);
+
+ if (!MatchedSymbols.empty() && GenerateDiagnostics) {
+ TypoCorrection Correction(Typo.getName());
+ Correction.setCorrectionRange(SS, Typo);
+ FileID FID = SM.getFileID(Typo.getLoc());
+ StringRef Code = SM.getBufferData(FID);
+ SourceLocation StartOfFile = SM.getLocForStartOfFile(FID);
+ if (addDiagnosticsForContext(
+ Correction, getIncludeFixerContext(
+ SM, CI->getPreprocessor().getHeaderSearchInfo(),
+ MatchedSymbols),
+ Code, StartOfFile, CI->getASTContext()))
+ return Correction;
+ }
+ return TypoCorrection();
+}
+
+/// Get the minimal include for a given path.
+std::string IncludeFixerSemaSource::minimizeInclude(
+ StringRef Include, const clang::SourceManager &SourceManager,
+ clang::HeaderSearch &HeaderSearch) const {
+ if (!MinimizeIncludePaths)
+ return Include;
+
+ // Get the FileEntry for the include.
+ StringRef StrippedInclude = Include.trim("\"<>");
+ const FileEntry *Entry =
+ SourceManager.getFileManager().getFile(StrippedInclude);
+
+ // If the file doesn't exist return the path from the database.
+ // FIXME: This should never happen.
+ if (!Entry)
+ return Include;
+
+ bool IsSystem;
+ std::string Suggestion =
+ HeaderSearch.suggestPathToFileForDiagnostics(Entry, &IsSystem);
+
+ return IsSystem ? '<' + Suggestion + '>' : '"' + Suggestion + '"';
+}
+
+/// Get the include fixer context for the queried symbol.
+IncludeFixerContext IncludeFixerSemaSource::getIncludeFixerContext(
+ const clang::SourceManager &SourceManager,
+ clang::HeaderSearch &HeaderSearch,
+ ArrayRef<find_all_symbols::SymbolInfo> MatchedSymbols) const {
+ std::vector<find_all_symbols::SymbolInfo> SymbolCandidates;
+ for (const auto &Symbol : MatchedSymbols) {
+ std::string FilePath = Symbol.getFilePath().str();
+ std::string MinimizedFilePath = minimizeInclude(
+ ((FilePath[0] == '"' || FilePath[0] == '<') ? FilePath
+ : "\"" + FilePath + "\""),
+ SourceManager, HeaderSearch);
+ SymbolCandidates.emplace_back(Symbol.getName(), Symbol.getSymbolKind(),
+ MinimizedFilePath, Symbol.getContexts());
+ }
+ return IncludeFixerContext(FilePath, QuerySymbolInfos, SymbolCandidates);
+}
+
+std::vector<find_all_symbols::SymbolInfo>
+IncludeFixerSemaSource::query(StringRef Query, StringRef ScopedQualifiers,
+ tooling::Range Range) {
+ assert(!Query.empty() && "Empty query!");
+
+ // Save all instances of an unidentified symbol.
+ //
+ // We use conservative behavior for detecting the same unidentified symbol
+ // here. The symbols which have the same ScopedQualifier and RawIdentifier
+ // are considered equal. So that clang-include-fixer avoids false positives,
+ // and always adds missing qualifiers to correct symbols.
+ if (!GenerateDiagnostics && !QuerySymbolInfos.empty()) {
+ if (ScopedQualifiers == QuerySymbolInfos.front().ScopedQualifiers &&
+ Query == QuerySymbolInfos.front().RawIdentifier) {
+ QuerySymbolInfos.push_back({Query.str(), ScopedQualifiers, Range});
+ }
+ return {};
+ }
+
+ LLVM_DEBUG(llvm::dbgs() << "Looking up '" << Query << "' at ");
+ LLVM_DEBUG(CI->getSourceManager()
+ .getLocForStartOfFile(CI->getSourceManager().getMainFileID())
+ .getLocWithOffset(Range.getOffset())
+ .print(llvm::dbgs(), CI->getSourceManager()));
+ LLVM_DEBUG(llvm::dbgs() << " ...");
+ llvm::StringRef FileName = CI->getSourceManager().getFilename(
+ CI->getSourceManager().getLocForStartOfFile(
+ CI->getSourceManager().getMainFileID()));
+
+ QuerySymbolInfos.push_back({Query.str(), ScopedQualifiers, Range});
+
+ // Query the symbol based on C++ name Lookup rules.
+ // Firstly, lookup the identifier with scoped namespace contexts;
+ // If that fails, falls back to look up the identifier directly.
+ //
+ // For example:
+ //
+ // namespace a {
+ // b::foo f;
+ // }
+ //
+ // 1. lookup a::b::foo.
+ // 2. lookup b::foo.
+ std::string QueryString = ScopedQualifiers.str() + Query.str();
+ // It's unsafe to do nested search for the identifier with scoped namespace
+ // context, it might treat the identifier as a nested class of the scoped
+ // namespace.
+ std::vector<find_all_symbols::SymbolInfo> MatchedSymbols =
+ SymbolIndexMgr.search(QueryString, /*IsNestedSearch=*/false, FileName);
+ if (MatchedSymbols.empty())
+ MatchedSymbols =
+ SymbolIndexMgr.search(Query, /*IsNestedSearch=*/true, FileName);
+ LLVM_DEBUG(llvm::dbgs() << "Having found " << MatchedSymbols.size()
+ << " symbols\n");
+ // We store a copy of MatchedSymbols in a place where it's globally reachable.
+ // This is used by the standalone version of the tool.
+ this->MatchedSymbols = MatchedSymbols;
+ return MatchedSymbols;
+}
+
+llvm::Expected<tooling::Replacements> createIncludeFixerReplacements(
+ StringRef Code, const IncludeFixerContext &Context,
+ const clang::format::FormatStyle &Style, bool AddQualifiers) {
+ if (Context.getHeaderInfos().empty())
+ return tooling::Replacements();
+ StringRef FilePath = Context.getFilePath();
+ std::string IncludeName =
+ "#include " + Context.getHeaderInfos().front().Header + "\n";
+ // Create replacements for the new header.
+ clang::tooling::Replacements Insertions;
+ auto Err =
+ Insertions.add(tooling::Replacement(FilePath, UINT_MAX, 0, IncludeName));
+ if (Err)
+ return std::move(Err);
+
+ auto CleanReplaces = cleanupAroundReplacements(Code, Insertions, Style);
+ if (!CleanReplaces)
+ return CleanReplaces;
+
+ auto Replaces = std::move(*CleanReplaces);
+ if (AddQualifiers) {
+ for (const auto &Info : Context.getQuerySymbolInfos()) {
+ // Ignore the empty range.
+ if (Info.Range.getLength() > 0) {
+ auto R = tooling::Replacement(
+ {FilePath, Info.Range.getOffset(), Info.Range.getLength(),
+ Context.getHeaderInfos().front().QualifiedName});
+ auto Err = Replaces.add(R);
+ if (Err) {
+ llvm::consumeError(std::move(Err));
+ R = tooling::Replacement(
+ R.getFilePath(), Replaces.getShiftedCodePosition(R.getOffset()),
+ R.getLength(), R.getReplacementText());
+ Replaces = Replaces.merge(tooling::Replacements(R));
+ }
+ }
+ }
+ }
+ return formatReplacements(Code, Replaces, Style);
+}
+
+} // namespace include_fixer
+} // namespace clang
diff --git a/clang-include-fixer/IncludeFixer.h b/clang-include-fixer/IncludeFixer.h
new file mode 100644
index 00000000..ccab65d2
--- /dev/null
+++ b/clang-include-fixer/IncludeFixer.h
@@ -0,0 +1,157 @@
+//===-- IncludeFixer.h - Include inserter -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXER_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXER_H
+
+#include "IncludeFixerContext.h"
+#include "SymbolIndexManager.h"
+#include "clang/Format/Format.h"
+#include "clang/Sema/ExternalSemaSource.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Tooling.h"
+#include <memory>
+#include <vector>
+
+namespace clang {
+
+class CompilerInvocation;
+class DiagnosticConsumer;
+class FileManager;
+class PCHContainerOperations;
+
+namespace include_fixer {
+
+class IncludeFixerActionFactory : public clang::tooling::ToolAction {
+public:
+ /// \param SymbolIndexMgr A source for matching symbols to header files.
+ /// \param Contexts The contexts for the symbols being queried.
+ /// \param StyleName Fallback style for reformatting.
+ /// \param MinimizeIncludePaths whether inserted include paths are optimized.
+ IncludeFixerActionFactory(SymbolIndexManager &SymbolIndexMgr,
+ std::vector<IncludeFixerContext> &Contexts,
+ StringRef StyleName,
+ bool MinimizeIncludePaths = true);
+
+ ~IncludeFixerActionFactory() override;
+
+ bool
+ runInvocation(std::shared_ptr<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.
+ SymbolIndexManager &SymbolIndexMgr;
+
+ /// Multiple contexts for files being processed.
+ std::vector<IncludeFixerContext> &Contexts;
+
+ /// Whether inserted include paths should be optimized.
+ bool MinimizeIncludePaths;
+
+ /// The fallback format style for formatting after insertion if no
+ /// clang-format config file was found.
+ std::string FallbackStyle;
+};
+
+/// Create replacements, which are generated by clang-format, for the
+/// missing header and mising qualifiers insertions. The function uses the
+/// first header for insertion.
+///
+/// \param Code The source code.
+/// \param Context The context which contains all information for creating
+/// clang-include-fixer replacements.
+/// \param Style clang-format style being used.
+/// \param AddQualifiers Whether we should add qualifiers to all instances of
+/// an unidentified symbol.
+///
+/// \return Formatted replacements for inserting, sorting headers and adding
+/// qualifiers on success; otherwise, an llvm::Error carrying llvm::StringError
+/// is returned.
+llvm::Expected<tooling::Replacements> createIncludeFixerReplacements(
+ StringRef Code, const IncludeFixerContext &Context,
+ const format::FormatStyle &Style = format::getLLVMStyle(),
+ bool AddQualifiers = true);
+
+/// Handles callbacks from sema, does the include lookup and turns it into an
+/// IncludeFixerContext.
+class IncludeFixerSemaSource : public clang::ExternalSemaSource {
+public:
+ explicit IncludeFixerSemaSource(SymbolIndexManager &SymbolIndexMgr,
+ bool MinimizeIncludePaths,
+ bool GenerateDiagnostics)
+ : SymbolIndexMgr(SymbolIndexMgr),
+ MinimizeIncludePaths(MinimizeIncludePaths),
+ GenerateDiagnostics(GenerateDiagnostics) {}
+
+ void setCompilerInstance(CompilerInstance *CI) { this->CI = CI; }
+ void setFilePath(StringRef FilePath) { this->FilePath = FilePath; }
+
+ /// 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;
+
+ /// 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;
+
+ /// Get the minimal include for a given path.
+ std::string minimizeInclude(StringRef Include,
+ const clang::SourceManager &SourceManager,
+ clang::HeaderSearch &HeaderSearch) const;
+
+ /// Get the include fixer context for the queried symbol.
+ IncludeFixerContext getIncludeFixerContext(
+ const clang::SourceManager &SourceManager,
+ clang::HeaderSearch &HeaderSearch,
+ ArrayRef<find_all_symbols::SymbolInfo> MatchedSymbols) const;
+
+ /// Get the global matched symbols.
+ ArrayRef<find_all_symbols::SymbolInfo> getMatchedSymbols() const {
+ return MatchedSymbols;
+ }
+
+private:
+ /// Query the database for a given identifier.
+ std::vector<find_all_symbols::SymbolInfo>
+ query(StringRef Query, StringRef ScopedQualifiers, tooling::Range Range);
+
+ CompilerInstance *CI;
+
+ /// The client to use to find cross-references.
+ SymbolIndexManager &SymbolIndexMgr;
+
+ /// The information of the symbols being queried.
+ std::vector<IncludeFixerContext::QuerySymbolInfo> QuerySymbolInfos;
+
+ /// All symbol candidates which match QuerySymbol. We only include the first
+ /// discovered identifier to avoid getting caught in results from error
+ /// recovery.
+ std::vector<find_all_symbols::SymbolInfo> MatchedSymbols;
+
+ /// The file path to the file being processed.
+ std::string FilePath;
+
+ /// Whether we should use the smallest possible include path.
+ bool MinimizeIncludePaths = true;
+
+ /// Whether we should generate diagnostics with fixits for missing symbols.
+ bool GenerateDiagnostics = false;
+};
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXER_H
diff --git a/clang-include-fixer/IncludeFixerContext.cpp b/clang-include-fixer/IncludeFixerContext.cpp
new file mode 100644
index 00000000..a9fef45c
--- /dev/null
+++ b/clang-include-fixer/IncludeFixerContext.cpp
@@ -0,0 +1,115 @@
+//===-- IncludeFixerContext.cpp - Include fixer context ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "IncludeFixerContext.h"
+#include <algorithm>
+
+namespace clang {
+namespace include_fixer {
+
+namespace {
+
+// Splits a multiply qualified names (e.g. a::b::c).
+llvm::SmallVector<llvm::StringRef, 8>
+SplitQualifiers(llvm::StringRef StringQualifiers) {
+ llvm::SmallVector<llvm::StringRef, 8> Qualifiers;
+ StringQualifiers.split(Qualifiers, "::");
+ return Qualifiers;
+}
+
+std::string createQualifiedNameForReplacement(
+ llvm::StringRef RawSymbolName,
+ llvm::StringRef SymbolScopedQualifiersName,
+ const find_all_symbols::SymbolInfo &MatchedSymbol) {
+ // No need to add missing qualifiers if SymbolIndentifer has a global scope
+ // operator "::".
+ if (RawSymbolName.startswith("::"))
+ return RawSymbolName;
+
+ std::string QualifiedName = MatchedSymbol.getQualifiedName();
+
+ // For nested classes, the qualified name constructed from database misses
+ // some stripped qualifiers, because when we search a symbol in database,
+ // we strip qualifiers from the end until we find a result. So append the
+ // missing stripped qualifiers here.
+ //
+ // Get stripped qualifiers.
+ auto SymbolQualifiers = SplitQualifiers(RawSymbolName);
+ std::string StrippedQualifiers;
+ while (!SymbolQualifiers.empty() &&
+ !llvm::StringRef(QualifiedName).endswith(SymbolQualifiers.back())) {
+ StrippedQualifiers =
+ "::" + SymbolQualifiers.back().str() + StrippedQualifiers;
+ SymbolQualifiers.pop_back();
+ }
+ // Append the missing stripped qualifiers.
+ std::string FullyQualifiedName = QualifiedName + StrippedQualifiers;
+
+ // Try to find and skip the common prefix qualifiers.
+ auto FullySymbolQualifiers = SplitQualifiers(FullyQualifiedName);
+ auto ScopedQualifiers = SplitQualifiers(SymbolScopedQualifiersName);
+ auto FullySymbolQualifiersIter = FullySymbolQualifiers.begin();
+ auto SymbolScopedQualifiersIter = ScopedQualifiers.begin();
+ while (FullySymbolQualifiersIter != FullySymbolQualifiers.end() &&
+ SymbolScopedQualifiersIter != ScopedQualifiers.end()) {
+ if (*FullySymbolQualifiersIter != *SymbolScopedQualifiersIter)
+ break;
+ ++FullySymbolQualifiersIter;
+ ++SymbolScopedQualifiersIter;
+ }
+ std::string Result;
+ for (; FullySymbolQualifiersIter != FullySymbolQualifiers.end();
+ ++FullySymbolQualifiersIter) {
+ if (!Result.empty())
+ Result += "::";
+ Result += *FullySymbolQualifiersIter;
+ }
+ return Result;
+}
+
+} // anonymous namespace
+
+IncludeFixerContext::IncludeFixerContext(
+ StringRef FilePath, std::vector<QuerySymbolInfo> QuerySymbols,
+ std::vector<find_all_symbols::SymbolInfo> Symbols)
+ : FilePath(FilePath), QuerySymbolInfos(std::move(QuerySymbols)),
+ MatchedSymbols(std::move(Symbols)) {
+ // Remove replicated QuerySymbolInfos with the same range.
+ //
+ // QuerySymbolInfos may contain replicated elements. Because CorrectTypo
+ // callback doesn't always work as we expected. In somecases, it will be
+ // triggered at the same position or unidentified symbol multiple times.
+ std::sort(QuerySymbolInfos.begin(), QuerySymbolInfos.end(),
+ [&](const QuerySymbolInfo &A, const QuerySymbolInfo &B) {
+ return std::make_pair(A.Range.getOffset(), A.Range.getLength()) <
+ std::make_pair(B.Range.getOffset(), B.Range.getLength());
+ });
+ QuerySymbolInfos.erase(
+ std::unique(QuerySymbolInfos.begin(), QuerySymbolInfos.end(),
+ [](const QuerySymbolInfo &A, const QuerySymbolInfo &B) {
+ return A.Range == B.Range;
+ }),
+ QuerySymbolInfos.end());
+ for (const auto &Symbol : MatchedSymbols) {
+ HeaderInfos.push_back(
+ {Symbol.getFilePath().str(),
+ createQualifiedNameForReplacement(
+ QuerySymbolInfos.front().RawIdentifier,
+ QuerySymbolInfos.front().ScopedQualifiers, Symbol)});
+ }
+ // Deduplicate header infos.
+ HeaderInfos.erase(std::unique(HeaderInfos.begin(), HeaderInfos.end(),
+ [](const HeaderInfo &A, const HeaderInfo &B) {
+ return A.Header == B.Header &&
+ A.QualifiedName == B.QualifiedName;
+ }),
+ HeaderInfos.end());
+}
+
+} // include_fixer
+} // clang
diff --git a/clang-include-fixer/IncludeFixerContext.h b/clang-include-fixer/IncludeFixerContext.h
new file mode 100644
index 00000000..bbb87e2b
--- /dev/null
+++ b/clang-include-fixer/IncludeFixerContext.h
@@ -0,0 +1,94 @@
+//===-- IncludeFixerContext.h - Include fixer context -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXERCONTEXT_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXERCONTEXT_H
+
+#include "find-all-symbols/SymbolInfo.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace include_fixer {
+
+/// \brief A context for a file being processed. It includes all query
+/// information, e.g. symbols being queried in database, all header candidates.
+class IncludeFixerContext {
+public:
+ struct HeaderInfo {
+ /// \brief The header where QualifiedName comes from.
+ std::string Header;
+ /// \brief A symbol name with completed namespace qualifiers which will
+ /// replace the original symbol.
+ std::string QualifiedName;
+ };
+
+ struct QuerySymbolInfo {
+ /// \brief The raw symbol name being queried in database. This name might
+ /// miss some namespace qualifiers, and will be replaced by a fully
+ /// qualified one.
+ std::string RawIdentifier;
+
+ /// \brief The qualifiers of the scope in which SymbolIdentifier lookup
+ /// occurs. It is represented as a sequence of names and scope resolution
+ /// operatiors ::, ending with a scope resolution operator (e.g. a::b::).
+ /// Empty if SymbolIdentifier is not in a specific scope.
+ std::string ScopedQualifiers;
+
+ /// \brief The replacement range of RawIdentifier.
+ tooling::Range Range;
+ };
+
+ IncludeFixerContext() = default;
+ IncludeFixerContext(StringRef FilePath,
+ std::vector<QuerySymbolInfo> QuerySymbols,
+ std::vector<find_all_symbols::SymbolInfo> Symbols);
+
+ /// \brief Get symbol name.
+ llvm::StringRef getSymbolIdentifier() const {
+ return QuerySymbolInfos.front().RawIdentifier;
+ }
+
+ /// \brief Get replacement range of the symbol.
+ tooling::Range getSymbolRange() const {
+ return QuerySymbolInfos.front().Range;
+ }
+
+ /// \brief Get the file path to the file being processed.
+ StringRef getFilePath() const { return FilePath; }
+
+ /// \brief Get header information.
+ const std::vector<HeaderInfo> &getHeaderInfos() const { return HeaderInfos; }
+
+ /// \brief Get information of symbols being querid.
+ const std::vector<QuerySymbolInfo> &getQuerySymbolInfos() const {
+ return QuerySymbolInfos;
+ }
+
+private:
+ friend struct llvm::yaml::MappingTraits<IncludeFixerContext>;
+
+ /// \brief The file path to the file being processed.
+ std::string FilePath;
+
+ /// \brief All instances of an unidentified symbol being queried.
+ std::vector<QuerySymbolInfo> QuerySymbolInfos;
+
+ /// \brief The symbol candidates which match SymbolIdentifier. The symbols are
+ /// sorted in a descending order based on the popularity info in SymbolInfo.
+ std::vector<find_all_symbols::SymbolInfo> MatchedSymbols;
+
+ /// \brief The header information.
+ std::vector<HeaderInfo> HeaderInfos;
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXERCONTEXT_H
diff --git a/clang-include-fixer/SymbolIndex.h b/clang-include-fixer/SymbolIndex.h
new file mode 100644
index 00000000..ca04d50a
--- /dev/null
+++ b/clang-include-fixer/SymbolIndex.h
@@ -0,0 +1,37 @@
+//===-- SymbolIndex.h - Interface for symbol-header matching ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEX_H
+
+#include "find-all-symbols/SymbolInfo.h"
+#include "llvm/ADT/StringRef.h"
+#include <vector>
+
+namespace clang {
+namespace include_fixer {
+
+/// This class provides an interface for finding all `SymbolInfo`s corresponding
+/// to a symbol name from a symbol database.
+class SymbolIndex {
+public:
+ virtual ~SymbolIndex() = default;
+
+ /// Search for all `SymbolInfo`s corresponding to an identifier.
+ /// \param Identifier The unqualified identifier being searched for.
+ /// \returns A list of `SymbolInfo` candidates.
+ // FIXME: Expose the type name so we can also insert using declarations (or
+ // fix the usage)
+ virtual std::vector<find_all_symbols::SymbolAndSignals>
+ search(llvm::StringRef Identifier) = 0;
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEX_H
diff --git a/clang-include-fixer/SymbolIndexManager.cpp b/clang-include-fixer/SymbolIndexManager.cpp
new file mode 100644
index 00000000..7b827536
--- /dev/null
+++ b/clang-include-fixer/SymbolIndexManager.cpp
@@ -0,0 +1,158 @@
+//===-- SymbolIndexManager.cpp - Managing multiple SymbolIndices-*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolIndexManager.h"
+#include "find-all-symbols/SymbolInfo.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Path.h"
+
+#define DEBUG_TYPE "clang-include-fixer"
+
+namespace clang {
+namespace include_fixer {
+
+using find_all_symbols::SymbolInfo;
+using find_all_symbols::SymbolAndSignals;
+
+// Calculate a score based on whether we think the given header is closely
+// related to the given source file.
+static double similarityScore(llvm::StringRef FileName,
+ llvm::StringRef Header) {
+ // Compute the maximum number of common path segements between Header and
+ // a suffix of FileName.
+ // We do not do a full longest common substring computation, as Header
+ // specifies the path we would directly #include, so we assume it is rooted
+ // relatively to a subproject of the repository.
+ int MaxSegments = 1;
+ for (auto FileI = llvm::sys::path::begin(FileName),
+ FileE = llvm::sys::path::end(FileName);
+ FileI != FileE; ++FileI) {
+ int Segments = 0;
+ for (auto HeaderI = llvm::sys::path::begin(Header),
+ HeaderE = llvm::sys::path::end(Header), I = FileI;
+ HeaderI != HeaderE && *I == *HeaderI && I != FileE; ++I, ++HeaderI) {
+ ++Segments;
+ }
+ MaxSegments = std::max(Segments, MaxSegments);
+ }
+ return MaxSegments;
+}
+
+static void rank(std::vector<SymbolAndSignals> &Symbols,
+ llvm::StringRef FileName) {
+ llvm::DenseMap<llvm::StringRef, double> Score;
+ for (const auto &Symbol : Symbols) {
+ // Calculate a score from the similarity of the header the symbol is in
+ // with the current file and the popularity of the symbol.
+ double NewScore = similarityScore(FileName, Symbol.Symbol.getFilePath()) *
+ (1.0 + std::log2(1 + Symbol.Signals.Seen));
+ double &S = Score[Symbol.Symbol.getFilePath()];
+ S = std::max(S, NewScore);
+ }
+ // Sort by the gathered scores. Use file name as a tie breaker so we can
+ // deduplicate.
+ std::sort(Symbols.begin(), Symbols.end(),
+ [&](const SymbolAndSignals &A, const SymbolAndSignals &B) {
+ auto AS = Score[A.Symbol.getFilePath()];
+ auto BS = Score[B.Symbol.getFilePath()];
+ if (AS != BS)
+ return AS > BS;
+ return A.Symbol.getFilePath() < B.Symbol.getFilePath();
+ });
+}
+
+std::vector<find_all_symbols::SymbolInfo>
+SymbolIndexManager::search(llvm::StringRef Identifier,
+ bool IsNestedSearch,
+ llvm::StringRef FileName) const {
+ // The identifier may be fully qualified, so split it and get all the context
+ // names.
+ llvm::SmallVector<llvm::StringRef, 8> Names;
+ Identifier.split(Names, "::");
+
+ bool IsFullyQualified = false;
+ if (Identifier.startswith("::")) {
+ Names.erase(Names.begin()); // Drop first (empty) element.
+ IsFullyQualified = true;
+ }
+
+ // As long as we don't find a result keep stripping name parts from the end.
+ // This is to support nested classes which aren't recorded in the database.
+ // Eventually we will either hit a class (namespaces aren't in the database
+ // either) and can report that result.
+ bool TookPrefix = false;
+ std::vector<SymbolAndSignals> MatchedSymbols;
+ do {
+ std::vector<SymbolAndSignals> Symbols;
+ for (const auto &DB : SymbolIndices) {
+ auto Res = DB.get()->search(Names.back());
+ Symbols.insert(Symbols.end(), Res.begin(), Res.end());
+ }
+
+ LLVM_DEBUG(llvm::dbgs() << "Searching " << Names.back() << "... got "
+ << Symbols.size() << " results...\n");
+
+ for (auto &SymAndSig : Symbols) {
+ const SymbolInfo &Symbol = SymAndSig.Symbol;
+ // Match the identifier name without qualifier.
+ bool IsMatched = true;
+ auto SymbolContext = Symbol.getContexts().begin();
+ auto IdentiferContext = Names.rbegin() + 1; // Skip identifier name.
+ // Match the remaining context names.
+ while (IdentiferContext != Names.rend() &&
+ SymbolContext != Symbol.getContexts().end()) {
+ if (SymbolContext->second == *IdentiferContext) {
+ ++IdentiferContext;
+ ++SymbolContext;
+ } else if (SymbolContext->first ==
+ find_all_symbols::SymbolInfo::ContextType::EnumDecl) {
+ // Skip non-scoped enum context.
+ ++SymbolContext;
+ } else {
+ IsMatched = false;
+ break;
+ }
+ }
+
+ // If the name was qualified we only want to add results if we evaluated
+ // all contexts.
+ if (IsFullyQualified)
+ IsMatched &= (SymbolContext == Symbol.getContexts().end());
+
+ // FIXME: Support full match. At this point, we only find symbols in
+ // database which end with the same contexts with the identifier.
+ if (IsMatched && IdentiferContext == Names.rend()) {
+ // If we're in a situation where we took a prefix but the thing we
+ // found couldn't possibly have a nested member ignore it.
+ if (TookPrefix &&
+ (Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Function ||
+ Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Variable ||
+ Symbol.getSymbolKind() ==
+ SymbolInfo::SymbolKind::EnumConstantDecl ||
+ Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Macro))
+ continue;
+
+ MatchedSymbols.push_back(std::move(SymAndSig));
+ }
+ }
+ Names.pop_back();
+ TookPrefix = true;
+ } while (MatchedSymbols.empty() && !Names.empty() && IsNestedSearch);
+
+ rank(MatchedSymbols, FileName);
+ // Strip signals, they are no longer needed.
+ std::vector<SymbolInfo> Res;
+ for (auto &SymAndSig : MatchedSymbols)
+ Res.push_back(std::move(SymAndSig.Symbol));
+ return Res;
+}
+
+} // namespace include_fixer
+} // namespace clang
diff --git a/clang-include-fixer/SymbolIndexManager.h b/clang-include-fixer/SymbolIndexManager.h
new file mode 100644
index 00000000..ca2d7399
--- /dev/null
+++ b/clang-include-fixer/SymbolIndexManager.h
@@ -0,0 +1,65 @@
+//===-- SymbolIndexManager.h - Managing multiple SymbolIndices --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEXMANAGER_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEXMANAGER_H
+
+#include "SymbolIndex.h"
+#include "find-all-symbols/SymbolInfo.h"
+#include "llvm/ADT/StringRef.h"
+
+#ifdef _MSC_VER
+// Disable warnings from ppltasks.h transitively included by <future>.
+#pragma warning(push)
+#pragma warning(disable:4530)
+#endif
+
+#include <future>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+namespace clang {
+namespace include_fixer {
+
+/// This class provides an interface for finding the header files corresponding
+/// to an identifier in the source code from multiple symbol databases.
+class SymbolIndexManager {
+public:
+ void addSymbolIndex(std::function<std::unique_ptr<SymbolIndex>()> F) {
+#if LLVM_ENABLE_THREADS
+ auto Strategy = std::launch::async;
+#else
+ auto Strategy = std::launch::deferred;
+#endif
+ SymbolIndices.push_back(std::async(Strategy, F));
+ }
+
+ /// Search for header files to be included for an identifier.
+ /// \param Identifier The identifier being searched for. May or may not be
+ /// fully qualified.
+ /// \param IsNestedSearch Whether searching nested classes. If true, the
+ /// method tries to strip identifier name parts from the end until it
+ /// finds the corresponding candidates in database (e.g for identifier
+ /// "b::foo", the method will try to find "b" if it fails to find
+ /// "b::foo").
+ ///
+ /// \returns A list of symbol candidates.
+ std::vector<find_all_symbols::SymbolInfo>
+ search(llvm::StringRef Identifier, bool IsNestedSearch = true,
+ llvm::StringRef FileName = "") const;
+
+private:
+ std::vector<std::shared_future<std::unique_ptr<SymbolIndex>>> SymbolIndices;
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif
diff --git a/clang-include-fixer/YamlSymbolIndex.cpp b/clang-include-fixer/YamlSymbolIndex.cpp
new file mode 100644
index 00000000..de72e9a9
--- /dev/null
+++ b/clang-include-fixer/YamlSymbolIndex.cpp
@@ -0,0 +1,60 @@
+//===-- YamlSymbolIndex.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "YamlSymbolIndex.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include <string>
+#include <vector>
+
+using clang::find_all_symbols::SymbolInfo;
+using clang::find_all_symbols::SymbolAndSignals;
+
+namespace clang {
+namespace include_fixer {
+
+llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
+YamlSymbolIndex::createFromFile(llvm::StringRef FilePath) {
+ auto Buffer = llvm::MemoryBuffer::getFile(FilePath);
+ if (!Buffer)
+ return Buffer.getError();
+
+ return std::unique_ptr<YamlSymbolIndex>(new YamlSymbolIndex(
+ find_all_symbols::ReadSymbolInfosFromYAML(Buffer.get()->getBuffer())));
+}
+
+llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
+YamlSymbolIndex::createFromDirectory(llvm::StringRef Directory,
+ llvm::StringRef Name) {
+ // Walk upwards from Directory, looking for files.
+ for (llvm::SmallString<128> PathStorage = Directory; !Directory.empty();
+ Directory = llvm::sys::path::parent_path(Directory)) {
+ assert(Directory.size() <= PathStorage.size());
+ PathStorage.resize(Directory.size()); // Shrink to parent.
+ llvm::sys::path::append(PathStorage, Name);
+ if (auto DB = createFromFile(PathStorage))
+ return DB;
+ }
+ return llvm::make_error_code(llvm::errc::no_such_file_or_directory);
+}
+
+std::vector<SymbolAndSignals>
+YamlSymbolIndex::search(llvm::StringRef Identifier) {
+ std::vector<SymbolAndSignals> Results;
+ for (const auto &Symbol : Symbols) {
+ if (Symbol.Symbol.getName() == Identifier)
+ Results.push_back(Symbol);
+ }
+ return Results;
+}
+
+} // namespace include_fixer
+} // namespace clang
diff --git a/clang-include-fixer/YamlSymbolIndex.h b/clang-include-fixer/YamlSymbolIndex.h
new file mode 100644
index 00000000..3c4f5144
--- /dev/null
+++ b/clang-include-fixer/YamlSymbolIndex.h
@@ -0,0 +1,45 @@
+//===-- YamlSymbolIndex.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_YAMLSYMBOLINDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_YAMLSYMBOLINDEX_H
+
+#include "SymbolIndex.h"
+#include "find-all-symbols/SymbolInfo.h"
+#include "llvm/Support/ErrorOr.h"
+#include <map>
+#include <vector>
+
+namespace clang {
+namespace include_fixer {
+
+/// Yaml format database.
+class YamlSymbolIndex : public SymbolIndex {
+public:
+ /// Create a new Yaml db from a file.
+ static llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
+ createFromFile(llvm::StringRef FilePath);
+ /// Look for a file called \c Name in \c Directory and all parent directories.
+ static llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
+ createFromDirectory(llvm::StringRef Directory, llvm::StringRef Name);
+
+ std::vector<find_all_symbols::SymbolAndSignals>
+ search(llvm::StringRef Identifier) override;
+
+private:
+ explicit YamlSymbolIndex(
+ std::vector<find_all_symbols::SymbolAndSignals> Symbols)
+ : Symbols(std::move(Symbols)) {}
+
+ std::vector<find_all_symbols::SymbolAndSignals> Symbols;
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_YAMLSYMBOLINDEX_H
diff --git a/clang-include-fixer/find-all-symbols/CMakeLists.txt b/clang-include-fixer/find-all-symbols/CMakeLists.txt
new file mode 100644
index 00000000..c5fe19bf
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/CMakeLists.txt
@@ -0,0 +1,24 @@
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_library(findAllSymbols
+ FindAllSymbols.cpp
+ FindAllSymbolsAction.cpp
+ FindAllMacros.cpp
+ HeaderMapCollector.cpp
+ PathConfig.cpp
+ PragmaCommentHandler.cpp
+ STLPostfixHeaderMap.cpp
+ SymbolInfo.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangFrontend
+ clangLex
+ clangTooling
+ )
+
+add_subdirectory(tool)
diff --git a/clang-include-fixer/find-all-symbols/FindAllMacros.cpp b/clang-include-fixer/find-all-symbols/FindAllMacros.cpp
new file mode 100644
index 00000000..ed1bc2f4
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/FindAllMacros.cpp
@@ -0,0 +1,69 @@
+//===-- FindAllMacros.cpp - find all macros ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "FindAllMacros.h"
+#include "HeaderMapCollector.h"
+#include "PathConfig.h"
+#include "SymbolInfo.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/MacroInfo.h"
+#include "clang/Lex/Token.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+llvm::Optional<SymbolInfo>
+FindAllMacros::CreateMacroSymbol(const Token &MacroNameTok,
+ const MacroInfo *info) {
+ std::string FilePath =
+ getIncludePath(*SM, info->getDefinitionLoc(), Collector);
+ if (FilePath.empty())
+ return llvm::None;
+ return SymbolInfo(MacroNameTok.getIdentifierInfo()->getName(),
+ SymbolInfo::SymbolKind::Macro, FilePath, {});
+}
+
+void FindAllMacros::MacroDefined(const Token &MacroNameTok,
+ const MacroDirective *MD) {
+ if (auto Symbol = CreateMacroSymbol(MacroNameTok, MD->getMacroInfo()))
+ ++FileSymbols[*Symbol].Seen;
+}
+
+void FindAllMacros::MacroUsed(const Token &Name, const MacroDefinition &MD) {
+ if (!MD || !SM->isInMainFile(SM->getExpansionLoc(Name.getLocation())))
+ return;
+ if (auto Symbol = CreateMacroSymbol(Name, MD.getMacroInfo()))
+ ++FileSymbols[*Symbol].Used;
+}
+
+void FindAllMacros::MacroExpands(const Token &MacroNameTok,
+ const MacroDefinition &MD, SourceRange Range,
+ const MacroArgs *Args) {
+ MacroUsed(MacroNameTok, MD);
+}
+
+void FindAllMacros::Ifdef(SourceLocation Loc, const Token &MacroNameTok,
+ const MacroDefinition &MD) {
+ MacroUsed(MacroNameTok, MD);
+}
+
+void FindAllMacros::Ifndef(SourceLocation Loc, const Token &MacroNameTok,
+ const MacroDefinition &MD) {
+ MacroUsed(MacroNameTok, MD);
+}
+
+void FindAllMacros::EndOfMainFile() {
+ Reporter->reportSymbols(SM->getFileEntryForID(SM->getMainFileID())->getName(),
+ FileSymbols);
+ FileSymbols.clear();
+}
+
+} // namespace find_all_symbols
+} // namespace clang
diff --git a/clang-include-fixer/find-all-symbols/FindAllMacros.h b/clang-include-fixer/find-all-symbols/FindAllMacros.h
new file mode 100644
index 00000000..5aaf3884
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/FindAllMacros.h
@@ -0,0 +1,64 @@
+//===-- FindAllMacros.h - find all macros -----------------------*- C++ -*-===//
+//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_MACROS_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_MACROS_H
+
+#include "SymbolInfo.h"
+#include "SymbolReporter.h"
+#include "clang/Lex/PPCallbacks.h"
+
+namespace clang {
+class MacroInfo;
+namespace find_all_symbols {
+
+class HeaderMapCollector;
+
+/// \brief A preprocessor that collects all macro symbols.
+/// The contexts of a macro will be ignored since they are not available during
+/// preprocessing period.
+class FindAllMacros : public clang::PPCallbacks {
+public:
+ explicit FindAllMacros(SymbolReporter *Reporter, SourceManager *SM,
+ HeaderMapCollector *Collector = nullptr)
+ : Reporter(Reporter), SM(SM), Collector(Collector) {}
+
+ void MacroDefined(const Token &MacroNameTok,
+ const MacroDirective *MD) override;
+
+ void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
+ SourceRange Range, const MacroArgs *Args) override;
+
+ void Ifdef(SourceLocation Loc, const Token &MacroNameTok,
+ const MacroDefinition &MD) override;
+
+ void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
+ const MacroDefinition &MD) override;
+
+ void EndOfMainFile() override;
+
+private:
+ llvm::Optional<SymbolInfo> CreateMacroSymbol(const Token &MacroNameTok,
+ const MacroInfo *MD);
+ // Not a callback, just a common path for all usage types.
+ void MacroUsed(const Token &Name, const MacroDefinition &MD);
+
+ SymbolInfo::SignalMap FileSymbols;
+ // Reporter for SymbolInfo.
+ SymbolReporter *const Reporter;
+ SourceManager *const SM;
+ // A remapping header file collector allowing clients to include a different
+ // header.
+ HeaderMapCollector *const Collector;
+};
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_MACROS_H
diff --git a/clang-include-fixer/find-all-symbols/FindAllSymbols.cpp b/clang-include-fixer/find-all-symbols/FindAllSymbols.cpp
new file mode 100644
index 00000000..bb6a3fa9
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/FindAllSymbols.cpp
@@ -0,0 +1,268 @@
+//===-- FindAllSymbols.cpp - find all symbols--------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "FindAllSymbols.h"
+#include "HeaderMapCollector.h"
+#include "PathConfig.h"
+#include "SymbolInfo.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/FileSystem.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace find_all_symbols {
+namespace {
+
+AST_MATCHER(EnumConstantDecl, isInScopedEnum) {
+ if (const auto *ED = dyn_cast<EnumDecl>(Node.getDeclContext()))
+ return ED->isScoped();
+ return false;
+}
+
+AST_POLYMORPHIC_MATCHER(isFullySpecialized,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl, VarDecl,
+ CXXRecordDecl)) {
+ if (Node.getTemplateSpecializationKind() == TSK_ExplicitSpecialization) {
+ bool IsPartialSpecialization =
+ llvm::isa<VarTemplatePartialSpecializationDecl>(Node) ||
+ llvm::isa<ClassTemplatePartialSpecializationDecl>(Node);
+ return !IsPartialSpecialization;
+ }
+ return false;
+}
+
+std::vector<SymbolInfo::Context> GetContexts(const NamedDecl *ND) {
+ std::vector<SymbolInfo::Context> Contexts;
+ for (const auto *Context = ND->getDeclContext(); Context;
+ Context = Context->getParent()) {
+ if (llvm::isa<TranslationUnitDecl>(Context) ||
+ llvm::isa<LinkageSpecDecl>(Context))
+ break;
+
+ assert(llvm::isa<NamedDecl>(Context) &&
+ "Expect Context to be a NamedDecl");
+ if (const auto *NSD = dyn_cast<NamespaceDecl>(Context)) {
+ if (!NSD->isInlineNamespace())
+ Contexts.emplace_back(SymbolInfo::ContextType::Namespace,
+ NSD->getName().str());
+ } else if (const auto *ED = dyn_cast<EnumDecl>(Context)) {
+ Contexts.emplace_back(SymbolInfo::ContextType::EnumDecl,
+ ED->getName().str());
+ } else {
+ const auto *RD = cast<RecordDecl>(Context);
+ Contexts.emplace_back(SymbolInfo::ContextType::Record,
+ RD->getName().str());
+ }
+ }
+ return Contexts;
+}
+
+llvm::Optional<SymbolInfo>
+CreateSymbolInfo(const NamedDecl *ND, const SourceManager &SM,
+ const HeaderMapCollector *Collector) {
+ SymbolInfo::SymbolKind Type;
+ if (llvm::isa<VarDecl>(ND)) {
+ Type = SymbolInfo::SymbolKind::Variable;
+ } else if (llvm::isa<FunctionDecl>(ND)) {
+ Type = SymbolInfo::SymbolKind::Function;
+ } else if (llvm::isa<TypedefNameDecl>(ND)) {
+ Type = SymbolInfo::SymbolKind::TypedefName;
+ } else if (llvm::isa<EnumConstantDecl>(ND)) {
+ Type = SymbolInfo::SymbolKind::EnumConstantDecl;
+ } else if (llvm::isa<EnumDecl>(ND)) {
+ Type = SymbolInfo::SymbolKind::EnumDecl;
+ // Ignore anonymous enum declarations.
+ if (ND->getName().empty())
+ return llvm::None;
+ } else {
+ assert(llvm::isa<RecordDecl>(ND) &&
+ "Matched decl must be one of VarDecl, "
+ "FunctionDecl, TypedefNameDecl, EnumConstantDecl, "
+ "EnumDecl and RecordDecl!");
+ // C-style record decl can have empty name, e.g "struct { ... } var;".
+ if (ND->getName().empty())
+ return llvm::None;
+ Type = SymbolInfo::SymbolKind::Class;
+ }
+
+ SourceLocation Loc = SM.getExpansionLoc(ND->getLocation());
+ if (!Loc.isValid()) {
+ llvm::errs() << "Declaration " << ND->getNameAsString() << "("
+ << ND->getDeclKindName()
+ << ") has invalid declaration location.";
+ return llvm::None;
+ }
+
+ std::string FilePath = getIncludePath(SM, Loc, Collector);
+ if (FilePath.empty()) return llvm::None;
+
+ return SymbolInfo(ND->getNameAsString(), Type, FilePath, GetContexts(ND));
+}
+
+} // namespace
+
+void FindAllSymbols::registerMatchers(MatchFinder *MatchFinder) {
+ // FIXME: Handle specialization.
+ auto IsInSpecialization = hasAncestor(
+ decl(anyOf(cxxRecordDecl(isExplicitTemplateSpecialization()),
+ functionDecl(isExplicitTemplateSpecialization()))));
+
+ // Matchers for both C and C++.
+ // We only match symbols from header files, i.e. not from main files (see
+ // function's comment for detailed explanation).
+ auto CommonFilter =
+ allOf(unless(isImplicit()), unless(isExpansionInMainFile()));
+
+ auto HasNSOrTUCtxMatcher =
+ hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl()));
+
+ // We need seperate rules for C record types and C++ record types since some
+ // template related matchers are inapplicable on C record declarations.
+ //
+ // Matchers specific to C++ code.
+ // All declarations should be in namespace or translation unit.
+ auto CCMatcher =
+ allOf(HasNSOrTUCtxMatcher, unless(IsInSpecialization),
+ unless(ast_matchers::isTemplateInstantiation()),
+ unless(isInstantiated()), unless(isFullySpecialized()));
+
+ // Matchers specific to code in extern "C" {...}.
+ auto ExternCMatcher = hasDeclContext(linkageSpecDecl());
+
+ // Matchers for variable declarations.
+ //
+ // In most cases, `ParmVarDecl` is filtered out by hasDeclContext(...)
+ // matcher since the declaration context is usually `MethodDecl`. However,
+ // this assumption does not hold for parameters of a function pointer
+ // parameter.
+ // For example, consider a function declaration:
+ // void Func(void (*)(float), int);
+ // The float parameter of the function pointer has an empty name, and its
+ // declaration context is an anonymous namespace; therefore, it won't be
+ // filtered out by our matchers above.
+ auto Vars = varDecl(CommonFilter, anyOf(ExternCMatcher, CCMatcher),
+ unless(parmVarDecl()));
+
+ // Matchers for C-style record declarations in extern "C" {...}.
+ auto CRecords = recordDecl(CommonFilter, ExternCMatcher, isDefinition());
+ // Matchers for C++ record declarations.
+ auto CXXRecords = cxxRecordDecl(CommonFilter, CCMatcher, isDefinition());
+
+ // Matchers for function declarations.
+ // We want to exclude friend declaration, but the `DeclContext` of a friend
+ // function declaration is not the class in which it is declared, so we need
+ // to explicitly check if the parent is a `friendDecl`.
+ auto Functions = functionDecl(CommonFilter, unless(hasParent(friendDecl())),
+ anyOf(ExternCMatcher, CCMatcher));
+
+ // Matcher for typedef and type alias declarations.
+ //
+ // typedef and type alias can come from C-style headers and C++ headers.
+ // For C-style headers, `DeclContxet` can be either `TranslationUnitDecl`
+ // or `LinkageSpecDecl`.
+ // For C++ headers, `DeclContext ` can be either `TranslationUnitDecl`
+ // or `NamespaceDecl`.
+ // With the following context matcher, we can match `typedefNameDecl` from
+ // both C-style headers and C++ headers (except for those in classes).
+ // "cc_matchers" are not included since template-related matchers are not
+ // applicable on `TypedefNameDecl`.
+ auto Typedefs =
+ typedefNameDecl(CommonFilter, anyOf(HasNSOrTUCtxMatcher,
+ hasDeclContext(linkageSpecDecl())));
+
+ // Matchers for enum declarations.
+ auto Enums = enumDecl(CommonFilter, isDefinition(),
+ anyOf(HasNSOrTUCtxMatcher, ExternCMatcher));
+
+ // Matchers for enum constant declarations.
+ // We only match the enum constants in non-scoped enum declarations which are
+ // inside toplevel translation unit or a namespace.
+ auto EnumConstants = enumConstantDecl(
+ CommonFilter, unless(isInScopedEnum()),
+ anyOf(hasDeclContext(enumDecl(HasNSOrTUCtxMatcher)), ExternCMatcher));
+
+ // Most of the time we care about all matchable decls, or all types.
+ auto Types = namedDecl(anyOf(CRecords, CXXRecords, Enums));
+ auto Decls = namedDecl(anyOf(CRecords, CXXRecords, Enums, Typedefs, Vars,
+ EnumConstants, Functions));
+
+ // We want eligible decls bound to "decl"...
+ MatchFinder->addMatcher(Decls.bind("decl"), this);
+
+ // ... and all uses of them bound to "use". These have many cases:
+ // Uses of values/functions: these generate a declRefExpr.
+ MatchFinder->addMatcher(
+ declRefExpr(isExpansionInMainFile(), to(Decls.bind("use"))), this);
+ // Uses of function templates:
+ MatchFinder->addMatcher(
+ declRefExpr(isExpansionInMainFile(),
+ to(functionDecl(hasParent(
+ functionTemplateDecl(has(Functions.bind("use"))))))),
+ this);
+
+ // Uses of most types: just look at what the typeLoc refers to.
+ MatchFinder->addMatcher(
+ typeLoc(isExpansionInMainFile(),
+ loc(qualType(hasDeclaration(Types.bind("use"))))),
+ this);
+ // Uses of typedefs: these are often transparent to hasDeclaration, so we need
+ // to handle them explicitly.
+ MatchFinder->addMatcher(
+ typeLoc(isExpansionInMainFile(),
+ loc(typedefType(hasDeclaration(Typedefs.bind("use"))))),
+ this);
+ // Uses of class templates:
+ // The typeLoc names the templateSpecializationType. Its declaration is the
+ // ClassTemplateDecl, which contains the CXXRecordDecl we want.
+ MatchFinder->addMatcher(
+ typeLoc(isExpansionInMainFile(),
+ loc(templateSpecializationType(hasDeclaration(
+ classTemplateSpecializationDecl(hasSpecializedTemplate(
+ classTemplateDecl(has(CXXRecords.bind("use"))))))))),
+ this);
+}
+
+void FindAllSymbols::run(const MatchFinder::MatchResult &Result) {
+ // Ignore Results in failing TUs.
+ if (Result.Context->getDiagnostics().hasErrorOccurred()) {
+ return;
+ }
+
+ SymbolInfo::Signals Signals;
+ const NamedDecl *ND;
+ if ((ND = Result.Nodes.getNodeAs<NamedDecl>("use")))
+ Signals.Used = 1;
+ else if ((ND = Result.Nodes.getNodeAs<NamedDecl>("decl")))
+ Signals.Seen = 1;
+ else
+ assert(false && "Must match a NamedDecl!");
+
+ const SourceManager *SM = Result.SourceManager;
+ if (auto Symbol = CreateSymbolInfo(ND, *SM, Collector)) {
+ Filename = SM->getFileEntryForID(SM->getMainFileID())->getName();
+ FileSymbols[*Symbol] += Signals;
+ }
+}
+
+void FindAllSymbols::onEndOfTranslationUnit() {
+ if (Filename != "") {
+ Reporter->reportSymbols(Filename, FileSymbols);
+ FileSymbols.clear();
+ Filename = "";
+ }
+}
+
+} // namespace find_all_symbols
+} // namespace clang
diff --git a/clang-include-fixer/find-all-symbols/FindAllSymbols.h b/clang-include-fixer/find-all-symbols/FindAllSymbols.h
new file mode 100644
index 00000000..d78da668
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/FindAllSymbols.h
@@ -0,0 +1,62 @@
+//===-- FindAllSymbols.h - find all symbols----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_MATCHER_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_MATCHER_H
+
+#include "SymbolInfo.h"
+#include "SymbolReporter.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <string>
+
+namespace clang {
+namespace find_all_symbols {
+
+class HeaderMapCollector;
+
+/// \brief FindAllSymbols collects all classes, free standing functions and
+/// global variables with some extra information such as the path of the header
+/// file, the namespaces they are contained in, the type of variables and the
+/// parameter types of functions.
+///
+/// NOTE:
+/// - Symbols declared in main files are not collected since they can not be
+/// included.
+/// - Member functions are not collected because accessing them must go
+/// through the class. #include fixer only needs the class name to find
+/// headers.
+///
+class FindAllSymbols : public ast_matchers::MatchFinder::MatchCallback {
+public:
+ explicit FindAllSymbols(SymbolReporter *Reporter,
+ HeaderMapCollector *Collector = nullptr)
+ : Reporter(Reporter), Collector(Collector) {}
+
+ void registerMatchers(ast_matchers::MatchFinder *MatchFinder);
+
+ void run(const ast_matchers::MatchFinder::MatchResult &result) override;
+
+protected:
+ void onEndOfTranslationUnit() override;
+
+private:
+ // Current source file being processed, filled by first symbol found.
+ std::string Filename;
+ // Findings for the current source file, flushed on onEndOfTranslationUnit.
+ SymbolInfo::SignalMap FileSymbols;
+ // Reporter for SymbolInfo.
+ SymbolReporter *const Reporter;
+ // A remapping header file collector allowing clients include a different
+ // header.
+ HeaderMapCollector *const Collector;
+};
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_MATCHER_H
diff --git a/clang-include-fixer/find-all-symbols/FindAllSymbolsAction.cpp b/clang-include-fixer/find-all-symbols/FindAllSymbolsAction.cpp
new file mode 100644
index 00000000..9f1d31dc
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/FindAllSymbolsAction.cpp
@@ -0,0 +1,36 @@
+//===-- FindAllSymbolsAction.cpp - find all symbols action --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "FindAllSymbolsAction.h"
+#include "FindAllMacros.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/STLExtras.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+FindAllSymbolsAction::FindAllSymbolsAction(
+ SymbolReporter *Reporter,
+ const HeaderMapCollector::RegexHeaderMap *RegexHeaderMap)
+ : Reporter(Reporter), Collector(RegexHeaderMap), Handler(&Collector),
+ Matcher(Reporter, &Collector) {
+ Matcher.registerMatchers(&MatchFinder);
+}
+
+std::unique_ptr<ASTConsumer>
+FindAllSymbolsAction::CreateASTConsumer(CompilerInstance &Compiler,
+ StringRef InFile) {
+ Compiler.getPreprocessor().addCommentHandler(&Handler);
+ Compiler.getPreprocessor().addPPCallbacks(llvm::make_unique<FindAllMacros>(
+ Reporter, &Compiler.getSourceManager(), &Collector));
+ return MatchFinder.newASTConsumer();
+}
+
+} // namespace find_all_symbols
+} // namespace clang
diff --git a/clang-include-fixer/find-all-symbols/FindAllSymbolsAction.h b/clang-include-fixer/find-all-symbols/FindAllSymbolsAction.h
new file mode 100644
index 00000000..ccffa4b3
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/FindAllSymbolsAction.h
@@ -0,0 +1,62 @@
+//===-- FindAllSymbolsAction.h - find all symbols action --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_SYMBOLS_ACTION_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_SYMBOLS_ACTION_H
+
+#include "FindAllSymbols.h"
+#include "HeaderMapCollector.h"
+#include "PragmaCommentHandler.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/StringRef.h"
+#include <memory>
+
+namespace clang {
+namespace find_all_symbols {
+
+class FindAllSymbolsAction : public clang::ASTFrontendAction {
+public:
+ explicit FindAllSymbolsAction(
+ SymbolReporter *Reporter,
+ const HeaderMapCollector::RegexHeaderMap *RegexHeaderMap = nullptr);
+
+ std::unique_ptr<clang::ASTConsumer>
+ CreateASTConsumer(clang::CompilerInstance &Compiler,
+ StringRef InFile) override;
+
+private:
+ SymbolReporter *const Reporter;
+ clang::ast_matchers::MatchFinder MatchFinder;
+ HeaderMapCollector Collector;
+ PragmaCommentHandler Handler;
+ FindAllSymbols Matcher;
+};
+
+class FindAllSymbolsActionFactory : public tooling::FrontendActionFactory {
+public:
+ FindAllSymbolsActionFactory(
+ SymbolReporter *Reporter,
+ const HeaderMapCollector::RegexHeaderMap *RegexHeaderMap = nullptr)
+ : Reporter(Reporter), RegexHeaderMap(RegexHeaderMap) {}
+
+ clang::FrontendAction *create() override {
+ return new FindAllSymbolsAction(Reporter, RegexHeaderMap);
+ }
+
+private:
+ SymbolReporter *const Reporter;
+ const HeaderMapCollector::RegexHeaderMap *const RegexHeaderMap;
+};
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_SYMBOLS_ACTION_H
diff --git a/clang-include-fixer/find-all-symbols/HeaderMapCollector.cpp b/clang-include-fixer/find-all-symbols/HeaderMapCollector.cpp
new file mode 100644
index 00000000..6ec49cae
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/HeaderMapCollector.cpp
@@ -0,0 +1,44 @@
+//===-- HeaderMapCoolector.h - find all symbols------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "HeaderMapCollector.h"
+#include "llvm/Support/Regex.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+HeaderMapCollector::HeaderMapCollector(
+ const RegexHeaderMap *RegexHeaderMappingTable) {
+ assert(RegexHeaderMappingTable);
+ this->RegexHeaderMappingTable.reserve(RegexHeaderMappingTable->size());
+ for (const auto &Entry : *RegexHeaderMappingTable) {
+ this->RegexHeaderMappingTable.emplace_back(llvm::Regex(Entry.first),
+ Entry.second);
+ }
+}
+
+llvm::StringRef
+HeaderMapCollector::getMappedHeader(llvm::StringRef Header) const {
+ auto Iter = HeaderMappingTable.find(Header);
+ if (Iter != HeaderMappingTable.end())
+ return Iter->second;
+ // If there is no complete header name mapping for this header, check the
+ // regex header mapping.
+ for (auto &Entry : RegexHeaderMappingTable) {
+#ifndef NDEBUG
+ std::string Dummy;
+ assert(Entry.first.isValid(Dummy) && "Regex should never be invalid!");
+#endif
+ if (Entry.first.match(Header))
+ return Entry.second;
+ }
+ return Header;
+}
+
+} // namespace find_all_symbols
+} // namespace clang
diff --git a/clang-include-fixer/find-all-symbols/HeaderMapCollector.h b/clang-include-fixer/find-all-symbols/HeaderMapCollector.h
new file mode 100644
index 00000000..21358275
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/HeaderMapCollector.h
@@ -0,0 +1,56 @@
+//===-- HeaderMapCoolector.h - find all symbols------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_HEADER_MAP_COLLECTOR_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_HEADER_MAP_COLLECTOR_H
+
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Regex.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace find_all_symbols {
+
+/// \brief HeaderMappCollector collects all remapping header files. This maps
+/// complete header names or header name regex patterns to header names.
+class HeaderMapCollector {
+public:
+ typedef llvm::StringMap<std::string> HeaderMap;
+ typedef std::vector<std::pair<const char *, const char *>> RegexHeaderMap;
+
+ HeaderMapCollector() = default;
+ explicit HeaderMapCollector(const RegexHeaderMap *RegexHeaderMappingTable);
+
+ void addHeaderMapping(llvm::StringRef OrignalHeaderPath,
+ llvm::StringRef MappingHeaderPath) {
+ HeaderMappingTable[OrignalHeaderPath] = MappingHeaderPath;
+ };
+
+ /// Check if there is a mapping from \p Header or a regex pattern that matches
+ /// it to another header name.
+ /// \param Header A header name.
+ /// \return \p Header itself if there is no mapping for it; otherwise, return
+ /// a mapped header name.
+ llvm::StringRef getMappedHeader(llvm::StringRef Header) const;
+
+private:
+ /// A string-to-string map saving the mapping relationship.
+ HeaderMap HeaderMappingTable;
+
+ // A map from header patterns to header names.
+ // The header names are not owned. This is only threadsafe because the regexes
+ // never fail.
+ mutable std::vector<std::pair<llvm::Regex, const char *>>
+ RegexHeaderMappingTable;
+};
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_HEADER_MAP_COLLECTOR_H
diff --git a/clang-include-fixer/find-all-symbols/PathConfig.cpp b/clang-include-fixer/find-all-symbols/PathConfig.cpp
new file mode 100644
index 00000000..4f1ebc77
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/PathConfig.cpp
@@ -0,0 +1,41 @@
+//===-- PathConfig.cpp - Process paths of symbols ---------------*- C++ -*-===//
+//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PathConfig.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+std::string getIncludePath(const SourceManager &SM, SourceLocation Loc,
+ const HeaderMapCollector *Collector) {
+ llvm::StringRef FilePath;
+ // Walk up the include stack to skip .inc files.
+ while (true) {
+ if (!Loc.isValid() || SM.isInMainFile(Loc))
+ return "";
+ FilePath = SM.getFilename(Loc);
+ if (FilePath.empty())
+ return "";
+ if (!FilePath.endswith(".inc"))
+ break;
+ FileID ID = SM.getFileID(Loc);
+ Loc = SM.getIncludeLoc(ID);
+ }
+
+ if (Collector)
+ FilePath = Collector->getMappedHeader(FilePath);
+ SmallString<256> CleanedFilePath = FilePath;
+ llvm::sys::path::remove_dots(CleanedFilePath, /*remove_dot_dot=*/false);
+
+ return CleanedFilePath.str();
+}
+
+} // namespace find_all_symbols
+} // namespace clang
diff --git a/clang-include-fixer/find-all-symbols/PathConfig.h b/clang-include-fixer/find-all-symbols/PathConfig.h
new file mode 100644
index 00000000..9c430f25
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/PathConfig.h
@@ -0,0 +1,36 @@
+//===-- PathConfig.h - Process paths of symbols -----------------*- C++ -*-===//
+//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PATH_CONFIG_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PATH_CONFIG_H
+
+#include "HeaderMapCollector.h"
+#include "clang/Basic/SourceManager.h"
+#include <string>
+
+namespace clang {
+namespace find_all_symbols {
+
+/// \brief This calculates the include path for \p Loc.
+///
+/// \param SM SourceManager.
+/// \param Loc A SourceLocation.
+/// \param Collector An optional header mapping collector.
+///
+/// \return The file path (or mapped file path if Collector is provided) of the
+/// header that includes \p Loc. If \p Loc comes from .inc header file, \p Loc
+/// is set to the location from which the .inc header file is included. If \p
+/// Loc is invalid or comes from a main file, this returns an empty string.
+std::string getIncludePath(const SourceManager &SM, SourceLocation Loc,
+ const HeaderMapCollector *Collector = nullptr);
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PATH_CONFIG_H
diff --git a/clang-include-fixer/find-all-symbols/PragmaCommentHandler.cpp b/clang-include-fixer/find-all-symbols/PragmaCommentHandler.cpp
new file mode 100644
index 00000000..49489752
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/PragmaCommentHandler.cpp
@@ -0,0 +1,36 @@
+//===-- PragmaCommentHandler.cpp - find all symbols -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PragmaCommentHandler.h"
+#include "FindAllSymbols.h"
+#include "HeaderMapCollector.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/Support/Regex.h"
+
+namespace clang {
+namespace find_all_symbols {
+namespace {
+const char IWYUPragma[] = "// IWYU pragma: private, include ";
+} // namespace
+
+bool PragmaCommentHandler::HandleComment(Preprocessor &PP, SourceRange Range) {
+ StringRef Text =
+ Lexer::getSourceText(CharSourceRange::getCharRange(Range),
+ PP.getSourceManager(), PP.getLangOpts());
+ size_t Pos = Text.find(IWYUPragma);
+ if (Pos == StringRef::npos)
+ return false;
+ StringRef RemappingFilePath = Text.substr(Pos + std::strlen(IWYUPragma));
+ Collector->addHeaderMapping(
+ PP.getSourceManager().getFilename(Range.getBegin()),
+ RemappingFilePath.trim("\"<>"));
+ return false;
+}
+
+} // namespace find_all_symbols
+} // namespace clang
diff --git a/clang-include-fixer/find-all-symbols/PragmaCommentHandler.h b/clang-include-fixer/find-all-symbols/PragmaCommentHandler.h
new file mode 100644
index 00000000..752c82f5
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/PragmaCommentHandler.h
@@ -0,0 +1,40 @@
+//===-- PragmaCommentHandler.h - find all symbols----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PRAGMA_COMMENT_HANDLER_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PRAGMA_COMMENT_HANDLER_H
+
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/Preprocessor.h"
+#include <map>
+
+namespace clang {
+namespace find_all_symbols {
+
+class HeaderMapCollector;
+
+/// \brief PragmaCommentHandler parses pragma comment on include files to
+/// determine when we should include a different header from the header that
+/// directly defines a symbol.
+///
+/// Currently it only supports IWYU private pragma:
+/// https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/IWYUPragmas.md#iwyu-pragma-private
+class PragmaCommentHandler : public clang::CommentHandler {
+public:
+ PragmaCommentHandler(HeaderMapCollector *Collector) : Collector(Collector) {}
+
+ bool HandleComment(Preprocessor &PP, SourceRange Range) override;
+
+private:
+ HeaderMapCollector *const Collector;
+};
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PRAGMA_COMMENT_HANDLER_H
diff --git a/clang-include-fixer/find-all-symbols/STLPostfixHeaderMap.cpp b/clang-include-fixer/find-all-symbols/STLPostfixHeaderMap.cpp
new file mode 100644
index 00000000..0d0bbd9f
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/STLPostfixHeaderMap.cpp
@@ -0,0 +1,653 @@
+//===-- STLPostfixHeaderMap.h - hardcoded STL header map --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "STLPostfixHeaderMap.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+const HeaderMapCollector::RegexHeaderMap *getSTLPostfixHeaderMap() {
+ static const HeaderMapCollector::RegexHeaderMap STLPostfixHeaderMap = {
+ {"include/__stddef_max_align_t.h$", "<cstddef>"},
+ {"include/__wmmintrin_aes.h$", "<wmmintrin.h>"},
+ {"include/__wmmintrin_pclmul.h$", "<wmmintrin.h>"},
+ {"include/adxintrin.h$", "<immintrin.h>"},
+ {"include/ammintrin.h$", "<ammintrin.h>"},
+ {"include/avx2intrin.h$", "<immintrin.h>"},
+ {"include/avx512bwintrin.h$", "<immintrin.h>"},
+ {"include/avx512cdintrin.h$", "<immintrin.h>"},
+ {"include/avx512dqintrin.h$", "<immintrin.h>"},
+ {"include/avx512erintrin.h$", "<immintrin.h>"},
+ {"include/avx512fintrin.h$", "<immintrin.h>"},
+ {"include/avx512ifmaintrin.h$", "<immintrin.h>"},
+ {"include/avx512ifmavlintrin.h$", "<immintrin.h>"},
+ {"include/avx512pfintrin.h$", "<immintrin.h>"},
+ {"include/avx512vbmiintrin.h$", "<immintrin.h>"},
+ {"include/avx512vbmivlintrin.h$", "<immintrin.h>"},
+ {"include/avx512vlbwintrin.h$", "<immintrin.h>"},
+ {"include/avx512vlcdintrin.h$", "<immintrin.h>"},
+ {"include/avx512vldqintrin.h$", "<immintrin.h>"},
+ {"include/avx512vlintrin.h$", "<immintrin.h>"},
+ {"include/avxintrin.h$", "<immintrin.h>"},
+ {"include/bmi2intrin.h$", "<x86intrin.h>"},
+ {"include/bmiintrin.h$", "<x86intrin.h>"},
+ {"include/emmintrin.h$", "<emmintrin.h>"},
+ {"include/f16cintrin.h$", "<emmintrin.h>"},
+ {"include/float.h$", "<cfloat>"},
+ {"include/fma4intrin.h$", "<x86intrin.h>"},
+ {"include/fmaintrin.h$", "<immintrin.h>"},
+ {"include/fxsrintrin.h$", "<immintrin.h>"},
+ {"include/ia32intrin.h$", "<x86intrin.h>"},
+ {"include/immintrin.h$", "<immintrin.h>"},
+ {"include/inttypes.h$", "<cinttypes>"},
+ {"include/limits.h$", "<climits>"},
+ {"include/lzcntintrin.h$", "<x86intrin.h>"},
+ {"include/mm3dnow.h$", "<mm3dnow.h>"},
+ {"include/mm_malloc.h$", "<mm_malloc.h>"},
+ {"include/mmintrin.h$", "<mmintrin>"},
+ {"include/mwaitxintrin.h$", "<x86intrin.h>"},
+ {"include/pkuintrin.h$", "<immintrin.h>"},
+ {"include/pmmintrin.h$", "<pmmintrin.h>"},
+ {"include/popcntintrin.h$", "<popcntintrin.h>"},
+ {"include/prfchwintrin.h$", "<x86intrin.h>"},
+ {"include/rdseedintrin.h$", "<x86intrin.h>"},
+ {"include/rtmintrin.h$", "<immintrin.h>"},
+ {"include/shaintrin.h$", "<immintrin.h>"},
+ {"include/smmintrin.h$", "<smmintrin.h>"},
+ {"include/stdalign.h$", "<cstdalign>"},
+ {"include/stdarg.h$", "<cstdarg>"},
+ {"include/stdbool.h$", "<cstdbool>"},
+ {"include/stddef.h$", "<cstddef>"},
+ {"include/stdint.h$", "<cstdint>"},
+ {"include/tbmintrin.h$", "<x86intrin.h>"},
+ {"include/tmmintrin.h$", "<tmmintrin.h>"},
+ {"include/wmmintrin.h$", "<wmmintrin.h>"},
+ {"include/x86intrin.h$", "<x86intrin.h>"},
+ {"include/xmmintrin.h$", "<xmmintrin.h>"},
+ {"include/xopintrin.h$", "<x86intrin.h>"},
+ {"include/xsavecintrin.h$", "<immintrin.h>"},
+ {"include/xsaveintrin.h$", "<immintrin.h>"},
+ {"include/xsaveoptintrin.h$", "<immintrin.h>"},
+ {"include/xsavesintrin.h$", "<immintrin.h>"},
+ {"include/xtestintrin.h$", "<immintrin.h>"},
+ {"include/_G_config.h$", "<cstdio>"},
+ {"include/assert.h$", "<cassert>"},
+ {"algorithm$", "<algorithm>"},
+ {"array$", "<array>"},
+ {"atomic$", "<atomic>"},
+ {"backward/auto_ptr.h$", "<memory>"},
+ {"backward/binders.h$", "<string>"},
+ {"bits/algorithmfwd.h$", "<algorithm>"},
+ {"bits/alloc_traits.h$", "<unordered_set>"},
+ {"bits/allocator.h$", "<string>"},
+ {"bits/atomic_base.h$", "<atomic>"},
+ {"bits/atomic_lockfree_defines.h$", "<exception>"},
+ {"bits/basic_ios.h$", "<ios>"},
+ {"bits/basic_ios.tcc$", "<ios>"},
+ {"bits/basic_string.h$", "<string>"},
+ {"bits/basic_string.tcc$", "<string>"},
+ {"bits/char_traits.h$", "<string>"},
+ {"bits/codecvt.h$", "<locale>"},
+ {"bits/concept_check.h$", "<numeric>"},
+ {"bits/cpp_type_traits.h$", "<cmath>"},
+ {"bits/cxxabi_forced.h$", "<cxxabi.h>"},
+ {"bits/deque.tcc$", "<deque>"},
+ {"bits/exception_defines.h$", "<exception>"},
+ {"bits/exception_ptr.h$", "<exception>"},
+ {"bits/forward_list.h$", "<forward_list>"},
+ {"bits/forward_list.tcc$", "<forward_list>"},
+ {"bits/fstream.tcc$", "<fstream>"},
+ {"bits/functexcept.h$", "<list>"},
+ {"bits/functional_hash.h$", "<string>"},
+ {"bits/gslice.h$", "<valarray>"},
+ {"bits/gslice_array.h$", "<valarray>"},
+ {"bits/hash_bytes.h$", "<typeinfo>"},
+ {"bits/hashtable.h$", "<unordered_set>"},
+ {"bits/hashtable_policy.h$", "<unordered_set>"},
+ {"bits/indirect_array.h$", "<valarray>"},
+ {"bits/ios_base.h$", "<ios>"},
+ {"bits/istream.tcc$", "<istream>"},
+ {"bits/list.tcc$", "<list>"},
+ {"bits/locale_classes.h$", "<locale>"},
+ {"bits/locale_classes.tcc$", "<locale>"},
+ {"bits/locale_facets.h$", "<locale>"},
+ {"bits/locale_facets.tcc$", "<locale>"},
+ {"bits/locale_facets_nonio.h$", "<locale>"},
+ {"bits/locale_facets_nonio.tcc$", "<locale>"},
+ {"bits/localefwd.h$", "<locale>"},
+ {"bits/mask_array.h$", "<valarray>"},
+ {"bits/memoryfwd.h$", "<memory>"},
+ {"bits/move.h$", "<utility>"},
+ {"bits/nested_exception.h$", "<exception>"},
+ {"bits/ostream.tcc$", "<ostream>"},
+ {"bits/ostream_insert.h$", "<ostream>"},
+ {"bits/postypes.h$", "<iosfwd>"},
+ {"bits/ptr_traits.h$", "<memory>"},
+ {"bits/random.h$", "<random>"},
+ {"bits/random.tcc$", "<random>"},
+ {"bits/range_access.h$", "<iterator>"},
+ {"bits/regex.h$", "<regex>"},
+ {"bits/regex_compiler.h$", "<regex>"},
+ {"bits/regex_constants.h$", "<regex>"},
+ {"bits/regex_cursor.h$", "<regex>"},
+ {"bits/regex_error.h$", "<regex>"},
+ {"bits/regex_grep_matcher.h$", "<regex>"},
+ {"bits/regex_grep_matcher.tcc$", "<regex>"},
+ {"bits/regex_nfa.h$", "<regex>"},
+ {"bits/shared_ptr.h$", "<memory>"},
+ {"bits/shared_ptr_base.h$", "<memory>"},
+ {"bits/slice_array.h$", "<valarray>"},
+ {"bits/sstream.tcc$", "<sstream>"},
+ {"bits/stl_algo.h$", "<algorithm>"},
+ {"bits/stl_algobase.h$", "<list>"},
+ {"bits/stl_bvector.h$", "<vector>"},
+ {"bits/stl_construct.h$", "<deque>"},
+ {"bits/stl_deque.h$", "<deque>"},
+ {"bits/stl_function.h$", "<string>"},
+ {"bits/stl_heap.h$", "<queue>"},
+ {"bits/stl_iterator.h$", "<iterator>"},
+ {"bits/stl_iterator_base_funcs.h$", "<iterator>"},
+ {"bits/stl_iterator_base_types.h$", "<numeric>"},
+ {"bits/stl_list.h$", "<list>"},
+ {"bits/stl_map.h$", "<map>"},
+ {"bits/stl_multimap.h$", "<map>"},
+ {"bits/stl_multiset.h$", "<set>"},
+ {"bits/stl_numeric.h$", "<numeric>"},
+ {"bits/stl_pair.h$", "<utility>"},
+ {"bits/stl_queue.h$", "<queue>"},
+ {"bits/stl_raw_storage_iter.h$", "<memory>"},
+ {"bits/stl_relops.h$", "<utility>"},
+ {"bits/stl_set.h$", "<set>"},
+ {"bits/stl_stack.h$", "<stack>"},
+ {"bits/stl_tempbuf.h$", "<memory>"},
+ {"bits/stl_tree.h$", "<map>"},
+ {"bits/stl_uninitialized.h$", "<deque>"},
+ {"bits/stl_vector.h$", "<vector>"},
+ {"bits/stream_iterator.h$", "<iterator>"},
+ {"bits/streambuf.tcc$", "<streambuf>"},
+ {"bits/streambuf_iterator.h$", "<iterator>"},
+ {"bits/stringfwd.h$", "<string>"},
+ {"bits/unique_ptr.h$", "<memory>"},
+ {"bits/unordered_map.h$", "<unordered_map>"},
+ {"bits/unordered_set.h$", "<unordered_set>"},
+ {"bits/uses_allocator.h$", "<tuple>"},
+ {"bits/valarray_after.h$", "<valarray>"},
+ {"bits/valarray_array.h$", "<valarray>"},
+ {"bits/valarray_array.tcc$", "<valarray>"},
+ {"bits/valarray_before.h$", "<valarray>"},
+ {"bits/vector.tcc$", "<vector>"},
+ {"bitset$", "<bitset>"},
+ {"ccomplex$", "<ccomplex>"},
+ {"cctype$", "<cctype>"},
+ {"cerrno$", "<cerrno>"},
+ {"cfenv$", "<cfenv>"},
+ {"cfloat$", "<cfloat>"},
+ {"chrono$", "<chrono>"},
+ {"cinttypes$", "<cinttypes>"},
+ {"climits$", "<climits>"},
+ {"clocale$", "<clocale>"},
+ {"cmath$", "<cmath>"},
+ {"complex$", "<complex>"},
+ {"complex.h$", "<complex.h>"},
+ {"condition_variable$", "<condition_variable>"},
+ {"csetjmp$", "<csetjmp>"},
+ {"csignal$", "<csignal>"},
+ {"cstdalign$", "<cstdalign>"},
+ {"cstdarg$", "<cstdarg>"},
+ {"cstdbool$", "<cstdbool>"},
+ {"cstdint$", "<cstdint>"},
+ {"cstdio$", "<cstdio>"},
+ {"cstdlib$", "<cstdlib>"},
+ {"cstring$", "<cstring>"},
+ {"ctgmath$", "<ctgmath>"},
+ {"ctime$", "<ctime>"},
+ {"cwchar$", "<cwchar>"},
+ {"cwctype$", "<cwctype>"},
+ {"cxxabi.h$", "<cxxabi.h>"},
+ {"debug/debug.h$", "<numeric>"},
+ {"debug/map.h$", "<map>"},
+ {"debug/multimap.h$", "<multimap>"},
+ {"debug/multiset.h$", "<multiset>"},
+ {"debug/set.h$", "<set>"},
+ {"deque$", "<deque>"},
+ {"exception$", "<exception>"},
+ {"ext/alloc_traits.h$", "<deque>"},
+ {"ext/atomicity.h$", "<memory>"},
+ {"ext/concurrence.h$", "<memory>"},
+ {"ext/new_allocator.h$", "<string>"},
+ {"ext/numeric_traits.h$", "<list>"},
+ {"ext/string_conversions.h$", "<string>"},
+ {"ext/type_traits.h$", "<cmath>"},
+ {"fenv.h$", "<fenv.h>"},
+ {"forward_list$", "<forward_list>"},
+ {"fstream$", "<fstream>"},
+ {"functional$", "<functional>"},
+ {"future$", "<future>"},
+ {"initializer_list$", "<initializer_list>"},
+ {"iomanip$", "<iomanip>"},
+ {"ios$", "<ios>"},
+ {"iosfwd$", "<iosfwd>"},
+ {"iostream$", "<iostream>"},
+ {"istream$", "<istream>"},
+ {"iterator$", "<iterator>"},
+ {"limits$", "<limits>"},
+ {"list$", "<list>"},
+ {"locale$", "<locale>"},
+ {"map$", "<map>"},
+ {"memory$", "<memory>"},
+ {"mutex$", "<mutex>"},
+ {"new$", "<new>"},
+ {"numeric$", "<numeric>"},
+ {"ostream$", "<ostream>"},
+ {"queue$", "<queue>"},
+ {"random$", "<random>"},
+ {"ratio$", "<ratio>"},
+ {"regex$", "<regex>"},
+ {"scoped_allocator$", "<scoped_allocator>"},
+ {"set$", "<set>"},
+ {"sstream$", "<sstream>"},
+ {"stack$", "<stack>"},
+ {"stdexcept$", "<stdexcept>"},
+ {"streambuf$", "<streambuf>"},
+ {"string$", "<string>"},
+ {"system_error$", "<system_error>"},
+ {"tgmath.h$", "<tgmath.h>"},
+ {"thread$", "<thread>"},
+ {"tuple$", "<tuple>"},
+ {"type_traits$", "<type_traits>"},
+ {"typeindex$", "<typeindex>"},
+ {"typeinfo$", "<typeinfo>"},
+ {"unordered_map$", "<unordered_map>"},
+ {"unordered_set$", "<unordered_set>"},
+ {"utility$", "<utility>"},
+ {"valarray$", "<valarray>"},
+ {"vector$", "<vector>"},
+ {"include/complex.h$", "<complex.h>"},
+ {"include/ctype.h$", "<cctype>"},
+ {"include/errno.h$", "<cerrno>"},
+ {"include/fenv.h$", "<fenv.h>"},
+ {"include/inttypes.h$", "<cinttypes>"},
+ {"include/libio.h$", "<cstdio>"},
+ {"include/limits.h$", "<climits>"},
+ {"include/locale.h$", "<clocale>"},
+ {"include/math.h$", "<cmath>"},
+ {"include/setjmp.h$", "<csetjmp>"},
+ {"include/signal.h$", "<csignal>"},
+ {"include/stdint.h$", "<cstdint>"},
+ {"include/stdio.h$", "<cstdio>"},
+ {"include/stdlib.h$", "<cstdlib>"},
+ {"include/string.h$", "<cstring>"},
+ {"include/time.h$", "<ctime>"},
+ {"include/wchar.h$", "<cwchar>"},
+ {"include/wctype.h$", "<cwctype>"},
+ {"bits/cmathcalls.h$", "<complex.h>"},
+ {"bits/errno.h$", "<cerrno>"},
+ {"bits/fenv.h$", "<fenv.h>"},
+ {"bits/huge_val.h$", "<cmath>"},
+ {"bits/huge_valf.h$", "<cmath>"},
+ {"bits/huge_vall.h$", "<cmath>"},
+ {"bits/inf.h$", "<cmath>"},
+ {"bits/local_lim.h$", "<climits>"},
+ {"bits/locale.h$", "<clocale>"},
+ {"bits/mathcalls.h$", "<math.h>"},
+ {"bits/mathdef.h$", "<cmath>"},
+ {"bits/nan.h$", "<cmath>"},
+ {"bits/posix1_lim.h$", "<climits>"},
+ {"bits/posix2_lim.h$", "<climits>"},
+ {"bits/setjmp.h$", "<csetjmp>"},
+ {"bits/sigaction.h$", "<csignal>"},
+ {"bits/sigcontext.h$", "<csignal>"},
+ {"bits/siginfo.h$", "<csignal>"},
+ {"bits/signum.h$", "<csignal>"},
+ {"bits/sigset.h$", "<csignal>"},
+ {"bits/sigstack.h$", "<csignal>"},
+ {"bits/stdio_lim.h$", "<cstdio>"},
+ {"bits/sys_errlist.h$", "<cstdio>"},
+ {"bits/time.h$", "<ctime>"},
+ {"bits/timex.h$", "<ctime>"},
+ {"bits/typesizes.h$", "<cstdio>"},
+ {"bits/wchar.h$", "<cwchar>"},
+ {"bits/wordsize.h$", "<csetjmp>"},
+ {"bits/xopen_lim.h$", "<climits>"},
+ {"include/xlocale.h$", "<cstring>"},
+ {"bits/atomic_word.h$", "<memory>"},
+ {"bits/basic_file.h$", "<fstream>"},
+ {"bits/c\\+\\+allocator.h$", "<string>"},
+ {"bits/c\\+\\+config.h$", "<iosfwd>"},
+ {"bits/c\\+\\+io.h$", "<ios>"},
+ {"bits/c\\+\\+locale.h$", "<locale>"},
+ {"bits/cpu_defines.h$", "<iosfwd>"},
+ {"bits/ctype_base.h$", "<locale>"},
+ {"bits/cxxabi_tweaks.h$", "<cxxabi.h>"},
+ {"bits/error_constants.h$", "<system_error>"},
+ {"bits/gthr-default.h$", "<memory>"},
+ {"bits/gthr.h$", "<memory>"},
+ {"bits/opt_random.h$", "<random>"},
+ {"bits/os_defines.h$", "<iosfwd>"},
+ // GNU C headers
+ {"include/aio.h$", "<aio.h>"},
+ {"include/aliases.h$", "<aliases.h>"},
+ {"include/alloca.h$", "<alloca.h>"},
+ {"include/ar.h$", "<ar.h>"},
+ {"include/argp.h$", "<argp.h>"},
+ {"include/argz.h$", "<argz.h>"},
+ {"include/arpa/nameser.h$", "<resolv.h>"},
+ {"include/arpa/nameser_compat.h$", "<resolv.h>"},
+ {"include/byteswap.h$", "<byteswap.h>"},
+ {"include/cpio.h$", "<cpio.h>"},
+ {"include/crypt.h$", "<crypt.h>"},
+ {"include/dirent.h$", "<dirent.h>"},
+ {"include/dlfcn.h$", "<dlfcn.h>"},
+ {"include/elf.h$", "<elf.h>"},
+ {"include/endian.h$", "<endian.h>"},
+ {"include/envz.h$", "<envz.h>"},
+ {"include/err.h$", "<err.h>"},
+ {"include/error.h$", "<error.h>"},
+ {"include/execinfo.h$", "<execinfo.h>"},
+ {"include/fcntl.h$", "<fcntl.h>"},
+ {"include/features.h$", "<features.h>"},
+ {"include/fenv.h$", "<fenv.h>"},
+ {"include/fmtmsg.h$", "<fmtmsg.h>"},
+ {"include/fnmatch.h$", "<fnmatch.h>"},
+ {"include/fstab.h$", "<fstab.h>"},
+ {"include/fts.h$", "<fts.h>"},
+ {"include/ftw.h$", "<ftw.h>"},
+ {"include/gconv.h$", "<gconv.h>"},
+ {"include/getopt.h$", "<getopt.h>"},
+ {"include/glob.h$", "<glob.h>"},
+ {"include/grp.h$", "<grp.h>"},
+ {"include/gshadow.h$", "<gshadow.h>"},
+ {"include/iconv.h$", "<iconv.h>"},
+ {"include/ifaddrs.h$", "<ifaddrs.h>"},
+ {"include/kdb.h$", "<kdb.h>"},
+ {"include/langinfo.h$", "<langinfo.h>"},
+ {"include/libgen.h$", "<libgen.h>"},
+ {"include/libintl.h$", "<libintl.h>"},
+ {"include/link.h$", "<link.h>"},
+ {"include/malloc.h$", "<malloc.h>"},
+ {"include/mcheck.h$", "<mcheck.h>"},
+ {"include/memory.h$", "<memory.h>"},
+ {"include/mntent.h$", "<mntent.h>"},
+ {"include/monetary.h$", "<monetary.h>"},
+ {"include/mqueue.h$", "<mqueue.h>"},
+ {"include/netdb.h$", "<netdb.h>"},
+ {"include/netinet/in.h$", "<netinet/in.h>"},
+ {"include/nl_types.h$", "<nl_types.h>"},
+ {"include/nss.h$", "<nss.h>"},
+ {"include/obstack.h$", "<obstack.h>"},
+ {"include/panel.h$", "<panel.h>"},
+ {"include/paths.h$", "<paths.h>"},
+ {"include/printf.h$", "<printf.h>"},
+ {"include/profile.h$", "<profile.h>"},
+ {"include/pthread.h$", "<pthread.h>"},
+ {"include/pty.h$", "<pty.h>"},
+ {"include/pwd.h$", "<pwd.h>"},
+ {"include/re_comp.h$", "<re_comp.h>"},
+ {"include/regex.h$", "<regex.h>"},
+ {"include/regexp.h$", "<regexp.h>"},
+ {"include/resolv.h$", "<resolv.h>"},
+ {"include/rpc/netdb.h$", "<netdb.h>"},
+ {"include/sched.h$", "<sched.h>"},
+ {"include/search.h$", "<search.h>"},
+ {"include/semaphore.h$", "<semaphore.h>"},
+ {"include/sgtty.h$", "<sgtty.h>"},
+ {"include/shadow.h$", "<shadow.h>"},
+ {"include/spawn.h$", "<spawn.h>"},
+ {"include/stab.h$", "<stab.h>"},
+ {"include/stdc-predef.h$", "<stdc-predef.h>"},
+ {"include/stdio_ext.h$", "<stdio_ext.h>"},
+ {"include/strings.h$", "<strings.h>"},
+ {"include/stropts.h$", "<stropts.h>"},
+ {"include/sudo_plugin.h$", "<sudo_plugin.h>"},
+ {"include/sysexits.h$", "<sysexits.h>"},
+ {"include/tar.h$", "<tar.h>"},
+ {"include/tcpd.h$", "<tcpd.h>"},
+ {"include/term.h$", "<term.h>"},
+ {"include/term_entry.h$", "<term_entry.h>"},
+ {"include/termcap.h$", "<termcap.h>"},
+ {"include/termios.h$", "<termios.h>"},
+ {"include/thread_db.h$", "<thread_db.h>"},
+ {"include/tic.h$", "<tic.h>"},
+ {"include/ttyent.h$", "<ttyent.h>"},
+ {"include/uchar.h$", "<uchar.h>"},
+ {"include/ucontext.h$", "<ucontext.h>"},
+ {"include/ulimit.h$", "<ulimit.h>"},
+ {"include/unctrl.h$", "<unctrl.h>"},
+ {"include/unistd.h$", "<unistd.h>"},
+ {"include/utime.h$", "<utime.h>"},
+ {"include/utmp.h$", "<utmp.h>"},
+ {"include/utmpx.h$", "<utmpx.h>"},
+ {"include/values.h$", "<values.h>"},
+ {"include/wordexp.h$", "<wordexp.h>"},
+ {"fpu_control.h$", "<fpu_control.h>"},
+ {"ieee754.h$", "<ieee754.h>"},
+ {"include/xlocale.h$", "<xlocale.h>"},
+ {"gnu/lib-names.h$", "<gnu/lib-names.h>"},
+ {"gnu/libc-version.h$", "<gnu/libc-version.h>"},
+ {"gnu/option-groups.h$", "<gnu/option-groups.h>"},
+ {"gnu/stubs-32.h$", "<gnu/stubs-32.h>"},
+ {"gnu/stubs-64.h$", "<gnu/stubs-64.h>"},
+ {"gnu/stubs-x32.h$", "<gnu/stubs-x32.h>"},
+ {"include/rpc/auth_des.h$", "<rpc/auth_des.h>"},
+ {"include/rpc/rpc_msg.h$", "<rpc/rpc_msg.h>"},
+ {"include/rpc/pmap_clnt.h$", "<rpc/pmap_clnt.h>"},
+ {"include/rpc/rpc.h$", "<rpc/rpc.h>"},
+ {"include/rpc/types.h$", "<rpc/types.h>"},
+ {"include/rpc/auth_unix.h$", "<rpc/auth_unix.h>"},
+ {"include/rpc/key_prot.h$", "<rpc/key_prot.h>"},
+ {"include/rpc/pmap_prot.h$", "<rpc/pmap_prot.h>"},
+ {"include/rpc/auth.h$", "<rpc/auth.h>"},
+ {"include/rpc/svc_auth.h$", "<rpc/svc_auth.h>"},
+ {"include/rpc/xdr.h$", "<rpc/xdr.h>"},
+ {"include/rpc/pmap_rmt.h$", "<rpc/pmap_rmt.h>"},
+ {"include/rpc/des_crypt.h$", "<rpc/des_crypt.h>"},
+ {"include/rpc/svc.h$", "<rpc/svc.h>"},
+ {"include/rpc/rpc_des.h$", "<rpc/rpc_des.h>"},
+ {"include/rpc/clnt.h$", "<rpc/clnt.h>"},
+ {"include/scsi/scsi.h$", "<scsi/scsi.h>"},
+ {"include/scsi/sg.h$", "<scsi/sg.h>"},
+ {"include/scsi/scsi_ioctl.h$", "<scsi/scsi_ioctl>"},
+ {"include/netrose/rose.h$", "<netrose/rose.h>"},
+ {"include/nfs/nfs.h$", "<nfs/nfs.h>"},
+ {"include/netatalk/at.h$", "<netatalk/at.h>"},
+ {"include/netinet/ether.h$", "<netinet/ether.h>"},
+ {"include/netinet/icmp6.h$", "<netinet/icmp6.h>"},
+ {"include/netinet/if_ether.h$", "<netinet/if_ether.h>"},
+ {"include/netinet/if_fddi.h$", "<netinet/if_fddi.h>"},
+ {"include/netinet/if_tr.h$", "<netinet/if_tr.h>"},
+ {"include/netinet/igmp.h$", "<netinet/igmp.h>"},
+ {"include/netinet/in.h$", "<netinet/in.h>"},
+ {"include/netinet/in_systm.h$", "<netinet/in_systm.h>"},
+ {"include/netinet/ip.h$", "<netinet/ip.h>"},
+ {"include/netinet/ip6.h$", "<netinet/ip6.h>"},
+ {"include/netinet/ip_icmp.h$", "<netinet/ip_icmp.h>"},
+ {"include/netinet/tcp.h$", "<netinet/tcp.h>"},
+ {"include/netinet/udp.h$", "<netinet/udp.h>"},
+ {"include/netrom/netrom.h$", "<netrom/netrom.h>"},
+ {"include/protocols/routed.h$", "<protocols/routed.h>"},
+ {"include/protocols/rwhod.h$", "<protocols/rwhod.h>"},
+ {"include/protocols/talkd.h$", "<protocols/talkd.h>"},
+ {"include/protocols/timed.h$", "<protocols/timed.h>"},
+ {"include/rpcsvc/klm_prot.x$", "<rpcsvc/klm_prot.x>"},
+ {"include/rpcsvc/rstat.h$", "<rpcsvc/rstat.h>"},
+ {"include/rpcsvc/spray.x$", "<rpcsvc/spray.x>"},
+ {"include/rpcsvc/nlm_prot.x$", "<rpcsvc/nlm_prot.x>"},
+ {"include/rpcsvc/nis_callback.x$", "<rpcsvc/nis_callback.x>"},
+ {"include/rpcsvc/yp.h$", "<rpcsvc/yp.h>"},
+ {"include/rpcsvc/yp.x$", "<rpcsvc/yp.x>"},
+ {"include/rpcsvc/nfs_prot.h$", "<rpcsvc/nfs_prot.h>"},
+ {"include/rpcsvc/rex.h$", "<rpcsvc/rex.h>"},
+ {"include/rpcsvc/yppasswd.h$", "<rpcsvc/yppasswd.h>"},
+ {"include/rpcsvc/rex.x$", "<rpcsvc/rex.x>"},
+ {"include/rpcsvc/nis_tags.h$", "<rpcsvc/nis_tags.h>"},
+ {"include/rpcsvc/nis_callback.h$", "<rpcsvc/nis_callback.h>"},
+ {"include/rpcsvc/nfs_prot.x$", "<rpcsvc/nfs_prot.x>"},
+ {"include/rpcsvc/bootparam_prot.x$", "<rpcsvc/bootparam_prot.x>"},
+ {"include/rpcsvc/rusers.x$", "<rpcsvc/rusers.x>"},
+ {"include/rpcsvc/rquota.x$", "<rpcsvc/rquota.x>"},
+ {"include/rpcsvc/nis.h$", "<rpcsvc/nis.h>"},
+ {"include/rpcsvc/nislib.h$", "<rpcsvc/nislib.h>"},
+ {"include/rpcsvc/ypupd.h$", "<rpcsvc/ypupd.h>"},
+ {"include/rpcsvc/bootparam.h$", "<rpcsvc/bootparam.h>"},
+ {"include/rpcsvc/spray.h$", "<rpcsvc/spray.h>"},
+ {"include/rpcsvc/key_prot.h$", "<rpcsvc/key_prot.h>"},
+ {"include/rpcsvc/klm_prot.h$", "<rpcsvc/klm_prot.h>"},
+ {"include/rpcsvc/sm_inter.h$", "<rpcsvc/sm_inter.h>"},
+ {"include/rpcsvc/nlm_prot.h$", "<rpcsvc/nlm_prot.h>"},
+ {"include/rpcsvc/yp_prot.h$", "<rpcsvc/yp_prot.h>"},
+ {"include/rpcsvc/ypclnt.h$", "<rpcsvc/ypclnt.h>"},
+ {"include/rpcsvc/rstat.x$", "<rpcsvc/rstat.x>"},
+ {"include/rpcsvc/rusers.h$", "<rpcsvc/rusers.h>"},
+ {"include/rpcsvc/key_prot.x$", "<rpcsvc/key_prot.x>"},
+ {"include/rpcsvc/sm_inter.x$", "<rpcsvc/sm_inter.x>"},
+ {"include/rpcsvc/rquota.h$", "<rpcsvc/rquota.h>"},
+ {"include/rpcsvc/nis.x$", "<rpcsvc/nis.x>"},
+ {"include/rpcsvc/bootparam_prot.h$", "<rpcsvc/bootparam_prot.h>"},
+ {"include/rpcsvc/mount.h$", "<rpcsvc/mount.h>"},
+ {"include/rpcsvc/mount.x$", "<rpcsvc/mount.x>"},
+ {"include/rpcsvc/nis_object.x$", "<rpcsvc/nis_object.x>"},
+ {"include/rpcsvc/yppasswd.x$", "<rpcsvc/yppasswd.x>"},
+ {"sys/acct.h$", "<sys/acct.h>"},
+ {"sys/auxv.h$", "<sys/auxv.h>"},
+ {"sys/cdefs.h$", "<sys/cdefs.h>"},
+ {"sys/debugreg.h$", "<sys/debugreg.h>"},
+ {"sys/dir.h$", "<sys/dir.h>"},
+ {"sys/elf.h$", "<sys/elf.h>"},
+ {"sys/epoll.h$", "<sys/epoll.h>"},
+ {"sys/eventfd.h$", "<sys/eventfd.h>"},
+ {"sys/fanotify.h$", "<sys/fanotify.h>"},
+ {"sys/file.h$", "<sys/file.h>"},
+ {"sys/fsuid.h$", "<sys/fsuid.h>"},
+ {"sys/gmon.h$", "<sys/gmon.h>"},
+ {"sys/gmon_out.h$", "<sys/gmon_out.h>"},
+ {"sys/inotify.h$", "<sys/inotify.h>"},
+ {"sys/io.h$", "<sys/io.h>"},
+ {"sys/ioctl.h$", "<sys/ioctl.h>"},
+ {"sys/ipc.h$", "<sys/ipc.h>"},
+ {"sys/kd.h$", "<sys/kd.h>"},
+ {"sys/kdaemon.h$", "<sys/kdaemon.h>"},
+ {"sys/klog.h$", "<sys/klog.h>"},
+ {"sys/mman.h$", "<sys/mman.h>"},
+ {"sys/mount.h$", "<sys/mount.h>"},
+ {"sys/msg.h$", "<sys/msg.h>"},
+ {"sys/mtio.h$", "<sys/mtio.h>"},
+ {"sys/param.h$", "<sys/param.h>"},
+ {"sys/pci.h$", "<sys/pci.h>"},
+ {"sys/perm.h$", "<sys/perm.h>"},
+ {"sys/personality.h$", "<sys/personality.h>"},
+ {"sys/poll.h$", "<sys/poll.h>"},
+ {"sys/prctl.h$", "<sys/prctl.h>"},
+ {"sys/procfs.h$", "<sys/procfs.h>"},
+ {"sys/profil.h$", "<sys/profil.h>"},
+ {"sys/ptrace.h$", "<sys/ptrace.h>"},
+ {"sys/queue.h$", "<sys/queue.h>"},
+ {"sys/quota.h$", "<sys/quota.h>"},
+ {"sys/raw.h$", "<sys/raw.h>"},
+ {"sys/reboot.h$", "<sys/reboot.h>"},
+ {"sys/reg.h$", "<sys/reg.h>"},
+ {"sys/resource.h$", "<sys/resource.h>"},
+ {"sys/select.h$", "<sys/select.h>"},
+ {"sys/sem.h$", "<sys/sem.h>"},
+ {"sys/sendfile.h$", "<sys/sendfile.h>"},
+ {"sys/shm.h$", "<sys/shm.h>"},
+ {"sys/signalfd.h$", "<sys/signalfd.h>"},
+ {"sys/socket.h$", "<sys/socket.h>"},
+ {"sys/stat.h$", "<sys/stat.h>"},
+ {"sys/statfs.h$", "<sys/statfs.h>"},
+ {"sys/statvfs.h$", "<sys/statvfs.h>"},
+ {"sys/swap.h$", "<sys/swap.h>"},
+ {"sys/syscall.h$", "<sys/syscall.h>"},
+ {"sys/sysctl.h$", "<sys/sysctl.h>"},
+ {"sys/sysinfo.h$", "<sys/sysinfo.h>"},
+ {"sys/syslog.h$", "<sys/syslog.h>"},
+ {"sys/sysmacros.h$", "<sys/sysmacros.h>"},
+ {"sys/termios.h$", "<sys/termios.h>"},
+ {"sys/time.h$", "<sys/select.h>"},
+ {"sys/timeb.h$", "<sys/timeb.h>"},
+ {"sys/timerfd.h$", "<sys/timerfd.h>"},
+ {"sys/times.h$", "<sys/times.h>"},
+ {"sys/timex.h$", "<sys/timex.h>"},
+ {"sys/ttychars.h$", "<sys/ttychars.h>"},
+ {"sys/ttydefaults.h$", "<sys/ttydefaults.h>"},
+ {"sys/types.h$", "<sys/types.h>"},
+ {"sys/ucontext.h$", "<sys/ucontext.h>"},
+ {"sys/uio.h$", "<sys/uio.h>"},
+ {"sys/un.h$", "<sys/un.h>"},
+ {"sys/user.h$", "<sys/user.h>"},
+ {"sys/ustat.h$", "<sys/ustat.h>"},
+ {"sys/utsname.h$", "<sys/utsname.h>"},
+ {"sys/vlimit.h$", "<sys/vlimit.h>"},
+ {"sys/vm86.h$", "<sys/vm86.h>"},
+ {"sys/vtimes.h$", "<sys/vtimes.h>"},
+ {"sys/wait.h$", "<sys/wait.h>"},
+ {"sys/xattr.h$", "<sys/xattr.h>"},
+ {"bits/epoll.h$", "<sys/epoll.h>"},
+ {"bits/eventfd.h$", "<sys/eventfd.h>"},
+ {"bits/inotify.h$", "<sys/inotify.h>"},
+ {"bits/ipc.h$", "<sys/ipc.h>"},
+ {"bits/ipctypes.h$", "<sys/ipc.h>"},
+ {"bits/mman-linux.h$", "<sys/mman.h>"},
+ {"bits/mman.h$", "<sys/mman.h>"},
+ {"bits/msq.h$", "<sys/msg.h>"},
+ {"bits/resource.h$", "<sys/resource.h>"},
+ {"bits/sem.h$", "<sys/sem.h>"},
+ {"bits/shm.h$", "<sys/shm.h>"},
+ {"bits/signalfd.h$", "<sys/signalfd.h>"},
+ {"bits/statfs.h$", "<sys/statfs.h>"},
+ {"bits/statvfs.h$", "<sys/statvfs.h>"},
+ {"bits/timerfd.h$", "<sys/timerfd.h>"},
+ {"bits/utsname.h$", "<sys/utsname.h>"},
+ {"bits/auxv.h$", "<sys/auxv.h>"},
+ {"bits/byteswap-16.h$", "<byteswap.h>"},
+ {"bits/byteswap.h$", "<byteswap.h>"},
+ {"bits/confname.h$", "<unistd.h>"},
+ {"bits/dirent.h$", "<dirent.h>"},
+ {"bits/dlfcn.h$", "<dlfcn.h>"},
+ {"bits/elfclass.h$", "<link.h>"},
+ {"bits/endian.h$", "<endian.h>"},
+ {"bits/environments.h$", "<unistd.h>"},
+ {"bits/fcntl-linux.h$", "<fcntl.h>"},
+ {"bits/fcntl.h$", "<fcntl.h>"},
+ {"bits/in.h$", "<netinet/in.h>"},
+ {"bits/ioctl-types.h$", "<sys/ioctl.h>"},
+ {"bits/ioctls.h$", "<sys/ioctl.h>"},
+ {"bits/link.h$", "<link.h>"},
+ {"bits/mqueue.h$", "<mqueue.h>"},
+ {"bits/netdb.h$", "<netdb.h>"},
+ {"bits/param.h$", "<sys/param.h>"},
+ {"bits/poll.h$", "<sys/poll.h>"},
+ {"bits/posix_opt.h$", "<bits/posix_opt.h>"},
+ {"bits/pthreadtypes.h$", "<pthread.h>"},
+ {"bits/sched.h$", "<sched.h>"},
+ {"bits/select.h$", "<sys/select.h>"},
+ {"bits/semaphore.h$", "<semaphore.h>"},
+ {"bits/sigthread.h$", "<pthread.h>"},
+ {"bits/sockaddr.h$", "<sys/socket.h>"},
+ {"bits/socket.h$", "<sys/socket.h>"},
+ {"bits/socket_type.h$", "<sys/socket.h>"},
+ {"bits/stab.def$", "<stab.h>"},
+ {"bits/stat.h$", "<sys/stat.h>"},
+ {"bits/stropts.h$", "<stropts.h>"},
+ {"bits/syscall.h$", "<sys/syscall.h>"},
+ {"bits/syslog-path.h$", "<sys/syslog.h>"},
+ {"bits/termios.h$", "<termios.h>"},
+ {"bits/types.h$", "<sys/types.h>"},
+ {"bits/typesizes.h$", "<sys/types.h>"},
+ {"bits/uio.h$", "<sys/uio.h>"},
+ {"bits/ustat.h$", "<sys/ustat.h>"},
+ {"bits/utmp.h$", "<utmp.h>"},
+ {"bits/utmpx.h$", "<utmpx.h>"},
+ {"bits/waitflags.h$", "<sys/wait.h>"},
+ {"bits/waitstatus.h$", "<sys/wait.h>"},
+ {"bits/xtitypes.h$", "<stropts.h>"},
+ };
+ return &STLPostfixHeaderMap;
+}
+
+} // namespace find_all_symbols
+} // namespace clang
diff --git a/clang-include-fixer/find-all-symbols/STLPostfixHeaderMap.h b/clang-include-fixer/find-all-symbols/STLPostfixHeaderMap.h
new file mode 100644
index 00000000..49bc5f30
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/STLPostfixHeaderMap.h
@@ -0,0 +1,22 @@
+//===-- STLPostfixHeaderMap.h - hardcoded header map for STL ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_TOOL_STL_POSTFIX_HEADER_MAP_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_TOOL_STL_POSTFIX_HEADER_MAP_H
+
+#include "HeaderMapCollector.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+const HeaderMapCollector::RegexHeaderMap *getSTLPostfixHeaderMap();
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_TOOL_STL_POSTFIX_HEADER_MAP_H
diff --git a/clang-include-fixer/find-all-symbols/SymbolInfo.cpp b/clang-include-fixer/find-all-symbols/SymbolInfo.cpp
new file mode 100644
index 00000000..e5b4dba4
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/SymbolInfo.cpp
@@ -0,0 +1,136 @@
+//===-- SymbolInfo.cpp - Symbol Info ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolInfo.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+
+using llvm::yaml::MappingTraits;
+using llvm::yaml::IO;
+using llvm::yaml::Input;
+using ContextType = clang::find_all_symbols::SymbolInfo::ContextType;
+using clang::find_all_symbols::SymbolInfo;
+using clang::find_all_symbols::SymbolAndSignals;
+using SymbolKind = clang::find_all_symbols::SymbolInfo::SymbolKind;
+
+LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(SymbolAndSignals)
+LLVM_YAML_IS_SEQUENCE_VECTOR(SymbolInfo::Context)
+
+namespace llvm {
+namespace yaml {
+template <> struct MappingTraits<SymbolAndSignals> {
+ static void mapping(IO &io, SymbolAndSignals &Symbol) {
+ io.mapRequired("Name", Symbol.Symbol.Name);
+ io.mapRequired("Contexts", Symbol.Symbol.Contexts);
+ io.mapRequired("FilePath", Symbol.Symbol.FilePath);
+ io.mapRequired("Type", Symbol.Symbol.Type);
+ io.mapRequired("Seen", Symbol.Signals.Seen);
+ io.mapRequired("Used", Symbol.Signals.Used);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<ContextType> {
+ static void enumeration(IO &io, ContextType &value) {
+ io.enumCase(value, "Record", ContextType::Record);
+ io.enumCase(value, "Namespace", ContextType::Namespace);
+ io.enumCase(value, "EnumDecl", ContextType::EnumDecl);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<SymbolKind> {
+ static void enumeration(IO &io, SymbolKind &value) {
+ io.enumCase(value, "Variable", SymbolKind::Variable);
+ io.enumCase(value, "Function", SymbolKind::Function);
+ io.enumCase(value, "Class", SymbolKind::Class);
+ io.enumCase(value, "TypedefName", SymbolKind::TypedefName);
+ io.enumCase(value, "EnumDecl", SymbolKind::EnumDecl);
+ io.enumCase(value, "EnumConstantDecl", SymbolKind::EnumConstantDecl);
+ io.enumCase(value, "Macro", SymbolKind::Macro);
+ io.enumCase(value, "Unknown", SymbolKind::Unknown);
+ }
+};
+
+template <> struct MappingTraits<SymbolInfo::Context> {
+ static void mapping(IO &io, SymbolInfo::Context &Context) {
+ io.mapRequired("ContextType", Context.first);
+ io.mapRequired("ContextName", Context.second);
+ }
+};
+
+} // namespace yaml
+} // namespace llvm
+
+namespace clang {
+namespace find_all_symbols {
+
+SymbolInfo::SymbolInfo(llvm::StringRef Name, SymbolKind Type,
+ llvm::StringRef FilePath,
+ const std::vector<Context> &Contexts)
+ : Name(Name), Type(Type), FilePath(FilePath), Contexts(Contexts) {}
+
+bool SymbolInfo::operator==(const SymbolInfo &Symbol) const {
+ return std::tie(Name, Type, FilePath, Contexts) ==
+ std::tie(Symbol.Name, Symbol.Type, Symbol.FilePath, Symbol.Contexts);
+}
+
+bool SymbolInfo::operator<(const SymbolInfo &Symbol) const {
+ return std::tie(Name, Type, FilePath, Contexts) <
+ std::tie(Symbol.Name, Symbol.Type, Symbol.FilePath, Symbol.Contexts);
+}
+
+std::string SymbolInfo::getQualifiedName() const {
+ std::string QualifiedName = Name;
+ for (const auto &Context : Contexts) {
+ if (Context.first == ContextType::EnumDecl)
+ continue;
+ QualifiedName = Context.second + "::" + QualifiedName;
+ }
+ return QualifiedName;
+}
+
+SymbolInfo::Signals &SymbolInfo::Signals::operator+=(const Signals &RHS) {
+ Seen += RHS.Seen;
+ Used += RHS.Used;
+ return *this;
+}
+
+SymbolInfo::Signals SymbolInfo::Signals::operator+(const Signals &RHS) const {
+ Signals Result = *this;
+ Result += RHS;
+ return Result;
+}
+
+bool SymbolInfo::Signals::operator==(const Signals &RHS) const {
+ return std::tie(Seen, Used) == std::tie(RHS.Seen, RHS.Used);
+}
+
+bool SymbolAndSignals::operator==(const SymbolAndSignals& RHS) const {
+ return std::tie(Symbol, Signals) == std::tie(RHS.Symbol, RHS.Signals);
+}
+
+bool WriteSymbolInfosToStream(llvm::raw_ostream &OS,
+ const SymbolInfo::SignalMap &Symbols) {
+ llvm::yaml::Output yout(OS);
+ for (const auto &Symbol : Symbols) {
+ SymbolAndSignals S{Symbol.first, Symbol.second};
+ yout << S;
+ }
+ return true;
+}
+
+std::vector<SymbolAndSignals> ReadSymbolInfosFromYAML(llvm::StringRef Yaml) {
+ std::vector<SymbolAndSignals> Symbols;
+ llvm::yaml::Input yin(Yaml);
+ yin >> Symbols;
+ return Symbols;
+}
+
+} // namespace find_all_symbols
+} // namespace clang
diff --git a/clang-include-fixer/find-all-symbols/SymbolInfo.h b/clang-include-fixer/find-all-symbols/SymbolInfo.h
new file mode 100644
index 00000000..6def1c70
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/SymbolInfo.h
@@ -0,0 +1,142 @@
+//===-- SymbolInfo.h - Symbol Info ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FIND_ALL_SYMBOLS_SYMBOLINFO_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FIND_ALL_SYMBOLS_SYMBOLINFO_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+#include <set>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace find_all_symbols {
+/// \brief Describes a named symbol from a header.
+/// Symbols with the same qualified name and type (e.g. function overloads)
+/// that appear in the same header are represented by a single SymbolInfo.
+///
+/// TODO: keep track of instances, e.g. overload locations and signatures.
+class SymbolInfo {
+public:
+ /// \brief The SymbolInfo Type.
+ enum class SymbolKind {
+ Function,
+ Class,
+ Variable,
+ TypedefName,
+ EnumDecl,
+ EnumConstantDecl,
+ Macro,
+ Unknown,
+ };
+
+ /// \brief The Context Type.
+ enum class ContextType {
+ Namespace, // Symbols declared in a namespace.
+ Record, // Symbols declared in a class.
+ EnumDecl, // Enum constants declared in a enum declaration.
+ };
+
+ /// \brief A pair of <ContextType, ContextName>.
+ typedef std::pair<ContextType, std::string> Context;
+
+ // \brief Signals are signals gathered by observing how a symbol is used.
+ // These are used to rank results.
+ struct Signals {
+ Signals() {}
+ Signals(unsigned Seen, unsigned Used) : Seen(Seen), Used(Used) {}
+
+ // Number of times this symbol was visible to a TU.
+ unsigned Seen = 0;
+
+ // Number of times this symbol was referenced a TU's main file.
+ unsigned Used = 0;
+
+ Signals &operator+=(const Signals &RHS);
+ Signals operator+(const Signals &RHS) const;
+ bool operator==(const Signals &RHS) const;
+ };
+
+ using SignalMap = std::map<SymbolInfo, Signals>;
+
+ // The default constructor is required by YAML traits in
+ // LLVM_YAML_IS_DOCUMENT_LIST_VECTOR.
+ SymbolInfo() : Type(SymbolKind::Unknown) {}
+
+ SymbolInfo(llvm::StringRef Name, SymbolKind Type, llvm::StringRef FilePath,
+ const std::vector<Context> &Contexts);
+
+ void SetFilePath(llvm::StringRef Path) { FilePath = Path; }
+
+ /// \brief Get symbol name.
+ llvm::StringRef getName() const { return Name; }
+
+ /// \brief Get the fully-qualified symbol name.
+ std::string getQualifiedName() const;
+
+ /// \brief Get symbol type.
+ SymbolKind getSymbolKind() const { return Type; }
+
+ /// \brief Get a relative file path where symbol comes from.
+ llvm::StringRef getFilePath() const { return FilePath; }
+
+ /// \brief Get symbol contexts.
+ const std::vector<SymbolInfo::Context> &getContexts() const {
+ return Contexts;
+ }
+
+ bool operator<(const SymbolInfo &Symbol) const;
+
+ bool operator==(const SymbolInfo &Symbol) const;
+
+private:
+ friend struct llvm::yaml::MappingTraits<struct SymbolAndSignals>;
+
+ /// \brief Identifier name.
+ std::string Name;
+
+ /// \brief Symbol type.
+ SymbolKind Type;
+
+ /// \brief The file path where the symbol comes from. It's a relative file
+ /// path based on the build directory.
+ std::string FilePath;
+
+ /// \brief Contains information about symbol contexts. Context information is
+ /// stored from the inner-most level to outer-most level.
+ ///
+ /// For example, if a symbol 'x' is declared as:
+ /// namespace na { namespace nb { class A { int x; } } }
+ /// The contexts would be { {RECORD, "A"}, {NAMESPACE, "nb"}, {NAMESPACE,
+ /// "na"} }.
+ /// The name of an anonymous namespace is "".
+ ///
+ /// If the symbol is declared in `TranslationUnitDecl`, it has no context.
+ std::vector<Context> Contexts;
+};
+
+struct SymbolAndSignals {
+ SymbolInfo Symbol;
+ SymbolInfo::Signals Signals;
+ bool operator==(const SymbolAndSignals& RHS) const;
+};
+
+/// \brief Write SymbolInfos to a stream (YAML format).
+bool WriteSymbolInfosToStream(llvm::raw_ostream &OS,
+ const SymbolInfo::SignalMap &Symbols);
+
+/// \brief Read SymbolInfos from a YAML document.
+std::vector<SymbolAndSignals> ReadSymbolInfosFromYAML(llvm::StringRef Yaml);
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FIND_ALL_SYMBOLS_SYMBOLINFO_H
diff --git a/clang-include-fixer/find-all-symbols/SymbolReporter.h b/clang-include-fixer/find-all-symbols/SymbolReporter.h
new file mode 100644
index 00000000..25e86219
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/SymbolReporter.h
@@ -0,0 +1,29 @@
+//===--- SymbolReporter.h - Symbol Reporter ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_REPORTER_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_REPORTER_H
+
+#include "SymbolInfo.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+/// \brief An interface for classes that collect symbols.
+class SymbolReporter {
+public:
+ virtual ~SymbolReporter() = default;
+
+ virtual void reportSymbols(llvm::StringRef FileName,
+ const SymbolInfo::SignalMap &Symbols) = 0;
+};
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_REPORTER_H
diff --git a/clang-include-fixer/find-all-symbols/tool/CMakeLists.txt b/clang-include-fixer/find-all-symbols/tool/CMakeLists.txt
new file mode 100644
index 00000000..64278ad1
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/tool/CMakeLists.txt
@@ -0,0 +1,24 @@
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+add_clang_executable(find-all-symbols
+ FindAllSymbolsMain.cpp
+ )
+
+target_link_libraries(find-all-symbols
+ PRIVATE
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangFrontend
+ clangLex
+ clangSerialization
+ clangTooling
+ findAllSymbols
+ )
+
+install(TARGETS find-all-symbols
+ RUNTIME DESTINATION bin)
+
+install(PROGRAMS run-find-all-symbols.py
+ DESTINATION share/clang
+ COMPONENT find-all-symbols)
diff --git a/clang-include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp b/clang-include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp
new file mode 100644
index 00000000..dbbe0738
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp
@@ -0,0 +1,151 @@
+//===-- FindAllSymbolsMain.cpp - find all symbols tool ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "FindAllSymbolsAction.h"
+#include "STLPostfixHeaderMap.h"
+#include "SymbolInfo.h"
+#include "SymbolReporter.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/raw_ostream.h"
+#include <map>
+#include <mutex>
+#include <set>
+#include <string>
+#include <system_error>
+#include <vector>
+
+using namespace clang::tooling;
+using namespace llvm;
+using SymbolInfo = clang::find_all_symbols::SymbolInfo;
+
+// Apply a custom category to all command-line options so that they are the
+// only ones displayed.
+static cl::OptionCategory FindAllSymbolsCategory("find_all_symbols options");
+
+// CommonOptionsParser declares HelpMessage with a description of the common
+// command-line options related to the compilation database and input files.
+// It's nice to have this help message in all tools.
+static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
+
+// A help message for this specific tool can be added afterwards.
+static cl::extrahelp MoreHelp("\nMore help text...");
+
+static cl::opt<std::string> OutputDir("output-dir", cl::desc(R"(
+The output directory for saving the results.)"),
+ cl::init("."),
+ cl::cat(FindAllSymbolsCategory));
+
+static cl::opt<std::string> MergeDir("merge-dir", cl::desc(R"(
+The directory for merging symbols.)"),
+ cl::init(""),
+ cl::cat(FindAllSymbolsCategory));
+namespace clang {
+namespace find_all_symbols {
+
+class YamlReporter : public SymbolReporter {
+public:
+ void reportSymbols(StringRef FileName,
+ const SymbolInfo::SignalMap &Symbols) override {
+ int FD;
+ SmallString<128> ResultPath;
+ llvm::sys::fs::createUniqueFile(
+ OutputDir + "/" + llvm::sys::path::filename(FileName) + "-%%%%%%.yaml",
+ FD, ResultPath);
+ llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
+ WriteSymbolInfosToStream(OS, Symbols);
+ }
+};
+
+bool Merge(llvm::StringRef MergeDir, llvm::StringRef OutputFile) {
+ std::error_code EC;
+ SymbolInfo::SignalMap Symbols;
+ std::mutex SymbolMutex;
+ auto AddSymbols = [&](ArrayRef<SymbolAndSignals> NewSymbols) {
+ // Synchronize set accesses.
+ std::unique_lock<std::mutex> LockGuard(SymbolMutex);
+ for (const auto &Symbol : NewSymbols) {
+ Symbols[Symbol.Symbol] += Symbol.Signals;
+ }
+ };
+
+ // Load all symbol files in MergeDir.
+ {
+ llvm::ThreadPool Pool;
+ for (llvm::sys::fs::directory_iterator Dir(MergeDir, EC), DirEnd;
+ Dir != DirEnd && !EC; Dir.increment(EC)) {
+ // Parse YAML files in parallel.
+ Pool.async(
+ [&AddSymbols](std::string Path) {
+ auto Buffer = llvm::MemoryBuffer::getFile(Path);
+ if (!Buffer) {
+ llvm::errs() << "Can't open " << Path << "\n";
+ return;
+ }
+ std::vector<SymbolAndSignals> Symbols =
+ ReadSymbolInfosFromYAML(Buffer.get()->getBuffer());
+ for (auto &Symbol : Symbols) {
+ // Only count one occurrence per file, to avoid spam.
+ Symbol.Signals.Seen = std::min(Symbol.Signals.Seen, 1u);
+ Symbol.Signals.Used = std::min(Symbol.Signals.Used, 1u);
+ }
+ // FIXME: Merge without creating such a heavy contention point.
+ AddSymbols(Symbols);
+ },
+ Dir->path());
+ }
+ }
+
+ llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_None);
+ if (EC) {
+ llvm::errs() << "Can't open '" << OutputFile << "': " << EC.message()
+ << '\n';
+ return false;
+ }
+ WriteSymbolInfosToStream(OS, Symbols);
+ return true;
+}
+
+} // namespace clang
+} // namespace find_all_symbols
+
+int main(int argc, const char **argv) {
+ CommonOptionsParser OptionsParser(argc, argv, FindAllSymbolsCategory);
+ ClangTool Tool(OptionsParser.getCompilations(),
+ OptionsParser.getSourcePathList());
+
+ std::vector<std::string> sources = OptionsParser.getSourcePathList();
+ if (sources.empty()) {
+ llvm::errs() << "Must specify at least one one source file.\n";
+ return 1;
+ }
+ if (!MergeDir.empty()) {
+ clang::find_all_symbols::Merge(MergeDir, sources[0]);
+ return 0;
+ }
+
+ clang::find_all_symbols::YamlReporter Reporter;
+
+ auto Factory =
+ llvm::make_unique<clang::find_all_symbols::FindAllSymbolsActionFactory>(
+ &Reporter, clang::find_all_symbols::getSTLPostfixHeaderMap());
+ return Tool.run(Factory.get());
+}
diff --git a/clang-include-fixer/find-all-symbols/tool/run-find-all-symbols.py b/clang-include-fixer/find-all-symbols/tool/run-find-all-symbols.py
new file mode 100755
index 00000000..5e9dde72
--- /dev/null
+++ b/clang-include-fixer/find-all-symbols/tool/run-find-all-symbols.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+#
+#=- run-find-all-symbols.py - Parallel find-all-symbols runner -*- python -*-=#
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+#===------------------------------------------------------------------------===#
+
+"""
+Parallel find-all-symbols runner
+================================
+
+Runs find-all-symbols over all files in a compilation database.
+
+Example invocations.
+- Run find-all-symbols on all files in the current working directory.
+ run-find-all-symbols.py <source-file>
+
+Compilation database setup:
+http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+"""
+
+import argparse
+import json
+import multiprocessing
+import os
+import Queue
+import shutil
+import subprocess
+import sys
+import tempfile
+import threading
+
+
+def find_compilation_database(path):
+ """Adjusts the directory until a compilation database is found."""
+ result = './'
+ while not os.path.isfile(os.path.join(result, path)):
+ if os.path.realpath(result) == '/':
+ print 'Error: could not find compilation database.'
+ sys.exit(1)
+ result += '../'
+ return os.path.realpath(result)
+
+
+def MergeSymbols(directory, args):
+ """Merge all symbol files (yaml) in a given directaory into a single file."""
+ invocation = [args.binary, '-merge-dir='+directory, args.saving_path]
+ subprocess.call(invocation)
+ print 'Merge is finished. Saving results in ' + args.saving_path
+
+
+def run_find_all_symbols(args, tmpdir, build_path, queue):
+ """Takes filenames out of queue and runs find-all-symbols on them."""
+ while True:
+ name = queue.get()
+ invocation = [args.binary, name, '-output-dir='+tmpdir, '-p='+build_path]
+ sys.stdout.write(' '.join(invocation) + '\n')
+ subprocess.call(invocation)
+ queue.task_done()
+
+
+def main():
+ parser = argparse.ArgumentParser(description='Runs find-all-symbols over all'
+ 'files in a compilation database.')
+ parser.add_argument('-binary', metavar='PATH',
+ default='./bin/find-all-symbols',
+ help='path to find-all-symbols binary')
+ parser.add_argument('-j', type=int, default=0,
+ help='number of instances to be run in parallel.')
+ parser.add_argument('-p', dest='build_path',
+ help='path used to read a compilation database.')
+ parser.add_argument('-saving-path', default='./find_all_symbols_db.yaml',
+ help='result saving path')
+ args = parser.parse_args()
+
+ db_path = 'compile_commands.json'
+
+ if args.build_path is not None:
+ build_path = args.build_path
+ else:
+ build_path = find_compilation_database(db_path)
+
+ tmpdir = tempfile.mkdtemp()
+
+ # Load the database and extract all files.
+ database = json.load(open(os.path.join(build_path, db_path)))
+ files = [entry['file'] for entry in database]
+
+ max_task = args.j
+ if max_task == 0:
+ max_task = multiprocessing.cpu_count()
+
+ try:
+ # Spin up a bunch of tidy-launching threads.
+ queue = Queue.Queue(max_task)
+ for _ in range(max_task):
+ t = threading.Thread(target=run_find_all_symbols,
+ args=(args, tmpdir, build_path, queue))
+ t.daemon = True
+ t.start()
+
+ # Fill the queue with files.
+ for name in files:
+ queue.put(name)
+
+ # Wait for all threads to be done.
+ queue.join()
+
+ MergeSymbols(tmpdir, args)
+
+
+ except KeyboardInterrupt:
+ # This is a sad hack. Unfortunately subprocess goes
+ # bonkers with ctrl-c and we start forking merrily.
+ print '\nCtrl-C detected, goodbye.'
+ os.kill(0, 9)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/clang-include-fixer/plugin/CMakeLists.txt b/clang-include-fixer/plugin/CMakeLists.txt
new file mode 100644
index 00000000..df792ea1
--- /dev/null
+++ b/clang-include-fixer/plugin/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_clang_library(clangIncludeFixerPlugin
+ IncludeFixerPlugin.cpp
+
+ LINK_LIBS
+ clangAST
+ clangBasic
+ clangFrontend
+ clangIncludeFixer
+ clangParse
+ clangSema
+ clangTooling
+ ${LLVM_PTHREAD_LIB}
+ )
diff --git a/clang-include-fixer/plugin/IncludeFixerPlugin.cpp b/clang-include-fixer/plugin/IncludeFixerPlugin.cpp
new file mode 100644
index 00000000..bc9c4973
--- /dev/null
+++ b/clang-include-fixer/plugin/IncludeFixerPlugin.cpp
@@ -0,0 +1,99 @@
+//===- IncludeFixerPlugin.cpp - clang-include-fixer as a clang plugin -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../IncludeFixer.h"
+#include "../YamlSymbolIndex.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendPluginRegistry.h"
+#include "clang/Parse/ParseAST.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace include_fixer {
+
+/// The core include fixer plugin action. This just provides the AST consumer
+/// and command line flag parsing for using include fixer as a clang plugin.
+class ClangIncludeFixerPluginAction : public PluginASTAction {
+ /// ASTConsumer to keep the symbol index alive. We don't really need an
+ /// ASTConsumer for this plugin (everything is funneled on the side through
+ /// Sema) but we have to keep the symbol index alive until sema is done.
+ struct ASTConsumerManagerWrapper : public ASTConsumer {
+ ASTConsumerManagerWrapper(std::shared_ptr<SymbolIndexManager> SIM)
+ : SymbolIndexMgr(std::move(SIM)) {}
+ std::shared_ptr<SymbolIndexManager> SymbolIndexMgr;
+ };
+
+public:
+ explicit ClangIncludeFixerPluginAction()
+ : SymbolIndexMgr(std::make_shared<SymbolIndexManager>()),
+ SemaSource(new IncludeFixerSemaSource(*SymbolIndexMgr,
+ /*MinimizeIncludePaths=*/true,
+ /*GenerateDiagnostics=*/true)) {}
+
+ std::unique_ptr<clang::ASTConsumer>
+ CreateASTConsumer(clang::CompilerInstance &CI, StringRef InFile) override {
+ CI.setExternalSemaSource(SemaSource);
+ SemaSource->setFilePath(InFile);
+ SemaSource->setCompilerInstance(&CI);
+ return llvm::make_unique<ASTConsumerManagerWrapper>(SymbolIndexMgr);
+ }
+
+ void ExecuteAction() override {} // Do nothing.
+
+ bool ParseArgs(const CompilerInstance &CI,
+ const std::vector<std::string> &Args) override {
+ StringRef DB = "yaml";
+ StringRef Input;
+
+ // Parse the extra command line args.
+ // FIXME: This is very limited at the moment.
+ for (StringRef Arg : Args) {
+ if (Arg.startswith("-db="))
+ DB = Arg.substr(strlen("-db="));
+ else if (Arg.startswith("-input="))
+ Input = Arg.substr(strlen("-input="));
+ }
+
+ std::string InputFile = CI.getFrontendOpts().Inputs[0].getFile();
+ auto CreateYamlIdx = [=]() -> std::unique_ptr<include_fixer::SymbolIndex> {
+ llvm::ErrorOr<std::unique_ptr<include_fixer::YamlSymbolIndex>> SymbolIdx(
+ nullptr);
+ if (DB == "yaml") {
+ if (!Input.empty()) {
+ SymbolIdx = include_fixer::YamlSymbolIndex::createFromFile(Input);
+ } else {
+ // If we don't have any input file, look in the directory of the first
+ // file and its parents.
+ SmallString<128> AbsolutePath(tooling::getAbsolutePath(InputFile));
+ StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
+ SymbolIdx = include_fixer::YamlSymbolIndex::createFromDirectory(
+ Directory, "find_all_symbols_db.yaml");
+ }
+ }
+ return std::move(*SymbolIdx);
+ };
+
+ SymbolIndexMgr->addSymbolIndex(std::move(CreateYamlIdx));
+ return true;
+ }
+
+private:
+ std::shared_ptr<SymbolIndexManager> SymbolIndexMgr;
+ IntrusiveRefCntPtr<IncludeFixerSemaSource> SemaSource;
+};
+} // namespace include_fixer
+} // namespace clang
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the include fixer plugin.
+volatile int ClangIncludeFixerPluginAnchorSource = 0;
+
+static clang::FrontendPluginRegistry::Add<
+ clang::include_fixer::ClangIncludeFixerPluginAction>
+ X("clang-include-fixer", "clang-include-fixer");
diff --git a/clang-include-fixer/tool/CMakeLists.txt b/clang-include-fixer/tool/CMakeLists.txt
new file mode 100644
index 00000000..207995aa
--- /dev/null
+++ b/clang-include-fixer/tool/CMakeLists.txt
@@ -0,0 +1,28 @@
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+add_clang_tool(clang-include-fixer
+ ClangIncludeFixer.cpp
+ )
+
+target_link_libraries(clang-include-fixer
+ PRIVATE
+ clangBasic
+ clangFormat
+ clangFrontend
+ clangIncludeFixer
+ clangRewrite
+ clangSerialization
+ clangTooling
+ clangToolingCore
+ findAllSymbols
+ )
+
+install(TARGETS clang-include-fixer
+ RUNTIME DESTINATION bin)
+
+install(PROGRAMS clang-include-fixer.el
+ DESTINATION share/clang
+ COMPONENT clang-include-fixer)
+install(PROGRAMS clang-include-fixer.py
+ DESTINATION share/clang
+ COMPONENT clang-include-fixer)
diff --git a/clang-include-fixer/tool/ClangIncludeFixer.cpp b/clang-include-fixer/tool/ClangIncludeFixer.cpp
new file mode 100644
index 00000000..15f6ed29
--- /dev/null
+++ b/clang-include-fixer/tool/ClangIncludeFixer.cpp
@@ -0,0 +1,473 @@
+//===-- ClangIncludeFixer.cpp - Standalone include fixer ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "FuzzySymbolIndex.h"
+#include "InMemorySymbolIndex.h"
+#include "IncludeFixer.h"
+#include "IncludeFixerContext.h"
+#include "SymbolIndexManager.h"
+#include "YamlSymbolIndex.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/YAMLTraits.h"
+
+using namespace clang;
+using namespace llvm;
+using clang::include_fixer::IncludeFixerContext;
+
+LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(IncludeFixerContext)
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(IncludeFixerContext::HeaderInfo)
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(IncludeFixerContext::QuerySymbolInfo)
+
+namespace llvm {
+namespace yaml {
+
+template <> struct MappingTraits<tooling::Range> {
+ struct NormalizedRange {
+ NormalizedRange(const IO &) : Offset(0), Length(0) {}
+
+ NormalizedRange(const IO &, const tooling::Range &R)
+ : Offset(R.getOffset()), Length(R.getLength()) {}
+
+ tooling::Range denormalize(const IO &) {
+ return tooling::Range(Offset, Length);
+ }
+
+ unsigned Offset;
+ unsigned Length;
+ };
+ static void mapping(IO &IO, tooling::Range &Info) {
+ MappingNormalization<NormalizedRange, tooling::Range> Keys(IO, Info);
+ IO.mapRequired("Offset", Keys->Offset);
+ IO.mapRequired("Length", Keys->Length);
+ }
+};
+
+template <> struct MappingTraits<IncludeFixerContext::HeaderInfo> {
+ static void mapping(IO &io, IncludeFixerContext::HeaderInfo &Info) {
+ io.mapRequired("Header", Info.Header);
+ io.mapRequired("QualifiedName", Info.QualifiedName);
+ }
+};
+
+template <> struct MappingTraits<IncludeFixerContext::QuerySymbolInfo> {
+ static void mapping(IO &io, IncludeFixerContext::QuerySymbolInfo &Info) {
+ io.mapRequired("RawIdentifier", Info.RawIdentifier);
+ io.mapRequired("Range", Info.Range);
+ }
+};
+
+template <> struct MappingTraits<IncludeFixerContext> {
+ static void mapping(IO &IO, IncludeFixerContext &Context) {
+ IO.mapRequired("QuerySymbolInfos", Context.QuerySymbolInfos);
+ IO.mapRequired("HeaderInfos", Context.HeaderInfos);
+ IO.mapRequired("FilePath", Context.FilePath);
+ }
+};
+} // namespace yaml
+} // namespace llvm
+
+namespace {
+cl::OptionCategory IncludeFixerCategory("Tool options");
+
+enum DatabaseFormatTy {
+ fixed, ///< Hard-coded mapping.
+ yaml, ///< Yaml database created by find-all-symbols.
+ fuzzyYaml, ///< Yaml database with fuzzy-matched identifiers.
+};
+
+cl::opt<DatabaseFormatTy> DatabaseFormat(
+ "db", cl::desc("Specify input format"),
+ cl::values(clEnumVal(fixed, "Hard-coded mapping"),
+ clEnumVal(yaml, "Yaml database created by find-all-symbols"),
+ clEnumVal(fuzzyYaml, "Yaml database, with fuzzy-matched names")),
+ cl::init(yaml), cl::cat(IncludeFixerCategory));
+
+cl::opt<std::string> Input("input",
+ cl::desc("String to initialize the database"),
+ cl::cat(IncludeFixerCategory));
+
+cl::opt<std::string>
+ QuerySymbol("query-symbol",
+ cl::desc("Query a given symbol (e.g. \"a::b::foo\") in\n"
+ "database directly without parsing the file."),
+ cl::cat(IncludeFixerCategory));
+
+cl::opt<bool>
+ MinimizeIncludePaths("minimize-paths",
+ cl::desc("Whether to minimize added include paths"),
+ cl::init(true), cl::cat(IncludeFixerCategory));
+
+cl::opt<bool> Quiet("q", cl::desc("Reduce terminal output"), cl::init(false),
+ cl::cat(IncludeFixerCategory));
+
+cl::opt<bool>
+ STDINMode("stdin",
+ cl::desc("Override source file's content (in the overlaying\n"
+ "virtual file system) with input from <stdin> and run\n"
+ "the tool on the new content with the compilation\n"
+ "options of the source file. This mode is currently\n"
+ "used for editor integration."),
+ cl::init(false), cl::cat(IncludeFixerCategory));
+
+cl::opt<bool> OutputHeaders(
+ "output-headers",
+ cl::desc("Print the symbol being queried and all its relevant headers in\n"
+ "JSON format to stdout:\n"
+ " {\n"
+ " \"FilePath\": \"/path/to/foo.cc\",\n"
+ " \"QuerySymbolInfos\": [\n"
+ " {\"RawIdentifier\": \"foo\",\n"
+ " \"Range\": {\"Offset\": 0, \"Length\": 3}}\n"
+ " ],\n"
+ " \"HeaderInfos\": [ {\"Header\": \"\\\"foo_a.h\\\"\",\n"
+ " \"QualifiedName\": \"a::foo\"} ]\n"
+ " }"),
+ cl::init(false), cl::cat(IncludeFixerCategory));
+
+cl::opt<std::string> InsertHeader(
+ "insert-header",
+ cl::desc("Insert a specific header. This should run with STDIN mode.\n"
+ "The result is written to stdout. It is currently used for\n"
+ "editor integration. Support YAML/JSON format:\n"
+ " -insert-header=\"{\n"
+ " FilePath: \"/path/to/foo.cc\",\n"
+ " QuerySymbolInfos: [\n"
+ " {RawIdentifier: foo,\n"
+ " Range: {Offset: 0, Length: 3}}\n"
+ " ],\n"
+ " HeaderInfos: [ {Headers: \"\\\"foo_a.h\\\"\",\n"
+ " QualifiedName: \"a::foo\"} ]}\""),
+ cl::init(""), cl::cat(IncludeFixerCategory));
+
+cl::opt<std::string>
+ Style("style",
+ cl::desc("Fallback style for reformatting after inserting new\n"
+ "headers if there is no clang-format config file found."),
+ cl::init("llvm"), cl::cat(IncludeFixerCategory));
+
+std::unique_ptr<include_fixer::SymbolIndexManager>
+createSymbolIndexManager(StringRef FilePath) {
+ using find_all_symbols::SymbolInfo;
+
+ auto SymbolIndexMgr = llvm::make_unique<include_fixer::SymbolIndexManager>();
+ switch (DatabaseFormat) {
+ case fixed: {
+ // Parse input and fill the database with it.
+ // <symbol>=<header><, header...>
+ // Multiple symbols can be given, separated by semicolons.
+ std::map<std::string, std::vector<std::string>> SymbolsMap;
+ SmallVector<StringRef, 4> SemicolonSplits;
+ StringRef(Input).split(SemicolonSplits, ";");
+ std::vector<find_all_symbols::SymbolAndSignals> Symbols;
+ for (StringRef Pair : SemicolonSplits) {
+ auto Split = Pair.split('=');
+ std::vector<std::string> Headers;
+ SmallVector<StringRef, 4> CommaSplits;
+ Split.second.split(CommaSplits, ",");
+ for (size_t I = 0, E = CommaSplits.size(); I != E; ++I)
+ Symbols.push_back(
+ {SymbolInfo(Split.first.trim(), SymbolInfo::SymbolKind::Unknown,
+ CommaSplits[I].trim(), {}),
+ // Use fake "seen" signal for tests, so first header wins.
+ SymbolInfo::Signals(/*Seen=*/static_cast<unsigned>(E - I),
+ /*Used=*/0)});
+ }
+ SymbolIndexMgr->addSymbolIndex([=]() {
+ return llvm::make_unique<include_fixer::InMemorySymbolIndex>(Symbols);
+ });
+ break;
+ }
+ case yaml: {
+ auto CreateYamlIdx = [=]() -> std::unique_ptr<include_fixer::SymbolIndex> {
+ llvm::ErrorOr<std::unique_ptr<include_fixer::YamlSymbolIndex>> DB(
+ nullptr);
+ if (!Input.empty()) {
+ DB = include_fixer::YamlSymbolIndex::createFromFile(Input);
+ } else {
+ // If we don't have any input file, look in the directory of the
+ // first
+ // file and its parents.
+ SmallString<128> AbsolutePath(tooling::getAbsolutePath(FilePath));
+ StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
+ DB = include_fixer::YamlSymbolIndex::createFromDirectory(
+ Directory, "find_all_symbols_db.yaml");
+ }
+
+ if (!DB) {
+ llvm::errs() << "Couldn't find YAML db: " << DB.getError().message()
+ << '\n';
+ return nullptr;
+ }
+ return std::move(*DB);
+ };
+
+ SymbolIndexMgr->addSymbolIndex(std::move(CreateYamlIdx));
+ break;
+ }
+ case fuzzyYaml: {
+ // This mode is not very useful, because we don't correct the identifier.
+ // It's main purpose is to expose FuzzySymbolIndex to tests.
+ SymbolIndexMgr->addSymbolIndex(
+ []() -> std::unique_ptr<include_fixer::SymbolIndex> {
+ auto DB = include_fixer::FuzzySymbolIndex::createFromYAML(Input);
+ if (!DB) {
+ llvm::errs() << "Couldn't load fuzzy YAML db: "
+ << llvm::toString(DB.takeError()) << '\n';
+ return nullptr;
+ }
+ return std::move(*DB);
+ });
+ break;
+ }
+ }
+ return SymbolIndexMgr;
+}
+
+void writeToJson(llvm::raw_ostream &OS, const IncludeFixerContext& Context) {
+ OS << "{\n"
+ << " \"FilePath\": \""
+ << llvm::yaml::escape(Context.getFilePath()) << "\",\n"
+ << " \"QuerySymbolInfos\": [\n";
+ for (const auto &Info : Context.getQuerySymbolInfos()) {
+ OS << " {\"RawIdentifier\": \"" << Info.RawIdentifier << "\",\n";
+ OS << " \"Range\":{";
+ OS << "\"Offset\":" << Info.Range.getOffset() << ",";
+ OS << "\"Length\":" << Info.Range.getLength() << "}}";
+ if (&Info != &Context.getQuerySymbolInfos().back())
+ OS << ",\n";
+ }
+ OS << "\n ],\n";
+ OS << " \"HeaderInfos\": [\n";
+ const auto &HeaderInfos = Context.getHeaderInfos();
+ for (const auto &Info : HeaderInfos) {
+ OS << " {\"Header\": \"" << llvm::yaml::escape(Info.Header) << "\",\n"
+ << " \"QualifiedName\": \"" << Info.QualifiedName << "\"}";
+ if (&Info != &HeaderInfos.back())
+ OS << ",\n";
+ }
+ OS << "\n";
+ OS << " ]\n";
+ OS << "}\n";
+}
+
+int includeFixerMain(int argc, const char **argv) {
+ tooling::CommonOptionsParser options(argc, argv, IncludeFixerCategory);
+ tooling::ClangTool tool(options.getCompilations(),
+ options.getSourcePathList());
+
+ llvm::StringRef SourceFilePath = options.getSourcePathList().front();
+ // In STDINMode, we override the file content with the <stdin> input.
+ // Since `tool.mapVirtualFile` takes `StringRef`, we define `Code` outside of
+ // the if-block so that `Code` is not released after the if-block.
+ std::unique_ptr<llvm::MemoryBuffer> Code;
+ if (STDINMode) {
+ assert(options.getSourcePathList().size() == 1 &&
+ "Expect exactly one file path in STDINMode.");
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> CodeOrErr =
+ MemoryBuffer::getSTDIN();
+ if (std::error_code EC = CodeOrErr.getError()) {
+ errs() << EC.message() << "\n";
+ return 1;
+ }
+ Code = std::move(CodeOrErr.get());
+ if (Code->getBufferSize() == 0)
+ return 0; // Skip empty files.
+
+ tool.mapVirtualFile(SourceFilePath, Code->getBuffer());
+ }
+
+ if (!InsertHeader.empty()) {
+ if (!STDINMode) {
+ errs() << "Should be running in STDIN mode\n";
+ return 1;
+ }
+
+ llvm::yaml::Input yin(InsertHeader);
+ IncludeFixerContext Context;
+ yin >> Context;
+
+ const auto &HeaderInfos = Context.getHeaderInfos();
+ assert(!HeaderInfos.empty());
+ // We only accept one unique header.
+ // Check all elements in HeaderInfos have the same header.
+ bool IsUniqueHeader = std::equal(
+ HeaderInfos.begin()+1, HeaderInfos.end(), HeaderInfos.begin(),
+ [](const IncludeFixerContext::HeaderInfo &LHS,
+ const IncludeFixerContext::HeaderInfo &RHS) {
+ return LHS.Header == RHS.Header;
+ });
+ if (!IsUniqueHeader) {
+ errs() << "Expect exactly one unique header.\n";
+ return 1;
+ }
+
+ // If a header has multiple symbols, we won't add the missing namespace
+ // qualifiers because we don't know which one is exactly used.
+ //
+ // Check whether all elements in HeaderInfos have the same qualified name.
+ bool IsUniqueQualifiedName = std::equal(
+ HeaderInfos.begin() + 1, HeaderInfos.end(), HeaderInfos.begin(),
+ [](const IncludeFixerContext::HeaderInfo &LHS,
+ const IncludeFixerContext::HeaderInfo &RHS) {
+ return LHS.QualifiedName == RHS.QualifiedName;
+ });
+ auto InsertStyle = format::getStyle(format::DefaultFormatStyle,
+ Context.getFilePath(), Style);
+ if (!InsertStyle) {
+ llvm::errs() << llvm::toString(InsertStyle.takeError()) << "\n";
+ return 1;
+ }
+ auto Replacements = clang::include_fixer::createIncludeFixerReplacements(
+ Code->getBuffer(), Context, *InsertStyle,
+ /*AddQualifiers=*/IsUniqueQualifiedName);
+ if (!Replacements) {
+ errs() << "Failed to create replacements: "
+ << llvm::toString(Replacements.takeError()) << "\n";
+ return 1;
+ }
+
+ auto ChangedCode =
+ tooling::applyAllReplacements(Code->getBuffer(), *Replacements);
+ if (!ChangedCode) {
+ llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
+ return 1;
+ }
+ llvm::outs() << *ChangedCode;
+ return 0;
+ }
+
+ // Set up data source.
+ std::unique_ptr<include_fixer::SymbolIndexManager> SymbolIndexMgr =
+ createSymbolIndexManager(SourceFilePath);
+ if (!SymbolIndexMgr)
+ return 1;
+
+ // Query symbol mode.
+ if (!QuerySymbol.empty()) {
+ auto MatchedSymbols = SymbolIndexMgr->search(
+ QuerySymbol, /*IsNestedSearch=*/true, SourceFilePath);
+ for (auto &Symbol : MatchedSymbols) {
+ std::string HeaderPath = Symbol.getFilePath().str();
+ Symbol.SetFilePath(((HeaderPath[0] == '"' || HeaderPath[0] == '<')
+ ? HeaderPath
+ : "\"" + HeaderPath + "\""));
+ }
+
+ // We leave an empty symbol range as we don't know the range of the symbol
+ // being queried in this mode. clang-include-fixer won't add namespace
+ // qualifiers if the symbol range is empty, which also fits this case.
+ IncludeFixerContext::QuerySymbolInfo Symbol;
+ Symbol.RawIdentifier = QuerySymbol;
+ auto Context =
+ IncludeFixerContext(SourceFilePath, {Symbol}, MatchedSymbols);
+ writeToJson(llvm::outs(), Context);
+ return 0;
+ }
+
+ // Now run our tool.
+ std::vector<include_fixer::IncludeFixerContext> Contexts;
+ include_fixer::IncludeFixerActionFactory Factory(*SymbolIndexMgr, Contexts,
+ Style, MinimizeIncludePaths);
+
+ if (tool.run(&Factory) != 0) {
+ // We suppress all Clang diagnostics (because they would be wrong,
+ // clang-include-fixer does custom recovery) but still want to give some
+ // feedback in case there was a compiler error we couldn't recover from.
+ // The most common case for this is a #include in the file that couldn't be
+ // found.
+ llvm::errs() << "Fatal compiler error occurred while parsing file!"
+ " (incorrect include paths?)\n";
+ return 1;
+ }
+
+ assert(!Contexts.empty());
+
+ if (OutputHeaders) {
+ // FIXME: Print contexts of all processing files instead of the first one.
+ writeToJson(llvm::outs(), Contexts.front());
+ return 0;
+ }
+
+ std::vector<tooling::Replacements> FixerReplacements;
+ for (const auto &Context : Contexts) {
+ StringRef FilePath = Context.getFilePath();
+ auto InsertStyle =
+ format::getStyle(format::DefaultFormatStyle, FilePath, Style);
+ if (!InsertStyle) {
+ llvm::errs() << llvm::toString(InsertStyle.takeError()) << "\n";
+ return 1;
+ }
+ auto Buffer = llvm::MemoryBuffer::getFile(FilePath);
+ if (!Buffer) {
+ errs() << "Couldn't open file: " + FilePath.str() + ": "
+ << Buffer.getError().message() + "\n";
+ return 1;
+ }
+
+ auto Replacements = clang::include_fixer::createIncludeFixerReplacements(
+ Buffer.get()->getBuffer(), Context, *InsertStyle);
+ if (!Replacements) {
+ errs() << "Failed to create replacement: "
+ << llvm::toString(Replacements.takeError()) << "\n";
+ return 1;
+ }
+ FixerReplacements.push_back(*Replacements);
+ }
+
+ if (!Quiet) {
+ for (const auto &Context : Contexts) {
+ if (!Context.getHeaderInfos().empty()) {
+ llvm::errs() << "Added #include "
+ << Context.getHeaderInfos().front().Header << " for "
+ << Context.getFilePath() << "\n";
+ }
+ }
+ }
+
+ if (STDINMode) {
+ assert(FixerReplacements.size() == 1);
+ auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(),
+ FixerReplacements.front());
+ if (!ChangedCode) {
+ llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
+ return 1;
+ }
+ llvm::outs() << *ChangedCode;
+ return 0;
+ }
+
+ // Set up a new source manager for applying the resulting replacements.
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions);
+ DiagnosticsEngine Diagnostics(new DiagnosticIDs, &*DiagOpts);
+ TextDiagnosticPrinter DiagnosticPrinter(outs(), &*DiagOpts);
+ SourceManager SM(Diagnostics, tool.getFiles());
+ Diagnostics.setClient(&DiagnosticPrinter, false);
+
+ // Write replacements to disk.
+ Rewriter Rewrites(SM, LangOptions());
+ for (const auto &Replacement : FixerReplacements) {
+ if (!tooling::applyAllReplacements(Replacement, Rewrites)) {
+ llvm::errs() << "Failed to apply replacements.\n";
+ return 1;
+ }
+ }
+ return Rewrites.overwriteChangedFiles();
+}
+
+} // namespace
+
+int main(int argc, const char **argv) {
+ return includeFixerMain(argc, argv);
+}
diff --git a/clang-include-fixer/tool/clang-include-fixer-test.el b/clang-include-fixer/tool/clang-include-fixer-test.el
new file mode 100644
index 00000000..d66d5300
--- /dev/null
+++ b/clang-include-fixer/tool/clang-include-fixer-test.el
@@ -0,0 +1,65 @@
+;;; clang-include-fixer-test.el --- unit tests for clang-include-fixer.el -*- lexical-binding: t; -*-
+
+;;; Commentary:
+
+;; Unit tests for clang-include-fixer.el.
+
+;;; Code:
+
+(require 'clang-include-fixer)
+
+(require 'cc-mode)
+(require 'ert)
+
+(ert-deftest clang-include-fixer--insert-line ()
+ "Unit test for `clang-include-fixer--insert-line'."
+ (with-temp-buffer
+ (insert "aa\nab\nac\nad\n")
+ (let ((from (current-buffer)))
+ (with-temp-buffer
+ (insert "aa\nac\nad\n")
+ (let ((to (current-buffer)))
+ (should (clang-include-fixer--insert-line from to))
+ (should (equal (buffer-string) "aa\nab\nac\nad\n")))))
+ (should (equal (buffer-string) "aa\nab\nac\nad\n"))))
+
+(ert-deftest clang-include-fixer--insert-line-diff-on-empty-line ()
+ "Unit test for `clang-include-fixer--insert-line'."
+ (with-temp-buffer
+ (insert "aa\nab\n\nac\nad\n")
+ (let ((from (current-buffer)))
+ (with-temp-buffer
+ (insert "aa\n\nac\nad\n")
+ (let ((to (current-buffer)))
+ (should (clang-include-fixer--insert-line from to))
+ (should (equal (buffer-string) "aa\nab\n\nac\nad\n")))))
+ (should (equal (buffer-string) "aa\nab\n\nac\nad\n"))))
+
+(ert-deftest clang-include-fixer--symbol-at-point ()
+ "Unit test for `clang-include-fixer--symbol-at-point'."
+ (with-temp-buffer
+ (insert "a+bbb::cc")
+ (c++-mode)
+ (goto-char (point-min))
+ (should (equal (clang-include-fixer--symbol-at-point) "a"))
+ (forward-char)
+ ;; Emacs treats the character immediately following a symbol as part of the
+ ;; symbol.
+ (should (equal (clang-include-fixer--symbol-at-point) "a"))
+ (forward-char)
+ (should (equal (clang-include-fixer--symbol-at-point) "bbb::cc"))
+ (goto-char (point-max))
+ (should (equal (clang-include-fixer--symbol-at-point) "bbb::cc"))))
+
+(ert-deftest clang-include-fixer--highlight ()
+ (with-temp-buffer
+ (insert "util::Status foo;\n")
+ (setq buffer-file-coding-system 'utf-8-unix)
+ (should (equal nil (clang-include-fixer--highlight
+ '((Range . ((Offset . 0) (Length . 0)))))))
+ (let ((overlay (clang-include-fixer--highlight
+ '((Range . ((Offset . 1) (Length . 12)))))))
+ (should (equal 2 (overlay-start overlay)))
+ (should (equal 14 (overlay-end overlay))))))
+
+;;; clang-include-fixer-test.el ends here
diff --git a/clang-include-fixer/tool/clang-include-fixer.el b/clang-include-fixer/tool/clang-include-fixer.el
new file mode 100644
index 00000000..84fff624
--- /dev/null
+++ b/clang-include-fixer/tool/clang-include-fixer.el
@@ -0,0 +1,460 @@
+;;; clang-include-fixer.el --- Emacs integration of the clang include fixer -*- lexical-binding: t; -*-
+
+;; Keywords: tools, c
+;; Package-Requires: ((cl-lib "0.5") (json "1.2") (let-alist "1.0.4"))
+
+;;; Commentary:
+
+;; This package allows Emacs users to invoke the 'clang-include-fixer' within
+;; Emacs. 'clang-include-fixer' provides an automated way of adding #include
+;; directives for missing symbols in one translation unit, see
+;; <http://clang.llvm.org/extra/clang-include-fixer.html>.
+
+;;; Code:
+
+(require 'cl-lib)
+(require 'json)
+(require 'let-alist)
+
+(defgroup clang-include-fixer nil
+ "Clang-based include fixer."
+ :group 'tools)
+
+(defvar clang-include-fixer-add-include-hook nil
+ "A hook that will be called for every added include.
+The first argument is the filename of the include, the second argument is
+non-nil if the include is a system-header.")
+
+(defcustom clang-include-fixer-executable
+ "clang-include-fixer"
+ "Location of the clang-include-fixer executable.
+
+A string containing the name or the full path of the executable."
+ :group 'clang-include-fixer
+ :type '(file :must-match t)
+ :risky t)
+
+(defcustom clang-include-fixer-input-format
+ 'yaml
+ "Input format for clang-include-fixer.
+This string is passed as -db argument to
+`clang-include-fixer-executable'."
+ :group 'clang-include-fixer
+ :type '(radio
+ (const :tag "Hard-coded mapping" :fixed)
+ (const :tag "YAML" yaml)
+ (symbol :tag "Other"))
+ :risky t)
+
+(defcustom clang-include-fixer-init-string
+ ""
+ "Database initialization string for clang-include-fixer.
+This string is passed as -input argument to
+`clang-include-fixer-executable'."
+ :group 'clang-include-fixer
+ :type 'string
+ :risky t)
+
+(defface clang-include-fixer-highlight '((t :background "green"))
+ "Used for highlighting the symbol for which a header file is being added.")
+
+;;;###autoload
+(defun clang-include-fixer ()
+ "Invoke the Include Fixer to insert missing C++ headers."
+ (interactive)
+ (message (concat "Calling the include fixer. "
+ "This might take some seconds. Please wait."))
+ (clang-include-fixer--start #'clang-include-fixer--add-header
+ "-output-headers"))
+
+;;;###autoload
+(defun clang-include-fixer-at-point ()
+ "Invoke the Clang include fixer for the symbol at point."
+ (interactive)
+ (let ((symbol (clang-include-fixer--symbol-at-point)))
+ (unless symbol
+ (user-error "No symbol at current location"))
+ (clang-include-fixer-from-symbol symbol)))
+
+;;;###autoload
+(defun clang-include-fixer-from-symbol (symbol)
+ "Invoke the Clang include fixer for the SYMBOL.
+When called interactively, prompts the user for a symbol."
+ (interactive
+ (list (read-string "Symbol: " (clang-include-fixer--symbol-at-point))))
+ (clang-include-fixer--start #'clang-include-fixer--add-header
+ (format "-query-symbol=%s" symbol)))
+
+(defun clang-include-fixer--start (callback &rest args)
+ "Asynchronously start clang-include-fixer with parameters ARGS.
+The current file name is passed after ARGS as last argument. If
+the call was successful the returned result is stored in a
+temporary buffer, and CALLBACK is called with the temporary
+buffer as only argument."
+ (unless buffer-file-name
+ (user-error "clang-include-fixer works only in buffers that visit a file"))
+ (let ((process (if (and (fboundp 'make-process)
+ ;; ‘make-process’ doesn’t support remote files
+ ;; (https://debbugs.gnu.org/cgi/bugreport.cgi?bug=28691).
+ (not (find-file-name-handler default-directory
+ 'start-file-process)))
+ ;; Prefer using ‘make-process’ if possible, because
+ ;; ‘start-process’ doesn’t allow us to separate the
+ ;; standard error from the output.
+ (clang-include-fixer--make-process callback args)
+ (clang-include-fixer--start-process callback args))))
+ (save-restriction
+ (widen)
+ (process-send-region process (point-min) (point-max)))
+ (process-send-eof process))
+ nil)
+
+(defun clang-include-fixer--make-process (callback args)
+ "Start a new clang-incude-fixer process using `make-process'.
+CALLBACK is called after the process finishes successfully; it is
+called with a single argument, the buffer where standard output
+has been inserted. ARGS is a list of additional command line
+arguments. Return the new process object."
+ (let ((stdin (current-buffer))
+ (stdout (generate-new-buffer "*clang-include-fixer output*"))
+ (stderr (generate-new-buffer "*clang-include-fixer errors*")))
+ (make-process :name "clang-include-fixer"
+ :buffer stdout
+ :command (clang-include-fixer--command args)
+ :coding 'utf-8-unix
+ :noquery t
+ :connection-type 'pipe
+ :sentinel (clang-include-fixer--sentinel stdin stdout stderr
+ callback)
+ :stderr stderr)))
+
+(defun clang-include-fixer--start-process (callback args)
+ "Start a new clang-incude-fixer process using `start-file-process'.
+CALLBACK is called after the process finishes successfully; it is
+called with a single argument, the buffer where standard output
+has been inserted. ARGS is a list of additional command line
+arguments. Return the new process object."
+ (let* ((stdin (current-buffer))
+ (stdout (generate-new-buffer "*clang-include-fixer output*"))
+ (process-connection-type nil)
+ (process (apply #'start-file-process "clang-include-fixer" stdout
+ (clang-include-fixer--command args))))
+ (set-process-coding-system process 'utf-8-unix 'utf-8-unix)
+ (set-process-query-on-exit-flag process nil)
+ (set-process-sentinel process
+ (clang-include-fixer--sentinel stdin stdout nil
+ callback))
+ process))
+
+(defun clang-include-fixer--command (args)
+ "Return the clang-include-fixer command line.
+Returns a list; the first element is the binary to
+execute (`clang-include-fixer-executable'), and the remaining
+elements are the command line arguments. Adds proper arguments
+for `clang-include-fixer-input-format' and
+`clang-include-fixer-init-string'. Appends the current buffer's
+file name; prepends ARGS directly in front of it."
+ (cl-check-type args list)
+ `(,clang-include-fixer-executable
+ ,(format "-db=%s" clang-include-fixer-input-format)
+ ,(format "-input=%s" clang-include-fixer-init-string)
+ "-stdin"
+ ,@args
+ ,(clang-include-fixer--file-local-name buffer-file-name)))
+
+(defun clang-include-fixer--sentinel (stdin stdout stderr callback)
+ "Return a process sentinel for clang-include-fixer processes.
+STDIN, STDOUT, and STDERR are buffers for the standard streams;
+only STDERR may be nil. CALLBACK is called in the case of
+success; it is called with a single argument, STDOUT. On
+failure, a buffer containing the error output is displayed."
+ (cl-check-type stdin buffer-live)
+ (cl-check-type stdout buffer-live)
+ (cl-check-type stderr (or null buffer-live))
+ (cl-check-type callback function)
+ (lambda (process event)
+ (cl-check-type process process)
+ (cl-check-type event string)
+ (unwind-protect
+ (if (string-equal event "finished\n")
+ (progn
+ (when stderr (kill-buffer stderr))
+ (with-current-buffer stdin
+ (funcall callback stdout))
+ (kill-buffer stdout))
+ (when stderr (kill-buffer stdout))
+ (message "clang-include-fixer failed")
+ (with-current-buffer (or stderr stdout)
+ (insert "\nProcess " (process-name process)
+ ?\s event))
+ (display-buffer (or stderr stdout))))
+ nil))
+
+(defun clang-include-fixer--replace-buffer (stdout)
+ "Replace current buffer by content of STDOUT."
+ (cl-check-type stdout buffer-live)
+ (barf-if-buffer-read-only)
+ (cond ((fboundp 'replace-buffer-contents) (replace-buffer-contents stdout))
+ ((clang-include-fixer--insert-line stdout (current-buffer)))
+ (t (erase-buffer) (insert-buffer-substring stdout)))
+ (message "Fix applied")
+ nil)
+
+(defun clang-include-fixer--insert-line (from to)
+ "Insert a single missing line from the buffer FROM into TO.
+FROM and TO must be buffers. If the contents of FROM and TO are
+equal, do nothing and return non-nil. If FROM contains a single
+line missing from TO, insert that line into TO so that the buffer
+contents are equal and return non-nil. Otherwise, do nothing and
+return nil. Buffer restrictions are ignored."
+ (cl-check-type from buffer-live)
+ (cl-check-type to buffer-live)
+ (with-current-buffer from
+ (save-excursion
+ (save-restriction
+ (widen)
+ (with-current-buffer to
+ (save-excursion
+ (save-restriction
+ (widen)
+ ;; Search for the first buffer difference.
+ (let ((chars (abs (compare-buffer-substrings to nil nil from nil nil))))
+ (if (zerop chars)
+ ;; Buffer contents are equal, nothing to do.
+ t
+ (goto-char chars)
+ ;; We might have ended up in the middle of a line if the
+ ;; current line partially matches. In this case we would
+ ;; have to insert more than a line. Move to the beginning of
+ ;; the line to avoid this situation.
+ (beginning-of-line)
+ (with-current-buffer from
+ (goto-char chars)
+ (beginning-of-line)
+ (let ((from-begin (point))
+ (from-end (progn (forward-line) (point)))
+ (to-point (with-current-buffer to (point))))
+ ;; Search for another buffer difference after the line in
+ ;; question. If there is none, we can proceed.
+ (when (zerop (compare-buffer-substrings from from-end nil
+ to to-point nil))
+ (with-current-buffer to
+ (insert-buffer-substring from from-begin from-end))
+ t))))))))))))
+
+(defun clang-include-fixer--add-header (stdout)
+ "Analyse the result of clang-include-fixer stored in STDOUT.
+Add a missing header if there is any. If there are multiple
+possible headers the user can select one of them to be included.
+Temporarily highlight the affected symbols. Asynchronously call
+clang-include-fixer to insert the selected header."
+ (cl-check-type stdout buffer-live)
+ (let ((context (clang-include-fixer--parse-json stdout)))
+ (let-alist context
+ (cond
+ ((null .QuerySymbolInfos)
+ (message "The file is fine, no need to add a header."))
+ ((null .HeaderInfos)
+ (message "Couldn't find header for '%s'"
+ (let-alist (car .QuerySymbolInfos) .RawIdentifier)))
+ (t
+ ;; Users may C-g in prompts, make sure the process sentinel
+ ;; behaves correctly.
+ (with-local-quit
+ ;; Replace the HeaderInfos list by a single header selected by
+ ;; the user.
+ (clang-include-fixer--select-header context)
+ ;; Call clang-include-fixer again to insert the selected header.
+ (clang-include-fixer--start
+ (let ((old-tick (buffer-chars-modified-tick)))
+ (lambda (stdout)
+ (when (/= old-tick (buffer-chars-modified-tick))
+ ;; Replacing the buffer now would undo the user’s changes.
+ (user-error (concat "The buffer has been changed "
+ "before the header could be inserted")))
+ (clang-include-fixer--replace-buffer stdout)
+ (let-alist context
+ (let-alist (car .HeaderInfos)
+ (with-local-quit
+ (run-hook-with-args 'clang-include-fixer-add-include-hook
+ (substring .Header 1 -1)
+ (string= (substring .Header 0 1) "<")))))))
+ (format "-insert-header=%s"
+ (clang-include-fixer--encode-json context))))))))
+ nil)
+
+(defun clang-include-fixer--select-header (context)
+ "Prompt the user for a header if necessary.
+CONTEXT must be a clang-include-fixer context object in
+association list format. If it contains more than one HeaderInfo
+element, prompt the user to select one of the headers. CONTEXT
+is modified to include only the selected element."
+ (cl-check-type context cons)
+ (let-alist context
+ (if (cdr .HeaderInfos)
+ (clang-include-fixer--prompt-for-header context)
+ (message "Only one include is missing: %s"
+ (let-alist (car .HeaderInfos) .Header))))
+ nil)
+
+(defvar clang-include-fixer--history nil
+ "History for `clang-include-fixer--prompt-for-header'.")
+
+(defun clang-include-fixer--prompt-for-header (context)
+ "Prompt the user for a single header.
+The choices are taken from the HeaderInfo elements in CONTEXT.
+They are replaced by the single element selected by the user."
+ (let-alist context
+ (let ((symbol (clang-include-fixer--symbol-name .QuerySymbolInfos))
+ ;; Add temporary highlighting so that the user knows which
+ ;; symbols the current session is about.
+ (overlays (remove nil
+ (mapcar #'clang-include-fixer--highlight .QuerySymbolInfos))))
+ (unwind-protect
+ (save-excursion
+ ;; While prompting, go to the closest overlay so that the user sees
+ ;; some context.
+ (when overlays
+ (goto-char (clang-include-fixer--closest-overlay overlays)))
+ (cl-flet ((header (info) (let-alist info .Header)))
+ ;; The header-infos is already sorted by clang-include-fixer.
+ (let* ((headers (mapcar #'header .HeaderInfos))
+ (header (completing-read
+ (clang-include-fixer--format-message
+ "Select include for '%s': " symbol)
+ headers nil :require-match nil
+ 'clang-include-fixer--history
+ ;; Specify a default to prevent the behavior
+ ;; described in
+ ;; https://github.com/DarwinAwardWinner/ido-completing-read-plus#why-does-ret-sometimes-not-select-the-first-completion-on-the-list--why-is-there-an-empty-entry-at-the-beginning-of-the-completion-list--what-happened-to-old-style-default-selection.
+ (car headers)))
+ (info (cl-find header .HeaderInfos :key #'header :test #'string=)))
+ (unless info (user-error "No header selected"))
+ (setcar .HeaderInfos info)
+ (setcdr .HeaderInfos nil))))
+ (mapc #'delete-overlay overlays)))))
+
+(defun clang-include-fixer--symbol-name (symbol-infos)
+ "Return the unique symbol name in SYMBOL-INFOS.
+Raise a signal if the symbol name is not unique."
+ (let ((symbols (delete-dups (mapcar (lambda (info)
+ (let-alist info .RawIdentifier))
+ symbol-infos))))
+ (when (cdr symbols)
+ (error "Multiple symbols %s returned" symbols))
+ (car symbols)))
+
+(defun clang-include-fixer--highlight (symbol-info)
+ "Add an overlay to highlight SYMBOL-INFO, if it points to a non-empty range.
+Return the overlay object, or nil."
+ (let-alist symbol-info
+ (unless (zerop .Range.Length)
+ (let ((overlay (make-overlay
+ (clang-include-fixer--filepos-to-bufferpos
+ .Range.Offset 'approximate)
+ (clang-include-fixer--filepos-to-bufferpos
+ (+ .Range.Offset .Range.Length) 'approximate))))
+ (overlay-put overlay 'face 'clang-include-fixer-highlight)
+ overlay))))
+
+(defun clang-include-fixer--closest-overlay (overlays)
+ "Return the start of the overlay in OVERLAYS that is closest to point."
+ (cl-check-type overlays cons)
+ (let ((point (point))
+ acc)
+ (dolist (overlay overlays acc)
+ (let ((start (overlay-start overlay)))
+ (when (or (null acc) (< (abs (- point start)) (abs (- point acc))))
+ (setq acc start))))))
+
+(defun clang-include-fixer--parse-json (buffer)
+ "Parse a JSON response from clang-include-fixer in BUFFER.
+Return the JSON object as an association list."
+ (with-current-buffer buffer
+ (save-excursion
+ (goto-char (point-min))
+ (let ((json-object-type 'alist)
+ (json-array-type 'list)
+ (json-key-type 'symbol)
+ (json-false :json-false)
+ (json-null nil)
+ (json-pre-element-read-function nil)
+ (json-post-element-read-function nil))
+ (json-read)))))
+
+(defun clang-include-fixer--encode-json (object)
+ "Return the JSON representation of OBJECT as a string."
+ (let ((json-encoding-separator ",")
+ (json-encoding-default-indentation " ")
+ (json-encoding-pretty-print nil)
+ (json-encoding-lisp-style-closings nil)
+ (json-encoding-object-sort-predicate nil))
+ (json-encode object)))
+
+(defun clang-include-fixer--symbol-at-point ()
+ "Return the qualified symbol at point.
+If there is no symbol at point, return nil."
+ ;; Let ‘bounds-of-thing-at-point’ to do the hard work and deal with edge
+ ;; cases.
+ (let ((bounds (bounds-of-thing-at-point 'symbol)))
+ (when bounds
+ (let ((beg (car bounds))
+ (end (cdr bounds)))
+ (save-excursion
+ ;; Extend the symbol range to the left. Skip over namespace
+ ;; delimiters and parent namespace names.
+ (goto-char beg)
+ (while (and (clang-include-fixer--skip-double-colon-backward)
+ (skip-syntax-backward "w_")))
+ ;; Skip over one more namespace delimiter, for absolute names.
+ (clang-include-fixer--skip-double-colon-backward)
+ (setq beg (point))
+ ;; Extend the symbol range to the right. Skip over namespace
+ ;; delimiters and child namespace names.
+ (goto-char end)
+ (while (and (clang-include-fixer--skip-double-colon-forward)
+ (skip-syntax-forward "w_")))
+ (setq end (point)))
+ (buffer-substring-no-properties beg end)))))
+
+(defun clang-include-fixer--skip-double-colon-forward ()
+ "Skip a double colon.
+When the next two characters are '::', skip them and return
+non-nil. Otherwise return nil."
+ (let ((end (+ (point) 2)))
+ (when (and (<= end (point-max))
+ (string-equal (buffer-substring-no-properties (point) end) "::"))
+ (goto-char end)
+ t)))
+
+(defun clang-include-fixer--skip-double-colon-backward ()
+ "Skip a double colon.
+When the previous two characters are '::', skip them and return
+non-nil. Otherwise return nil."
+ (let ((beg (- (point) 2)))
+ (when (and (>= beg (point-min))
+ (string-equal (buffer-substring-no-properties beg (point)) "::"))
+ (goto-char beg)
+ t)))
+
+;; ‘filepos-to-bufferpos’ is new in Emacs 25.1. Provide a fallback for older
+;; versions.
+(defalias 'clang-include-fixer--filepos-to-bufferpos
+ (if (fboundp 'filepos-to-bufferpos)
+ 'filepos-to-bufferpos
+ (lambda (byte &optional _quality _coding-system)
+ (byte-to-position (1+ byte)))))
+
+;; ‘format-message’ is new in Emacs 25.1. Provide a fallback for older
+;; versions.
+(defalias 'clang-include-fixer--format-message
+ (if (fboundp 'format-message) 'format-message 'format))
+
+;; ‘file-local-name’ is new in Emacs 26.1. Provide a fallback for older
+;; versions.
+(defalias 'clang-include-fixer--file-local-name
+ (if (fboundp 'file-local-name) #'file-local-name
+ (lambda (file) (or (file-remote-p file 'localname) file))))
+
+(provide 'clang-include-fixer)
+;;; clang-include-fixer.el ends here
diff --git a/clang-include-fixer/tool/clang-include-fixer.py b/clang-include-fixer/tool/clang-include-fixer.py
new file mode 100644
index 00000000..4c38f71e
--- /dev/null
+++ b/clang-include-fixer/tool/clang-include-fixer.py
@@ -0,0 +1,210 @@
+# This file is a minimal clang-include-fixer vim-integration. To install:
+# - Change 'binary' if clang-include-fixer is not on the path (see below).
+# - Add to your .vimrc:
+#
+# noremap <leader>cf :pyf path/to/llvm/source/tools/clang/tools/extra/clang-include-fixer/tool/clang-include-fixer.py<cr>
+#
+# This enables clang-include-fixer for NORMAL and VISUAL mode. Change
+# "<leader>cf" to another binding if you need clang-include-fixer on a
+# different key.
+#
+# To set up clang-include-fixer, see
+# http://clang.llvm.org/extra/clang-include-fixer.html
+#
+# With this integration you can press the bound key and clang-include-fixer will
+# be run on the current buffer.
+#
+# It operates on the current, potentially unsaved buffer and does not create
+# or save any files. To revert a fix, just undo.
+
+import argparse
+import difflib
+import json
+import re
+import subprocess
+import vim
+
+# set g:clang_include_fixer_path to the path to clang-include-fixer if it is not
+# on the path.
+# Change this to the full path if clang-include-fixer is not on the path.
+binary = 'clang-include-fixer'
+if vim.eval('exists("g:clang_include_fixer_path")') == "1":
+ binary = vim.eval('g:clang_include_fixer_path')
+
+maximum_suggested_headers = 3
+if vim.eval('exists("g:clang_include_fixer_maximum_suggested_headers")') == "1":
+ maximum_suggested_headers = max(
+ 1,
+ vim.eval('g:clang_include_fixer_maximum_suggested_headers'))
+
+increment_num = 5
+if vim.eval('exists("g:clang_include_fixer_increment_num")') == "1":
+ increment_num = max(
+ 1,
+ vim.eval('g:clang_include_fixer_increment_num'))
+
+jump_to_include = False
+if vim.eval('exists("g:clang_include_fixer_jump_to_include")') == "1":
+ jump_to_include = vim.eval('g:clang_include_fixer_jump_to_include') != "0"
+
+query_mode = False
+if vim.eval('exists("g:clang_include_fixer_query_mode")') == "1":
+ query_mode = vim.eval('g:clang_include_fixer_query_mode') != "0"
+
+
+def GetUserSelection(message, headers, maximum_suggested_headers):
+ eval_message = message + '\n'
+ for idx, header in enumerate(headers[0:maximum_suggested_headers]):
+ eval_message += "({0}). {1}\n".format(idx + 1, header)
+ eval_message += "Enter (q) to quit;"
+ if maximum_suggested_headers < len(headers):
+ eval_message += " (m) to show {0} more candidates.".format(
+ min(increment_num, len(headers) - maximum_suggested_headers))
+
+ eval_message += "\nSelect (default 1): "
+ res = vim.eval("input('{0}')".format(eval_message))
+ if res == '':
+ # choose the top ranked header by default
+ idx = 1
+ elif res == 'q':
+ raise Exception(' Insertion cancelled...')
+ elif res == 'm':
+ return GetUserSelection(message,
+ headers, maximum_suggested_headers + increment_num)
+ else:
+ try:
+ idx = int(res)
+ if idx <= 0 or idx > len(headers):
+ raise Exception()
+ except Exception:
+ # Show a new prompt on invalid option instead of aborting so that users
+ # don't need to wait for another clang-include-fixer run.
+ print >> sys.stderr, "Invalid option:", res
+ return GetUserSelection(message, headers, maximum_suggested_headers)
+ return headers[idx - 1]
+
+
+def execute(command, text):
+ p = subprocess.Popen(command,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ stdin=subprocess.PIPE)
+ return p.communicate(input=text)
+
+
+def InsertHeaderToVimBuffer(header, text):
+ command = [binary, "-stdin", "-insert-header=" + json.dumps(header),
+ vim.current.buffer.name]
+ stdout, stderr = execute(command, text)
+ if stderr:
+ raise Exception(stderr)
+ if stdout:
+ lines = stdout.splitlines()
+ sequence = difflib.SequenceMatcher(None, vim.current.buffer, lines)
+ line_num = None
+ for op in reversed(sequence.get_opcodes()):
+ if op[0] != 'equal':
+ vim.current.buffer[op[1]:op[2]] = lines[op[3]:op[4]]
+ if op[0] == 'insert':
+ # line_num in vim is 1-based.
+ line_num = op[1] + 1
+
+ if jump_to_include and line_num:
+ vim.current.window.cursor = (line_num, 0)
+
+
+# The vim internal implementation (expand("cword"/"cWORD")) doesn't support
+# our use case very well, we re-implement our own one.
+def get_symbol_under_cursor():
+ line = vim.eval("line(\".\")")
+ # column number in vim is 1-based.
+ col = int(vim.eval("col(\".\")")) - 1
+ line_text = vim.eval("getline({0})".format(line))
+ if len(line_text) == 0: return ""
+ symbol_pos_begin = col
+ p = re.compile('[a-zA-Z0-9:_]')
+ while symbol_pos_begin >= 0 and p.match(line_text[symbol_pos_begin]):
+ symbol_pos_begin -= 1
+
+ symbol_pos_end = col
+ while symbol_pos_end < len(line_text) and p.match(line_text[symbol_pos_end]):
+ symbol_pos_end += 1
+ return line_text[symbol_pos_begin+1:symbol_pos_end]
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description='Vim integration for clang-include-fixer')
+ parser.add_argument('-db', default='yaml',
+ help='clang-include-fixer input format.')
+ parser.add_argument('-input', default='',
+ help='String to initialize the database.')
+ # Don't throw exception when parsing unknown arguements to make the script
+ # work in neovim.
+ # Neovim (at least v0.2.1) somehow mangles the sys.argv in a weird way: it
+ # will pass additional arguments (e.g. "-c script_host.py") to sys.argv,
+ # which makes the script fail.
+ args, _ = parser.parse_known_args()
+
+ # Get the current text.
+ buf = vim.current.buffer
+ text = '\n'.join(buf)
+
+ if query_mode:
+ symbol = get_symbol_under_cursor()
+ if len(symbol) == 0:
+ print "Skip querying empty symbol."
+ return
+ command = [binary, "-stdin", "-query-symbol="+get_symbol_under_cursor(),
+ "-db=" + args.db, "-input=" + args.input,
+ vim.current.buffer.name]
+ else:
+ # Run command to get all headers.
+ command = [binary, "-stdin", "-output-headers", "-db=" + args.db,
+ "-input=" + args.input, vim.current.buffer.name]
+ stdout, stderr = execute(command, text)
+ if stderr:
+ print >> sys.stderr, "Error while running clang-include-fixer: " + stderr
+ return
+
+ include_fixer_context = json.loads(stdout)
+ query_symbol_infos = include_fixer_context["QuerySymbolInfos"]
+ if not query_symbol_infos:
+ print "The file is fine, no need to add a header."
+ return
+ symbol = query_symbol_infos[0]["RawIdentifier"]
+ # The header_infos is already sorted by clang-include-fixer.
+ header_infos = include_fixer_context["HeaderInfos"]
+ # Deduplicate headers while keeping the order, so that the same header would
+ # not be suggested twice.
+ unique_headers = []
+ seen = set()
+ for header_info in header_infos:
+ header = header_info["Header"]
+ if header not in seen:
+ seen.add(header)
+ unique_headers.append(header)
+
+ if not unique_headers:
+ print "Couldn't find a header for {0}.".format(symbol)
+ return
+
+ try:
+ selected = unique_headers[0]
+ inserted_header_infos = header_infos
+ if len(unique_headers) > 1:
+ selected = GetUserSelection(
+ "choose a header file for {0}.".format(symbol),
+ unique_headers, maximum_suggested_headers)
+ inserted_header_infos = [
+ header for header in header_infos if header["Header"] == selected]
+ include_fixer_context["HeaderInfos"] = inserted_header_infos
+
+ InsertHeaderToVimBuffer(include_fixer_context, text)
+ print "Added #include {0} for {1}.".format(selected, symbol)
+ except Exception as error:
+ print >> sys.stderr, error.message
+ return
+
+
+if __name__ == '__main__':
+ main()