16#include "clang-include-cleaner/Analysis.h"
17#include "clang-include-cleaner/IncludeSpeller.h"
18#include "clang-include-cleaner/Record.h"
19#include "clang-include-cleaner/Types.h"
26#include "clang/AST/Decl.h"
27#include "clang/AST/DeclBase.h"
28#include "clang/AST/DeclCXX.h"
29#include "clang/AST/DeclObjC.h"
30#include "clang/AST/DeclTemplate.h"
31#include "clang/AST/DeclarationName.h"
32#include "clang/AST/Expr.h"
33#include "clang/Basic/FileEntry.h"
34#include "clang/Basic/LangOptions.h"
35#include "clang/Basic/SourceLocation.h"
36#include "clang/Basic/SourceManager.h"
37#include "clang/Index/IndexSymbol.h"
38#include "clang/Lex/Preprocessor.h"
39#include "clang/Lex/Token.h"
40#include "clang/Tooling/Inclusions/HeaderAnalysis.h"
41#include "clang/Tooling/Inclusions/StandardLibrary.h"
42#include "llvm/ADT/ArrayRef.h"
43#include "llvm/ADT/DenseMap.h"
44#include "llvm/ADT/SmallVector.h"
45#include "llvm/ADT/StringRef.h"
46#include "llvm/Support/Casting.h"
47#include "llvm/Support/ErrorHandling.h"
48#include "llvm/Support/Path.h"
61const NamedDecl &getTemplateOrThis(
const NamedDecl &ND) {
62 if (
auto *T = ND.getDescribedTemplate())
71bool isPrivateProtoDecl(
const NamedDecl &ND) {
72 const auto &SM = ND.getASTContext().getSourceManager();
77 if (ND.getIdentifier() ==
nullptr)
79 auto Name = ND.getIdentifier()->getName();
81 if (Name.contains(
"_internal_"))
105 if (ND.getDeclContext()->isNamespace()) {
109 Name.consume_back(
"_descriptor");
110 Name.consume_back(
"_IsValid");
111 Name.consume_back(
"_Name");
112 Name.consume_back(
"_Parse");
113 Name.consume_back(
"_MIN");
114 Name.consume_back(
"_MAX");
115 Name.consume_back(
"_ARRAYSIZE");
116 return Name.contains(
'_');
124 if (llvm::isa<EnumConstantDecl>(&ND)) {
125 auto *DC = llvm::cast<EnumDecl>(ND.getDeclContext());
126 if (!DC || !DC->getIdentifier())
128 auto CtxName = DC->getIdentifier()->getName();
129 return !CtxName.empty() && Name.consume_front(CtxName) &&
130 Name.consume_front(
"_");
142 using SK = index::SymbolKind;
153 case SK::EnumConstant:
164std::pair<SymbolLocation::Position, SymbolLocation::Position>
165getTokenRange(SourceLocation TokLoc,
const SourceManager &SM,
166 const LangOptions &LangOpts) {
167 auto CreatePosition = [&SM](SourceLocation Loc) {
171 Pos.setColumn(LSPLoc.character);
175 auto TokenLength = clang::Lexer::MeasureTokenLength(TokLoc, SM, LangOpts);
176 return {CreatePosition(TokLoc),
177 CreatePosition(TokLoc.getLocWithOffset(TokenLength))};
185bool isPreferredDeclaration(
const NamedDecl &ND, index::SymbolRoleSet Roles) {
186 const auto &SM = ND.getASTContext().getSourceManager();
187 if (isa<TagDecl>(ND))
188 return (Roles &
static_cast<unsigned>(index::SymbolRole::Definition)) &&
190 if (
const auto *ID = dyn_cast<ObjCInterfaceDecl>(&ND))
191 return ID->isThisDeclarationADefinition();
192 if (
const auto *PD = dyn_cast<ObjCProtocolDecl>(&ND))
193 return PD->isThisDeclarationADefinition();
197RefKind toRefKind(index::SymbolRoleSet Roles,
bool Spelled =
false) {
199 if (Roles &
static_cast<unsigned>(index::SymbolRole::Declaration))
201 if (Roles &
static_cast<unsigned>(index::SymbolRole::Definition))
203 if (Roles &
static_cast<unsigned>(index::SymbolRole::Reference))
210std::optional<RelationKind> indexableRelation(
const index::SymbolRelation &R) {
211 if (R.Roles &
static_cast<unsigned>(index::SymbolRole::RelationBaseOf))
213 if (R.Roles &
static_cast<unsigned>(index::SymbolRole::RelationOverrideOf))
219bool isSpelled(SourceLocation Loc,
const NamedDecl &ND) {
220 auto Name = ND.getDeclName();
221 const auto NameKind = Name.getNameKind();
222 if (NameKind != DeclarationName::Identifier &&
223 NameKind != DeclarationName::CXXConstructorName &&
224 NameKind != DeclarationName::ObjCZeroArgSelector &&
225 NameKind != DeclarationName::ObjCOneArgSelector &&
226 NameKind != DeclarationName::ObjCMultiArgSelector)
228 const auto &
AST = ND.getASTContext();
229 const auto &SM =
AST.getSourceManager();
230 const auto &LO =
AST.getLangOpts();
232 if (clang::Lexer::getRawToken(Loc, Tok, SM, LO))
234 auto TokSpelling = clang::Lexer::getSpelling(Tok, SM, LO);
235 if (
const auto *MD = dyn_cast<ObjCMethodDecl>(&ND))
236 return TokSpelling == MD->getSelector().getNameForSlot(0);
237 return TokSpelling == Name.getAsString();
245 struct FrameworkUmbrellaSpelling {
247 std::optional<std::string> PublicHeader;
250 std::optional<std::string> PrivateHeader;
256 const SourceManager &SM;
257 const include_cleaner::PragmaIncludes *PI;
258 llvm::StringRef FallbackDir;
259 llvm::DenseMap<const FileEntry *, const std::string *> CacheFEToURI;
260 llvm::StringMap<std::string> CachePathToURI;
261 llvm::DenseMap<FileID, llvm::StringRef> CacheFIDToInclude;
262 llvm::StringMap<std::string> CachePathToFrameworkSpelling;
263 llvm::StringMap<FrameworkUmbrellaSpelling>
264 CacheFrameworkToUmbrellaHeaderSpelling;
269 : PP(PP), SM(SM), PI(Opts.PragmaIncludes), FallbackDir(Opts.FallbackDir) {
274 const std::string &
toURI(
const FileEntryRef FE) {
275 auto R = CacheFEToURI.try_emplace(FE);
278 R.first->second = &toURIInternal(CanonPath ? *CanonPath : FE.getName());
280 return *R.first->second;
287 if (
auto File = SM.getFileManager().getFileRef(
Path))
289 return toURIInternal(
Path);
297 auto R = CacheFIDToInclude.try_emplace(FID);
299 R.first->second = getIncludeHeaderUncached(FID);
300 return R.first->second;
312 auto Canonical = SysHeaderMapping.
mapHeader(HeaderPath);
313 if (Canonical.empty())
316 assert(Canonical.starts_with(
"<") || Canonical.starts_with(
"\""));
323 const std::string &toURIInternal(llvm::StringRef
Path) {
324 auto R = CachePathToURI.try_emplace(
Path);
326 llvm::SmallString<256> AbsPath =
Path;
327 if (!llvm::sys::path::is_absolute(AbsPath) && !FallbackDir.empty())
328 llvm::sys::path::make_absolute(FallbackDir, AbsPath);
329 assert(llvm::sys::path::is_absolute(AbsPath) &&
330 "If the VFS can't make paths absolute, a FallbackDir must be "
332 llvm::sys::path::remove_dots(AbsPath,
true);
335 return R.first->second;
338 struct FrameworkHeaderPath {
340 llvm::StringRef FrameworkParentDir;
342 llvm::StringRef FrameworkName;
345 llvm::StringRef HeaderSubpath;
347 bool IsPrivateHeader;
350 std::optional<FrameworkHeaderPath>
351 splitFrameworkHeaderPath(llvm::StringRef
Path) {
352 using namespace llvm::sys;
353 path::reverse_iterator I = path::rbegin(
Path);
354 path::reverse_iterator Prev = I;
355 path::reverse_iterator E = path::rend(
Path);
356 FrameworkHeaderPath HeaderPath;
358 if (*I ==
"Headers" || *I ==
"PrivateHeaders") {
359 HeaderPath.HeaderSubpath =
Path.substr(Prev - E);
360 HeaderPath.IsPrivateHeader = *I ==
"PrivateHeaders";
363 HeaderPath.FrameworkName = *I;
364 if (!HeaderPath.FrameworkName.consume_back(
".framework"))
366 HeaderPath.FrameworkParentDir =
Path.substr(0, I - E);
381 std::optional<std::string>
382 getFrameworkUmbrellaSpelling(
const HeaderSearch &HS,
383 FrameworkHeaderPath &HeaderPath) {
384 StringRef Framework = HeaderPath.FrameworkName;
385 auto Res = CacheFrameworkToUmbrellaHeaderSpelling.try_emplace(Framework);
386 auto *CachedSpelling = &Res.first->second;
388 return HeaderPath.IsPrivateHeader ? CachedSpelling->PrivateHeader
389 : CachedSpelling->PublicHeader;
391 SmallString<256> UmbrellaPath(HeaderPath.FrameworkParentDir);
392 llvm::sys::path::append(UmbrellaPath, Framework +
".framework",
"Headers",
395 llvm::vfs::Status Status;
396 auto StatErr = HS.getFileMgr().getNoncachedStatValue(UmbrellaPath, Status);
398 CachedSpelling->PublicHeader = llvm::formatv(
"<{0}/{0}.h>", Framework);
400 UmbrellaPath = HeaderPath.FrameworkParentDir;
401 llvm::sys::path::append(UmbrellaPath, Framework +
".framework",
402 "PrivateHeaders", Framework +
"_Private.h");
404 StatErr = HS.getFileMgr().getNoncachedStatValue(UmbrellaPath, Status);
406 CachedSpelling->PrivateHeader =
407 llvm::formatv(
"<{0}/{0}_Private.h>", Framework);
409 return HeaderPath.IsPrivateHeader ? CachedSpelling->PrivateHeader
410 : CachedSpelling->PublicHeader;
417 std::optional<llvm::StringRef>
418 getFrameworkHeaderIncludeSpelling(FileEntryRef FE, HeaderSearch &HS) {
419 auto Res = CachePathToFrameworkSpelling.try_emplace(FE.getName());
420 auto *CachedHeaderSpelling = &Res.first->second;
422 return llvm::StringRef(*CachedHeaderSpelling);
424 auto HeaderPath = splitFrameworkHeaderPath(FE.getName());
428 CachePathToFrameworkSpelling.erase(Res.first);
431 if (
auto UmbrellaSpelling =
432 getFrameworkUmbrellaSpelling(HS, *HeaderPath)) {
433 *CachedHeaderSpelling = *UmbrellaSpelling;
434 return llvm::StringRef(*CachedHeaderSpelling);
437 *CachedHeaderSpelling =
438 llvm::formatv(
"<{0}/{1}>", HeaderPath->FrameworkName,
439 HeaderPath->HeaderSubpath)
441 return llvm::StringRef(*CachedHeaderSpelling);
444 llvm::StringRef getIncludeHeaderUncached(FileID FID) {
445 const auto FE = SM.getFileEntryRefForID(FID);
446 if (!FE || FE->getName().empty())
449 if (
auto Verbatim = PI->getPublic(*FE); !Verbatim.empty())
452 llvm::StringRef Filename = FE->getName();
453 if (
auto Canonical =
mapCanonical(Filename); !Canonical.empty())
458 auto &HS = PP->getHeaderSearchInfo();
459 if (
auto Spelling = getFrameworkHeaderIncludeSpelling(*FE, HS))
462 if (!tooling::isSelfContainedHeader(*FE, PP->getSourceManager(),
463 PP->getHeaderSearchInfo())) {
466 if (Filename.ends_with(
".inc") || Filename.ends_with(
".def"))
468 return getIncludeHeaderUncached(SM.getFileID(SM.getIncludeLoc(FID)));
478std::optional<SymbolLocation>
479SymbolCollector::getTokenLocation(SourceLocation TokLoc) {
480 const auto &SM = ASTCtx->getSourceManager();
481 const auto FE = SM.getFileEntryRefForID(SM.getFileID(TokLoc));
485 SymbolLocation Result;
486 Result.FileURI = HeaderFileURIs->toURI(*FE).c_str();
487 auto Range = getTokenRange(TokLoc, SM, ASTCtx->getLangOpts());
488 Result.Start = Range.first;
489 Result.End = Range.second;
499 HeaderFileURIs = std::make_unique<HeaderFileURICache>(
500 this->PP, ASTCtx->getSourceManager(), Opts);
501 CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
503 std::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
507 const ASTContext &ASTCtx,
509 bool IsMainFileOnly) {
511 if (ND.getDeclName().isEmpty())
515 if (IsMainFileOnly && !Opts.CollectMainFileSymbols)
519 if (!IsMainFileOnly && ND.isInAnonymousNamespace())
523 if (index::isFunctionLocalSymbol(&ND))
524 return isa<RecordDecl>(ND) ||
525 (ND.isCXXInstanceMember() && ND.isFunctionOrFunctionTemplate());
531 const auto *DeclCtx = ND.getDeclContext();
532 switch (DeclCtx->getDeclKind()) {
533 case Decl::TranslationUnit:
534 case Decl::Namespace:
535 case Decl::LinkageSpec:
537 case Decl::ObjCProtocol:
538 case Decl::ObjCInterface:
539 case Decl::ObjCCategory:
540 case Decl::ObjCCategoryImpl:
541 case Decl::ObjCImplementation:
546 if (!isa<RecordDecl>(DeclCtx))
551 if (isPrivateProtoDecl(ND))
555 if (!Opts.CollectReserved &&
557 ASTCtx.getSourceManager().isInSystemHeader(ND.getLocation()) &&
558 !ASTCtx.getSourceManager()
559 .getFilename(ND.getLocation())
560 .ends_with(
"intrin.h"))
570 const auto *ND = dyn_cast<NamedDecl>(Enclosing);
574 Enclosing = dyn_cast_or_null<Decl>(Enclosing->getDeclContext());
579SmallVector<const CXXConstructorDecl *, 1>
580SymbolCollector::findIndirectConstructors(
const Decl *D) {
581 auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D);
582 if (FD ==
nullptr || !FD->isTemplateInstantiation())
584 if (
auto Entry = ForwardingToConstructorCache.find(FD);
585 Entry != ForwardingToConstructorCache.end())
586 return Entry->getSecond();
587 if (
auto *PT = FD->getPrimaryTemplate();
591 SmallVector<const CXXConstructorDecl *, 1> FoundConstructors =
593 auto Iter = ForwardingToConstructorCache.try_emplace(
594 FD, std::move(FoundConstructors));
595 return Iter.first->getSecond();
600 const Decl *D, index::SymbolRoleSet Roles,
601 llvm::ArrayRef<index::SymbolRelation> Relations, SourceLocation Loc,
602 index::IndexDataConsumer::ASTNodeInfo
ASTNode) {
603 assert(ASTCtx && PP && HeaderFileURIs);
604 assert(CompletionAllocator && CompletionTUInfo);
609 if (D->getLocation().isInvalid())
614 if ((
ASTNode.OrigD->getFriendObjectKind() !=
615 Decl::FriendObjectKind::FOK_None) &&
616 !(Roles &
static_cast<unsigned>(index::SymbolRole::Definition)))
621 if (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None)
622 D = CanonicalDecls.try_emplace(D,
ASTNode.OrigD).first->second;
625 bool DeclIsCanonical =
false;
628 if (
const auto *IID = dyn_cast<ObjCImplementationDecl>(D)) {
629 DeclIsCanonical =
true;
630 if (
const auto *CID = IID->getClassInterface())
631 if (
const auto *DD = CID->getDefinition())
632 if (!DD->isImplicitInterfaceDecl())
637 if (
const auto *CID = dyn_cast<ObjCCategoryImplDecl>(D)) {
638 DeclIsCanonical =
true;
639 if (
const auto *CD = CID->getCategoryDecl())
642 const NamedDecl *ND = dyn_cast<NamedDecl>(D);
646 auto ID = getSymbolIDCached(ND);
652 auto &SM = ASTCtx->getSourceManager();
653 if (Opts.CountReferences &&
654 (Roles &
static_cast<unsigned>(index::SymbolRole::Reference)) &&
655 SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID())
656 ReferencedSymbols.insert(ID);
661 auto CheckIsMainFileOnly = [&](
const NamedDecl *Decl) {
662 return SM.isWrittenInMainFile(SM.getExpansionLoc(Decl->getBeginLoc())) &&
663 !
isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
664 ASTCtx->getLangOpts());
666 bool IsMainFileOnly = CheckIsMainFileOnly(ND);
668 if (
ASTNode.OrigD->isImplicit() ||
676 processRelations(*ND, ID, Relations);
678 bool CollectRef =
static_cast<bool>(Opts.RefFilter & toRefKind(Roles));
684 (!IsMainFileOnly || Opts.CollectMainFileRefs ||
685 ND->isExternallyVisible()) &&
686 !isa<NamespaceDecl>(ND)) {
687 auto FileLoc = SM.getFileLoc(Loc);
688 auto FID = SM.getFileID(FileLoc);
689 if (Opts.RefsInHeaders || FID == SM.getMainFileID()) {
691 addRef(ID, SymbolRef{FileLoc, FID, Roles, index::getSymbolInfo(ND).Kind,
692 Container, isSpelled(FileLoc, *ND)});
698 if (
auto ConstructorID = getSymbolIDCached(
Constructor))
699 addRef(ConstructorID,
700 SymbolRef{FileLoc, FID, Roles,
707 if (!(Roles & (
static_cast<unsigned>(index::SymbolRole::Declaration) |
708 static_cast<unsigned>(index::SymbolRole::Definition))))
714 auto *OriginalDecl = dyn_cast<NamedDecl>(
ASTNode.OrigD);
718 const Symbol *BasicSymbol = Symbols.find(ID);
719 bool SkipDocCheckInDef =
false;
720 if (isPreferredDeclaration(*OriginalDecl, Roles)) {
725 BasicSymbol = addDeclaration(*OriginalDecl, std::move(ID), IsMainFileOnly);
726 SkipDocCheckInDef =
true;
727 }
else if (!BasicSymbol || DeclIsCanonical) {
728 BasicSymbol = addDeclaration(*ND, std::move(ID), IsMainFileOnly);
729 SkipDocCheckInDef =
true;
732 if (Roles &
static_cast<unsigned>(index::SymbolRole::Definition))
733 addDefinition(*OriginalDecl, *BasicSymbol, SkipDocCheckInDef);
739 assert(HeaderFileURIs && PP);
740 const auto &SM = PP->getSourceManager();
741 const auto MainFileEntryRef = SM.getFileEntryRefForID(SM.getMainFileID());
742 assert(MainFileEntryRef);
744 const std::string &MainFileURI = HeaderFileURIs->toURI(*MainFileEntryRef);
746 for (
const auto &IDToRefs : MacroRefsToIndex.
MacroRefs) {
747 for (
const auto &MacroRef : IDToRefs.second) {
748 const auto &SR = MacroRef.toSourceRange(SM);
750 bool IsDefinition = MacroRef.IsDefinition;
758 Refs.insert(IDToRefs.first, R);
761 S.
ID = IDToRefs.first;
763 S.
SymInfo.Kind = index::SymbolKind::Macro;
764 S.
SymInfo.SubKind = index::SymbolSubKind::None;
765 S.
SymInfo.Properties = index::SymbolPropertySet();
766 S.
SymInfo.Lang = index::SymbolLanguage::C;
771 if (!HeaderFileURIs->getIncludeHeader(SM.getMainFileID()).empty()) {
783 index::SymbolRoleSet Roles,
784 SourceLocation Loc) {
787 if (MI->isBuiltinMacro())
790 const auto &SM = PP->getSourceManager();
791 auto DefLoc = MI->getDefinitionLoc();
794 if (SM.isWrittenInBuiltinFile(DefLoc) ||
795 SM.isWrittenInCommandLineFile(DefLoc) ||
796 Name->getName() ==
"__GCC_HAVE_DWARF2_CFI_ASM")
799 auto ID = getSymbolIDCached(Name->getName(), MI, SM);
803 auto SpellingLoc = SM.getSpellingLoc(Loc);
804 bool IsMainFileOnly =
805 SM.isInMainFile(SM.getExpansionLoc(DefLoc)) &&
806 !
isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
807 ASTCtx->getLangOpts());
809 if ((
static_cast<unsigned>(Opts.RefFilter) & Roles) && !IsMainFileOnly &&
810 (Opts.RefsInHeaders || SM.getFileID(SpellingLoc) == SM.getMainFileID())) {
815 SymbolRef{Loc, SM.getFileID(Loc), Roles, index::SymbolKind::Macro,
821 if (!Opts.CollectMacro)
825 if (IsMainFileOnly && !Opts.CollectMainFileSymbols)
831 if (Opts.CountReferences &&
832 (Roles &
static_cast<unsigned>(index::SymbolRole::Reference)) &&
833 SM.getFileID(SpellingLoc) == SM.getMainFileID())
834 ReferencedSymbols.insert(ID);
838 if (!(Roles &
static_cast<unsigned>(index::SymbolRole::Declaration) ||
839 Roles &
static_cast<unsigned>(index::SymbolRole::Definition)))
843 if (Symbols.find(ID) !=
nullptr)
847 S.
ID = std::move(ID);
848 S.
Name = Name->getName();
849 if (!IsMainFileOnly) {
853 S.
SymInfo = index::getSymbolInfoForMacro(*MI);
857 if (
auto DeclLoc = getTokenLocation(DefLoc))
860 CodeCompletionResult SymbolCompletion(Name);
861 const auto *CCS = SymbolCompletion.CreateCodeCompletionStringForMacro(
862 *PP, *CompletionAllocator, *CompletionTUInfo);
863 std::string Signature;
864 std::string SnippetSuffix;
865 getSignature(*CCS, &Signature, &SnippetSuffix, SymbolCompletion.Kind,
866 SymbolCompletion.CursorKind);
870 IndexedMacros.insert(Name);
872 setIncludeLocation(S, DefLoc, include_cleaner::Macro{Name, DefLoc});
877void SymbolCollector::processRelations(
878 const NamedDecl &ND,
const SymbolID &ID,
879 ArrayRef<index::SymbolRelation> Relations) {
880 for (
const auto &R : Relations) {
881 auto RKind = indexableRelation(R);
884 const Decl *
Object = R.RelatedSymbol;
886 auto ObjectID = getSymbolIDCached(
Object);
900 this->Relations.insert({ID, *RKind, ObjectID});
902 this->Relations.insert({ObjectID, *RKind, ID});
906void SymbolCollector::setIncludeLocation(
const Symbol &S, SourceLocation DefLoc,
907 const include_cleaner::Symbol &Sym) {
908 const auto &SM = PP->getSourceManager();
909 if (!Opts.CollectIncludePath ||
915 if (FileID FID = SM.getDecomposedExpansionLoc(DefLoc).first; FID.isValid())
916 IncludeFiles[S.ID] = FID;
921 SymbolProviders[S.ID] =
922 include_cleaner::headersForSymbol(Sym, *PP, Opts.PragmaIncludes);
926 tooling::stdlib::Lang Lang = tooling::stdlib::Lang::CXX;
928 Lang = tooling::stdlib::Lang::C;
929 else if(!LangOpts.CPlusPlus)
932 if (S->
Scope ==
"std::" && S->
Name ==
"move") {
935 return "<algorithm>";
938 if (
auto StdSym = tooling::stdlib::Symbol::named(S->
Scope, S->
Name, Lang))
939 if (
auto Header = StdSym->header())
940 return Header->name();
946 for (
const auto &ID : ReferencedSymbols) {
947 if (
const auto *S = Symbols.find(ID)) {
955 if (Opts.CollectMacro) {
958 for (
const IdentifierInfo *II : IndexedMacros) {
959 if (
const auto *MI = PP->getMacroDefinition(II).getMacroInfo())
961 getSymbolIDCached(II->getName(), MI, PP->getSourceManager()))
962 if (MI->isUsedForHeaderGuard())
966 llvm::DenseMap<FileID, bool> FileToContainsImportsOrObjC;
967 llvm::DenseMap<include_cleaner::Header, std::string> HeaderSpelling;
970 for (
const auto &[SID, Providers] : SymbolProviders) {
971 const Symbol *S = Symbols.find(SID);
975 FileID FID = IncludeFiles.lookup(SID);
978 auto CollectDirectives = shouldCollectIncludePath(S->
SymInfo.Kind);
983 auto [It, Inserted] = FileToContainsImportsOrObjC.try_emplace(FID);
985 It->second = FilesWithObjCConstructs.contains(FID) ||
986 tooling::codeContainsImports(
987 ASTCtx->getSourceManager().getBufferData(FID));
997 llvm::StringRef IncludeHeader =
getStdHeader(S, ASTCtx->getLangOpts());
998 if (IncludeHeader.empty())
999 IncludeHeader = HeaderFileURIs->getIncludeHeader(FID);
1001 if (!IncludeHeader.empty()) {
1004 Symbols.insert(NewSym);
1019 auto SelfContainedProvider =
1020 [
this](llvm::ArrayRef<include_cleaner::Header> Providers)
1021 -> std::optional<include_cleaner::Header> {
1022 for (
const auto &H : Providers) {
1023 if (H.kind() != include_cleaner::Header::Physical)
1025 if (tooling::isSelfContainedHeader(H.physical(), PP->getSourceManager(),
1026 PP->getHeaderSearchInfo()))
1029 return std::nullopt;
1031 const auto OptionalProvider = SelfContainedProvider(Providers);
1032 if (!OptionalProvider)
1034 const auto &H = *OptionalProvider;
1035 const auto [SpellingIt, Inserted] = HeaderSpelling.try_emplace(H);
1037 auto &SM = ASTCtx->getSourceManager();
1038 if (H.kind() == include_cleaner::Header::Kind::Physical) {
1041 if (
auto Canonical =
1042 HeaderFileURIs->mapCanonical(H.physical().getName());
1044 SpellingIt->second = Canonical;
1047 else if (tooling::isSelfContainedHeader(H.physical(), SM,
1048 PP->getHeaderSearchInfo()))
1049 SpellingIt->second =
1050 HeaderFileURIs->toURI(H.physical());
1052 SpellingIt->second = include_cleaner::spellHeader(
1053 {H, PP->getHeaderSearchInfo(),
1054 SM.getFileEntryForID(SM.getMainFileID())});
1058 if (!SpellingIt->second.empty()) {
1060 NewSym.
IncludeHeaders.push_back({SpellingIt->second, 1, Directives});
1061 Symbols.insert(NewSym);
1065 ReferencedSymbols.clear();
1066 IncludeFiles.clear();
1067 SymbolProviders.clear();
1068 FilesWithObjCConstructs.clear();
1071const Symbol *SymbolCollector::addDeclaration(
const NamedDecl &ND,
SymbolID ID,
1072 bool IsMainFileOnly) {
1073 auto &Ctx = ND.getASTContext();
1074 auto &SM = Ctx.getSourceManager();
1077 S.
ID = std::move(ID);
1090 if (!IsMainFileOnly)
1092 S.
SymInfo = index::getSymbolInfo(&ND);
1094 assert(Loc.isValid() &&
"Invalid source location for NamedDecl");
1096 auto FID = SM.getFileID(Loc);
1098 if (
auto DeclLoc = getTokenLocation(Loc))
1102 if (ND.getAvailability() == AR_Deprecated)
1107 assert(ASTCtx && PP &&
"ASTContext and Preprocessor must be set.");
1109 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
1110 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
1111 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
1114 std::string DocComment;
1115 std::string Documentation;
1117 if (!AlreadyHasDoc) {
1122 const auto UpdateDoc = [&] {
1123 if (!AlreadyHasDoc) {
1124 if (!DocComment.empty())
1130 if (Opts.StoreAllDocumentation)
1133 return Symbols.find(S.
ID);
1136 std::string Signature;
1137 std::string SnippetSuffix;
1138 getSignature(*CCS, &Signature, &SnippetSuffix, SymbolCompletion.Kind,
1139 SymbolCompletion.CursorKind);
1145 std::optional<OpaqueType> TypeStorage;
1149 S.
Type = TypeStorage->raw();
1153 setIncludeLocation(S, ND.getLocation(), include_cleaner::Symbol{ND});
1154 if (S.
SymInfo.Lang == index::SymbolLanguage::ObjC)
1155 FilesWithObjCConstructs.insert(FID);
1156 return Symbols.find(S.
ID);
1159void SymbolCollector::addDefinition(
const NamedDecl &ND,
const Symbol &DeclSym,
1160 bool SkipDocCheck) {
1161 if (DeclSym.Definition)
1163 const auto &SM = ND.getASTContext().getSourceManager();
1166 auto DefLoc = getTokenLocation(Loc);
1174 S.Definition = *DefLoc;
1176 std::string DocComment;
1177 std::string Documentation;
1179 (llvm::isa<FunctionDecl>(ND) || llvm::isa<CXXMethodDecl>(ND))) {
1180 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
1181 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
1182 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
1185 DocComment =
getDocComment(ND.getASTContext(), SymbolCompletion,
1187 if (!S.Documentation.empty())
1188 Documentation = S.Documentation.str() +
'\n' + DocComment;
1191 if (!DocComment.empty())
1193 S.Documentation = Documentation;
1200 if (!Opts.FileFilter)
1202 auto I = FilesToIndexCache.try_emplace(FID);
1204 I.first->second = Opts.FileFilter(ASTCtx->getSourceManager(), FID);
1205 return I.first->second;
1209 using SK = index::SymbolKind;
1210 return Kind == SK::Function || Kind == SK::InstanceMethod ||
1211 Kind == SK::ClassMethod || Kind == SK::StaticMethod ||
1212 Kind == SK::Constructor || Kind == SK::Destructor ||
1213 Kind == SK::ConversionFunction;
1216void SymbolCollector::addRef(SymbolID ID,
const SymbolRef &SR) {
1217 const auto &SM = ASTCtx->getSourceManager();
1220 if (
const auto FE = SM.getFileEntryRefForID(SR.FID)) {
1221 auto Range = getTokenRange(SR.Loc, SM, ASTCtx->getLangOpts());
1223 R.Location.Start = Range.first;
1224 R.Location.End = Range.second;
1225 R.Location.FileURI = HeaderFileURIs->toURI(*FE).c_str();
1226 R.Kind = toRefKind(SR.Roles, SR.Spelled);
1230 R.Container = getSymbolIDCached(SR.Container);
1235SymbolID SymbolCollector::getSymbolIDCached(
const Decl *D) {
1236 auto It = DeclToIDCache.try_emplace(D);
1239 return It.first->second;
1242SymbolID SymbolCollector::getSymbolIDCached(
const llvm::StringRef MacroName,
1243 const MacroInfo *MI,
1244 const SourceManager &SM) {
1245 auto It = MacroToIDCache.try_emplace(MI);
1247 It.first->second =
getSymbolID(MacroName, MI, SM);
1248 return It.first->second;
Maps a definition location onto an include file, based on a set of filename rules.
void addSystemHeadersMapping(const LangOptions &Language)
Adds mapping for system headers and some special symbols (e.g.
llvm::StringRef mapHeader(llvm::StringRef HeaderPath) const
Returns the overridden verbatim spelling for files in Header that can be directly included (i....
static std::optional< OpaqueType > fromCompletionResult(ASTContext &Ctx, const CodeCompletionResult &R)
Create a type from a code completion result.
void insert(const SymbolID &ID, const Ref &S)
Adds a ref to the slab. Deep copy: Strings will be owned by the slab.
bool shouldIndexFile(FileID FID)
Returns true if we are interested in references and declarations from FID.
static bool shouldCollectSymbol(const NamedDecl &ND, const ASTContext &ASTCtx, const Options &Opts, bool IsMainFileSymbol)
Returns true is ND should be collected.
static const Decl * getRefContainer(const Decl *Enclosing, const SymbolCollector::Options &Opts)
SymbolCollector(Options Opts)
bool handleDeclOccurrence(const Decl *D, index::SymbolRoleSet Roles, ArrayRef< index::SymbolRelation > Relations, SourceLocation Loc, index::IndexDataConsumer::ASTNodeInfo ASTNode) override
void handleMacros(const MainFileMacros &MacroRefsToIndex)
void initialize(ASTContext &Ctx) override
bool handleMacroOccurrence(const IdentifierInfo *Name, const MacroInfo *MI, index::SymbolRoleSet Roles, SourceLocation Loc) override
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
std::string printTemplateSpecializationArgs(const NamedDecl &ND)
Prints template arguments of a decl as written in the source code, including enclosing '<' and '>',...
std::pair< StringRef, StringRef > splitQualifiedName(StringRef QName)
bool isLikelyForwardingFunction(const FunctionTemplateDecl *FT)
Heuristic that checks if FT is likely to be forwarding a parameter pack to another function (e....
SymbolID getSymbolID(const Decl *D)
Gets the symbol ID for a declaration. Returned SymbolID might be null.
std::string formatDocumentation(const CodeCompletionString &CCS, llvm::StringRef DocComment)
Assembles formatted documentation for a completion result.
Range halfOpenToRange(const SourceManager &SM, CharSourceRange R)
static bool refIsCall(index::SymbolKind Kind)
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
SourceLocation nameLocation(const clang::Decl &D, const SourceManager &SM)
Find the source location of the identifier for D.
std::string getReturnType(const CodeCompletionString &CCS)
Gets detail to be used as the detail field in an LSP completion item.
Position sourceLocToPosition(const SourceManager &SM, SourceLocation Loc)
Turn a SourceLocation into a [line, column] pair.
std::optional< std::string > getCanonicalPath(const FileEntryRef F, FileManager &FileMgr)
Get the canonical path of F.
bool hasReservedName(const Decl &D)
Returns true if this is a NamedDecl with a reserved name.
SmallVector< const CXXConstructorDecl *, 1 > searchConstructorsInForwardingFunction(const FunctionDecl *FD)
Only call if FD is a likely forwarding function.
llvm::StringRef toSourceCode(const SourceManager &SM, SourceRange R)
Returns the source code covered by the source range.
RefKind
Describes the kind of a cross-reference.
void getSignature(const CodeCompletionString &CCS, std::string *Signature, std::string *Snippet, CodeCompletionResult::ResultKind ResultKind, CXCursorKind CursorKind, bool IncludeFunctionArguments, std::string *RequiredQualifiers)
Formats the signature for an item, as a display string and snippet.
bool isImplementationDetail(const Decl *D)
Returns true if the declaration is considered implementation detail based on heuristics.
std::string Path
A typedef to represent a file path.
bool hasReservedScope(const DeclContext &DC)
Returns true if this scope would be written with a reserved name.
std::string printQualifiedName(const NamedDecl &ND)
Returns the qualified name of ND.
bool isProtoFile(SourceLocation Loc, const SourceManager &SM)
Returns true if the given location is in a generated protobuf file.
std::string getDocComment(const ASTContext &Ctx, const CodeCompletionResult &Result, bool CommentsFromHeaders)
Gets a minimally formatted documentation comment of Result, with comment markers stripped.
llvm::StringRef getStdHeader(const Symbol *S, const LangOptions &LangOpts)
bool isHeaderFile(llvm::StringRef FileName, std::optional< LangOptions > LangOpts)
Infers whether this is a header from the FileName and LangOpts (if presents).
bool isIndexedForCodeCompletion(const NamedDecl &ND, ASTContext &ASTCtx)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Simplified description of a clang AST node.
llvm::DenseMap< SymbolID, std::vector< MacroOccurrence > > MacroRefs
int line
Line position in a document (zero-based).
int character
Character offset on a line in a document (zero-based).
Position start
The range's start position.
Position end
The range's end position.
Represents a symbol occurrence in the source file.
SymbolLocation Location
The source location where the symbol is named.
void setColumn(uint32_t Column)
void setLine(uint32_t Line)
Position Start
The symbol range, using half-open range [Start, End).
The class presents a C++ symbol, e.g.
@ IndexedForCodeCompletion
Whether or not this symbol is meant to be used for the code completion.
@ Deprecated
Indicates if the symbol is deprecated.
@ ImplementationDetail
Symbol is an implementation detail.
@ HasDocComment
Symbol has an attached documentation comment.
@ VisibleOutsideFile
Symbol is visible to other files (not e.g. a static helper function).
@ Include
#include "header.h"
@ Import
#import "header.h"
llvm::StringRef Type
Raw representation of the OpaqueType of the symbol, used for scoring purposes.
llvm::StringRef Documentation
Documentation including comment for the symbol declaration.
index::SymbolInfo SymInfo
The symbol information, like symbol kind.
llvm::SmallVector< IncludeHeaderWithReferences, 1 > IncludeHeaders
One Symbol can potentially be included via different headers.
llvm::StringRef Name
The unqualified name of the symbol, e.g. "bar" (for ns::bar).
llvm::StringRef Scope
The containing namespace. e.g. "" (global), "ns::" (top-level namespace).
llvm::StringRef Signature
A brief description of the symbol that can be appended in the completion candidate list.
llvm::StringRef ReturnType
Type when this symbol is used in an expression.
llvm::StringRef TemplateSpecializationArgs
Argument list in human-readable format, will be displayed to help disambiguate between different spec...
SymbolLocation CanonicalDeclaration
The location of the preferred declaration of the symbol.
llvm::StringRef CompletionSnippetSuffix
What to insert when completing this symbol, after the symbol name.
SymbolID ID
The ID of the symbol.
SymbolOrigin Origin
Where this symbol came from. Usually an index provides a constant value.