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; @@ -92,6 +98,8 @@ 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 ReferencedMacros; // 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,15 +183,13 @@ 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); +// Return the symbol location of the declaration at \p Loc. The range covers the +// token. +llvm::Optional +getSymbolLocation(SourceLocation NameLoc, const SourceManager &SM, + const SymbolCollector::Options &Opts, + const clang::LangOptions &LangOpts, + std::string &FileURIStorage) { auto U = toURI(SM, SM.getFilename(NameLoc), Opts); if (!U) return llvm::None; @@ -346,17 +344,94 @@ } void SymbolCollector::finish() { - // At the end of the TU, add 1 to the refcount of the ReferencedDecls. + // At the end of the TU, add 1 to the refcount of all referenced symbols. + auto IncRef = [this](const SymbolID &ID) { + if (const auto *S = Symbols.find(ID)) { + Symbol Inc = *S; + ++Inc.References; + Symbols.insert(Inc); + } + }; 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); - } + IncRef(SymbolID(USR)); } + for (const auto &ID : ReferencedMacros) + IncRef(ID); ReferencedDecls.clear(); + ReferencedMacros.clear(); +} + +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()) + ReferencedMacros.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 = getSymbolLocation(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); + } + S.Signature = Signature; + S.CompletionSnippetSuffix = SnippetSuffix; + Symbol::Details Detail; + Detail.IncludeHeader = Include; + S.Detail = &Detail; + Symbols.insert(S); + return true; } const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND, @@ -374,8 +449,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 = getSymbolLocation(findNameLoc(&ND), SM, Opts, + ASTCtx->getLangOpts(), FileURI)) S.CanonicalDeclaration = *DeclLoc; // Add completion info. @@ -425,7 +500,8 @@ // in clang::index. We should only see one definition. Symbol S = DeclSym; std::string FileURI; - if (auto DefLoc = getSymbolLocation(ND, ND.getASTContext().getSourceManager(), + if (auto DefLoc = getSymbolLocation(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