diff --git a/clang-tools-extra/clangd/DumpAST.cpp b/clang-tools-extra/clangd/DumpAST.cpp index 2bf75e65a9efb..85f2592445f2a 100644 --- a/clang-tools-extra/clangd/DumpAST.cpp +++ b/clang-tools-extra/clangd/DumpAST.cpp @@ -205,6 +205,11 @@ class DumpVisitor : public RecursiveASTVisitor { // 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 { 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 @@ class DumpVisitor : public RecursiveASTVisitor { 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(TAL)); else if (const auto *CBS = N.get()) V.TraverseCXXBaseSpecifier(*const_cast(CBS)); + else if (const auto *CR = N.get()) + V.TraverseConceptReference(const_cast(CR)); else elog("dumpAST: unhandled DynTypedNode kind {0}", N.getNodeKind().asStringRef()); diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp index c1ec030275c4f..a766122ad3dee 100644 --- a/clang-tools-extra/clangd/FindTarget.cpp +++ b/clang-tools-extra/clangd/FindTarget.cpp @@ -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 @@ struct TargetFinder { 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 @@ struct TargetFinder { 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()) Finder.add(PL->getProtocol(), Flags); + else if (const ConceptReference *CR = N.get()) + Finder.add(CR, Flags); return Finder.takeDecls(); } @@ -1056,11 +1063,8 @@ class ExplicitReferenceCollector 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 @@ class ExplicitReferenceCollector PL->getLocation(), /*IsDecl=*/false, {PL->getProtocol()}}}; + if (const ConceptReference *CR = N.get()) + return {ReferenceLoc{CR->getNestedNameSpecifierLoc(), + CR->getConceptNameLoc(), + /*IsDecl=*/false, + {CR->getNamedConcept()}}}; // We do not have location information for other nodes (QualType, etc) return {}; diff --git a/clang-tools-extra/clangd/Selection.cpp b/clang-tools-extra/clangd/Selection.cpp index 233a3c7cd313d..8c6d5750ecefd 100644 --- a/clang-tools-extra/clangd/Selection.cpp +++ b/clang-tools-extra/clangd/Selection.cpp @@ -635,8 +635,12 @@ class SelectionVisitor : public RecursiveASTVisitor { if (llvm::isa_and_nonnull(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 @@ class SelectionVisitor : public RecursiveASTVisitor { 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)) diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp index 19e80658de063..fbd10c4a47a79 100644 --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -537,7 +537,7 @@ TEST_F(TargetDeclTest, Concept) { } )cpp"; EXPECT_DECLS( - "ConceptSpecializationExpr", + "ConceptReference", {"template concept Fooable = requires (T t) { t.foo(); }"}); // trailing requires clause @@ -548,7 +548,7 @@ TEST_F(TargetDeclTest, Concept) { template void foo() requires [[Fooable]]; )cpp"; - EXPECT_DECLS("ConceptSpecializationExpr", + EXPECT_DECLS("ConceptReference", {"template 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 concept Fooable = true"}); // partial-concept-id @@ -570,7 +570,7 @@ TEST_F(TargetDeclTest, Concept) { template <[[Fooable]] T> void bar(T t); )cpp"; - EXPECT_DECLS("ConceptSpecializationExpr", + EXPECT_DECLS("ConceptReference", {"template concept Fooable = true"}); } diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp index c26bda898687c..8c88cd5257453 100644 --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -487,6 +487,16 @@ class Foo final {})cpp"; HI.Kind = index::SymbolKind::TypeAlias; HI.Definition = "int"; }}, + {R"cpp( + template concept F = true; + [[^F]] auto x = 1; + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.Name = "F"; + HI.Kind = index::SymbolKind::Concept; + HI.Definition = "template \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 \nconcept Fooable = true"; }}, - {R"cpp( + {R"cpp( template concept Fooable = true; template void bar(TT t) {} @@ -549,6 +559,28 @@ class Foo final {})cpp"; HI.Kind = index::SymbolKind::TemplateTypeParm; HI.Definition = "Fooable TT"; }}, + {R"cpp( + template 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 \nconcept Fooable = true"; + }}, + // concept reference + {R"cpp( + template concept Fooable = true; + auto X = [[Fooa^ble]]; + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.Name = "Fooable"; + HI.Kind = index::SymbolKind::Concept; + HI.Definition = "template \nconcept Fooable = true"; + HI.Value = "true"; + }}, // empty macro {R"cpp( diff --git a/clang-tools-extra/clangd/unittests/SelectionTests.cpp b/clang-tools-extra/clangd/unittests/SelectionTests.cpp index 422a89a49630d..4c019a1524f3c 100644 --- a/clang-tools-extra/clangd/unittests/SelectionTests.cpp +++ b/clang-tools-extra/clangd/unittests/SelectionTests.cpp @@ -561,6 +561,33 @@ TEST(SelectionTest, CommonAncestor) { [[^using enum ns::A]]; )cpp", "UsingEnumDecl"}, + + // concepts + {R"cpp( + template concept C = true; + auto x = [[^C]]; + )cpp", + "ConceptReference"}, + {R"cpp( + template concept C = true; + [[^C]] auto x = 0; + )cpp", + "ConceptReference"}, + {R"cpp( + template concept C = true; + void foo([[^C]] auto x) {} + )cpp", + "ConceptReference"}, + {R"cpp( + template concept C = true; + template <[[^C]] x> int i = 0; + )cpp", + "ConceptReference"}, + {R"cpp( + namespace ns { template concept C = true; } + auto x = [[ns::^C]]; + )cpp", + "ConceptReference"}, }; for (const Case &C : Cases) {