12 #include "../CodeComplete.h" 13 #include "../CodeCompletionStrings.h" 14 #include "../Logger.h" 15 #include "../SourceCode.h" 18 #include "clang/AST/DeclCXX.h" 19 #include "clang/AST/DeclTemplate.h" 20 #include "clang/ASTMatchers/ASTMatchFinder.h" 21 #include "clang/Basic/SourceManager.h" 22 #include "clang/Index/IndexSymbol.h" 23 #include "clang/Index/USRGeneration.h" 24 #include "llvm/Support/FileSystem.h" 25 #include "llvm/Support/MemoryBuffer.h" 26 #include "llvm/Support/Path.h" 34 const NamedDecl &getTemplateOrThis(
const NamedDecl &ND) {
35 if (
auto T = ND.getDescribedTemplate())
49 llvm::Optional<std::string> toURI(
const SourceManager &SM, StringRef
Path,
50 const SymbolCollector::Options &Opts) {
51 llvm::SmallString<128> AbsolutePath(Path);
52 if (std::error_code EC =
53 SM.getFileManager().getVirtualFileSystem()->makeAbsolute(
55 log(
"Warning: could not make absolute file: {0}", EC.message());
56 if (llvm::sys::path::is_absolute(AbsolutePath)) {
68 if (
const DirectoryEntry *Dir = SM.getFileManager().getDirectory(
69 llvm::sys::path::parent_path(AbsolutePath.str()))) {
70 StringRef DirName = SM.getFileManager().getCanonicalName(Dir);
71 SmallString<128> AbsoluteFilename;
72 llvm::sys::path::append(AbsoluteFilename, DirName,
73 llvm::sys::path::filename(AbsolutePath.str()));
74 AbsolutePath = AbsoluteFilename;
76 }
else if (!Opts.FallbackDir.empty()) {
80 llvm::sys::path::remove_dots(AbsolutePath,
true);
83 for (
const auto &
Scheme : Opts.URISchemes) {
89 log(
"Failed to create an URI for file {0}: {1}", AbsolutePath, ErrMsg);
94 static const char *PROTO_HEADER_COMMENT =
95 "// Generated by the protocol buffer compiler. DO NOT EDIT!";
103 bool isPrivateProtoDecl(
const NamedDecl &ND) {
104 const auto &SM = ND.getASTContext().getSourceManager();
109 auto FID = SM.getFileID(
Loc);
111 if (!SM.getBufferData(FID).startswith(PROTO_HEADER_COMMENT))
115 if (ND.getIdentifier() ==
nullptr)
117 auto Name = ND.getIdentifier()->getName();
118 if (!
Name.contains(
'_'))
129 return (ND.getKind() != Decl::EnumConstant) ||
130 std::any_of(
Name.begin(),
Name.end(), islower);
148 case SK::EnumConstant:
158 llvm::Optional<std::string>
159 getIncludeHeader(llvm::StringRef QName,
const SourceManager &SM,
160 SourceLocation
Loc,
const SymbolCollector::Options &Opts) {
161 std::vector<std::string> Headers;
166 auto FilePath = SM.getFilename(Loc);
167 if (FilePath.empty())
169 Headers.push_back(FilePath);
170 if (SM.isInMainFile(Loc))
172 Loc = SM.getIncludeLoc(SM.getFileID(Loc));
176 llvm::StringRef Header = Headers[0];
178 Header = Opts.Includes->mapHeader(Headers, QName);
179 if (Header.startswith(
"<") || Header.startswith(
"\""))
182 return toURI(SM, Header, Opts);
186 llvm::Optional<SymbolLocation>
187 getTokenLocation(SourceLocation TokLoc,
const SourceManager &SM,
188 const SymbolCollector::Options &Opts,
189 const clang::LangOptions &LangOpts,
190 std::string &FileURIStorage) {
191 auto U = toURI(SM, SM.getFilename(TokLoc), Opts);
194 FileURIStorage = std::move(*U);
195 SymbolLocation Result;
196 Result.FileURI = FileURIStorage;
197 auto TokenLength = clang::Lexer::MeasureTokenLength(TokLoc, SM, LangOpts);
199 auto CreatePosition = [&SM](SourceLocation
Loc) {
201 SymbolLocation::Position
Pos;
202 Pos.Line = LSPLoc.
line;
203 Pos.Column = LSPLoc.character;
207 Result.Start = CreatePosition(TokLoc);
208 auto EndLoc = TokLoc.getLocWithOffset(TokenLength);
209 Result.End = CreatePosition(EndLoc);
211 return std::move(Result);
220 bool isPreferredDeclaration(
const NamedDecl &ND, index::SymbolRoleSet Roles) {
222 return (Roles & static_cast<unsigned>(index::SymbolRole::Definition)) &&
223 llvm::isa<TagDecl>(&ND) &&
224 match(decl(isExpansionInMainFile()), ND, ND.getASTContext()).empty();
233 CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
235 llvm::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
245 if (ND.getDeclName().isEmpty())
256 if (ND.isInAnonymousNamespace())
263 auto InNonLocalContext = hasDeclContext(anyOf(
264 translationUnitDecl(), namespaceDecl(), linkageSpecDecl(), recordDecl(),
265 enumDecl(), objcProtocolDecl(), objcInterfaceDecl(), objcCategoryDecl(),
266 objcCategoryImplDecl(), objcImplementationDecl()));
268 auto IsSpecialization =
269 anyOf(functionDecl(isExplicitTemplateSpecialization()),
270 cxxRecordDecl(isExplicitTemplateSpecialization()),
271 varDecl(isExplicitTemplateSpecialization()));
272 if (match(decl(allOf(unless(isExpansionInMainFile()), InNonLocalContext,
273 unless(IsSpecialization))),
279 if (isPrivateProtoDecl(ND))
286 const Decl *D, index::SymbolRoleSet Roles,
287 ArrayRef<index::SymbolRelation> Relations, SourceLocation Loc,
288 index::IndexDataConsumer::ASTNodeInfo ASTNode) {
289 assert(ASTCtx && PP.get() &&
"ASTContext and Preprocessor must be set.");
290 assert(CompletionAllocator && CompletionTUInfo);
291 assert(ASTNode.OrigD);
295 if ((ASTNode.OrigD->getFriendObjectKind() !=
296 Decl::FriendObjectKind::FOK_None) &&
297 !(Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
302 if (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None)
303 D = CanonicalDecls.try_emplace(D, ASTNode.OrigD).first->second;
304 const NamedDecl *ND = llvm::dyn_cast<NamedDecl>(D);
310 auto &SM = ASTCtx->getSourceManager();
311 if (Opts.CountReferences &&
312 (Roles & static_cast<unsigned>(index::SymbolRole::Reference)) &&
313 SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID())
314 ReferencedDecls.insert(ND);
317 if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
318 Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
323 llvm::SmallString<128> USR;
324 if (index::generateUSRForDecl(ND, USR))
328 const NamedDecl &OriginalDecl = *cast<NamedDecl>(ASTNode.OrigD);
329 const Symbol *BasicSymbol = Symbols.find(ID);
331 BasicSymbol = addDeclaration(*ND, std::move(ID));
332 else if (isPreferredDeclaration(OriginalDecl, Roles))
337 BasicSymbol = addDeclaration(OriginalDecl, std::move(ID));
339 if (Roles & static_cast<unsigned>(index::SymbolRole::Definition))
340 addDefinition(OriginalDecl, *BasicSymbol);
346 index::SymbolRoleSet Roles,
347 SourceLocation Loc) {
348 if (!Opts.CollectMacro)
352 const auto &SM = PP->getSourceManager();
353 if (SM.isInMainFile(SM.getExpansionLoc(MI->getDefinitionLoc())))
357 if (MI->isUsedForHeaderGuard() || MI->isBuiltinMacro())
363 if (Opts.CountReferences &&
364 (Roles & static_cast<unsigned>(index::SymbolRole::Reference)) &&
365 SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID())
366 ReferencedMacros.insert(Name);
369 if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
370 Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
373 llvm::SmallString<128> USR;
374 if (index::generateUSRForMacro(Name->getName(), MI->getDefinitionLoc(), SM,
380 if (Symbols.find(ID) !=
nullptr)
384 S.
ID = std::move(ID);
385 S.
Name = Name->getName();
387 S.
SymInfo = index::getSymbolInfoForMacro(*MI);
389 if (
auto DeclLoc = getTokenLocation(MI->getDefinitionLoc(), SM, Opts,
390 PP->getLangOpts(), FileURI))
393 CodeCompletionResult SymbolCompletion(Name);
394 const auto *CCS = SymbolCompletion.CreateCodeCompletionStringForMacro(
395 *PP, *CompletionAllocator, *CompletionTUInfo);
401 if (Opts.CollectIncludePath && shouldCollectIncludePath(S.
SymInfo.Kind)) {
403 getIncludeHeader(Name->getName(), SM,
404 SM.getExpansionLoc(MI->getDefinitionLoc()), Opts))
405 Include = std::move(*Header);
418 auto IncRef = [
this](
const SymbolID &ID) {
419 if (
const auto *S = Symbols.find(ID)) {
425 for (
const NamedDecl *ND : ReferencedDecls) {
426 llvm::SmallString<128> USR;
427 if (!index::generateUSRForDecl(ND, USR))
430 if (Opts.CollectMacro) {
432 for (
const IdentifierInfo *II : ReferencedMacros) {
433 llvm::SmallString<128> USR;
434 if (
const auto *MI = PP->getMacroDefinition(II).getMacroInfo())
435 if (!index::generateUSRForMacro(II->getName(), MI->getDefinitionLoc(),
436 PP->getSourceManager(), USR))
440 ReferencedDecls.clear();
441 ReferencedMacros.clear();
444 const Symbol *SymbolCollector::addDeclaration(
const NamedDecl &ND,
446 auto &
Ctx = ND.getASTContext();
447 auto &SM =
Ctx.getSourceManager();
450 S.
ID = std::move(ID);
457 S.
SymInfo = index::getSymbolInfo(&ND);
459 if (
auto DeclLoc = getTokenLocation(
findNameLoc(&ND), SM, Opts,
460 ASTCtx->getLangOpts(), FileURI))
465 assert(ASTCtx && PP.get() &&
"ASTContext and Preprocessor must be set.");
467 CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
468 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
469 *ASTCtx, *PP, CodeCompletionContext::CCC_Name, *CompletionAllocator,
475 std::string Documentation =
481 if (Opts.CollectIncludePath && shouldCollectIncludePath(S.
SymInfo.Kind)) {
484 if (
auto Header = getIncludeHeader(
485 QName, SM, SM.getExpansionLoc(ND.getLocation()), Opts))
486 Include = std::move(*Header);
498 return Symbols.find(S.
ID);
501 void SymbolCollector::addDefinition(
const NamedDecl &ND,
510 if (
auto DefLoc = getTokenLocation(
findNameLoc(&ND),
511 ND.getASTContext().getSourceManager(),
512 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
llvm::StringRef Documentation
Documentation including comment for the symbol declaration.
static const char * Scheme
Optional symbol details that are not required to be set.
std::string printQualifiedName(const NamedDecl &ND)
Returns the qualified name of ND.
bool IsIndexedForCodeCompletion
Whether or not this symbol is meant to be used for the code completion.
llvm::StringRef IncludeHeader
This can be either a URI of the header to be #include'd for this symbol, or a literal header quoted w...
clang::find_all_symbols::SymbolInfo::SymbolKind SymbolKind
SourceLocation findNameLoc(const clang::Decl *D)
Find the identifier source location of the given D.
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
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
llvm::StringRef ReturnType
Type when this symbol is used in an expression.
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
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::StringRef Signature
A brief description of the symbol that can be appended in the completion candidate list...
void log(const char *Fmt, Ts &&... Vals)
std::string Path
A typedef to represent a file path.
SymbolLocation CanonicalDeclaration
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.
int line
Line position in a document (zero-based).
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.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::pair< llvm::StringRef, llvm::StringRef > splitQualifiedName(llvm::StringRef QName)
From "a::b::c", return {"a::b::", "c"}.
static bool shouldCollectSymbol(const NamedDecl &ND, ASTContext &ASTCtx, const Options &Opts)
Returns true is ND should be collected.
llvm::StringRef CompletionSnippetSuffix
What to insert when completing this symbol, after the symbol name.
tooling::ExecutionContext * Ctx
SymbolOrigin Origin
Where this symbol came from. Usually an index provides a constant value.
std::array< uint8_t, 20 > SymbolID