xref: /llvm-project/clang/unittests/Tooling/RecursiveASTVisitorTests/Concept.cpp (revision 4e600751d2f7e8e7b85a71b7128b68444bdde91b)
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