summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang-tools-extra/clangd/AST.cpp111
-rw-r--r--clang-tools-extra/clangd/AST.h5
-rw-r--r--clang-tools-extra/clangd/CMakeLists.txt1
-rw-r--r--clang-tools-extra/clangd/ClangdServer.h1
-rw-r--r--clang-tools-extra/clangd/Hover.cpp443
-rw-r--r--clang-tools-extra/clangd/Hover.h93
-rw-r--r--clang-tools-extra/clangd/XRefs.cpp537
-rw-r--r--clang-tools-extra/clangd/XRefs.h81
-rw-r--r--clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp5
-rw-r--r--clang-tools-extra/clangd/unittests/ASTTests.cpp30
-rw-r--r--clang-tools-extra/clangd/unittests/CMakeLists.txt1
-rw-r--r--clang-tools-extra/clangd/unittests/HoverTests.cpp1339
-rw-r--r--clang-tools-extra/clangd/unittests/XRefsTests.cpp1339
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;
OpenPOWER on IntegriCloud