blob: 6a8d91672f1d936aaddc29561b5c4c7201cf93f0 [file] [log] [blame]
//===- unittest/Tooling/RecursiveASTVisitorTests/Concept.cpp----------------==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "TestVisitor.h"
#include "clang/AST/ASTConcept.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/Type.h"
using namespace clang;
namespace {
struct ConceptVisitor : ExpectedLocationVisitor<ConceptVisitor> {
bool VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) {
++ConceptSpecializationExprsVisited;
return true;
}
bool TraverseTypeConstraint(const TypeConstraint *C) {
++TypeConstraintsTraversed;
return ExpectedLocationVisitor::TraverseTypeConstraint(C);
}
bool TraverseConceptRequirement(concepts::Requirement *R) {
++ConceptRequirementsTraversed;
return ExpectedLocationVisitor::TraverseConceptRequirement(R);
}
bool TraverseConceptReference(ConceptReference *CR) {
++ConceptReferencesTraversed;
return ExpectedLocationVisitor::TraverseConceptReference(CR);
}
bool VisitConceptReference(ConceptReference *CR) {
++ConceptReferencesVisited;
return true;
}
bool shouldVisitImplicitCode() { return ShouldVisitImplicitCode; }
int ConceptSpecializationExprsVisited = 0;
int TypeConstraintsTraversed = 0;
int ConceptRequirementsTraversed = 0;
int ConceptReferencesTraversed = 0;
int ConceptReferencesVisited = 0;
bool ShouldVisitImplicitCode = false;
};
TEST(RecursiveASTVisitor, Concepts) {
ConceptVisitor Visitor;
Visitor.ShouldVisitImplicitCode = true;
EXPECT_TRUE(Visitor.runOver("template <typename T> concept Fooable = true;\n"
"template <Fooable T> void bar(T);",
ConceptVisitor::Lang_CXX2a));
// Check that we traverse the "Fooable T" template parameter's
// TypeConstraint's ImmediatelyDeclaredConstraint, which is a
// ConceptSpecializationExpr.
EXPECT_EQ(1, Visitor.ConceptSpecializationExprsVisited);
// Also check we traversed the TypeConstraint that produced the expr.
EXPECT_EQ(1, Visitor.TypeConstraintsTraversed);
EXPECT_EQ(1, Visitor.ConceptReferencesTraversed);
EXPECT_EQ(1, Visitor.ConceptReferencesVisited);
Visitor = {}; // Don't visit implicit code now.
EXPECT_TRUE(Visitor.runOver("template <typename T> concept Fooable = true;\n"
"template <Fooable T> void bar(T);",
ConceptVisitor::Lang_CXX2a));
// Check that we only visit the TypeConstraint, but not the implicitly
// generated immediately declared expression.
EXPECT_EQ(0, Visitor.ConceptSpecializationExprsVisited);
EXPECT_EQ(1, Visitor.TypeConstraintsTraversed);
EXPECT_EQ(1, Visitor.ConceptReferencesTraversed);
EXPECT_EQ(1, Visitor.ConceptReferencesVisited);
Visitor = {};
EXPECT_TRUE(Visitor.runOver("template <class T> concept A = true;\n"
"template <class T> struct vector {};\n"
"template <class T> concept B = requires(T x) {\n"
" typename vector<T*>;\n"
" {x} -> A;\n"
" requires true;\n"
"};",
ConceptVisitor::Lang_CXX2a));
EXPECT_EQ(3, Visitor.ConceptRequirementsTraversed);
EXPECT_EQ(1, Visitor.ConceptReferencesTraversed);
EXPECT_EQ(1, Visitor.ConceptReferencesVisited);
Visitor = {};
llvm::StringRef Code =
R"cpp(
template<typename T> concept True = false;
template <typename F> struct Foo {};
template <typename F>
requires requires { requires True<F>; }
struct Foo<F> {};
template <typename F> requires True<F>
struct Foo<F> {};
)cpp";
EXPECT_TRUE(Visitor.runOver(Code, ConceptVisitor::Lang_CXX2a));
// Check that the concept references from the partial specializations are
// visited.
EXPECT_EQ(2, Visitor.ConceptReferencesTraversed);
EXPECT_EQ(2, Visitor.ConceptReferencesVisited);
}
struct VisitDeclOnlyOnce : ExpectedLocationVisitor<VisitDeclOnlyOnce> {
bool VisitConceptDecl(ConceptDecl *D) {
++ConceptDeclsVisited;
return true;
}
bool VisitAutoType(AutoType *) {
++AutoTypeVisited;
return true;
}
bool VisitAutoTypeLoc(AutoTypeLoc) {
++AutoTypeLocVisited;
return true;
}
bool VisitConceptReference(ConceptReference *) {
++ConceptReferencesVisited;
return true;
}
bool TraverseVarDecl(VarDecl *V) {
// The base traversal visits only the `TypeLoc`.
// However, in the test we also validate the underlying `QualType`.
TraverseType(V->getType());
return ExpectedLocationVisitor::TraverseVarDecl(V);
}
bool shouldWalkTypesOfTypeLocs() { return false; }
int ConceptDeclsVisited = 0;
int AutoTypeVisited = 0;
int AutoTypeLocVisited = 0;
int ConceptReferencesVisited = 0;
};
TEST(RecursiveASTVisitor, ConceptDeclInAutoType) {
// Check `AutoType` and `AutoTypeLoc` do not repeatedly traverse the
// underlying concept.
VisitDeclOnlyOnce Visitor;
Visitor.runOver("template <class T> concept A = true;\n"
"A auto i = 0;\n",
VisitDeclOnlyOnce::Lang_CXX2a);
EXPECT_EQ(1, Visitor.AutoTypeVisited);
EXPECT_EQ(1, Visitor.AutoTypeLocVisited);
EXPECT_EQ(1, Visitor.ConceptDeclsVisited);
EXPECT_EQ(1, Visitor.ConceptReferencesVisited);
}
} // end anonymous namespace