mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 22:16:05 +00:00
[clangd] Support ConceptReference in generic AST wrangling code
Now we can store it in DynTypedNode, we can target these nodes (SelectionTree) and resolve them (FindTarget). This makes Hover, go-to-def etc work in all(?) cases. Also support it in DumpAST. Differential Revision: https://reviews.llvm.org/D159299
This commit is contained in:
parent
fae3f9ec4f
commit
a8c9b9f140
@ -205,6 +205,11 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> {
|
||||
// To avoid special cases in the API/UI, use public/private as the kind.
|
||||
return getAccessSpelling(CBS.getAccessSpecifier()).str();
|
||||
}
|
||||
std::string getKind(const ConceptReference *CR) {
|
||||
// Again there are no variants here.
|
||||
// Kind is "Concept", role is "reference"
|
||||
return "Concept";
|
||||
}
|
||||
|
||||
// Detail is the single most important fact about the node.
|
||||
// Often this is the name, sometimes a "kind" enum like operators or casts.
|
||||
@ -305,6 +310,9 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> {
|
||||
std::string getDetail(const CXXBaseSpecifier &CBS) {
|
||||
return CBS.isVirtual() ? "virtual" : "";
|
||||
}
|
||||
std::string getDetail(const ConceptReference *CR) {
|
||||
return CR->getNamedConcept()->getNameAsString();
|
||||
}
|
||||
|
||||
/// Arcana is produced by TextNodeDumper, for the types it supports.
|
||||
|
||||
@ -365,6 +373,10 @@ public:
|
||||
bool TraverseAttr(Attr *A) {
|
||||
return !A || traverseNode("attribute", A, [&] { Base::TraverseAttr(A); });
|
||||
}
|
||||
bool TraverseConceptReference(ConceptReference *C) {
|
||||
return !C || traverseNode("reference", C,
|
||||
[&] { Base::TraverseConceptReference(C); });
|
||||
}
|
||||
bool TraverseCXXBaseSpecifier(const CXXBaseSpecifier &CBS) {
|
||||
return traverseNode("base", CBS,
|
||||
[&] { Base::TraverseCXXBaseSpecifier(CBS); });
|
||||
@ -422,6 +434,8 @@ ASTNode dumpAST(const DynTypedNode &N, const syntax::TokenBuffer &Tokens,
|
||||
V.TraverseTemplateArgumentLoc(*const_cast<TemplateArgumentLoc *>(TAL));
|
||||
else if (const auto *CBS = N.get<CXXBaseSpecifier>())
|
||||
V.TraverseCXXBaseSpecifier(*const_cast<CXXBaseSpecifier *>(CBS));
|
||||
else if (const auto *CR = N.get<ConceptReference>())
|
||||
V.TraverseConceptReference(const_cast<ConceptReference *>(CR));
|
||||
else
|
||||
elog("dumpAST: unhandled DynTypedNode kind {0}",
|
||||
N.getNodeKind().asStringRef());
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "AST.h"
|
||||
#include "HeuristicResolver.h"
|
||||
#include "support/Logger.h"
|
||||
#include "clang/AST/ASTConcept.h"
|
||||
#include "clang/AST/ASTTypeTraits.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclBase.h"
|
||||
@ -257,7 +258,7 @@ public:
|
||||
Outer.add(CE->getCalleeDecl(), Flags);
|
||||
}
|
||||
void VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E) {
|
||||
Outer.add(E->getNamedConcept(), Flags);
|
||||
Outer.add(E->getConceptReference(), Flags);
|
||||
}
|
||||
void VisitDeclRefExpr(const DeclRefExpr *DRE) {
|
||||
const Decl *D = DRE->getDecl();
|
||||
@ -532,6 +533,10 @@ public:
|
||||
add(USD, Flags);
|
||||
}
|
||||
}
|
||||
|
||||
void add(const ConceptReference *CR, RelSet Flags) {
|
||||
add(CR->getNamedConcept(), Flags);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@ -561,6 +566,8 @@ allTargetDecls(const DynTypedNode &N, const HeuristicResolver *Resolver) {
|
||||
Finder.add(CBS->getTypeSourceInfo()->getType(), Flags);
|
||||
else if (const ObjCProtocolLoc *PL = N.get<ObjCProtocolLoc>())
|
||||
Finder.add(PL->getProtocol(), Flags);
|
||||
else if (const ConceptReference *CR = N.get<ConceptReference>())
|
||||
Finder.add(CR, Flags);
|
||||
return Finder.takeDecls();
|
||||
}
|
||||
|
||||
@ -1056,11 +1063,8 @@ public:
|
||||
return RecursiveASTVisitor::TraverseConstructorInitializer(Init);
|
||||
}
|
||||
|
||||
bool VisitConceptReference(ConceptReference *ConceptRef) {
|
||||
Out(ReferenceLoc{ConceptRef->getNestedNameSpecifierLoc(),
|
||||
ConceptRef->getConceptNameLoc(),
|
||||
/*IsDecl=*/false,
|
||||
{ConceptRef->getNamedConcept()}});
|
||||
bool VisitConceptReference(const ConceptReference *CR) {
|
||||
visitNode(DynTypedNode::create(*CR));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1109,6 +1113,11 @@ private:
|
||||
PL->getLocation(),
|
||||
/*IsDecl=*/false,
|
||||
{PL->getProtocol()}}};
|
||||
if (const ConceptReference *CR = N.get<ConceptReference>())
|
||||
return {ReferenceLoc{CR->getNestedNameSpecifierLoc(),
|
||||
CR->getConceptNameLoc(),
|
||||
/*IsDecl=*/false,
|
||||
{CR->getNamedConcept()}}};
|
||||
|
||||
// We do not have location information for other nodes (QualType, etc)
|
||||
return {};
|
||||
|
@ -635,8 +635,12 @@ public:
|
||||
if (llvm::isa_and_nonnull<TranslationUnitDecl>(X))
|
||||
return Base::TraverseDecl(X); // Already pushed by constructor.
|
||||
// Base::TraverseDecl will suppress children, but not this node itself.
|
||||
if (X && X->isImplicit())
|
||||
return true;
|
||||
if (X && X->isImplicit()) {
|
||||
// Most implicit nodes have only implicit children and can be skipped.
|
||||
// However there are exceptions (`void foo(Concept auto x)`), and
|
||||
// the base implementation knows how to find them.
|
||||
return Base::TraverseDecl(X);
|
||||
}
|
||||
return traverseNode(X, [&] { return Base::TraverseDecl(X); });
|
||||
}
|
||||
bool TraverseTypeLoc(TypeLoc X) {
|
||||
@ -660,6 +664,9 @@ public:
|
||||
bool TraverseAttr(Attr *X) {
|
||||
return traverseNode(X, [&] { return Base::TraverseAttr(X); });
|
||||
}
|
||||
bool TraverseConceptReference(ConceptReference *X) {
|
||||
return traverseNode(X, [&] { return Base::TraverseConceptReference(X); });
|
||||
}
|
||||
// Stmt is the same, but this form allows the data recursion optimization.
|
||||
bool dataTraverseStmtPre(Stmt *X) {
|
||||
if (!X || isImplicit(X))
|
||||
|
@ -537,7 +537,7 @@ TEST_F(TargetDeclTest, Concept) {
|
||||
}
|
||||
)cpp";
|
||||
EXPECT_DECLS(
|
||||
"ConceptSpecializationExpr",
|
||||
"ConceptReference",
|
||||
{"template <typename T> concept Fooable = requires (T t) { t.foo(); }"});
|
||||
|
||||
// trailing requires clause
|
||||
@ -548,7 +548,7 @@ TEST_F(TargetDeclTest, Concept) {
|
||||
template <typename T>
|
||||
void foo() requires [[Fooable]]<T>;
|
||||
)cpp";
|
||||
EXPECT_DECLS("ConceptSpecializationExpr",
|
||||
EXPECT_DECLS("ConceptReference",
|
||||
{"template <typename T> concept Fooable = true"});
|
||||
|
||||
// constrained-parameter
|
||||
@ -559,7 +559,7 @@ TEST_F(TargetDeclTest, Concept) {
|
||||
template <[[Fooable]] T>
|
||||
void bar(T t);
|
||||
)cpp";
|
||||
EXPECT_DECLS("ConceptSpecializationExpr",
|
||||
EXPECT_DECLS("ConceptReference",
|
||||
{"template <typename T> concept Fooable = true"});
|
||||
|
||||
// partial-concept-id
|
||||
@ -570,7 +570,7 @@ TEST_F(TargetDeclTest, Concept) {
|
||||
template <[[Fooable]]<int> T>
|
||||
void bar(T t);
|
||||
)cpp";
|
||||
EXPECT_DECLS("ConceptSpecializationExpr",
|
||||
EXPECT_DECLS("ConceptReference",
|
||||
{"template <typename T, typename U> concept Fooable = true"});
|
||||
}
|
||||
|
||||
|
@ -487,6 +487,16 @@ class Foo final {})cpp";
|
||||
HI.Kind = index::SymbolKind::TypeAlias;
|
||||
HI.Definition = "int";
|
||||
}},
|
||||
{R"cpp(
|
||||
template <class T> concept F = true;
|
||||
[[^F]] auto x = 1;
|
||||
)cpp",
|
||||
[](HoverInfo &HI) {
|
||||
HI.NamespaceScope = "";
|
||||
HI.Name = "F";
|
||||
HI.Kind = index::SymbolKind::Concept;
|
||||
HI.Definition = "template <class T>\nconcept F = true";
|
||||
}},
|
||||
// auto on lambda
|
||||
{R"cpp(
|
||||
void foo() {
|
||||
@ -535,7 +545,7 @@ class Foo final {})cpp";
|
||||
HI.Kind = index::SymbolKind::Concept;
|
||||
HI.Definition = "template <class T>\nconcept Fooable = true";
|
||||
}},
|
||||
{R"cpp(
|
||||
{R"cpp(
|
||||
template<class T> concept Fooable = true;
|
||||
template<Fooable [[T^T]]>
|
||||
void bar(TT t) {}
|
||||
@ -549,6 +559,28 @@ class Foo final {})cpp";
|
||||
HI.Kind = index::SymbolKind::TemplateTypeParm;
|
||||
HI.Definition = "Fooable TT";
|
||||
}},
|
||||
{R"cpp(
|
||||
template<class T> concept Fooable = true;
|
||||
void bar([[Foo^able]] auto t) {}
|
||||
)cpp",
|
||||
[](HoverInfo &HI) {
|
||||
HI.NamespaceScope = "";
|
||||
HI.Name = "Fooable";
|
||||
HI.Kind = index::SymbolKind::Concept;
|
||||
HI.Definition = "template <class T>\nconcept Fooable = true";
|
||||
}},
|
||||
// concept reference
|
||||
{R"cpp(
|
||||
template<class T> concept Fooable = true;
|
||||
auto X = [[Fooa^ble]]<int>;
|
||||
)cpp",
|
||||
[](HoverInfo &HI) {
|
||||
HI.NamespaceScope = "";
|
||||
HI.Name = "Fooable";
|
||||
HI.Kind = index::SymbolKind::Concept;
|
||||
HI.Definition = "template <class T>\nconcept Fooable = true";
|
||||
HI.Value = "true";
|
||||
}},
|
||||
|
||||
// empty macro
|
||||
{R"cpp(
|
||||
|
@ -561,6 +561,33 @@ TEST(SelectionTest, CommonAncestor) {
|
||||
[[^using enum ns::A]];
|
||||
)cpp",
|
||||
"UsingEnumDecl"},
|
||||
|
||||
// concepts
|
||||
{R"cpp(
|
||||
template <class> concept C = true;
|
||||
auto x = [[^C<int>]];
|
||||
)cpp",
|
||||
"ConceptReference"},
|
||||
{R"cpp(
|
||||
template <class> concept C = true;
|
||||
[[^C]] auto x = 0;
|
||||
)cpp",
|
||||
"ConceptReference"},
|
||||
{R"cpp(
|
||||
template <class> concept C = true;
|
||||
void foo([[^C]] auto x) {}
|
||||
)cpp",
|
||||
"ConceptReference"},
|
||||
{R"cpp(
|
||||
template <class> concept C = true;
|
||||
template <[[^C]] x> int i = 0;
|
||||
)cpp",
|
||||
"ConceptReference"},
|
||||
{R"cpp(
|
||||
namespace ns { template <class> concept C = true; }
|
||||
auto x = [[ns::^C<int>]];
|
||||
)cpp",
|
||||
"ConceptReference"},
|
||||
};
|
||||
|
||||
for (const Case &C : Cases) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user