sammccall created this revision. sammccall added a reviewer: massberg. Herald added subscribers: kadircet, arphaman. Herald added a project: All. sammccall requested review of this revision. Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov. Herald added a project: clang-tools-extra.
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. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D159299 Files: clang-tools-extra/clangd/DumpAST.cpp clang-tools-extra/clangd/FindTarget.cpp clang-tools-extra/clangd/Selection.cpp clang-tools-extra/clangd/unittests/FindTargetTests.cpp clang-tools-extra/clangd/unittests/HoverTests.cpp clang-tools-extra/clangd/unittests/SelectionTests.cpp
Index: clang-tools-extra/clangd/unittests/SelectionTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/SelectionTests.cpp +++ clang-tools-extra/clangd/unittests/SelectionTests.cpp @@ -561,6 +561,33 @@ [[^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) { Index: clang-tools-extra/clangd/unittests/HoverTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/HoverTests.cpp +++ clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -487,6 +487,16 @@ 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 @@ 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 @@ 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( Index: clang-tools-extra/clangd/unittests/FindTargetTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -537,7 +537,7 @@ } )cpp"; EXPECT_DECLS( - "ConceptSpecializationExpr", + "ConceptReference", {"template <typename T> concept Fooable = requires (T t) { t.foo(); }"}); // trailing requires clause @@ -548,7 +548,7 @@ 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 @@ 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 @@ template <[[Fooable]]<int> T> void bar(T t); )cpp"; - EXPECT_DECLS("ConceptSpecializationExpr", + EXPECT_DECLS("ConceptReference", {"template <typename T, typename U> concept Fooable = true"}); } Index: clang-tools-extra/clangd/Selection.cpp =================================================================== --- clang-tools-extra/clangd/Selection.cpp +++ clang-tools-extra/clangd/Selection.cpp @@ -635,8 +635,12 @@ 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 @@ 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)) Index: clang-tools-extra/clangd/FindTarget.cpp =================================================================== --- clang-tools-extra/clangd/FindTarget.cpp +++ 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 @@ 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 @@ add(USD, Flags); } } + + void add(const ConceptReference *CR, RelSet Flags) { + add(CR->getNamedConcept(), Flags); + } }; } // namespace @@ -561,6 +566,8 @@ 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(); } @@ -742,13 +749,6 @@ // FIXME: handle more complicated cases: more ObjC, designated initializers. llvm::SmallVector<ReferenceLoc> Refs; - void VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E) { - Refs.push_back(ReferenceLoc{E->getNestedNameSpecifierLoc(), - E->getConceptNameLoc(), - /*IsDecl=*/false, - {E->getNamedConcept()}}); - } - void VisitDeclRefExpr(const DeclRefExpr *E) { Refs.push_back(ReferenceLoc{E->getQualifierLoc(), E->getNameInfo().getLoc(), @@ -1063,15 +1063,9 @@ return RecursiveASTVisitor::TraverseConstructorInitializer(Init); } - bool TraverseTypeConstraint(const TypeConstraint *TC) { - // We want to handle all ConceptReferences but RAV is missing a - // polymorphic Visit or Traverse method for it, so we handle - // TypeConstraints specially here. - Out(ReferenceLoc{TC->getNestedNameSpecifierLoc(), - TC->getConceptNameLoc(), - /*IsDecl=*/false, - {TC->getNamedConcept()}}); - return RecursiveASTVisitor::TraverseTypeConstraint(TC); + bool VisitConceptReference(const ConceptReference *CR) { + visitNode(DynTypedNode::create(*CR)); + return true; } private: @@ -1119,6 +1113,11 @@ 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 {}; Index: clang-tools-extra/clangd/DumpAST.cpp =================================================================== --- clang-tools-extra/clangd/DumpAST.cpp +++ clang-tools-extra/clangd/DumpAST.cpp @@ -205,6 +205,11 @@ // 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 @@ 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 @@ 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 @@ 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());
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits