#ifndef LLVM_CLANG_AST_RECURSIVEASTVISITOR_H
#define LLVM_CLANG_AST_RECURSIVEASTVISITOR_H
+#include "clang/AST/ASTConcept.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
-#include "clang/AST/DeclarationName.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclOpenMP.h"
#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/DeclarationName.h"
#include "clang/AST/Expr.h"
-#include "clang/AST/ExprConcepts.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprConcepts.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ExprOpenMP.h"
#include "clang/AST/LambdaCapture.h"
bool TraverseSynOrSemInitListExpr(InitListExpr *S,
DataRecursionQueue *Queue = nullptr);
- /// Recursively visit a reference to a concept with potential arguments.
- ///
- /// \returns false if the visitation was terminated early, true otherwise.
- bool TraverseConceptReference(const ConceptReference &C);
-
/// Recursively visit an Objective-C protocol reference with location
/// information.
///
DEF_TRAVERSE_TMPL_INST(Function)
#undef DEF_TRAVERSE_TMPL_INST
+ bool TraverseTypeConstraint(const TypeConstraint *C);
+
+ bool TraverseConceptRequirement(concepts::Requirement *R);
+ bool TraverseConceptTypeRequirement(concepts::TypeRequirement *R);
+ bool TraverseConceptExprRequirement(concepts::ExprRequirement *R);
+ bool TraverseConceptNestedRequirement(concepts::NestedRequirement *R);
+
bool dataTraverseNode(Stmt *S, DataRecursionQueue *Queue);
private:
// These are helper methods used by more than one Traverse* method.
bool TraverseTemplateParameterListHelper(TemplateParameterList *TPL);
+ /// Traverses the qualifier, name and template arguments of a concept
+ /// reference.
+ bool TraverseConceptReferenceHelper(const ConceptReference &C);
// Traverses template parameter lists of either a DeclaratorDecl or TagDecl.
template <typename T>
};
template <typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseTypeConstraint(
+ const TypeConstraint *C) {
+ if (!getDerived().shouldVisitImplicitCode()) {
+ TRY_TO(TraverseConceptReferenceHelper(*C));
+ return true;
+ }
+ if (Expr *IDC = C->getImmediatelyDeclaredConstraint()) {
+ TRY_TO(TraverseStmt(IDC));
+ } else {
+ // Avoid traversing the ConceptReference in the TypeConstraint
+ // if we have an immediately-declared-constraint, otherwise
+ // we'll end up visiting the concept and the arguments in
+ // the TC twice.
+ TRY_TO(TraverseConceptReferenceHelper(*C));
+ }
+ return true;
+}
+
+template <typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseConceptRequirement(
+ concepts::Requirement *R) {
+ switch (R->getKind()) {
+ case concepts::Requirement::RK_Type:
+ return getDerived().TraverseConceptTypeRequirement(
+ cast<concepts::TypeRequirement>(R));
+ case concepts::Requirement::RK_Simple:
+ case concepts::Requirement::RK_Compound:
+ return getDerived().TraverseConceptExprRequirement(
+ cast<concepts::ExprRequirement>(R));
+ case concepts::Requirement::RK_Nested:
+ return getDerived().TraverseConceptNestedRequirement(
+ cast<concepts::NestedRequirement>(R));
+ }
+}
+
+template <typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseConceptReferenceHelper(
+ const ConceptReference &C) {
+ TRY_TO(TraverseNestedNameSpecifierLoc(C.getNestedNameSpecifierLoc()));
+ TRY_TO(TraverseDeclarationNameInfo(C.getConceptNameInfo()));
+ if (C.hasExplicitTemplateArgs())
+ TRY_TO(TraverseTemplateArgumentLocsHelper(
+ C.getTemplateArgsAsWritten()->getTemplateArgs(),
+ C.getTemplateArgsAsWritten()->NumTemplateArgs));
+ return true;
+}
+
+template <typename Derived>
bool RecursiveASTVisitor<Derived>::dataTraverseNode(Stmt *S,
DataRecursionQueue *Queue) {
// Top switch stmt: dispatch to TraverseFooStmt for each concrete FooStmt.
#undef DISPATCH_STMT
template <typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseConceptTypeRequirement(
+ concepts::TypeRequirement *R) {
+ if (R->isSubstitutionFailure())
+ return true;
+ return getDerived().TraverseTypeLoc(R->getType()->getTypeLoc());
+}
+
+template <typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseConceptExprRequirement(
+ concepts::ExprRequirement *R) {
+ if (!R->isExprSubstitutionFailure())
+ TRY_TO(TraverseStmt(R->getExpr()));
+ auto &RetReq = R->getReturnTypeRequirement();
+ if (RetReq.isTypeConstraint()) {
+ if (getDerived().shouldVisitImplicitCode()) {
+ TRY_TO(TraverseTemplateParameterListHelper(
+ RetReq.getTypeConstraintTemplateParameterList()));
+ } else {
+ // Template parameter list is implicit, visit constraint directly.
+ TRY_TO(TraverseTypeConstraint(RetReq.getTypeConstraint()));
+ }
+ }
+ return true;
+}
+
+template <typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseConceptNestedRequirement(
+ concepts::NestedRequirement *R) {
+ if (!R->isSubstitutionFailure())
+ return getDerived().TraverseStmt(R->getConstraintExpr());
+ return true;
+}
+
+template <typename Derived>
bool RecursiveASTVisitor<Derived>::PostVisitStmt(Stmt *S) {
// In pre-order traversal mode, each Traverse##STMT method is responsible for
// calling WalkUpFrom. Therefore, if the user overrides Traverse##STMT and
DEF_TRAVERSE_TYPE(AutoType, {
TRY_TO(TraverseType(T->getDeducedType()));
if (T->isConstrained()) {
- TRY_TO(TraverseDecl(T->getTypeConstraintConcept()));
TRY_TO(TraverseTemplateArguments(T->getArgs(), T->getNumArgs()));
}
})
template <typename Derived>
bool RecursiveASTVisitor<Derived>::TraverseTemplateTypeParamDeclConstraints(
const TemplateTypeParmDecl *D) {
- if (const auto *TC = D->getTypeConstraint()) {
- if (Expr *IDC = TC->getImmediatelyDeclaredConstraint()) {
- TRY_TO(TraverseStmt(IDC));
- } else {
- // Avoid traversing the ConceptReference in the TypeCosntraint
- // if we have an immediately-declared-constraint, otherwise
- // we'll end up visiting the concept and the arguments in
- // the TC twice.
- TRY_TO(TraverseConceptReference(*TC));
- }
- }
+ if (const auto *TC = D->getTypeConstraint())
+ TRY_TO(TraverseTypeConstraint(TC));
return true;
}
return true;
}
-template<typename Derived>
-bool RecursiveASTVisitor<Derived>::TraverseConceptReference(
- const ConceptReference &C) {
- TRY_TO(TraverseNestedNameSpecifierLoc(C.getNestedNameSpecifierLoc()));
- TRY_TO(TraverseDeclarationNameInfo(C.getConceptNameInfo()));
- if (C.hasExplicitTemplateArgs())
- TRY_TO(TraverseTemplateArgumentLocsHelper(
- C.getTemplateArgsAsWritten()->getTemplateArgs(),
- C.getTemplateArgsAsWritten()->NumTemplateArgs));
- return true;
-}
-
template <typename Derived>
bool RecursiveASTVisitor<Derived>::TraverseObjCProtocolLoc(
ObjCProtocolLoc ProtocolLoc) {
}
})
-DEF_TRAVERSE_STMT(ConceptSpecializationExpr, {
- TRY_TO(TraverseConceptReference(*S));
-})
+DEF_TRAVERSE_STMT(ConceptSpecializationExpr,
+ { TRY_TO(TraverseConceptReferenceHelper(*S)); })
DEF_TRAVERSE_STMT(RequiresExpr, {
TRY_TO(TraverseDecl(S->getBody()));
for (ParmVarDecl *Parm : S->getLocalParameters())
TRY_TO(TraverseDecl(Parm));
for (concepts::Requirement *Req : S->getRequirements())
- if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req)) {
- if (!TypeReq->isSubstitutionFailure())
- TRY_TO(TraverseTypeLoc(TypeReq->getType()->getTypeLoc()));
- } else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req)) {
- if (!ExprReq->isExprSubstitutionFailure())
- TRY_TO(TraverseStmt(ExprReq->getExpr()));
- auto &RetReq = ExprReq->getReturnTypeRequirement();
- if (RetReq.isTypeConstraint()) {
- TRY_TO(TraverseStmt(
- RetReq.getTypeConstraint()->getImmediatelyDeclaredConstraint()));
- }
- } else {
- auto *NestedReq = cast<concepts::NestedRequirement>(Req);
- if (!NestedReq->isSubstitutionFailure())
- TRY_TO(TraverseStmt(NestedReq->getConstraintExpr()));
- }
+ TRY_TO(TraverseConceptRequirement(Req));
})
// These literals (all of them) do not need any action.
//===----------------------------------------------------------------------===//
#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;
++ConceptSpecializationExprsVisited;
return true;
}
- bool TraverseConceptReference(const ConceptReference &R) {
- ++ConceptReferencesTraversed;
- return true;
+ bool TraverseTypeConstraint(const TypeConstraint *C) {
+ ++TypeConstraintsTraversed;
+ return ExpectedLocationVisitor::TraverseTypeConstraint(C);
+ }
+ bool TraverseConceptRequirement(concepts::Requirement *R) {
+ ++ConceptRequirementsTraversed;
+ return ExpectedLocationVisitor::TraverseConceptRequirement(R);
}
+ bool shouldVisitImplicitCode() { return ShouldVisitImplicitCode; }
+
int ConceptSpecializationExprsVisited = 0;
- int ConceptReferencesTraversed = 0;
+ int TypeConstraintsTraversed = 0;
+ int ConceptRequirementsTraversed = 0;
+ bool ShouldVisitImplicitCode = false;
};
-TEST(RecursiveASTVisitor, ConstrainedParameter) {
+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 visit the "Fooable T" template parameter's TypeConstraint's
- // ImmediatelyDeclaredConstraint, which is a ConceptSpecializationExpr.
+ // Check that we traverse the "Fooable T" template parameter's
+ // TypeConstraint's ImmediatelyDeclaredConstraint, which is a
+ // ConceptSpecializationExpr.
EXPECT_EQ(1, Visitor.ConceptSpecializationExprsVisited);
- // There are two ConceptReference objects in the AST: the base subobject
- // of the ConceptSpecializationExpr, and the base subobject of the
- // TypeConstraint itself. To avoid traversing the concept and arguments
- // multiple times, we only traverse one.
- EXPECT_EQ(1, Visitor.ConceptReferencesTraversed);
+ // Also check we traversed the TypeConstraint that produced the expr.
+ EXPECT_EQ(1, Visitor.TypeConstraintsTraversed);
+
+ 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);
+
+ 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);
+}
+
+struct VisitDeclOnlyOnce : ExpectedLocationVisitor<VisitDeclOnlyOnce> {
+ bool VisitConceptDecl(ConceptDecl *D) {
+ ++ConceptDeclsVisited;
+ return true;
+ }
+
+ bool VisitAutoType(AutoType *) {
+ ++AutoTypeVisited;
+ return true;
+ }
+ bool VisitAutoTypeLoc(AutoTypeLoc) {
+ ++AutoTypeLocVisited;
+ 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;
+};
+
+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);
}
} // end anonymous namespace