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) {
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())) {
466 if (Filename.ends_with(
".inc") || Filename.ends_with(
".def"))
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();
634 if (Opts.CountReferences &&
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));
663 (!IsMainFileOnly || Opts.CollectMainFileRefs ||
664 ND->isExternallyVisible()) &&
665 !isa<NamespaceDecl>(ND)) {
666 auto FileLoc = SM.getFileLoc(Loc);
667 auto FID = SM.getFileID(FileLoc);
668 if (Opts.RefsInHeaders || FID == SM.getMainFileID()) {
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);
686 const Symbol *BasicSymbol = Symbols.find(ID);
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 &SR = MacroRef.toSourceRange(SM);
718 bool IsDefinition = MacroRef.IsDefinition;
726 Refs.insert(IDToRefs.first, R);
729 S.
ID = IDToRefs.first;
731 S.
SymInfo.Kind = index::SymbolKind::Macro;
732 S.
SymInfo.SubKind = index::SymbolSubKind::None;
733 S.
SymInfo.Properties = index::SymbolPropertySet();
734 S.
SymInfo.Lang = index::SymbolLanguage::C;
739 if (!HeaderFileURIs->getIncludeHeader(SM.getMainFileID()).empty()) {
751 index::SymbolRoleSet Roles,
752 SourceLocation Loc) {
755 if (MI->isBuiltinMacro())
758 const auto &SM = PP->getSourceManager();
759 auto DefLoc = MI->getDefinitionLoc();
762 if (SM.isWrittenInBuiltinFile(DefLoc) ||
763 SM.isWrittenInCommandLineFile(DefLoc) ||
764 Name->getName() ==
"__GCC_HAVE_DWARF2_CFI_ASM")
767 auto ID = getSymbolIDCached(Name->getName(), MI, SM);
771 auto SpellingLoc = SM.getSpellingLoc(Loc);
772 bool IsMainFileOnly =
773 SM.isInMainFile(SM.getExpansionLoc(DefLoc)) &&
774 !
isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
775 ASTCtx->getLangOpts());
777 if ((
static_cast<unsigned>(Opts.RefFilter) & Roles) && !IsMainFileOnly &&
778 (Opts.RefsInHeaders || SM.getFileID(SpellingLoc) == SM.getMainFileID())) {
783 SymbolRef{Loc, SM.getFileID(Loc), Roles, index::SymbolKind::Macro,
789 if (!Opts.CollectMacro)
793 if (IsMainFileOnly && !Opts.CollectMainFileSymbols)
799 if (Opts.CountReferences &&
800 (Roles &
static_cast<unsigned>(index::SymbolRole::Reference)) &&
801 SM.getFileID(SpellingLoc) == SM.getMainFileID())
802 ReferencedSymbols.insert(ID);
806 if (!(Roles &
static_cast<unsigned>(index::SymbolRole::Declaration) ||
807 Roles &
static_cast<unsigned>(index::SymbolRole::Definition)))
811 if (Symbols.find(ID) !=
nullptr)
815 S.
ID = std::move(ID);
816 S.
Name = Name->getName();
817 if (!IsMainFileOnly) {
821 S.
SymInfo = index::getSymbolInfoForMacro(*MI);
825 if (
auto DeclLoc = getTokenLocation(DefLoc))
828 CodeCompletionResult SymbolCompletion(Name);
829 const auto *CCS = SymbolCompletion.CreateCodeCompletionStringForMacro(
830 *PP, *CompletionAllocator, *CompletionTUInfo);
831 std::string Signature;
832 std::string SnippetSuffix;
833 getSignature(*CCS, &Signature, &SnippetSuffix, SymbolCompletion.Kind,
834 SymbolCompletion.CursorKind);
838 IndexedMacros.insert(Name);
840 setIncludeLocation(S, DefLoc, include_cleaner::Macro{Name, DefLoc});
845void SymbolCollector::processRelations(
846 const NamedDecl &ND,
const SymbolID &ID,
847 ArrayRef<index::SymbolRelation> Relations) {
848 for (
const auto &R : Relations) {
849 auto RKind = indexableRelation(R);
852 const Decl *
Object = R.RelatedSymbol;
854 auto ObjectID = getSymbolIDCached(
Object);
868 this->Relations.insert({ID, *RKind, ObjectID});
870 this->Relations.insert({ObjectID, *RKind, ID});
874void SymbolCollector::setIncludeLocation(
const Symbol &S, SourceLocation DefLoc,
875 const include_cleaner::Symbol &Sym) {
876 const auto &SM = PP->getSourceManager();
877 if (!Opts.CollectIncludePath ||
883 if (FileID FID = SM.getDecomposedExpansionLoc(DefLoc).first; FID.isValid())
884 IncludeFiles[S.ID] = FID;
889 SymbolProviders[S.ID] =
890 include_cleaner::headersForSymbol(Sym, *PP, Opts.PragmaIncludes);
894 tooling::stdlib::Lang Lang = tooling::stdlib::Lang::CXX;
896 Lang = tooling::stdlib::Lang::C;
897 else if(!LangOpts.CPlusPlus)
900 if (S->
Scope ==
"std::" && S->
Name ==
"move") {
903 return "<algorithm>";
906 if (
auto StdSym = tooling::stdlib::Symbol::named(S->
Scope, S->
Name, Lang))
907 if (
auto Header = StdSym->header())
908 return Header->name();
914 for (
const auto &ID : ReferencedSymbols) {
915 if (
const auto *S = Symbols.find(ID)) {
923 if (Opts.CollectMacro) {
926 for (
const IdentifierInfo *II : IndexedMacros) {
927 if (
const auto *MI = PP->getMacroDefinition(II).getMacroInfo())
929 getSymbolIDCached(II->getName(), MI, PP->getSourceManager()))
930 if (MI->isUsedForHeaderGuard())
934 llvm::DenseMap<FileID, bool> FileToContainsImportsOrObjC;
935 llvm::DenseMap<include_cleaner::Header, std::string> HeaderSpelling;
938 for (
const auto &[SID, Providers] : SymbolProviders) {
939 const Symbol *S = Symbols.find(SID);
943 FileID FID = IncludeFiles.lookup(SID);
946 auto CollectDirectives = shouldCollectIncludePath(S->
SymInfo.Kind);
951 auto [It, Inserted] = FileToContainsImportsOrObjC.try_emplace(FID);
953 It->second = FilesWithObjCConstructs.contains(FID) ||
954 tooling::codeContainsImports(
955 ASTCtx->getSourceManager().getBufferData(FID));
965 llvm::StringRef IncludeHeader =
getStdHeader(S, ASTCtx->getLangOpts());
966 if (IncludeHeader.empty())
967 IncludeHeader = HeaderFileURIs->getIncludeHeader(FID);
969 if (!IncludeHeader.empty()) {
972 Symbols.insert(NewSym);
987 auto SelfContainedProvider =
988 [
this](llvm::ArrayRef<include_cleaner::Header> Providers)
989 -> std::optional<include_cleaner::Header> {
990 for (
const auto &H : Providers) {
991 if (H.kind() != include_cleaner::Header::Physical)
993 if (tooling::isSelfContainedHeader(H.physical(), PP->getSourceManager(),
994 PP->getHeaderSearchInfo()))
999 const auto OptionalProvider = SelfContainedProvider(Providers);
1000 if (!OptionalProvider)
1002 const auto &H = *OptionalProvider;
1003 const auto [SpellingIt, Inserted] = HeaderSpelling.try_emplace(H);
1005 auto &SM = ASTCtx->getSourceManager();
1006 if (H.kind() == include_cleaner::Header::Kind::Physical) {
1009 if (
auto Canonical =
1010 HeaderFileURIs->mapCanonical(H.physical().getName());
1012 SpellingIt->second = Canonical;
1015 else if (tooling::isSelfContainedHeader(H.physical(), SM,
1016 PP->getHeaderSearchInfo()))
1017 SpellingIt->second =
1018 HeaderFileURIs->toURI(H.physical());
1020 SpellingIt->second = include_cleaner::spellHeader(
1021 {H, PP->getHeaderSearchInfo(),
1022 SM.getFileEntryForID(SM.getMainFileID())});
1026 if (!SpellingIt->second.empty()) {
1028 NewSym.
IncludeHeaders.push_back({SpellingIt->second, 1, Directives});
1029 Symbols.insert(NewSym);
1033 ReferencedSymbols.clear();
1034 IncludeFiles.clear();
1035 SymbolProviders.clear();
1036 FilesWithObjCConstructs.clear();
1039const Symbol *SymbolCollector::addDeclaration(
const NamedDecl &ND,
SymbolID ID,
1040 bool IsMainFileOnly) {
1041 auto &Ctx = ND.getASTContext();
1042 auto &SM = Ctx.getSourceManager();
1045 S.
ID = std::move(ID);
1058 if (!IsMainFileOnly)
1060 S.
SymInfo = index::getSymbolInfo(&ND);
1062 assert(Loc.isValid() &&
"Invalid source location for NamedDecl");
1064 auto FID = SM.getFileID(Loc);
1066 if (
auto DeclLoc = getTokenLocation(Loc))
1070 if (ND.getAvailability() == AR_Deprecated)
1075 assert(ASTCtx && PP &&
"ASTContext and Preprocessor must be set.");
1077 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
1078 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
1079 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
1082 std::string DocComment;
1083 std::string Documentation;
1085 if (!AlreadyHasDoc) {
1090 const auto UpdateDoc = [&] {
1091 if (!AlreadyHasDoc) {
1092 if (!DocComment.empty())
1098 if (Opts.StoreAllDocumentation)
1101 return Symbols.find(S.
ID);
1104 std::string Signature;
1105 std::string SnippetSuffix;
1106 getSignature(*CCS, &Signature, &SnippetSuffix, SymbolCompletion.Kind,
1107 SymbolCompletion.CursorKind);
1113 std::optional<OpaqueType> TypeStorage;
1117 S.
Type = TypeStorage->raw();
1121 setIncludeLocation(S, ND.getLocation(), include_cleaner::Symbol{ND});
1122 if (S.
SymInfo.Lang == index::SymbolLanguage::ObjC)
1123 FilesWithObjCConstructs.insert(FID);
1124 return Symbols.find(S.
ID);
1127void SymbolCollector::addDefinition(
const NamedDecl &ND,
const Symbol &DeclSym,
1128 bool SkipDocCheck) {
1129 if (DeclSym.Definition)
1131 const auto &SM = ND.getASTContext().getSourceManager();
1134 auto DefLoc = getTokenLocation(Loc);
1142 S.Definition = *DefLoc;
1144 std::string DocComment;
1145 std::string Documentation;
1147 (llvm::isa<FunctionDecl>(ND) || llvm::isa<CXXMethodDecl>(ND))) {
1148 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
1149 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
1150 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
1153 DocComment =
getDocComment(ND.getASTContext(), SymbolCompletion,
1155 if (!S.Documentation.empty())
1156 Documentation = S.Documentation.str() +
'\n' + DocComment;
1159 if (!DocComment.empty())
1161 S.Documentation = Documentation;
1168 if (!Opts.FileFilter)
1170 auto I = FilesToIndexCache.try_emplace(FID);
1172 I.first->second = Opts.FileFilter(ASTCtx->getSourceManager(), FID);
1173 return I.first->second;
1177 using SK = index::SymbolKind;
1178 return Kind == SK::Function || Kind == SK::InstanceMethod ||
1179 Kind == SK::ClassMethod || Kind == SK::StaticMethod ||
1180 Kind == SK::Constructor || Kind == SK::Destructor ||
1181 Kind == SK::ConversionFunction;
1184void SymbolCollector::addRef(SymbolID ID,
const SymbolRef &SR) {
1185 const auto &SM = ASTCtx->getSourceManager();
1188 if (
const auto FE = SM.getFileEntryRefForID(SR.FID)) {
1189 auto Range = getTokenRange(SR.Loc, SM, ASTCtx->getLangOpts());
1191 R.Location.Start = Range.first;
1192 R.Location.End = Range.second;
1193 R.Location.FileURI = HeaderFileURIs->toURI(*FE).c_str();
1194 R.Kind = toRefKind(SR.Roles, SR.Spelled);
1198 R.Container = getSymbolIDCached(SR.Container);
1203SymbolID SymbolCollector::getSymbolIDCached(
const Decl *D) {
1204 auto It = DeclToIDCache.try_emplace(D);
1207 return It.first->second;
1210SymbolID SymbolCollector::getSymbolIDCached(
const llvm::StringRef MacroName,
1211 const MacroInfo *MI,
1212 const SourceManager &SM) {
1213 auto It = MacroToIDCache.try_emplace(MI);
1215 It.first->second =
getSymbolID(MacroName, MI, SM);
1216 return It.first->second;
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.
bool isImplementationDetail(const Decl *D)
Returns true if the declaration is considered implementation detail based on heuristics.
std::string Path
A typedef to represent a file path.
bool hasReservedScope(const DeclContext &DC)
Returns true if this scope would be written with a reserved name.
std::string printQualifiedName(const NamedDecl &ND)
Returns the qualified name of ND.
bool isProtoFile(SourceLocation Loc, const SourceManager &SM)
Returns true if the given location is in a generated protobuf file.
std::string getDocComment(const ASTContext &Ctx, const CodeCompletionResult &Result, bool CommentsFromHeaders)
Gets a minimally formatted documentation comment of Result, with comment markers stripped.
llvm::StringRef getStdHeader(const Symbol *S, const LangOptions &LangOpts)
bool isHeaderFile(llvm::StringRef FileName, std::optional< LangOptions > LangOpts)
Infers whether this is a header from the FileName and LangOpts (if presents).
bool isIndexedForCodeCompletion(const NamedDecl &ND, ASTContext &ASTCtx)
@ 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.
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.