158 lines
5.5 KiB
C++
158 lines
5.5 KiB
C++
|
//===- 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
|