Index: clangd/index/SymbolCollector.h =================================================================== --- clangd/index/SymbolCollector.h +++ clangd/index/SymbolCollector.h @@ -50,10 +50,12 @@ /// If set, this is used to map symbol #include path to a potentially /// different #include path. const CanonicalIncludes *Includes = nullptr; - // Populate the Symbol.References field. + /// Populate the Symbol.References field. bool CountReferences = false; // Every symbol collected will be stamped with this origin. SymbolOrigin Origin = SymbolOrigin::Unknown; + /// Collect macros. + bool CollectMacro = false; }; SymbolCollector(Options Opts); @@ -75,6 +77,10 @@ SourceLocation Loc, index::IndexDataConsumer::ASTNodeInfo ASTNode) override; + bool handleMacroOccurence(const IdentifierInfo &Name, const MacroInfo &MI, + index::SymbolRoleSet Roles, + SourceLocation Loc) override; + SymbolSlab takeSymbols() { return std::move(Symbols).build(); } void finish() override; @@ -90,8 +96,8 @@ std::shared_ptr CompletionAllocator; std::unique_ptr CompletionTUInfo; Options Opts; - // Decls referenced from the current TU, flushed on finish(). - llvm::DenseSet ReferencedDecls; + // Macros referenced from the current TU, flushed on finish(). + llvm::DenseSet ReferencedSymbols; // Maps canonical declaration provided by clang to canonical declaration for // an index symbol, if clangd prefers a different declaration than that // provided by clang. For example, friend declaration might be considered Index: clangd/index/SymbolCollector.cpp =================================================================== --- clangd/index/SymbolCollector.cpp +++ clangd/index/SymbolCollector.cpp @@ -183,22 +183,19 @@ return toURI(SM, Header, Opts); } -// Return the symbol location of the given declaration `D`. -// -// For symbols defined inside macros: -// * use expansion location, if the symbol is formed via macro concatenation. -// * use spelling location, otherwise. -llvm::Optional getSymbolLocation( - const NamedDecl &D, SourceManager &SM, const SymbolCollector::Options &Opts, - const clang::LangOptions &LangOpts, std::string &FileURIStorage) { - SourceLocation NameLoc = findNameLoc(&D); - auto U = toURI(SM, SM.getFilename(NameLoc), Opts); +// Return the symbol location of the token at \p Loc. +llvm::Optional +getTokenLocation(SourceLocation TokLoc, const SourceManager &SM, + const SymbolCollector::Options &Opts, + const clang::LangOptions &LangOpts, + std::string &FileURIStorage) { + auto U = toURI(SM, SM.getFilename(TokLoc), Opts); if (!U) return llvm::None; FileURIStorage = std::move(*U); SymbolLocation Result; Result.FileURI = FileURIStorage; - auto TokenLength = clang::Lexer::MeasureTokenLength(NameLoc, SM, LangOpts); + auto TokenLength = clang::Lexer::MeasureTokenLength(TokLoc, SM, LangOpts); auto CreatePosition = [&SM](SourceLocation Loc) { auto LSPLoc = sourceLocToPosition(SM, Loc); @@ -208,8 +205,8 @@ return Pos; }; - Result.Start = CreatePosition(NameLoc); - auto EndLoc = NameLoc.getLocWithOffset(TokenLength); + Result.Start = CreatePosition(TokLoc); + auto EndLoc = TokLoc.getLocWithOffset(TokenLength); Result.End = CreatePosition(EndLoc); return std::move(Result); @@ -309,13 +306,18 @@ if (!ND) return true; + llvm::SmallString<128> USR; + if (index::generateUSRForDecl(ND, USR)) + return true; + SymbolID ID(USR); + // Mark D as referenced if this is a reference coming from the main file. // D may not be an interesting symbol, but it's cheaper to check at the end. auto &SM = ASTCtx->getSourceManager(); if (Opts.CountReferences && (Roles & static_cast(index::SymbolRole::Reference)) && SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID()) - ReferencedDecls.insert(ND); + ReferencedSymbols.insert(ID); // Don't continue indexing if this is a mere reference. if (!(Roles & static_cast(index::SymbolRole::Declaration) || @@ -324,11 +326,6 @@ if (!shouldCollectSymbol(*ND, *ASTCtx, Opts)) return true; - llvm::SmallString<128> USR; - if (index::generateUSRForDecl(ND, USR)) - return true; - SymbolID ID(USR); - const NamedDecl &OriginalDecl = *cast(ASTNode.OrigD); const Symbol *BasicSymbol = Symbols.find(ID); if (!BasicSymbol) // Regardless of role, ND is the canonical declaration. @@ -345,18 +342,86 @@ return true; } -void SymbolCollector::finish() { - // At the end of the TU, add 1 to the refcount of the ReferencedDecls. - for (const auto *ND : ReferencedDecls) { - llvm::SmallString<128> USR; - if (!index::generateUSRForDecl(ND, USR)) - if (const auto *S = Symbols.find(SymbolID(USR))) { - Symbol Inc = *S; - ++Inc.References; - Symbols.insert(Inc); - } +bool SymbolCollector::handleMacroOccurence(const IdentifierInfo &Name, + const MacroInfo &MI, + index::SymbolRoleSet Roles, + SourceLocation Loc) { + assert(PP.get() && "Preprocessor must be set."); + if (!Opts.CollectMacro) + return true; + + const auto &SM = PP->getSourceManager(); + if (SM.isInMainFile(SM.getExpansionLoc(MI.getDefinitionLoc()))) + return true; + // Builtin macro should already be available in sema. + if (MI.isUsedForHeaderGuard() || MI.isBuiltinMacro()) + return true; + + llvm::SmallString<128> USR; + if (index::generateUSRForMacro(Name.getName(), MI.getDefinitionLoc(), SM, + USR)) + return true; + SymbolID ID(USR); + + // Mark the macro as referenced if this is a reference coming from the main + // file. The macro may not be an interesting symbol, but it's cheaper to check + // at the end. + if (Opts.CountReferences && + (Roles & static_cast(index::SymbolRole::Reference)) && + SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID()) + ReferencedSymbols.insert(ID); + // Don't continue indexing if this is a mere reference. + // FIXME: remove macro with ID if it is undefined. + if (!(Roles & static_cast(index::SymbolRole::Declaration) || + Roles & static_cast(index::SymbolRole::Definition))) + return true; + + // Only collect one instance in case there are multiple. + if (Symbols.find(ID) != nullptr) + return true; + + Symbol S; + S.ID = std::move(ID); + S.Name = Name.getName(); + S.IsIndexedForCodeCompletion = true; + S.SymInfo = index::getSymbolInfoForMacro(MI); + std::string FileURI; + if (auto DeclLoc = getTokenLocation(MI.getDefinitionLoc(), SM, Opts, + PP->getLangOpts(), FileURI)) + S.CanonicalDeclaration = *DeclLoc; + + CodeCompletionResult SymbolCompletion(&Name); + const auto *CCS = SymbolCompletion.CreateCodeCompletionStringForMacro( + *PP, *CompletionAllocator, *CompletionTUInfo); + std::string Signature; + std::string SnippetSuffix; + getSignature(*CCS, &Signature, &SnippetSuffix); + + std::string Include; + if (Opts.CollectIncludePath && shouldCollectIncludePath(S.SymInfo.Kind)) { + if (auto Header = + getIncludeHeader(Name.getName(), SM, + SM.getExpansionLoc(MI.getDefinitionLoc()), Opts)) + Include = std::move(*Header); } - ReferencedDecls.clear(); + S.Signature = Signature; + S.CompletionSnippetSuffix = SnippetSuffix; + Symbol::Details Detail; + Detail.IncludeHeader = Include; + S.Detail = &Detail; + Symbols.insert(S); + return true; +} + +void SymbolCollector::finish() { + // At the end of the TU, add 1 to the refcount of all referenced symbols. + for (const auto &ID : ReferencedSymbols) + if (const auto *S = Symbols.find(ID)) { + Symbol Inc = *S; + ++Inc.References; + Symbols.insert(Inc); + } + ReferencedSymbols.clear(); } const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND, @@ -374,8 +439,8 @@ S.IsIndexedForCodeCompletion = isIndexedForCodeCompletion(ND, Ctx); S.SymInfo = index::getSymbolInfo(&ND); std::string FileURI; - if (auto DeclLoc = - getSymbolLocation(ND, SM, Opts, ASTCtx->getLangOpts(), FileURI)) + if (auto DeclLoc = getTokenLocation(findNameLoc(&ND), SM, Opts, + ASTCtx->getLangOpts(), FileURI)) S.CanonicalDeclaration = *DeclLoc; // Add completion info. @@ -425,8 +490,9 @@ // in clang::index. We should only see one definition. Symbol S = DeclSym; std::string FileURI; - if (auto DefLoc = getSymbolLocation(ND, ND.getASTContext().getSourceManager(), - Opts, ASTCtx->getLangOpts(), FileURI)) + if (auto DefLoc = getTokenLocation(findNameLoc(&ND), + ND.getASTContext().getSourceManager(), + Opts, ASTCtx->getLangOpts(), FileURI)) S.Definition = *DefLoc; Symbols.insert(S); } Index: unittests/clangd/SymbolCollectorTests.cpp =================================================================== --- unittests/clangd/SymbolCollectorTests.cpp +++ unittests/clangd/SymbolCollectorTests.cpp @@ -992,6 +992,32 @@ Field(&Symbol::Origin, SymbolOrigin::Static))); } +TEST_F(SymbolCollectorTest, CollectMacros) { + CollectorOpts.CollectIncludePath = true; + Annotations Header(R"( + #define X 1 + #define $mac[[MAC]](x) int x + #define $used[[USED]](y) float y; + + MAC(p); + )"); + const std::string Main = R"( + #define MAIN 1 // not indexed + USED(t); + )"; + CollectorOpts.CountReferences = true; + CollectorOpts.CollectMacro = true; + runSymbolCollector(Header.code(), Main); + EXPECT_THAT( + Symbols, + UnorderedElementsAre( + QName("p"), + AllOf(QName("X"), DeclURI(TestHeaderURI), + IncludeHeader(TestHeaderURI)), + AllOf(Labeled("MAC(x)"), Refs(0), DeclRange(Header.range("mac"))), + AllOf(Labeled("USED(y)"), Refs(1), DeclRange(Header.range("used"))))); +} + } // namespace } // namespace clangd } // namespace clang