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"
25#include "clang/AST/Decl.h"
26#include "clang/AST/DeclBase.h"
27#include "clang/AST/DeclObjC.h"
28#include "clang/AST/DeclTemplate.h"
29#include "clang/AST/DeclarationName.h"
30#include "clang/AST/Expr.h"
31#include "clang/Basic/FileEntry.h"
32#include "clang/Basic/LangOptions.h"
33#include "clang/Basic/SourceLocation.h"
34#include "clang/Basic/SourceManager.h"
35#include "clang/Index/IndexSymbol.h"
36#include "clang/Lex/Preprocessor.h"
37#include "clang/Lex/Token.h"
38#include "clang/Tooling/Inclusions/HeaderAnalysis.h"
39#include "clang/Tooling/Inclusions/StandardLibrary.h"
40#include "llvm/ADT/ArrayRef.h"
41#include "llvm/ADT/DenseMap.h"
42#include "llvm/ADT/SmallVector.h"
43#include "llvm/ADT/StringRef.h"
44#include "llvm/Support/ErrorHandling.h"
45#include "llvm/Support/FileSystem.h"
46#include "llvm/Support/Path.h"
59const NamedDecl &getTemplateOrThis(
const NamedDecl &ND) {
60 if (
auto *T = ND.getDescribedTemplate())
69bool isPrivateProtoDecl(
const NamedDecl &ND) {
70 const auto &SM = ND.getASTContext().getSourceManager();
75 if (ND.getIdentifier() ==
nullptr)
77 auto Name = ND.getIdentifier()->getName();
78 if (!
Name.contains(
'_'))
89 return (ND.getKind() != Decl::EnumConstant) || llvm::any_of(
Name, islower);
96 using SK = index::SymbolKind;
107 case SK::EnumConstant:
118std::pair<SymbolLocation::Position, SymbolLocation::Position>
119getTokenRange(SourceLocation TokLoc,
const SourceManager &SM,
120 const LangOptions &LangOpts) {
121 auto CreatePosition = [&SM](SourceLocation
Loc) {
123 SymbolLocation::Position
Pos;
124 Pos.setLine(LSPLoc.line);
125 Pos.setColumn(LSPLoc.character);
129 auto TokenLength = clang::Lexer::MeasureTokenLength(TokLoc, SM, LangOpts);
130 return {CreatePosition(TokLoc),
131 CreatePosition(TokLoc.getLocWithOffset(TokenLength))};
139bool isPreferredDeclaration(
const NamedDecl &ND, index::SymbolRoleSet Roles) {
140 const auto &SM = ND.getASTContext().getSourceManager();
141 if (isa<TagDecl>(ND))
142 return (Roles &
static_cast<unsigned>(index::SymbolRole::Definition)) &&
144 if (
const auto *
ID = dyn_cast<ObjCInterfaceDecl>(&ND))
145 return ID->isThisDeclarationADefinition();
146 if (
const auto *PD = dyn_cast<ObjCProtocolDecl>(&ND))
147 return PD->isThisDeclarationADefinition();
151RefKind toRefKind(index::SymbolRoleSet Roles,
bool Spelled =
false) {
153 if (Roles &
static_cast<unsigned>(index::SymbolRole::Declaration))
155 if (Roles &
static_cast<unsigned>(index::SymbolRole::Definition))
157 if (Roles &
static_cast<unsigned>(index::SymbolRole::Reference))
164std::optional<RelationKind> indexableRelation(
const index::SymbolRelation &R) {
165 if (R.Roles &
static_cast<unsigned>(index::SymbolRole::RelationBaseOf))
167 if (R.Roles &
static_cast<unsigned>(index::SymbolRole::RelationOverrideOf))
173bool isSpelled(SourceLocation
Loc,
const NamedDecl &ND) {
174 auto Name = ND.getDeclName();
175 const auto NameKind =
Name.getNameKind();
176 if (NameKind != DeclarationName::Identifier &&
177 NameKind != DeclarationName::CXXConstructorName &&
178 NameKind != DeclarationName::ObjCZeroArgSelector &&
179 NameKind != DeclarationName::ObjCOneArgSelector &&
180 NameKind != DeclarationName::ObjCMultiArgSelector)
182 const auto &
AST = ND.getASTContext();
183 const auto &SM =
AST.getSourceManager();
184 const auto &LO =
AST.getLangOpts();
186 if (clang::Lexer::getRawToken(
Loc, Tok, SM, LO))
188 auto TokSpelling = clang::Lexer::getSpelling(Tok, SM, LO);
189 if (
const auto *MD = dyn_cast<ObjCMethodDecl>(&ND))
190 return TokSpelling == MD->getSelector().getNameForSlot(0);
191 return TokSpelling ==
Name.getAsString();
199 struct FrameworkUmbrellaSpelling {
201 std::optional<std::string> PublicHeader;
204 std::optional<std::string> PrivateHeader;
210 const SourceManager &SM;
211 const include_cleaner::PragmaIncludes *PI;
212 llvm::StringRef FallbackDir;
213 llvm::DenseMap<const FileEntry *, const std::string *> CacheFEToURI;
214 llvm::StringMap<std::string> CachePathToURI;
215 llvm::DenseMap<FileID, llvm::StringRef> CacheFIDToInclude;
216 llvm::StringMap<std::string> CachePathToFrameworkSpelling;
217 llvm::StringMap<FrameworkUmbrellaSpelling>
218 CacheFrameworkToUmbrellaHeaderSpelling;
223 : PP(PP), SM(SM), PI(Opts.PragmaIncludes), FallbackDir(Opts.FallbackDir) {
228 const std::string &
toURI(
const FileEntryRef FE) {
229 auto R = CacheFEToURI.try_emplace(FE);
232 R.first->second = &toURIInternal(CanonPath ? *CanonPath : FE.getName());
234 return *R.first->second;
241 if (
auto File = SM.getFileManager().getFileRef(
Path))
243 return toURIInternal(
Path);
251 auto R = CacheFIDToInclude.try_emplace(FID);
253 R.first->second = getIncludeHeaderUncached(FID);
254 return R.first->second;
266 auto Canonical = SysHeaderMapping.
mapHeader(HeaderPath);
267 if (Canonical.empty())
270 assert(Canonical.starts_with(
"<") || Canonical.starts_with(
"\""));
277 const std::string &toURIInternal(llvm::StringRef
Path) {
278 auto R = CachePathToURI.try_emplace(
Path);
280 llvm::SmallString<256> AbsPath =
Path;
281 if (!llvm::sys::path::is_absolute(AbsPath) && !FallbackDir.empty())
282 llvm::sys::fs::make_absolute(FallbackDir, AbsPath);
283 assert(llvm::sys::path::is_absolute(AbsPath) &&
284 "If the VFS can't make paths absolute, a FallbackDir must be "
286 llvm::sys::path::remove_dots(AbsPath,
true);
289 return R.first->second;
292 struct FrameworkHeaderPath {
295 llvm::StringRef HeadersParentDir;
298 llvm::StringRef HeaderSubpath;
300 bool IsPrivateHeader;
303 std::optional<FrameworkHeaderPath>
304 splitFrameworkHeaderPath(llvm::StringRef
Path) {
305 using namespace llvm::sys;
306 path::reverse_iterator I = path::rbegin(
Path);
307 path::reverse_iterator Prev = I;
308 path::reverse_iterator
E = path::rend(
Path);
310 if (*I ==
"Headers") {
311 FrameworkHeaderPath HeaderPath;
312 HeaderPath.HeadersParentDir =
Path.substr(0, I -
E);
313 HeaderPath.HeaderSubpath =
Path.substr(Prev -
E);
314 HeaderPath.IsPrivateHeader =
false;
317 if (*I ==
"PrivateHeaders") {
318 FrameworkHeaderPath HeaderPath;
319 HeaderPath.HeadersParentDir =
Path.substr(0, I -
E);
320 HeaderPath.HeaderSubpath =
Path.substr(Prev -
E);
321 HeaderPath.IsPrivateHeader =
true;
336 std::optional<std::string>
337 getFrameworkUmbrellaSpelling(llvm::StringRef Framework,
338 const HeaderSearch &HS,
339 FrameworkHeaderPath &HeaderPath) {
340 auto Res = CacheFrameworkToUmbrellaHeaderSpelling.try_emplace(Framework);
341 auto *CachedSpelling = &Res.first->second;
343 return HeaderPath.IsPrivateHeader ? CachedSpelling->PrivateHeader
344 : CachedSpelling->PublicHeader;
346 SmallString<256> UmbrellaPath(HeaderPath.HeadersParentDir);
347 llvm::sys::path::append(UmbrellaPath,
"Headers", Framework +
".h");
349 llvm::vfs::Status Status;
350 auto StatErr = HS.getFileMgr().getNoncachedStatValue(UmbrellaPath, Status);
352 CachedSpelling->PublicHeader = llvm::formatv(
"<{0}/{0}.h>", Framework);
354 UmbrellaPath = HeaderPath.HeadersParentDir;
355 llvm::sys::path::append(UmbrellaPath,
"PrivateHeaders",
356 Framework +
"_Private.h");
358 StatErr = HS.getFileMgr().getNoncachedStatValue(UmbrellaPath, Status);
360 CachedSpelling->PrivateHeader =
361 llvm::formatv(
"<{0}/{0}_Private.h>", Framework);
363 return HeaderPath.IsPrivateHeader ? CachedSpelling->PrivateHeader
364 : CachedSpelling->PublicHeader;
371 std::optional<llvm::StringRef>
372 getFrameworkHeaderIncludeSpelling(FileEntryRef FE, llvm::StringRef Framework,
374 auto Res = CachePathToFrameworkSpelling.try_emplace(FE.getName());
375 auto *CachedHeaderSpelling = &Res.first->second;
377 return llvm::StringRef(*CachedHeaderSpelling);
379 auto HeaderPath = splitFrameworkHeaderPath(FE.getName());
383 CachePathToFrameworkSpelling.erase(Res.first);
386 if (
auto UmbrellaSpelling =
387 getFrameworkUmbrellaSpelling(Framework, HS, *HeaderPath)) {
388 *CachedHeaderSpelling = *UmbrellaSpelling;
389 return llvm::StringRef(*CachedHeaderSpelling);
392 *CachedHeaderSpelling =
393 llvm::formatv(
"<{0}/{1}>", Framework, HeaderPath->HeaderSubpath).str();
394 return llvm::StringRef(*CachedHeaderSpelling);
397 llvm::StringRef getIncludeHeaderUncached(FileID FID) {
398 const auto FE = SM.getFileEntryRefForID(FID);
399 if (!FE || FE->getName().empty())
402 if (
auto Verbatim = PI->getPublic(*FE); !Verbatim.empty())
405 llvm::StringRef
Filename = FE->getName();
411 auto &HS = PP->getHeaderSearchInfo();
412 if (
const auto *HFI = HS.getExistingFileInfo(*FE))
413 if (!HFI->Framework.empty())
415 getFrameworkHeaderIncludeSpelling(*FE, HFI->Framework, HS))
418 if (!tooling::isSelfContainedHeader(*FE, PP->getSourceManager(),
419 PP->getHeaderSearchInfo())) {
424 return getIncludeHeaderUncached(SM.getFileID(SM.getIncludeLoc(FID)));
434std::optional<SymbolLocation>
435SymbolCollector::getTokenLocation(SourceLocation TokLoc) {
436 const auto &SM = ASTCtx->getSourceManager();
437 const auto FE = SM.getFileEntryRefForID(SM.getFileID(TokLoc));
441 SymbolLocation Result;
442 Result.FileURI = HeaderFileURIs->toURI(*FE).c_str();
443 auto Range = getTokenRange(TokLoc, SM, ASTCtx->getLangOpts());
444 Result.Start =
Range.first;
445 Result.End =
Range.second;
455 HeaderFileURIs = std::make_unique<HeaderFileURICache>(
456 this->PP, ASTCtx->getSourceManager(), Opts);
457 CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
459 std::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
463 const ASTContext &ASTCtx,
465 bool IsMainFileOnly) {
467 if (ND.getDeclName().isEmpty())
471 if (IsMainFileOnly && !Opts.CollectMainFileSymbols)
475 if (!IsMainFileOnly && ND.isInAnonymousNamespace())
479 if (index::isFunctionLocalSymbol(&ND))
480 return isa<RecordDecl>(ND) ||
481 (ND.isCXXInstanceMember() && ND.isFunctionOrFunctionTemplate());
487 const auto *DeclCtx = ND.getDeclContext();
488 switch (DeclCtx->getDeclKind()) {
489 case Decl::TranslationUnit:
490 case Decl::Namespace:
491 case Decl::LinkageSpec:
493 case Decl::ObjCProtocol:
494 case Decl::ObjCInterface:
495 case Decl::ObjCCategory:
496 case Decl::ObjCCategoryImpl:
497 case Decl::ObjCImplementation:
502 if (!isa<RecordDecl>(DeclCtx))
507 if (isPrivateProtoDecl(ND))
509 if (!Opts.CollectReserved &&
511 ASTCtx.getSourceManager().isInSystemHeader(ND.getLocation()))
521 const auto *ND = dyn_cast<NamedDecl>(Enclosing);
525 Enclosing = dyn_cast_or_null<Decl>(Enclosing->getDeclContext());
532 const Decl *D, index::SymbolRoleSet Roles,
533 llvm::ArrayRef<index::SymbolRelation> Relations, SourceLocation
Loc,
534 index::IndexDataConsumer::ASTNodeInfo
ASTNode) {
535 assert(ASTCtx && PP && HeaderFileURIs);
536 assert(CompletionAllocator && CompletionTUInfo);
541 if (D->getLocation().isInvalid())
546 if ((
ASTNode.OrigD->getFriendObjectKind() !=
547 Decl::FriendObjectKind::FOK_None) &&
548 !(Roles &
static_cast<unsigned>(index::SymbolRole::Definition)))
553 if (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None)
554 D = CanonicalDecls.try_emplace(D,
ASTNode.OrigD).first->second;
557 bool DeclIsCanonical =
false;
560 if (
const auto *IID = dyn_cast<ObjCImplementationDecl>(D)) {
561 DeclIsCanonical =
true;
562 if (
const auto *CID = IID->getClassInterface())
563 if (
const auto *DD = CID->getDefinition())
564 if (!DD->isImplicitInterfaceDecl())
569 if (
const auto *CID = dyn_cast<ObjCCategoryImplDecl>(D)) {
570 DeclIsCanonical =
true;
571 if (
const auto *CD = CID->getCategoryDecl())
574 const NamedDecl *ND = dyn_cast<NamedDecl>(D);
578 auto ID = getSymbolIDCached(ND);
584 auto &SM = ASTCtx->getSourceManager();
586 (Roles &
static_cast<unsigned>(index::SymbolRole::Reference)) &&
587 SM.getFileID(SM.getSpellingLoc(
Loc)) == SM.getMainFileID())
588 ReferencedSymbols.insert(
ID);
593 bool IsMainFileOnly =
594 SM.isWrittenInMainFile(SM.getExpansionLoc(ND->getBeginLoc())) &&
595 !
isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
596 ASTCtx->getLangOpts());
598 if (
ASTNode.OrigD->isImplicit() ||
606 processRelations(*ND,
ID, Relations);
608 bool CollectRef =
static_cast<bool>(Opts.
RefFilter & toRefKind(Roles));
615 ND->isExternallyVisible()) &&
616 !isa<NamespaceDecl>(ND)) {
617 auto FileLoc = SM.getFileLoc(
Loc);
618 auto FID = SM.getFileID(FileLoc);
620 addRef(
ID, SymbolRef{FileLoc, FID, Roles,
622 isSpelled(FileLoc, *ND)});
626 if (!(Roles & (
static_cast<unsigned>(index::SymbolRole::Declaration) |
627 static_cast<unsigned>(index::SymbolRole::Definition))))
633 auto *OriginalDecl = dyn_cast<NamedDecl>(
ASTNode.OrigD);
638 if (isPreferredDeclaration(*OriginalDecl, Roles))
643 BasicSymbol = addDeclaration(*OriginalDecl, std::move(
ID), IsMainFileOnly);
644 else if (!BasicSymbol || DeclIsCanonical)
645 BasicSymbol = addDeclaration(*ND, std::move(
ID), IsMainFileOnly);
647 if (Roles &
static_cast<unsigned>(index::SymbolRole::Definition))
648 addDefinition(*OriginalDecl, *BasicSymbol);
654 assert(HeaderFileURIs && PP);
655 const auto &SM = PP->getSourceManager();
656 const auto MainFileEntryRef = SM.getFileEntryRefForID(SM.getMainFileID());
657 assert(MainFileEntryRef);
659 const std::string &MainFileURI = HeaderFileURIs->toURI(*MainFileEntryRef);
661 for (
const auto &IDToRefs : MacroRefsToIndex.
MacroRefs) {
662 for (
const auto &MacroRef : IDToRefs.second) {
663 const auto &
Range = MacroRef.toRange(SM);
664 bool IsDefinition = MacroRef.IsDefinition;
672 Refs.
insert(IDToRefs.first, R);
675 S.ID = IDToRefs.first;
678 S.Name =
toSourceCode(SM, SourceRange(StartLoc, EndLoc));
679 S.SymInfo.Kind = index::SymbolKind::Macro;
680 S.SymInfo.SubKind = index::SymbolSubKind::None;
681 S.SymInfo.Properties = index::SymbolPropertySet();
682 S.SymInfo.Lang = index::SymbolLanguage::C;
684 S.CanonicalDeclaration = R.
Location;
687 if (!HeaderFileURIs->getIncludeHeader(SM.getMainFileID()).empty()) {
699 index::SymbolRoleSet Roles,
700 SourceLocation
Loc) {
703 if (MI->isBuiltinMacro())
706 const auto &SM = PP->getSourceManager();
707 auto DefLoc = MI->getDefinitionLoc();
710 if (SM.isWrittenInBuiltinFile(DefLoc) ||
711 SM.isWrittenInCommandLineFile(DefLoc) ||
712 Name->getName() ==
"__GCC_HAVE_DWARF2_CFI_ASM")
715 auto ID = getSymbolIDCached(
Name->getName(), MI, SM);
719 auto SpellingLoc = SM.getSpellingLoc(
Loc);
720 bool IsMainFileOnly =
721 SM.isInMainFile(SM.getExpansionLoc(DefLoc)) &&
722 !
isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
723 ASTCtx->getLangOpts());
725 if ((
static_cast<unsigned>(Opts.
RefFilter) & Roles) && !IsMainFileOnly &&
726 (Opts.
RefsInHeaders || SM.getFileID(SpellingLoc) == SM.getMainFileID())) {
730 addRef(
ID, SymbolRef{
Loc, SM.getFileID(
Loc), Roles,
nullptr,
746 (Roles &
static_cast<unsigned>(index::SymbolRole::Reference)) &&
747 SM.getFileID(SpellingLoc) == SM.getMainFileID())
748 ReferencedSymbols.insert(
ID);
752 if (!(Roles &
static_cast<unsigned>(index::SymbolRole::Declaration) ||
753 Roles &
static_cast<unsigned>(index::SymbolRole::Definition)))
757 if (Symbols.
find(
ID) !=
nullptr)
761 S.ID = std::move(
ID);
762 S.Name =
Name->getName();
763 if (!IsMainFileOnly) {
767 S.SymInfo = index::getSymbolInfoForMacro(*MI);
771 if (
auto DeclLoc = getTokenLocation(DefLoc))
772 S.CanonicalDeclaration = *DeclLoc;
774 CodeCompletionResult SymbolCompletion(
Name);
775 const auto *CCS = SymbolCompletion.CreateCodeCompletionStringForMacro(
776 *PP, *CompletionAllocator, *CompletionTUInfo);
780 SymbolCompletion.CursorKind);
784 IndexedMacros.insert(
Name);
786 setIncludeLocation(S, DefLoc, include_cleaner::Macro{
Name, DefLoc});
791void SymbolCollector::processRelations(
793 ArrayRef<index::SymbolRelation> Relations) {
794 for (
const auto &R : Relations) {
795 auto RKind = indexableRelation(R);
800 auto ObjectID = getSymbolIDCached(
Object);
814 this->Relations.insert({
ID, *RKind, ObjectID});
816 this->Relations.insert({ObjectID, *RKind,
ID});
820void SymbolCollector::setIncludeLocation(
const Symbol &S, SourceLocation DefLoc,
821 const include_cleaner::Symbol &Sym) {
822 const auto &SM = PP->getSourceManager();
829 if (FileID FID = SM.getDecomposedExpansionLoc(DefLoc).first; FID.isValid())
830 IncludeFiles[S.ID] = FID;
835 SymbolProviders[S.ID] =
840 tooling::stdlib::Lang
Lang = tooling::stdlib::Lang::CXX;
842 Lang = tooling::stdlib::Lang::C;
843 else if(!LangOpts.CPlusPlus)
846 if (S->Scope ==
"std::" && S->Name ==
"move") {
847 if (!S->Signature.contains(
','))
849 return "<algorithm>";
852 if (
auto StdSym = tooling::stdlib::Symbol::named(S->Scope, S->Name,
Lang))
853 if (
auto Header = StdSym->header())
854 return Header->name();
860 for (
const auto &
ID : ReferencedSymbols) {
861 if (
const auto *S = Symbols.
find(
ID)) {
872 for (
const IdentifierInfo *II : IndexedMacros) {
873 if (
const auto *MI = PP->getMacroDefinition(II).getMacroInfo())
875 getSymbolIDCached(II->getName(), MI, PP->getSourceManager()))
876 if (MI->isUsedForHeaderGuard())
880 llvm::DenseMap<FileID, bool> FileToContainsImportsOrObjC;
881 llvm::DenseMap<include_cleaner::Header, std::string> HeaderSpelling;
884 for (
const auto &[SID, Providers] : SymbolProviders) {
889 FileID FID = IncludeFiles.lookup(SID);
892 auto CollectDirectives = shouldCollectIncludePath(S->SymInfo.Kind);
897 auto [It, Inserted] = FileToContainsImportsOrObjC.try_emplace(FID);
899 It->second = FilesWithObjCConstructs.contains(FID) ||
900 tooling::codeContainsImports(
901 ASTCtx->getSourceManager().getBufferData(FID));
911 llvm::StringRef IncludeHeader =
getStdHeader(S, ASTCtx->getLangOpts());
912 if (IncludeHeader.empty())
913 IncludeHeader = HeaderFileURIs->getIncludeHeader(FID);
915 if (!IncludeHeader.empty()) {
917 NewSym.IncludeHeaders.push_back({IncludeHeader, 1, Directives});
933 auto SelfContainedProvider =
934 [
this](llvm::ArrayRef<include_cleaner::Header> Providers)
935 -> std::optional<include_cleaner::Header> {
936 for (
const auto &H : Providers) {
937 if (H.kind() != include_cleaner::Header::Physical)
939 if (tooling::isSelfContainedHeader(H.physical(), PP->getSourceManager(),
940 PP->getHeaderSearchInfo()))
945 const auto OptionalProvider = SelfContainedProvider(Providers);
946 if (!OptionalProvider)
948 const auto &H = *OptionalProvider;
949 const auto [SpellingIt, Inserted] = HeaderSpelling.try_emplace(H);
951 auto &SM = ASTCtx->getSourceManager();
952 if (H.kind() == include_cleaner::Header::Kind::Physical) {
956 HeaderFileURIs->mapCanonical(H.physical().getName());
958 SpellingIt->second = Canonical;
961 else if (tooling::isSelfContainedHeader(H.physical(), SM,
962 PP->getHeaderSearchInfo()))
964 HeaderFileURIs->toURI(H.physical());
966 SpellingIt->second = include_cleaner::spellHeader(
967 {H, PP->getHeaderSearchInfo(),
968 SM.getFileEntryForID(SM.getMainFileID())});
972 if (!SpellingIt->second.empty()) {
974 NewSym.IncludeHeaders.push_back({SpellingIt->second, 1, Directives});
979 ReferencedSymbols.clear();
980 IncludeFiles.clear();
981 SymbolProviders.clear();
982 FilesWithObjCConstructs.clear();
985const Symbol *SymbolCollector::addDeclaration(
const NamedDecl &ND,
SymbolID ID,
986 bool IsMainFileOnly) {
987 auto &Ctx = ND.getASTContext();
988 auto &SM = Ctx.getSourceManager();
991 S.ID = std::move(
ID);
997 S.TemplateSpecializationArgs = TemplateSpecializationArgs;
1004 if (!IsMainFileOnly)
1006 S.SymInfo = index::getSymbolInfo(&ND);
1008 assert(
Loc.isValid() &&
"Invalid source location for NamedDecl");
1010 auto FID = SM.getFileID(
Loc);
1012 if (
auto DeclLoc = getTokenLocation(
Loc))
1013 S.CanonicalDeclaration = *DeclLoc;
1016 if (ND.getAvailability() == AR_Deprecated)
1021 assert(ASTCtx && PP &&
"ASTContext and Preprocessor must be set.");
1023 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
1024 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
1025 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
1028 std::string Documentation =
1033 S.Documentation = Documentation;
1035 return Symbols.
find(S.ID);
1037 S.Documentation = Documentation;
1041 SymbolCompletion.CursorKind);
1047 std::optional<OpaqueType> TypeStorage;
1051 S.Type = TypeStorage->raw();
1055 setIncludeLocation(S, ND.getLocation(), include_cleaner::Symbol{ND});
1056 if (S.SymInfo.Lang == index::SymbolLanguage::ObjC)
1057 FilesWithObjCConstructs.insert(FID);
1058 return Symbols.
find(S.ID);
1061void SymbolCollector::addDefinition(
const NamedDecl &ND,
1062 const Symbol &DeclSym) {
1063 if (DeclSym.Definition)
1065 const auto &SM = ND.getASTContext().getSourceManager();
1068 auto DefLoc = getTokenLocation(
Loc);
1076 S.Definition = *DefLoc;
1083 auto I = FilesToIndexCache.try_emplace(FID);
1085 I.first->second = Opts.
FileFilter(ASTCtx->getSourceManager(), FID);
1086 return I.first->second;
1089void SymbolCollector::addRef(
SymbolID ID,
const SymbolRef &SR) {
1090 const auto &SM = ASTCtx->getSourceManager();
1093 if (
const auto FE = SM.getFileEntryRefForID(SR.FID)) {
1094 auto Range = getTokenRange(SR.Loc, SM, ASTCtx->getLangOpts());
1099 R.
Kind = toRefKind(SR.Roles, SR.Spelled);
1100 R.
Container = getSymbolIDCached(SR.Container);
1105SymbolID SymbolCollector::getSymbolIDCached(
const Decl *D) {
1106 auto It = DeclToIDCache.try_emplace(D, SymbolID{});
1109 return It.first->second;
1113 const MacroInfo *MI,
1114 const SourceManager &SM) {
1115 auto It = MacroToIDCache.try_emplace(MI, SymbolID{});
1118 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.
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.
SymbolID Container
The ID of the symbol whose definition contains this reference.
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.
@ VisibleOutsideFile
Symbol is visible to other files (not e.g. a static helper function).
@ Include
#include "header.h"
@ Import
#import "header.h"