19 #include "clang/AST/Decl.h" 20 #include "clang/AST/DeclBase.h" 21 #include "clang/AST/DeclCXX.h" 22 #include "clang/AST/DeclTemplate.h" 23 #include "clang/Basic/SourceLocation.h" 24 #include "clang/Basic/SourceManager.h" 25 #include "clang/Basic/Specifiers.h" 26 #include "clang/Index/IndexSymbol.h" 27 #include "clang/Index/IndexingAction.h" 28 #include "clang/Index/USRGeneration.h" 29 #include "clang/Lex/Preprocessor.h" 30 #include "llvm/Support/Casting.h" 31 #include "llvm/Support/FileSystem.h" 32 #include "llvm/Support/MemoryBuffer.h" 33 #include "llvm/Support/Path.h" 41 const NamedDecl &getTemplateOrThis(
const NamedDecl &ND) {
42 if (
auto T = ND.getDescribedTemplate())
56 std::string toURI(
const SourceManager &SM, llvm::StringRef
Path,
57 const SymbolCollector::Options &Opts) {
58 llvm::SmallString<128> AbsolutePath(Path);
61 AbsolutePath = *CanonPath;
65 if (!llvm::sys::path::is_absolute(AbsolutePath) && !Opts.FallbackDir.empty())
67 llvm::sys::path::remove_dots(AbsolutePath,
true);
72 static const char *PROTO_HEADER_COMMENT =
73 "// Generated by the protocol buffer compiler. DO NOT EDIT!";
81 bool isPrivateProtoDecl(
const NamedDecl &ND) {
82 const auto &SM = ND.getASTContext().getSourceManager();
87 auto FID = SM.getFileID(
Loc);
89 if (!SM.getBufferData(FID).startswith(PROTO_HEADER_COMMENT))
93 if (ND.getIdentifier() ==
nullptr)
95 auto Name = ND.getIdentifier()->getName();
96 if (!
Name.contains(
'_'))
133 std::pair<SymbolLocation::Position, SymbolLocation::Position>
134 getTokenRange(SourceLocation TokLoc,
const SourceManager &SM,
135 const LangOptions &LangOpts) {
136 auto CreatePosition = [&SM](SourceLocation
Loc) {
138 SymbolLocation::Position Pos;
139 Pos.setLine(LSPLoc.line);
140 Pos.setColumn(LSPLoc.character);
144 auto TokenLength = clang::Lexer::MeasureTokenLength(TokLoc, SM, LangOpts);
145 return {CreatePosition(TokLoc),
146 CreatePosition(TokLoc.getLocWithOffset(TokenLength))};
150 llvm::Optional<SymbolLocation>
151 getTokenLocation(SourceLocation TokLoc,
const SourceManager &SM,
152 const SymbolCollector::Options &Opts,
153 const clang::LangOptions &LangOpts,
154 std::string &FileURIStorage) {
155 auto Path = SM.getFilename(TokLoc);
158 FileURIStorage = toURI(SM, Path, Opts);
160 Result.FileURI = FileURIStorage.c_str();
162 Result.Start =
Range.first;
163 Result.End =
Range.second;
174 bool isPreferredDeclaration(
const NamedDecl &ND, index::SymbolRoleSet Roles) {
175 const auto &SM = ND.getASTContext().getSourceManager();
176 return (Roles & static_cast<unsigned>(index::SymbolRole::Definition)) &&
180 RefKind toRefKind(index::SymbolRoleSet Roles) {
184 bool shouldIndexRelation(
const index::SymbolRelation &R) {
186 return R.Roles &
static_cast<unsigned>(index::SymbolRole::RelationBaseOf);
195 CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
197 llvm::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
201 const ASTContext &ASTCtx,
203 bool IsMainFileOnly) {
205 if (ND.getDeclName().isEmpty())
213 if (!IsMainFileOnly && ND.isInAnonymousNamespace())
220 const auto *DeclCtx = ND.getDeclContext();
221 switch (DeclCtx->getDeclKind()) {
222 case Decl::TranslationUnit:
224 case Decl::LinkageSpec:
226 case Decl::ObjCProtocol:
227 case Decl::ObjCInterface:
228 case Decl::ObjCCategory:
229 case Decl::ObjCCategoryImpl:
230 case Decl::ObjCImplementation:
235 if (!isa<RecordDecl>(DeclCtx))
240 if (isPrivateProtoDecl(ND))
247 const Decl *
D, index::SymbolRoleSet Roles,
248 llvm::ArrayRef<index::SymbolRelation> Relations, SourceLocation
Loc,
249 index::IndexDataConsumer::ASTNodeInfo ASTNode) {
250 assert(ASTCtx && PP.get() &&
"ASTContext and Preprocessor must be set.");
251 assert(CompletionAllocator && CompletionTUInfo);
252 assert(ASTNode.OrigD);
256 if (D->getLocation().isInvalid())
261 if ((ASTNode.OrigD->getFriendObjectKind() !=
262 Decl::FriendObjectKind::FOK_None) &&
263 !(Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
267 if ((Roles & static_cast<unsigned>(index::SymbolRole::NameReference)))
272 if (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None)
273 D = CanonicalDecls.try_emplace(D, ASTNode.OrigD).first->second;
274 const NamedDecl *ND = dyn_cast<NamedDecl>(
D);
280 auto &SM = ASTCtx->getSourceManager();
281 auto SpellingLoc = SM.getSpellingLoc(Loc);
282 if (Opts.CountReferences &&
283 (Roles & static_cast<unsigned>(index::SymbolRole::Reference)) &&
284 SM.getFileID(SpellingLoc) == SM.getMainFileID())
285 ReferencedDecls.insert(ND);
295 processRelations(*ND, *ID, Relations);
297 bool CollectRef =
static_cast<unsigned>(Opts.RefFilter) & Roles;
299 !(Roles & (
static_cast<unsigned>(index::SymbolRole::Declaration) |
300 static_cast<unsigned>(index::SymbolRole::Definition)));
302 if (IsOnlyRef && !CollectRef)
308 bool IsMainFileOnly =
309 SM.isWrittenInMainFile(SM.getExpansionLoc(ND->getBeginLoc())) &&
310 !ASTCtx->getLangOpts().IsHeaderFile;
312 if (ASTNode.OrigD->isImplicit() ||
316 if (CollectRef && !IsMainFileOnly && !isa<NamespaceDecl>(ND) &&
317 (Opts.RefsInHeaders || SM.getFileID(SpellingLoc) == SM.getMainFileID()))
318 DeclRefs[ND].emplace_back(SpellingLoc, Roles);
326 auto *OriginalDecl = dyn_cast<NamedDecl>(ASTNode.OrigD);
330 const Symbol *BasicSymbol = Symbols.
find(*ID);
332 BasicSymbol = addDeclaration(*ND, std::move(*ID), IsMainFileOnly);
333 else if (isPreferredDeclaration(*OriginalDecl, Roles))
338 BasicSymbol = addDeclaration(*OriginalDecl, std::move(*ID), IsMainFileOnly);
340 if (Roles & static_cast<unsigned>(index::SymbolRole::Definition))
341 addDefinition(*OriginalDecl, *BasicSymbol);
348 index::SymbolRoleSet Roles,
349 SourceLocation
Loc) {
350 if (!Opts.CollectMacro)
354 const auto &SM = PP->getSourceManager();
355 auto DefLoc = MI->getDefinitionLoc();
358 if (MI->isBuiltinMacro())
362 bool IsMainFileSymbol = SM.isInMainFile(SM.getExpansionLoc(DefLoc));
363 if (IsMainFileSymbol && !Opts.CollectMainFileSymbols)
367 if (SM.isWrittenInBuiltinFile(DefLoc))
373 if (Opts.CountReferences &&
374 (Roles & static_cast<unsigned>(index::SymbolRole::Reference)) &&
375 SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID())
376 ReferencedMacros.insert(Name);
379 if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
380 Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
388 if (Symbols.
find(*ID) !=
nullptr)
392 S.
ID = std::move(*ID);
393 S.
Name = Name->getName();
394 if (!IsMainFileSymbol) {
398 S.
SymInfo = index::getSymbolInfoForMacro(*MI);
403 getTokenLocation(DefLoc, SM, Opts, PP->getLangOpts(), FileURI))
406 CodeCompletionResult SymbolCompletion(Name);
407 const auto *CCS = SymbolCompletion.CreateCodeCompletionStringForMacro(
408 *PP, *CompletionAllocator, *CompletionTUInfo);
415 IndexedMacros.insert(Name);
416 setIncludeLocation(S, DefLoc);
421 void SymbolCollector::processRelations(
422 const NamedDecl &ND,
const SymbolID &ID,
423 ArrayRef<index::SymbolRelation> Relations) {
425 if (!dyn_cast<TagDecl>(&ND))
428 for (
const auto &R : Relations) {
429 if (!shouldIndexRelation(R))
432 const Decl *
Object = R.RelatedSymbol;
447 this->Relations.insert(
448 Relation{ID, index::SymbolRole::RelationBaseOf, *ObjectID});
452 void SymbolCollector::setIncludeLocation(
const Symbol &S, SourceLocation
Loc) {
453 if (Opts.CollectIncludePath)
454 if (shouldCollectIncludePath(S.
SymInfo.Kind))
458 PP->getSourceManager().getDecomposedExpansionLoc(Loc).first;
463 auto IncRef = [
this](
const SymbolID &ID) {
464 if (
const auto *S = Symbols.
find(ID)) {
470 for (
const NamedDecl *ND : ReferencedDecls) {
475 if (Opts.CollectMacro) {
478 for (
const IdentifierInfo *II : IndexedMacros) {
479 if (
const auto *MI = PP->getMacroDefinition(II).getMacroInfo())
480 if (
auto ID =
getSymbolID(*II, MI, PP->getSourceManager()))
481 if (MI->isUsedForHeaderGuard())
485 for (
const IdentifierInfo *II : ReferencedMacros) {
486 if (
const auto *MI = PP->getMacroDefinition(II).getMacroInfo())
487 if (
auto ID =
getSymbolID(*II, MI, PP->getSourceManager()))
495 llvm::SmallString<256>
QName;
496 for (
const auto &
Entry : IncludeFiles)
499 QName.append(S->
Name);
500 if (
auto Header = getIncludeHeader(QName,
Entry.second)) {
507 const auto &SM = ASTCtx->getSourceManager();
508 llvm::DenseMap<FileID, std::string> URICache;
509 auto GetURI = [&](FileID FID) -> llvm::Optional<std::string> {
510 auto Found = URICache.find(FID);
511 if (Found == URICache.end()) {
512 if (
auto *FileEntry = SM.getFileEntryForID(FID)) {
513 auto FileURI = toURI(SM, FileEntry->getName(), Opts);
514 Found = URICache.insert({FID, FileURI}).first;
522 return Found->second;
525 if (
auto MainFileURI = GetURI(SM.getMainFileID())) {
526 for (
const auto &It : DeclRefs) {
528 for (
const auto &LocAndRole : It.second) {
529 auto FileID = SM.getFileID(LocAndRole.first);
532 if (
auto FileURI = GetURI(FileID)) {
537 R.Location.End =
Range.second;
538 R.Location.FileURI = FileURI->c_str();
539 R.Kind = toRefKind(LocAndRole.second);
547 ReferencedDecls.clear();
548 ReferencedMacros.clear();
550 FilesToIndexCache.clear();
551 HeaderIsSelfContainedCache.clear();
552 IncludeFiles.clear();
555 const Symbol *SymbolCollector::addDeclaration(
const NamedDecl &ND,
SymbolID ID,
556 bool IsMainFileOnly) {
557 auto &
Ctx = ND.getASTContext();
558 auto &SM =
Ctx.getSourceManager();
561 S.
ID = std::move(ID);
579 assert(Loc.isValid() &&
"Invalid source location for NamedDecl");
583 getTokenLocation(Loc, SM, Opts, ASTCtx->getLangOpts(), FileURI))
587 if (ND.getAvailability() == AR_Deprecated)
592 assert(ASTCtx && PP.get() &&
"ASTContext and Preprocessor must be set.");
594 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
595 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
596 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
599 std::string Documentation =
603 if (Opts.StoreAllDocumentation)
606 return Symbols.
find(S.
ID);
617 llvm::Optional<OpaqueType> TypeStorage;
621 S.
Type = TypeStorage->raw();
625 setIncludeLocation(S, ND.getLocation());
626 return Symbols.
find(S.
ID);
629 void SymbolCollector::addDefinition(
const NamedDecl &ND,
639 const auto &SM = ND.getASTContext().getSourceManager();
643 getTokenLocation(Loc, SM, Opts, ASTCtx->getLangOpts(), FileURI))
651 llvm::Optional<std::string>
652 SymbolCollector::getIncludeHeader(llvm::StringRef
QName, FileID FID) {
653 const SourceManager &SM = ASTCtx->getSourceManager();
654 const FileEntry *FE = SM.getFileEntryForID(FID);
655 if (!FE || FE->getName().empty())
657 llvm::StringRef
Filename = FE->getName();
661 llvm::StringRef Canonical = Opts.Includes->mapHeader(Filename, QName);
663 if (Canonical.startswith(
"<") || Canonical.startswith(
"\""))
664 return Canonical.str();
665 if (Canonical != Filename)
666 return toURI(SM, Canonical, Opts);
668 if (!isSelfContainedHeader(FID)) {
671 if (Filename.endswith(
".inc") || Filename.endswith(
".def"))
672 return getIncludeHeader(QName, SM.getFileID(SM.getIncludeLoc(FID)));
677 return toURI(SM, Filename, Opts);
680 bool SymbolCollector::isSelfContainedHeader(FileID FID) {
683 const SourceManager &SM = ASTCtx->getSourceManager();
684 const FileEntry *FE = SM.getFileEntryForID(FID);
687 if (!PP->getHeaderSearchInfo().isFileMultipleIncludeGuarded(FE))
691 if (isDontIncludeMeHeader(SM.getBufferData(FID)))
696 auto R = HeaderIsSelfContainedCache.try_emplace(FID,
false);
698 R.first->second = Compute();
699 return R.first->second;
705 if (!Line.consume_front(
"#"))
708 return Line.startswith(
"if");
713 if (!Line.consume_front(
"#"))
716 if (!Line.startswith(
"error"))
718 return Line.contains_lower(
"includ");
721 bool SymbolCollector::isDontIncludeMeHeader(llvm::StringRef Content) {
722 llvm::StringRef
Line;
724 Content = Content.take_front(100 * 100);
725 for (
unsigned I = 0; I < 100 && !Content.empty(); ++I) {
726 std::tie(Line, Content) = Content.split(
'\n');
734 if (!Opts.FileFilter)
736 auto I = FilesToIndexCache.try_emplace(FID);
738 I.first->second = Opts.FileFilter(ASTCtx->getSourceManager(), FID);
739 return I.first->second;
SourceLocation Loc
'#' location in the include directive
bool handleMacroOccurence(const IdentifierInfo *Name, const MacroInfo *MI, index::SymbolRoleSet Roles, SourceLocation Loc) override
std::string printQualifiedName(const NamedDecl &ND)
Returns the qualified name of ND.
Represents a relation between two symbols.
llvm::Optional< SymbolID > getSymbolID(const Decl *D)
Gets the symbol ID for a declaration, if possible.
const Symbol * find(const SymbolID &ID)
Returns the symbol with an ID, if it exists. Valid until insert/remove.
clang::find_all_symbols::SymbolInfo::SymbolKind SymbolKind
std::pair< StringRef, StringRef > splitQualifiedName(StringRef QName)
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
Represents a symbol occurrence in the source file.
SourceLocation findNameLoc(const clang::Decl *D)
Find the identifier source location of the given D.
Symbol is visible to other files (not e.g. a static helper function).
void insert(const Symbol &S)
Adds a symbol, overwriting any existing one with the same ID.
def make_absolute(f, directory)
llvm::StringRef Scope
The containing namespace. e.g. "" (global), "ns::" (top-level namespace).
std::string getDocComment(const ASTContext &Ctx, const CodeCompletionResult &Result, bool CommentsFromHeaders)
Gets a minimally formatted documentation comment of Result, with comment markers stripped.
Documents should not be synced at all.
std::string printTemplateSpecializationArgs(const NamedDecl &ND)
Prints template arguments of a decl as written in the source code, including enclosing '<' and '>'...
void erase(const SymbolID &ID)
Removes the symbol with an ID, if it exists.
void initialize(ASTContext &Ctx) override
unsigned References
The number of translation units that reference this symbol from their main file.
SymbolID ID
The ID of the symbol.
index::SymbolInfo SymInfo
The symbol information, like symbol kind.
std::string getReturnType(const CodeCompletionString &CCS)
Gets detail to be used as the detail field in an LSP completion item.
Symbol is an implementation detail.
SymbolLocation Definition
The location of the symbol's definition, if one was found.
std::vector< SymbolDetails > getSymbolInfo(ParsedAST &AST, Position Pos)
Get info about symbols at Pos.
std::string Filename
Filename as a string.
Whether or not this symbol is meant to be used for the code completion.
bool handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles, ArrayRef< index::SymbolRelation > Relations, SourceLocation Loc, index::IndexDataConsumer::ASTNodeInfo ASTNode) override
bool isIndexedForCodeCompletion(const NamedDecl &ND, ASTContext &ASTCtx)
llvm::SmallVector< IncludeHeaderWithReferences, 1 > IncludeHeaders
One Symbol can potentially be incuded via different headers.
static bool isErrorAboutInclude(llvm::StringRef Line)
llvm::StringRef Signature
A brief description of the symbol that can be appended in the completion candidate list...
SymbolLocation Location
The source location where the symbol is named.
llvm::StringRef Documentation
Documentation including comment for the symbol declaration.
std::string Path
A typedef to represent a file path.
static constexpr llvm::StringLiteral Name
SymbolLocation CanonicalDeclaration
The location of the preferred declaration of the symbol.
RefKind
Describes the kind of a cross-reference.
std::string formatDocumentation(const CodeCompletionString &CCS, llvm::StringRef DocComment)
Assembles formatted documentation for a completion result.
SymbolCollector(Options Opts)
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
bool shouldIndexFile(FileID FID)
Returns true if we are interested in references and declarations from FID.
std::string SnippetSuffix
Position sourceLocToPosition(const SourceManager &SM, SourceLocation Loc)
Turn a SourceLocation into a [line, column] pair.
bool CollectMainFileSymbols
Collect symbols local to main-files, such as static functions and symbols inside an anonymous namespa...
static bool shouldCollectSymbol(const NamedDecl &ND, const ASTContext &ASTCtx, const Options &Opts, bool IsMainFileSymbol)
Returns true is ND should be collected.
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
The class presents a C++ symbol, e.g.
Position Start
The symbol range, using half-open range [Start, End).
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
llvm::StringRef Name
The unqualified name of the symbol, e.g. "bar" (for ns::bar).
CharSourceRange Range
SourceRange for the file name.
llvm::Optional< std::string > getCanonicalPath(const FileEntry *F, const SourceManager &SourceMgr)
Get the canonical path of F.
void getSignature(const CodeCompletionString &CCS, std::string *Signature, std::string *Snippet, std::string *RequiredQualifiers, bool CompletingPattern)
Formats the signature for an item, as a display string and snippet.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > 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 isImplementationDetail(const Decl *D)
Returns true if the declaration is considered implementation detail based on heuristics.
llvm::StringRef CompletionSnippetSuffix
What to insert when completing this symbol, after the symbol name.
Indicates if the symbol is deprecated.
static bool isIf(llvm::StringRef Line)
llvm::StringRef Type
Raw representation of the OpaqueType of the symbol, used for scoring purposes.
SymbolOrigin Origin
Where this symbol came from. Usually an index provides a constant value.
llvm::StringRef TemplateSpecializationArgs
Argument list in human-readable format, will be displayed to help disambiguate between different spec...
llvm::StringRef ReturnType
Type when this symbol is used in an expression.
static llvm::Optional< OpaqueType > fromCompletionResult(ASTContext &Ctx, const CodeCompletionResult &R)
Create a type from a code completion result.