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 | |