This affects overloaded operators, which are represented by a
CXXOperatorCallExpr whose first child is always a DeclRefExpr referring to the
operator. For infix, postfix and call operators we want the first argument
to be traversed before the operator.
Details
Diff Detail
- Repository
- rL LLVM
Event Timeline
include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h | ||
---|---|---|
69 ↗ | (On Diff #112843) | Why exactly is this template struct needed? |
75 ↗ | (On Diff #112843) | I don't think you need this since getDerived in RecursiveASTVisitor is already public. |
include/clang/AST/RecursiveASTVisitor.h | ||
3212 ↗ | (On Diff #112843) | Getting rid of undef is not ideal. You might want to extract these macros into one .def file that's included by both RecursiveASTVisitor.h and the LexicallyOrdered one. |
include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h | ||
---|---|---|
75 ↗ | (On Diff #112843) | has_same_member_pointer and getDerived are used by the TRAVERSE_STMT macro, which should be the right one to use here. |
include/clang/AST/RecursiveASTVisitor.h | ||
3212 ↗ | (On Diff #112843) | Ok, I will do that |
Ah I see,
then you should something completely different (forget the header).
Please extract this code into a protected function in RecursiveASTVisitor:
for (Stmt *SubStmt : Children) if (!TRAVERSE_STMT_BASE(Stmt, Stmt, SubStmt, Queue)) return false;
And reuse it in this class. Then you won't have to use any macros or related template magic here.
This works as well I think
include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h | ||
---|---|---|
63 ↗ | (On Diff #112885) | Do you still need the using here? |
The previous version didn't call TraverseDecl of the derived class, this is fixed now.
The public getDerived.TraverseStmt() does not accept a DataRecursionQueue, so this also could not be used (I think)
I used the wrapper TraverseStmtBase, which should behave exactly as the method that originally traverses CXXOperatorCallExpr
include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h | ||
---|---|---|
63 ↗ | (On Diff #112885) | removed it now as it's only used once |
include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h | ||
---|---|---|
149 ↗ | (On Diff #112889) | For ++ and -- you can see whether its prefix or postfix by looking at the number of arguments. If there's one argument, then ++ and -- are prefix. Otherwise, they're postfix. |
The fact that TraverseCXXOperatorCallExpr can't call its super TraverseCXXOperatorCallExpr makes the current solution kind of broken. The super implementation of TraverseCXXOperatorCallExpr is responsible for dispatch to WalkUp##STMT functions which actually call VisitCXXOperatorCallExpr which our current visitor will never call because of the custom "override". I would like to suggest an alternative approach that I think solves the problem more "elegantly":
Modify the DEF_TRAVERSE_STMT macro in RecursiveASTVisitor.h call to a wrapper function to get the children:
class RecursiveASTVisitor { public: Stmt::child_range getStmtChildren(Stmt *S) { return S->children(); } ... #define DEF_TRAVERSE_STMT(STMT, CODE) \ template <typename Derived> \ bool RecursiveASTVisitor<Derived>::Traverse##STMT( \ STMT *S, DataRecursionQueue *Queue) { \ bool ShouldVisitChildren = true; \ bool ReturnValue = true; \ if (!getDerived().shouldTraversePostOrder()) \ TRY_TO(WalkUpFrom##STMT(S)); \ { CODE; } \ if (ShouldVisitChildren) { \ for (Stmt *SubStmt : getDerived().getStmtChildren(S)) { \ // The only change is on this line. TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(SubStmt); \ } \ } \ if (!Queue && ReturnValue && getDerived().shouldTraversePostOrder()) \ TRY_TO(WalkUpFrom##STMT(S)); \ return ReturnValue; \ } ... };
Then you could provide an "override" in the LexicallyOrderedRecursiveASTVisitor that adjusts children just for CXXOperatorCallExpr. Something like this should work:
class LexicallyOrderedRecursiveASTVisitor { public: ... Stmt::child_range getStmtChildren(Stmt *S) { return S->children(); } SmallVector<Stmt *, 8> getStmtChildren(CXXOperatorCallExpr *CE) { SmallVector<Stmt *, 8> Children(CE->children()); bool Swap; // Switch the operator and the first operand for all infix and postfix // operations. switch (CE->getOperator()) { case OO_Arrow: case OO_Call: case OO_Subscript: Swap = true; break; case OO_PlusPlus: case OO_MinusMinus: // These are postfix unless there is exactly one argument. Swap = Children.size() != 2; break; default: Swap = CE->isInfixBinaryOp(); break; } if (Swap && Children.size() > 1) std::swap(Children[0], Children[1]); return Children; } ... };
WDYT?
Sorry about not seeing this earlier. Thanks for your patience!
Yeah, this seems to be the best solution for this. I think I ran into the same issue when working on the StmtDataCollector - basically there can only be two Traverse*, one in the Derived class and the other one needs to do all the magic with walking the specialisation hierarchy.