diff --git a/clang-tools-extra/clangd/index/SymbolCollector.h b/clang-tools-extra/clangd/index/SymbolCollector.h --- a/clang-tools-extra/clangd/index/SymbolCollector.h +++ b/clang-tools-extra/clangd/index/SymbolCollector.h @@ -8,18 +8,21 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_SYMBOLCOLLECTOR_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_SYMBOLCOLLECTOR_H -#include "index/CanonicalIncludes.h" #include "CollectMacros.h" +#include "index/CanonicalIncludes.h" #include "index/Ref.h" #include "index/Relation.h" #include "index/Symbol.h" +#include "index/SymbolID.h" #include "index/SymbolOrigin.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Index/IndexDataConsumer.h" #include "clang/Index/IndexSymbol.h" +#include "clang/Lex/MacroInfo.h" #include "clang/Sema/CodeCompleteConsumer.h" #include "llvm/ADT/DenseMap.h" #include @@ -141,6 +144,11 @@ llvm::Optional getTokenLocation(SourceLocation TokLoc); llvm::Optional getIncludeHeader(const Symbol &S, FileID); + SymbolID getSymbolIDCached(const Decl *D); + SymbolID getSymbolIDCached(const llvm::StringRef MacroName, + const MacroInfo *MI, const SourceManager &SM); + void addRef(SymbolID ID, SourceLocation RefLoc, FileID FID, + index::SymbolRoleSet Roles, const Decl *Container, bool Spelled); // All Symbols collected from the AST. SymbolSlab::Builder Symbols; @@ -162,16 +170,8 @@ std::shared_ptr CompletionAllocator; std::unique_ptr CompletionTUInfo; Options Opts; - struct SymbolRef { - SourceLocation Loc; - index::SymbolRoleSet Roles; - const Decl *Container; - }; // Symbols referenced from the current TU, flushed on finish(). - llvm::DenseSet ReferencedDecls; - llvm::DenseSet ReferencedMacros; - llvm::DenseMap> DeclRefs; - llvm::DenseMap> MacroRefs; + 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 @@ -184,6 +184,8 @@ // to insert for which symbol, etc. class HeaderFileURICache; std::unique_ptr HeaderFileURIs; + llvm::DenseMap DeclToIDCache; + llvm::DenseMap MacroToIDCache; }; } // namespace clangd diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp --- a/clang-tools-extra/clangd/index/SymbolCollector.cpp +++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp @@ -14,6 +14,7 @@ #include "SourceCode.h" #include "URI.h" #include "index/CanonicalIncludes.h" +#include "index/Ref.h" #include "index/Relation.h" #include "index/SymbolID.h" #include "index/SymbolLocation.h" @@ -543,6 +544,9 @@ const NamedDecl *ND = dyn_cast(D); if (!ND) return true; + auto ID = getSymbolIDCached(ND); + if (!ID) + return true; // 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. @@ -550,11 +554,7 @@ if (Opts.CountReferences && (Roles & static_cast(index::SymbolRole::Reference)) && SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID()) - ReferencedDecls.insert(ND); - - auto ID = getSymbolID(ND); - if (!ID) - return true; + ReferencedSymbols.insert(ID); // ND is the canonical (i.e. first) declaration. If it's in the main file // (which is not a header), then no public declaration was visible, so assume @@ -575,13 +575,6 @@ processRelations(*ND, ID, Relations); bool CollectRef = static_cast(Opts.RefFilter & toRefKind(Roles)); - bool IsOnlyRef = - !(Roles & (static_cast(index::SymbolRole::Declaration) | - static_cast(index::SymbolRole::Definition))); - - if (IsOnlyRef && !CollectRef) - return true; - // Unlike other fields, e.g. Symbols (which use spelling locations), we use // file locations for references (as it aligns the behavior of clangd's // AST-based xref). @@ -589,13 +582,33 @@ if (CollectRef && (!IsMainFileOnly || Opts.CollectMainFileRefs || ND->isExternallyVisible()) && - !isa(ND) && - (Opts.RefsInHeaders || - SM.getFileID(SM.getFileLoc(Loc)) == SM.getMainFileID())) - DeclRefs[ND].push_back(SymbolRef{SM.getFileLoc(Loc), Roles, - getRefContainer(ASTNode.Parent, Opts)}); + !isa(ND)) { + auto FileLoc = SM.getFileLoc(Loc); + auto FID = SM.getFileID(FileLoc); + if (Opts.RefsInHeaders || FID == SM.getMainFileID()) { + // FIXME: It's better to use TokenBuffer by passing spelled tokens from + // the caller of SymbolCollector. + llvm::DenseMap> FilesToTokensCache; + if (!FilesToTokensCache.count(FID)) + FilesToTokensCache[FID] = + syntax::tokenize(FID, SM, ASTCtx->getLangOpts()); + llvm::ArrayRef Tokens = FilesToTokensCache[FID]; + // Check if the referenced symbol is spelled exactly the same way the + // corresponding NamedDecl is. If it is, mark this reference as spelled. + const auto *IdentifierToken = spelledIdentifierTouching(FileLoc, Tokens); + DeclarationName Name = ND->getDeclName(); + const auto NameKind = Name.getNameKind(); + bool IsTargetKind = NameKind == DeclarationName::Identifier || + NameKind == DeclarationName::CXXConstructorName; + bool Spelled = IdentifierToken && IsTargetKind && + Name.getAsString() == IdentifierToken->text(SM); + addRef(ID, FileLoc, FID, Roles, getRefContainer(ASTNode.Parent, Opts), + Spelled); + } + } // Don't continue indexing if this is a mere reference. - if (IsOnlyRef) + if (!(Roles & (static_cast(index::SymbolRole::Declaration) | + static_cast(index::SymbolRole::Definition)))) return true; // FIXME: ObjCPropertyDecl are not properly indexed here: @@ -681,7 +694,7 @@ Name->getName() == "__GCC_HAVE_DWARF2_CFI_ASM") return true; - auto ID = getSymbolID(Name->getName(), MI, SM); + auto ID = getSymbolIDCached(Name->getName(), MI, SM); if (!ID) return true; @@ -694,7 +707,10 @@ if ((static_cast(Opts.RefFilter) & Roles) && !IsMainFileOnly && (Opts.RefsInHeaders || SM.getFileID(SpellingLoc) == SM.getMainFileID())) // FIXME: Populate container information for macro references. - MacroRefs[ID].push_back({Loc, Roles, /*Container=*/nullptr}); + // FIXME: All macro references are marked as Spelled now, but this should be + // checked. + addRef(ID, Loc, SM.getFileID(Loc), Roles, + /*Container=*/nullptr, /*Spelled=*/true); // Collect symbols. if (!Opts.CollectMacro) @@ -710,7 +726,7 @@ if (Opts.CountReferences && (Roles & static_cast(index::SymbolRole::Reference)) && SM.getFileID(SpellingLoc) == SM.getMainFileID()) - ReferencedMacros.insert(Name); + ReferencedSymbols.insert(ID); // Don't continue indexing if this is a mere reference. // FIXME: remove macro with ID if it is undefined. @@ -760,7 +776,7 @@ continue; const Decl *Object = R.RelatedSymbol; - auto ObjectID = getSymbolID(Object); + auto ObjectID = getSymbolIDCached(Object); if (!ObjectID) continue; @@ -798,11 +814,8 @@ Symbols.insert(Inc); } }; - for (const NamedDecl *ND : ReferencedDecls) { - if (auto ID = getSymbolID(ND)) { - IncRef(ID); - } - } + for (auto &ID : ReferencedSymbols) + IncRef(ID); if (Opts.CollectMacro) { assert(PP); // First, drop header guards. We can't identify these until EOF. @@ -812,12 +825,6 @@ if (MI->isUsedForHeaderGuard()) Symbols.erase(ID); } - // Now increment refcounts. - for (const IdentifierInfo *II : ReferencedMacros) { - if (const auto *MI = PP->getMacroDefinition(II).getMacroInfo()) - if (auto ID = getSymbolID(II->getName(), MI, PP->getSourceManager())) - IncRef(ID); - } } // Fill in IncludeHeaders. // We delay this until end of TU so header guards are all resolved. @@ -851,58 +858,7 @@ } } - const auto &SM = ASTCtx->getSourceManager(); - auto CollectRef = [&](SymbolID ID, const SymbolRef &LocAndRole, - bool Spelled = false) { - auto FileID = SM.getFileID(LocAndRole.Loc); - // FIXME: use the result to filter out references. - shouldIndexFile(FileID); - if (const auto *FE = SM.getFileEntryForID(FileID)) { - auto Range = getTokenRange(LocAndRole.Loc, SM, ASTCtx->getLangOpts()); - Ref R; - R.Location.Start = Range.first; - R.Location.End = Range.second; - R.Location.FileURI = HeaderFileURIs->toURI(FE).c_str(); - R.Kind = toRefKind(LocAndRole.Roles, Spelled); - R.Container = getSymbolID(LocAndRole.Container); - Refs.insert(ID, R); - } - }; - // Populate Refs slab from MacroRefs. - // FIXME: All MacroRefs are marked as Spelled now, but this should be checked. - for (const auto &IDAndRefs : MacroRefs) - for (const auto &LocAndRole : IDAndRefs.second) - CollectRef(IDAndRefs.first, LocAndRole, /*Spelled=*/true); - // Populate Refs slab from DeclRefs. - llvm::DenseMap> FilesToTokensCache; - for (auto &DeclAndRef : DeclRefs) { - if (auto ID = getSymbolID(DeclAndRef.first)) { - for (auto &LocAndRole : DeclAndRef.second) { - const auto FileID = SM.getFileID(LocAndRole.Loc); - // FIXME: It's better to use TokenBuffer by passing spelled tokens from - // the caller of SymbolCollector. - if (!FilesToTokensCache.count(FileID)) - FilesToTokensCache[FileID] = - syntax::tokenize(FileID, SM, ASTCtx->getLangOpts()); - llvm::ArrayRef Tokens = FilesToTokensCache[FileID]; - // Check if the referenced symbol is spelled exactly the same way the - // corresponding NamedDecl is. If it is, mark this reference as spelled. - const auto *IdentifierToken = - spelledIdentifierTouching(LocAndRole.Loc, Tokens); - DeclarationName Name = DeclAndRef.first->getDeclName(); - const auto NameKind = Name.getNameKind(); - bool IsTargetKind = NameKind == DeclarationName::Identifier || - NameKind == DeclarationName::CXXConstructorName; - bool Spelled = IdentifierToken && IsTargetKind && - Name.getAsString() == IdentifierToken->text(SM); - CollectRef(ID, LocAndRole, Spelled); - } - } - } - - ReferencedDecls.clear(); - ReferencedMacros.clear(); - DeclRefs.clear(); + ReferencedSymbols.clear(); IncludeFiles.clear(); } @@ -1004,5 +960,38 @@ return I.first->second; } +SymbolID SymbolCollector::getSymbolIDCached(const Decl *D) { + auto It = DeclToIDCache.try_emplace(D, SymbolID{}); + if (It.second) + It.first->second = getSymbolID(D); + return It.first->second; +} + +SymbolID SymbolCollector::getSymbolIDCached(const llvm::StringRef MacroName, + const MacroInfo *MI, + const SourceManager &SM) { + auto It = MacroToIDCache.try_emplace(MI, SymbolID{}); + if (It.second) + It.first->second = getSymbolID(MacroName, MI, SM); + return It.first->second; +} + +void SymbolCollector::addRef(SymbolID ID, SourceLocation RefLoc, FileID FID, + index::SymbolRoleSet Roles, const Decl *Container, + bool Spelled) { + const auto &SM = ASTCtx->getSourceManager(); + // FIXME: use the result to filter out references. + shouldIndexFile(FID); + if (const auto *FE = SM.getFileEntryForID(FID)) { + auto Range = getTokenRange(RefLoc, SM, ASTCtx->getLangOpts()); + Ref R; + R.Location.Start = Range.first; + R.Location.End = Range.second; + R.Location.FileURI = HeaderFileURIs->toURI(FE).c_str(); + R.Kind = toRefKind(Roles, Spelled); + R.Container = getSymbolIDCached(Container); + Refs.insert(ID, R); + } +} } // namespace clangd } // namespace clang