18 #include "clang/AST/Decl.h" 19 #include "clang/AST/DeclBase.h" 20 #include "clang/AST/DeclCXX.h" 21 #include "clang/AST/DeclTemplate.h" 22 #include "clang/Basic/SourceLocation.h" 23 #include "clang/Basic/SourceManager.h" 24 #include "clang/Basic/Specifiers.h" 25 #include "clang/Index/IndexSymbol.h" 26 #include "clang/Index/USRGeneration.h" 27 #include "llvm/Support/Casting.h" 28 #include "llvm/Support/FileSystem.h" 29 #include "llvm/Support/MemoryBuffer.h" 30 #include "llvm/Support/Path.h" 38 const NamedDecl &getTemplateOrThis(
const NamedDecl &ND) {
39 if (
auto T = ND.getDescribedTemplate())
53 std::string toURI(
const SourceManager &SM, llvm::StringRef
Path,
54 const SymbolCollector::Options &Opts) {
55 llvm::SmallString<128> AbsolutePath(Path);
58 AbsolutePath = *CanonPath;
62 if (!llvm::sys::path::is_absolute(AbsolutePath) && !Opts.FallbackDir.empty())
64 llvm::sys::path::remove_dots(AbsolutePath,
true);
69 static const char *PROTO_HEADER_COMMENT =
70 "// Generated by the protocol buffer compiler. DO NOT EDIT!";
78 bool isPrivateProtoDecl(
const NamedDecl &ND) {
79 const auto &SM = ND.getASTContext().getSourceManager();
84 auto FID = SM.getFileID(
Loc);
86 if (!SM.getBufferData(FID).startswith(PROTO_HEADER_COMMENT))
90 if (ND.getIdentifier() ==
nullptr)
92 auto Name = ND.getIdentifier()->getName();
93 if (!
Name.contains(
'_'))
104 return (ND.getKind() != Decl::EnumConstant) || llvm::any_of(
Name, islower);
122 case SK::EnumConstant:
132 llvm::Optional<std::string>
133 getIncludeHeader(llvm::StringRef QName,
const SourceManager &SM,
134 SourceLocation
Loc,
const SymbolCollector::Options &Opts) {
135 std::vector<std::string> Headers;
140 auto FilePath = SM.getFilename(Loc);
141 if (FilePath.empty())
143 Headers.push_back(FilePath);
144 if (SM.isInMainFile(Loc))
146 Loc = SM.getIncludeLoc(SM.getFileID(Loc));
150 llvm::StringRef Header = Headers[0];
152 Header = Opts.Includes->mapHeader(Headers, QName);
153 if (Header.startswith(
"<") || Header.startswith(
"\""))
156 return toURI(SM, Header, Opts);
160 std::pair<SymbolLocation::Position, SymbolLocation::Position>
161 getTokenRange(SourceLocation TokLoc,
const SourceManager &SM,
162 const LangOptions &LangOpts) {
163 auto CreatePosition = [&SM](SourceLocation
Loc) {
165 SymbolLocation::Position
Pos;
166 Pos.setLine(LSPLoc.line);
167 Pos.setColumn(LSPLoc.character);
171 auto TokenLength = clang::Lexer::MeasureTokenLength(TokLoc, SM, LangOpts);
172 return {CreatePosition(TokLoc),
173 CreatePosition(TokLoc.getLocWithOffset(TokenLength))};
176 bool shouldIndexFile(
const SourceManager &SM, FileID FID,
177 const SymbolCollector::Options &Opts,
178 llvm::DenseMap<FileID, bool> *FilesToIndexCache) {
179 if (!Opts.FileFilter)
181 auto I = FilesToIndexCache->try_emplace(FID);
183 I.first->second = Opts.FileFilter(SM, FID);
184 return I.first->second;
188 llvm::Optional<SymbolLocation>
189 getTokenLocation(SourceLocation TokLoc,
const SourceManager &SM,
190 const SymbolCollector::Options &Opts,
191 const clang::LangOptions &LangOpts,
192 std::string &FileURIStorage) {
193 auto Path = SM.getFilename(TokLoc);
196 FileURIStorage = toURI(SM, Path, Opts);
198 Result.FileURI = FileURIStorage.c_str();
199 auto Range = getTokenRange(TokLoc, SM, LangOpts);
200 Result.Start =
Range.first;
201 Result.End =
Range.second;
212 bool isPreferredDeclaration(
const NamedDecl &ND, index::SymbolRoleSet Roles) {
213 const auto& SM = ND.getASTContext().getSourceManager();
214 return (Roles & static_cast<unsigned>(index::SymbolRole::Definition)) &&
216 !SM.isWrittenInMainFile(SM.getExpansionLoc(ND.getLocation()));
219 RefKind toRefKind(index::SymbolRoleSet Roles) {
223 template <
class T>
bool explicitTemplateSpecialization(
const NamedDecl &ND) {
224 if (
const auto *TD = dyn_cast<T>(&ND))
225 if (TD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
236 CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
238 llvm::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
242 const ASTContext &ASTCtx,
244 bool IsMainFileOnly) {
248 if (ND.getDeclName().isEmpty())
256 if (!IsMainFileOnly && ND.isInAnonymousNamespace())
263 const auto *DeclCtx = ND.getDeclContext();
264 switch (DeclCtx->getDeclKind()) {
265 case Decl::TranslationUnit:
267 case Decl::LinkageSpec:
269 case Decl::ObjCProtocol:
270 case Decl::ObjCInterface:
271 case Decl::ObjCCategory:
272 case Decl::ObjCCategoryImpl:
273 case Decl::ObjCImplementation:
278 if (!isa<RecordDecl>(DeclCtx))
281 if (explicitTemplateSpecialization<FunctionDecl>(ND) ||
282 explicitTemplateSpecialization<CXXRecordDecl>(ND) ||
283 explicitTemplateSpecialization<VarDecl>(ND))
287 if (isPrivateProtoDecl(ND))
294 const Decl *
D, index::SymbolRoleSet Roles,
295 llvm::ArrayRef<index::SymbolRelation> Relations, SourceLocation Loc,
296 index::IndexDataConsumer::ASTNodeInfo ASTNode) {
297 assert(ASTCtx && PP.get() &&
"ASTContext and Preprocessor must be set.");
298 assert(CompletionAllocator && CompletionTUInfo);
299 assert(ASTNode.OrigD);
303 if ((ASTNode.OrigD->getFriendObjectKind() !=
304 Decl::FriendObjectKind::FOK_None) &&
305 !(Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
310 if (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None)
311 D = CanonicalDecls.try_emplace(D, ASTNode.OrigD).first->second;
312 const NamedDecl *ND = dyn_cast<NamedDecl>(
D);
318 auto &SM = ASTCtx->getSourceManager();
319 auto SpellingLoc = SM.getSpellingLoc(Loc);
320 if (Opts.CountReferences &&
321 (Roles & static_cast<unsigned>(index::SymbolRole::Reference)) &&
322 SM.getFileID(SpellingLoc) == SM.getMainFileID())
323 ReferencedDecls.insert(ND);
325 bool CollectRef =
static_cast<unsigned>(Opts.RefFilter) & Roles;
327 !(Roles & (
static_cast<unsigned>(index::SymbolRole::Declaration) |
328 static_cast<unsigned>(index::SymbolRole::Definition)));
330 if (IsOnlyRef && !CollectRef)
335 bool IsMainFileOnly = SM.isWrittenInMainFile(SM.getExpansionLoc(
340 if (CollectRef && !IsMainFileOnly && !isa<NamespaceDecl>(ND) &&
341 (Opts.RefsInHeaders || SM.getFileID(SpellingLoc) == SM.getMainFileID()))
342 DeclRefs[ND].emplace_back(SpellingLoc, Roles);
351 const NamedDecl &OriginalDecl = *cast<NamedDecl>(ASTNode.OrigD);
352 const Symbol *BasicSymbol = Symbols.
find(*ID);
354 BasicSymbol = addDeclaration(*ND, std::move(*ID), IsMainFileOnly);
355 else if (isPreferredDeclaration(OriginalDecl, Roles))
360 BasicSymbol = addDeclaration(OriginalDecl, std::move(*ID), IsMainFileOnly);
362 if (Roles & static_cast<unsigned>(index::SymbolRole::Definition))
363 addDefinition(OriginalDecl, *BasicSymbol);
369 index::SymbolRoleSet Roles,
370 SourceLocation Loc) {
371 if (!Opts.CollectMacro)
375 const auto &SM = PP->getSourceManager();
376 auto DefLoc = MI->getDefinitionLoc();
377 if (SM.isInMainFile(SM.getExpansionLoc(DefLoc)))
381 if (MI->isUsedForHeaderGuard() || MI->isBuiltinMacro())
387 if (Opts.CountReferences &&
388 (Roles & static_cast<unsigned>(index::SymbolRole::Reference)) &&
389 SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID())
390 ReferencedMacros.insert(Name);
393 if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
394 Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
402 if (Symbols.
find(*ID) !=
nullptr)
406 S.
ID = std::move(*ID);
407 S.
Name = Name->getName();
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);
425 if (Opts.CollectIncludePath && shouldCollectIncludePath(S.
SymInfo.Kind)) {
426 if (
auto Header = getIncludeHeader(Name->getName(), SM,
427 SM.getExpansionLoc(DefLoc), Opts))
428 Include = std::move(*Header);
432 if (!Include.empty())
441 auto IncRef = [
this](
const SymbolID &ID) {
442 if (
const auto *S = Symbols.
find(ID)) {
448 for (
const NamedDecl *ND : ReferencedDecls) {
453 if (Opts.CollectMacro) {
455 for (
const IdentifierInfo *II : ReferencedMacros) {
456 if (
const auto *MI = PP->getMacroDefinition(II).getMacroInfo())
457 if (
auto ID =
getSymbolID(*II, MI, PP->getSourceManager()))
462 const auto &SM = ASTCtx->getSourceManager();
463 llvm::DenseMap<FileID, std::string> URICache;
464 auto GetURI = [&](FileID FID) -> llvm::Optional<std::string> {
465 auto Found = URICache.find(FID);
466 if (Found == URICache.end()) {
467 if (
auto *FileEntry = SM.getFileEntryForID(FID)) {
468 auto FileURI = toURI(SM, FileEntry->getName(), Opts);
469 Found = URICache.insert({FID, FileURI}).first;
477 return Found->second;
480 if (
auto MainFileURI = GetURI(SM.getMainFileID())) {
481 for (
const auto &It : DeclRefs) {
483 for (
const auto &LocAndRole : It.second) {
484 auto FileID = SM.getFileID(LocAndRole.first);
486 shouldIndexFile(SM, FileID, Opts, &FilesToIndexCache);
487 if (
auto FileURI = GetURI(FileID)) {
489 getTokenRange(LocAndRole.first, SM, ASTCtx->getLangOpts());
492 R.Location.End =
Range.second;
493 R.Location.FileURI = FileURI->c_str();
494 R.Kind = toRefKind(LocAndRole.second);
502 ReferencedDecls.clear();
503 ReferencedMacros.clear();
505 FilesToIndexCache.clear();
508 const Symbol *SymbolCollector::addDeclaration(
const NamedDecl &ND,
510 bool IsMainFileOnly) {
511 auto &
Ctx = ND.getASTContext();
512 auto &SM =
Ctx.getSourceManager();
515 S.
ID = std::move(ID);
532 shouldIndexFile(SM, SM.getFileID(Loc), Opts, &FilesToIndexCache);
534 getTokenLocation(Loc, SM, Opts, ASTCtx->getLangOpts(), FileURI))
538 if (ND.getAvailability() == AR_Deprecated)
543 assert(ASTCtx && PP.get() &&
"ASTContext and Preprocessor must be set.");
545 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
546 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
547 *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
550 std::string Documentation =
562 return Symbols.
find(S.
ID);
574 if (Opts.CollectIncludePath && shouldCollectIncludePath(S.
SymInfo.Kind)) {
577 if (
auto Header = getIncludeHeader(
578 QName, SM, SM.getExpansionLoc(ND.getLocation()), Opts))
579 Include = std::move(*Header);
581 if (!Include.empty())
584 llvm::Optional<OpaqueType> TypeStorage;
588 S.
Type = TypeStorage->raw();
592 return Symbols.
find(S.
ID);
595 void SymbolCollector::addDefinition(
const NamedDecl &ND,
605 const auto &SM = ND.getASTContext().getSourceManager();
607 shouldIndexFile(SM, SM.getFileID(Loc), Opts, &FilesToIndexCache);
609 getTokenLocation(Loc, SM, Opts, ASTCtx->getLangOpts(), FileURI))
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.
llvm::Optional< SymbolID > getSymbolID(const Decl *D)
Gets the symbol ID for a declaration, if possible.
const Symbol * find(const SymbolID &ID)
clang::find_all_symbols::SymbolInfo::SymbolKind SymbolKind
SourceLocation findNameLoc(const clang::Decl *D)
Find the identifier source location of the given D.
void insert(const Symbol &S)
def make_absolute(f, directory)
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.
void initialize(ASTContext &Ctx) override
index::SymbolInfo SymInfo
std::string getReturnType(const CodeCompletionString &CCS)
Gets detail to be used as the detail field in an LSP completion item.
SymbolLocation Definition
std::vector< SymbolDetails > getSymbolInfo(ParsedAST &AST, Position Pos)
Get info about symbols at Pos.
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.
llvm::StringRef Signature
A brief description of the symbol that can be appended in the completion candidate list...
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
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
std::string formatDocumentation(const CodeCompletionString &CCS, llvm::StringRef DocComment)
Assembles formatted documentation for a completion result.
SymbolCollector(Options Opts)
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.
void getSignature(const CodeCompletionString &CCS, std::string *Signature, std::string *Snippet, std::string *RequiredQualifiers)
Formats the signature for an item, as a display string and snippet.
Position Start
The symbol range, using half-open range [Start, End).
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.
std::pair< llvm::StringRef, llvm::StringRef > splitQualifiedName(llvm::StringRef QName)
From "a::b::c", return {"a::b::", "c"}.
llvm::Optional< std::string > getCanonicalPath(const FileEntry *F, const SourceManager &SourceMgr)
Get the canonical path of F.
void insert(const SymbolID &ID, const Ref &S)
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.
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 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.