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 if (HS.getFileMgr().getOptionalFileRef(UmbrellaPath))
396 CachedSpelling->PublicHeader = llvm::formatv(
"<{0}/{0}.h>", Framework);
398 UmbrellaPath = HeaderPath.FrameworkParentDir;
399 llvm::sys::path::append(UmbrellaPath, Framework +
".framework",
400 "PrivateHeaders", Framework +
"_Private.h");
402 if (HS.getFileMgr().getOptionalFileRef(UmbrellaPath))
403 CachedSpelling->PrivateHeader =
404 llvm::formatv(
"<{0}/{0}_Private.h>", Framework);
406 return HeaderPath.IsPrivateHeader ? CachedSpelling->PrivateHeader
407 : CachedSpelling->PublicHeader;
414 std::optional<llvm::StringRef>
415 getFrameworkHeaderIncludeSpelling(FileEntryRef FE, HeaderSearch &HS) {
416 auto Res = CachePathToFrameworkSpelling.try_emplace(FE.getName());
417 auto *CachedHeaderSpelling = &Res.first->second;
419 return llvm::StringRef(*CachedHeaderSpelling);
421 auto HeaderPath = splitFrameworkHeaderPath(FE.getName());
425 CachePathToFrameworkSpelling.erase(Res.first);
428 if (
auto UmbrellaSpelling =
429 getFrameworkUmbrellaSpelling(HS, *HeaderPath)) {
430 *CachedHeaderSpelling = *UmbrellaSpelling;
431 return llvm::StringRef(*CachedHeaderSpelling);
434 *CachedHeaderSpelling =
435 llvm::formatv(
"<{0}/{1}>", HeaderPath->FrameworkName,
436 HeaderPath->HeaderSubpath)
438 return llvm::StringRef(*CachedHeaderSpelling);
441 llvm::StringRef getIncludeHeaderUncached(FileID FID) {
442 const auto FE = SM.getFileEntryRefForID(FID);
443 if (!FE || FE->getName().empty())
446 if (
auto Verbatim = PI->getPublic(*FE); !Verbatim.empty())
449 llvm::StringRef Filename = FE->getName();
450 if (
auto Canonical =
mapCanonical(Filename); !Canonical.empty())
455 auto &HS = PP->getHeaderSearchInfo();
456 if (
auto Spelling = getFrameworkHeaderIncludeSpelling(*FE, HS))
459 if (!tooling::isSelfContainedHeader(*FE, PP->getSourceManager(),
460 PP->getHeaderSearchInfo())) {
463 if (Filename.ends_with(
".inc") || Filename.ends_with(
".def"))
465 return getIncludeHeaderUncached(SM.getFileID(SM.getIncludeLoc(FID)));
475std::optional<SymbolLocation>
476SymbolCollector::getTokenLocation(SourceLocation TokLoc) {
477 const auto &SM = ASTCtx->getSourceManager();
478 const auto FE = SM.getFileEntryRefForID(SM.getFileID(TokLoc));
482 SymbolLocation Result;
483 Result.FileURI = HeaderFileURIs->toURI(*FE).c_str();
484 auto Range = getTokenRange(TokLoc, SM, ASTCtx->getLangOpts());
485 Result.Start = Range.first;
486 Result.End = Range.second;
496 HeaderFileURIs = std::make_unique<HeaderFileURICache>(
497 this->PP, ASTCtx->getSourceManager(), Opts);
498 CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
500 std::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
504 const ASTContext &ASTCtx,
506 bool IsMainFileOnly) {
508 if (ND.getDeclName().isEmpty())
512 if (IsMainFileOnly && !Opts.CollectMainFileSymbols)
516 if (!IsMainFileOnly && ND.isInAnonymousNamespace())
520 if (index::isFunctionLocalSymbol(&ND))
521 return isa<RecordDecl>(ND) ||
522 (ND.isCXXInstanceMember() && ND.isFunctionOrFunctionTemplate());
528 const auto *DeclCtx = ND.getDeclContext();
529 switch (DeclCtx->getDeclKind()) {
530 case Decl::TranslationUnit:
531 case Decl::Namespace:
532 case Decl::LinkageSpec:
534 case Decl::ObjCProtocol:
535 case Decl::ObjCInterface:
536 case Decl::ObjCCategory:
537 case Decl::ObjCCategoryImpl:
538 case Decl::ObjCImplementation:
543 if (!isa<RecordDecl>(DeclCtx))
548 if (isPrivateProtoDecl(ND))
552 if (!Opts.CollectReserved &&
554 ASTCtx.getSourceManager().isInSystemHeader(ND.getLocation()) &&
555 !ASTCtx.getSourceManager()
556 .getFilename(ND.getLocation())
557 .ends_with(
"intrin.h"))
567 const auto *ND = dyn_cast<NamedDecl>(Enclosing);
571 Enclosing = dyn_cast_or_null<Decl>(Enclosing->getDeclContext());
577SymbolCollector::findIndirectConstructors(
const Decl *D) {
578 auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D);
579 if (FD ==
nullptr || !FD->isTemplateInstantiation())
581 if (
auto Entry = ForwardingToConstructorCache.find(FD);
582 Entry != ForwardingToConstructorCache.end())
583 return Entry->getSecond();
584 if (
auto *PT = FD->getPrimaryTemplate();
590 auto Iter = ForwardingToConstructorCache.try_emplace(
591 FD, std::move(FoundConstructors));
592 return Iter.first->getSecond();
597 const Decl *D, index::SymbolRoleSet Roles,
598 llvm::ArrayRef<index::SymbolRelation> Relations, SourceLocation Loc,
599 index::IndexDataConsumer::ASTNodeInfo
ASTNode) {
600 assert(ASTCtx && PP && HeaderFileURIs);
601 assert(CompletionAllocator && CompletionTUInfo);
606 if (D->getLocation().isInvalid())
611 if ((
ASTNode.OrigD->getFriendObjectKind() !=
612 Decl::FriendObjectKind::FOK_None) &&
613 !(Roles &
static_cast<unsigned>(index::SymbolRole::Definition)))
618 if (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None)
619 D = CanonicalDecls.try_emplace(D,
ASTNode.OrigD).first->second;
622 bool DeclIsCanonical =
false;
625 if (
const auto *IID = dyn_cast<ObjCImplementationDecl>(D)) {
626 DeclIsCanonical =
true;
627 if (
const auto *CID = IID->getClassInterface())
628 if (
const auto *DD = CID->getDefinition())
629 if (!DD->isImplicitInterfaceDecl())
634 if (
const auto *CID = dyn_cast<ObjCCategoryImplDecl>(D)) {
635 DeclIsCanonical =
true;
636 if (
const auto *CD = CID->getCategoryDecl())
639 const NamedDecl *ND = dyn_cast<NamedDecl>(D);
643 auto ID = getSymbolIDCached(ND);
649 auto &SM = ASTCtx->getSourceManager();
650 if (Opts.CountReferences &&
651 (Roles &
static_cast<unsigned>(index::SymbolRole::Reference)) &&
652 SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID())
653 ReferencedSymbols.insert(ID);
658 auto CheckIsMainFileOnly = [&](
const NamedDecl *Decl) {
659 return SM.isWrittenInMainFile(SM.getExpansionLoc(Decl->getBeginLoc())) &&
660 !
isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
661 ASTCtx->getLangOpts());
663 bool IsMainFileOnly = CheckIsMainFileOnly(ND);
665 if (
ASTNode.OrigD->isImplicit() ||
673 processRelations(*ND, ID, Relations);
675 bool CollectRef =
static_cast<bool>(Opts.RefFilter & toRefKind(Roles));
681 (!IsMainFileOnly || Opts.CollectMainFileRefs ||
682 ND->isExternallyVisible()) &&
683 !isa<NamespaceDecl>(ND)) {
684 auto FileLoc = SM.getFileLoc(Loc);
685 auto FID = SM.getFileID(FileLoc);
686 if (Opts.RefsInHeaders || FID == SM.getMainFileID()) {
688 addRef(ID, SymbolRef{FileLoc, FID, Roles, index::getSymbolInfo(ND).Kind,
689 Container, isSpelled(FileLoc, *ND)});
695 if (
auto ConstructorID = getSymbolIDCached(
Constructor))
696 addRef(ConstructorID,
697 SymbolRef{FileLoc, FID, Roles,
704 if (!(Roles & (
static_cast<unsigned>(index::SymbolRole::Declaration) |
705 static_cast<unsigned>(index::SymbolRole::Definition))))
711 auto *OriginalDecl = dyn_cast<NamedDecl>(
ASTNode.OrigD);
715 const Symbol *BasicSymbol = Symbols.find(ID);
716 bool SkipDocCheckInDef =
false;
717 if (isPreferredDeclaration(*OriginalDecl, Roles)) {
722 BasicSymbol = addDeclaration(*OriginalDecl, std::move(ID), IsMainFileOnly);
723 SkipDocCheckInDef =
true;
724 }
else if (!BasicSymbol || DeclIsCanonical) {
725 BasicSymbol = addDeclaration(*ND, std::move(ID), IsMainFileOnly);
726 SkipDocCheckInDef =
true;
729 if (Roles &
static_cast<unsigned>(index::SymbolRole::Definition))
730 addDefinition(*OriginalDecl, *BasicSymbol, SkipDocCheckInDef);
736 assert(HeaderFileURIs && PP);
737 const auto &SM = PP->getSourceManager();
738 const auto MainFileEntryRef = SM.getFileEntryRefForID(SM.getMainFileID());
739 assert(MainFileEntryRef);
741 const std::string &MainFileURI = HeaderFileURIs->toURI(*MainFileEntryRef);
743 for (
const auto &IDToRefs : MacroRefsToIndex.
MacroRefs) {
744 for (
const auto &MacroRef : IDToRefs.second) {
745 const auto &SR = MacroRef.toSourceRange(SM);
747 bool IsDefinition = MacroRef.IsDefinition;
755 Refs.insert(IDToRefs.first, R);
758 S.
ID = IDToRefs.first;
760 S.
SymInfo.Kind = index::SymbolKind::Macro;
761 S.
SymInfo.SubKind = index::SymbolSubKind::None;
762 S.
SymInfo.Properties = index::SymbolPropertySet();
763 S.
SymInfo.Lang = index::SymbolLanguage::C;
768 if (!HeaderFileURIs->getIncludeHeader(SM.getMainFileID()).empty()) {
780 index::SymbolRoleSet Roles,
781 SourceLocation Loc) {
784 if (MI->isBuiltinMacro())
787 const auto &SM = PP->getSourceManager();
788 auto DefLoc = MI->getDefinitionLoc();
791 if (SM.isWrittenInBuiltinFile(DefLoc) ||
792 SM.isWrittenInCommandLineFile(DefLoc) ||
793 Name->getName() ==
"__GCC_HAVE_DWARF2_CFI_ASM")
796 auto ID = getSymbolIDCached(Name->getName(), MI, SM);
800 auto SpellingLoc = SM.getSpellingLoc(Loc);
801 bool IsMainFileOnly =
802 SM.isInMainFile(SM.getExpansionLoc(DefLoc)) &&
803 !
isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
804 ASTCtx->getLangOpts());
806 if ((
static_cast<unsigned>(Opts.RefFilter) & Roles) && !IsMainFileOnly &&
807 (Opts.RefsInHeaders || SM.getFileID(SpellingLoc) == SM.getMainFileID())) {
812 SymbolRef{Loc, SM.getFileID(Loc), Roles, index::SymbolKind::Macro,
818 if (!Opts.CollectMacro)
822 if (IsMainFileOnly && !Opts.CollectMainFileSymbols)
828 if (Opts.CountReferences &&
829 (Roles &
static_cast<unsigned>(index::SymbolRole::Reference)) &&
830 SM.getFileID(SpellingLoc) == SM.getMainFileID())
831 ReferencedSymbols.insert(ID);
835 if (!(Roles &
static_cast<unsigned>(index::SymbolRole::Declaration) ||
836 Roles &
static_cast<unsigned>(index::SymbolRole::Definition)))
840 if (Symbols.find(ID) !=
nullptr)
844 S.
ID = std::move(ID);
845 S.
Name = Name->getName();
846 if (!IsMainFileOnly) {
850 S.
SymInfo = index::getSymbolInfoForMacro(*MI);
854 if (
auto DeclLoc = getTokenLocation(DefLoc))
857 CodeCompletionResult SymbolCompletion(Name);
858 const auto *CCS = SymbolCompletion.CreateCodeCompletionStringForMacro(
859 *PP, *CompletionAllocator, *CompletionTUInfo);
860 std::string Signature;
861 std::string SnippetSuffix;
862 getSignature(*CCS, &Signature, &SnippetSuffix, SymbolCompletion.Kind,
863 SymbolCompletion.CursorKind);
867 IndexedMacros.insert(Name);
869 setIncludeLocation(S, DefLoc, include_cleaner::Macro{Name, DefLoc});
874void SymbolCollector::processRelations(
875 const NamedDecl &ND,
const SymbolID &ID,
876 ArrayRef<index::SymbolRelation> Relations) {
877 for (
const auto &R : Relations) {
878 auto RKind = indexableRelation(R);
881 const Decl *
Object = R.RelatedSymbol;
883 auto ObjectID = getSymbolIDCached(
Object);
897 this->Relations.insert({ID, *RKind, ObjectID});
899 this->Relations.insert({ObjectID, *RKind, ID});
903void SymbolCollector::setIncludeLocation(
const Symbol &S, SourceLocation DefLoc,
904 const include_cleaner::Symbol &Sym) {
905 const auto &SM = PP->getSourceManager();
906 if (!Opts.CollectIncludePath ||
912 if (FileID FID = SM.getDecomposedExpansionLoc(DefLoc).first; FID.isValid())
913 IncludeFiles[S.ID] = FID;
918 SymbolProviders[S.ID] =
919 include_cleaner::headersForSymbol(Sym, *PP, Opts.PragmaIncludes);
923 tooling::stdlib::Lang Lang = tooling::stdlib::Lang::CXX;
925 Lang = tooling::stdlib::Lang::C;
926 else if(!LangOpts.CPlusPlus)
929 if (S->
Scope ==
"std::" && S->
Name ==
"move") {
932 return "<algorithm>";
935 if (
auto StdSym = tooling::stdlib::Symbol::named(S->
Scope, S->
Name, Lang))
936 if (
auto Header = StdSym->header())
937 return Header->name();
943 for (
const auto &ID : ReferencedSymbols) {
944 if (
const auto *S = Symbols.find(ID)) {
952 if (Opts.CollectMacro) {
955 for (
const IdentifierInfo *II : IndexedMacros) {
956 if (
const auto *MI = PP->getMacroDefinition(II).getMacroInfo())
958 getSymbolIDCached(II->getName(), MI, PP->getSourceManager()))
959 if (MI->isUsedForHeaderGuard())
963 llvm::DenseMap<FileID, bool> FileToContainsImportsOrObjC;
964 llvm::DenseMap<include_cleaner::Header, std::string> HeaderSpelling;
967 for (
const auto &[SID, Providers] : SymbolProviders) {
968 const Symbol *S = Symbols.find(SID);
972 FileID FID = IncludeFiles.lookup(SID);
975 auto CollectDirectives = shouldCollectIncludePath(S->
SymInfo.Kind);
980 auto [It, Inserted] = FileToContainsImportsOrObjC.try_emplace(FID);
982 It->second = FilesWithObjCConstructs.contains(FID) ||
983 tooling::codeContainsImports(
984 ASTCtx->getSourceManager().getBufferData(FID));
994 llvm::StringRef IncludeHeader =
getStdHeader(S, ASTCtx->getLangOpts());
995 if (IncludeHeader.empty())
996 IncludeHeader = HeaderFileURIs->getIncludeHeader(FID);
998 if (!IncludeHeader.empty()) {
1001 Symbols.insert(NewSym);
1016 auto SelfContainedProvider =
1017 [
this](llvm::ArrayRef<include_cleaner::Header> Providers)
1018 -> std::optional<include_cleaner::Header> {
1019 for (
const auto &H : Providers) {
1020 if (H.kind() != include_cleaner::Header::Physical)
1022 if (tooling::isSelfContainedHeader(H.physical(), PP->getSourceManager(),
1023 PP->getHeaderSearchInfo()))
1026 return std::nullopt;
1028 const auto OptionalProvider = SelfContainedProvider(Providers);
1029 if (!OptionalProvider)
1031 const auto &H = *OptionalProvider;
1032 const auto [SpellingIt, Inserted] = HeaderSpelling.try_emplace(H);
1034 auto &SM = ASTCtx->getSourceManager();
1035 if (H.kind() == include_cleaner::Header::Kind::Physical) {
1038 if (
auto Canonical =
1039 HeaderFileURIs->mapCanonical(H.physical().getName());
1041 SpellingIt->second = Canonical;
1044 else if (tooling::isSelfContainedHeader(H.physical(), SM,
1045 PP->getHeaderSearchInfo()))
1046 SpellingIt->second =
1047 HeaderFileURIs->toURI(H.physical());
1049 SpellingIt->second = include_cleaner::spellHeader(
1050 {H, PP->getHeaderSearchInfo(),
1051 SM.getFileEntryForID(SM.getMainFileID())});
1055 if (!SpellingIt->second.empty()) {
1057 NewSym.
IncludeHeaders.push_back({SpellingIt->second, 1, Directives});
1058 Symbols.insert(NewSym);
1062 ReferencedSymbols.clear();
1063 IncludeFiles.clear();
1064 SymbolProviders.clear();
1065 FilesWithObjCConstructs.clear();
1068const Symbol *SymbolCollector::addDeclaration(
const NamedDecl &ND,
SymbolID ID,
1069 bool IsMainFileOnly) {
1070 auto &Ctx = ND.getASTContext();
1071 auto &SM = Ctx.getSourceManager();
1074 S.
ID = std::move(ID);
1087 if (!IsMainFileOnly)
1089 S.
SymInfo = index::getSymbolInfo(&ND);
1091 assert(Loc.isValid() &&
"Invalid source location for NamedDecl");
1093 auto FID = SM.getFileID(Loc);
1095 if (
auto DeclLoc = getTokenLocation(Loc))
1099 if (ND.getAvailability() == AR_Deprecated)
1104 assert(ASTCtx && PP &&
"ASTContext and Preprocessor must be set.");
1106 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
1107 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
1108 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
1111 std::string DocComment;
1112 std::string Documentation;
1114 if (!AlreadyHasDoc) {
1119 const auto UpdateDoc = [&] {
1120 if (!AlreadyHasDoc) {
1121 if (!DocComment.empty())
1127 if (Opts.StoreAllDocumentation)
1130 return Symbols.find(S.
ID);
1133 std::string Signature;
1134 std::string SnippetSuffix;
1135 getSignature(*CCS, &Signature, &SnippetSuffix, SymbolCompletion.Kind,
1136 SymbolCompletion.CursorKind);
1142 std::optional<OpaqueType> TypeStorage;
1146 S.
Type = TypeStorage->raw();
1150 setIncludeLocation(S, ND.getLocation(), include_cleaner::Symbol{ND});
1151 if (S.
SymInfo.Lang == index::SymbolLanguage::ObjC)
1152 FilesWithObjCConstructs.insert(FID);
1153 return Symbols.find(S.
ID);
1156void SymbolCollector::addDefinition(
const NamedDecl &ND,
const Symbol &DeclSym,
1157 bool SkipDocCheck) {
1158 if (DeclSym.Definition)
1160 const auto &SM = ND.getASTContext().getSourceManager();
1163 auto DefLoc = getTokenLocation(Loc);
1171 S.Definition = *DefLoc;
1173 std::string DocComment;
1174 std::string Documentation;
1176 (llvm::isa<FunctionDecl>(ND) || llvm::isa<CXXMethodDecl>(ND))) {
1177 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
1178 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
1179 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
1182 DocComment =
getDocComment(ND.getASTContext(), SymbolCompletion,
1184 if (!S.Documentation.empty())
1185 Documentation = S.Documentation.str() +
'\n' + DocComment;
1188 if (!DocComment.empty())
1190 S.Documentation = Documentation;
1197 if (!Opts.FileFilter)
1199 auto I = FilesToIndexCache.try_emplace(FID);
1201 I.first->second = Opts.FileFilter(ASTCtx->getSourceManager(), FID);
1202 return I.first->second;
1206 using SK = index::SymbolKind;
1207 return Kind == SK::Function || Kind == SK::InstanceMethod ||
1208 Kind == SK::ClassMethod || Kind == SK::StaticMethod ||
1209 Kind == SK::Constructor || Kind == SK::Destructor ||
1210 Kind == SK::ConversionFunction;
1213void SymbolCollector::addRef(SymbolID ID,
const SymbolRef &SR) {
1214 const auto &SM = ASTCtx->getSourceManager();
1217 if (
const auto FE = SM.getFileEntryRefForID(SR.FID)) {
1218 auto Range = getTokenRange(SR.Loc, SM, ASTCtx->getLangOpts());
1220 R.Location.Start = Range.first;
1221 R.Location.End = Range.second;
1222 R.Location.FileURI = HeaderFileURIs->toURI(*FE).c_str();
1223 R.Kind = toRefKind(SR.Roles, SR.Spelled);
1227 R.Container = getSymbolIDCached(SR.Container);
1232SymbolID SymbolCollector::getSymbolIDCached(
const Decl *D) {
1233 auto It = DeclToIDCache.try_emplace(D);
1236 return It.first->second;
1239SymbolID SymbolCollector::getSymbolIDCached(
const llvm::StringRef MacroName,
1240 const MacroInfo *MI,
1241 const SourceManager &SM) {
1242 auto It = MacroToIDCache.try_emplace(MI);
1244 It.first->second =
getSymbolID(MacroName, MI, SM);
1245 return It.first->second;
static GeneratorRegistry::Add< MDGenerator > MD(MDGenerator::Format, "Generator for MD 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)
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.