1 //===- unittest/Tooling/RecursiveASTVisitorTests/Concept.cpp----------------==// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "TestVisitor.h" 10 #include "clang/AST/ASTConcept.h" 11 #include "clang/AST/DeclTemplate.h" 12 #include "clang/AST/ExprConcepts.h" 13 #include "clang/AST/Type.h" 14 15 using namespace clang; 16 17 namespace { 18 19 struct ConceptVisitor : ExpectedLocationVisitor { 20 ConceptVisitor(bool VisitImplicitCode = false) { 21 ShouldVisitImplicitCode = VisitImplicitCode; 22 } 23 24 bool VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) override { 25 ++ConceptSpecializationExprsVisited; 26 return true; 27 } 28 bool TraverseTypeConstraint(const TypeConstraint *C) override { 29 ++TypeConstraintsTraversed; 30 return ExpectedLocationVisitor::TraverseTypeConstraint(C); 31 } 32 bool TraverseConceptRequirement(concepts::Requirement *R) override { 33 ++ConceptRequirementsTraversed; 34 return ExpectedLocationVisitor::TraverseConceptRequirement(R); 35 } 36 bool TraverseConceptReference(ConceptReference *CR) override { 37 ++ConceptReferencesTraversed; 38 return ExpectedLocationVisitor::TraverseConceptReference(CR); 39 } 40 bool VisitConceptReference(ConceptReference *CR) override { 41 ++ConceptReferencesVisited; 42 return true; 43 } 44 45 int ConceptSpecializationExprsVisited = 0; 46 int TypeConstraintsTraversed = 0; 47 int ConceptRequirementsTraversed = 0; 48 int ConceptReferencesTraversed = 0; 49 int ConceptReferencesVisited = 0; 50 }; 51 52 TEST(RecursiveASTVisitor, Concepts) { 53 { 54 ConceptVisitor Visitor{true}; 55 EXPECT_TRUE( 56 Visitor.runOver("template <typename T> concept Fooable = true;\n" 57 "template <Fooable T> void bar(T);", 58 ConceptVisitor::Lang_CXX2a)); 59 // Check that we traverse the "Fooable T" template parameter's 60 // TypeConstraint's ImmediatelyDeclaredConstraint, which is a 61 // ConceptSpecializationExpr. 62 EXPECT_EQ(1, Visitor.ConceptSpecializationExprsVisited); 63 // Also check we traversed the TypeConstraint that produced the expr. 64 EXPECT_EQ(1, Visitor.TypeConstraintsTraversed); 65 EXPECT_EQ(1, Visitor.ConceptReferencesTraversed); 66 EXPECT_EQ(1, Visitor.ConceptReferencesVisited); 67 } 68 69 { 70 ConceptVisitor Visitor; // Don't visit implicit code now. 71 EXPECT_TRUE( 72 Visitor.runOver("template <typename T> concept Fooable = true;\n" 73 "template <Fooable T> void bar(T);", 74 ConceptVisitor::Lang_CXX2a)); 75 // Check that we only visit the TypeConstraint, but not the implicitly 76 // generated immediately declared expression. 77 EXPECT_EQ(0, Visitor.ConceptSpecializationExprsVisited); 78 EXPECT_EQ(1, Visitor.TypeConstraintsTraversed); 79 EXPECT_EQ(1, Visitor.ConceptReferencesTraversed); 80 EXPECT_EQ(1, Visitor.ConceptReferencesVisited); 81 } 82 83 { 84 ConceptVisitor Visitor; 85 EXPECT_TRUE( 86 Visitor.runOver("template <class T> concept A = true;\n" 87 "template <class T> struct vector {};\n" 88 "template <class T> concept B = requires(T x) {\n" 89 " typename vector<T*>;\n" 90 " {x} -> A;\n" 91 " requires true;\n" 92 "};", 93 ConceptVisitor::Lang_CXX2a)); 94 EXPECT_EQ(3, Visitor.ConceptRequirementsTraversed); 95 EXPECT_EQ(1, Visitor.ConceptReferencesTraversed); 96 EXPECT_EQ(1, Visitor.ConceptReferencesVisited); 97 } 98 99 ConceptVisitor Visitor; 100 llvm::StringRef Code = 101 R"cpp( 102 template<typename T> concept True = false; 103 template <typename F> struct Foo {}; 104 105 template <typename F> 106 requires requires { requires True<F>; } 107 struct Foo<F> {}; 108 109 template <typename F> requires True<F> 110 struct Foo<F> {}; 111 )cpp"; 112 EXPECT_TRUE(Visitor.runOver(Code, ConceptVisitor::Lang_CXX2a)); 113 // Check that the concept references from the partial specializations are 114 // visited. 115 EXPECT_EQ(2, Visitor.ConceptReferencesTraversed); 116 EXPECT_EQ(2, Visitor.ConceptReferencesVisited); 117 } 118 119 struct VisitDeclOnlyOnce : ExpectedLocationVisitor { 120 VisitDeclOnlyOnce() { ShouldWalkTypesOfTypeLocs = false; } 121 122 bool VisitConceptDecl(ConceptDecl *D) override { 123 ++ConceptDeclsVisited; 124 return true; 125 } 126 127 bool VisitAutoType(AutoType *) override { 128 ++AutoTypeVisited; 129 return true; 130 } 131 bool VisitAutoTypeLoc(AutoTypeLoc) override { 132 ++AutoTypeLocVisited; 133 return true; 134 } 135 bool VisitConceptReference(ConceptReference *) override { 136 ++ConceptReferencesVisited; 137 return true; 138 } 139 140 bool TraverseVarDecl(VarDecl *V) override { 141 // The base traversal visits only the `TypeLoc`. 142 // However, in the test we also validate the underlying `QualType`. 143 TraverseType(V->getType()); 144 return ExpectedLocationVisitor::TraverseVarDecl(V); 145 } 146 147 int ConceptDeclsVisited = 0; 148 int AutoTypeVisited = 0; 149 int AutoTypeLocVisited = 0; 150 int ConceptReferencesVisited = 0; 151 }; 152 153 TEST(RecursiveASTVisitor, ConceptDeclInAutoType) { 154 // Check `AutoType` and `AutoTypeLoc` do not repeatedly traverse the 155 // underlying concept. 156 VisitDeclOnlyOnce Visitor; 157 Visitor.runOver("template <class T> concept A = true;\n" 158 "A auto i = 0;\n", 159 VisitDeclOnlyOnce::Lang_CXX2a); 160 EXPECT_EQ(1, Visitor.AutoTypeVisited); 161 EXPECT_EQ(1, Visitor.AutoTypeLocVisited); 162 EXPECT_EQ(1, Visitor.ConceptDeclsVisited); 163 EXPECT_EQ(1, Visitor.ConceptReferencesVisited); 164 } 165 166 } // end anonymous namespace 167