20 #include "clang/AST/Decl.h" 21 #include "clang/AST/DeclBase.h" 22 #include "clang/AST/DeclCXX.h" 23 #include "clang/AST/DeclTemplate.h" 24 #include "clang/Basic/SourceLocation.h" 25 #include "clang/Basic/SourceManager.h" 26 #include "clang/Basic/Specifiers.h" 27 #include "clang/Index/IndexSymbol.h" 28 #include "clang/Index/IndexingAction.h" 29 #include "clang/Index/USRGeneration.h" 30 #include "clang/Lex/Preprocessor.h" 31 #include "llvm/Support/Casting.h" 32 #include "llvm/Support/FileSystem.h" 33 #include "llvm/Support/MemoryBuffer.h" 34 #include "llvm/Support/Path.h" 42 const NamedDecl &getTemplateOrThis(
const NamedDecl &ND) {
43 if (
auto T = ND.getDescribedTemplate())
57 std::string toURI(
const SourceManager &SM, llvm::StringRef
Path,
58 const SymbolCollector::Options &Opts) {
59 llvm::SmallString<128> AbsolutePath(Path);
60 if (
auto File = SM.getFileManager().getFile(Path)) {
62 AbsolutePath = *CanonPath;
67 if (!llvm::sys::path::is_absolute(AbsolutePath) && !Opts.FallbackDir.empty())
69 llvm::sys::path::remove_dots(AbsolutePath,
true);
74 static const char *PROTO_HEADER_COMMENT =
75 "// Generated by the protocol buffer compiler. DO NOT EDIT!";
83 bool isPrivateProtoDecl(
const NamedDecl &ND) {
84 const auto &SM = ND.getASTContext().getSourceManager();
89 auto FID = SM.getFileID(
Loc);
91 if (!SM.getBufferData(FID).startswith(PROTO_HEADER_COMMENT))
95 if (ND.getIdentifier() ==
nullptr)
97 auto Name = ND.getIdentifier()->getName();
98 if (!
Name.contains(
'_'))
135 std::pair<SymbolLocation::Position, SymbolLocation::Position>
136 getTokenRange(SourceLocation TokLoc,
const SourceManager &SM,
137 const LangOptions &LangOpts) {
138 auto CreatePosition = [&SM](SourceLocation
Loc) {
140 SymbolLocation::Position
Pos;
141 Pos.setLine(LSPLoc.line);
142 Pos.setColumn(LSPLoc.character);
146 auto TokenLength = clang::Lexer::MeasureTokenLength(TokLoc, SM, LangOpts);
147 return {CreatePosition(TokLoc),
148 CreatePosition(TokLoc.getLocWithOffset(TokenLength))};
152 llvm::Optional<SymbolLocation>
153 getTokenLocation(SourceLocation TokLoc,
const SourceManager &SM,
154 const SymbolCollector::Options &Opts,
155 const clang::LangOptions &LangOpts,
156 std::string &FileURIStorage) {
157 auto Path = SM.getFilename(TokLoc);
160 FileURIStorage = toURI(SM, Path, Opts);
161 SymbolLocation Result;
162 Result.FileURI = FileURIStorage.c_str();
164 Result.Start =
Range.first;
165 Result.End =
Range.second;
176 bool isPreferredDeclaration(
const NamedDecl &ND, index::SymbolRoleSet Roles) {
177 const auto &SM = ND.getASTContext().getSourceManager();
178 return (Roles & static_cast<unsigned>(index::SymbolRole::Definition)) &&
182 RefKind toRefKind(index::SymbolRoleSet Roles) {
186 bool shouldIndexRelation(
const index::SymbolRelation &R) {
188 return R.Roles &
static_cast<unsigned>(index::SymbolRole::RelationBaseOf);
197 CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
199 std::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
203 const ASTContext &ASTCtx,
205 bool IsMainFileOnly) {
207 if (ND.getDeclName().isEmpty())
215 if (!IsMainFileOnly && ND.isInAnonymousNamespace())
222 const auto *DeclCtx = ND.getDeclContext();
223 switch (DeclCtx->getDeclKind()) {
224 case Decl::TranslationUnit:
226 case Decl::LinkageSpec:
228 case Decl::ObjCProtocol:
229 case Decl::ObjCInterface:
230 case Decl::ObjCCategory:
231 case Decl::ObjCCategoryImpl:
232 case Decl::ObjCImplementation:
237 if (!isa<RecordDecl>(DeclCtx))
242 if (isPrivateProtoDecl(ND))
249 const Decl *D, index::SymbolRoleSet Roles,
250 llvm::ArrayRef<index::SymbolRelation> Relations, SourceLocation
Loc,
251 index::IndexDataConsumer::ASTNodeInfo ASTNode) {
252 assert(ASTCtx && PP.get() &&
"ASTContext and Preprocessor must be set.");
253 assert(CompletionAllocator && CompletionTUInfo);
254 assert(ASTNode.OrigD);
258 if (D->getLocation().isInvalid())
263 if ((ASTNode.OrigD->getFriendObjectKind() !=
264 Decl::FriendObjectKind::FOK_None) &&
265 !(Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
270 if (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None)
271 D = CanonicalDecls.try_emplace(D, ASTNode.OrigD).first->second;
272 const NamedDecl *ND = dyn_cast<NamedDecl>(D);
278 auto &SM = ASTCtx->getSourceManager();
279 if (Opts.CountReferences &&
280 (Roles & static_cast<unsigned>(index::SymbolRole::Reference)) &&
281 SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID())
282 ReferencedDecls.insert(ND);
292 processRelations(*ND, *ID, Relations);
294 bool CollectRef =
static_cast<unsigned>(Opts.RefFilter) & Roles;
296 !(Roles & (
static_cast<unsigned>(index::SymbolRole::Declaration) |
297 static_cast<unsigned>(index::SymbolRole::Definition)));
299 if (IsOnlyRef && !CollectRef)
305 bool IsMainFileOnly =
306 SM.isWrittenInMainFile(SM.getExpansionLoc(ND->getBeginLoc())) &&
307 !
isHeaderFile(SM.getFileEntryForID(SM.getMainFileID())->getName(),
308 ASTCtx->getLangOpts());
310 if (ASTNode.OrigD->isImplicit() ||
318 if (CollectRef && !IsMainFileOnly && !isa<NamespaceDecl>(ND) &&
319 (Opts.RefsInHeaders ||
320 SM.getFileID(SM.getFileLoc(Loc)) == SM.getMainFileID()))
321 DeclRefs[ND].emplace_back(SM.getFileLoc(Loc), Roles);
329 auto *OriginalDecl = dyn_cast<NamedDecl>(ASTNode.OrigD);
333 const Symbol *BasicSymbol = Symbols.
find(*ID);
335 BasicSymbol = addDeclaration(*ND, std::move(*ID), IsMainFileOnly);
336 else if (isPreferredDeclaration(*OriginalDecl, Roles))
341 BasicSymbol = addDeclaration(*OriginalDecl, std::move(*ID), IsMainFileOnly);
343 if (Roles & static_cast<unsigned>(index::SymbolRole::Definition))
344 addDefinition(*OriginalDecl, *BasicSymbol);
351 const auto &SM = PP->getSourceManager();
352 const auto *MainFileEntry = SM.getFileEntryForID(SM.getMainFileID());
353 assert(MainFileEntry);
355 const auto MainFileURI = toURI(SM, MainFileEntry->getName(), Opts);
357 for (
const auto &IDToRefs : MacroRefsToIndex.
MacroRefs) {
358 for (
const auto &
Range : IDToRefs.second) {
367 Refs.
insert(IDToRefs.first, R);
374 index::SymbolRoleSet Roles,
375 SourceLocation
Loc) {
378 const auto &SM = PP->getSourceManager();
379 auto DefLoc = MI->getDefinitionLoc();
380 auto SpellingLoc = SM.getSpellingLoc(Loc);
381 bool IsMainFileSymbol = SM.isInMainFile(SM.getExpansionLoc(DefLoc));
384 if (MI->isBuiltinMacro())
388 if (SM.isWrittenInBuiltinFile(DefLoc))
396 if ((static_cast<unsigned>(Opts.RefFilter) & Roles) && !IsMainFileSymbol &&
397 (Opts.RefsInHeaders || SM.getFileID(SpellingLoc) == SM.getMainFileID()))
398 MacroRefs[*ID].push_back({
Loc, Roles});
401 if (!Opts.CollectMacro)
405 if (IsMainFileSymbol && !Opts.CollectMainFileSymbols)
411 if (Opts.CountReferences &&
412 (Roles & static_cast<unsigned>(index::SymbolRole::Reference)) &&
413 SM.getFileID(SpellingLoc) == SM.getMainFileID())
414 ReferencedMacros.insert(Name);
418 if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
419 Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
423 if (Symbols.
find(*ID) !=
nullptr)
427 S.
ID = std::move(*ID);
428 S.
Name = Name->getName();
429 if (!IsMainFileSymbol) {
433 S.
SymInfo = index::getSymbolInfoForMacro(*MI);
438 getTokenLocation(DefLoc, SM, Opts, PP->getLangOpts(), FileURI))
441 CodeCompletionResult SymbolCompletion(Name);
442 const auto *CCS = SymbolCompletion.CreateCodeCompletionStringForMacro(
443 *PP, *CompletionAllocator, *CompletionTUInfo);
450 IndexedMacros.insert(Name);
451 setIncludeLocation(S, DefLoc);
456 void SymbolCollector::processRelations(
457 const NamedDecl &ND,
const SymbolID &ID,
458 ArrayRef<index::SymbolRelation> Relations) {
460 if (!dyn_cast<TagDecl>(&ND))
463 for (
const auto &R : Relations) {
464 if (!shouldIndexRelation(R))
486 void SymbolCollector::setIncludeLocation(
const Symbol &S, SourceLocation
Loc) {
487 if (Opts.CollectIncludePath)
488 if (shouldCollectIncludePath(S.
SymInfo.Kind))
492 PP->getSourceManager().getDecomposedExpansionLoc(Loc).first;
497 auto IncRef = [
this](
const SymbolID &ID) {
498 if (
const auto *S = Symbols.
find(ID)) {
504 for (
const NamedDecl *ND : ReferencedDecls) {
509 if (Opts.CollectMacro) {
512 for (
const IdentifierInfo *II : IndexedMacros) {
513 if (
const auto *MI = PP->getMacroDefinition(II).getMacroInfo())
514 if (
auto ID =
getSymbolID(II->getName(), MI, PP->getSourceManager()))
515 if (MI->isUsedForHeaderGuard())
519 for (
const IdentifierInfo *II : ReferencedMacros) {
520 if (
const auto *MI = PP->getMacroDefinition(II).getMacroInfo())
521 if (
auto ID =
getSymbolID(II->getName(), MI, PP->getSourceManager()))
529 llvm::SmallString<256>
QName;
530 for (
const auto &
Entry : IncludeFiles)
533 QName.append(S->
Name);
534 if (
auto Header = getIncludeHeader(QName,
Entry.second)) {
541 const auto &SM = ASTCtx->getSourceManager();
542 llvm::DenseMap<FileID, std::string> URICache;
543 auto GetURI = [&](FileID FID) -> llvm::Optional<std::string> {
544 auto Found = URICache.find(FID);
545 if (Found == URICache.end()) {
546 if (
auto *FileEntry = SM.getFileEntryForID(FID)) {
547 auto FileURI = toURI(SM, FileEntry->getName(), Opts);
548 Found = URICache.insert({FID, FileURI}).first;
555 return Found->second;
559 const std::pair<SourceLocation, index::SymbolRoleSet> &LocAndRole) {
560 auto FileID = SM.getFileID(LocAndRole.first);
563 if (
auto FileURI = GetURI(FileID)) {
568 R.Location.End =
Range.second;
569 R.Location.FileURI = FileURI->c_str();
570 R.Kind = toRefKind(LocAndRole.second);
575 for (
const auto &IDAndRefs : MacroRefs) {
576 for (
const auto &LocAndRole : IDAndRefs.second)
577 CollectRef(IDAndRefs.first, LocAndRole);
580 if (
auto MainFileURI = GetURI(SM.getMainFileID())) {
581 for (
const auto &It : DeclRefs) {
583 for (
const auto &LocAndRole : It.second)
584 CollectRef(*ID, LocAndRole);
589 ReferencedDecls.clear();
590 ReferencedMacros.clear();
592 FilesToIndexCache.clear();
593 HeaderIsSelfContainedCache.clear();
594 IncludeFiles.clear();
597 const Symbol *SymbolCollector::addDeclaration(
const NamedDecl &ND,
SymbolID ID,
598 bool IsMainFileOnly) {
599 auto &
Ctx = ND.getASTContext();
600 auto &SM =
Ctx.getSourceManager();
603 S.
ID = std::move(ID);
621 assert(Loc.isValid() &&
"Invalid source location for NamedDecl");
625 getTokenLocation(Loc, SM, Opts, ASTCtx->getLangOpts(), FileURI))
629 if (ND.getAvailability() == AR_Deprecated)
634 assert(ASTCtx && PP.get() &&
"ASTContext and Preprocessor must be set.");
636 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
637 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
638 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
641 std::string Documentation =
645 if (Opts.StoreAllDocumentation)
648 return Symbols.
find(S.
ID);
659 llvm::Optional<OpaqueType> TypeStorage;
663 S.
Type = TypeStorage->raw();
667 setIncludeLocation(S, ND.getLocation());
668 return Symbols.
find(S.
ID);
671 void SymbolCollector::addDefinition(
const NamedDecl &ND,
680 const auto &SM = ND.getASTContext().getSourceManager();
685 getTokenLocation(Loc, SM, Opts, ASTCtx->getLangOpts(), FileURI))
693 llvm::Optional<std::string>
694 SymbolCollector::getIncludeHeader(llvm::StringRef
QName, FileID FID) {
695 const SourceManager &SM = ASTCtx->getSourceManager();
696 const FileEntry *FE = SM.getFileEntryForID(FID);
697 if (!FE || FE->getName().empty())
699 llvm::StringRef
Filename = FE->getName();
703 llvm::StringRef Canonical = Opts.Includes->mapHeader(Filename, QName);
705 if (Canonical.startswith(
"<") || Canonical.startswith(
"\""))
706 return Canonical.str();
707 if (Canonical != Filename)
708 return toURI(SM, Canonical, Opts);
710 if (!isSelfContainedHeader(FID)) {
713 if (Filename.endswith(
".inc") || Filename.endswith(
".def"))
714 return getIncludeHeader(QName, SM.getFileID(SM.getIncludeLoc(FID)));
719 return toURI(SM, Filename, Opts);
722 bool SymbolCollector::isSelfContainedHeader(FileID FID) {
725 const SourceManager &SM = ASTCtx->getSourceManager();
726 const FileEntry *FE = SM.getFileEntryForID(FID);
729 if (!PP->getHeaderSearchInfo().isFileMultipleIncludeGuarded(FE))
733 if (isDontIncludeMeHeader(SM.getBufferData(FID)))
738 auto R = HeaderIsSelfContainedCache.try_emplace(FID,
false);
740 R.first->second = Compute();
741 return R.first->second;
747 if (!Line.consume_front(
"#"))
750 return Line.startswith(
"if");
755 if (!Line.consume_front(
"#"))
758 if (!Line.startswith(
"error"))
760 return Line.contains_lower(
"includ");
763 bool SymbolCollector::isDontIncludeMeHeader(llvm::StringRef Content) {
764 llvm::StringRef
Line;
766 Content = Content.take_front(100 * 100);
767 for (
unsigned I = 0; I < 100 && !Content.empty(); ++I) {
768 std::tie(Line, Content) = Content.split(
'\n');
776 if (!Opts.FileFilter)
778 auto I = FilesToIndexCache.try_emplace(FID);
780 I.first->second = Opts.FileFilter(ASTCtx->getSourceManager(), FID);
781 return I.first->second;
SourceLocation Loc
'#' location in the include directive
bool handleMacroOccurrence(const IdentifierInfo *Name, const MacroInfo *MI, index::SymbolRoleSet Roles, SourceLocation Loc) override
const FunctionDecl * Decl
std::string printQualifiedName(const NamedDecl &ND)
Returns the qualified name of ND.
SourceLocation nameLocation(const clang::Decl &D, const SourceManager &SM)
Find the source location of the identifier for D.
Position start
The range's start position.
Represents a relation between two symbols.
void setColumn(uint32_t Column)
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
void setLine(uint32_t Line)
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.
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.
llvm::DenseMap< SymbolID, std::vector< Range > > MacroRefs
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 isIndexedForCodeCompletion(const NamedDecl &ND, ASTContext &ASTCtx)
llvm::SmallVector< IncludeHeaderWithReferences, 1 > IncludeHeaders
One Symbol can potentially be included 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.
void handleMacros(const MainFileMacros &MacroRefsToIndex)
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...
bool handleDeclOccurrence(const Decl *D, index::SymbolRoleSet Roles, ArrayRef< index::SymbolRelation > Relations, SourceLocation Loc, index::IndexDataConsumer::ASTNodeInfo ASTNode) override
int line
Line position in a document (zero-based).
int character
Character offset on a line in a document (zero-based).
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.
void insert(const SymbolID &ID, const Ref &S)
Adds a ref to the slab. Deep copy: Strings will be owned by the slab.
bool isHeaderFile(llvm::StringRef FileName, llvm::Optional< LangOptions > LangOpts)
Infers whether this is a header from the FileName and LangOpts (if presents).
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)
Position end
The range's end position.
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.