diff options
-rw-r--r-- | clang-tools-extra/clangd/AST.cpp | 111 | ||||
-rw-r--r-- | clang-tools-extra/clangd/AST.h | 5 | ||||
-rw-r--r-- | clang-tools-extra/clangd/CMakeLists.txt | 1 | ||||
-rw-r--r-- | clang-tools-extra/clangd/ClangdServer.h | 1 | ||||
-rw-r--r-- | clang-tools-extra/clangd/Hover.cpp | 443 | ||||
-rw-r--r-- | clang-tools-extra/clangd/Hover.h | 93 | ||||
-rw-r--r-- | clang-tools-extra/clangd/XRefs.cpp | 537 | ||||
-rw-r--r-- | clang-tools-extra/clangd/XRefs.h | 81 | ||||
-rw-r--r-- | clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp | 5 | ||||
-rw-r--r-- | clang-tools-extra/clangd/unittests/ASTTests.cpp | 30 | ||||
-rw-r--r-- | clang-tools-extra/clangd/unittests/CMakeLists.txt | 1 | ||||
-rw-r--r-- | clang-tools-extra/clangd/unittests/HoverTests.cpp | 1339 | ||||
-rw-r--r-- | clang-tools-extra/clangd/unittests/XRefsTests.cpp | 1339 |
13 files changed, 2025 insertions, 1961 deletions
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp index d04ebcf22a8..6c69367633b 100644 --- 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 @@ QualType declaredType(const TypeDecl *D) { 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<DeducedTypeVisitor> { + 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<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; + + const AutoType *AT = D->getReturnType()->getContainedAutoType(); + if (AT && !AT->getDeducedType().isNull()) { + DeducedType = AT->getDeducedType(); + } else if (auto DT = dyn_cast<DecltypeType>(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<DecltypeType>(TL.getTypePtr()); + while (DT && !DT->getUnderlyingType().isNull()) { + DeducedType = DT->getUnderlyingType(); + DT = dyn_cast<DecltypeType>(DeducedType.getTypePtr()); + } + return true; + } + + QualType DeducedType; +}; +} // namespace + +llvm::Optional<QualType> 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/AST.h b/clang-tools-extra/clangd/AST.h index b106e06f8d9..3850ab4f6b4 100644 --- a/clang-tools-extra/clangd/AST.h +++ b/clang-tools-extra/clangd/AST.h @@ -116,6 +116,11 @@ NestedNameSpecifierLoc getQualifierLoc(const NamedDecl &ND); // (i.e. vector<T*> rather than vector<type-parameter-0-0 *>. 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<QualType> getDeducedType(ASTContext &, SourceLocation Loc); + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt index d428c8ad269..8ab2ae6b91d 100644 --- a/clang-tools-extra/clangd/CMakeLists.txt +++ b/clang-tools-extra/clangd/CMakeLists.txt @@ -57,6 +57,7 @@ add_clang_library(clangDaemon 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 index fdea9b389e3..cd0b91c08f0 100644 --- 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/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp new file mode 100644 index 00000000000..0e28e30482e --- /dev/null +++ b/clang-tools-extra/clangd/Hover.cpp @@ -0,0 +1,443 @@ +//===--- Hover.cpp - Information about code at the cursor location --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Hover.h" + +#include "AST.h" +#include "CodeCompletionStrings.h" +#include "FindTarget.h" +#include "Logger.h" +#include "Selection.h" +#include "SourceCode.h" +#include "index/SymbolCollector.h" + +#include "clang/AST/ASTTypeTraits.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/PrettyPrinter.h" + +namespace clang { +namespace clangd { +namespace { + +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. +std::string getLocalScope(const Decl *D) { + std::vector<std::string> 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<RecordDecl>(D)) + return ("(anonymous " + RD->getKindName() + ")").str(); + return std::string(""); + }; + while (DC) { + if (const TypeDecl *TD = dyn_cast<TypeDecl>(DC)) + Scopes.push_back(GetName(TD)); + else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(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. +std::string getNamespaceScope(const Decl *D) { + const DeclContext *DC = D->getDeclContext(); + + if (const TypeDecl *TD = dyn_cast<TypeDecl>(DC)) + return getNamespaceScope(TD); + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DC)) + return getNamespaceScope(FD); + if (const NamedDecl *ND = dyn_cast<NamedDecl>(DC)) + return ND->getQualifiedNameAsString(); + + return ""; +} + +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; +} + +void printParams(llvm::raw_ostream &OS, + const std::vector<HoverInfo::Param> &Params) { + for (size_t I = 0, E = Params.size(); I != E; ++I) { + if (I) + OS << ", "; + OS << Params.at(I); + } +} + +std::vector<HoverInfo::Param> +fetchTemplateParameters(const TemplateParameterList *Params, + const PrintingPolicy &PP) { + assert(Params); + std::vector<HoverInfo::Param> TempParameters; + + for (const Decl *Param : *Params) { + HoverInfo::Param P; + P.Type.emplace(); + if (const auto TTP = dyn_cast<TemplateTypeParmDecl>(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<NonTypeTemplateParmDecl>(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<TemplateTemplateParmDecl>(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; +} + +const FunctionDecl *getUnderlyingFunction(const Decl *D) { + // Extract lambda from variables. + if (const VarDecl *VD = llvm::dyn_cast<VarDecl>(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. +void enhanceFromIndex(HoverInfo &Hover, const Decl *D, + const SymbolIndex *Index) { + if (!Index || !llvm::isa<NamedDecl>(D)) + return; + const NamedDecl &ND = *cast<NamedDecl>(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. +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<CXXConstructorDecl>(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<CXXDestructorDecl>(FD)){ + HI.ReturnType = "void"; + } else { + HI.ReturnType = FD->getReturnType().getAsString(Policy); + + QualType FunctionType = FD->getType(); + if (const VarDecl *VD = llvm::dyn_cast<VarDecl>(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. +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<NamedDecl>(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<ValueDecl>(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<VarDecl>(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<ASTContext &>(Ctx), + Init->getType()); + } + } + } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(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. +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. +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 + +llvm::Optional<HoverInfo> getHover(ParsedAST &AST, Position Pos, + format::FormatStyle Style, + const SymbolIndex *Index) { + const SourceManager &SM = AST.getSourceManager(); + llvm::Optional<HoverInfo> HI; + SourceLocation SourceLocationBeg = SM.getMacroArgExpandedLocation( + getBeginningOfIdentifier(Pos, SM, AST.getASTContext().getLangOpts())); + + if (auto Deduced = getDeducedType(AST.getASTContext(), SourceLocationBeg)) { + // Find the corresponding decl to populate kind and fetch documentation. + DeclRelationSet Rel = DeclRelation::TemplatePattern | DeclRelation::Alias; + auto Decls = + targetDecl(ast_type_traits::DynTypedNode::create(*Deduced), Rel); + HI = getHoverContents(*Deduced, Decls.empty() ? nullptr : Decls.front(), + AST.getASTContext(), Index); + } else if (auto M = locateMacroAt(SourceLocationBeg, AST.getPreprocessor())) { + HI = getHoverContents(*M, AST); + } else { + auto Offset = positionToOffset(SM.getBufferData(SM.getMainFileID()), Pos); + if (!Offset) { + llvm::consumeError(Offset.takeError()); + return llvm::None; + } + SelectionTree Selection(AST.getASTContext(), AST.getTokens(), *Offset); + std::vector<const Decl *> Result; + if (const SelectionTree::Node *N = Selection.commonAncestor()) { + DeclRelationSet Rel = DeclRelation::TemplatePattern | DeclRelation::Alias; + auto Decls = targetDecl(N->ASTNode, Rel); + 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; +} + +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<llvm::StringRef> 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; +} + +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/Hover.h b/clang-tools-extra/clangd/Hover.h new file mode 100644 index 00000000000..704e5c4b14e --- /dev/null +++ b/clang-tools-extra/clangd/Hover.h @@ -0,0 +1,93 @@ +//===--- Hover.h - Information about code at the cursor location -*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_HOVER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_HOVER_H + +#include "FormattedString.h" +#include "ParsedAST.h" +#include "Protocol.h" + +namespace clang { +namespace clangd { + +/// 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 <ParamType Name = DefaultType> class Foo {}; + struct Param { + /// The pretty-printed parameter type, e.g. "int", or "typename" (in + /// TemplateParameters) + llvm::Optional<std::string> Type; + /// None for unnamed parameters. + llvm::Optional<std::string> Name; + /// None if no default is provided. + llvm::Optional<std::string> 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<std::string> 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<Range> 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<std::string> Type; + /// Set for functions and lambadas. + llvm::Optional<std::string> ReturnType; + /// Set for functions, lambdas and macros with parameters. + llvm::Optional<std::vector<Param>> Parameters; + /// Set for all templates(function, class, variable). + llvm::Optional<std::vector<Param>> TemplateParameters; + /// Contains the evaluated value of the symbol if available. + llvm::Optional<std::string> 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<HoverInfo> getHover(ParsedAST &AST, Position Pos, + format::FormatStyle Style, + const SymbolIndex *Index); + +} // namespace clangd +} // namespace clang + +#endif diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index 3165633e60f..de10e3c48e2 100644 --- 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 @@ std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST, 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<std::string> 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<RecordDecl>(D)) - return ("(anonymous " + RD->getKindName() + ")").str(); - return std::string(""); - }; - while (DC) { - if (const TypeDecl *TD = dyn_cast<TypeDecl>(DC)) - Scopes.push_back(GetName(TD)); - else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(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<TypeDecl>(DC)) - return getNamespaceScope(TD); - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DC)) - return getNamespaceScope(FD); - if (const NamedDecl *ND = dyn_cast<NamedDecl>(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<HoverInfo::Param> &Params) { - for (size_t I = 0, E = Params.size(); I != E; ++I) { - if (I) - OS << ", "; - OS << Params.at(I); - } -} - -static std::vector<HoverInfo::Param> -fetchTemplateParameters(const TemplateParameterList *Params, - const PrintingPolicy &PP) { - assert(Params); - std::vector<HoverInfo::Param> TempParameters; - - for (const Decl *Param : *Params) { - HoverInfo::Param P; - P.Type.emplace(); - if (const auto TTP = dyn_cast<TemplateTypeParmDecl>(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<NonTypeTemplateParmDecl>(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<TemplateTemplateParmDecl>(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<VarDecl>(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<NamedDecl>(D)) - return; - const NamedDecl &ND = *cast<NamedDecl>(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<CXXConstructorDecl>(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<CXXDestructorDecl>(FD)){ - HI.ReturnType = "void"; - } else { - HI.ReturnType = FD->getReturnType().getAsString(Policy); - - QualType FunctionType = FD->getType(); - if (const VarDecl *VD = llvm::dyn_cast<VarDecl>(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<NamedDecl>(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<ValueDecl>(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<VarDecl>(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<ASTContext &>(Ctx), - Init->getType()); - } - } - } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(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<DeducedTypeVisitor> { - 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<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; - - const AutoType *AT = D->getReturnType()->getContainedAutoType(); - if (AT && !AT->getDeducedType().isNull()) { - DeducedType = AT->getDeducedType(); - this->D = D; - } else if (auto DT = dyn_cast<DecltypeType>(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<DecltypeType>(TL.getTypePtr()); - while (DT && !DT->getUnderlyingType().isNull()) { - DeducedType = DT->getUnderlyingType(); - D = DT->getAsTagDecl(); - DT = dyn_cast<DecltypeType>(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<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); - 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<HoverInfo> getHover(ParsedAST &AST, Position Pos, - format::FormatStyle Style, - const SymbolIndex *Index) { - const SourceManager &SM = AST.getSourceManager(); - llvm::Optional<HoverInfo> 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; -} - ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit, const SymbolIndex *Index) { if (!Limit) @@ -1255,44 +756,6 @@ void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels, } } -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<llvm::StringRef> 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<const Decl *> getNonLocalDeclRefs(ParsedAST &AST, const FunctionDecl *FD) { if (!FD->hasBody()) diff --git a/clang-tools-extra/clangd/XRefs.h b/clang-tools-extra/clangd/XRefs.h index 8bd91024be3..dc9e4f23f1a 100644 --- a/clang-tools-extra/clangd/XRefs.h +++ b/clang-tools-extra/clangd/XRefs.h @@ -53,77 +53,6 @@ std::vector<LocatedSymbol> locateSymbolAt(ParsedAST &AST, Position Pos, std::vector<DocumentHighlight> 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 <ParamType Name = DefaultType> class Foo {}; - struct Param { - /// The pretty-printed parameter type, e.g. "int", or "typename" (in - /// TemplateParameters) - llvm::Optional<std::string> Type; - /// None for unnamed parameters. - llvm::Optional<std::string> Name; - /// None if no default is provided. - llvm::Optional<std::string> 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<std::string> 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<Range> 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<std::string> Type; - /// Set for functions and lambadas. - llvm::Optional<std::string> ReturnType; - /// Set for functions, lambdas and macros with parameters. - llvm::Optional<std::vector<Param>> Parameters; - /// Set for all templates(function, class, variable). - llvm::Optional<std::vector<Param>> TemplateParameters; - /// Contains the evaluated value of the symbol if available. - llvm::Optional<std::string> 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<HoverInfo> getHover(ParsedAST &AST, Position Pos, - format::FormatStyle Style, - const SymbolIndex *Index); - struct ReferencesResult { std::vector<Location> References; bool HasMore = false; @@ -151,16 +80,6 @@ void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels, 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<QualType> 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<const Decl *> getNonLocalDeclRefs(ParsedAST &AST, const FunctionDecl *FD); diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp index 02c9cdb8280..31e79d27d06 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp @@ -74,12 +74,11 @@ Expected<Tweak::Effect> ExpandAutoType::apply(const Selection& Inputs) { auto& SrcMgr = Inputs.AST.getASTContext().getSourceManager(); llvm::Optional<clang::QualType> 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); - } // if it's a lambda expression, return an error message if (isa<RecordType>(*DeducedType) && diff --git a/clang-tools-extra/clangd/unittests/ASTTests.cpp b/clang-tools-extra/clangd/unittests/ASTTests.cpp index cbd18b5bf90..b3040e2a7ed 100644 --- a/clang-tools-extra/clangd/unittests/ASTTests.cpp +++ b/clang-tools-extra/clangd/unittests/ASTTests.cpp @@ -7,13 +7,17 @@ //===----------------------------------------------------------------------===// #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 +40,30 @@ TEST(ExpandAutoType, ShortenNamespace) { "testns1::TestClass<testns1::OtherClass>", "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 index 21c29196034..a991fe26d27 100644 --- a/clang-tools-extra/clangd/unittests/CMakeLists.txt +++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt @@ -48,6 +48,7 @@ add_unittest(ClangdUnitTests ClangdTests GlobalCompilationDatabaseTests.cpp HeadersTests.cpp HeaderSourceSwitchTests.cpp + HoverTests.cpp IndexActionTests.cpp IndexTests.cpp JSONTransportTests.cpp diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp new file mode 100644 index 00000000000..7b5907faaa2 --- /dev/null +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -0,0 +1,1339 @@ +//===-- HoverTests.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Annotations.h" +#include "Hover.h" +#include "TestIndex.h" +#include "TestTU.h" +#include "index/MemIndex.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace clang { +namespace clangd { +namespace { + +TEST(Hover, Structured) { + struct { + const char *const Code; + const std::function<void(HoverInfo &)> 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 <typename T, class... Ts> class Foo { public: Foo(int); }; + Foo<int, char, bool> [[fo^o]] = Foo<int, char, bool>(5); + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.Name = "foo"; + HI.Kind = SymbolKind::Variable; + HI.Definition = "Foo<int, char, bool> foo = Foo<int, char, bool>(5)"; + HI.Type = "Foo<int, char, bool>"; + }}, + // Implicit template instantiation + {R"cpp( + template <typename T> class vector{}; + [[vec^tor]]<int> foo; + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.Name = "vector"; + HI.Kind = SymbolKind::Class; + HI.Definition = "template <typename T> class vector {}"; + HI.TemplateParameters = { + {std::string("typename"), std::string("T"), llvm::None}, + }; + }}, + // Class template + {R"cpp( + template <template<typename, bool...> class C, + typename = char, + int = 0, + bool Q = false, + class... Ts> class Foo {}; + template <template<typename, bool...> class T> + [[F^oo]]<T> foo; + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.Name = "Foo"; + HI.Kind = SymbolKind::Class; + HI.Definition = + R"cpp(template <template <typename, bool...> class C, typename = char, int = 0, + bool Q = false, class... Ts> +class Foo {})cpp"; + HI.TemplateParameters = { + {std::string("template <typename, bool...> class"), + std::string("C"), llvm::None}, + {std::string("typename"), llvm::None, std::string("char")}, + {std::string("int"), llvm::None, std::string("0")}, + {std::string("bool"), std::string("Q"), std::string("false")}, + {std::string("class..."), std::string("Ts"), llvm::None}, + }; + }}, + // Function template + {R"cpp( + template <template<typename, bool...> class C, + typename = char, + int = 0, + bool Q = false, + class... Ts> void foo(); + template<typename, bool...> class Foo; + + void bar() { + [[fo^o]]<Foo>(); + } + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.Name = "foo"; + HI.Kind = SymbolKind::Function; + HI.Definition = + R"cpp(template <template <typename, bool...> class C, typename = char, int = 0, + bool Q = false, class... Ts> +void foo())cpp"; + HI.ReturnType = "void"; + HI.Type = "void ()"; + HI.Parameters.emplace(); + HI.TemplateParameters = { + {std::string("template <typename, bool...> class"), + std::string("C"), llvm::None}, + {std::string("typename"), llvm::None, std::string("char")}, + {std::string("int"), llvm::None, std::string("0")}, + {std::string("bool"), std::string("Q"), std::string("false")}, + {std::string("class..."), std::string("Ts"), llvm::None}, + }; + }}, + // Function decl + {R"cpp( + template<typename, bool...> class Foo {}; + Foo<bool, true, false> foo(int, bool T = false); + + void bar() { + [[fo^o]](3); + } + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.Name = "foo"; + HI.Kind = SymbolKind::Function; + HI.Definition = "Foo<bool, true, false> foo(int, bool T = false)"; + HI.ReturnType = "Foo<bool, true, false>"; + HI.Type = "Foo<bool, true, false> (int, bool)"; + HI.Parameters = { + {std::string("int"), llvm::None, llvm::None}, + {std::string("bool"), std::string("T"), std::string("false")}, + }; + }}, + // Pointers to lambdas + {R"cpp( + void foo() { + auto lamb = [](int T, bool B) -> bool { return T && B; }; + auto *b = &lamb; + auto *[[^c]] = &b; + } + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.LocalScope = "foo::"; + HI.Name = "c"; + HI.Kind = SymbolKind::Variable; + HI.Definition = "auto *c = &b"; + HI.Type = "class (lambda) **"; + HI.ReturnType = "bool"; + HI.Parameters = { + {std::string("int"), std::string("T"), llvm::None}, + {std::string("bool"), std::string("B"), llvm::None}, + }; + return HI; + }}, + // Lambda parameter with decltype reference + {R"cpp( + auto lamb = [](int T, bool B) -> bool { return T && B; }; + void foo(decltype(lamb)& bar) { + [[ba^r]](0, false); + } + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.LocalScope = "foo::"; + HI.Name = "bar"; + HI.Kind = SymbolKind::Variable; + HI.Definition = "decltype(lamb) &bar"; + HI.Type = "decltype(lamb) &"; + HI.ReturnType = "bool"; + HI.Parameters = { + {std::string("int"), std::string("T"), llvm::None}, + {std::string("bool"), std::string("B"), llvm::None}, + }; + return HI; + }}, + // Lambda parameter with decltype + {R"cpp( + auto lamb = [](int T, bool B) -> bool { return T && B; }; + void foo(decltype(lamb) bar) { + [[ba^r]](0, false); + } + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.LocalScope = "foo::"; + HI.Name = "bar"; + HI.Kind = SymbolKind::Variable; + HI.Definition = "decltype(lamb) bar"; + HI.Type = "class (lambda)"; + HI.ReturnType = "bool"; + HI.Parameters = { + {std::string("int"), std::string("T"), llvm::None}, + {std::string("bool"), std::string("B"), llvm::None}, + }; + return HI; + }}, + // Lambda variable + {R"cpp( + void foo() { + int bar = 5; + auto lamb = [&bar](int T, bool B) -> bool { return T && B && bar; }; + bool res = [[lam^b]](bar, false); + } + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.LocalScope = "foo::"; + HI.Name = "lamb"; + HI.Kind = SymbolKind::Variable; + HI.Definition = "auto lamb = [&bar](int T, bool B) -> bool {}"; + HI.Type = "class (lambda)"; + HI.ReturnType = "bool"; + HI.Parameters = { + {std::string("int"), std::string("T"), llvm::None}, + {std::string("bool"), std::string("B"), llvm::None}, + }; + return HI; + }}, + // Local variable in lambda + {R"cpp( + void foo() { + auto lamb = []{int [[te^st]];}; + } + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.LocalScope = "foo::(anonymous class)::operator()::"; + HI.Name = "test"; + HI.Kind = SymbolKind::Variable; + HI.Definition = "int test"; + HI.Type = "int"; + }}, + // Partially-specialized class template. (formerly type-parameter-0-0) + {R"cpp( + template <typename T> class X; + template <typename T> class [[^X]]<T*> {}; + )cpp", + [](HoverInfo &HI) { + HI.Name = "X<T *>"; + HI.NamespaceScope = ""; + HI.Kind = SymbolKind::Class; + HI.Definition = "template <typename T> class X<T *> {}"; + }}, + // Constructor of partially-specialized class template + {R"cpp( + template<typename, typename=void> struct X; + template<typename T> struct X<T*>{ [[^X]](); }; + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.Name = "X"; + HI.LocalScope = "X<T *>::"; // FIXME: X<T *, void>:: + HI.Kind = SymbolKind::Constructor; + HI.ReturnType = "X<T *>"; + HI.Definition = "X()"; + HI.Parameters.emplace(); + }}, + {"class X { [[^~]]X(); };", // FIXME: Should be [[~X]]() + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.Name = "~X"; + HI.LocalScope = "X::"; + HI.Kind = SymbolKind::Constructor; + HI.ReturnType = "void"; + HI.Definition = "~X()"; + HI.Parameters.emplace(); + }}, + + // auto on lambda + {R"cpp( + void foo() { + [[au^to]] lamb = []{}; + } + )cpp", + [](HoverInfo &HI) { + HI.Name = "class (lambda)"; + HI.Kind = SymbolKind::Class; + }}, + // auto on template instantiation + {R"cpp( + template<typename T> class Foo{}; + void foo() { + [[au^to]] x = Foo<int>(); + } + )cpp", + [](HoverInfo &HI) { + HI.Name = "class Foo<int>"; + HI.Kind = SymbolKind::Class; + }}, + // auto on specialized template + {R"cpp( + template<typename T> class Foo{}; + template<> class Foo<int>{}; + void foo() { + [[au^to]] x = Foo<int>(); + } + )cpp", + [](HoverInfo &HI) { + HI.Name = "class Foo<int>"; + HI.Kind = SymbolKind::Class; + }}, + + // macro + {R"cpp( + // Best MACRO ever. + #define MACRO(x,y,z) void foo(x, y, z); + [[MAC^RO]](int, double d, bool z = false); + )cpp", + [](HoverInfo &HI) { + HI.Name = "MACRO", HI.Kind = SymbolKind::String, + HI.Definition = "#define MACRO(x, y, z) void foo(x, y, z);"; + }}, + + // constexprs + {R"cpp( + constexpr int add(int a, int b) { return a + b; } + int [[b^ar]] = add(1, 2); + )cpp", + [](HoverInfo &HI) { + HI.Name = "bar"; + HI.Definition = "int bar = add(1, 2)"; + HI.Kind = SymbolKind::Variable; + HI.Type = "int"; + HI.NamespaceScope = ""; + HI.Value = "3"; + }}, + {R"cpp( + int [[b^ar]] = sizeof(char); + )cpp", + [](HoverInfo &HI) { + HI.Name = "bar"; + HI.Definition = "int bar = sizeof(char)"; + HI.Kind = SymbolKind::Variable; + HI.Type = "int"; + HI.NamespaceScope = ""; + HI.Value = "1"; + }}, + {R"cpp( + template<int a, int b> struct Add { + static constexpr int result = a + b; + }; + int [[ba^r]] = Add<1, 2>::result; + )cpp", + [](HoverInfo &HI) { + HI.Name = "bar"; + HI.Definition = "int bar = Add<1, 2>::result"; + HI.Kind = SymbolKind::Variable; + HI.Type = "int"; + HI.NamespaceScope = ""; + HI.Value = "3"; + }}, + {R"cpp( + enum Color { RED, GREEN, }; + Color x = [[GR^EEN]]; + )cpp", + [](HoverInfo &HI) { + HI.Name = "GREEN"; + HI.NamespaceScope = ""; + HI.LocalScope = "Color::"; + HI.Definition = "GREEN"; + HI.Kind = SymbolKind::EnumMember; + HI.Type = "enum Color"; + HI.Value = "1"; + }}, + // FIXME: We should use the Decl referenced, even if from an implicit + // instantiation. Then the scope would be Add<1, 2> and the value 3. + {R"cpp( + template<int a, int b> struct Add { + static constexpr int result = a + b; + }; + int bar = Add<1, 2>::[[resu^lt]]; + )cpp", + [](HoverInfo &HI) { + HI.Name = "result"; + HI.Definition = "static constexpr int result = a + b"; + HI.Kind = SymbolKind::Property; + HI.Type = "const int"; + HI.NamespaceScope = ""; + HI.LocalScope = "Add<a, b>::"; + }}, + {R"cpp( + const char *[[ba^r]] = "1234"; + )cpp", + [](HoverInfo &HI) { + HI.Name = "bar"; + HI.Definition = "const char *bar = \"1234\""; + HI.Kind = SymbolKind::Variable; + HI.Type = "const char *"; + HI.NamespaceScope = ""; + HI.Value = "&\"1234\"[0]"; + }}, + }; + for (const auto &Case : Cases) { + SCOPED_TRACE(Case.Code); + + Annotations T(Case.Code); + TestTU TU = TestTU::withCode(T.code()); + TU.ExtraArgs.push_back("-std=c++17"); + auto AST = TU.build(); + ASSERT_TRUE(AST.getDiagnostics().empty()); + + auto H = getHover(AST, T.point(), format::getLLVMStyle(), nullptr); + ASSERT_TRUE(H); + HoverInfo Expected; + Expected.SymRange = T.range(); + Case.ExpectedBuilder(Expected); + + EXPECT_EQ(H->NamespaceScope, Expected.NamespaceScope); + EXPECT_EQ(H->LocalScope, Expected.LocalScope); + EXPECT_EQ(H->Name, Expected.Name); + EXPECT_EQ(H->Kind, Expected.Kind); + EXPECT_EQ(H->Documentation, Expected.Documentation); + EXPECT_EQ(H->Definition, Expected.Definition); + EXPECT_EQ(H->Type, Expected.Type); + EXPECT_EQ(H->ReturnType, Expected.ReturnType); + EXPECT_EQ(H->Parameters, Expected.Parameters); + EXPECT_EQ(H->TemplateParameters, Expected.TemplateParameters); + EXPECT_EQ(H->SymRange, Expected.SymRange); + EXPECT_EQ(H->Value, Expected.Value); + } +} + +TEST(Hover, All) { + struct OneTest { + StringRef Input; + StringRef ExpectedHover; + }; + + OneTest Tests[] = { + { + R"cpp(// No hover + ^int main() { + } + )cpp", + "", + }, + { + R"cpp(// Local variable + int main() { + int bonjour; + ^bonjour = 2; + int test1 = bonjour; + } + )cpp", + "text[Declared in]code[main]\n" + "codeblock(cpp) [\n" + "int bonjour\n" + "]", + }, + { + R"cpp(// Local variable in method + struct s { + void method() { + int bonjour; + ^bonjour = 2; + } + }; + )cpp", + "text[Declared in]code[s::method]\n" + "codeblock(cpp) [\n" + "int bonjour\n" + "]", + }, + { + R"cpp(// Struct + namespace ns1 { + struct MyClass {}; + } // namespace ns1 + int main() { + ns1::My^Class* Params; + } + )cpp", + "text[Declared in]code[ns1]\n" + "codeblock(cpp) [\n" + "struct MyClass {}\n" + "]", + }, + { + R"cpp(// Class + namespace ns1 { + class MyClass {}; + } // namespace ns1 + int main() { + ns1::My^Class* Params; + } + )cpp", + "text[Declared in]code[ns1]\n" + "codeblock(cpp) [\n" + "class MyClass {}\n" + "]", + }, + { + R"cpp(// Union + namespace ns1 { + union MyUnion { int x; int y; }; + } // namespace ns1 + int main() { + ns1::My^Union Params; + } + )cpp", + "text[Declared in]code[ns1]\n" + "codeblock(cpp) [\n" + "union MyUnion {}\n" + "]", + }, + { + R"cpp(// Function definition via pointer + int foo(int) {} + int main() { + auto *X = &^foo; + } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "int foo(int)\n" + "]\n" + "text[Function definition via pointer]", + }, + { + R"cpp(// Function declaration via call + int foo(int); + int main() { + return ^foo(42); + } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "int foo(int)\n" + "]\n" + "text[Function declaration via call]", + }, + { + R"cpp(// Field + struct Foo { int x; }; + int main() { + Foo bar; + bar.^x; + } + )cpp", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "int x\n" + "]", + }, + { + R"cpp(// Field with initialization + struct Foo { int x = 5; }; + int main() { + Foo bar; + bar.^x; + } + )cpp", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "int x = 5\n" + "]", + }, + { + R"cpp(// Static field + struct Foo { static int x; }; + int main() { + Foo::^x; + } + )cpp", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "static int x\n" + "]", + }, + { + R"cpp(// Field, member initializer + struct Foo { + int x; + Foo() : ^x(0) {} + }; + )cpp", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "int x\n" + "]", + }, + { + R"cpp(// Field, GNU old-style field designator + struct Foo { int x; }; + int main() { + Foo bar = { ^x : 1 }; + } + )cpp", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "int x\n" + "]", + }, + { + R"cpp(// Field, field designator + struct Foo { int x; }; + int main() { + Foo bar = { .^x = 2 }; + } + )cpp", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "int x\n" + "]", + }, + { + R"cpp(// Method call + struct Foo { int x(); }; + int main() { + Foo bar; + bar.^x(); + } + )cpp", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "int x()\n" + "]", + }, + { + R"cpp(// Static method call + struct Foo { static int x(); }; + int main() { + Foo::^x(); + } + )cpp", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "static int x()\n" + "]", + }, + { + R"cpp(// Typedef + typedef int Foo; + int main() { + ^Foo bar; + } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "typedef int Foo\n" + "]\n" + "text[Typedef]", + }, + { + R"cpp(// Typedef with embedded definition + typedef struct Bar {} Foo; + int main() { + ^Foo bar; + } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "typedef struct Bar Foo\n" + "]\n" + "text[Typedef with embedded definition]", + }, + { + R"cpp(// Namespace + namespace ns { + struct Foo { static void bar(); } + } // namespace ns + int main() { ^ns::Foo::bar(); } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "namespace ns {}\n" + "]", + }, + { + R"cpp(// Anonymous namespace + namespace ns { + namespace { + int foo; + } // anonymous namespace + } // namespace ns + int main() { ns::f^oo++; } + )cpp", + "text[Declared in]code[ns::(anonymous)]\n" + "codeblock(cpp) [\n" + "int foo\n" + "]", + }, + { + R"cpp(// Macro + #define MACRO 0 + #define MACRO 1 + int main() { return ^MACRO; } + #define MACRO 2 + #undef macro + )cpp", + "codeblock(cpp) [\n" + "#define MACRO 1\n" + "]", + }, + { + R"cpp(// Macro + #define MACRO 0 + #define MACRO2 ^MACRO + )cpp", + "codeblock(cpp) [\n" + "#define MACRO 0\n" + "]", + }, + { + R"cpp(// Macro + #define MACRO {\ + return 0;\ + } + int main() ^MACRO + )cpp", + R"cpp(codeblock(cpp) [ +#define MACRO \ + { return 0; } +])cpp", + }, + { + R"cpp(// Forward class declaration + class Foo; + class Foo {}; + F^oo* foo(); + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "class Foo {}\n" + "]\n" + "text[Forward class declaration]", + }, + { + R"cpp(// Function declaration + void foo(); + void g() { f^oo(); } + void foo() {} + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "void foo()\n" + "]\n" + "text[Function declaration]", + }, + { + R"cpp(// Enum declaration + enum Hello { + ONE, TWO, THREE, + }; + void foo() { + Hel^lo hello = ONE; + } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "enum Hello {}\n" + "]\n" + "text[Enum declaration]", + }, + { + R"cpp(// Enumerator + enum Hello { + ONE, TWO, THREE, + }; + void foo() { + Hello hello = O^NE; + } + )cpp", + "text[Declared in]code[Hello]\n" + "codeblock(cpp) [\n" + "ONE\n" + "]", + }, + { + R"cpp(// Enumerator in anonymous enum + enum { + ONE, TWO, THREE, + }; + void foo() { + int hello = O^NE; + } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "ONE\n" + "]", + }, + { + R"cpp(// Global variable + static int hey = 10; + void foo() { + he^y++; + } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "static int hey = 10\n" + "]\n" + "text[Global variable]", + }, + { + R"cpp(// Global variable in namespace + namespace ns1 { + static int hey = 10; + } + void foo() { + ns1::he^y++; + } + )cpp", + "text[Declared in]code[ns1]\n" + "codeblock(cpp) [\n" + "static int hey = 10\n" + "]", + }, + { + R"cpp(// Field in anonymous struct + static struct { + int hello; + } s; + void foo() { + s.he^llo++; + } + )cpp", + "text[Declared in]code[(anonymous struct)]\n" + "codeblock(cpp) [\n" + "int hello\n" + "]", + }, + { + R"cpp(// Templated function + template <typename T> + T foo() { + return 17; + } + void g() { auto x = f^oo<int>(); } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "template <typename T> T foo()\n" + "]\n" + "text[Templated function]", + }, + { + R"cpp(// Anonymous union + struct outer { + union { + int abc, def; + } v; + }; + void g() { struct outer o; o.v.d^ef++; } + )cpp", + "text[Declared in]code[outer::(anonymous union)]\n" + "codeblock(cpp) [\n" + "int def\n" + "]", + }, + { + R"cpp(// documentation from index + int nextSymbolIsAForwardDeclFromIndexWithNoLocalDocs; + void indexSymbol(); + void g() { ind^exSymbol(); } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "void indexSymbol()\n" + "]\n" + "text[comment from index]", + }, + { + R"cpp(// Nothing + void foo() { + ^ + } + )cpp", + "", + }, + { + R"cpp(// Simple initialization with auto + void foo() { + ^auto i = 1; + } + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// Simple initialization with const auto + void foo() { + const ^auto i = 1; + } + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// Simple initialization with const auto& + void foo() { + const ^auto& i = 1; + } + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// Simple initialization with auto& + void foo() { + ^auto& i = 1; + } + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// Simple initialization with auto* + void foo() { + int a = 1; + ^auto* i = &a; + } + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// Auto with initializer list. + namespace std + { + template<class _E> + class initializer_list {}; + } + void foo() { + ^auto i = {1,2}; + } + )cpp", + "codeblock(cpp) [\n" + "class std::initializer_list<int>\n" + "]", + }, + { + R"cpp(// User defined conversion to auto + struct Bar { + operator ^auto() const { return 10; } + }; + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// Simple initialization with decltype(auto) + void foo() { + ^decltype(auto) i = 1; + } + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// Simple initialization with const decltype(auto) + void foo() { + const int j = 0; + ^decltype(auto) i = j; + } + )cpp", + "codeblock(cpp) [\n" + "const int\n" + "]", + }, + { + R"cpp(// Simple initialization with const& decltype(auto) + void foo() { + int k = 0; + const int& j = k; + ^decltype(auto) i = j; + } + )cpp", + "codeblock(cpp) [\n" + "const int &\n" + "]", + }, + { + R"cpp(// Simple initialization with & decltype(auto) + void foo() { + int k = 0; + int& j = k; + ^decltype(auto) i = j; + } + )cpp", + "codeblock(cpp) [\n" + "int &\n" + "]", + }, + { + R"cpp(// decltype with initializer list: nothing + namespace std + { + template<class _E> + class initializer_list {}; + } + void foo() { + ^decltype(auto) i = {1,2}; + } + )cpp", + "", + }, + { + R"cpp(// simple trailing return type + ^auto main() -> int { + return 0; + } + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// auto function return with trailing type + struct Bar {}; + ^auto test() -> decltype(Bar()) { + return Bar(); + } + )cpp", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", + }, + { + R"cpp(// trailing return type + struct Bar {}; + auto test() -> ^decltype(Bar()) { + return Bar(); + } + )cpp", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", + }, + { + R"cpp(// auto in function return + struct Bar {}; + ^auto test() { + return Bar(); + } + )cpp", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", + }, + { + R"cpp(// auto& in function return + struct Bar {}; + ^auto& test() { + return Bar(); + } + )cpp", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", + }, + { + R"cpp(// auto* in function return + struct Bar {}; + ^auto* test() { + Bar* bar; + return bar; + } + )cpp", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", + }, + { + R"cpp(// const auto& in function return + struct Bar {}; + const ^auto& test() { + return Bar(); + } + )cpp", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", + }, + { + R"cpp(// decltype(auto) in function return + struct Bar {}; + ^decltype(auto) test() { + return Bar(); + } + )cpp", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", + }, + { + R"cpp(// decltype(auto) reference in function return + struct Bar {}; + ^decltype(auto) test() { + int a; + return (a); + } + )cpp", + "codeblock(cpp) [\n" + "int &\n" + "]", + }, + { + R"cpp(// decltype lvalue reference + void foo() { + int I = 0; + ^decltype(I) J = I; + } + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// decltype lvalue reference + void foo() { + int I= 0; + int &K = I; + ^decltype(K) J = I; + } + )cpp", + "codeblock(cpp) [\n" + "int &\n" + "]", + }, + { + R"cpp(// decltype lvalue reference parenthesis + void foo() { + int I = 0; + ^decltype((I)) J = I; + } + )cpp", + "codeblock(cpp) [\n" + "int &\n" + "]", + }, + { + R"cpp(// decltype rvalue reference + void foo() { + int I = 0; + ^decltype(static_cast<int&&>(I)) J = static_cast<int&&>(I); + } + )cpp", + "codeblock(cpp) [\n" + "int &&\n" + "]", + }, + { + R"cpp(// decltype rvalue reference function call + int && bar(); + void foo() { + int I = 0; + ^decltype(bar()) J = bar(); + } + )cpp", + "codeblock(cpp) [\n" + "int &&\n" + "]", + }, + { + R"cpp(// decltype of function with trailing return type. + struct Bar {}; + auto test() -> decltype(Bar()) { + return Bar(); + } + void foo() { + ^decltype(test()) i = test(); + } + )cpp", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", + }, + { + R"cpp(// decltype of var with decltype. + void foo() { + int I = 0; + decltype(I) J = I; + ^decltype(J) K = J; + } + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// structured binding. Not supported yet + struct Bar {}; + void foo() { + Bar a[2]; + ^auto [x,y] = a; + } + )cpp", + "", + }, + { + R"cpp(// Template auto parameter. Nothing (Not useful). + template<^auto T> + void func() { + } + void foo() { + func<1>(); + } + )cpp", + "", + }, + { + R"cpp(// More compilcated structured types. + int bar(); + ^auto (*foo)() = bar; + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// Should not crash when evaluating the initializer. + struct Test {}; + void test() { Test && te^st = {}; } + )cpp", + "text[Declared in]code[test]\n" + "codeblock(cpp) [\n" + "struct Test &&test = {}\n" + "]", + }, + }; + + // Create a tiny index, so tests above can verify documentation is fetched. + Symbol IndexSym = func("indexSymbol"); + IndexSym.Documentation = "comment from index"; + SymbolSlab::Builder Symbols; + Symbols.insert(IndexSym); + auto Index = + MemIndex::build(std::move(Symbols).build(), RefSlab(), RelationSlab()); + + for (const OneTest &Test : Tests) { + Annotations T(Test.Input); + TestTU TU = TestTU::withCode(T.code()); + TU.ExtraArgs.push_back("-std=c++17"); + auto AST = TU.build(); + if (auto H = + getHover(AST, T.point(), format::getLLVMStyle(), Index.get())) { + EXPECT_NE("", Test.ExpectedHover) << Test.Input; + EXPECT_EQ(H->present().renderForTests(), Test.ExpectedHover.str()) + << Test.Input; + } else + EXPECT_EQ("", Test.ExpectedHover.str()) << Test.Input; + } +} + +} // namespace +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp index 483f216ca66..b6115065d1b 100644 --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -603,1321 +603,6 @@ int [[bar_not_preamble]]; HeaderNotInPreambleAnnotations.range()))); } -TEST(Hover, Structured) { - struct { - const char *const Code; - const std::function<void(HoverInfo &)> 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 <typename T, class... Ts> class Foo { public: Foo(int); }; - Foo<int, char, bool> [[fo^o]] = Foo<int, char, bool>(5); - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = ""; - HI.Name = "foo"; - HI.Kind = SymbolKind::Variable; - HI.Definition = "Foo<int, char, bool> foo = Foo<int, char, bool>(5)"; - HI.Type = "Foo<int, char, bool>"; - }}, - // Implicit template instantiation - {R"cpp( - template <typename T> class vector{}; - [[vec^tor]]<int> foo; - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = ""; - HI.Name = "vector"; - HI.Kind = SymbolKind::Class; - HI.Definition = "template <typename T> class vector {}"; - HI.TemplateParameters = { - {std::string("typename"), std::string("T"), llvm::None}, - }; - }}, - // Class template - {R"cpp( - template <template<typename, bool...> class C, - typename = char, - int = 0, - bool Q = false, - class... Ts> class Foo {}; - template <template<typename, bool...> class T> - [[F^oo]]<T> foo; - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = ""; - HI.Name = "Foo"; - HI.Kind = SymbolKind::Class; - HI.Definition = - R"cpp(template <template <typename, bool...> class C, typename = char, int = 0, - bool Q = false, class... Ts> -class Foo {})cpp"; - HI.TemplateParameters = { - {std::string("template <typename, bool...> class"), - std::string("C"), llvm::None}, - {std::string("typename"), llvm::None, std::string("char")}, - {std::string("int"), llvm::None, std::string("0")}, - {std::string("bool"), std::string("Q"), std::string("false")}, - {std::string("class..."), std::string("Ts"), llvm::None}, - }; - }}, - // Function template - {R"cpp( - template <template<typename, bool...> class C, - typename = char, - int = 0, - bool Q = false, - class... Ts> void foo(); - template<typename, bool...> class Foo; - - void bar() { - [[fo^o]]<Foo>(); - } - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = ""; - HI.Name = "foo"; - HI.Kind = SymbolKind::Function; - HI.Definition = - R"cpp(template <template <typename, bool...> class C, typename = char, int = 0, - bool Q = false, class... Ts> -void foo())cpp"; - HI.ReturnType = "void"; - HI.Type = "void ()"; - HI.Parameters.emplace(); - HI.TemplateParameters = { - {std::string("template <typename, bool...> class"), - std::string("C"), llvm::None}, - {std::string("typename"), llvm::None, std::string("char")}, - {std::string("int"), llvm::None, std::string("0")}, - {std::string("bool"), std::string("Q"), std::string("false")}, - {std::string("class..."), std::string("Ts"), llvm::None}, - }; - }}, - // Function decl - {R"cpp( - template<typename, bool...> class Foo {}; - Foo<bool, true, false> foo(int, bool T = false); - - void bar() { - [[fo^o]](3); - } - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = ""; - HI.Name = "foo"; - HI.Kind = SymbolKind::Function; - HI.Definition = "Foo<bool, true, false> foo(int, bool T = false)"; - HI.ReturnType = "Foo<bool, true, false>"; - HI.Type = "Foo<bool, true, false> (int, bool)"; - HI.Parameters = { - {std::string("int"), llvm::None, llvm::None}, - {std::string("bool"), std::string("T"), std::string("false")}, - }; - }}, - // Pointers to lambdas - {R"cpp( - void foo() { - auto lamb = [](int T, bool B) -> bool { return T && B; }; - auto *b = &lamb; - auto *[[^c]] = &b; - } - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = ""; - HI.LocalScope = "foo::"; - HI.Name = "c"; - HI.Kind = SymbolKind::Variable; - HI.Definition = "auto *c = &b"; - HI.Type = "class (lambda) **"; - HI.ReturnType = "bool"; - HI.Parameters = { - {std::string("int"), std::string("T"), llvm::None}, - {std::string("bool"), std::string("B"), llvm::None}, - }; - return HI; - }}, - // Lambda parameter with decltype reference - {R"cpp( - auto lamb = [](int T, bool B) -> bool { return T && B; }; - void foo(decltype(lamb)& bar) { - [[ba^r]](0, false); - } - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = ""; - HI.LocalScope = "foo::"; - HI.Name = "bar"; - HI.Kind = SymbolKind::Variable; - HI.Definition = "decltype(lamb) &bar"; - HI.Type = "decltype(lamb) &"; - HI.ReturnType = "bool"; - HI.Parameters = { - {std::string("int"), std::string("T"), llvm::None}, - {std::string("bool"), std::string("B"), llvm::None}, - }; - return HI; - }}, - // Lambda parameter with decltype - {R"cpp( - auto lamb = [](int T, bool B) -> bool { return T && B; }; - void foo(decltype(lamb) bar) { - [[ba^r]](0, false); - } - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = ""; - HI.LocalScope = "foo::"; - HI.Name = "bar"; - HI.Kind = SymbolKind::Variable; - HI.Definition = "decltype(lamb) bar"; - HI.Type = "class (lambda)"; - HI.ReturnType = "bool"; - HI.Parameters = { - {std::string("int"), std::string("T"), llvm::None}, - {std::string("bool"), std::string("B"), llvm::None}, - }; - return HI; - }}, - // Lambda variable - {R"cpp( - void foo() { - int bar = 5; - auto lamb = [&bar](int T, bool B) -> bool { return T && B && bar; }; - bool res = [[lam^b]](bar, false); - } - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = ""; - HI.LocalScope = "foo::"; - HI.Name = "lamb"; - HI.Kind = SymbolKind::Variable; - HI.Definition = "auto lamb = [&bar](int T, bool B) -> bool {}"; - HI.Type = "class (lambda)"; - HI.ReturnType = "bool"; - HI.Parameters = { - {std::string("int"), std::string("T"), llvm::None}, - {std::string("bool"), std::string("B"), llvm::None}, - }; - return HI; - }}, - // Local variable in lambda - {R"cpp( - void foo() { - auto lamb = []{int [[te^st]];}; - } - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = ""; - HI.LocalScope = "foo::(anonymous class)::operator()::"; - HI.Name = "test"; - HI.Kind = SymbolKind::Variable; - HI.Definition = "int test"; - HI.Type = "int"; - }}, - // Partially-specialized class template. (formerly type-parameter-0-0) - {R"cpp( - template <typename T> class X; - template <typename T> class [[^X]]<T*> {}; - )cpp", - [](HoverInfo &HI) { - HI.Name = "X<T *>"; - HI.NamespaceScope = ""; - HI.Kind = SymbolKind::Class; - HI.Definition = "template <typename T> class X<T *> {}"; - }}, - // Constructor of partially-specialized class template - {R"cpp( - template<typename, typename=void> struct X; - template<typename T> struct X<T*>{ [[^X]](); }; - )cpp", - [](HoverInfo &HI) { - HI.NamespaceScope = ""; - HI.Name = "X"; - HI.LocalScope = "X<T *>::"; // FIXME: X<T *, void>:: - HI.Kind = SymbolKind::Constructor; - HI.ReturnType = "X<T *>"; - HI.Definition = "X()"; - HI.Parameters.emplace(); - }}, - {"class X { [[^~]]X(); };", // FIXME: Should be [[~X]]() - [](HoverInfo &HI) { - HI.NamespaceScope = ""; - HI.Name = "~X"; - HI.LocalScope = "X::"; - HI.Kind = SymbolKind::Constructor; - HI.ReturnType = "void"; - HI.Definition = "~X()"; - HI.Parameters.emplace(); - }}, - - // auto on lambda - {R"cpp( - void foo() { - [[au^to]] lamb = []{}; - } - )cpp", - [](HoverInfo &HI) { - HI.Name = "class (lambda)"; - HI.Kind = SymbolKind::Variable; - }}, - // auto on template instantiation - {R"cpp( - template<typename T> class Foo{}; - void foo() { - [[au^to]] x = Foo<int>(); - } - )cpp", - [](HoverInfo &HI) { - HI.Name = "class Foo<int>"; - HI.Kind = SymbolKind::Variable; - }}, - // auto on specialized template - {R"cpp( - template<typename T> class Foo{}; - template<> class Foo<int>{}; - void foo() { - [[au^to]] x = Foo<int>(); - } - )cpp", - [](HoverInfo &HI) { - HI.Name = "class Foo<int>"; - HI.Kind = SymbolKind::Variable; - }}, - - // macro - {R"cpp( - // Best MACRO ever. - #define MACRO(x,y,z) void foo(x, y, z); - [[MAC^RO]](int, double d, bool z = false); - )cpp", - [](HoverInfo &HI) { - HI.Name = "MACRO", HI.Kind = SymbolKind::String, - HI.Definition = "#define MACRO(x, y, z) void foo(x, y, z);"; - }}, - - // constexprs - {R"cpp( - constexpr int add(int a, int b) { return a + b; } - int [[b^ar]] = add(1, 2); - )cpp", - [](HoverInfo &HI) { - HI.Name = "bar"; - HI.Definition = "int bar = add(1, 2)"; - HI.Kind = SymbolKind::Variable; - HI.Type = "int"; - HI.NamespaceScope = ""; - HI.Value = "3"; - }}, - {R"cpp( - int [[b^ar]] = sizeof(char); - )cpp", - [](HoverInfo &HI) { - HI.Name = "bar"; - HI.Definition = "int bar = sizeof(char)"; - HI.Kind = SymbolKind::Variable; - HI.Type = "int"; - HI.NamespaceScope = ""; - HI.Value = "1"; - }}, - {R"cpp( - template<int a, int b> struct Add { - static constexpr int result = a + b; - }; - int [[ba^r]] = Add<1, 2>::result; - )cpp", - [](HoverInfo &HI) { - HI.Name = "bar"; - HI.Definition = "int bar = Add<1, 2>::result"; - HI.Kind = SymbolKind::Variable; - HI.Type = "int"; - HI.NamespaceScope = ""; - HI.Value = "3"; - }}, - {R"cpp( - enum Color { RED, GREEN, }; - Color x = [[GR^EEN]]; - )cpp", - [](HoverInfo &HI) { - HI.Name = "GREEN"; - HI.NamespaceScope = ""; - HI.LocalScope = "Color::"; - HI.Definition = "GREEN"; - HI.Kind = SymbolKind::EnumMember; - HI.Type = "enum Color"; - HI.Value = "1"; - }}, - // FIXME: We should use the Decl referenced, even if from an implicit - // instantiation. Then the scope would be Add<1, 2> and the value 3. - {R"cpp( - template<int a, int b> struct Add { - static constexpr int result = a + b; - }; - int bar = Add<1, 2>::[[resu^lt]]; - )cpp", - [](HoverInfo &HI) { - HI.Name = "result"; - HI.Definition = "static constexpr int result = a + b"; - HI.Kind = SymbolKind::Property; - HI.Type = "const int"; - HI.NamespaceScope = ""; - HI.LocalScope = "Add<a, b>::"; - }}, - {R"cpp( - const char *[[ba^r]] = "1234"; - )cpp", - [](HoverInfo &HI) { - HI.Name = "bar"; - HI.Definition = "const char *bar = \"1234\""; - HI.Kind = SymbolKind::Variable; - HI.Type = "const char *"; - HI.NamespaceScope = ""; - HI.Value = "&\"1234\"[0]"; - }}, - }; - for (const auto &Case : Cases) { - SCOPED_TRACE(Case.Code); - - Annotations T(Case.Code); - TestTU TU = TestTU::withCode(T.code()); - TU.ExtraArgs.push_back("-std=c++17"); - auto AST = TU.build(); - ASSERT_TRUE(AST.getDiagnostics().empty()); - - auto H = getHover(AST, T.point(), format::getLLVMStyle(), nullptr); - ASSERT_TRUE(H); - HoverInfo Expected; - Expected.SymRange = T.range(); - Case.ExpectedBuilder(Expected); - - EXPECT_EQ(H->NamespaceScope, Expected.NamespaceScope); - EXPECT_EQ(H->LocalScope, Expected.LocalScope); - EXPECT_EQ(H->Name, Expected.Name); - EXPECT_EQ(H->Kind, Expected.Kind); - EXPECT_EQ(H->Documentation, Expected.Documentation); - EXPECT_EQ(H->Definition, Expected.Definition); - EXPECT_EQ(H->Type, Expected.Type); - EXPECT_EQ(H->ReturnType, Expected.ReturnType); - EXPECT_EQ(H->Parameters, Expected.Parameters); - EXPECT_EQ(H->TemplateParameters, Expected.TemplateParameters); - EXPECT_EQ(H->SymRange, Expected.SymRange); - EXPECT_EQ(H->Value, Expected.Value); - } -} // namespace clang - -TEST(Hover, All) { - struct OneTest { - StringRef Input; - StringRef ExpectedHover; - }; - - OneTest Tests[] = { - { - R"cpp(// No hover - ^int main() { - } - )cpp", - "", - }, - { - R"cpp(// Local variable - int main() { - int bonjour; - ^bonjour = 2; - int test1 = bonjour; - } - )cpp", - "text[Declared in]code[main]\n" - "codeblock(cpp) [\n" - "int bonjour\n" - "]", - }, - { - R"cpp(// Local variable in method - struct s { - void method() { - int bonjour; - ^bonjour = 2; - } - }; - )cpp", - "text[Declared in]code[s::method]\n" - "codeblock(cpp) [\n" - "int bonjour\n" - "]", - }, - { - R"cpp(// Struct - namespace ns1 { - struct MyClass {}; - } // namespace ns1 - int main() { - ns1::My^Class* Params; - } - )cpp", - "text[Declared in]code[ns1]\n" - "codeblock(cpp) [\n" - "struct MyClass {}\n" - "]", - }, - { - R"cpp(// Class - namespace ns1 { - class MyClass {}; - } // namespace ns1 - int main() { - ns1::My^Class* Params; - } - )cpp", - "text[Declared in]code[ns1]\n" - "codeblock(cpp) [\n" - "class MyClass {}\n" - "]", - }, - { - R"cpp(// Union - namespace ns1 { - union MyUnion { int x; int y; }; - } // namespace ns1 - int main() { - ns1::My^Union Params; - } - )cpp", - "text[Declared in]code[ns1]\n" - "codeblock(cpp) [\n" - "union MyUnion {}\n" - "]", - }, - { - R"cpp(// Function definition via pointer - int foo(int) {} - int main() { - auto *X = &^foo; - } - )cpp", - "text[Declared in]code[global namespace]\n" - "codeblock(cpp) [\n" - "int foo(int)\n" - "]\n" - "text[Function definition via pointer]", - }, - { - R"cpp(// Function declaration via call - int foo(int); - int main() { - return ^foo(42); - } - )cpp", - "text[Declared in]code[global namespace]\n" - "codeblock(cpp) [\n" - "int foo(int)\n" - "]\n" - "text[Function declaration via call]", - }, - { - R"cpp(// Field - struct Foo { int x; }; - int main() { - Foo bar; - bar.^x; - } - )cpp", - "text[Declared in]code[Foo]\n" - "codeblock(cpp) [\n" - "int x\n" - "]", - }, - { - R"cpp(// Field with initialization - struct Foo { int x = 5; }; - int main() { - Foo bar; - bar.^x; - } - )cpp", - "text[Declared in]code[Foo]\n" - "codeblock(cpp) [\n" - "int x = 5\n" - "]", - }, - { - R"cpp(// Static field - struct Foo { static int x; }; - int main() { - Foo::^x; - } - )cpp", - "text[Declared in]code[Foo]\n" - "codeblock(cpp) [\n" - "static int x\n" - "]", - }, - { - R"cpp(// Field, member initializer - struct Foo { - int x; - Foo() : ^x(0) {} - }; - )cpp", - "text[Declared in]code[Foo]\n" - "codeblock(cpp) [\n" - "int x\n" - "]", - }, - { - R"cpp(// Field, GNU old-style field designator - struct Foo { int x; }; - int main() { - Foo bar = { ^x : 1 }; - } - )cpp", - "text[Declared in]code[Foo]\n" - "codeblock(cpp) [\n" - "int x\n" - "]", - }, - { - R"cpp(// Field, field designator - struct Foo { int x; }; - int main() { - Foo bar = { .^x = 2 }; - } - )cpp", - "text[Declared in]code[Foo]\n" - "codeblock(cpp) [\n" - "int x\n" - "]", - }, - { - R"cpp(// Method call - struct Foo { int x(); }; - int main() { - Foo bar; - bar.^x(); - } - )cpp", - "text[Declared in]code[Foo]\n" - "codeblock(cpp) [\n" - "int x()\n" - "]", - }, - { - R"cpp(// Static method call - struct Foo { static int x(); }; - int main() { - Foo::^x(); - } - )cpp", - "text[Declared in]code[Foo]\n" - "codeblock(cpp) [\n" - "static int x()\n" - "]", - }, - { - R"cpp(// Typedef - typedef int Foo; - int main() { - ^Foo bar; - } - )cpp", - "text[Declared in]code[global namespace]\n" - "codeblock(cpp) [\n" - "typedef int Foo\n" - "]\n" - "text[Typedef]", - }, - { - R"cpp(// Typedef with embedded definition - typedef struct Bar {} Foo; - int main() { - ^Foo bar; - } - )cpp", - "text[Declared in]code[global namespace]\n" - "codeblock(cpp) [\n" - "typedef struct Bar Foo\n" - "]\n" - "text[Typedef with embedded definition]", - }, - { - R"cpp(// Namespace - namespace ns { - struct Foo { static void bar(); } - } // namespace ns - int main() { ^ns::Foo::bar(); } - )cpp", - "text[Declared in]code[global namespace]\n" - "codeblock(cpp) [\n" - "namespace ns {}\n" - "]", - }, - { - R"cpp(// Anonymous namespace - namespace ns { - namespace { - int foo; - } // anonymous namespace - } // namespace ns - int main() { ns::f^oo++; } - )cpp", - "text[Declared in]code[ns::(anonymous)]\n" - "codeblock(cpp) [\n" - "int foo\n" - "]", - }, - { - R"cpp(// Macro - #define MACRO 0 - #define MACRO 1 - int main() { return ^MACRO; } - #define MACRO 2 - #undef macro - )cpp", - "codeblock(cpp) [\n" - "#define MACRO 1\n" - "]", - }, - { - R"cpp(// Macro - #define MACRO 0 - #define MACRO2 ^MACRO - )cpp", - "codeblock(cpp) [\n" - "#define MACRO 0\n" - "]", - }, - { - R"cpp(// Macro - #define MACRO {\ - return 0;\ - } - int main() ^MACRO - )cpp", - R"cpp(codeblock(cpp) [ -#define MACRO \ - { return 0; } -])cpp", - }, - { - R"cpp(// Forward class declaration - class Foo; - class Foo {}; - F^oo* foo(); - )cpp", - "text[Declared in]code[global namespace]\n" - "codeblock(cpp) [\n" - "class Foo {}\n" - "]\n" - "text[Forward class declaration]", - }, - { - R"cpp(// Function declaration - void foo(); - void g() { f^oo(); } - void foo() {} - )cpp", - "text[Declared in]code[global namespace]\n" - "codeblock(cpp) [\n" - "void foo()\n" - "]\n" - "text[Function declaration]", - }, - { - R"cpp(// Enum declaration - enum Hello { - ONE, TWO, THREE, - }; - void foo() { - Hel^lo hello = ONE; - } - )cpp", - "text[Declared in]code[global namespace]\n" - "codeblock(cpp) [\n" - "enum Hello {}\n" - "]\n" - "text[Enum declaration]", - }, - { - R"cpp(// Enumerator - enum Hello { - ONE, TWO, THREE, - }; - void foo() { - Hello hello = O^NE; - } - )cpp", - "text[Declared in]code[Hello]\n" - "codeblock(cpp) [\n" - "ONE\n" - "]", - }, - { - R"cpp(// Enumerator in anonymous enum - enum { - ONE, TWO, THREE, - }; - void foo() { - int hello = O^NE; - } - )cpp", - "text[Declared in]code[global namespace]\n" - "codeblock(cpp) [\n" - "ONE\n" - "]", - }, - { - R"cpp(// Global variable - static int hey = 10; - void foo() { - he^y++; - } - )cpp", - "text[Declared in]code[global namespace]\n" - "codeblock(cpp) [\n" - "static int hey = 10\n" - "]\n" - "text[Global variable]", - }, - { - R"cpp(// Global variable in namespace - namespace ns1 { - static int hey = 10; - } - void foo() { - ns1::he^y++; - } - )cpp", - "text[Declared in]code[ns1]\n" - "codeblock(cpp) [\n" - "static int hey = 10\n" - "]", - }, - { - R"cpp(// Field in anonymous struct - static struct { - int hello; - } s; - void foo() { - s.he^llo++; - } - )cpp", - "text[Declared in]code[(anonymous struct)]\n" - "codeblock(cpp) [\n" - "int hello\n" - "]", - }, - { - R"cpp(// Templated function - template <typename T> - T foo() { - return 17; - } - void g() { auto x = f^oo<int>(); } - )cpp", - "text[Declared in]code[global namespace]\n" - "codeblock(cpp) [\n" - "template <typename T> T foo()\n" - "]\n" - "text[Templated function]", - }, - { - R"cpp(// Anonymous union - struct outer { - union { - int abc, def; - } v; - }; - void g() { struct outer o; o.v.d^ef++; } - )cpp", - "text[Declared in]code[outer::(anonymous union)]\n" - "codeblock(cpp) [\n" - "int def\n" - "]", - }, - { - R"cpp(// documentation from index - int nextSymbolIsAForwardDeclFromIndexWithNoLocalDocs; - void indexSymbol(); - void g() { ind^exSymbol(); } - )cpp", - "text[Declared in]code[global namespace]\n" - "codeblock(cpp) [\n" - "void indexSymbol()\n" - "]\n" - "text[comment from index]", - }, - { - R"cpp(// Nothing - void foo() { - ^ - } - )cpp", - "", - }, - { - R"cpp(// Simple initialization with auto - void foo() { - ^auto i = 1; - } - )cpp", - "codeblock(cpp) [\n" - "int\n" - "]", - }, - { - R"cpp(// Simple initialization with const auto - void foo() { - const ^auto i = 1; - } - )cpp", - "codeblock(cpp) [\n" - "int\n" - "]", - }, - { - R"cpp(// Simple initialization with const auto& - void foo() { - const ^auto& i = 1; - } - )cpp", - "codeblock(cpp) [\n" - "int\n" - "]", - }, - { - R"cpp(// Simple initialization with auto& - void foo() { - ^auto& i = 1; - } - )cpp", - "codeblock(cpp) [\n" - "int\n" - "]", - }, - { - R"cpp(// Simple initialization with auto* - void foo() { - int a = 1; - ^auto* i = &a; - } - )cpp", - "codeblock(cpp) [\n" - "int\n" - "]", - }, - { - R"cpp(// Auto with initializer list. - namespace std - { - template<class _E> - class initializer_list {}; - } - void foo() { - ^auto i = {1,2}; - } - )cpp", - "codeblock(cpp) [\n" - "class std::initializer_list<int>\n" - "]", - }, - { - R"cpp(// User defined conversion to auto - struct Bar { - operator ^auto() const { return 10; } - }; - )cpp", - "codeblock(cpp) [\n" - "int\n" - "]", - }, - { - R"cpp(// Simple initialization with decltype(auto) - void foo() { - ^decltype(auto) i = 1; - } - )cpp", - "codeblock(cpp) [\n" - "int\n" - "]", - }, - { - R"cpp(// Simple initialization with const decltype(auto) - void foo() { - const int j = 0; - ^decltype(auto) i = j; - } - )cpp", - "codeblock(cpp) [\n" - "const int\n" - "]", - }, - { - R"cpp(// Simple initialization with const& decltype(auto) - void foo() { - int k = 0; - const int& j = k; - ^decltype(auto) i = j; - } - )cpp", - "codeblock(cpp) [\n" - "const int &\n" - "]", - }, - { - R"cpp(// Simple initialization with & decltype(auto) - void foo() { - int k = 0; - int& j = k; - ^decltype(auto) i = j; - } - )cpp", - "codeblock(cpp) [\n" - "int &\n" - "]", - }, - { - R"cpp(// decltype with initializer list: nothing - namespace std - { - template<class _E> - class initializer_list {}; - } - void foo() { - ^decltype(auto) i = {1,2}; - } - )cpp", - "", - }, - { - R"cpp(// simple trailing return type - ^auto main() -> int { - return 0; - } - )cpp", - "codeblock(cpp) [\n" - "int\n" - "]", - }, - { - R"cpp(// auto function return with trailing type - struct Bar {}; - ^auto test() -> decltype(Bar()) { - return Bar(); - } - )cpp", - "codeblock(cpp) [\n" - "struct Bar\n" - "]", - }, - { - R"cpp(// trailing return type - struct Bar {}; - auto test() -> ^decltype(Bar()) { - return Bar(); - } - )cpp", - "codeblock(cpp) [\n" - "struct Bar\n" - "]", - }, - { - R"cpp(// auto in function return - struct Bar {}; - ^auto test() { - return Bar(); - } - )cpp", - "codeblock(cpp) [\n" - "struct Bar\n" - "]", - }, - { - R"cpp(// auto& in function return - struct Bar {}; - ^auto& test() { - return Bar(); - } - )cpp", - "codeblock(cpp) [\n" - "struct Bar\n" - "]", - }, - { - R"cpp(// auto* in function return - struct Bar {}; - ^auto* test() { - Bar* bar; - return bar; - } - )cpp", - "codeblock(cpp) [\n" - "struct Bar\n" - "]", - }, - { - R"cpp(// const auto& in function return - struct Bar {}; - const ^auto& test() { - return Bar(); - } - )cpp", - "codeblock(cpp) [\n" - "struct Bar\n" - "]", - }, - { - R"cpp(// decltype(auto) in function return - struct Bar {}; - ^decltype(auto) test() { - return Bar(); - } - )cpp", - "codeblock(cpp) [\n" - "struct Bar\n" - "]", - }, - { - R"cpp(// decltype(auto) reference in function return - struct Bar {}; - ^decltype(auto) test() { - int a; - return (a); - } - )cpp", - "codeblock(cpp) [\n" - "int &\n" - "]", - }, - { - R"cpp(// decltype lvalue reference - void foo() { - int I = 0; - ^decltype(I) J = I; - } - )cpp", - "codeblock(cpp) [\n" - "int\n" - "]", - }, - { - R"cpp(// decltype lvalue reference - void foo() { - int I= 0; - int &K = I; - ^decltype(K) J = I; - } - )cpp", - "codeblock(cpp) [\n" - "int &\n" - "]", - }, - { - R"cpp(// decltype lvalue reference parenthesis - void foo() { - int I = 0; - ^decltype((I)) J = I; - } - )cpp", - "codeblock(cpp) [\n" - "int &\n" - "]", - }, - { - R"cpp(// decltype rvalue reference - void foo() { - int I = 0; - ^decltype(static_cast<int&&>(I)) J = static_cast<int&&>(I); - } - )cpp", - "codeblock(cpp) [\n" - "int &&\n" - "]", - }, - { - R"cpp(// decltype rvalue reference function call - int && bar(); - void foo() { - int I = 0; - ^decltype(bar()) J = bar(); - } - )cpp", - "codeblock(cpp) [\n" - "int &&\n" - "]", - }, - { - R"cpp(// decltype of function with trailing return type. - struct Bar {}; - auto test() -> decltype(Bar()) { - return Bar(); - } - void foo() { - ^decltype(test()) i = test(); - } - )cpp", - "codeblock(cpp) [\n" - "struct Bar\n" - "]", - }, - { - R"cpp(// decltype of var with decltype. - void foo() { - int I = 0; - decltype(I) J = I; - ^decltype(J) K = J; - } - )cpp", - "codeblock(cpp) [\n" - "int\n" - "]", - }, - { - R"cpp(// structured binding. Not supported yet - struct Bar {}; - void foo() { - Bar a[2]; - ^auto [x,y] = a; - } - )cpp", - "", - }, - { - R"cpp(// Template auto parameter. Nothing (Not useful). - template<^auto T> - void func() { - } - void foo() { - func<1>(); - } - )cpp", - "", - }, - { - R"cpp(// More compilcated structured types. - int bar(); - ^auto (*foo)() = bar; - )cpp", - "codeblock(cpp) [\n" - "int\n" - "]", - }, - { - R"cpp(// Should not crash when evaluating the initializer. - struct Test {}; - void test() { Test && te^st = {}; } - )cpp", - "text[Declared in]code[test]\n" - "codeblock(cpp) [\n" - "struct Test &&test = {}\n" - "]", - }, - }; - - // Create a tiny index, so tests above can verify documentation is fetched. - Symbol IndexSym = func("indexSymbol"); - IndexSym.Documentation = "comment from index"; - SymbolSlab::Builder Symbols; - Symbols.insert(IndexSym); - auto Index = - MemIndex::build(std::move(Symbols).build(), RefSlab(), RelationSlab()); - - for (const OneTest &Test : Tests) { - Annotations T(Test.Input); - TestTU TU = TestTU::withCode(T.code()); - TU.ExtraArgs.push_back("-std=c++17"); - auto AST = TU.build(); - if (auto H = - getHover(AST, T.point(), format::getLLVMStyle(), Index.get())) { - EXPECT_NE("", Test.ExpectedHover) << Test.Input; - EXPECT_EQ(H->present().renderForTests(), Test.ExpectedHover.str()) - << Test.Input; - } else - EXPECT_EQ("", Test.ExpectedHover.str()) << Test.Input; - } -} - TEST(GoToInclude, All) { MockFSProvider FS; IgnoreDiagnostics DiagConsumer; @@ -2256,30 +941,6 @@ TEST(FindReferences, NoQueryForLocalSymbols) { } } -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, *Location); - EXPECT_EQ(DeducedType->getAsString(), T.DeducedType); - } - } -} - TEST(GetNonLocalDeclRefs, All) { struct Case { llvm::StringRef AnnotatedCode; |