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());
576ArrayRef<const CXXConstructorDecl *>
577SymbolCollector::findIndirectConstructors(
const Decl *D) {
578 const auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D);
586 const Decl *D, index::SymbolRoleSet Roles,
587 llvm::ArrayRef<index::SymbolRelation> Relations, SourceLocation Loc,
588 index::IndexDataConsumer::ASTNodeInfo
ASTNode) {
589 assert(ASTCtx && PP && HeaderFileURIs);
590 assert(CompletionAllocator && CompletionTUInfo);
595 if (D->getLocation().isInvalid())
600 if ((
ASTNode.OrigD->getFriendObjectKind() !=
601 Decl::FriendObjectKind::FOK_None) &&
602 !(Roles &
static_cast<unsigned>(index::SymbolRole::Definition)))
607 if (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None)
608 D = CanonicalDecls.try_emplace(D,
ASTNode.OrigD).first->second;
611 bool DeclIsCanonical =
false;
614 if (
const auto *IID = dyn_cast<ObjCImplementationDecl>(D)) {
615 DeclIsCanonical =
true;
616 if (
const auto *CID = IID->getClassInterface())
617 if (
const auto *DD = CID->getDefinition())
618 if (!DD->isImplicitInterfaceDecl())
623 if (
const auto *CID = dyn_cast<ObjCCategoryImplDecl>(D)) {
624 DeclIsCanonical =
true;
625 if (
const auto *CD = CID->getCategoryDecl())
628 const NamedDecl *ND = dyn_cast<NamedDecl>(D);
632 auto ID = getSymbolIDCached(ND);
638 auto &SM = ASTCtx->getSourceManager();
639 if (Opts.CountReferences &&
640 (Roles &
static_cast<unsigned>(index::SymbolRole::Reference)) &&
641 SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID())
642 ReferencedSymbols.insert(ID);
647 auto CheckIsMainFileOnly = [&](
const NamedDecl *Decl) {
648 return SM.isWrittenInMainFile(SM.getExpansionLoc(Decl->getBeginLoc())) &&
649 !
isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
650 ASTCtx->getLangOpts());
652 bool IsMainFileOnly = CheckIsMainFileOnly(ND);
654 if (
ASTNode.OrigD->isImplicit() ||
662 processRelations(*ND, ID, Relations);
664 bool CollectRef =
static_cast<bool>(Opts.RefFilter & toRefKind(Roles));
670 (!IsMainFileOnly || Opts.CollectMainFileRefs ||
671 ND->isExternallyVisible()) &&
672 !isa<NamespaceDecl>(ND)) {
673 auto FileLoc = SM.getFileLoc(Loc);
674 auto FID = SM.getFileID(FileLoc);
675 if (Opts.RefsInHeaders || FID == SM.getMainFileID()) {
677 addRef(ID, SymbolRef{FileLoc, FID, Roles, index::getSymbolInfo(ND).Kind,
678 Container, isSpelled(FileLoc, *ND)});
684 if (
auto ConstructorID = getSymbolIDCached(
Constructor))
685 addRef(ConstructorID,
686 SymbolRef{FileLoc, FID, Roles,
693 if (!(Roles & (
static_cast<unsigned>(index::SymbolRole::Declaration) |
694 static_cast<unsigned>(index::SymbolRole::Definition))))
700 auto *OriginalDecl = dyn_cast<NamedDecl>(
ASTNode.OrigD);
704 const Symbol *BasicSymbol = Symbols.find(ID);
705 bool SkipDocCheckInDef =
false;
706 if (isPreferredDeclaration(*OriginalDecl, Roles)) {
711 BasicSymbol = addDeclaration(*OriginalDecl, std::move(ID), IsMainFileOnly);
712 SkipDocCheckInDef =
true;
713 }
else if (!BasicSymbol || DeclIsCanonical) {
714 BasicSymbol = addDeclaration(*ND, std::move(ID), IsMainFileOnly);
715 SkipDocCheckInDef =
true;
718 if (Roles &
static_cast<unsigned>(index::SymbolRole::Definition))
719 addDefinition(*OriginalDecl, *BasicSymbol, SkipDocCheckInDef);
725 assert(HeaderFileURIs && PP);
726 const auto &SM = PP->getSourceManager();
727 const auto MainFileEntryRef = SM.getFileEntryRefForID(SM.getMainFileID());
728 assert(MainFileEntryRef);
730 const std::string &MainFileURI = HeaderFileURIs->toURI(*MainFileEntryRef);
732 for (
const auto &IDToRefs : MacroRefsToIndex.
MacroRefs) {
733 for (
const auto &MacroRef : IDToRefs.second) {
734 const auto &SR = MacroRef.toSourceRange(SM);
736 bool IsDefinition = MacroRef.IsDefinition;
744 Refs.insert(IDToRefs.first, R);
747 S.
ID = IDToRefs.first;
749 S.
SymInfo.Kind = index::SymbolKind::Macro;
750 S.
SymInfo.SubKind = index::SymbolSubKind::None;
751 S.
SymInfo.Properties = index::SymbolPropertySet();
752 S.
SymInfo.Lang = index::SymbolLanguage::C;
757 if (!HeaderFileURIs->getIncludeHeader(SM.getMainFileID()).empty()) {
769 index::SymbolRoleSet Roles,
770 SourceLocation Loc) {
773 if (MI->isBuiltinMacro())
776 const auto &SM = PP->getSourceManager();
777 auto DefLoc = MI->getDefinitionLoc();
780 if (SM.isWrittenInBuiltinFile(DefLoc) ||
781 SM.isWrittenInCommandLineFile(DefLoc) ||
782 Name->getName() ==
"__GCC_HAVE_DWARF2_CFI_ASM")
785 auto ID = getSymbolIDCached(Name->getName(), MI, SM);
789 auto SpellingLoc = SM.getSpellingLoc(Loc);
790 bool IsMainFileOnly =
791 SM.isInMainFile(SM.getExpansionLoc(DefLoc)) &&
792 !
isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
793 ASTCtx->getLangOpts());
795 if ((
static_cast<unsigned>(Opts.RefFilter) & Roles) && !IsMainFileOnly &&
796 (Opts.RefsInHeaders || SM.getFileID(SpellingLoc) == SM.getMainFileID())) {
801 SymbolRef{Loc, SM.getFileID(Loc), Roles, index::SymbolKind::Macro,
807 if (!Opts.CollectMacro)
811 if (IsMainFileOnly && !Opts.CollectMainFileSymbols)
817 if (Opts.CountReferences &&
818 (Roles &
static_cast<unsigned>(index::SymbolRole::Reference)) &&
819 SM.getFileID(SpellingLoc) == SM.getMainFileID())
820 ReferencedSymbols.insert(ID);
824 if (!(Roles &
static_cast<unsigned>(index::SymbolRole::Declaration) ||
825 Roles &
static_cast<unsigned>(index::SymbolRole::Definition)))
829 if (Symbols.find(ID) !=
nullptr)
833 S.
ID = std::move(ID);
834 S.
Name = Name->getName();
835 if (!IsMainFileOnly) {
839 S.
SymInfo = index::getSymbolInfoForMacro(*MI);
843 if (
auto DeclLoc = getTokenLocation(DefLoc))
846 CodeCompletionResult SymbolCompletion(Name);
847 const auto *CCS = SymbolCompletion.CreateCodeCompletionStringForMacro(
848 *PP, *CompletionAllocator, *CompletionTUInfo);
849 std::string Signature;
850 std::string SnippetSuffix;
851 getSignature(*CCS, &Signature, &SnippetSuffix, SymbolCompletion.Kind,
852 SymbolCompletion.CursorKind);
856 IndexedMacros.insert(Name);
858 setIncludeLocation(S, DefLoc, include_cleaner::Macro{Name, DefLoc});
863void SymbolCollector::processRelations(
864 const NamedDecl &ND,
const SymbolID &ID,
865 ArrayRef<index::SymbolRelation> Relations) {
866 for (
const auto &R : Relations) {
867 auto RKind = indexableRelation(R);
870 const Decl *
Object = R.RelatedSymbol;
872 auto ObjectID = getSymbolIDCached(
Object);
886 this->Relations.insert({ID, *RKind, ObjectID});
888 this->Relations.insert({ObjectID, *RKind, ID});
892void SymbolCollector::setIncludeLocation(
const Symbol &S, SourceLocation DefLoc,
893 const include_cleaner::Symbol &Sym) {
894 const auto &SM = PP->getSourceManager();
895 if (!Opts.CollectIncludePath ||
901 if (FileID FID = SM.getDecomposedExpansionLoc(DefLoc).first; FID.isValid())
902 IncludeFiles[S.ID] = FID;
907 SymbolProviders[S.ID] =
908 include_cleaner::headersForSymbol(Sym, *PP, Opts.PragmaIncludes);
912 tooling::stdlib::Lang Lang = tooling::stdlib::Lang::CXX;
914 Lang = tooling::stdlib::Lang::C;
915 else if(!LangOpts.CPlusPlus)
918 if (S->
Scope ==
"std::" && S->
Name ==
"move") {
921 return "<algorithm>";
924 if (
auto StdSym = tooling::stdlib::Symbol::named(S->
Scope, S->
Name, Lang))
925 if (
auto Header = StdSym->header())
926 return Header->name();
932 for (
const auto &ID : ReferencedSymbols) {
933 if (
const auto *S = Symbols.find(ID)) {
941 if (Opts.CollectMacro) {
944 for (
const IdentifierInfo *II : IndexedMacros) {
945 if (
const auto *MI = PP->getMacroDefinition(II).getMacroInfo())
947 getSymbolIDCached(II->getName(), MI, PP->getSourceManager()))
948 if (MI->isUsedForHeaderGuard())
952 llvm::DenseMap<FileID, bool> FileToContainsImportsOrObjC;
953 llvm::DenseMap<include_cleaner::Header, std::string> HeaderSpelling;
956 for (
const auto &[SID, Providers] : SymbolProviders) {
957 const Symbol *S = Symbols.find(SID);
961 FileID FID = IncludeFiles.lookup(SID);
964 auto CollectDirectives = shouldCollectIncludePath(S->
SymInfo.Kind);
969 auto [It, Inserted] = FileToContainsImportsOrObjC.try_emplace(FID);
971 It->second = FilesWithObjCConstructs.contains(FID) ||
972 tooling::codeContainsImports(
973 ASTCtx->getSourceManager().getBufferData(FID));
983 llvm::StringRef IncludeHeader =
getStdHeader(S, ASTCtx->getLangOpts());
984 if (IncludeHeader.empty())
985 IncludeHeader = HeaderFileURIs->getIncludeHeader(FID);
987 if (!IncludeHeader.empty()) {
990 Symbols.insert(NewSym);
1005 auto SelfContainedProvider =
1006 [
this](llvm::ArrayRef<include_cleaner::Header> Providers)
1007 -> std::optional<include_cleaner::Header> {
1008 for (
const auto &H : Providers) {
1009 if (H.kind() != include_cleaner::Header::Physical)
1011 if (tooling::isSelfContainedHeader(H.physical(), PP->getSourceManager(),
1012 PP->getHeaderSearchInfo()))
1015 return std::nullopt;
1017 const auto OptionalProvider = SelfContainedProvider(Providers);
1018 if (!OptionalProvider)
1020 const auto &H = *OptionalProvider;
1021 const auto [SpellingIt, Inserted] = HeaderSpelling.try_emplace(H);
1023 auto &SM = ASTCtx->getSourceManager();
1024 if (H.kind() == include_cleaner::Header::Kind::Physical) {
1027 if (
auto Canonical =
1028 HeaderFileURIs->mapCanonical(H.physical().getName());
1030 SpellingIt->second = Canonical;
1033 else if (tooling::isSelfContainedHeader(H.physical(), SM,
1034 PP->getHeaderSearchInfo()))
1035 SpellingIt->second =
1036 HeaderFileURIs->toURI(H.physical());
1038 SpellingIt->second = include_cleaner::spellHeader(
1039 {H, PP->getHeaderSearchInfo(),
1040 SM.getFileEntryForID(SM.getMainFileID())});
1044 if (!SpellingIt->second.empty()) {
1046 NewSym.
IncludeHeaders.push_back({SpellingIt->second, 1, Directives});
1047 Symbols.insert(NewSym);
1051 ReferencedSymbols.clear();
1052 IncludeFiles.clear();
1053 SymbolProviders.clear();
1054 FilesWithObjCConstructs.clear();
1057const Symbol *SymbolCollector::addDeclaration(
const NamedDecl &ND,
SymbolID ID,
1058 bool IsMainFileOnly) {
1059 auto &Ctx = ND.getASTContext();
1060 auto &SM = Ctx.getSourceManager();
1063 S.
ID = std::move(ID);
1076 if (!IsMainFileOnly)
1078 S.
SymInfo = index::getSymbolInfo(&ND);
1080 assert(Loc.isValid() &&
"Invalid source location for NamedDecl");
1082 auto FID = SM.getFileID(Loc);
1084 if (
auto DeclLoc = getTokenLocation(Loc))
1088 if (ND.getAvailability() == AR_Deprecated)
1093 assert(ASTCtx && PP &&
"ASTContext and Preprocessor must be set.");
1095 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
1096 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
1097 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
1100 std::string DocComment;
1101 std::string Documentation;
1103 if (!AlreadyHasDoc) {
1108 const auto UpdateDoc = [&] {
1109 if (!AlreadyHasDoc) {
1110 if (!DocComment.empty())
1116 if (Opts.StoreAllDocumentation)
1119 return Symbols.find(S.
ID);
1122 std::string Signature;
1123 std::string SnippetSuffix;
1124 getSignature(*CCS, &Signature, &SnippetSuffix, SymbolCompletion.Kind,
1125 SymbolCompletion.CursorKind);
1131 std::optional<OpaqueType> TypeStorage;
1135 S.
Type = TypeStorage->raw();
1139 setIncludeLocation(S, ND.getLocation(), include_cleaner::Symbol{ND});
1140 if (S.
SymInfo.Lang == index::SymbolLanguage::ObjC)
1141 FilesWithObjCConstructs.insert(FID);
1142 return Symbols.find(S.
ID);
1145void SymbolCollector::addDefinition(
const NamedDecl &ND,
const Symbol &DeclSym,
1146 bool SkipDocCheck) {
1147 if (DeclSym.Definition)
1149 const auto &SM = ND.getASTContext().getSourceManager();
1152 auto DefLoc = getTokenLocation(Loc);
1160 S.Definition = *DefLoc;
1162 std::string DocComment;
1163 std::string Documentation;
1165 (llvm::isa<FunctionDecl>(ND) || llvm::isa<CXXMethodDecl>(ND))) {
1166 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
1167 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
1168 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
1171 DocComment =
getDocComment(ND.getASTContext(), SymbolCompletion,
1173 if (!S.Documentation.empty())
1174 Documentation = S.Documentation.str() +
'\n' + DocComment;
1177 if (!DocComment.empty())
1179 S.Documentation = Documentation;
1186 if (!Opts.FileFilter)
1188 auto I = FilesToIndexCache.try_emplace(FID);
1190 I.first->second = Opts.FileFilter(ASTCtx->getSourceManager(), FID);
1191 return I.first->second;
1195 using SK = index::SymbolKind;
1196 return Kind == SK::Function || Kind == SK::InstanceMethod ||
1197 Kind == SK::ClassMethod || Kind == SK::StaticMethod ||
1198 Kind == SK::Constructor || Kind == SK::Destructor ||
1199 Kind == SK::ConversionFunction;
1202void SymbolCollector::addRef(SymbolID ID,
const SymbolRef &SR) {
1203 const auto &SM = ASTCtx->getSourceManager();
1206 if (
const auto FE = SM.getFileEntryRefForID(SR.FID)) {
1207 auto Range = getTokenRange(SR.Loc, SM, ASTCtx->getLangOpts());
1209 R.Location.Start = Range.first;
1210 R.Location.End = Range.second;
1211 R.Location.FileURI = HeaderFileURIs->toURI(*FE).c_str();
1212 R.Kind = toRefKind(SR.Roles, SR.Spelled);
1216 R.Container = getSymbolIDCached(SR.Container);
1221SymbolID SymbolCollector::getSymbolIDCached(
const Decl *D) {
1222 auto It = DeclToIDCache.try_emplace(D);
1225 return It.first->second;
1228SymbolID SymbolCollector::getSymbolIDCached(
const llvm::StringRef MacroName,
1229 const MacroInfo *MI,
1230 const SourceManager &SM) {
1231 auto It = MacroToIDCache.try_emplace(MI);
1233 It.first->second =
getSymbolID(MacroName, MI, SM);
1234 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)
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).
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.