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.getNonTransparentDeclContext();
530 switch (DeclCtx->getDeclKind()) {
531 case Decl::TranslationUnit:
532 case Decl::Namespace:
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)
1095 assert(ASTCtx && PP &&
"ASTContext and Preprocessor must be set.");
1097 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
1098 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
1099 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
1102 std::string DocComment;
1103 std::string Documentation;
1105 if (!AlreadyHasDoc) {
1110 const auto UpdateDoc = [&] {
1111 if (!AlreadyHasDoc) {
1112 if (!DocComment.empty())
1118 if (Opts.StoreAllDocumentation)
1121 return Symbols.find(S.
ID);
1124 std::string Signature;
1125 std::string SnippetSuffix;
1126 getSignature(*CCS, &Signature, &SnippetSuffix, SymbolCompletion.Kind,
1127 SymbolCompletion.CursorKind);
1133 std::optional<OpaqueType> TypeStorage;
1137 S.
Type = TypeStorage->raw();
1141 setIncludeLocation(S, ND.getLocation(), include_cleaner::Symbol{ND});
1142 if (S.
SymInfo.Lang == index::SymbolLanguage::ObjC)
1143 FilesWithObjCConstructs.insert(FID);
1144 return Symbols.find(S.
ID);
1147void SymbolCollector::addDefinition(
const NamedDecl &ND,
const Symbol &DeclSym,
1148 bool SkipDocCheck) {
1149 if (DeclSym.Definition)
1151 const auto &SM = ND.getASTContext().getSourceManager();
1154 auto DefLoc = getTokenLocation(Loc);
1162 S.Definition = *DefLoc;
1164 std::string DocComment;
1165 std::string Documentation;
1167 (llvm::isa<FunctionDecl>(ND) || llvm::isa<CXXMethodDecl>(ND))) {
1168 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
1169 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
1170 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
1173 DocComment =
getDocComment(ND.getASTContext(), SymbolCompletion,
1175 if (!S.Documentation.empty())
1176 Documentation = S.Documentation.str() +
'\n' + DocComment;
1179 if (!DocComment.empty())
1181 S.Documentation = Documentation;
1188 if (!Opts.FileFilter)
1190 auto I = FilesToIndexCache.try_emplace(FID);
1192 I.first->second = Opts.FileFilter(ASTCtx->getSourceManager(), FID);
1193 return I.first->second;
1197 using SK = index::SymbolKind;
1198 return Kind == SK::Function || Kind == SK::InstanceMethod ||
1199 Kind == SK::ClassMethod || Kind == SK::StaticMethod ||
1200 Kind == SK::Constructor || Kind == SK::Destructor ||
1201 Kind == SK::ConversionFunction;
1204void SymbolCollector::addRef(SymbolID ID,
const SymbolRef &SR) {
1205 const auto &SM = ASTCtx->getSourceManager();
1208 if (
const auto FE = SM.getFileEntryRefForID(SR.FID)) {
1209 auto Range = getTokenRange(SR.Loc, SM, ASTCtx->getLangOpts());
1211 R.Location.Start = Range.first;
1212 R.Location.End = Range.second;
1213 R.Location.FileURI = HeaderFileURIs->toURI(*FE).c_str();
1214 R.Kind = toRefKind(SR.Roles, SR.Spelled);
1218 R.Container = getSymbolIDCached(SR.Container);
1223SymbolID SymbolCollector::getSymbolIDCached(
const Decl *D) {
1224 auto It = DeclToIDCache.try_emplace(D);
1227 return It.first->second;
1230SymbolID SymbolCollector::getSymbolIDCached(
const llvm::StringRef MacroName,
1231 const MacroInfo *MI,
1232 const SourceManager &SM) {
1233 auto It = MacroToIDCache.try_emplace(MI);
1235 It.first->second =
getSymbolID(MacroName, MI, SM);
1236 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.