================ @@ -0,0 +1,452 @@ +//=== DynamicRecursiveASTVisitor.cpp - Dynamic AST Visitor Implementation -===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements DynamicRecursiveASTVisitor in terms of the CRTP-based +// RecursiveASTVisitor. +// +//===----------------------------------------------------------------------===// +#include "clang/AST/DynamicRecursiveASTVisitor.h" +#include "clang/AST/RecursiveASTVisitor.h" + +using namespace clang; + +// The implementation of DRAV deserves some explanation: +// +// We want to implement DynamicRecursiveASTVisitor without having to inherit or +// reference RecursiveASTVisitor in any way in the header: if we instantiate +// RAV in the header, then every user of (or rather every file that uses) DRAV +// still has to instantiate a RAV, which gets us nowhere. Moreover, even just +// including RecursiveASTVisitor.h would probably cause some amount of slowdown +// because we'd have to parse a huge template. For these reasons, the fact that +// DRAV is implemented using a RAV is solely an implementation detail. +// +// As for the implementation itself, DRAV by default acts exactly like a RAV +// that overrides none of RAV's functions. There are two parts to this: +// +// 1. Any function in DRAV has to act like the corresponding function in RAV, +// unless overridden by a derived class, of course. +// +// 2. Any call to a function by the RAV implementation that DRAV allows to be +// overridden must be transformed to a virtual call on the user-provided +// DRAV object: if some function in RAV calls e.g. TraverseCallExpr() +// during traversal, then the derived class's TraverseCallExpr() must be +// called (provided it overrides TraverseCallExpr()). +// +// The 'Impl' class is a helper that connects the two implementations; it is +// a wrapper around a reference to a DRAV that is itself a RecursiveASTVisitor. +// It overrides every function in RAV *that is virtual in DRAV* to perform a +// virtual call on its DRAV reference. This accomplishes point 2 above. +// +// Point 1 is accomplished by, first, having the base class implementation of +// each of the virtual functions construct an Impl object (which is actually +// just a no-op), passing in itself so that any virtual calls use the right +// vtable. Secondly, it then calls RAV's implementation of that same function +// *on Impl* (using a qualified call so that we actually call into the RAV +// implementation instead of Impl's version of that same function); this way, +// we both execute RAV's implementation for this function only and ensure that +// calls to subsequent functions call into Impl via CRTP (and Impl then calls +// back into DRAV and so on). +// +// While this ends up constructing a lot of Impl instances (almost one per +// function call), this doesn't really matter since Impl just holds a single +// pointer, and everything in this file should get inlined into all the DRAV +// functions here anyway. +// +//===----------------------------------------------------------------------===// +// +// The following illustrates how a call to an (overridden) function is actually +// resolved: given some class 'Derived' that derives from DRAV and overrides +// TraverseStmt(), if we are traversing some AST, and TraverseStmt() is called +// by the RAV implementation, the following happens: +// +// 1. Impl::TraverseStmt() overrides RAV::TraverseStmt() via CRTP, so the +// former is called. +// +// 2. Impl::TraverseStmt() performs a virtual call to the visitor (which is +// an instance to Derived), so Derived::TraverseStmt() is called. +// +// End result: Derived::TraverseStmt() is executed. +// +// Suppose some other function, e.g. TraverseCallExpr(), which is NOT overridden +// by Derived is called, we get: +// +// 1. Impl::TraverseCallExpr() overrides RAV::TraverseCallExpr() via CRTP, +// so the former is called. +// +// 2. Impl::TraverseCallExpr() performs a virtual call, but since Derived +// does not override that function, DRAV::TraverseCallExpr() is called. +// +// 3. DRAV::TraverseCallExpr() creates a new instance of Impl, passing in +// itself (this doesn't change that the pointer is an instance of Derived); +// it then calls RAV::TraverseCallExpr() on the Impl object, which actually +// ends up executing RAV's implementation because we used a qualified +// function call. +// +// End result: RAV::TraverseCallExpr() is executed, +namespace { +struct Impl : RecursiveASTVisitor<Impl> { + DynamicRecursiveASTVisitor &Visitor; + Impl(DynamicRecursiveASTVisitor &Visitor) : Visitor(Visitor) {} + + bool shouldVisitTemplateInstantiations() const { + return Visitor.ShouldVisitTemplateInstantiations; + } + + bool shouldWalkTypesOfTypeLocs() const { + return Visitor.ShouldWalkTypesOfTypeLocs; + } + + bool shouldVisitImplicitCode() const { + return Visitor.ShouldVisitImplicitCode; + } + + bool shouldVisitLambdaBody() const { return Visitor.ShouldVisitLambdaBody; } + + // Supporting post-order would be very hard because of quirks of the + // RAV implementation that only work with CRTP. It also is only used + // by less than 5 visitors in the entire code base. + bool shouldTraversePostOrder() const { return false; } + + bool TraverseAST(ASTContext &AST) { return Visitor.TraverseAST(AST); } + bool TraverseAttr(Attr *At) { return Visitor.TraverseAttr(At); } + bool TraverseDecl(Decl *D) { return Visitor.TraverseDecl(D); } + bool TraverseType(QualType T) { return Visitor.TraverseType(T); } + bool TraverseTypeLoc(TypeLoc TL) { return Visitor.TraverseTypeLoc(TL); } + bool TraverseStmt(Stmt *S) { return Visitor.TraverseStmt(S); } + + bool TraverseConstructorInitializer(CXXCtorInitializer *Init) { + return Visitor.TraverseConstructorInitializer(Init); + } + + bool TraverseTemplateArgument(const TemplateArgument &Arg) { + return Visitor.TraverseTemplateArgument(Arg); + } + + bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) { + return Visitor.TraverseTemplateArgumentLoc(ArgLoc); + } + + bool TraverseTemplateName(TemplateName Template) { + return Visitor.TraverseTemplateName(Template); + } + + bool TraverseObjCProtocolLoc(ObjCProtocolLoc ProtocolLoc) { + return Visitor.TraverseObjCProtocolLoc(ProtocolLoc); + } + + bool TraverseTypeConstraint(const TypeConstraint *C) { + return Visitor.TraverseTypeConstraint(C); + } + bool TraverseConceptRequirement(concepts::Requirement *R) { + return Visitor.TraverseConceptRequirement(R); + } + bool TraverseConceptTypeRequirement(concepts::TypeRequirement *R) { + return Visitor.TraverseConceptTypeRequirement(R); + } + bool TraverseConceptExprRequirement(concepts::ExprRequirement *R) { + return Visitor.TraverseConceptExprRequirement(R); + } + bool TraverseConceptNestedRequirement(concepts::NestedRequirement *R) { + return Visitor.TraverseConceptNestedRequirement(R); + } + + bool TraverseConceptReference(ConceptReference *CR) { + return Visitor.TraverseConceptReference(CR); + } + + bool TraverseCXXBaseSpecifier(const CXXBaseSpecifier &Base) { + return Visitor.TraverseCXXBaseSpecifier(Base); + } + + bool TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo) { + return Visitor.TraverseDeclarationNameInfo(NameInfo); + } + + bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C, + Expr *Init) { + return Visitor.TraverseLambdaCapture(LE, C, Init); + } + + bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS) { + return Visitor.TraverseNestedNameSpecifier(NNS); + } + + bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { + return Visitor.TraverseNestedNameSpecifierLoc(NNS); + } + + bool VisitConceptReference(ConceptReference *CR) { + return Visitor.VisitConceptReference(CR); + } + + bool dataTraverseStmtPre(Stmt *S) { return Visitor.dataTraverseStmtPre(S); } + bool dataTraverseStmtPost(Stmt *S) { return Visitor.dataTraverseStmtPost(S); } + + // TraverseStmt() always passes in a queue, so we have no choice but to + // accept it as a parameter here. + bool dataTraverseNode(Stmt *S, DataRecursionQueue * = nullptr) { + // But since don't support postorder traversal, we don't need it, so + // simply discard it here. This way, derived classes don't need to worry + // about including it as a parameter that they never use. + return Visitor.dataTraverseNode(S); ---------------- Sirraide wrote:
Post-order traversal is already disabled because `shouldTraversePostOrder()` in `Impl` is hard-coded to return `false`. We can’t assert that the queue is null here because the RAV’s `TraverseStmt()` always passes a non-null queue to this. https://github.com/llvm/llvm-project/pull/110040 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits