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/DeclObjC.h"
29#include "clang/AST/DeclTemplate.h"
30#include "clang/AST/DeclarationName.h"
31#include "clang/AST/Expr.h"
32#include "clang/Basic/FileEntry.h"
33#include "clang/Basic/LangOptions.h"
34#include "clang/Basic/SourceLocation.h"
35#include "clang/Basic/SourceManager.h"
36#include "clang/Index/IndexSymbol.h"
37#include "clang/Lex/Preprocessor.h"
38#include "clang/Lex/Token.h"
39#include "clang/Tooling/Inclusions/HeaderAnalysis.h"
40#include "clang/Tooling/Inclusions/StandardLibrary.h"
41#include "llvm/ADT/ArrayRef.h"
42#include "llvm/ADT/DenseMap.h"
43#include "llvm/ADT/SmallVector.h"
44#include "llvm/ADT/StringRef.h"
45#include "llvm/Support/Casting.h"
46#include "llvm/Support/ErrorHandling.h"
47#include "llvm/Support/FileSystem.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) {
169 SymbolLocation::Position
Pos;
170 Pos.setLine(LSPLoc.line);
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::fs::make_absolute(FallbackDir, AbsPath);
329 assert(llvm::sys::path::is_absolute(AbsPath) &&
330 "If the VFS can't make paths absolute, a FallbackDir must be "
332 llvm::sys::path::remove_dots(AbsPath,
true);
335 return R.first->second;
338 struct FrameworkHeaderPath {
340 llvm::StringRef FrameworkParentDir;
342 llvm::StringRef FrameworkName;
345 llvm::StringRef HeaderSubpath;
347 bool IsPrivateHeader;
350 std::optional<FrameworkHeaderPath>
351 splitFrameworkHeaderPath(llvm::StringRef
Path) {
352 using namespace llvm::sys;
353 path::reverse_iterator I = path::rbegin(
Path);
354 path::reverse_iterator Prev = I;
355 path::reverse_iterator
E = path::rend(
Path);
356 FrameworkHeaderPath HeaderPath;
358 if (*I ==
"Headers" || *I ==
"PrivateHeaders") {
359 HeaderPath.HeaderSubpath =
Path.substr(Prev -
E);
360 HeaderPath.IsPrivateHeader = *I ==
"PrivateHeaders";
363 HeaderPath.FrameworkName = *I;
364 if (!HeaderPath.FrameworkName.consume_back(
".framework"))
366 HeaderPath.FrameworkParentDir =
Path.substr(0, I -
E);
381 std::optional<std::string>
382 getFrameworkUmbrellaSpelling(
const HeaderSearch &HS,
383 FrameworkHeaderPath &HeaderPath) {
384 StringRef Framework = HeaderPath.FrameworkName;
385 auto Res = CacheFrameworkToUmbrellaHeaderSpelling.try_emplace(Framework);
386 auto *CachedSpelling = &Res.first->second;
388 return HeaderPath.IsPrivateHeader ? CachedSpelling->PrivateHeader
389 : CachedSpelling->PublicHeader;
391 SmallString<256> UmbrellaPath(HeaderPath.FrameworkParentDir);
392 llvm::sys::path::append(UmbrellaPath, Framework +
".framework",
"Headers",
395 llvm::vfs::Status Status;
396 auto StatErr = HS.getFileMgr().getNoncachedStatValue(UmbrellaPath, Status);
398 CachedSpelling->PublicHeader = llvm::formatv(
"<{0}/{0}.h>", Framework);
400 UmbrellaPath = HeaderPath.FrameworkParentDir;
401 llvm::sys::path::append(UmbrellaPath, Framework +
".framework",
402 "PrivateHeaders", Framework +
"_Private.h");
404 StatErr = HS.getFileMgr().getNoncachedStatValue(UmbrellaPath, Status);
406 CachedSpelling->PrivateHeader =
407 llvm::formatv(
"<{0}/{0}_Private.h>", Framework);
409 return HeaderPath.IsPrivateHeader ? CachedSpelling->PrivateHeader
410 : CachedSpelling->PublicHeader;
417 std::optional<llvm::StringRef>
418 getFrameworkHeaderIncludeSpelling(FileEntryRef FE, HeaderSearch &HS) {
419 auto Res = CachePathToFrameworkSpelling.try_emplace(FE.getName());
420 auto *CachedHeaderSpelling = &Res.first->second;
422 return llvm::StringRef(*CachedHeaderSpelling);
424 auto HeaderPath = splitFrameworkHeaderPath(FE.getName());
428 CachePathToFrameworkSpelling.erase(Res.first);
431 if (
auto UmbrellaSpelling =
432 getFrameworkUmbrellaSpelling(HS, *HeaderPath)) {
433 *CachedHeaderSpelling = *UmbrellaSpelling;
434 return llvm::StringRef(*CachedHeaderSpelling);
437 *CachedHeaderSpelling =
438 llvm::formatv(
"<{0}/{1}>", HeaderPath->FrameworkName,
439 HeaderPath->HeaderSubpath)
441 return llvm::StringRef(*CachedHeaderSpelling);
444 llvm::StringRef getIncludeHeaderUncached(FileID FID) {
445 const auto FE = SM.getFileEntryRefForID(FID);
446 if (!FE || FE->getName().empty())
449 if (
auto Verbatim = PI->getPublic(*FE); !Verbatim.empty())
452 llvm::StringRef
Filename = FE->getName();
458 auto &HS = PP->getHeaderSearchInfo();
459 if (
auto Spelling = getFrameworkHeaderIncludeSpelling(*FE, HS))
462 if (!tooling::isSelfContainedHeader(*FE, PP->getSourceManager(),
463 PP->getHeaderSearchInfo())) {
468 return getIncludeHeaderUncached(SM.getFileID(SM.getIncludeLoc(FID)));
478std::optional<SymbolLocation>
479SymbolCollector::getTokenLocation(SourceLocation TokLoc) {
480 const auto &SM = ASTCtx->getSourceManager();
481 const auto FE = SM.getFileEntryRefForID(SM.getFileID(TokLoc));
485 SymbolLocation Result;
486 Result.FileURI = HeaderFileURIs->toURI(*FE).c_str();
487 auto Range = getTokenRange(TokLoc, SM, ASTCtx->getLangOpts());
488 Result.Start =
Range.first;
489 Result.End =
Range.second;
499 HeaderFileURIs = std::make_unique<HeaderFileURICache>(
500 this->PP, ASTCtx->getSourceManager(), Opts);
501 CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
503 std::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
507 const ASTContext &ASTCtx,
509 bool IsMainFileOnly) {
511 if (ND.getDeclName().isEmpty())
515 if (IsMainFileOnly && !Opts.CollectMainFileSymbols)
519 if (!IsMainFileOnly && ND.isInAnonymousNamespace())
523 if (index::isFunctionLocalSymbol(&ND))
524 return isa<RecordDecl>(ND) ||
525 (ND.isCXXInstanceMember() && ND.isFunctionOrFunctionTemplate());
531 const auto *DeclCtx = ND.getDeclContext();
532 switch (DeclCtx->getDeclKind()) {
533 case Decl::TranslationUnit:
534 case Decl::Namespace:
535 case Decl::LinkageSpec:
537 case Decl::ObjCProtocol:
538 case Decl::ObjCInterface:
539 case Decl::ObjCCategory:
540 case Decl::ObjCCategoryImpl:
541 case Decl::ObjCImplementation:
546 if (!isa<RecordDecl>(DeclCtx))
551 if (isPrivateProtoDecl(ND))
555 if (!Opts.CollectReserved &&
557 ASTCtx.getSourceManager().isInSystemHeader(ND.getLocation()) &&
558 !ASTCtx.getSourceManager()
559 .getFilename(ND.getLocation())
560 .ends_with(
"intrin.h"))
570 const auto *ND = dyn_cast<NamedDecl>(Enclosing);
574 Enclosing = dyn_cast_or_null<Decl>(Enclosing->getDeclContext());
581 const Decl *D, index::SymbolRoleSet Roles,
582 llvm::ArrayRef<index::SymbolRelation> Relations, SourceLocation
Loc,
583 index::IndexDataConsumer::ASTNodeInfo
ASTNode) {
584 assert(ASTCtx && PP && HeaderFileURIs);
585 assert(CompletionAllocator && CompletionTUInfo);
590 if (D->getLocation().isInvalid())
595 if ((
ASTNode.OrigD->getFriendObjectKind() !=
596 Decl::FriendObjectKind::FOK_None) &&
597 !(Roles &
static_cast<unsigned>(index::SymbolRole::Definition)))
602 if (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None)
603 D = CanonicalDecls.try_emplace(D,
ASTNode.OrigD).first->second;
606 bool DeclIsCanonical =
false;
609 if (
const auto *IID = dyn_cast<ObjCImplementationDecl>(D)) {
610 DeclIsCanonical =
true;
611 if (
const auto *CID = IID->getClassInterface())
612 if (
const auto *DD = CID->getDefinition())
613 if (!DD->isImplicitInterfaceDecl())
618 if (
const auto *CID = dyn_cast<ObjCCategoryImplDecl>(D)) {
619 DeclIsCanonical =
true;
620 if (
const auto *CD = CID->getCategoryDecl())
623 const NamedDecl *ND = dyn_cast<NamedDecl>(D);
627 auto ID = getSymbolIDCached(ND);
633 auto &SM = ASTCtx->getSourceManager();
635 (Roles &
static_cast<unsigned>(index::SymbolRole::Reference)) &&
636 SM.getFileID(SM.getSpellingLoc(
Loc)) == SM.getMainFileID())
637 ReferencedSymbols.insert(
ID);
642 bool IsMainFileOnly =
643 SM.isWrittenInMainFile(SM.getExpansionLoc(ND->getBeginLoc())) &&
644 !
isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
645 ASTCtx->getLangOpts());
647 if (
ASTNode.OrigD->isImplicit() ||
655 processRelations(*ND,
ID, Relations);
657 bool CollectRef =
static_cast<bool>(Opts.
RefFilter & toRefKind(Roles));
664 ND->isExternallyVisible()) &&
665 !isa<NamespaceDecl>(ND)) {
666 auto FileLoc = SM.getFileLoc(
Loc);
667 auto FID = SM.getFileID(FileLoc);
669 addRef(
ID, SymbolRef{FileLoc, FID, Roles, index::getSymbolInfo(ND).Kind,
671 isSpelled(FileLoc, *ND)});
675 if (!(Roles & (
static_cast<unsigned>(index::SymbolRole::Declaration) |
676 static_cast<unsigned>(index::SymbolRole::Definition))))
682 auto *OriginalDecl = dyn_cast<NamedDecl>(
ASTNode.OrigD);
687 bool SkipDocCheckInDef =
false;
688 if (isPreferredDeclaration(*OriginalDecl, Roles)) {
693 BasicSymbol = addDeclaration(*OriginalDecl, std::move(
ID), IsMainFileOnly);
694 SkipDocCheckInDef =
true;
695 }
else if (!BasicSymbol || DeclIsCanonical) {
696 BasicSymbol = addDeclaration(*ND, std::move(
ID), IsMainFileOnly);
697 SkipDocCheckInDef =
true;
700 if (Roles &
static_cast<unsigned>(index::SymbolRole::Definition))
701 addDefinition(*OriginalDecl, *BasicSymbol, SkipDocCheckInDef);
707 assert(HeaderFileURIs && PP);
708 const auto &SM = PP->getSourceManager();
709 const auto MainFileEntryRef = SM.getFileEntryRefForID(SM.getMainFileID());
710 assert(MainFileEntryRef);
712 const std::string &MainFileURI = HeaderFileURIs->toURI(*MainFileEntryRef);
714 for (
const auto &IDToRefs : MacroRefsToIndex.
MacroRefs) {
715 for (
const auto &MacroRef : IDToRefs.second) {
716 const auto &
Range = MacroRef.toRange(SM);
717 bool IsDefinition = MacroRef.IsDefinition;
725 Refs.
insert(IDToRefs.first, R);
728 S.ID = IDToRefs.first;
731 S.Name =
toSourceCode(SM, SourceRange(StartLoc, EndLoc));
732 S.SymInfo.Kind = index::SymbolKind::Macro;
733 S.SymInfo.SubKind = index::SymbolSubKind::None;
734 S.SymInfo.Properties = index::SymbolPropertySet();
735 S.SymInfo.Lang = index::SymbolLanguage::C;
737 S.CanonicalDeclaration = R.
Location;
740 if (!HeaderFileURIs->getIncludeHeader(SM.getMainFileID()).empty()) {
752 index::SymbolRoleSet Roles,
753 SourceLocation
Loc) {
756 if (MI->isBuiltinMacro())
759 const auto &SM = PP->getSourceManager();
760 auto DefLoc = MI->getDefinitionLoc();
763 if (SM.isWrittenInBuiltinFile(DefLoc) ||
764 SM.isWrittenInCommandLineFile(DefLoc) ||
765 Name->getName() ==
"__GCC_HAVE_DWARF2_CFI_ASM")
768 auto ID = getSymbolIDCached(
Name->getName(), MI, SM);
772 auto SpellingLoc = SM.getSpellingLoc(
Loc);
773 bool IsMainFileOnly =
774 SM.isInMainFile(SM.getExpansionLoc(DefLoc)) &&
775 !
isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
776 ASTCtx->getLangOpts());
778 if ((
static_cast<unsigned>(Opts.
RefFilter) & Roles) && !IsMainFileOnly &&
779 (Opts.
RefsInHeaders || SM.getFileID(SpellingLoc) == SM.getMainFileID())) {
784 SymbolRef{
Loc, SM.getFileID(
Loc), Roles, index::SymbolKind::Macro,
801 (Roles &
static_cast<unsigned>(index::SymbolRole::Reference)) &&
802 SM.getFileID(SpellingLoc) == SM.getMainFileID())
803 ReferencedSymbols.insert(
ID);
807 if (!(Roles &
static_cast<unsigned>(index::SymbolRole::Declaration) ||
808 Roles &
static_cast<unsigned>(index::SymbolRole::Definition)))
812 if (Symbols.
find(
ID) !=
nullptr)
816 S.ID = std::move(
ID);
817 S.Name =
Name->getName();
818 if (!IsMainFileOnly) {
822 S.SymInfo = index::getSymbolInfoForMacro(*MI);
826 if (
auto DeclLoc = getTokenLocation(DefLoc))
827 S.CanonicalDeclaration = *DeclLoc;
829 CodeCompletionResult SymbolCompletion(
Name);
830 const auto *CCS = SymbolCompletion.CreateCodeCompletionStringForMacro(
831 *PP, *CompletionAllocator, *CompletionTUInfo);
835 SymbolCompletion.CursorKind);
839 IndexedMacros.insert(
Name);
841 setIncludeLocation(S, DefLoc, include_cleaner::Macro{
Name, DefLoc});
846void SymbolCollector::processRelations(
848 ArrayRef<index::SymbolRelation> Relations) {
849 for (
const auto &R : Relations) {
850 auto RKind = indexableRelation(R);
855 auto ObjectID = getSymbolIDCached(
Object);
869 this->Relations.insert({
ID, *RKind, ObjectID});
871 this->Relations.insert({ObjectID, *RKind,
ID});
875void SymbolCollector::setIncludeLocation(
const Symbol &S, SourceLocation DefLoc,
876 const include_cleaner::Symbol &Sym) {
877 const auto &SM = PP->getSourceManager();
884 if (FileID FID = SM.getDecomposedExpansionLoc(DefLoc).first; FID.isValid())
885 IncludeFiles[S.ID] = FID;
890 SymbolProviders[S.ID] =
895 tooling::stdlib::Lang
Lang = tooling::stdlib::Lang::CXX;
897 Lang = tooling::stdlib::Lang::C;
898 else if(!LangOpts.CPlusPlus)
901 if (S->Scope ==
"std::" && S->Name ==
"move") {
902 if (!S->Signature.contains(
','))
904 return "<algorithm>";
907 if (
auto StdSym = tooling::stdlib::Symbol::named(S->Scope, S->Name,
Lang))
908 if (
auto Header = StdSym->header())
909 return Header->name();
915 for (
const auto &
ID : ReferencedSymbols) {
916 if (
const auto *S = Symbols.
find(
ID)) {
927 for (
const IdentifierInfo *II : IndexedMacros) {
928 if (
const auto *MI = PP->getMacroDefinition(II).getMacroInfo())
930 getSymbolIDCached(II->getName(), MI, PP->getSourceManager()))
931 if (MI->isUsedForHeaderGuard())
935 llvm::DenseMap<FileID, bool> FileToContainsImportsOrObjC;
936 llvm::DenseMap<include_cleaner::Header, std::string> HeaderSpelling;
939 for (
const auto &[SID, Providers] : SymbolProviders) {
944 FileID FID = IncludeFiles.lookup(SID);
947 auto CollectDirectives = shouldCollectIncludePath(S->SymInfo.Kind);
952 auto [It, Inserted] = FileToContainsImportsOrObjC.try_emplace(FID);
954 It->second = FilesWithObjCConstructs.contains(FID) ||
955 tooling::codeContainsImports(
956 ASTCtx->getSourceManager().getBufferData(FID));
966 llvm::StringRef IncludeHeader =
getStdHeader(S, ASTCtx->getLangOpts());
967 if (IncludeHeader.empty())
968 IncludeHeader = HeaderFileURIs->getIncludeHeader(FID);
970 if (!IncludeHeader.empty()) {
972 NewSym.IncludeHeaders.push_back({IncludeHeader, 1,
Directives});
988 auto SelfContainedProvider =
989 [
this](llvm::ArrayRef<include_cleaner::Header> Providers)
990 -> std::optional<include_cleaner::Header> {
991 for (
const auto &H : Providers) {
992 if (H.kind() != include_cleaner::Header::Physical)
994 if (tooling::isSelfContainedHeader(H.physical(), PP->getSourceManager(),
995 PP->getHeaderSearchInfo()))
1000 const auto OptionalProvider = SelfContainedProvider(Providers);
1001 if (!OptionalProvider)
1003 const auto &H = *OptionalProvider;
1004 const auto [SpellingIt, Inserted] = HeaderSpelling.try_emplace(H);
1006 auto &SM = ASTCtx->getSourceManager();
1007 if (H.kind() == include_cleaner::Header::Kind::Physical) {
1010 if (
auto Canonical =
1011 HeaderFileURIs->mapCanonical(H.physical().getName());
1013 SpellingIt->second = Canonical;
1016 else if (tooling::isSelfContainedHeader(H.physical(), SM,
1017 PP->getHeaderSearchInfo()))
1018 SpellingIt->second =
1019 HeaderFileURIs->toURI(H.physical());
1021 SpellingIt->second = include_cleaner::spellHeader(
1022 {H, PP->getHeaderSearchInfo(),
1023 SM.getFileEntryForID(SM.getMainFileID())});
1027 if (!SpellingIt->second.empty()) {
1029 NewSym.IncludeHeaders.push_back({SpellingIt->second, 1,
Directives});
1034 ReferencedSymbols.clear();
1035 IncludeFiles.clear();
1036 SymbolProviders.clear();
1037 FilesWithObjCConstructs.clear();
1040const Symbol *SymbolCollector::addDeclaration(
const NamedDecl &ND,
SymbolID ID,
1041 bool IsMainFileOnly) {
1042 auto &Ctx = ND.getASTContext();
1043 auto &SM = Ctx.getSourceManager();
1046 S.ID = std::move(
ID);
1052 S.TemplateSpecializationArgs = TemplateSpecializationArgs;
1059 if (!IsMainFileOnly)
1061 S.SymInfo = index::getSymbolInfo(&ND);
1063 assert(
Loc.isValid() &&
"Invalid source location for NamedDecl");
1065 auto FID = SM.getFileID(
Loc);
1067 if (
auto DeclLoc = getTokenLocation(
Loc))
1068 S.CanonicalDeclaration = *DeclLoc;
1071 if (ND.getAvailability() == AR_Deprecated)
1076 assert(ASTCtx && PP &&
"ASTContext and Preprocessor must be set.");
1078 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
1079 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
1080 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
1083 std::string DocComment;
1084 std::string Documentation;
1086 if (!AlreadyHasDoc) {
1091 const auto UpdateDoc = [&] {
1092 if (!AlreadyHasDoc) {
1093 if (!DocComment.empty())
1095 S.Documentation = Documentation;
1102 return Symbols.
find(S.ID);
1108 SymbolCompletion.CursorKind);
1114 std::optional<OpaqueType> TypeStorage;
1118 S.Type = TypeStorage->raw();
1122 setIncludeLocation(S, ND.getLocation(), include_cleaner::Symbol{ND});
1123 if (S.SymInfo.Lang == index::SymbolLanguage::ObjC)
1124 FilesWithObjCConstructs.insert(FID);
1125 return Symbols.
find(S.ID);
1128void SymbolCollector::addDefinition(
const NamedDecl &ND,
const Symbol &DeclSym,
1129 bool SkipDocCheck) {
1130 if (DeclSym.Definition)
1132 const auto &SM = ND.getASTContext().getSourceManager();
1135 auto DefLoc = getTokenLocation(
Loc);
1143 S.Definition = *DefLoc;
1145 std::string DocComment;
1146 std::string Documentation;
1148 (llvm::isa<FunctionDecl>(ND) || llvm::isa<CXXMethodDecl>(ND))) {
1149 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
1150 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
1151 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
1154 DocComment =
getDocComment(ND.getASTContext(), SymbolCompletion,
1156 if (!S.Documentation.empty())
1157 Documentation = S.Documentation.str() +
'\n' + DocComment;
1160 if (!DocComment.empty())
1162 S.Documentation = Documentation;
1171 auto I = FilesToIndexCache.try_emplace(FID);
1173 I.first->second = Opts.
FileFilter(ASTCtx->getSourceManager(), FID);
1174 return I.first->second;
1178 using SK = index::SymbolKind;
1179 return Kind == SK::Function ||
Kind == SK::InstanceMethod ||
1180 Kind == SK::ClassMethod ||
Kind == SK::StaticMethod ||
1181 Kind == SK::Constructor ||
Kind == SK::Destructor ||
1182 Kind == SK::ConversionFunction;
1185void SymbolCollector::addRef(SymbolID
ID,
const SymbolRef &SR) {
1186 const auto &SM = ASTCtx->getSourceManager();
1189 if (
const auto FE = SM.getFileEntryRefForID(SR.FID)) {
1190 auto Range = getTokenRange(SR.Loc, SM, ASTCtx->getLangOpts());
1192 R.Location.Start =
Range.first;
1193 R.Location.End =
Range.second;
1194 R.Location.FileURI = HeaderFileURIs->toURI(*FE).c_str();
1195 R.Kind = toRefKind(SR.Roles, SR.Spelled);
1199 R.Container = getSymbolIDCached(SR.Container);
1204SymbolID SymbolCollector::getSymbolIDCached(
const Decl *D) {
1205 auto It = DeclToIDCache.try_emplace(D, SymbolID{});
1208 return It.first->second;
1212 const MacroInfo *MI,
1213 const SourceManager &SM) {
1214 auto It = MacroToIDCache.try_emplace(MI, SymbolID{});
1217 return It.first->second;
const FunctionDecl * Decl
llvm::SmallString< 256U > Name
std::string SnippetSuffix
CharSourceRange Range
SourceRange for the file name.
std::string Filename
Filename as a string.
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
const Symbol * find(const SymbolID &ID)
Returns the symbol with an ID, if it exists. Valid until insert/remove.
void erase(const SymbolID &ID)
Removes the symbol with an ID, if it exists.
void insert(const Symbol &S)
Adds a symbol, overwriting any existing one with the same ID.
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
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.
std::string Path
A typedef to represent a file path.
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.
llvm::Expected< SourceLocation > sourceLocationInMainFile(const SourceManager &SM, Position P)
Return the file location, corresponding to P.
bool isImplementationDetail(const Decl *D)
Returns true if the declaration is considered implementation detail based on heuristics.
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)
std::array< uint8_t, 20 > SymbolID
@ Canonical
The two mix because the types refer to the same CanonicalType, but we do not elaborate as to how.
===– 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.
bool CollectMacro
Collect macros.
RefKind RefFilter
The symbol ref kinds that will be collected.
bool CollectMainFileSymbols
Collect symbols local to main-files, such as static functions, symbols inside an anonymous namespace,...
bool StoreAllDocumentation
If set to true, SymbolCollector will collect doc for all symbols.
bool CollectMainFileRefs
Collect references to main-file symbols.
bool RefsInHeaders
If set to true, SymbolCollector will collect all refs (from main file and included headers); otherwis...
const include_cleaner::PragmaIncludes * PragmaIncludes
If set, this is used to map symbol #include path to a potentially different #include path specified b...
std::function< bool(const SourceManager &, FileID)> FileFilter
If this is set, only collect symbols/references from a file if FileFilter(SM, FID) is true.
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"