Skip to content

Commit 2b833d4

Browse files
committed
[AST] Improve traversal of concepts and concept requirements
- Do not traverse concept decl inside `AutoType`. We only traverse declaration and definitions, not references to a declaration. - Do not visit implicit AST node the relevant traversal mode. - Add traversal extension points for concept requirements. - Renamed `TraverseConceptReference` to mark as helper to share the code. Having an extension point there seems confusing given that there are many concept refences in the AST that do not call the helper. Those are `AutoType`, `AutoTypeLoc` and constraint requirements. Only clangd code requires an update. There are no use-cases for concept requirement traversals yet, but I added them in the earlier version of the patch and decided to keep them for completeness. Reviewed By: sammccall Differential Revision: https://reviews.llvm.org/D124532
1 parent be656df commit 2b833d4

File tree

5 files changed

+210
-69
lines changed

5 files changed

+210
-69
lines changed

clang-tools-extra/clangd/Selection.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "AST.h"
1111
#include "support/Logger.h"
1212
#include "support/Trace.h"
13+
#include "clang/AST/ASTConcept.h"
1314
#include "clang/AST/ASTTypeTraits.h"
1415
#include "clang/AST/Decl.h"
1516
#include "clang/AST/DeclCXX.h"
@@ -709,6 +710,15 @@ class SelectionVisitor : public RecursiveASTVisitor<SelectionVisitor> {
709710
bool TraversePseudoObjectExpr(PseudoObjectExpr *E) {
710711
return traverseNode(E, [&] { return TraverseStmt(E->getSyntacticForm()); });
711712
}
713+
bool TraverseTypeConstraint(const TypeConstraint *C) {
714+
if (auto *E = C->getImmediatelyDeclaredConstraint()) {
715+
// Technically this expression is 'implicit' and not traversed by the RAV.
716+
// However, the range is correct, so we visit expression to avoid adding
717+
// an extra kind to 'DynTypeNode' that hold 'TypeConstraint'.
718+
return TraverseStmt(E);
719+
}
720+
return Base::TraverseTypeConstraint(C);
721+
}
712722

713723
private:
714724
using Base = RecursiveASTVisitor<SelectionVisitor>;

clang-tools-extra/clangd/unittests/XRefsTests.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2085,6 +2085,19 @@ TEST(FindReferences, ConceptsWithinAST) {
20852085
checkFindRefs(Code);
20862086
}
20872087

2088+
TEST(FindReferences, ConceptReq) {
2089+
constexpr llvm::StringLiteral Code = R"cpp(
2090+
template <class T>
2091+
concept $def[[IsSmal^l]] = sizeof(T) <= 8;
2092+
2093+
template <class T>
2094+
concept IsSmallPtr = requires(T x) {
2095+
{ *x } -> [[IsSmal^l]];
2096+
};
2097+
)cpp";
2098+
checkFindRefs(Code);
2099+
}
2100+
20882101
TEST(FindReferences, RequiresExprParameters) {
20892102
constexpr llvm::StringLiteral Code = R"cpp(
20902103
template <class T>

clang/include/clang/AST/RecursiveASTVisitor.h

Lines changed: 100 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,19 @@
1313
#ifndef LLVM_CLANG_AST_RECURSIVEASTVISITOR_H
1414
#define LLVM_CLANG_AST_RECURSIVEASTVISITOR_H
1515

16+
#include "clang/AST/ASTConcept.h"
1617
#include "clang/AST/Attr.h"
1718
#include "clang/AST/Decl.h"
18-
#include "clang/AST/DeclarationName.h"
1919
#include "clang/AST/DeclBase.h"
2020
#include "clang/AST/DeclCXX.h"
2121
#include "clang/AST/DeclFriend.h"
2222
#include "clang/AST/DeclObjC.h"
2323
#include "clang/AST/DeclOpenMP.h"
2424
#include "clang/AST/DeclTemplate.h"
25+
#include "clang/AST/DeclarationName.h"
2526
#include "clang/AST/Expr.h"
26-
#include "clang/AST/ExprConcepts.h"
2727
#include "clang/AST/ExprCXX.h"
28+
#include "clang/AST/ExprConcepts.h"
2829
#include "clang/AST/ExprObjC.h"
2930
#include "clang/AST/ExprOpenMP.h"
3031
#include "clang/AST/LambdaCapture.h"
@@ -319,11 +320,6 @@ template <typename Derived> class RecursiveASTVisitor {
319320
bool TraverseSynOrSemInitListExpr(InitListExpr *S,
320321
DataRecursionQueue *Queue = nullptr);
321322

322-
/// Recursively visit a reference to a concept with potential arguments.
323-
///
324-
/// \returns false if the visitation was terminated early, true otherwise.
325-
bool TraverseConceptReference(const ConceptReference &C);
326-
327323
/// Recursively visit an Objective-C protocol reference with location
328324
/// information.
329325
///
@@ -475,11 +471,21 @@ template <typename Derived> class RecursiveASTVisitor {
475471
DEF_TRAVERSE_TMPL_INST(Function)
476472
#undef DEF_TRAVERSE_TMPL_INST
477473

474+
bool TraverseTypeConstraint(const TypeConstraint *C);
475+
476+
bool TraverseConceptRequirement(concepts::Requirement *R);
477+
bool TraverseConceptTypeRequirement(concepts::TypeRequirement *R);
478+
bool TraverseConceptExprRequirement(concepts::ExprRequirement *R);
479+
bool TraverseConceptNestedRequirement(concepts::NestedRequirement *R);
480+
478481
bool dataTraverseNode(Stmt *S, DataRecursionQueue *Queue);
479482

480483
private:
481484
// These are helper methods used by more than one Traverse* method.
482485
bool TraverseTemplateParameterListHelper(TemplateParameterList *TPL);
486+
/// Traverses the qualifier, name and template arguments of a concept
487+
/// reference.
488+
bool TraverseConceptReferenceHelper(const ConceptReference &C);
483489

484490
// Traverses template parameter lists of either a DeclaratorDecl or TagDecl.
485491
template <typename T>
@@ -511,6 +517,54 @@ template <typename Derived> class RecursiveASTVisitor {
511517
bool PostVisitStmt(Stmt *S);
512518
};
513519

520+
template <typename Derived>
521+
bool RecursiveASTVisitor<Derived>::TraverseTypeConstraint(
522+
const TypeConstraint *C) {
523+
if (!getDerived().shouldVisitImplicitCode()) {
524+
TRY_TO(TraverseConceptReferenceHelper(*C));
525+
return true;
526+
}
527+
if (Expr *IDC = C->getImmediatelyDeclaredConstraint()) {
528+
TRY_TO(TraverseStmt(IDC));
529+
} else {
530+
// Avoid traversing the ConceptReference in the TypeConstraint
531+
// if we have an immediately-declared-constraint, otherwise
532+
// we'll end up visiting the concept and the arguments in
533+
// the TC twice.
534+
TRY_TO(TraverseConceptReferenceHelper(*C));
535+
}
536+
return true;
537+
}
538+
539+
template <typename Derived>
540+
bool RecursiveASTVisitor<Derived>::TraverseConceptRequirement(
541+
concepts::Requirement *R) {
542+
switch (R->getKind()) {
543+
case concepts::Requirement::RK_Type:
544+
return getDerived().TraverseConceptTypeRequirement(
545+
cast<concepts::TypeRequirement>(R));
546+
case concepts::Requirement::RK_Simple:
547+
case concepts::Requirement::RK_Compound:
548+
return getDerived().TraverseConceptExprRequirement(
549+
cast<concepts::ExprRequirement>(R));
550+
case concepts::Requirement::RK_Nested:
551+
return getDerived().TraverseConceptNestedRequirement(
552+
cast<concepts::NestedRequirement>(R));
553+
}
554+
}
555+
556+
template <typename Derived>
557+
bool RecursiveASTVisitor<Derived>::TraverseConceptReferenceHelper(
558+
const ConceptReference &C) {
559+
TRY_TO(TraverseNestedNameSpecifierLoc(C.getNestedNameSpecifierLoc()));
560+
TRY_TO(TraverseDeclarationNameInfo(C.getConceptNameInfo()));
561+
if (C.hasExplicitTemplateArgs())
562+
TRY_TO(TraverseTemplateArgumentLocsHelper(
563+
C.getTemplateArgsAsWritten()->getTemplateArgs(),
564+
C.getTemplateArgsAsWritten()->NumTemplateArgs));
565+
return true;
566+
}
567+
514568
template <typename Derived>
515569
bool RecursiveASTVisitor<Derived>::dataTraverseNode(Stmt *S,
516570
DataRecursionQueue *Queue) {
@@ -530,6 +584,40 @@ bool RecursiveASTVisitor<Derived>::dataTraverseNode(Stmt *S,
530584

531585
#undef DISPATCH_STMT
532586

587+
template <typename Derived>
588+
bool RecursiveASTVisitor<Derived>::TraverseConceptTypeRequirement(
589+
concepts::TypeRequirement *R) {
590+
if (R->isSubstitutionFailure())
591+
return true;
592+
return getDerived().TraverseTypeLoc(R->getType()->getTypeLoc());
593+
}
594+
595+
template <typename Derived>
596+
bool RecursiveASTVisitor<Derived>::TraverseConceptExprRequirement(
597+
concepts::ExprRequirement *R) {
598+
if (!R->isExprSubstitutionFailure())
599+
TRY_TO(TraverseStmt(R->getExpr()));
600+
auto &RetReq = R->getReturnTypeRequirement();
601+
if (RetReq.isTypeConstraint()) {
602+
if (getDerived().shouldVisitImplicitCode()) {
603+
TRY_TO(TraverseTemplateParameterListHelper(
604+
RetReq.getTypeConstraintTemplateParameterList()));
605+
} else {
606+
// Template parameter list is implicit, visit constraint directly.
607+
TRY_TO(TraverseTypeConstraint(RetReq.getTypeConstraint()));
608+
}
609+
}
610+
return true;
611+
}
612+
613+
template <typename Derived>
614+
bool RecursiveASTVisitor<Derived>::TraverseConceptNestedRequirement(
615+
concepts::NestedRequirement *R) {
616+
if (!R->isSubstitutionFailure())
617+
return getDerived().TraverseStmt(R->getConstraintExpr());
618+
return true;
619+
}
620+
533621
template <typename Derived>
534622
bool RecursiveASTVisitor<Derived>::PostVisitStmt(Stmt *S) {
535623
// In pre-order traversal mode, each Traverse##STMT method is responsible for
@@ -1007,7 +1095,6 @@ DEF_TRAVERSE_TYPE(UnaryTransformType, {
10071095
DEF_TRAVERSE_TYPE(AutoType, {
10081096
TRY_TO(TraverseType(T->getDeducedType()));
10091097
if (T->isConstrained()) {
1010-
TRY_TO(TraverseDecl(T->getTypeConstraintConcept()));
10111098
TRY_TO(TraverseTemplateArguments(T->getArgs(), T->getNumArgs()));
10121099
}
10131100
})
@@ -1838,17 +1925,8 @@ DEF_TRAVERSE_DECL(BuiltinTemplateDecl, {
18381925
template <typename Derived>
18391926
bool RecursiveASTVisitor<Derived>::TraverseTemplateTypeParamDeclConstraints(
18401927
const TemplateTypeParmDecl *D) {
1841-
if (const auto *TC = D->getTypeConstraint()) {
1842-
if (Expr *IDC = TC->getImmediatelyDeclaredConstraint()) {
1843-
TRY_TO(TraverseStmt(IDC));
1844-
} else {
1845-
// Avoid traversing the ConceptReference in the TypeCosntraint
1846-
// if we have an immediately-declared-constraint, otherwise
1847-
// we'll end up visiting the concept and the arguments in
1848-
// the TC twice.
1849-
TRY_TO(TraverseConceptReference(*TC));
1850-
}
1851-
}
1928+
if (const auto *TC = D->getTypeConstraint())
1929+
TRY_TO(TraverseTypeConstraint(TC));
18521930
return true;
18531931
}
18541932

@@ -2435,18 +2513,6 @@ bool RecursiveASTVisitor<Derived>::TraverseSynOrSemInitListExpr(
24352513
return true;
24362514
}
24372515

2438-
template<typename Derived>
2439-
bool RecursiveASTVisitor<Derived>::TraverseConceptReference(
2440-
const ConceptReference &C) {
2441-
TRY_TO(TraverseNestedNameSpecifierLoc(C.getNestedNameSpecifierLoc()));
2442-
TRY_TO(TraverseDeclarationNameInfo(C.getConceptNameInfo()));
2443-
if (C.hasExplicitTemplateArgs())
2444-
TRY_TO(TraverseTemplateArgumentLocsHelper(
2445-
C.getTemplateArgsAsWritten()->getTemplateArgs(),
2446-
C.getTemplateArgsAsWritten()->NumTemplateArgs));
2447-
return true;
2448-
}
2449-
24502516
template <typename Derived>
24512517
bool RecursiveASTVisitor<Derived>::TraverseObjCProtocolLoc(
24522518
ObjCProtocolLoc ProtocolLoc) {
@@ -2825,31 +2891,15 @@ DEF_TRAVERSE_STMT(CoyieldExpr, {
28252891
}
28262892
})
28272893

2828-
DEF_TRAVERSE_STMT(ConceptSpecializationExpr, {
2829-
TRY_TO(TraverseConceptReference(*S));
2830-
})
2894+
DEF_TRAVERSE_STMT(ConceptSpecializationExpr,
2895+
{ TRY_TO(TraverseConceptReferenceHelper(*S)); })
28312896

28322897
DEF_TRAVERSE_STMT(RequiresExpr, {
28332898
TRY_TO(TraverseDecl(S->getBody()));
28342899
for (ParmVarDecl *Parm : S->getLocalParameters())
28352900
TRY_TO(TraverseDecl(Parm));
28362901
for (concepts::Requirement *Req : S->getRequirements())
2837-
if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req)) {
2838-
if (!TypeReq->isSubstitutionFailure())
2839-
TRY_TO(TraverseTypeLoc(TypeReq->getType()->getTypeLoc()));
2840-
} else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req)) {
2841-
if (!ExprReq->isExprSubstitutionFailure())
2842-
TRY_TO(TraverseStmt(ExprReq->getExpr()));
2843-
auto &RetReq = ExprReq->getReturnTypeRequirement();
2844-
if (RetReq.isTypeConstraint()) {
2845-
TRY_TO(TraverseStmt(
2846-
RetReq.getTypeConstraint()->getImmediatelyDeclaredConstraint()));
2847-
}
2848-
} else {
2849-
auto *NestedReq = cast<concepts::NestedRequirement>(Req);
2850-
if (!NestedReq->isSubstitutionFailure())
2851-
TRY_TO(TraverseStmt(NestedReq->getConstraintExpr()));
2852-
}
2902+
TRY_TO(TraverseConceptRequirement(Req));
28532903
})
28542904

28552905
// These literals (all of them) do not need any action.

clang/lib/Index/IndexBody.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -483,13 +483,10 @@ class BodyIndexer : public RecursiveASTVisitor<BodyIndexer> {
483483
return true;
484484
}
485485

486-
bool VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) {
487-
// This handles references in return type requirements of RequiresExpr.
488-
// E.g. `requires (T x) { {*x} -> ConceptRef }`
489-
if (auto *C = D->getTypeConstraint())
490-
IndexCtx.handleReference(C->getNamedConcept(), C->getConceptNameLoc(),
491-
Parent, ParentDC);
492-
return true;
486+
bool TraverseTypeConstraint(const TypeConstraint *C) {
487+
IndexCtx.handleReference(C->getNamedConcept(), C->getConceptNameLoc(),
488+
Parent, ParentDC);
489+
return RecursiveASTVisitor::TraverseTypeConstraint(C);
493490
}
494491
};
495492

0 commit comments

Comments
 (0)