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