[clangd] collect symbol #include & insert #include in global code completion.
Summary:
o Collect suitable #include paths for index symbols. This also does smart mapping
for STL symbols and IWYU pragma (code borrowed from include-fixer).
o For global code completion, add a command for inserting new #include in each code
completion item.
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: klimek, mgorny, ilya-biryukov, jkorous-apple, hintonda, cfe-commits
Differential Revision: https://reviews.llvm.org/D42640
llvm-svn: 325343
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 0e2186d..2a32565 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -11,6 +11,7 @@
#include "../CodeCompletionStrings.h"
#include "../Logger.h"
#include "../URI.h"
+#include "CanonicalIncludes.h"
#include "clang/AST/DeclCXX.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/SourceManager.h"
@@ -127,6 +128,51 @@
return false;
}
+// We only collect #include paths for symbols that are suitable for global code
+// completion, except for namespaces since #include path for a namespace is hard
+// to define.
+bool shouldCollectIncludePath(index::SymbolKind Kind) {
+ using SK = index::SymbolKind;
+ switch (Kind) {
+ case SK::Macro:
+ case SK::Enum:
+ case SK::Struct:
+ case SK::Class:
+ case SK::Union:
+ case SK::TypeAlias:
+ case SK::Using:
+ case SK::Function:
+ case SK::Variable:
+ case SK::EnumConstant:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/// Gets a canonical include (<header> or "header") for header of \p Loc.
+/// Returns None if the header has no canonical include.
+/// FIXME: we should handle .inc files whose symbols are expected be exported by
+/// their containing headers.
+llvm::Optional<std::string>
+getIncludeHeader(const SourceManager &SM, SourceLocation Loc,
+ const SymbolCollector::Options &Opts) {
+ llvm::StringRef FilePath = SM.getFilename(Loc);
+ if (FilePath.empty())
+ return llvm::None;
+ if (Opts.Includes) {
+ llvm::StringRef Mapped = Opts.Includes->mapHeader(FilePath);
+ if (Mapped != FilePath)
+ return (Mapped.startswith("<") || Mapped.startswith("\""))
+ ? Mapped.str()
+ : ("\"" + Mapped + "\"").str();
+ }
+ // If the header path is the same as the file path of the declaration, we skip
+ // storing the #include path; users can use the URI in declaration location to
+ // calculate the #include path.
+ return llvm::None;
+}
+
// Return the symbol location of the given declaration `D`.
//
// For symbols defined inside macros:
@@ -252,6 +298,14 @@
std::string Documentation = getDocumentation(*CCS);
std::string CompletionDetail = getDetail(*CCS);
+ std::string Include;
+ if (Opts.CollectIncludePath && shouldCollectIncludePath(S.SymInfo.Kind)) {
+ // Use the expansion location to get the #include header since this is
+ // where the symbol is exposed.
+ if (auto Header =
+ getIncludeHeader(SM, SM.getExpansionLoc(ND.getLocation()), Opts))
+ Include = std::move(*Header);
+ }
S.CompletionFilterText = FilterText;
S.CompletionLabel = Label;
S.CompletionPlainInsertText = PlainInsertText;
@@ -259,6 +313,7 @@
Symbol::Details Detail;
Detail.Documentation = Documentation;
Detail.CompletionDetail = CompletionDetail;
+ Detail.IncludeHeader = Include;
S.Detail = &Detail;
Symbols.insert(S);