17#include "clang-include-cleaner/Analysis.h"
18#include "clang-include-cleaner/IncludeSpeller.h"
19#include "clang-include-cleaner/Record.h"
20#include "clang-include-cleaner/Types.h"
27#include "clang/AST/Decl.h"
28#include "clang/AST/DeclBase.h"
29#include "clang/AST/DeclCXX.h"
30#include "clang/AST/DeclObjC.h"
31#include "clang/AST/DeclTemplate.h"
32#include "clang/AST/DeclarationName.h"
33#include "clang/AST/Expr.h"
34#include "clang/Basic/FileEntry.h"
35#include "clang/Basic/LangOptions.h"
36#include "clang/Basic/SourceLocation.h"
37#include "clang/Basic/SourceManager.h"
38#include "clang/Index/IndexSymbol.h"
39#include "clang/Lex/Preprocessor.h"
40#include "clang/Lex/Token.h"
41#include "clang/Tooling/Inclusions/HeaderAnalysis.h"
42#include "clang/Tooling/Inclusions/StandardLibrary.h"
43#include "llvm/ADT/ArrayRef.h"
44#include "llvm/ADT/DenseMap.h"
45#include "llvm/ADT/SmallVector.h"
46#include "llvm/ADT/StringRef.h"
47#include "llvm/Support/Casting.h"
48#include "llvm/Support/ErrorHandling.h"
49#include "llvm/Support/Path.h"
62const NamedDecl &getTemplateOrThis(
const NamedDecl &ND) {
63 if (
auto *T = ND.getDescribedTemplate())
72bool isPrivateProtoDecl(
const NamedDecl &ND) {
73 const auto &SM = ND.getASTContext().getSourceManager();
78 if (ND.getIdentifier() ==
nullptr)
80 auto Name = ND.getIdentifier()->getName();
82 if (Name.contains(
"_internal_"))
106 if (ND.getDeclContext()->isNamespace()) {
110 Name.consume_back(
"_descriptor");
111 Name.consume_back(
"_IsValid");
112 Name.consume_back(
"_Name");
113 Name.consume_back(
"_Parse");
114 Name.consume_back(
"_MIN");
115 Name.consume_back(
"_MAX");
116 Name.consume_back(
"_ARRAYSIZE");
117 return Name.contains(
'_');
125 if (llvm::isa<EnumConstantDecl>(&ND)) {
126 auto *DC = llvm::cast<EnumDecl>(ND.getDeclContext());
127 if (!DC || !DC->getIdentifier())
129 auto CtxName = DC->getIdentifier()->getName();
130 return !CtxName.empty() && Name.consume_front(CtxName) &&
131 Name.consume_front(
"_");
143 using SK = index::SymbolKind;
154 case SK::EnumConstant:
165std::pair<SymbolLocation::Position, SymbolLocation::Position>
166getTokenRange(SourceLocation TokLoc,
const SourceManager &SM,
167 const LangOptions &LangOpts) {
168 auto CreatePosition = [&SM](SourceLocation Loc) {
172 Pos.setColumn(LSPLoc.character);
176 auto TokenLength = clang::Lexer::MeasureTokenLength(TokLoc, SM, LangOpts);
177 return {CreatePosition(TokLoc),
178 CreatePosition(TokLoc.getLocWithOffset(TokenLength))};
186bool isPreferredDeclaration(
const NamedDecl &ND, index::SymbolRoleSet Roles) {
187 const auto &SM = ND.getASTContext().getSourceManager();
188 if (isa<TagDecl>(ND))
189 return (Roles &
static_cast<unsigned>(index::SymbolRole::Definition)) &&
191 if (
const auto *ID = dyn_cast<ObjCInterfaceDecl>(&ND))
192 return ID->isThisDeclarationADefinition();
193 if (
const auto *PD = dyn_cast<ObjCProtocolDecl>(&ND))
194 return PD->isThisDeclarationADefinition();
198RefKind toRefKind(index::SymbolRoleSet Roles,
bool Spelled =
false) {
200 if (Roles &
static_cast<unsigned>(index::SymbolRole::Declaration))
202 if (Roles &
static_cast<unsigned>(index::SymbolRole::Definition))
204 if (Roles &
static_cast<unsigned>(index::SymbolRole::Reference))
211std::optional<RelationKind> indexableRelation(
const index::SymbolRelation &R) {
212 if (R.Roles &
static_cast<unsigned>(index::SymbolRole::RelationBaseOf))
214 if (R.Roles &
static_cast<unsigned>(index::SymbolRole::RelationOverrideOf))
220bool isSpelled(SourceLocation Loc,
const NamedDecl &ND) {
221 auto Name = ND.getDeclName();
222 const auto NameKind = Name.getNameKind();
223 if (NameKind != DeclarationName::Identifier &&
224 NameKind != DeclarationName::CXXConstructorName &&
225 NameKind != DeclarationName::ObjCZeroArgSelector &&
226 NameKind != DeclarationName::ObjCOneArgSelector &&
227 NameKind != DeclarationName::ObjCMultiArgSelector)
229 const auto &
AST = ND.getASTContext();
230 const auto &SM =
AST.getSourceManager();
231 const auto &LO =
AST.getLangOpts();
233 if (clang::Lexer::getRawToken(Loc, Tok, SM, LO))
235 auto TokSpelling = clang::Lexer::getSpelling(Tok, SM, LO);
236 if (
const auto *
MD = dyn_cast<ObjCMethodDecl>(&ND))
237 return TokSpelling ==
MD->getSelector().getNameForSlot(0);
238 return TokSpelling == Name.getAsString();
246 struct FrameworkUmbrellaSpelling {
248 std::optional<std::string> PublicHeader;
251 std::optional<std::string> PrivateHeader;
257 const SourceManager &SM;
258 const include_cleaner::PragmaIncludes *PI;
259 llvm::StringRef FallbackDir;
260 llvm::DenseMap<const FileEntry *, const std::string *> CacheFEToURI;
261 llvm::StringMap<std::string> CachePathToURI;
262 llvm::DenseMap<FileID, llvm::StringRef> CacheFIDToInclude;
263 llvm::StringMap<std::string> CachePathToFrameworkSpelling;
264 llvm::StringMap<FrameworkUmbrellaSpelling>
265 CacheFrameworkToUmbrellaHeaderSpelling;
270 : PP(PP), SM(SM), PI(Opts.PragmaIncludes), FallbackDir(Opts.FallbackDir) {
275 const std::string &
toURI(
const FileEntryRef FE) {
276 auto R = CacheFEToURI.try_emplace(FE);
279 R.first->second = &toURIInternal(CanonPath ? *CanonPath : FE.getName());
281 return *R.first->second;
288 if (
auto File = SM.getFileManager().getFileRef(
Path))
290 return toURIInternal(
Path);
298 auto R = CacheFIDToInclude.try_emplace(FID);
300 R.first->second = getIncludeHeaderUncached(FID);
301 return R.first->second;
313 auto Canonical = SysHeaderMapping.
mapHeader(HeaderPath);
314 if (Canonical.empty())
317 assert(Canonical.starts_with(
"<") || Canonical.starts_with(
"\""));
324 const std::string &toURIInternal(llvm::StringRef
Path) {
325 auto R = CachePathToURI.try_emplace(
Path);
327 llvm::SmallString<256> AbsPath =
Path;
328 if (!llvm::sys::path::is_absolute(AbsPath) && !FallbackDir.empty())
329 llvm::sys::path::make_absolute(FallbackDir, AbsPath);
330 assert(llvm::sys::path::is_absolute(AbsPath) &&
331 "If the VFS can't make paths absolute, a FallbackDir must be "
333 llvm::sys::path::remove_dots(AbsPath,
true);
336 return R.first->second;
339 struct FrameworkHeaderPath {
341 llvm::StringRef FrameworkParentDir;
343 llvm::StringRef FrameworkName;
346 llvm::StringRef HeaderSubpath;
348 bool IsPrivateHeader;
351 std::optional<FrameworkHeaderPath>
352 splitFrameworkHeaderPath(llvm::StringRef
Path) {
353 using namespace llvm::sys;
354 path::reverse_iterator I = path::rbegin(
Path);
355 path::reverse_iterator Prev = I;
356 path::reverse_iterator E = path::rend(
Path);
357 FrameworkHeaderPath HeaderPath;
359 if (*I ==
"Headers" || *I ==
"PrivateHeaders") {
360 HeaderPath.HeaderSubpath =
Path.substr(Prev - E);
361 HeaderPath.IsPrivateHeader = *I ==
"PrivateHeaders";
364 HeaderPath.FrameworkName = *I;
365 if (!HeaderPath.FrameworkName.consume_back(
".framework"))
367 HeaderPath.FrameworkParentDir =
Path.substr(0, I - E);
382 std::optional<std::string>
383 getFrameworkUmbrellaSpelling(
const HeaderSearch &HS,
384 FrameworkHeaderPath &HeaderPath) {
385 StringRef Framework = HeaderPath.FrameworkName;
386 auto Res = CacheFrameworkToUmbrellaHeaderSpelling.try_emplace(Framework);
387 auto *CachedSpelling = &Res.first->second;
389 return HeaderPath.IsPrivateHeader ? CachedSpelling->PrivateHeader
390 : CachedSpelling->PublicHeader;
392 SmallString<256> UmbrellaPath(HeaderPath.FrameworkParentDir);
393 llvm::sys::path::append(UmbrellaPath, Framework +
".framework",
"Headers",
396 if (HS.getFileMgr().getOptionalFileRef(UmbrellaPath))
397 CachedSpelling->PublicHeader = llvm::formatv(
"<{0}/{0}.h>", Framework);
399 UmbrellaPath = HeaderPath.FrameworkParentDir;
400 llvm::sys::path::append(UmbrellaPath, Framework +
".framework",
401 "PrivateHeaders", Framework +
"_Private.h");
403 if (HS.getFileMgr().getOptionalFileRef(UmbrellaPath))
404 CachedSpelling->PrivateHeader =
405 llvm::formatv(
"<{0}/{0}_Private.h>", Framework);
407 return HeaderPath.IsPrivateHeader ? CachedSpelling->PrivateHeader
408 : CachedSpelling->PublicHeader;
415 std::optional<llvm::StringRef>
416 getFrameworkHeaderIncludeSpelling(FileEntryRef FE, HeaderSearch &HS) {
417 auto Res = CachePathToFrameworkSpelling.try_emplace(FE.getName());
418 auto *CachedHeaderSpelling = &Res.first->second;
420 return llvm::StringRef(*CachedHeaderSpelling);
422 auto HeaderPath = splitFrameworkHeaderPath(FE.getName());
426 CachePathToFrameworkSpelling.erase(Res.first);
429 if (
auto UmbrellaSpelling =
430 getFrameworkUmbrellaSpelling(HS, *HeaderPath)) {
431 *CachedHeaderSpelling = *UmbrellaSpelling;
432 return llvm::StringRef(*CachedHeaderSpelling);
435 *CachedHeaderSpelling =
436 llvm::formatv(
"<{0}/{1}>", HeaderPath->FrameworkName,
437 HeaderPath->HeaderSubpath)
439 return llvm::StringRef(*CachedHeaderSpelling);
442 llvm::StringRef getIncludeHeaderUncached(FileID FID) {
443 const auto FE = SM.getFileEntryRefForID(FID);
444 if (!FE || FE->getName().empty())
447 if (
auto Verbatim = PI->getPublic(*FE); !Verbatim.empty())
450 llvm::StringRef Filename = FE->getName();
451 if (
auto Canonical =
mapCanonical(Filename); !Canonical.empty())
456 auto &HS = PP->getHeaderSearchInfo();
457 if (
auto Spelling = getFrameworkHeaderIncludeSpelling(*FE, HS))
460 if (!tooling::isSelfContainedHeader(*FE, PP->getSourceManager(),
461 PP->getHeaderSearchInfo())) {
464 if (Filename.ends_with(
".inc") || Filename.ends_with(
".def"))
466 return getIncludeHeaderUncached(SM.getFileID(SM.getIncludeLoc(FID)));
476std::optional<SymbolLocation>
477SymbolCollector::getTokenLocation(SourceLocation TokLoc) {
478 const auto &SM = ASTCtx->getSourceManager();
479 const auto FE = SM.getFileEntryRefForID(SM.getFileID(TokLoc));
483 SymbolLocation Result;
484 Result.FileURI = HeaderFileURIs->toURI(*FE).c_str();
485 auto Range = getTokenRange(TokLoc, SM, ASTCtx->getLangOpts());
486 Result.Start = Range.first;
487 Result.End = Range.second;
497 HeaderFileURIs = std::make_unique<HeaderFileURICache>(
498 this->PP, ASTCtx->getSourceManager(), Opts);
499 CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
501 std::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
505 const ASTContext &ASTCtx,
507 bool IsMainFileOnly) {
509 if (ND.getDeclName().isEmpty())
513 if (IsMainFileOnly && !Opts.CollectMainFileSymbols)
517 if (!IsMainFileOnly && ND.isInAnonymousNamespace())
521 if (index::isFunctionLocalSymbol(&ND))
522 return isa<RecordDecl>(ND) ||
523 (ND.isCXXInstanceMember() && ND.isFunctionOrFunctionTemplate());
529 const auto *DeclCtx = ND.getDeclContext();
530 switch (DeclCtx->getDeclKind()) {
531 case Decl::TranslationUnit:
532 case Decl::Namespace:
533 case Decl::LinkageSpec:
535 case Decl::ObjCProtocol:
536 case Decl::ObjCInterface:
537 case Decl::ObjCCategory:
538 case Decl::ObjCCategoryImpl:
539 case Decl::ObjCImplementation:
544 if (!isa<RecordDecl>(DeclCtx))
549 if (isPrivateProtoDecl(ND))
553 if (!Opts.CollectReserved &&
555 ASTCtx.getSourceManager().isInSystemHeader(ND.getLocation()) &&
556 !ASTCtx.getSourceManager()
557 .getFilename(ND.getLocation())
558 .ends_with(
"intrin.h"))
568 const auto *ND = dyn_cast<NamedDecl>(Enclosing);
572 Enclosing = dyn_cast_or_null<Decl>(Enclosing->getDeclContext());
577ArrayRef<const CXXConstructorDecl *>
578SymbolCollector::findIndirectConstructors(
const Decl *D) {
579 const auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D);
587 const Decl *D, index::SymbolRoleSet Roles,
588 llvm::ArrayRef<index::SymbolRelation> Relations, SourceLocation Loc,
589 index::IndexDataConsumer::ASTNodeInfo
ASTNode) {
590 assert(ASTCtx && PP && HeaderFileURIs);
591 assert(CompletionAllocator && CompletionTUInfo);
596 if (D->getLocation().isInvalid())
601 if ((
ASTNode.OrigD->getFriendObjectKind() !=
602 Decl::FriendObjectKind::FOK_None) &&
603 !(Roles &
static_cast<unsigned>(index::SymbolRole::Definition)))
608 if (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None)
609 D = CanonicalDecls.try_emplace(D,
ASTNode.OrigD).first->second;
612 bool DeclIsCanonical =
false;
615 if (
const auto *IID = dyn_cast<ObjCImplementationDecl>(D)) {
616 DeclIsCanonical =
true;
617 if (
const auto *CID = IID->getClassInterface())
618 if (
const auto *DD = CID->getDefinition())
619 if (!DD->isImplicitInterfaceDecl())
624 if (
const auto *CID = dyn_cast<ObjCCategoryImplDecl>(D)) {
625 DeclIsCanonical =
true;
626 if (
const auto *CD = CID->getCategoryDecl())
629 const NamedDecl *ND = dyn_cast<NamedDecl>(D);
633 auto ID = getSymbolIDCached(ND);
639 auto &SM = ASTCtx->getSourceManager();
640 if (Opts.CountReferences &&
641 (Roles &
static_cast<unsigned>(index::SymbolRole::Reference)) &&
642 SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID())
643 ReferencedSymbols.insert(ID);
648 auto CheckIsMainFileOnly = [&](
const NamedDecl *Decl) {
649 return SM.isWrittenInMainFile(SM.getExpansionLoc(Decl->getBeginLoc())) &&
650 !
isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
651 ASTCtx->getLangOpts());
653 bool IsMainFileOnly = CheckIsMainFileOnly(ND);
655 if (
ASTNode.OrigD->isImplicit() ||
663 processRelations(*ND, ID, Relations);
665 bool CollectRef =
static_cast<bool>(Opts.RefFilter & toRefKind(Roles));
671 (!IsMainFileOnly || Opts.CollectMainFileRefs ||
672 ND->isExternallyVisible()) &&
673 !isa<NamespaceDecl>(ND)) {
674 auto FileLoc = SM.getFileLoc(Loc);
675 auto FID = SM.getFileID(FileLoc);
676 if (Opts.RefsInHeaders || FID == SM.getMainFileID()) {
678 addRef(ID, SymbolRef{FileLoc, FID, Roles, index::getSymbolInfo(ND).Kind,
679 Container, isSpelled(FileLoc, *ND)});
685 if (
auto ConstructorID = getSymbolIDCached(
Constructor))
686 addRef(ConstructorID,
687 SymbolRef{FileLoc, FID, Roles,
694 if (!(Roles & (
static_cast<unsigned>(index::SymbolRole::Declaration) |
695 static_cast<unsigned>(index::SymbolRole::Definition))))
701 auto *OriginalDecl = dyn_cast<NamedDecl>(
ASTNode.OrigD);
705 const Symbol *BasicSymbol = Symbols.find(ID);
706 bool SkipDocCheckInDef =
false;
707 if (isPreferredDeclaration(*OriginalDecl, Roles)) {
712 BasicSymbol = addDeclaration(*OriginalDecl, std::move(ID), IsMainFileOnly);
713 SkipDocCheckInDef =
true;
714 }
else if (!BasicSymbol || DeclIsCanonical) {
715 BasicSymbol = addDeclaration(*ND, std::move(ID), IsMainFileOnly);
716 SkipDocCheckInDef =
true;
719 if (Roles &
static_cast<unsigned>(index::SymbolRole::Definition))
720 addDefinition(*OriginalDecl, *BasicSymbol, SkipDocCheckInDef);
726 assert(HeaderFileURIs && PP);
727 const auto &SM = PP->getSourceManager();
728 const auto MainFileEntryRef = SM.getFileEntryRefForID(SM.getMainFileID());
729 assert(MainFileEntryRef);
731 const std::string &MainFileURI = HeaderFileURIs->toURI(*MainFileEntryRef);
733 for (
const auto &IDToRefs : MacroRefsToIndex.
MacroRefs) {
734 for (
const auto &MacroRef : IDToRefs.second) {
735 const auto &SR = MacroRef.toSourceRange(SM);
737 bool IsDefinition = MacroRef.IsDefinition;
745 Refs.insert(IDToRefs.first, R);
748 S.
ID = IDToRefs.first;
750 S.
SymInfo.Kind = index::SymbolKind::Macro;
751 S.
SymInfo.SubKind = index::SymbolSubKind::None;
752 S.
SymInfo.Properties = index::SymbolPropertySet();
753 S.
SymInfo.Lang = index::SymbolLanguage::C;
758 if (!HeaderFileURIs->getIncludeHeader(SM.getMainFileID()).empty()) {
770 index::SymbolRoleSet Roles,
771 SourceLocation Loc) {
774 if (MI->isBuiltinMacro())
777 const auto &SM = PP->getSourceManager();
778 auto DefLoc = MI->getDefinitionLoc();
781 if (SM.isWrittenInBuiltinFile(DefLoc) ||
782 SM.isWrittenInCommandLineFile(DefLoc) ||
783 Name->getName() ==
"__GCC_HAVE_DWARF2_CFI_ASM")
786 auto ID = getSymbolIDCached(Name->getName(), MI, SM);
790 auto SpellingLoc = SM.getSpellingLoc(Loc);
791 bool IsMainFileOnly =
792 SM.isInMainFile(SM.getExpansionLoc(DefLoc)) &&
793 !
isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
794 ASTCtx->getLangOpts());
796 if ((
static_cast<unsigned>(Opts.RefFilter) & Roles) && !IsMainFileOnly &&
797 (Opts.RefsInHeaders || SM.getFileID(SpellingLoc) == SM.getMainFileID())) {
802 SymbolRef{Loc, SM.getFileID(Loc), Roles, index::SymbolKind::Macro,
808 if (!Opts.CollectMacro)
812 if (IsMainFileOnly && !Opts.CollectMainFileSymbols)
818 if (Opts.CountReferences &&
819 (Roles &
static_cast<unsigned>(index::SymbolRole::Reference)) &&
820 SM.getFileID(SpellingLoc) == SM.getMainFileID())
821 ReferencedSymbols.insert(ID);
825 if (!(Roles &
static_cast<unsigned>(index::SymbolRole::Declaration) ||
826 Roles &
static_cast<unsigned>(index::SymbolRole::Definition)))
830 if (Symbols.find(ID) !=
nullptr)
834 S.
ID = std::move(ID);
835 S.
Name = Name->getName();
836 if (!IsMainFileOnly) {
840 S.
SymInfo = index::getSymbolInfoForMacro(*MI);
844 if (
auto DeclLoc = getTokenLocation(DefLoc))
847 CodeCompletionResult SymbolCompletion(Name);
848 const auto *CCS = SymbolCompletion.CreateCodeCompletionStringForMacro(
849 *PP, *CompletionAllocator, *CompletionTUInfo);
850 std::string Signature;
851 std::string SnippetSuffix;
852 getSignature(*CCS, &Signature, &SnippetSuffix, SymbolCompletion.Kind,
853 SymbolCompletion.CursorKind);
857 IndexedMacros.insert(Name);
859 setIncludeLocation(S, DefLoc, include_cleaner::Macro{Name, DefLoc});
864void SymbolCollector::processRelations(
865 const NamedDecl &ND,
const SymbolID &ID,
866 ArrayRef<index::SymbolRelation> Relations) {
867 for (
const auto &R : Relations) {
868 auto RKind = indexableRelation(R);
871 const Decl *
Object = R.RelatedSymbol;
873 auto ObjectID = getSymbolIDCached(
Object);
887 this->Relations.insert({ID, *RKind, ObjectID});
889 this->Relations.insert({ObjectID, *RKind, ID});
893void SymbolCollector::setIncludeLocation(
const Symbol &S, SourceLocation DefLoc,
894 const include_cleaner::Symbol &Sym) {
895 const auto &SM = PP->getSourceManager();
896 if (!Opts.CollectIncludePath ||
902 if (FileID FID = SM.getDecomposedExpansionLoc(DefLoc).first; FID.isValid())
903 IncludeFiles[S.ID] = FID;
908 SymbolProviders[S.ID] =
909 include_cleaner::headersForSymbol(Sym, *PP, Opts.PragmaIncludes);
913 tooling::stdlib::Lang Lang = tooling::stdlib::Lang::CXX;
915 Lang = tooling::stdlib::Lang::C;
916 else if(!LangOpts.CPlusPlus)
919 if (S->
Scope ==
"std::" && S->
Name ==
"move") {
922 return "<algorithm>";
925 if (
auto StdSym = tooling::stdlib::Symbol::named(S->
Scope, S->
Name, Lang))
926 if (
auto Header = StdSym->header())
927 return Header->name();
933 for (
const auto &ID : ReferencedSymbols) {
934 if (
const auto *S = Symbols.find(ID)) {
942 if (Opts.CollectMacro) {
945 for (
const IdentifierInfo *II : IndexedMacros) {
946 if (
const auto *MI = PP->getMacroDefinition(II).getMacroInfo())
948 getSymbolIDCached(II->getName(), MI, PP->getSourceManager()))
949 if (MI->isUsedForHeaderGuard())
953 llvm::DenseMap<FileID, bool> FileToContainsImportsOrObjC;
954 llvm::DenseMap<include_cleaner::Header, std::string> HeaderSpelling;
957 for (
const auto &[SID, Providers] : SymbolProviders) {
958 const Symbol *S = Symbols.find(SID);
962 FileID FID = IncludeFiles.lookup(SID);
965 auto CollectDirectives = shouldCollectIncludePath(S->
SymInfo.Kind);
970 auto [It, Inserted] = FileToContainsImportsOrObjC.try_emplace(FID);
972 It->second = FilesWithObjCConstructs.contains(FID) ||
973 tooling::codeContainsImports(
974 ASTCtx->getSourceManager().getBufferData(FID));
984 llvm::StringRef IncludeHeader =
getStdHeader(S, ASTCtx->getLangOpts());
985 if (IncludeHeader.empty())
986 IncludeHeader = HeaderFileURIs->getIncludeHeader(FID);
988 if (!IncludeHeader.empty()) {
991 Symbols.insert(NewSym);
1006 auto SelfContainedProvider =
1007 [
this](llvm::ArrayRef<include_cleaner::Header> Providers)
1008 -> std::optional<include_cleaner::Header> {
1009 for (
const auto &H : Providers) {
1010 if (H.kind() != include_cleaner::Header::Physical)
1012 if (tooling::isSelfContainedHeader(H.physical(), PP->getSourceManager(),
1013 PP->getHeaderSearchInfo()))
1016 return std::nullopt;
1018 const auto OptionalProvider = SelfContainedProvider(Providers);
1019 if (!OptionalProvider)
1021 const auto &H = *OptionalProvider;
1022 const auto [SpellingIt, Inserted] = HeaderSpelling.try_emplace(H);
1024 auto &SM = ASTCtx->getSourceManager();
1025 if (H.kind() == include_cleaner::Header::Kind::Physical) {
1028 if (
auto Canonical =
1029 HeaderFileURIs->mapCanonical(H.physical().getName());
1031 SpellingIt->second = Canonical;
1034 else if (tooling::isSelfContainedHeader(H.physical(), SM,
1035 PP->getHeaderSearchInfo()))
1036 SpellingIt->second =
1037 HeaderFileURIs->toURI(H.physical());
1039 SpellingIt->second = include_cleaner::spellHeader(
1040 {H, PP->getHeaderSearchInfo(),
1041 SM.getFileEntryForID(SM.getMainFileID())});
1045 if (!SpellingIt->second.empty()) {
1047 NewSym.
IncludeHeaders.push_back({SpellingIt->second, 1, Directives});
1048 Symbols.insert(NewSym);
1052 ReferencedSymbols.clear();
1053 IncludeFiles.clear();
1054 SymbolProviders.clear();
1055 FilesWithObjCConstructs.clear();
1058const Symbol *SymbolCollector::addDeclaration(
const NamedDecl &ND,
SymbolID ID,
1059 bool IsMainFileOnly) {
1060 auto &Ctx = ND.getASTContext();
1061 auto &SM = Ctx.getSourceManager();
1064 S.
ID = std::move(ID);
1077 if (!IsMainFileOnly)
1079 S.
SymInfo = index::getSymbolInfo(&ND);
1081 assert(Loc.isValid() &&
"Invalid source location for NamedDecl");
1083 auto FID = SM.getFileID(Loc);
1085 if (
auto DeclLoc = getTokenLocation(Loc))
1089 if (ND.getAvailability() == AR_Deprecated)
1096 assert(ASTCtx && PP &&
"ASTContext and Preprocessor must be set.");
1098 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
1099 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
1100 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
1103 std::string DocComment;
1104 std::string Documentation;
1106 if (!AlreadyHasDoc) {
1111 const auto UpdateDoc = [&] {
1112 if (!AlreadyHasDoc) {
1113 if (!DocComment.empty())
1119 if (Opts.StoreAllDocumentation)
1122 return Symbols.find(S.
ID);
1125 std::string Signature;
1126 std::string SnippetSuffix;
1127 getSignature(*CCS, &Signature, &SnippetSuffix, SymbolCompletion.Kind,
1128 SymbolCompletion.CursorKind);
1134 std::optional<OpaqueType> TypeStorage;
1138 S.
Type = TypeStorage->raw();
1142 setIncludeLocation(S, ND.getLocation(), include_cleaner::Symbol{ND});
1143 if (S.
SymInfo.Lang == index::SymbolLanguage::ObjC)
1144 FilesWithObjCConstructs.insert(FID);
1145 return Symbols.find(S.
ID);
1148void SymbolCollector::addDefinition(
const NamedDecl &ND,
const Symbol &DeclSym,
1149 bool SkipDocCheck) {
1150 if (DeclSym.Definition)
1152 const auto &SM = ND.getASTContext().getSourceManager();
1155 auto DefLoc = getTokenLocation(Loc);
1163 S.Definition = *DefLoc;
1165 std::string DocComment;
1166 std::string Documentation;
1168 (llvm::isa<FunctionDecl>(ND) || llvm::isa<CXXMethodDecl>(ND))) {
1169 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
1170 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
1171 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
1174 DocComment =
getDocComment(ND.getASTContext(), SymbolCompletion,
1176 if (!S.Documentation.empty())
1177 Documentation = S.Documentation.str() +
'\n' + DocComment;
1180 if (!DocComment.empty())
1182 S.Documentation = Documentation;
1189 if (!Opts.FileFilter)
1191 auto I = FilesToIndexCache.try_emplace(FID);
1193 I.first->second = Opts.FileFilter(ASTCtx->getSourceManager(), FID);
1194 return I.first->second;
1198 using SK = index::SymbolKind;
1199 return Kind == SK::Function || Kind == SK::InstanceMethod ||
1200 Kind == SK::ClassMethod || Kind == SK::StaticMethod ||
1201 Kind == SK::Constructor || Kind == SK::Destructor ||
1202 Kind == SK::ConversionFunction;
1205void SymbolCollector::addRef(SymbolID ID,
const SymbolRef &SR) {
1206 const auto &SM = ASTCtx->getSourceManager();
1209 if (
const auto FE = SM.getFileEntryRefForID(SR.FID)) {
1210 auto Range = getTokenRange(SR.Loc, SM, ASTCtx->getLangOpts());
1212 R.Location.Start = Range.first;
1213 R.Location.End = Range.second;
1214 R.Location.FileURI = HeaderFileURIs->toURI(*FE).c_str();
1215 R.Kind = toRefKind(SR.Roles, SR.Spelled);
1219 R.Container = getSymbolIDCached(SR.Container);
1224SymbolID SymbolCollector::getSymbolIDCached(
const Decl *D) {
1225 auto It = DeclToIDCache.try_emplace(D);
1228 return It.first->second;
1231SymbolID SymbolCollector::getSymbolIDCached(
const llvm::StringRef MacroName,
1232 const MacroInfo *MI,
1233 const SourceManager &SM) {
1234 auto It = MacroToIDCache.try_emplace(MI);
1236 It.first->second =
getSymbolID(MacroName, MI, SM);
1237 return It.first->second;
static GeneratorRegistry::Add< MDGenerator > MD(MDGenerator::Format, "Generator for Markdown output.")
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)
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.
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.
ArrayRef< const CXXConstructorDecl * > getForwardedConstructors(const FunctionDecl *FD, ForwardingToConstructorCache &Cache)
Returns the constructors that FD forwards to, if FD is a template instantiation of a likely forwardin...
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.
llvm::DenseMap< const FunctionDecl *, SmallVector< const CXXConstructorDecl *, 1 > > ForwardingToConstructorCache
Cache mapping forwarding function instantiations (e.g.
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).
SymbolTags computeSymbolTags(const NamedDecl &ND)
Computes symbol tags for a given NamedDecl.
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).
Ensure we have enough bits to represent all SymbolTag values.
@ 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.
SymbolTags Tags
Symbol tags for LSP protocol (Deprecated, Static, Virtual, Abstract, Final, ReadOnly,...
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.