| //===--- XRefs.cpp ----------------------------------------------*- C++-*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===---------------------------------------------------------------------===// |
| #include "XRefs.h" |
| #include "AST.h" |
| #include "Logger.h" |
| #include "SourceCode.h" |
| #include "URI.h" |
| #include "clang/AST/DeclTemplate.h" |
| #include "clang/AST/RecursiveASTVisitor.h" |
| #include "clang/Index/IndexDataConsumer.h" |
| #include "clang/Index/IndexingAction.h" |
| #include "clang/Index/USRGeneration.h" |
| #include "llvm/Support/Path.h" |
| namespace clang { |
| namespace clangd { |
| using namespace llvm; |
| namespace { |
| |
| // Get the definition from a given declaration `D`. |
| // Return nullptr if no definition is found, or the declaration type of `D` is |
| // not supported. |
| const Decl *getDefinition(const Decl *D) { |
| assert(D); |
| if (const auto *TD = dyn_cast<TagDecl>(D)) |
| return TD->getDefinition(); |
| else if (const auto *VD = dyn_cast<VarDecl>(D)) |
| return VD->getDefinition(); |
| else if (const auto *FD = dyn_cast<FunctionDecl>(D)) |
| return FD->getDefinition(); |
| return nullptr; |
| } |
| |
| // Convert a SymbolLocation to LSP's Location. |
| // HintPath is used to resolve the path of URI. |
| // FIXME: figure out a good home for it, and share the implementation with |
| // FindSymbols. |
| llvm::Optional<Location> toLSPLocation(const SymbolLocation &Loc, |
| llvm::StringRef HintPath) { |
| if (!Loc) |
| return llvm::None; |
| auto Uri = URI::parse(Loc.FileURI); |
| if (!Uri) { |
| log("Could not parse URI: {0}", Loc.FileURI); |
| return llvm::None; |
| } |
| auto Path = URI::resolve(*Uri, HintPath); |
| if (!Path) { |
| log("Could not resolve URI: {0}", Loc.FileURI); |
| return llvm::None; |
| } |
| Location LSPLoc; |
| LSPLoc.uri = URIForFile(*Path); |
| LSPLoc.range.start.line = Loc.Start.Line; |
| LSPLoc.range.start.character = Loc.Start.Column; |
| LSPLoc.range.end.line = Loc.End.Line; |
| LSPLoc.range.end.character = Loc.End.Column; |
| return LSPLoc; |
| } |
| |
| struct MacroDecl { |
| StringRef Name; |
| const MacroInfo *Info; |
| }; |
| |
| /// Finds declarations locations that a given source location refers to. |
| class DeclarationAndMacrosFinder : public index::IndexDataConsumer { |
| std::vector<const Decl *> Decls; |
| std::vector<MacroDecl> MacroInfos; |
| const SourceLocation &SearchedLocation; |
| const ASTContext &AST; |
| Preprocessor &PP; |
| |
| public: |
| DeclarationAndMacrosFinder(raw_ostream &OS, |
| const SourceLocation &SearchedLocation, |
| ASTContext &AST, Preprocessor &PP) |
| : SearchedLocation(SearchedLocation), AST(AST), PP(PP) {} |
| |
| std::vector<const Decl *> takeDecls() { |
| // Don't keep the same declaration multiple times. |
| // This can happen when nodes in the AST are visited twice. |
| std::sort(Decls.begin(), Decls.end()); |
| auto Last = std::unique(Decls.begin(), Decls.end()); |
| Decls.erase(Last, Decls.end()); |
| return std::move(Decls); |
| } |
| |
| std::vector<MacroDecl> takeMacroInfos() { |
| // Don't keep the same Macro info multiple times. |
| std::sort(MacroInfos.begin(), MacroInfos.end(), |
| [](const MacroDecl &Left, const MacroDecl &Right) { |
| return Left.Info < Right.Info; |
| }); |
| |
| auto Last = std::unique(MacroInfos.begin(), MacroInfos.end(), |
| [](const MacroDecl &Left, const MacroDecl &Right) { |
| return Left.Info == Right.Info; |
| }); |
| MacroInfos.erase(Last, MacroInfos.end()); |
| return std::move(MacroInfos); |
| } |
| |
| bool |
| handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles, |
| ArrayRef<index::SymbolRelation> Relations, |
| SourceLocation Loc, |
| index::IndexDataConsumer::ASTNodeInfo ASTNode) override { |
| if (Loc == SearchedLocation) { |
| // Find and add definition declarations (for GoToDefinition). |
| // We don't use parameter `D`, as Parameter `D` is the canonical |
| // declaration, which is the first declaration of a redeclarable |
| // declaration, and it could be a forward declaration. |
| if (const auto *Def = getDefinition(D)) { |
| Decls.push_back(Def); |
| } else { |
| // Couldn't find a definition, fall back to use `D`. |
| Decls.push_back(D); |
| } |
| } |
| return true; |
| } |
| |
| private: |
| void finish() override { |
| // Also handle possible macro at the searched location. |
| Token Result; |
| auto &Mgr = AST.getSourceManager(); |
| if (!Lexer::getRawToken(Mgr.getSpellingLoc(SearchedLocation), Result, Mgr, |
| AST.getLangOpts(), false)) { |
| if (Result.is(tok::raw_identifier)) { |
| PP.LookUpIdentifierInfo(Result); |
| } |
| IdentifierInfo *IdentifierInfo = Result.getIdentifierInfo(); |
| if (IdentifierInfo && IdentifierInfo->hadMacroDefinition()) { |
| std::pair<FileID, unsigned int> DecLoc = |
| Mgr.getDecomposedExpansionLoc(SearchedLocation); |
| // Get the definition just before the searched location so that a macro |
| // referenced in a '#undef MACRO' can still be found. |
| SourceLocation BeforeSearchedLocation = Mgr.getMacroArgExpandedLocation( |
| Mgr.getLocForStartOfFile(DecLoc.first) |
| .getLocWithOffset(DecLoc.second - 1)); |
| MacroDefinition MacroDef = |
| PP.getMacroDefinitionAtLoc(IdentifierInfo, BeforeSearchedLocation); |
| MacroInfo *MacroInf = MacroDef.getMacroInfo(); |
| if (MacroInf) { |
| MacroInfos.push_back(MacroDecl{IdentifierInfo->getName(), MacroInf}); |
| assert(Decls.empty()); |
| } |
| } |
| } |
| } |
| }; |
| |
| struct IdentifiedSymbol { |
| std::vector<const Decl *> Decls; |
| std::vector<MacroDecl> Macros; |
| }; |
| |
| IdentifiedSymbol getSymbolAtPosition(ParsedAST &AST, SourceLocation Pos) { |
| auto DeclMacrosFinder = DeclarationAndMacrosFinder( |
| llvm::errs(), Pos, AST.getASTContext(), AST.getPreprocessor()); |
| index::IndexingOptions IndexOpts; |
| IndexOpts.SystemSymbolFilter = |
| index::IndexingOptions::SystemSymbolFilterKind::All; |
| IndexOpts.IndexFunctionLocals = true; |
| indexTopLevelDecls(AST.getASTContext(), AST.getLocalTopLevelDecls(), |
| DeclMacrosFinder, IndexOpts); |
| |
| return {DeclMacrosFinder.takeDecls(), DeclMacrosFinder.takeMacroInfos()}; |
| } |
| |
| llvm::Optional<Location> |
| makeLocation(ParsedAST &AST, const SourceRange &ValSourceRange) { |
| const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); |
| const LangOptions &LangOpts = AST.getASTContext().getLangOpts(); |
| SourceLocation LocStart = ValSourceRange.getBegin(); |
| |
| const FileEntry *F = |
| SourceMgr.getFileEntryForID(SourceMgr.getFileID(LocStart)); |
| if (!F) |
| return llvm::None; |
| SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(), 0, |
| SourceMgr, LangOpts); |
| Position Begin = sourceLocToPosition(SourceMgr, LocStart); |
| Position End = sourceLocToPosition(SourceMgr, LocEnd); |
| Range R = {Begin, End}; |
| Location L; |
| |
| auto FilePath = getAbsoluteFilePath(F, SourceMgr); |
| if (!FilePath) { |
| log("failed to get path!"); |
| return llvm::None; |
| } |
| L.uri = URIForFile(*FilePath); |
| L.range = R; |
| return L; |
| } |
| |
| // Get the symbol ID for a declaration, if possible. |
| llvm::Optional<SymbolID> getSymbolID(const Decl *D) { |
| llvm::SmallString<128> USR; |
| if (index::generateUSRForDecl(D, USR)) { |
| return None; |
| } |
| return SymbolID(USR); |
| } |
| |
| } // namespace |
| |
| std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos, |
| const SymbolIndex *Index) { |
| const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); |
| |
| std::vector<Location> Result; |
| // Handle goto definition for #include. |
| for (auto &Inc : AST.getIncludeStructure().MainFileIncludes) { |
| if (!Inc.Resolved.empty() && Inc.R.contains(Pos)) |
| Result.push_back(Location{URIForFile{Inc.Resolved}, {}}); |
| } |
| if (!Result.empty()) |
| return Result; |
| |
| // Identified symbols at a specific position. |
| SourceLocation SourceLocationBeg = |
| getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID()); |
| auto Symbols = getSymbolAtPosition(AST, SourceLocationBeg); |
| |
| for (auto Item : Symbols.Macros) { |
| auto Loc = Item.Info->getDefinitionLoc(); |
| auto L = makeLocation(AST, SourceRange(Loc, Loc)); |
| if (L) |
| Result.push_back(*L); |
| } |
| |
| // Declaration and definition are different terms in C-family languages, and |
| // LSP only defines the "GoToDefinition" specification, so we try to perform |
| // the "most sensible" GoTo operation: |
| // |
| // - We use the location from AST and index (if available) to provide the |
| // final results. When there are duplicate results, we prefer AST over |
| // index because AST is more up-to-date. |
| // |
| // - For each symbol, we will return a location of the canonical declaration |
| // (e.g. function declaration in header), and a location of definition if |
| // they are available. |
| // |
| // So the work flow: |
| // |
| // 1. Identify the symbols being search for by traversing the AST. |
| // 2. Populate one of the locations with the AST location. |
| // 3. Use the AST information to query the index, and populate the index |
| // location (if available). |
| // 4. Return all populated locations for all symbols, definition first ( |
| // which we think is the users wants most often). |
| struct CandidateLocation { |
| llvm::Optional<Location> Def; |
| llvm::Optional<Location> Decl; |
| }; |
| llvm::DenseMap<SymbolID, CandidateLocation> ResultCandidates; |
| |
| // Emit all symbol locations (declaration or definition) from AST. |
| for (const auto *D : Symbols.Decls) { |
| // Fake key for symbols don't have USR (no SymbolID). |
| // Ideally, there should be a USR for each identified symbols. Symbols |
| // without USR are rare and unimportant cases, we use the a fake holder to |
| // minimize the invasiveness of these cases. |
| SymbolID Key(""); |
| if (auto ID = getSymbolID(D)) |
| Key = *ID; |
| |
| auto &Candidate = ResultCandidates[Key]; |
| auto Loc = findNameLoc(D); |
| auto L = makeLocation(AST, SourceRange(Loc, Loc)); |
| // The declaration in the identified symbols is a definition if possible |
| // otherwise it is declaration. |
| bool IsDef = getDefinition(D) == D; |
| // Populate one of the slots with location for the AST. |
| if (!IsDef) |
| Candidate.Decl = L; |
| else |
| Candidate.Def = L; |
| } |
| |
| if (Index) { |
| LookupRequest QueryRequest; |
| // Build request for index query, using SymbolID. |
| for (auto It : ResultCandidates) |
| QueryRequest.IDs.insert(It.first); |
| std::string HintPath; |
| const FileEntry *FE = |
| SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); |
| if (auto Path = getAbsoluteFilePath(FE, SourceMgr)) |
| HintPath = *Path; |
| // Query the index and populate the empty slot. |
| Index->lookup( |
| QueryRequest, [&HintPath, &ResultCandidates](const Symbol &Sym) { |
| auto It = ResultCandidates.find(Sym.ID); |
| assert(It != ResultCandidates.end()); |
| auto &Value = It->second; |
| |
| if (!Value.Def) |
| Value.Def = toLSPLocation(Sym.Definition, HintPath); |
| if (!Value.Decl) |
| Value.Decl = toLSPLocation(Sym.CanonicalDeclaration, HintPath); |
| }); |
| } |
| |
| // Populate the results, definition first. |
| for (auto It : ResultCandidates) { |
| const auto &Candidate = It.second; |
| if (Candidate.Def) |
| Result.push_back(*Candidate.Def); |
| if (Candidate.Decl && |
| Candidate.Decl != Candidate.Def) // Decl and Def might be the same |
| Result.push_back(*Candidate.Decl); |
| } |
| |
| return Result; |
| } |
| |
| namespace { |
| |
| /// Finds document highlights that a given list of declarations refers to. |
| class DocumentHighlightsFinder : public index::IndexDataConsumer { |
| std::vector<const Decl *> &Decls; |
| std::vector<DocumentHighlight> DocumentHighlights; |
| const ASTContext &AST; |
| |
| public: |
| DocumentHighlightsFinder(raw_ostream &OS, ASTContext &AST, Preprocessor &PP, |
| std::vector<const Decl *> &Decls) |
| : Decls(Decls), AST(AST) {} |
| std::vector<DocumentHighlight> takeHighlights() { |
| // Don't keep the same highlight multiple times. |
| // This can happen when nodes in the AST are visited twice. |
| std::sort(DocumentHighlights.begin(), DocumentHighlights.end()); |
| auto Last = |
| std::unique(DocumentHighlights.begin(), DocumentHighlights.end()); |
| DocumentHighlights.erase(Last, DocumentHighlights.end()); |
| return std::move(DocumentHighlights); |
| } |
| |
| bool |
| handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles, |
| ArrayRef<index::SymbolRelation> Relations, |
| SourceLocation Loc, |
| index::IndexDataConsumer::ASTNodeInfo ASTNode) override { |
| const SourceManager &SourceMgr = AST.getSourceManager(); |
| SourceLocation HighlightStartLoc = SourceMgr.getFileLoc(Loc); |
| if (SourceMgr.getMainFileID() != SourceMgr.getFileID(HighlightStartLoc) || |
| std::find(Decls.begin(), Decls.end(), D) == Decls.end()) { |
| return true; |
| } |
| SourceLocation End; |
| const LangOptions &LangOpts = AST.getLangOpts(); |
| End = Lexer::getLocForEndOfToken(HighlightStartLoc, 0, SourceMgr, LangOpts); |
| SourceRange SR(HighlightStartLoc, End); |
| |
| DocumentHighlightKind Kind = DocumentHighlightKind::Text; |
| if (static_cast<index::SymbolRoleSet>(index::SymbolRole::Write) & Roles) |
| Kind = DocumentHighlightKind::Write; |
| else if (static_cast<index::SymbolRoleSet>(index::SymbolRole::Read) & Roles) |
| Kind = DocumentHighlightKind::Read; |
| |
| DocumentHighlights.push_back(getDocumentHighlight(SR, Kind)); |
| return true; |
| } |
| |
| private: |
| DocumentHighlight getDocumentHighlight(SourceRange SR, |
| DocumentHighlightKind Kind) { |
| const SourceManager &SourceMgr = AST.getSourceManager(); |
| Position Begin = sourceLocToPosition(SourceMgr, SR.getBegin()); |
| Position End = sourceLocToPosition(SourceMgr, SR.getEnd()); |
| Range R = {Begin, End}; |
| DocumentHighlight DH; |
| DH.range = R; |
| DH.kind = Kind; |
| return DH; |
| } |
| }; |
| |
| } // namespace |
| |
| std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST, |
| Position Pos) { |
| const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); |
| SourceLocation SourceLocationBeg = |
| getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID()); |
| |
| auto Symbols = getSymbolAtPosition(AST, SourceLocationBeg); |
| std::vector<const Decl *> SelectedDecls = Symbols.Decls; |
| |
| DocumentHighlightsFinder DocHighlightsFinder( |
| llvm::errs(), AST.getASTContext(), AST.getPreprocessor(), SelectedDecls); |
| |
| index::IndexingOptions IndexOpts; |
| IndexOpts.SystemSymbolFilter = |
| index::IndexingOptions::SystemSymbolFilterKind::All; |
| IndexOpts.IndexFunctionLocals = true; |
| indexTopLevelDecls(AST.getASTContext(), AST.getLocalTopLevelDecls(), |
| DocHighlightsFinder, IndexOpts); |
| |
| return DocHighlightsFinder.takeHighlights(); |
| } |
| |
| static PrintingPolicy printingPolicyForDecls(PrintingPolicy Base) { |
| PrintingPolicy Policy(Base); |
| |
| Policy.AnonymousTagLocations = false; |
| Policy.TerseOutput = true; |
| Policy.PolishForDeclaration = true; |
| Policy.ConstantsAsWritten = true; |
| Policy.SuppressTagKeyword = false; |
| |
| return Policy; |
| } |
| |
| /// Return a string representation (e.g. "class MyNamespace::MyClass") of |
| /// the type declaration \p TD. |
| static std::string typeDeclToString(const TypeDecl *TD) { |
| QualType Type = TD->getASTContext().getTypeDeclType(TD); |
| |
| PrintingPolicy Policy = |
| printingPolicyForDecls(TD->getASTContext().getPrintingPolicy()); |
| |
| std::string Name; |
| llvm::raw_string_ostream Stream(Name); |
| Type.print(Stream, Policy); |
| |
| return Stream.str(); |
| } |
| |
| /// Return a string representation (e.g. "namespace ns1::ns2") of |
| /// the named declaration \p ND. |
| static std::string namedDeclQualifiedName(const NamedDecl *ND, |
| StringRef Prefix) { |
| PrintingPolicy Policy = |
| printingPolicyForDecls(ND->getASTContext().getPrintingPolicy()); |
| |
| std::string Name; |
| llvm::raw_string_ostream Stream(Name); |
| Stream << Prefix << ' '; |
| ND->printQualifiedName(Stream, Policy); |
| |
| return Stream.str(); |
| } |
| |
| /// Given a declaration \p D, return a human-readable string representing the |
| /// scope in which it is declared. If the declaration is in the global scope, |
| /// return the string "global namespace". |
| static llvm::Optional<std::string> getScopeName(const Decl *D) { |
| const DeclContext *DC = D->getDeclContext(); |
| |
| if (isa<TranslationUnitDecl>(DC)) |
| return std::string("global namespace"); |
| if (const TypeDecl *TD = dyn_cast<TypeDecl>(DC)) |
| return typeDeclToString(TD); |
| else if (const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC)) |
| return namedDeclQualifiedName(ND, "namespace"); |
| else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DC)) |
| return namedDeclQualifiedName(FD, "function"); |
| |
| return llvm::None; |
| } |
| |
| /// Generate a \p Hover object given the declaration \p D. |
| static Hover getHoverContents(const Decl *D) { |
| Hover H; |
| llvm::Optional<std::string> NamedScope = getScopeName(D); |
| |
| // Generate the "Declared in" section. |
| if (NamedScope) { |
| assert(!NamedScope->empty()); |
| |
| H.contents.value += "Declared in "; |
| H.contents.value += *NamedScope; |
| H.contents.value += "\n\n"; |
| } |
| |
| // We want to include the template in the Hover. |
| if (TemplateDecl *TD = D->getDescribedTemplate()) |
| D = TD; |
| |
| std::string DeclText; |
| llvm::raw_string_ostream OS(DeclText); |
| |
| PrintingPolicy Policy = |
| printingPolicyForDecls(D->getASTContext().getPrintingPolicy()); |
| |
| D->print(OS, Policy); |
| |
| OS.flush(); |
| |
| H.contents.value += DeclText; |
| return H; |
| } |
| |
| /// Generate a \p Hover object given the type \p T. |
| static Hover getHoverContents(QualType T, ASTContext &ASTCtx) { |
| Hover H; |
| std::string TypeText; |
| llvm::raw_string_ostream OS(TypeText); |
| PrintingPolicy Policy = printingPolicyForDecls(ASTCtx.getPrintingPolicy()); |
| T.print(OS, Policy); |
| OS.flush(); |
| H.contents.value += TypeText; |
| return H; |
| } |
| |
| /// Generate a \p Hover object given the macro \p MacroInf. |
| static Hover getHoverContents(StringRef MacroName) { |
| Hover H; |
| |
| H.contents.value = "#define "; |
| H.contents.value += MacroName; |
| |
| return H; |
| } |
| |
| namespace { |
| /// Computes the deduced type at a given location by visiting the relevant |
| /// nodes. We use this to display the actual type when hovering over an "auto" |
| /// keyword or "decltype()" expression. |
| /// FIXME: This could have been a lot simpler by visiting AutoTypeLocs but it |
| /// seems that the AutoTypeLocs that can be visited along with their AutoType do |
| /// not have the deduced type set. Instead, we have to go to the appropriate |
| /// DeclaratorDecl/FunctionDecl and work our back to the AutoType that does have |
| /// a deduced type set. The AST should be improved to simplify this scenario. |
| class DeducedTypeVisitor : public RecursiveASTVisitor<DeducedTypeVisitor> { |
| SourceLocation SearchedLocation; |
| llvm::Optional<QualType> DeducedType; |
| |
| public: |
| DeducedTypeVisitor(SourceLocation SearchedLocation) |
| : SearchedLocation(SearchedLocation) {} |
| |
| llvm::Optional<QualType> getDeducedType() { return DeducedType; } |
| |
| // Handle auto initializers: |
| //- auto i = 1; |
| //- decltype(auto) i = 1; |
| //- auto& i = 1; |
| bool VisitDeclaratorDecl(DeclaratorDecl *D) { |
| if (!D->getTypeSourceInfo() || |
| D->getTypeSourceInfo()->getTypeLoc().getLocStart() != SearchedLocation) |
| return true; |
| |
| auto DeclT = D->getType(); |
| // "auto &" is represented as a ReferenceType containing an AutoType |
| if (const ReferenceType *RT = dyn_cast<ReferenceType>(DeclT.getTypePtr())) |
| DeclT = RT->getPointeeType(); |
| |
| const AutoType *AT = dyn_cast<AutoType>(DeclT.getTypePtr()); |
| if (AT && !AT->getDeducedType().isNull()) { |
| // For auto, use the underlying type because the const& would be |
| // represented twice: written in the code and in the hover. |
| // Example: "const auto I = 1", we only want "int" when hovering on auto, |
| // not "const int". |
| // |
| // For decltype(auto), take the type as is because it cannot be written |
| // with qualifiers or references but its decuded type can be const-ref. |
| DeducedType = AT->isDecltypeAuto() ? DeclT : DeclT.getUnqualifiedType(); |
| } |
| return true; |
| } |
| |
| // Handle auto return types: |
| //- auto foo() {} |
| //- auto& foo() {} |
| //- auto foo() -> decltype(1+1) {} |
| //- operator auto() const { return 10; } |
| bool VisitFunctionDecl(FunctionDecl *D) { |
| if (!D->getTypeSourceInfo()) |
| return true; |
| // Loc of auto in return type (c++14). |
| auto CurLoc = D->getReturnTypeSourceRange().getBegin(); |
| // Loc of "auto" in operator auto() |
| if (CurLoc.isInvalid() && dyn_cast<CXXConversionDecl>(D)) |
| CurLoc = D->getTypeSourceInfo()->getTypeLoc().getBeginLoc(); |
| // Loc of "auto" in function with traling return type (c++11). |
| if (CurLoc.isInvalid()) |
| CurLoc = D->getSourceRange().getBegin(); |
| if (CurLoc != SearchedLocation) |
| return true; |
| |
| auto T = D->getReturnType(); |
| // "auto &" is represented as a ReferenceType containing an AutoType. |
| if (const ReferenceType *RT = dyn_cast<ReferenceType>(T.getTypePtr())) |
| T = RT->getPointeeType(); |
| |
| const AutoType *AT = dyn_cast<AutoType>(T.getTypePtr()); |
| if (AT && !AT->getDeducedType().isNull()) { |
| DeducedType = T.getUnqualifiedType(); |
| } else { // auto in a trailing return type just points to a DecltypeType. |
| const DecltypeType *DT = dyn_cast<DecltypeType>(T.getTypePtr()); |
| if (!DT->getUnderlyingType().isNull()) |
| DeducedType = DT->getUnderlyingType(); |
| } |
| return true; |
| } |
| |
| // Handle non-auto decltype, e.g.: |
| // - auto foo() -> decltype(expr) {} |
| // - decltype(expr); |
| bool VisitDecltypeTypeLoc(DecltypeTypeLoc TL) { |
| if (TL.getBeginLoc() != SearchedLocation) |
| return true; |
| |
| // A DecltypeType's underlying type can be another DecltypeType! E.g. |
| // int I = 0; |
| // decltype(I) J = I; |
| // decltype(J) K = J; |
| const DecltypeType *DT = dyn_cast<DecltypeType>(TL.getTypePtr()); |
| while (DT && !DT->getUnderlyingType().isNull()) { |
| DeducedType = DT->getUnderlyingType(); |
| DT = dyn_cast<DecltypeType>(DeducedType->getTypePtr()); |
| } |
| return true; |
| } |
| }; |
| } // namespace |
| |
| /// Retrieves the deduced type at a given location (auto, decltype). |
| llvm::Optional<QualType> getDeducedType(ParsedAST &AST, |
| SourceLocation SourceLocationBeg) { |
| Token Tok; |
| auto &ASTCtx = AST.getASTContext(); |
| // Only try to find a deduced type if the token is auto or decltype. |
| if (!SourceLocationBeg.isValid() || |
| Lexer::getRawToken(SourceLocationBeg, Tok, ASTCtx.getSourceManager(), |
| ASTCtx.getLangOpts(), false) || |
| !Tok.is(tok::raw_identifier)) { |
| return {}; |
| } |
| AST.getPreprocessor().LookUpIdentifierInfo(Tok); |
| if (!(Tok.is(tok::kw_auto) || Tok.is(tok::kw_decltype))) |
| return {}; |
| |
| DeducedTypeVisitor V(SourceLocationBeg); |
| for (Decl *D : AST.getLocalTopLevelDecls()) |
| V.TraverseDecl(D); |
| return V.getDeducedType(); |
| } |
| |
| Optional<Hover> getHover(ParsedAST &AST, Position Pos) { |
| const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); |
| SourceLocation SourceLocationBeg = |
| getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID()); |
| // Identified symbols at a specific position. |
| auto Symbols = getSymbolAtPosition(AST, SourceLocationBeg); |
| |
| if (!Symbols.Macros.empty()) |
| return getHoverContents(Symbols.Macros[0].Name); |
| |
| if (!Symbols.Decls.empty()) |
| return getHoverContents(Symbols.Decls[0]); |
| |
| auto DeducedType = getDeducedType(AST, SourceLocationBeg); |
| if (DeducedType && !DeducedType->isNull()) |
| return getHoverContents(*DeducedType, AST.getASTContext()); |
| |
| return None; |
| } |
| |
| } // namespace clangd |
| } // namespace clang |