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))};
149 bool shouldIndexFile(
const SourceManager &SM, FileID FID,
150 const SymbolCollector::Options &Opts,
151 llvm::DenseMap<FileID, bool> *FilesToIndexCache) {
152 if (!Opts.FileFilter)
154 auto I = FilesToIndexCache->try_emplace(FID);
156 I.first->second = Opts.FileFilter(SM, FID);
157 return I.first->second;
161 llvm::Optional<SymbolLocation>
162 getTokenLocation(SourceLocation TokLoc,
const SourceManager &SM,
163 const SymbolCollector::Options &Opts,
164 const clang::LangOptions &LangOpts,
165 std::string &FileURIStorage) {
166 auto Path = SM.getFilename(TokLoc);
169 FileURIStorage = toURI(SM, Path, Opts);
171 Result.FileURI = FileURIStorage.c_str();
173 Result.Start =
Range.first;
174 Result.End =
Range.second;
185 bool isPreferredDeclaration(
const NamedDecl &ND, index::SymbolRoleSet Roles) {
186 const auto &SM = ND.getASTContext().getSourceManager();
187 return (Roles & static_cast<unsigned>(index::SymbolRole::Definition)) &&
191 RefKind toRefKind(index::SymbolRoleSet Roles) {
195 bool shouldIndexRelation(
const index::SymbolRelation &R) {
197 return R.Roles &
static_cast<unsigned>(index::SymbolRole::RelationBaseOf);
206 CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
208 llvm::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
212 const ASTContext &ASTCtx,
214 bool IsMainFileOnly) {
216 if (ND.getDeclName().isEmpty())
224 if (!IsMainFileOnly && ND.isInAnonymousNamespace())
231 const auto *DeclCtx = ND.getDeclContext();
232 switch (DeclCtx->getDeclKind()) {
233 case Decl::TranslationUnit:
235 case Decl::LinkageSpec:
237 case Decl::ObjCProtocol:
238 case Decl::ObjCInterface:
239 case Decl::ObjCCategory:
240 case Decl::ObjCCategoryImpl:
241 case Decl::ObjCImplementation:
246 if (!isa<RecordDecl>(DeclCtx))
251 if (isPrivateProtoDecl(ND))
258 const Decl *
D, index::SymbolRoleSet Roles,
259 llvm::ArrayRef<index::SymbolRelation> Relations, SourceLocation
Loc,
260 index::IndexDataConsumer::ASTNodeInfo ASTNode) {
261 assert(ASTCtx && PP.get() &&
"ASTContext and Preprocessor must be set.");
262 assert(CompletionAllocator && CompletionTUInfo);
263 assert(ASTNode.OrigD);
267 if (D->getLocation().isInvalid())
272 if ((ASTNode.OrigD->getFriendObjectKind() !=
273 Decl::FriendObjectKind::FOK_None) &&
274 !(Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
278 if ((Roles & static_cast<unsigned>(index::SymbolRole::NameReference)))
283 if (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None)
284 D = CanonicalDecls.try_emplace(D, ASTNode.OrigD).first->second;
285 const NamedDecl *ND = dyn_cast<NamedDecl>(
D);
291 auto &SM = ASTCtx->getSourceManager();
292 auto SpellingLoc = SM.getSpellingLoc(Loc);
293 if (Opts.CountReferences &&
294 (Roles & static_cast<unsigned>(index::SymbolRole::Reference)) &&
295 SM.getFileID(SpellingLoc) == SM.getMainFileID())
296 ReferencedDecls.insert(ND);
306 processRelations(*ND, *ID, Relations);
308 bool CollectRef =
static_cast<unsigned>(Opts.RefFilter) & Roles;
310 !(Roles & (
static_cast<unsigned>(index::SymbolRole::Declaration) |
311 static_cast<unsigned>(index::SymbolRole::Definition)));
313 if (IsOnlyRef && !CollectRef)
319 bool IsMainFileOnly =
320 SM.isWrittenInMainFile(SM.getExpansionLoc(ND->getBeginLoc())) &&
321 !ASTCtx->getLangOpts().IsHeaderFile;
323 if (ASTNode.OrigD->isImplicit() ||
327 if (CollectRef && !IsMainFileOnly && !isa<NamespaceDecl>(ND) &&
328 (Opts.RefsInHeaders || SM.getFileID(SpellingLoc) == SM.getMainFileID()))
329 DeclRefs[ND].emplace_back(SpellingLoc, Roles);
337 auto *OriginalDecl = dyn_cast<NamedDecl>(ASTNode.OrigD);
341 const Symbol *BasicSymbol = Symbols.
find(*ID);
343 BasicSymbol = addDeclaration(*ND, std::move(*ID), IsMainFileOnly);
344 else if (isPreferredDeclaration(*OriginalDecl, Roles))
349 BasicSymbol = addDeclaration(*OriginalDecl, std::move(*ID), IsMainFileOnly);
351 if (Roles & static_cast<unsigned>(index::SymbolRole::Definition))
352 addDefinition(*OriginalDecl, *BasicSymbol);
359 index::SymbolRoleSet Roles,
360 SourceLocation
Loc) {
361 if (!Opts.CollectMacro)
365 const auto &SM = PP->getSourceManager();
366 auto DefLoc = MI->getDefinitionLoc();
369 if (MI->isBuiltinMacro())
373 bool IsMainFileSymbol = SM.isInMainFile(SM.getExpansionLoc(DefLoc));
374 if (IsMainFileSymbol && !Opts.CollectMainFileSymbols)
378 if (SM.isWrittenInBuiltinFile(DefLoc))
384 if (Opts.CountReferences &&
385 (Roles & static_cast<unsigned>(index::SymbolRole::Reference)) &&
386 SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID())
387 ReferencedMacros.insert(Name);
390 if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
391 Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
399 if (Symbols.
find(*ID) !=
nullptr)
403 S.
ID = std::move(*ID);
404 S.
Name = Name->getName();
405 if (!IsMainFileSymbol) {
409 S.
SymInfo = index::getSymbolInfoForMacro(*MI);
412 shouldIndexFile(SM, SM.getFileID(Loc), Opts, &FilesToIndexCache);
414 getTokenLocation(DefLoc, SM, Opts, PP->getLangOpts(), FileURI))
417 CodeCompletionResult SymbolCompletion(Name);
418 const auto *CCS = SymbolCompletion.CreateCodeCompletionStringForMacro(
419 *PP, *CompletionAllocator, *CompletionTUInfo);
426 IndexedMacros.insert(Name);
427 setIncludeLocation(S, DefLoc);
432 void SymbolCollector::processRelations(
433 const NamedDecl &ND,
const SymbolID &ID,
434 ArrayRef<index::SymbolRelation> Relations) {
436 if (!dyn_cast<TagDecl>(&ND))
439 for (
const auto &R : Relations) {
440 if (!shouldIndexRelation(R))
443 const Decl *
Object = R.RelatedSymbol;
458 this->Relations.insert(
459 Relation{ID, index::SymbolRole::RelationBaseOf, *ObjectID});
463 void SymbolCollector::setIncludeLocation(
const Symbol &S, SourceLocation
Loc) {
464 if (Opts.CollectIncludePath)
465 if (shouldCollectIncludePath(S.
SymInfo.Kind))
469 PP->getSourceManager().getDecomposedExpansionLoc(Loc).first;
474 auto IncRef = [
this](
const SymbolID &ID) {
475 if (
const auto *S = Symbols.
find(ID)) {
481 for (
const NamedDecl *ND : ReferencedDecls) {
486 if (Opts.CollectMacro) {
489 for (
const IdentifierInfo *II : IndexedMacros) {
490 if (
const auto *MI = PP->getMacroDefinition(II).getMacroInfo())
491 if (
auto ID =
getSymbolID(*II, MI, PP->getSourceManager()))
492 if (MI->isUsedForHeaderGuard())
496 for (
const IdentifierInfo *II : ReferencedMacros) {
497 if (
const auto *MI = PP->getMacroDefinition(II).getMacroInfo())
498 if (
auto ID =
getSymbolID(*II, MI, PP->getSourceManager()))
506 llvm::SmallString<256>
QName;
507 for (
const auto &
Entry : IncludeFiles)
510 QName.append(S->
Name);
511 if (
auto Header = getIncludeHeader(QName,
Entry.second)) {
518 const auto &SM = ASTCtx->getSourceManager();
519 llvm::DenseMap<FileID, std::string> URICache;
520 auto GetURI = [&](FileID FID) -> llvm::Optional<std::string> {
521 auto Found = URICache.find(FID);
522 if (Found == URICache.end()) {
523 if (
auto *FileEntry = SM.getFileEntryForID(FID)) {
524 auto FileURI = toURI(SM, FileEntry->getName(), Opts);
525 Found = URICache.insert({FID, FileURI}).first;
533 return Found->second;
536 if (
auto MainFileURI = GetURI(SM.getMainFileID())) {
537 for (
const auto &It : DeclRefs) {
539 for (
const auto &LocAndRole : It.second) {
540 auto FileID = SM.getFileID(LocAndRole.first);
542 shouldIndexFile(SM, FileID, Opts, &FilesToIndexCache);
543 if (
auto FileURI = GetURI(FileID)) {
548 R.Location.End =
Range.second;
549 R.Location.FileURI = FileURI->c_str();
550 R.Kind = toRefKind(LocAndRole.second);
558 ReferencedDecls.clear();
559 ReferencedMacros.clear();
561 FilesToIndexCache.clear();
562 HeaderIsSelfContainedCache.clear();
563 IncludeFiles.clear();
566 const Symbol *SymbolCollector::addDeclaration(
const NamedDecl &ND,
SymbolID ID,
567 bool IsMainFileOnly) {
568 auto &
Ctx = ND.getASTContext();
569 auto &SM =
Ctx.getSourceManager();
572 S.
ID = std::move(ID);
590 assert(Loc.isValid() &&
"Invalid source location for NamedDecl");
592 shouldIndexFile(SM, SM.getFileID(Loc), Opts, &FilesToIndexCache);
594 getTokenLocation(Loc, SM, Opts, ASTCtx->getLangOpts(), FileURI))
598 if (ND.getAvailability() == AR_Deprecated)
603 assert(ASTCtx && PP.get() &&
"ASTContext and Preprocessor must be set.");
605 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
606 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
607 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
610 std::string Documentation =
614 if (Opts.StoreAllDocumentation)
617 return Symbols.
find(S.
ID);
628 llvm::Optional<OpaqueType> TypeStorage;
632 S.
Type = TypeStorage->raw();
636 setIncludeLocation(S, ND.getLocation());
637 return Symbols.
find(S.
ID);
640 void SymbolCollector::addDefinition(
const NamedDecl &ND,
650 const auto &SM = ND.getASTContext().getSourceManager();
652 shouldIndexFile(SM, SM.getFileID(Loc), Opts, &FilesToIndexCache);
654 getTokenLocation(Loc, SM, Opts, ASTCtx->getLangOpts(), FileURI))
662 llvm::Optional<std::string>
663 SymbolCollector::getIncludeHeader(llvm::StringRef
QName, FileID FID) {
664 const SourceManager &SM = ASTCtx->getSourceManager();
665 const FileEntry *FE = SM.getFileEntryForID(FID);
666 if (!FE || FE->getName().empty())
668 llvm::StringRef
Filename = FE->getName();
672 llvm::StringRef Canonical = Opts.Includes->mapHeader(Filename, QName);
674 if (Canonical.startswith(
"<") || Canonical.startswith(
"\""))
675 return Canonical.str();
676 if (Canonical != Filename)
677 return toURI(SM, Canonical, Opts);
679 if (!isSelfContainedHeader(FID)) {
682 if (Filename.endswith(
".inc") || Filename.endswith(
".def"))
683 return getIncludeHeader(QName, SM.getFileID(SM.getIncludeLoc(FID)));
688 return toURI(SM, Filename, Opts);
691 bool SymbolCollector::isSelfContainedHeader(FileID FID) {
694 const SourceManager &SM = ASTCtx->getSourceManager();
695 const FileEntry *FE = SM.getFileEntryForID(FID);
698 if (!PP->getHeaderSearchInfo().isFileMultipleIncludeGuarded(FE))
702 if (isDontIncludeMeHeader(SM.getBufferData(FID)))
707 auto R = HeaderIsSelfContainedCache.try_emplace(FID,
false);
709 R.first->second = Compute();
710 return R.first->second;
716 if (!Line.consume_front(
"#"))
719 return Line.startswith(
"if");
724 if (!Line.consume_front(
"#"))
727 if (!Line.startswith(
"error"))
729 return Line.contains_lower(
"includ");
732 bool SymbolCollector::isDontIncludeMeHeader(llvm::StringRef Content) {
733 llvm::StringRef
Line;
735 Content = Content.take_front(100 * 100);
736 for (
unsigned I = 0; I < 100 && !Content.empty(); ++I) {
737 std::tie(Line, Content) = Content.split(
'\n');
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.
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.