167 lines · cpp
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-exception6//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 15using namespace clang;16 17namespace {18 19struct 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 52TEST(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's60 // TypeConstraint's ImmediatelyDeclaredConstraint, which is a61 // 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 implicitly76 // 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(102template<typename T> concept True = false;103template <typename F> struct Foo {};104 105template <typename F>106 requires requires { requires True<F>; }107struct Foo<F> {};108 109template <typename F> requires True<F>110struct Foo<F> {};111 )cpp";112 EXPECT_TRUE(Visitor.runOver(Code, ConceptVisitor::Lang_CXX2a));113 // Check that the concept references from the partial specializations are114 // visited.115 EXPECT_EQ(2, Visitor.ConceptReferencesTraversed);116 EXPECT_EQ(2, Visitor.ConceptReferencesVisited);117}118 119struct 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 153TEST(RecursiveASTVisitor, ConceptDeclInAutoType) {154 // Check `AutoType` and `AutoTypeLoc` do not repeatedly traverse the155 // 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 namespace167