100d7b7d0SNathan Ridge //===- unittest/Tooling/RecursiveASTVisitorTests/Concept.cpp----------------==// 200d7b7d0SNathan Ridge // 300d7b7d0SNathan Ridge // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 400d7b7d0SNathan Ridge // See https://llvm.org/LICENSE.txt for license information. 500d7b7d0SNathan Ridge // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 600d7b7d0SNathan Ridge // 700d7b7d0SNathan Ridge //===----------------------------------------------------------------------===// 800d7b7d0SNathan Ridge 900d7b7d0SNathan Ridge #include "TestVisitor.h" 102b833d40SIlya Biryukov #include "clang/AST/ASTConcept.h" 112b833d40SIlya Biryukov #include "clang/AST/DeclTemplate.h" 1200d7b7d0SNathan Ridge #include "clang/AST/ExprConcepts.h" 132b833d40SIlya Biryukov #include "clang/AST/Type.h" 1400d7b7d0SNathan Ridge 1500d7b7d0SNathan Ridge using namespace clang; 1600d7b7d0SNathan Ridge 1700d7b7d0SNathan Ridge namespace { 1800d7b7d0SNathan Ridge 19*4e600751SSirraide struct ConceptVisitor : ExpectedLocationVisitor { 20*4e600751SSirraide ConceptVisitor(bool VisitImplicitCode = false) { 21*4e600751SSirraide ShouldVisitImplicitCode = VisitImplicitCode; 22*4e600751SSirraide } 23*4e600751SSirraide 24*4e600751SSirraide bool VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) override { 2500d7b7d0SNathan Ridge ++ConceptSpecializationExprsVisited; 2600d7b7d0SNathan Ridge return true; 2700d7b7d0SNathan Ridge } 28*4e600751SSirraide bool TraverseTypeConstraint(const TypeConstraint *C) override { 292b833d40SIlya Biryukov ++TypeConstraintsTraversed; 302b833d40SIlya Biryukov return ExpectedLocationVisitor::TraverseTypeConstraint(C); 312b833d40SIlya Biryukov } 32*4e600751SSirraide bool TraverseConceptRequirement(concepts::Requirement *R) override { 332b833d40SIlya Biryukov ++ConceptRequirementsTraversed; 342b833d40SIlya Biryukov return ExpectedLocationVisitor::TraverseConceptRequirement(R); 3500d7b7d0SNathan Ridge } 36*4e600751SSirraide bool TraverseConceptReference(ConceptReference *CR) override { 37c2bf9bafSJens Massberg ++ConceptReferencesTraversed; 38c2bf9bafSJens Massberg return ExpectedLocationVisitor::TraverseConceptReference(CR); 39c2bf9bafSJens Massberg } 40*4e600751SSirraide bool VisitConceptReference(ConceptReference *CR) override { 41c2bf9bafSJens Massberg ++ConceptReferencesVisited; 42c2bf9bafSJens Massberg return true; 43c2bf9bafSJens Massberg } 4400d7b7d0SNathan Ridge 4500d7b7d0SNathan Ridge int ConceptSpecializationExprsVisited = 0; 462b833d40SIlya Biryukov int TypeConstraintsTraversed = 0; 472b833d40SIlya Biryukov int ConceptRequirementsTraversed = 0; 48c2bf9bafSJens Massberg int ConceptReferencesTraversed = 0; 49c2bf9bafSJens Massberg int ConceptReferencesVisited = 0; 5000d7b7d0SNathan Ridge }; 5100d7b7d0SNathan Ridge 522b833d40SIlya Biryukov TEST(RecursiveASTVisitor, Concepts) { 53*4e600751SSirraide { 54*4e600751SSirraide ConceptVisitor Visitor{true}; 55*4e600751SSirraide EXPECT_TRUE( 56*4e600751SSirraide Visitor.runOver("template <typename T> concept Fooable = true;\n" 5700d7b7d0SNathan Ridge "template <Fooable T> void bar(T);", 5800d7b7d0SNathan Ridge ConceptVisitor::Lang_CXX2a)); 592b833d40SIlya Biryukov // Check that we traverse the "Fooable T" template parameter's 602b833d40SIlya Biryukov // TypeConstraint's ImmediatelyDeclaredConstraint, which is a 612b833d40SIlya Biryukov // ConceptSpecializationExpr. 6200d7b7d0SNathan Ridge EXPECT_EQ(1, Visitor.ConceptSpecializationExprsVisited); 632b833d40SIlya Biryukov // Also check we traversed the TypeConstraint that produced the expr. 642b833d40SIlya Biryukov EXPECT_EQ(1, Visitor.TypeConstraintsTraversed); 65c2bf9bafSJens Massberg EXPECT_EQ(1, Visitor.ConceptReferencesTraversed); 66c2bf9bafSJens Massberg EXPECT_EQ(1, Visitor.ConceptReferencesVisited); 67*4e600751SSirraide } 682b833d40SIlya Biryukov 69*4e600751SSirraide { 70*4e600751SSirraide ConceptVisitor Visitor; // Don't visit implicit code now. 71*4e600751SSirraide EXPECT_TRUE( 72*4e600751SSirraide Visitor.runOver("template <typename T> concept Fooable = true;\n" 732b833d40SIlya Biryukov "template <Fooable T> void bar(T);", 742b833d40SIlya Biryukov ConceptVisitor::Lang_CXX2a)); 752b833d40SIlya Biryukov // Check that we only visit the TypeConstraint, but not the implicitly 762b833d40SIlya Biryukov // generated immediately declared expression. 772b833d40SIlya Biryukov EXPECT_EQ(0, Visitor.ConceptSpecializationExprsVisited); 782b833d40SIlya Biryukov EXPECT_EQ(1, Visitor.TypeConstraintsTraversed); 79c2bf9bafSJens Massberg EXPECT_EQ(1, Visitor.ConceptReferencesTraversed); 80c2bf9bafSJens Massberg EXPECT_EQ(1, Visitor.ConceptReferencesVisited); 81*4e600751SSirraide } 822b833d40SIlya Biryukov 83*4e600751SSirraide { 84*4e600751SSirraide ConceptVisitor Visitor; 85*4e600751SSirraide EXPECT_TRUE( 86*4e600751SSirraide Visitor.runOver("template <class T> concept A = true;\n" 872b833d40SIlya Biryukov "template <class T> struct vector {};\n" 882b833d40SIlya Biryukov "template <class T> concept B = requires(T x) {\n" 892b833d40SIlya Biryukov " typename vector<T*>;\n" 902b833d40SIlya Biryukov " {x} -> A;\n" 912b833d40SIlya Biryukov " requires true;\n" 922b833d40SIlya Biryukov "};", 932b833d40SIlya Biryukov ConceptVisitor::Lang_CXX2a)); 942b833d40SIlya Biryukov EXPECT_EQ(3, Visitor.ConceptRequirementsTraversed); 95c2bf9bafSJens Massberg EXPECT_EQ(1, Visitor.ConceptReferencesTraversed); 96c2bf9bafSJens Massberg EXPECT_EQ(1, Visitor.ConceptReferencesVisited); 97*4e600751SSirraide } 983b1f06e5SHaojian Wu 99*4e600751SSirraide ConceptVisitor Visitor; 1003b1f06e5SHaojian Wu llvm::StringRef Code = 1013b1f06e5SHaojian Wu R"cpp( 1023b1f06e5SHaojian Wu template<typename T> concept True = false; 1033b1f06e5SHaojian Wu template <typename F> struct Foo {}; 1043b1f06e5SHaojian Wu 1053b1f06e5SHaojian Wu template <typename F> 1063b1f06e5SHaojian Wu requires requires { requires True<F>; } 1073b1f06e5SHaojian Wu struct Foo<F> {}; 1083b1f06e5SHaojian Wu 1093b1f06e5SHaojian Wu template <typename F> requires True<F> 1103b1f06e5SHaojian Wu struct Foo<F> {}; 1113b1f06e5SHaojian Wu )cpp"; 1123b1f06e5SHaojian Wu EXPECT_TRUE(Visitor.runOver(Code, ConceptVisitor::Lang_CXX2a)); 1133b1f06e5SHaojian Wu // Check that the concept references from the partial specializations are 1143b1f06e5SHaojian Wu // visited. 1153b1f06e5SHaojian Wu EXPECT_EQ(2, Visitor.ConceptReferencesTraversed); 1163b1f06e5SHaojian Wu EXPECT_EQ(2, Visitor.ConceptReferencesVisited); 1172b833d40SIlya Biryukov } 1182b833d40SIlya Biryukov 119*4e600751SSirraide struct VisitDeclOnlyOnce : ExpectedLocationVisitor { 120*4e600751SSirraide VisitDeclOnlyOnce() { ShouldWalkTypesOfTypeLocs = false; } 121*4e600751SSirraide 122*4e600751SSirraide bool VisitConceptDecl(ConceptDecl *D) override { 1232b833d40SIlya Biryukov ++ConceptDeclsVisited; 1242b833d40SIlya Biryukov return true; 1252b833d40SIlya Biryukov } 1262b833d40SIlya Biryukov 127*4e600751SSirraide bool VisitAutoType(AutoType *) override { 1282b833d40SIlya Biryukov ++AutoTypeVisited; 1292b833d40SIlya Biryukov return true; 1302b833d40SIlya Biryukov } 131*4e600751SSirraide bool VisitAutoTypeLoc(AutoTypeLoc) override { 1322b833d40SIlya Biryukov ++AutoTypeLocVisited; 1332b833d40SIlya Biryukov return true; 1342b833d40SIlya Biryukov } 135*4e600751SSirraide bool VisitConceptReference(ConceptReference *) override { 136c2bf9bafSJens Massberg ++ConceptReferencesVisited; 137c2bf9bafSJens Massberg return true; 138c2bf9bafSJens Massberg } 1392b833d40SIlya Biryukov 140*4e600751SSirraide bool TraverseVarDecl(VarDecl *V) override { 1412b833d40SIlya Biryukov // The base traversal visits only the `TypeLoc`. 1422b833d40SIlya Biryukov // However, in the test we also validate the underlying `QualType`. 1432b833d40SIlya Biryukov TraverseType(V->getType()); 1442b833d40SIlya Biryukov return ExpectedLocationVisitor::TraverseVarDecl(V); 1452b833d40SIlya Biryukov } 1462b833d40SIlya Biryukov 1472b833d40SIlya Biryukov int ConceptDeclsVisited = 0; 1482b833d40SIlya Biryukov int AutoTypeVisited = 0; 1492b833d40SIlya Biryukov int AutoTypeLocVisited = 0; 150c2bf9bafSJens Massberg int ConceptReferencesVisited = 0; 1512b833d40SIlya Biryukov }; 1522b833d40SIlya Biryukov 1532b833d40SIlya Biryukov TEST(RecursiveASTVisitor, ConceptDeclInAutoType) { 1542b833d40SIlya Biryukov // Check `AutoType` and `AutoTypeLoc` do not repeatedly traverse the 1552b833d40SIlya Biryukov // underlying concept. 1562b833d40SIlya Biryukov VisitDeclOnlyOnce Visitor; 1572b833d40SIlya Biryukov Visitor.runOver("template <class T> concept A = true;\n" 1582b833d40SIlya Biryukov "A auto i = 0;\n", 1592b833d40SIlya Biryukov VisitDeclOnlyOnce::Lang_CXX2a); 1602b833d40SIlya Biryukov EXPECT_EQ(1, Visitor.AutoTypeVisited); 1612b833d40SIlya Biryukov EXPECT_EQ(1, Visitor.AutoTypeLocVisited); 1622b833d40SIlya Biryukov EXPECT_EQ(1, Visitor.ConceptDeclsVisited); 163c2bf9bafSJens Massberg EXPECT_EQ(1, Visitor.ConceptReferencesVisited); 16400d7b7d0SNathan Ridge } 16500d7b7d0SNathan Ridge 16600d7b7d0SNathan Ridge } // end anonymous namespace 167