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<ConceptVisitor> {
VisitConceptSpecializationExpr__anon0eba5fef0111::ConceptVisitor20   bool VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) {
21     ++ConceptSpecializationExprsVisited;
22     return true;
23   }
TraverseTypeConstraint__anon0eba5fef0111::ConceptVisitor24   bool TraverseTypeConstraint(const TypeConstraint *C) {
25     ++TypeConstraintsTraversed;
26     return ExpectedLocationVisitor::TraverseTypeConstraint(C);
27   }
TraverseConceptRequirement__anon0eba5fef0111::ConceptVisitor28   bool TraverseConceptRequirement(concepts::Requirement *R) {
29     ++ConceptRequirementsTraversed;
30     return ExpectedLocationVisitor::TraverseConceptRequirement(R);
31   }
32 
shouldVisitImplicitCode__anon0eba5fef0111::ConceptVisitor33   bool shouldVisitImplicitCode() { return ShouldVisitImplicitCode; }
34 
35   int ConceptSpecializationExprsVisited = 0;
36   int TypeConstraintsTraversed = 0;
37   int ConceptRequirementsTraversed = 0;
38   bool ShouldVisitImplicitCode = false;
39 };
40 
TEST(RecursiveASTVisitor,Concepts)41 TEST(RecursiveASTVisitor, Concepts) {
42   ConceptVisitor Visitor;
43   Visitor.ShouldVisitImplicitCode = true;
44   EXPECT_TRUE(Visitor.runOver("template <typename T> concept Fooable = true;\n"
45                               "template <Fooable T> void bar(T);",
46                               ConceptVisitor::Lang_CXX2a));
47   // Check that we traverse the "Fooable T" template parameter's
48   // TypeConstraint's ImmediatelyDeclaredConstraint, which is a
49   // ConceptSpecializationExpr.
50   EXPECT_EQ(1, Visitor.ConceptSpecializationExprsVisited);
51   // Also check we traversed the TypeConstraint that produced the expr.
52   EXPECT_EQ(1, Visitor.TypeConstraintsTraversed);
53 
54   Visitor = {}; // Don't visit implicit code now.
55   EXPECT_TRUE(Visitor.runOver("template <typename T> concept Fooable = true;\n"
56                               "template <Fooable T> void bar(T);",
57                               ConceptVisitor::Lang_CXX2a));
58   // Check that we only visit the TypeConstraint, but not the implicitly
59   // generated immediately declared expression.
60   EXPECT_EQ(0, Visitor.ConceptSpecializationExprsVisited);
61   EXPECT_EQ(1, Visitor.TypeConstraintsTraversed);
62 
63   Visitor = {};
64   EXPECT_TRUE(Visitor.runOver("template <class T> concept A = true;\n"
65                               "template <class T> struct vector {};\n"
66                               "template <class T> concept B = requires(T x) {\n"
67                               "  typename vector<T*>;\n"
68                               "  {x} -> A;\n"
69                               "  requires true;\n"
70                               "};",
71                               ConceptVisitor::Lang_CXX2a));
72   EXPECT_EQ(3, Visitor.ConceptRequirementsTraversed);
73 }
74 
75 struct VisitDeclOnlyOnce : ExpectedLocationVisitor<VisitDeclOnlyOnce> {
VisitConceptDecl__anon0eba5fef0111::VisitDeclOnlyOnce76   bool VisitConceptDecl(ConceptDecl *D) {
77     ++ConceptDeclsVisited;
78     return true;
79   }
80 
VisitAutoType__anon0eba5fef0111::VisitDeclOnlyOnce81   bool VisitAutoType(AutoType *) {
82     ++AutoTypeVisited;
83     return true;
84   }
VisitAutoTypeLoc__anon0eba5fef0111::VisitDeclOnlyOnce85   bool VisitAutoTypeLoc(AutoTypeLoc) {
86     ++AutoTypeLocVisited;
87     return true;
88   }
89 
TraverseVarDecl__anon0eba5fef0111::VisitDeclOnlyOnce90   bool TraverseVarDecl(VarDecl *V) {
91     // The base traversal visits only the `TypeLoc`.
92     // However, in the test we also validate the underlying `QualType`.
93     TraverseType(V->getType());
94     return ExpectedLocationVisitor::TraverseVarDecl(V);
95   }
96 
shouldWalkTypesOfTypeLocs__anon0eba5fef0111::VisitDeclOnlyOnce97   bool shouldWalkTypesOfTypeLocs() { return false; }
98 
99   int ConceptDeclsVisited = 0;
100   int AutoTypeVisited = 0;
101   int AutoTypeLocVisited = 0;
102 };
103 
TEST(RecursiveASTVisitor,ConceptDeclInAutoType)104 TEST(RecursiveASTVisitor, ConceptDeclInAutoType) {
105   // Check `AutoType` and `AutoTypeLoc` do not repeatedly traverse the
106   // underlying concept.
107   VisitDeclOnlyOnce Visitor;
108   Visitor.runOver("template <class T> concept A = true;\n"
109                   "A auto i = 0;\n",
110                   VisitDeclOnlyOnce::Lang_CXX2a);
111   EXPECT_EQ(1, Visitor.AutoTypeVisited);
112   EXPECT_EQ(1, Visitor.AutoTypeLocVisited);
113   EXPECT_EQ(1, Visitor.ConceptDeclsVisited);
114 }
115 
116 } // end anonymous namespace
117