diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h --- a/clang-tools-extra/clangd/AST.h +++ b/clang-tools-extra/clangd/AST.h @@ -116,6 +116,11 @@ // (i.e. vector rather than vector. QualType declaredType(const TypeDecl *D); +/// Retrieves the deduced type at a given location (auto, decltype). +/// Retuns None unless Loc starts an auto/decltype token. +/// It will return the underlying type. +llvm::Optional getDeducedType(ASTContext &, SourceLocation Loc); + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp --- a/clang-tools-extra/clangd/AST.cpp +++ b/clang-tools-extra/clangd/AST.cpp @@ -15,11 +15,13 @@ #include "clang/AST/DeclarationName.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/PrettyPrinter.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/TemplateBase.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Specifiers.h" #include "clang/Index/USRGeneration.h" +#include "clang/Lex/Lexer.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ScopedPrinter.h" @@ -253,5 +255,114 @@ return D->getASTContext().getTypeDeclType(D); } +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 { + SourceLocation SearchedLocation; + +public: + DeducedTypeVisitor(SourceLocation SearchedLocation) + : SearchedLocation(SearchedLocation) {} + + // Handle auto initializers: + //- auto i = 1; + //- decltype(auto) i = 1; + //- auto& i = 1; + //- auto* i = &a; + bool VisitDeclaratorDecl(DeclaratorDecl *D) { + if (!D->getTypeSourceInfo() || + D->getTypeSourceInfo()->getTypeLoc().getBeginLoc() != SearchedLocation) + return true; + + if (auto *AT = D->getType()->getContainedAutoType()) { + if (!AT->getDeducedType().isNull()) + DeducedType = AT->getDeducedType(); + } + return true; + } + + // Handle auto return types: + //- auto foo() {} + //- auto& foo() {} + //- auto foo() -> int {} + //- 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(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; + + const AutoType *AT = D->getReturnType()->getContainedAutoType(); + if (AT && !AT->getDeducedType().isNull()) { + DeducedType = AT->getDeducedType(); + } else if (auto DT = dyn_cast(D->getReturnType())) { + // auto in a trailing return type just points to a DecltypeType and + // getContainedAutoType does not unwrap it. + if (!DT->getUnderlyingType().isNull()) + DeducedType = DT->getUnderlyingType(); + } else if (!D->getReturnType().isNull()) { + DeducedType = D->getReturnType(); + } + 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(TL.getTypePtr()); + while (DT && !DT->getUnderlyingType().isNull()) { + DeducedType = DT->getUnderlyingType(); + DT = dyn_cast(DeducedType.getTypePtr()); + } + return true; + } + + QualType DeducedType; +}; +} // namespace + +llvm::Optional getDeducedType(ASTContext &ASTCtx, + SourceLocation Loc) { + Token Tok; + // Only try to find a deduced type if the token is auto or decltype. + if (!Loc.isValid() || + Lexer::getRawToken(Loc, Tok, ASTCtx.getSourceManager(), + ASTCtx.getLangOpts(), false) || + !Tok.is(tok::raw_identifier) || + !(Tok.getRawIdentifier() == "auto" || + Tok.getRawIdentifier() == "decltype")) { + return {}; + } + DeducedTypeVisitor V(Loc); + V.TraverseAST(ASTCtx); + if (V.DeducedType.isNull()) + return llvm::None; + return V.DeducedType; +} + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt --- a/clang-tools-extra/clangd/CMakeLists.txt +++ b/clang-tools-extra/clangd/CMakeLists.txt @@ -57,6 +57,7 @@ GlobalCompilationDatabase.cpp Headers.cpp HeaderSourceSwitch.cpp + Hover.cpp IncludeFixer.cpp JSONTransport.cpp Logger.cpp diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -16,6 +16,7 @@ #include "FormattedString.h" #include "Function.h" #include "GlobalCompilationDatabase.h" +#include "Hover.h" #include "Protocol.h" #include "SemanticHighlighting.h" #include "TUScheduler.h" diff --git a/clang-tools-extra/clangd/XRefs.h b/clang-tools-extra/clangd/XRefs.h --- a/clang-tools-extra/clangd/XRefs.h +++ b/clang-tools-extra/clangd/XRefs.h @@ -53,77 +53,6 @@ std::vector findDocumentHighlights(ParsedAST &AST, Position Pos); -/// Contains detailed information about a Symbol. Especially useful when -/// generating hover responses. It can be rendered as a hover panel, or -/// embedding clients can use the structured information to provide their own -/// UI. -struct HoverInfo { - /// Represents parameters of a function, a template or a macro. - /// For example: - /// - void foo(ParamType Name = DefaultValue) - /// - #define FOO(Name) - /// - template class Foo {}; - struct Param { - /// The pretty-printed parameter type, e.g. "int", or "typename" (in - /// TemplateParameters) - llvm::Optional Type; - /// None for unnamed parameters. - llvm::Optional Name; - /// None if no default is provided. - llvm::Optional Default; - }; - - /// For a variable named Bar, declared in clang::clangd::Foo::getFoo the - /// following fields will hold: - /// - NamespaceScope: clang::clangd:: - /// - LocalScope: Foo::getFoo:: - /// - Name: Bar - - /// Scopes might be None in cases where they don't make sense, e.g. macros and - /// auto/decltype. - /// Contains all of the enclosing namespaces, empty string means global - /// namespace. - llvm::Optional NamespaceScope; - /// Remaining named contexts in symbol's qualified name, empty string means - /// symbol is not local. - std::string LocalScope; - /// Name of the symbol, does not contain any "::". - std::string Name; - llvm::Optional SymRange; - /// Scope containing the symbol. e.g, "global namespace", "function x::Y" - /// - None for deduced types, e.g "auto", "decltype" keywords. - SymbolKind Kind; - std::string Documentation; - /// Source code containing the definition of the symbol. - std::string Definition; - - /// Pretty-printed variable type. - /// Set only for variables. - llvm::Optional Type; - /// Set for functions and lambadas. - llvm::Optional ReturnType; - /// Set for functions, lambdas and macros with parameters. - llvm::Optional> Parameters; - /// Set for all templates(function, class, variable). - llvm::Optional> TemplateParameters; - /// Contains the evaluated value of the symbol if available. - llvm::Optional Value; - - /// Produce a user-readable information. - FormattedString present() const; -}; -llvm::raw_ostream &operator<<(llvm::raw_ostream &, const HoverInfo::Param &); -inline bool operator==(const HoverInfo::Param &LHS, - const HoverInfo::Param &RHS) { - return std::tie(LHS.Type, LHS.Name, LHS.Default) == - std::tie(RHS.Type, RHS.Name, RHS.Default); -} - -/// Get the hover information when hovering at \p Pos. -llvm::Optional getHover(ParsedAST &AST, Position Pos, - format::FormatStyle Style, - const SymbolIndex *Index); - /// Returns reference locations of the symbol at a specified \p Pos. /// \p Limit limits the number of results returned (0 means no limit). std::vector findReferences(ParsedAST &AST, Position Pos, @@ -148,16 +77,6 @@ TypeHierarchyDirection Direction, const SymbolIndex *Index); -/// Retrieves the deduced type at a given location (auto, decltype). -/// Retuns None unless SourceLocationBeg starts an auto/decltype token. -/// It will return the underlying type. -llvm::Optional getDeducedType(ParsedAST &AST, - SourceLocation SourceLocationBeg); - -/// Check if there is a deduced type at a given location (auto, decltype). -/// SourceLocationBeg must point to the first character of the token -bool hasDeducedType(ParsedAST &AST, SourceLocation SourceLocationBeg); - /// Returns all decls that are referenced in the \p FD except local symbols. llvm::DenseSet getNonLocalDeclRefs(ParsedAST &AST, const FunctionDecl *FD); diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -10,7 +10,6 @@ #include "CodeCompletionStrings.h" #include "FindSymbols.h" #include "FindTarget.h" -#include "FormattedString.h" #include "Logger.h" #include "ParsedAST.h" #include "Protocol.h" @@ -20,15 +19,12 @@ #include "index/Index.h" #include "index/Merge.h" #include "index/Relation.h" -#include "index/SymbolCollector.h" #include "index/SymbolLocation.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/ExprCXX.h" -#include "clang/AST/PrettyPrinter.h" -#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Type.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" @@ -44,7 +40,6 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" -#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" @@ -396,500 +391,6 @@ return Result; } -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; -} - -/// Given a declaration \p D, return a human-readable string representing the -/// local scope in which it is declared, i.e. class(es) and method name. Returns -/// an empty string if it is not local. -static std::string getLocalScope(const Decl *D) { - std::vector Scopes; - const DeclContext *DC = D->getDeclContext(); - auto GetName = [](const TypeDecl *D) { - if (!D->getDeclName().isEmpty()) { - PrintingPolicy Policy = D->getASTContext().getPrintingPolicy(); - Policy.SuppressScope = true; - return declaredType(D).getAsString(Policy); - } - if (auto RD = dyn_cast(D)) - return ("(anonymous " + RD->getKindName() + ")").str(); - return std::string(""); - }; - while (DC) { - if (const TypeDecl *TD = dyn_cast(DC)) - Scopes.push_back(GetName(TD)); - else if (const FunctionDecl *FD = dyn_cast(DC)) - Scopes.push_back(FD->getNameAsString()); - DC = DC->getParent(); - } - - return llvm::join(llvm::reverse(Scopes), "::"); -} - -/// Returns the human-readable representation for namespace containing the -/// declaration \p D. Returns empty if it is contained global namespace. -static std::string getNamespaceScope(const Decl *D) { - const DeclContext *DC = D->getDeclContext(); - - if (const TypeDecl *TD = dyn_cast(DC)) - return getNamespaceScope(TD); - if (const FunctionDecl *FD = dyn_cast(DC)) - return getNamespaceScope(FD); - if (const NamedDecl *ND = dyn_cast(DC)) - return ND->getQualifiedNameAsString(); - - return ""; -} - -static std::string printDefinition(const Decl *D) { - std::string Definition; - llvm::raw_string_ostream OS(Definition); - PrintingPolicy Policy = - printingPolicyForDecls(D->getASTContext().getPrintingPolicy()); - Policy.IncludeTagDefinition = false; - Policy.SuppressTemplateArgsInCXXConstructors = true; - D->print(OS, Policy); - OS.flush(); - return Definition; -} - -static void printParams(llvm::raw_ostream &OS, - const std::vector &Params) { - for (size_t I = 0, E = Params.size(); I != E; ++I) { - if (I) - OS << ", "; - OS << Params.at(I); - } -} - -static std::vector -fetchTemplateParameters(const TemplateParameterList *Params, - const PrintingPolicy &PP) { - assert(Params); - std::vector TempParameters; - - for (const Decl *Param : *Params) { - HoverInfo::Param P; - P.Type.emplace(); - if (const auto TTP = dyn_cast(Param)) { - P.Type = TTP->wasDeclaredWithTypename() ? "typename" : "class"; - if (TTP->isParameterPack()) - *P.Type += "..."; - - if (!TTP->getName().empty()) - P.Name = TTP->getNameAsString(); - if (TTP->hasDefaultArgument()) - P.Default = TTP->getDefaultArgument().getAsString(PP); - } else if (const auto NTTP = dyn_cast(Param)) { - if (IdentifierInfo *II = NTTP->getIdentifier()) - P.Name = II->getName().str(); - - llvm::raw_string_ostream Out(*P.Type); - NTTP->getType().print(Out, PP); - if (NTTP->isParameterPack()) - Out << "..."; - - if (NTTP->hasDefaultArgument()) { - P.Default.emplace(); - llvm::raw_string_ostream Out(*P.Default); - NTTP->getDefaultArgument()->printPretty(Out, nullptr, PP); - } - } else if (const auto TTPD = dyn_cast(Param)) { - llvm::raw_string_ostream OS(*P.Type); - OS << "template <"; - printParams(OS, - fetchTemplateParameters(TTPD->getTemplateParameters(), PP)); - OS << "> class"; // FIXME: TemplateTemplateParameter doesn't store the - // info on whether this param was a "typename" or - // "class". - if (!TTPD->getName().empty()) - P.Name = TTPD->getNameAsString(); - if (TTPD->hasDefaultArgument()) { - P.Default.emplace(); - llvm::raw_string_ostream Out(*P.Default); - TTPD->getDefaultArgument().getArgument().print(PP, Out); - } - } - TempParameters.push_back(std::move(P)); - } - - return TempParameters; -} - -static const FunctionDecl *getUnderlyingFunction(const Decl *D) { - // Extract lambda from variables. - if (const VarDecl *VD = llvm::dyn_cast(D)) { - auto QT = VD->getType(); - if (!QT.isNull()) { - while (!QT->getPointeeType().isNull()) - QT = QT->getPointeeType(); - - if (const auto *CD = QT->getAsCXXRecordDecl()) - return CD->getLambdaCallOperator(); - } - } - - // Non-lambda functions. - return D->getAsFunction(); -} - -// Look up information about D from the index, and add it to Hover. -static void enhanceFromIndex(HoverInfo &Hover, const Decl *D, - const SymbolIndex *Index) { - if (!Index || !llvm::isa(D)) - return; - const NamedDecl &ND = *cast(D); - // We only add documentation, so don't bother if we already have some. - if (!Hover.Documentation.empty()) - return; - // Skip querying for non-indexable symbols, there's no point. - // We're searching for symbols that might be indexed outside this main file. - if (!SymbolCollector::shouldCollectSymbol(ND, ND.getASTContext(), - SymbolCollector::Options(), - /*IsMainFileOnly=*/false)) - return; - auto ID = getSymbolID(&ND); - if (!ID) - return; - LookupRequest Req; - Req.IDs.insert(*ID); - Index->lookup( - Req, [&](const Symbol &S) { Hover.Documentation = S.Documentation; }); -} - -// Populates Type, ReturnType, and Parameters for function-like decls. -static void fillFunctionTypeAndParams(HoverInfo &HI, const Decl *D, - const FunctionDecl *FD, - const PrintingPolicy &Policy) { - HI.Parameters.emplace(); - for (const ParmVarDecl *PVD : FD->parameters()) { - HI.Parameters->emplace_back(); - auto &P = HI.Parameters->back(); - if (!PVD->getType().isNull()) { - P.Type.emplace(); - llvm::raw_string_ostream OS(*P.Type); - PVD->getType().print(OS, Policy); - } else { - std::string Param; - llvm::raw_string_ostream OS(Param); - PVD->dump(OS); - OS.flush(); - elog("Got param with null type: {0}", Param); - } - if (!PVD->getName().empty()) - P.Name = PVD->getNameAsString(); - if (PVD->hasDefaultArg()) { - P.Default.emplace(); - llvm::raw_string_ostream Out(*P.Default); - PVD->getDefaultArg()->printPretty(Out, nullptr, Policy); - } - } - - if (const auto* CCD = llvm::dyn_cast(FD)) { - // Constructor's "return type" is the class type. - HI.ReturnType = declaredType(CCD->getParent()).getAsString(Policy); - // Don't provide any type for the constructor itself. - } else if (const auto* CDD = llvm::dyn_cast(FD)){ - HI.ReturnType = "void"; - } else { - HI.ReturnType = FD->getReturnType().getAsString(Policy); - - QualType FunctionType = FD->getType(); - if (const VarDecl *VD = llvm::dyn_cast(D)) // Lambdas - FunctionType = VD->getType().getDesugaredType(D->getASTContext()); - HI.Type = FunctionType.getAsString(Policy); - } - // FIXME: handle variadics. -} - -/// Generate a \p Hover object given the declaration \p D. -static HoverInfo getHoverContents(const Decl *D, const SymbolIndex *Index) { - HoverInfo HI; - const ASTContext &Ctx = D->getASTContext(); - - HI.NamespaceScope = getNamespaceScope(D); - if (!HI.NamespaceScope->empty()) - HI.NamespaceScope->append("::"); - HI.LocalScope = getLocalScope(D); - if (!HI.LocalScope.empty()) - HI.LocalScope.append("::"); - - PrintingPolicy Policy = printingPolicyForDecls(Ctx.getPrintingPolicy()); - if (const NamedDecl *ND = llvm::dyn_cast(D)) { - HI.Documentation = getDeclComment(Ctx, *ND); - HI.Name = printName(Ctx, *ND); - } - - HI.Kind = indexSymbolKindToSymbolKind(index::getSymbolInfo(D).Kind); - - // Fill in template params. - if (const TemplateDecl *TD = D->getDescribedTemplate()) { - HI.TemplateParameters = - fetchTemplateParameters(TD->getTemplateParameters(), Policy); - D = TD; - } else if (const FunctionDecl *FD = D->getAsFunction()) { - if (const auto FTD = FD->getDescribedTemplate()) { - HI.TemplateParameters = - fetchTemplateParameters(FTD->getTemplateParameters(), Policy); - D = FTD; - } - } - - // Fill in types and params. - if (const FunctionDecl *FD = getUnderlyingFunction(D)) { - fillFunctionTypeAndParams(HI, D, FD, Policy); - } else if (const auto *VD = dyn_cast(D)) { - HI.Type.emplace(); - llvm::raw_string_ostream OS(*HI.Type); - VD->getType().print(OS, Policy); - } - - // Fill in value with evaluated initializer if possible. - // FIXME(kadircet): Also set Value field for expressions like "sizeof" and - // function calls. - if (const auto *Var = dyn_cast(D)) { - if (const Expr *Init = Var->getInit()) { - Expr::EvalResult Result; - if (!Init->isValueDependent() && Init->EvaluateAsRValue(Result, Ctx)) { - HI.Value.emplace(); - llvm::raw_string_ostream ValueOS(*HI.Value); - Result.Val.printPretty(ValueOS, const_cast(Ctx), - Init->getType()); - } - } - } else if (const auto *ECD = dyn_cast(D)) { - // Dependent enums (e.g. nested in template classes) don't have values yet. - if (!ECD->getType()->isDependentType()) - HI.Value = ECD->getInitVal().toString(10); - } - - HI.Definition = printDefinition(D); - enhanceFromIndex(HI, D, Index); - return HI; -} - -/// Generate a \p Hover object given the type \p T. -static HoverInfo getHoverContents(QualType T, const Decl *D, ASTContext &ASTCtx, - const SymbolIndex *Index) { - HoverInfo HI; - llvm::raw_string_ostream OS(HI.Name); - PrintingPolicy Policy = printingPolicyForDecls(ASTCtx.getPrintingPolicy()); - T.print(OS, Policy); - OS.flush(); - - if (D) { - HI.Kind = indexSymbolKindToSymbolKind(index::getSymbolInfo(D).Kind); - enhanceFromIndex(HI, D, Index); - } - return HI; -} - -/// Generate a \p Hover object given the macro \p MacroDecl. -static HoverInfo getHoverContents(const DefinedMacro &Macro, ParsedAST &AST) { - HoverInfo HI; - SourceManager &SM = AST.getSourceManager(); - HI.Name = Macro.Name; - HI.Kind = indexSymbolKindToSymbolKind( - index::getSymbolInfoForMacro(*Macro.Info).Kind); - // FIXME: Populate documentation - // FIXME: Pupulate parameters - - // Try to get the full definition, not just the name - SourceLocation StartLoc = Macro.Info->getDefinitionLoc(); - SourceLocation EndLoc = Macro.Info->getDefinitionEndLoc(); - if (EndLoc.isValid()) { - EndLoc = Lexer::getLocForEndOfToken(EndLoc, 0, SM, - AST.getASTContext().getLangOpts()); - bool Invalid; - StringRef Buffer = SM.getBufferData(SM.getFileID(StartLoc), &Invalid); - if (!Invalid) { - unsigned StartOffset = SM.getFileOffset(StartLoc); - unsigned EndOffset = SM.getFileOffset(EndLoc); - if (EndOffset <= Buffer.size() && StartOffset < EndOffset) - HI.Definition = - ("#define " + Buffer.substr(StartOffset, EndOffset - StartOffset)) - .str(); - } - } - return HI; -} - -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 { - SourceLocation SearchedLocation; - -public: - DeducedTypeVisitor(SourceLocation SearchedLocation) - : SearchedLocation(SearchedLocation) {} - - // Handle auto initializers: - //- auto i = 1; - //- decltype(auto) i = 1; - //- auto& i = 1; - //- auto* i = &a; - bool VisitDeclaratorDecl(DeclaratorDecl *D) { - if (!D->getTypeSourceInfo() || - D->getTypeSourceInfo()->getTypeLoc().getBeginLoc() != SearchedLocation) - return true; - - if (auto *AT = D->getType()->getContainedAutoType()) { - if (!AT->getDeducedType().isNull()) { - DeducedType = AT->getDeducedType(); - this->D = D; - } - } - return true; - } - - // Handle auto return types: - //- auto foo() {} - //- auto& foo() {} - //- auto foo() -> int {} - //- 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(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; - - const AutoType *AT = D->getReturnType()->getContainedAutoType(); - if (AT && !AT->getDeducedType().isNull()) { - DeducedType = AT->getDeducedType(); - this->D = D; - } else if (auto DT = dyn_cast(D->getReturnType())) { - // auto in a trailing return type just points to a DecltypeType and - // getContainedAutoType does not unwrap it. - if (!DT->getUnderlyingType().isNull()) { - DeducedType = DT->getUnderlyingType(); - this->D = D; - } - } else if (!D->getReturnType().isNull()) { - DeducedType = D->getReturnType(); - this->D = D; - } - 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(TL.getTypePtr()); - while (DT && !DT->getUnderlyingType().isNull()) { - DeducedType = DT->getUnderlyingType(); - D = DT->getAsTagDecl(); - DT = dyn_cast(DeducedType.getTypePtr()); - } - return true; - } - - QualType DeducedType; - const Decl *D = nullptr; -}; -} // namespace - -/// Retrieves the deduced type at a given location (auto, decltype). -/// SourceLocationBeg must point to the first character of the token -llvm::Optional 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); - V.TraverseAST(AST.getASTContext()); - return V.DeducedType; -} - -/// Retrieves the deduced type at a given location (auto, decltype). -bool hasDeducedType(ParsedAST &AST, SourceLocation SourceLocationBeg) { - return (bool)getDeducedType(AST, SourceLocationBeg); -} - -llvm::Optional getHover(ParsedAST &AST, Position Pos, - format::FormatStyle Style, - const SymbolIndex *Index) { - const SourceManager &SM = AST.getSourceManager(); - llvm::Optional HI; - SourceLocation SourceLocationBeg = SM.getMacroArgExpandedLocation( - getBeginningOfIdentifier(Pos, SM, AST.getASTContext().getLangOpts())); - - if (hasDeducedType(AST, SourceLocationBeg)) { - DeducedTypeVisitor V(SourceLocationBeg); - V.TraverseAST(AST.getASTContext()); - if (!V.DeducedType.isNull()) - HI = getHoverContents(V.DeducedType, V.D, AST.getASTContext(), Index); - } - - if (!HI) { - if (auto M = locateMacroAt(SourceLocationBeg, AST.getPreprocessor())) { - HI = getHoverContents(*M, AST); - } else { - DeclRelationSet Relations = - DeclRelation::TemplatePattern | DeclRelation::Alias; - auto Decls = getDeclAtPosition(AST, SourceLocationBeg, Relations); - if (!Decls.empty()) - HI = getHoverContents(Decls.front(), Index); - } - } - - if (!HI) - return llvm::None; - - auto Replacements = format::reformat( - Style, HI->Definition, tooling::Range(0, HI->Definition.size())); - if (auto Formatted = - tooling::applyAllReplacements(HI->Definition, Replacements)) - HI->Definition = *Formatted; - - HI->SymRange = - getTokenRange(AST.getASTContext().getSourceManager(), - AST.getASTContext().getLangOpts(), SourceLocationBeg); - return HI; -} - std::vector findReferences(ParsedAST &AST, Position Pos, uint32_t Limit, const SymbolIndex *Index) { if (!Limit) @@ -1250,44 +751,6 @@ } } -FormattedString HoverInfo::present() const { - FormattedString Output; - if (NamespaceScope) { - Output.appendText("Declared in"); - // Drop trailing "::". - if (!LocalScope.empty()) - Output.appendInlineCode(llvm::StringRef(LocalScope).drop_back(2)); - else if (NamespaceScope->empty()) - Output.appendInlineCode("global namespace"); - else - Output.appendInlineCode(llvm::StringRef(*NamespaceScope).drop_back(2)); - } - - if (!Definition.empty()) { - Output.appendCodeBlock(Definition); - } else { - // Builtin types - Output.appendCodeBlock(Name); - } - - if (!Documentation.empty()) - Output.appendText(Documentation); - return Output; -} - -llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, - const HoverInfo::Param &P) { - std::vector Output; - if (P.Type) - Output.push_back(*P.Type); - if (P.Name) - Output.push_back(*P.Name); - OS << llvm::join(Output, " "); - if (P.Default) - OS << " = " << *P.Default; - return OS; -} - llvm::DenseSet getNonLocalDeclRefs(ParsedAST &AST, const FunctionDecl *FD) { if (!FD->hasBody()) diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp --- a/clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp @@ -74,10 +74,10 @@ auto& SrcMgr = Inputs.AST.getASTContext().getSourceManager(); llvm::Optional DeducedType = - getDeducedType(Inputs.AST, CachedLocation->getBeginLoc()); + getDeducedType(Inputs.AST.getASTContext(), CachedLocation->getBeginLoc()); // if we can't resolve the type, return an error message - if (DeducedType == llvm::None || DeducedType->isNull()) { + if (DeducedType == llvm::None) { return createErrorMessage("Could not deduce type for 'auto' type", Inputs); } diff --git a/clang-tools-extra/clangd/unittests/ASTTests.cpp b/clang-tools-extra/clangd/unittests/ASTTests.cpp --- a/clang-tools-extra/clangd/unittests/ASTTests.cpp +++ b/clang-tools-extra/clangd/unittests/ASTTests.cpp @@ -7,13 +7,18 @@ //===----------------------------------------------------------------------===// #include "AST.h" + +#include "Annotations.h" +#include "TestTU.h" + +#include "clang/Basic/SourceManager.h" #include "gtest/gtest.h" namespace clang { namespace clangd { namespace { -TEST(ExpandAutoType, ShortenNamespace) { +TEST(ShortenNamespace, All) { ASSERT_EQ("TestClass", shortenNamespace("TestClass", "")); ASSERT_EQ("TestClass", shortenNamespace( @@ -36,6 +41,30 @@ "testns1::TestClass", "testns1")); } +TEST(GetDeducedType, KwAutoExpansion) { + struct Test { + StringRef AnnotatedCode; + const char *DeducedType; + } Tests[] = { + {"^auto i = 0;", "int"}, + {"^auto f(){ return 1;};", "int"}, + }; + for (Test T : Tests) { + Annotations File(T.AnnotatedCode); + auto AST = TestTU::withCode(File.code()).build(); + ASSERT_TRUE(AST.getDiagnostics().empty()) + << AST.getDiagnostics().begin()->Message; + SourceManagerForFile SM("foo.cpp", File.code()); + + for (Position Pos : File.points()) { + auto Location = sourceLocationInMainFile(SM.get(), Pos); + ASSERT_TRUE(!!Location) << llvm::toString(Location.takeError()); + auto DeducedType = getDeducedType(AST.getASTContext(), *Location); + EXPECT_EQ(DeducedType->getAsString(), T.DeducedType); + } + } +} + } // namespace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/CMakeLists.txt b/clang-tools-extra/clangd/unittests/CMakeLists.txt --- a/clang-tools-extra/clangd/unittests/CMakeLists.txt +++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt @@ -47,6 +47,7 @@ GlobalCompilationDatabaseTests.cpp HeadersTests.cpp HeaderSourceSwitchTests.cpp + HoverTests.cpp IndexActionTests.cpp IndexTests.cpp JSONTransportTests.cpp diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -603,1298 +603,6 @@ HeaderNotInPreambleAnnotations.range()))); } -TEST(Hover, Structured) { - struct { - const char *const Code; - const std::function ExpectedBuilder; - } Cases[] = { - // Global scope. - {R"cpp( - // Best foo ever. - void [[fo^o]]() {} - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = ""; - HI.Name = "foo"; - HI.Kind = SymbolKind::Function; - HI.Documentation = "Best foo ever."; - HI.Definition = "void foo()"; - HI.ReturnType = "void"; - HI.Type = "void ()"; - HI.Parameters.emplace(); - }}, - // Inside namespace - {R"cpp( - namespace ns1 { namespace ns2 { - /// Best foo ever. - void [[fo^o]]() {} - }} - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = "ns1::ns2::"; - HI.Name = "foo"; - HI.Kind = SymbolKind::Function; - HI.Documentation = "Best foo ever."; - HI.Definition = "void foo()"; - HI.ReturnType = "void"; - HI.Type = "void ()"; - HI.Parameters.emplace(); - }}, - // Field - {R"cpp( - namespace ns1 { namespace ns2 { - struct Foo { - int [[b^ar]]; - }; - }} - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = "ns1::ns2::"; - HI.LocalScope = "Foo::"; - HI.Name = "bar"; - HI.Kind = SymbolKind::Field; - HI.Definition = "int bar"; - HI.Type = "int"; - }}, - // Local to class method. - {R"cpp( - namespace ns1 { namespace ns2 { - struct Foo { - void foo() { - int [[b^ar]]; - } - }; - }} - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = "ns1::ns2::"; - HI.LocalScope = "Foo::foo::"; - HI.Name = "bar"; - HI.Kind = SymbolKind::Variable; - HI.Definition = "int bar"; - HI.Type = "int"; - }}, - // Anon namespace and local scope. - {R"cpp( - namespace ns1 { namespace { - struct { - int [[b^ar]]; - } T; - }} - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = "ns1::(anonymous)::"; - HI.LocalScope = "(anonymous struct)::"; - HI.Name = "bar"; - HI.Kind = SymbolKind::Field; - HI.Definition = "int bar"; - HI.Type = "int"; - }}, - // Variable with template type - {R"cpp( - template class Foo { public: Foo(int); }; - Foo [[fo^o]] = Foo(5); - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = ""; - HI.Name = "foo"; - HI.Kind = SymbolKind::Variable; - HI.Definition = "Foo foo = Foo(5)"; - HI.Type = "Foo"; - }}, - // Implicit template instantiation - {R"cpp( - template class vector{}; - [[vec^tor]] foo; - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = ""; - HI.Name = "vector"; - HI.Kind = SymbolKind::Class; - HI.Definition = "template class vector {}"; - HI.TemplateParameters = { - {std::string("typename"), std::string("T"), llvm::None}, - }; - }}, - // Class template - {R"cpp( - template class C, - typename = char, - int = 0, - bool Q = false, - class... Ts> class Foo {}; - template class T> - [[F^oo]] foo; - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = ""; - HI.Name = "Foo"; - HI.Kind = SymbolKind::Class; - HI.Definition = - R"cpp(template