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)) &&
189 !SM.isWrittenInMainFile(SM.getExpansionLoc(ND.getLocation()));
192 RefKind toRefKind(index::SymbolRoleSet Roles) {
196 bool shouldIndexRelation(
const index::SymbolRelation &R) {
198 return R.Roles &
static_cast<unsigned>(index::SymbolRole::RelationBaseOf);
207 CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
209 llvm::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
213 const ASTContext &ASTCtx,
215 bool IsMainFileOnly) {
217 if (ND.getDeclName().isEmpty())
225 if (!IsMainFileOnly && ND.isInAnonymousNamespace())
232 const auto *DeclCtx = ND.getDeclContext();
233 switch (DeclCtx->getDeclKind()) {
234 case Decl::TranslationUnit:
236 case Decl::LinkageSpec:
238 case Decl::ObjCProtocol:
239 case Decl::ObjCInterface:
240 case Decl::ObjCCategory:
241 case Decl::ObjCCategoryImpl:
242 case Decl::ObjCImplementation:
247 if (!isa<RecordDecl>(DeclCtx))
252 if (isPrivateProtoDecl(ND))
259 const Decl *
D, index::SymbolRoleSet Roles,
260 llvm::ArrayRef<index::SymbolRelation> Relations, SourceLocation
Loc,
261 index::IndexDataConsumer::ASTNodeInfo ASTNode) {
262 assert(ASTCtx && PP.get() &&
"ASTContext and Preprocessor must be set.");
263 assert(CompletionAllocator && CompletionTUInfo);
264 assert(ASTNode.OrigD);
268 if (D->getLocation().isInvalid())
273 if ((ASTNode.OrigD->getFriendObjectKind() !=
274 Decl::FriendObjectKind::FOK_None) &&
275 !(Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
279 if ((Roles & static_cast<unsigned>(index::SymbolRole::NameReference)))
284 if (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None)
285 D = CanonicalDecls.try_emplace(D, ASTNode.OrigD).first->second;
286 const NamedDecl *ND = dyn_cast<NamedDecl>(
D);
292 auto &SM = ASTCtx->getSourceManager();
293 auto SpellingLoc = SM.getSpellingLoc(Loc);
294 if (Opts.CountReferences &&
295 (Roles & static_cast<unsigned>(index::SymbolRole::Reference)) &&
296 SM.getFileID(SpellingLoc) == SM.getMainFileID())
297 ReferencedDecls.insert(ND);
307 processRelations(*ND, *ID, Relations);
309 bool CollectRef =
static_cast<unsigned>(Opts.RefFilter) & Roles;
311 !(Roles & (
static_cast<unsigned>(index::SymbolRole::Declaration) |
312 static_cast<unsigned>(index::SymbolRole::Definition)));
314 if (IsOnlyRef && !CollectRef)
320 bool IsMainFileOnly =
321 SM.isWrittenInMainFile(SM.getExpansionLoc(ND->getBeginLoc())) &&
322 !ASTCtx->getLangOpts().IsHeaderFile;
324 if (ASTNode.OrigD->isImplicit() ||
328 if (CollectRef && !IsMainFileOnly && !isa<NamespaceDecl>(ND) &&
329 (Opts.RefsInHeaders || SM.getFileID(SpellingLoc) == SM.getMainFileID()))
330 DeclRefs[ND].emplace_back(SpellingLoc, Roles);
338 auto *OriginalDecl = dyn_cast<NamedDecl>(ASTNode.OrigD);
342 const Symbol *BasicSymbol = Symbols.
find(*ID);
344 BasicSymbol = addDeclaration(*ND, std::move(*ID), IsMainFileOnly);
345 else if (isPreferredDeclaration(*OriginalDecl, Roles))
350 BasicSymbol = addDeclaration(*OriginalDecl, std::move(*ID), IsMainFileOnly);
352 if (Roles & static_cast<unsigned>(index::SymbolRole::Definition))
353 addDefinition(*OriginalDecl, *BasicSymbol);
360 index::SymbolRoleSet Roles,
361 SourceLocation
Loc) {
362 if (!Opts.CollectMacro)
366 const auto &SM = PP->getSourceManager();
367 auto DefLoc = MI->getDefinitionLoc();
370 if (MI->isBuiltinMacro())
374 bool IsMainFileSymbol = SM.isInMainFile(SM.getExpansionLoc(DefLoc));
375 if (IsMainFileSymbol && !Opts.CollectMainFileSymbols)
379 if (SM.isWrittenInBuiltinFile(DefLoc))
385 if (Opts.CountReferences &&
386 (Roles & static_cast<unsigned>(index::SymbolRole::Reference)) &&
387 SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID())
388 ReferencedMacros.insert(Name);
391 if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
392 Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
400 if (Symbols.
find(*ID) !=
nullptr)
404 S.
ID = std::move(*ID);
405 S.
Name = Name->getName();
406 if (!IsMainFileSymbol) {
410 S.
SymInfo = index::getSymbolInfoForMacro(*MI);
413 shouldIndexFile(SM, SM.getFileID(Loc), Opts, &FilesToIndexCache);
415 getTokenLocation(DefLoc, SM, Opts, PP->getLangOpts(), FileURI))
418 CodeCompletionResult SymbolCompletion(Name);
419 const auto *CCS = SymbolCompletion.CreateCodeCompletionStringForMacro(
420 *PP, *CompletionAllocator, *CompletionTUInfo);
427 IndexedMacros.insert(Name);
428 setIncludeLocation(S, DefLoc);
433 void SymbolCollector::processRelations(
434 const NamedDecl &ND,
const SymbolID &ID,
435 ArrayRef<index::SymbolRelation> Relations) {
437 if (!dyn_cast<TagDecl>(&ND))
440 for (
const auto &R : Relations) {
441 if (!shouldIndexRelation(R))
444 const Decl *
Object = R.RelatedSymbol;
459 this->Relations.insert(
460 Relation{ID, index::SymbolRole::RelationBaseOf, *ObjectID});
464 void SymbolCollector::setIncludeLocation(
const Symbol &S, SourceLocation
Loc) {
465 if (Opts.CollectIncludePath)
466 if (shouldCollectIncludePath(S.
SymInfo.Kind))
470 PP->getSourceManager().getDecomposedExpansionLoc(Loc).first;
475 auto IncRef = [
this](
const SymbolID &ID) {
476 if (
const auto *S = Symbols.
find(ID)) {
482 for (
const NamedDecl *ND : ReferencedDecls) {
487 if (Opts.CollectMacro) {
490 for (
const IdentifierInfo *II : IndexedMacros) {
491 if (
const auto *MI = PP->getMacroDefinition(II).getMacroInfo())
492 if (
auto ID =
getSymbolID(*II, MI, PP->getSourceManager()))
493 if (MI->isUsedForHeaderGuard())
497 for (
const IdentifierInfo *II : ReferencedMacros) {
498 if (
const auto *MI = PP->getMacroDefinition(II).getMacroInfo())
499 if (
auto ID =
getSymbolID(*II, MI, PP->getSourceManager()))
507 llvm::SmallString<256>
QName;
508 for (
const auto &
Entry : IncludeFiles)
511 QName.append(S->
Name);
512 if (
auto Header = getIncludeHeader(QName,
Entry.second)) {
519 const auto &SM = ASTCtx->getSourceManager();
520 llvm::DenseMap<FileID, std::string> URICache;
521 auto GetURI = [&](FileID FID) -> llvm::Optional<std::string> {
522 auto Found = URICache.find(FID);
523 if (Found == URICache.end()) {
524 if (
auto *FileEntry = SM.getFileEntryForID(FID)) {
525 auto FileURI = toURI(SM, FileEntry->getName(), Opts);
526 Found = URICache.insert({FID, FileURI}).first;
534 return Found->second;
537 if (
auto MainFileURI = GetURI(SM.getMainFileID())) {
538 for (
const auto &It : DeclRefs) {
540 for (
const auto &LocAndRole : It.second) {
541 auto FileID = SM.getFileID(LocAndRole.first);
543 shouldIndexFile(SM, FileID, Opts, &FilesToIndexCache);
544 if (
auto FileURI = GetURI(FileID)) {
549 R.Location.End =
Range.second;
550 R.Location.FileURI = FileURI->c_str();
551 R.Kind = toRefKind(LocAndRole.second);
559 ReferencedDecls.clear();
560 ReferencedMacros.clear();
562 FilesToIndexCache.clear();
563 HeaderIsSelfContainedCache.clear();
564 IncludeFiles.clear();
567 const Symbol *SymbolCollector::addDeclaration(
const NamedDecl &ND,
SymbolID ID,
568 bool IsMainFileOnly) {
569 auto &
Ctx = ND.getASTContext();
570 auto &SM =
Ctx.getSourceManager();
573 S.
ID = std::move(ID);
591 assert(Loc.isValid() &&
"Invalid source location for NamedDecl");
593 shouldIndexFile(SM, SM.getFileID(Loc), Opts, &FilesToIndexCache);
595 getTokenLocation(Loc, SM, Opts, ASTCtx->getLangOpts(), FileURI))
599 if (ND.getAvailability() == AR_Deprecated)
604 assert(ASTCtx && PP.get() &&
"ASTContext and Preprocessor must be set.");
606 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
607 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
608 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
611 std::string Documentation =
615 if (Opts.StoreAllDocumentation)
618 return Symbols.
find(S.
ID);
629 llvm::Optional<OpaqueType> TypeStorage;
633 S.
Type = TypeStorage->raw();
637 setIncludeLocation(S, ND.getLocation());
638 return Symbols.
find(S.
ID);
641 void SymbolCollector::addDefinition(
const NamedDecl &ND,
651 const auto &SM = ND.getASTContext().getSourceManager();
653 shouldIndexFile(SM, SM.getFileID(Loc), Opts, &FilesToIndexCache);
655 getTokenLocation(Loc, SM, Opts, ASTCtx->getLangOpts(), FileURI))
663 llvm::Optional<std::string>
664 SymbolCollector::getIncludeHeader(llvm::StringRef
QName, FileID FID) {
665 const SourceManager &SM = ASTCtx->getSourceManager();
666 const FileEntry *FE = SM.getFileEntryForID(FID);
667 if (!FE || FE->getName().empty())
669 llvm::StringRef
Filename = FE->getName();
673 llvm::StringRef Canonical = Opts.Includes->mapHeader(Filename, QName);
675 if (Canonical.startswith(
"<") || Canonical.startswith(
"\""))
676 return Canonical.str();
677 if (Canonical != Filename)
678 return toURI(SM, Canonical, Opts);
680 if (!isSelfContainedHeader(FID)) {
683 if (Filename.endswith(
".inc") || Filename.endswith(
".def"))
684 return getIncludeHeader(QName, SM.getFileID(SM.getIncludeLoc(FID)));
689 return toURI(SM, Filename, Opts);
692 bool SymbolCollector::isSelfContainedHeader(FileID FID) {
695 const SourceManager &SM = ASTCtx->getSourceManager();
696 const FileEntry *FE = SM.getFileEntryForID(FID);
699 if (!PP->getHeaderSearchInfo().isFileMultipleIncludeGuarded(FE))
703 if (isDontIncludeMeHeader(SM.getBufferData(FID)))
708 auto R = HeaderIsSelfContainedCache.try_emplace(FID,
false);
710 R.first->second = Compute();
711 return R.first->second;
717 if (!Line.consume_front(
"#"))
720 return Line.startswith(
"if");
725 if (!Line.consume_front(
"#"))
728 if (!Line.startswith(
"error"))
730 return Line.contains_lower(
"includ");
733 bool SymbolCollector::isDontIncludeMeHeader(llvm::StringRef Content) {
734 llvm::StringRef
Line;
736 Content = Content.take_front(100 * 100);
737 for (
unsigned I = 0; I < 100 && !Content.empty(); ++I) {
738 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)
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.