[clang] Fix visitation of ConceptSpecializationExpr in constrained-parameter

Summary: RecursiveASTVisitor needs to traverse TypeConstraint::ImmediatelyDeclaredConstraint

Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D84136
This commit is contained in:
Nathan Ridge 2020-07-26 22:45:24 -04:00
parent 15673d748a
commit 00d7b7d014
4 changed files with 79 additions and 2 deletions

View File

@ -442,6 +442,28 @@ TEST_F(TargetDeclTest, Concept) {
)cpp";
EXPECT_DECLS("ConceptSpecializationExpr",
{"template <typename T> concept Fooable = true;"});
// constrained-parameter
Code = R"cpp(
template <typename T>
concept Fooable = true;
template <[[Fooable]] T>
void bar(T t);
)cpp";
EXPECT_DECLS("ConceptSpecializationExpr",
{"template <typename T> concept Fooable = true;"});
// partial-concept-id
Code = R"cpp(
template <typename T, typename U>
concept Fooable = true;
template <[[Fooable]]<int> T>
void bar(T t);
)cpp";
EXPECT_DECLS("ConceptSpecializationExpr",
{"template <typename T, typename U> concept Fooable = true;"});
}
TEST_F(TargetDeclTest, FunctionTemplate) {

View File

@ -1777,8 +1777,17 @@ DEF_TRAVERSE_DECL(TemplateTypeParmDecl, {
// D is the "T" in something like "template<typename T> class vector;"
if (D->getTypeForDecl())
TRY_TO(TraverseType(QualType(D->getTypeForDecl(), 0)));
if (const auto *TC = D->getTypeConstraint())
TRY_TO(TraverseConceptReference(*TC));
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 (D->hasDefaultArgument() && !D->defaultArgumentWasInherited())
TRY_TO(TraverseTypeLoc(D->getDefaultArgumentInfo()->getTypeLoc()));
})

View File

@ -22,6 +22,7 @@ add_clang_unittest(ToolingTests
RecursiveASTVisitorTests/Attr.cpp
RecursiveASTVisitorTests/Callbacks.cpp
RecursiveASTVisitorTests/Class.cpp
RecursiveASTVisitorTests/Concept.cpp
RecursiveASTVisitorTests/ConstructExpr.cpp
RecursiveASTVisitorTests/CXXBoolLiteralExpr.cpp
RecursiveASTVisitorTests/CXXMemberCall.cpp

View File

@ -0,0 +1,45 @@
//===- 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/ExprConcepts.h"
using namespace clang;
namespace {
struct ConceptVisitor : ExpectedLocationVisitor<ConceptVisitor> {
bool VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) {
++ConceptSpecializationExprsVisited;
return true;
}
bool TraverseConceptReference(const ConceptReference &R) {
++ConceptReferencesTraversed;
return true;
}
int ConceptSpecializationExprsVisited = 0;
int ConceptReferencesTraversed = 0;
};
TEST(RecursiveASTVisitor, ConstrainedParameter) {
ConceptVisitor Visitor;
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.
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);
}
} // end anonymous namespace