[clang] 7988969 - Added tests for RecursiveASTVisitor for AST nodes that are special cased

Dmitri Gribenko via cfe-commits cfe-commits at lists.llvm.org
Fri Jul 3 04:05:30 PDT 2020


Author: Dmitri Gribenko
Date: 2020-07-03T13:03:18+02:00
New Revision: 79889691430d8e76e908706170102a8b46432a07

URL: https://github.com/llvm/llvm-project/commit/79889691430d8e76e908706170102a8b46432a07
DIFF: https://github.com/llvm/llvm-project/commit/79889691430d8e76e908706170102a8b46432a07.diff

LOG: Added tests for RecursiveASTVisitor for AST nodes that are special cased

Summary:
RecursiveASTVisitor has special code for handling operator AST nodes,
specifically, unary, binary, and compound assignment operators. In this
change I'm adding tests for operator AST nodes that follow the existing
pattern of tests for the CallExpr node (an AST node that triggers the
common code path).

Reviewers: ymandel, eduucaldas

Reviewed By: ymandel, eduucaldas

Subscribers: gribozavr2, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D82875

Added: 
    

Modified: 
    clang/unittests/Tooling/RecursiveASTVisitorTests/Callbacks.cpp

Removed: 
    


################################################################################
diff  --git a/clang/unittests/Tooling/RecursiveASTVisitorTests/Callbacks.cpp b/clang/unittests/Tooling/RecursiveASTVisitorTests/Callbacks.cpp
index 0eb9a43ecb14..66aa1d763833 100644
--- a/clang/unittests/Tooling/RecursiveASTVisitorTests/Callbacks.cpp
+++ b/clang/unittests/Tooling/RecursiveASTVisitorTests/Callbacks.cpp
@@ -41,6 +41,11 @@ class RecordingVisitorBase : public TestVisitor<Derived> {
     if (IntegerLiteral *IL = dyn_cast<IntegerLiteral>(S)) {
       return (ClassName + "(" + IL->getValue().toString(10, false) + ")").str();
     }
+    if (UnaryOperator *UO = dyn_cast<UnaryOperator>(S)) {
+      return (ClassName + "(" + UnaryOperator::getOpcodeStr(UO->getOpcode()) +
+              ")")
+          .str();
+    }
     if (BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) {
       return (ClassName + "(" + BinaryOperator::getOpcodeStr(BO->getOpcode()) +
               ")")
@@ -390,15 +395,15 @@ WalkUpFromStmt CompoundStmt
 )txt"));
 }
 
-TEST(RecursiveASTVisitor, StmtCallbacks_TraverseBinaryOperator) {
+TEST(RecursiveASTVisitor, StmtCallbacks_TraverseUnaryOperator) {
   class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
   public:
     RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
         : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
 
-    bool TraverseBinaryOperator(BinaryOperator *BO) {
-      recordCallback(__func__, BO, [&]() {
-        RecordingVisitorBase::TraverseBinaryOperator(BO);
+    bool TraverseUnaryOperator(UnaryOperator *UO) {
+      recordCallback(__func__, UO, [&]() {
+        RecordingVisitorBase::TraverseUnaryOperator(UO);
       });
       return true;
     }
@@ -411,27 +416,24 @@ TEST(RecursiveASTVisitor, StmtCallbacks_TraverseBinaryOperator) {
   };
 
   StringRef Code = R"cpp(
-void add(int, int);
 void test() {
   1;
-  2 + 3;
-  add(4, 5);
+  -2;
+  3;
 }
 )cpp";
 
+  // TraverseUnaryOperator is not called because RecursiveASTVisitor treats
+  // individual operators as subclasses, for which it calls their Traverse
+  // methods.
   EXPECT_TRUE(visitorCallbackLogEqual(
       RecordingVisitor(ShouldTraversePostOrder::No), Code,
       R"txt(
 WalkUpFromStmt CompoundStmt
 WalkUpFromStmt IntegerLiteral(1)
-WalkUpFromStmt BinaryOperator(+)
+WalkUpFromStmt UnaryOperator(-)
 WalkUpFromStmt IntegerLiteral(2)
 WalkUpFromStmt IntegerLiteral(3)
-WalkUpFromStmt CallExpr(add)
-WalkUpFromStmt ImplicitCastExpr
-WalkUpFromStmt DeclRefExpr(add)
-WalkUpFromStmt IntegerLiteral(4)
-WalkUpFromStmt IntegerLiteral(5)
 )txt"));
 
   EXPECT_TRUE(visitorCallbackLogEqual(
@@ -439,27 +441,22 @@ WalkUpFromStmt IntegerLiteral(5)
       R"txt(
 WalkUpFromStmt IntegerLiteral(1)
 WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromStmt UnaryOperator(-)
 WalkUpFromStmt IntegerLiteral(3)
-WalkUpFromStmt BinaryOperator(+)
-WalkUpFromStmt DeclRefExpr(add)
-WalkUpFromStmt ImplicitCastExpr
-WalkUpFromStmt IntegerLiteral(4)
-WalkUpFromStmt IntegerLiteral(5)
-WalkUpFromStmt CallExpr(add)
 WalkUpFromStmt CompoundStmt
 )txt"));
 }
 
 TEST(RecursiveASTVisitor,
-     StmtCallbacks_TraverseBinaryOperator_WalkUpFromBinaryOperator) {
+     StmtCallbacks_TraverseUnaryOperator_WalkUpFromUnaryOperator) {
   class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
   public:
     RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
         : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
 
-    bool TraverseBinaryOperator(BinaryOperator *BO) {
-      recordCallback(__func__, BO, [&]() {
-        RecordingVisitorBase::TraverseBinaryOperator(BO);
+    bool TraverseUnaryOperator(UnaryOperator *UO) {
+      recordCallback(__func__, UO, [&]() {
+        RecordingVisitorBase::TraverseUnaryOperator(UO);
       });
       return true;
     }
@@ -476,49 +473,38 @@ TEST(RecursiveASTVisitor,
       return true;
     }
 
-    bool WalkUpFromBinaryOperator(BinaryOperator *BO) {
-      recordCallback(__func__, BO, [&]() {
-        RecordingVisitorBase::WalkUpFromBinaryOperator(BO);
+    bool WalkUpFromUnaryOperator(UnaryOperator *UO) {
+      recordCallback(__func__, UO, [&]() {
+        RecordingVisitorBase::WalkUpFromUnaryOperator(UO);
       });
       return true;
     }
   };
 
   StringRef Code = R"cpp(
-void add(int, int);
 void test() {
   1;
-  2 + 3;
-  add(4, 5);
+  -2;
+  3;
 }
 )cpp";
 
-  // FIXME: It is arguably a bug in RecursiveASTVisitor that
-  // WalkUpFromBinaryOperator is called, but TraverseBinaryOperator is not
-  // called.
+  // TraverseUnaryOperator is not called because RecursiveASTVisitor treats
+  // individual operators as subclasses, for which it calls their Traverse
+  // methods.
   EXPECT_TRUE(visitorCallbackLogEqual(
       RecordingVisitor(ShouldTraversePostOrder::No), Code,
       R"txt(
 WalkUpFromStmt CompoundStmt
 WalkUpFromExpr IntegerLiteral(1)
   WalkUpFromStmt IntegerLiteral(1)
-WalkUpFromBinaryOperator BinaryOperator(+)
-  WalkUpFromExpr BinaryOperator(+)
-    WalkUpFromStmt BinaryOperator(+)
+WalkUpFromUnaryOperator UnaryOperator(-)
+  WalkUpFromExpr UnaryOperator(-)
+    WalkUpFromStmt UnaryOperator(-)
 WalkUpFromExpr IntegerLiteral(2)
   WalkUpFromStmt IntegerLiteral(2)
 WalkUpFromExpr IntegerLiteral(3)
   WalkUpFromStmt IntegerLiteral(3)
-WalkUpFromExpr CallExpr(add)
-  WalkUpFromStmt CallExpr(add)
-WalkUpFromExpr ImplicitCastExpr
-  WalkUpFromStmt ImplicitCastExpr
-WalkUpFromExpr DeclRefExpr(add)
-  WalkUpFromStmt DeclRefExpr(add)
-WalkUpFromExpr IntegerLiteral(4)
-  WalkUpFromStmt IntegerLiteral(4)
-WalkUpFromExpr IntegerLiteral(5)
-  WalkUpFromStmt IntegerLiteral(5)
 )txt"));
 
   EXPECT_TRUE(visitorCallbackLogEqual(
@@ -528,26 +514,16 @@ WalkUpFromExpr IntegerLiteral(1)
   WalkUpFromStmt IntegerLiteral(1)
 WalkUpFromExpr IntegerLiteral(2)
   WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromUnaryOperator UnaryOperator(-)
+  WalkUpFromExpr UnaryOperator(-)
+    WalkUpFromStmt UnaryOperator(-)
 WalkUpFromExpr IntegerLiteral(3)
   WalkUpFromStmt IntegerLiteral(3)
-WalkUpFromBinaryOperator BinaryOperator(+)
-  WalkUpFromExpr BinaryOperator(+)
-    WalkUpFromStmt BinaryOperator(+)
-WalkUpFromExpr DeclRefExpr(add)
-  WalkUpFromStmt DeclRefExpr(add)
-WalkUpFromExpr ImplicitCastExpr
-  WalkUpFromStmt ImplicitCastExpr
-WalkUpFromExpr IntegerLiteral(4)
-  WalkUpFromStmt IntegerLiteral(4)
-WalkUpFromExpr IntegerLiteral(5)
-  WalkUpFromStmt IntegerLiteral(5)
-WalkUpFromExpr CallExpr(add)
-  WalkUpFromStmt CallExpr(add)
 WalkUpFromStmt CompoundStmt
 )txt"));
 }
 
-TEST(RecursiveASTVisitor, StmtCallbacks_WalkUpFromBinaryOperator) {
+TEST(RecursiveASTVisitor, StmtCallbacks_WalkUpFromUnaryOperator) {
   class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
   public:
     RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
@@ -565,20 +541,19 @@ TEST(RecursiveASTVisitor, StmtCallbacks_WalkUpFromBinaryOperator) {
       return true;
     }
 
-    bool WalkUpFromBinaryOperator(BinaryOperator *BO) {
-      recordCallback(__func__, BO, [&]() {
-        RecordingVisitorBase::WalkUpFromBinaryOperator(BO);
+    bool WalkUpFromUnaryOperator(UnaryOperator *UO) {
+      recordCallback(__func__, UO, [&]() {
+        RecordingVisitorBase::WalkUpFromUnaryOperator(UO);
       });
       return true;
     }
   };
 
   StringRef Code = R"cpp(
-void add(int, int);
 void test() {
   1;
-  2 + 3;
-  add(4, 5);
+  -2;
+  3;
 }
 )cpp";
 
@@ -588,23 +563,13 @@ void test() {
 WalkUpFromStmt CompoundStmt
 WalkUpFromExpr IntegerLiteral(1)
   WalkUpFromStmt IntegerLiteral(1)
-WalkUpFromBinaryOperator BinaryOperator(+)
-  WalkUpFromExpr BinaryOperator(+)
-    WalkUpFromStmt BinaryOperator(+)
+WalkUpFromUnaryOperator UnaryOperator(-)
+  WalkUpFromExpr UnaryOperator(-)
+    WalkUpFromStmt UnaryOperator(-)
 WalkUpFromExpr IntegerLiteral(2)
   WalkUpFromStmt IntegerLiteral(2)
 WalkUpFromExpr IntegerLiteral(3)
   WalkUpFromStmt IntegerLiteral(3)
-WalkUpFromExpr CallExpr(add)
-  WalkUpFromStmt CallExpr(add)
-WalkUpFromExpr ImplicitCastExpr
-  WalkUpFromStmt ImplicitCastExpr
-WalkUpFromExpr DeclRefExpr(add)
-  WalkUpFromStmt DeclRefExpr(add)
-WalkUpFromExpr IntegerLiteral(4)
-  WalkUpFromStmt IntegerLiteral(4)
-WalkUpFromExpr IntegerLiteral(5)
-  WalkUpFromStmt IntegerLiteral(5)
 )txt"));
 
   EXPECT_TRUE(visitorCallbackLogEqual(
@@ -614,21 +579,1002 @@ WalkUpFromExpr IntegerLiteral(1)
   WalkUpFromStmt IntegerLiteral(1)
 WalkUpFromExpr IntegerLiteral(2)
   WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromUnaryOperator UnaryOperator(-)
+  WalkUpFromExpr UnaryOperator(-)
+    WalkUpFromStmt UnaryOperator(-)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTVisitor, StmtCallbacks_TraverseUnaryMinus) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseUnaryMinus(UnaryOperator *UO) {
+      recordCallback(__func__, UO,
+                     [&]() { RecordingVisitorBase::TraverseUnaryMinus(UO); });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test() {
+  1;
+  -2;
+  3;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromStmt IntegerLiteral(1)
+TraverseUnaryMinus UnaryOperator(-)
+  WalkUpFromStmt UnaryOperator(-)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromStmt IntegerLiteral(3)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromStmt IntegerLiteral(1)
+TraverseUnaryMinus UnaryOperator(-)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromStmt UnaryOperator(-)
+WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTVisitor,
+     StmtCallbacks_TraverseUnaryMinus_WalkUpFromUnaryMinus) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseUnaryMinus(UnaryOperator *UO) {
+      recordCallback(__func__, UO,
+                     [&]() { RecordingVisitorBase::TraverseUnaryMinus(UO); });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromUnaryMinus(UnaryOperator *UO) {
+      recordCallback(__func__, UO,
+                     [&]() { RecordingVisitorBase::WalkUpFromUnaryMinus(UO); });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test() {
+  1;
+  -2;
+  3;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+TraverseUnaryMinus UnaryOperator(-)
+  WalkUpFromUnaryMinus UnaryOperator(-)
+    WalkUpFromExpr UnaryOperator(-)
+      WalkUpFromStmt UnaryOperator(-)
+  WalkUpFromExpr IntegerLiteral(2)
+    WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+)txt"));
+
+  // FIXME: The following log should include a call to WalkUpFromUnaryMinus.
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+TraverseUnaryMinus UnaryOperator(-)
+  WalkUpFromExpr IntegerLiteral(2)
+    WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr UnaryOperator(-)
+  WalkUpFromStmt UnaryOperator(-)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTVisitor, StmtCallbacks_WalkUpFromUnaryMinus) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromUnaryMinus(UnaryOperator *UO) {
+      recordCallback(__func__, UO,
+                     [&]() { RecordingVisitorBase::WalkUpFromUnaryMinus(UO); });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test() {
+  1;
+  -2;
+  3;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromUnaryMinus UnaryOperator(-)
+  WalkUpFromExpr UnaryOperator(-)
+    WalkUpFromStmt UnaryOperator(-)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+)txt"));
+
+  // FIXME: The following log should include a call to WalkUpFromUnaryMinus.
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr UnaryOperator(-)
+  WalkUpFromStmt UnaryOperator(-)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTVisitor, StmtCallbacks_TraverseBinaryOperator) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseBinaryOperator(BinaryOperator *BO) {
+      recordCallback(__func__, BO, [&]() {
+        RecordingVisitorBase::TraverseBinaryOperator(BO);
+      });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test() {
+  1;
+  2 + 3;
+  4;
+}
+)cpp";
+
+  // TraverseBinaryOperator is not called because RecursiveASTVisitor treats
+  // individual operators as subclasses, for which it calls their Traverse
+  // methods.
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromStmt BinaryOperator(+)
+WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt IntegerLiteral(4)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt BinaryOperator(+)
+WalkUpFromStmt IntegerLiteral(4)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTVisitor,
+     StmtCallbacks_TraverseBinaryOperator_WalkUpFromBinaryOperator) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseBinaryOperator(BinaryOperator *BO) {
+      recordCallback(__func__, BO, [&]() {
+        RecordingVisitorBase::TraverseBinaryOperator(BO);
+      });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromBinaryOperator(BinaryOperator *BO) {
+      recordCallback(__func__, BO, [&]() {
+        RecordingVisitorBase::WalkUpFromBinaryOperator(BO);
+      });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test() {
+  1;
+  2 + 3;
+  4;
+}
+)cpp";
+
+  // TraverseBinaryOperator is not called because RecursiveASTVisitor treats
+  // individual operators as subclasses, for which it calls their Traverse
+  // methods.
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromBinaryOperator BinaryOperator(+)
+  WalkUpFromExpr BinaryOperator(+)
+    WalkUpFromStmt BinaryOperator(+)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromExpr IntegerLiteral(4)
+  WalkUpFromStmt IntegerLiteral(4)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromBinaryOperator BinaryOperator(+)
+  WalkUpFromExpr BinaryOperator(+)
+    WalkUpFromStmt BinaryOperator(+)
+WalkUpFromExpr IntegerLiteral(4)
+  WalkUpFromStmt IntegerLiteral(4)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTVisitor, StmtCallbacks_WalkUpFromBinaryOperator) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromBinaryOperator(BinaryOperator *BO) {
+      recordCallback(__func__, BO, [&]() {
+        RecordingVisitorBase::WalkUpFromBinaryOperator(BO);
+      });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test() {
+  1;
+  2 + 3;
+  4;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromBinaryOperator BinaryOperator(+)
+  WalkUpFromExpr BinaryOperator(+)
+    WalkUpFromStmt BinaryOperator(+)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromExpr IntegerLiteral(4)
+  WalkUpFromStmt IntegerLiteral(4)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromBinaryOperator BinaryOperator(+)
+  WalkUpFromExpr BinaryOperator(+)
+    WalkUpFromStmt BinaryOperator(+)
+WalkUpFromExpr IntegerLiteral(4)
+  WalkUpFromStmt IntegerLiteral(4)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTVisitor, StmtCallbacks_TraverseBinAdd) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseBinAdd(BinaryOperator *BO) {
+      recordCallback(__func__, BO,
+                     [&]() { RecordingVisitorBase::TraverseBinAdd(BO); });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test() {
+  1;
+  2 + 3;
+  4;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromStmt IntegerLiteral(1)
+TraverseBinAdd BinaryOperator(+)
+  WalkUpFromStmt BinaryOperator(+)
+  WalkUpFromStmt IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt IntegerLiteral(4)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromStmt IntegerLiteral(1)
+TraverseBinAdd BinaryOperator(+)
+  WalkUpFromStmt IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt BinaryOperator(+)
+WalkUpFromStmt IntegerLiteral(4)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTVisitor, StmtCallbacks_TraverseBinAdd_WalkUpFromBinAdd) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseBinAdd(BinaryOperator *BO) {
+      recordCallback(__func__, BO,
+                     [&]() { RecordingVisitorBase::TraverseBinAdd(BO); });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromBinAdd(BinaryOperator *BO) {
+      recordCallback(__func__, BO,
+                     [&]() { RecordingVisitorBase::WalkUpFromBinAdd(BO); });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test() {
+  1;
+  2 + 3;
+  4;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+TraverseBinAdd BinaryOperator(+)
+  WalkUpFromBinAdd BinaryOperator(+)
+    WalkUpFromExpr BinaryOperator(+)
+      WalkUpFromStmt BinaryOperator(+)
+  WalkUpFromExpr IntegerLiteral(2)
+    WalkUpFromStmt IntegerLiteral(2)
+  WalkUpFromExpr IntegerLiteral(3)
+    WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromExpr IntegerLiteral(4)
+  WalkUpFromStmt IntegerLiteral(4)
+)txt"));
+
+  // FIXME: The following log should include a call to WalkUpFromBinAdd.
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+TraverseBinAdd BinaryOperator(+)
+  WalkUpFromExpr IntegerLiteral(2)
+    WalkUpFromStmt IntegerLiteral(2)
+  WalkUpFromExpr IntegerLiteral(3)
+    WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromExpr BinaryOperator(+)
+  WalkUpFromStmt BinaryOperator(+)
+WalkUpFromExpr IntegerLiteral(4)
+  WalkUpFromStmt IntegerLiteral(4)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTVisitor, StmtCallbacks_WalkUpFromBinAdd) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromBinAdd(BinaryOperator *BO) {
+      recordCallback(__func__, BO,
+                     [&]() { RecordingVisitorBase::WalkUpFromBinAdd(BO); });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test() {
+  1;
+  2 + 3;
+  4;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromBinAdd BinaryOperator(+)
+  WalkUpFromExpr BinaryOperator(+)
+    WalkUpFromStmt BinaryOperator(+)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromExpr IntegerLiteral(4)
+  WalkUpFromStmt IntegerLiteral(4)
+)txt"));
+
+  // FIXME: The following log should include a call to WalkUpFromBinAdd.
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromExpr BinaryOperator(+)
+  WalkUpFromStmt BinaryOperator(+)
+WalkUpFromExpr IntegerLiteral(4)
+  WalkUpFromStmt IntegerLiteral(4)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTVisitor, StmtCallbacks_TraverseCompoundAssignOperator) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseCompoundAssignOperator(CompoundAssignOperator *CAO) {
+      recordCallback(__func__, CAO, [&]() {
+        RecordingVisitorBase::TraverseCompoundAssignOperator(CAO);
+      });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test(int a) {
+  1;
+  a += 2;
+  3;
+}
+)cpp";
+
+  // TraverseCompoundAssignOperator is not called because RecursiveASTVisitor
+  // treats individual operators as subclasses, for which it calls their
+  // Traverse methods.
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromStmt CompoundAssignOperator(+=)
+WalkUpFromStmt DeclRefExpr(a)
+WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromStmt IntegerLiteral(3)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromStmt DeclRefExpr(a)
+WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromStmt CompoundAssignOperator(+=)
+WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(
+    RecursiveASTVisitor,
+    StmtCallbacks_TraverseCompoundAssignOperator_WalkUpFromCompoundAssignOperator) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseCompoundAssignOperator(CompoundAssignOperator *CAO) {
+      recordCallback(__func__, CAO, [&]() {
+        RecordingVisitorBase::TraverseCompoundAssignOperator(CAO);
+      });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromCompoundAssignOperator(CompoundAssignOperator *CAO) {
+      recordCallback(__func__, CAO, [&]() {
+        RecordingVisitorBase::WalkUpFromCompoundAssignOperator(CAO);
+      });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test(int a) {
+  1;
+  a += 2;
+  3;
+}
+)cpp";
+
+  // TraverseCompoundAssignOperator is not called because RecursiveASTVisitor
+  // treats individual operators as subclasses, for which it calls their
+  // Traverse methods.
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromCompoundAssignOperator CompoundAssignOperator(+=)
+  WalkUpFromExpr CompoundAssignOperator(+=)
+    WalkUpFromStmt CompoundAssignOperator(+=)
+WalkUpFromExpr DeclRefExpr(a)
+  WalkUpFromStmt DeclRefExpr(a)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromExpr DeclRefExpr(a)
+  WalkUpFromStmt DeclRefExpr(a)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromCompoundAssignOperator CompoundAssignOperator(+=)
+  WalkUpFromExpr CompoundAssignOperator(+=)
+    WalkUpFromStmt CompoundAssignOperator(+=)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTVisitor, StmtCallbacks_WalkUpFromCompoundAssignOperator) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromCompoundAssignOperator(CompoundAssignOperator *CAO) {
+      recordCallback(__func__, CAO, [&]() {
+        RecordingVisitorBase::WalkUpFromCompoundAssignOperator(CAO);
+      });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test(int a) {
+  1;
+  a += 2;
+  3;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromCompoundAssignOperator CompoundAssignOperator(+=)
+  WalkUpFromExpr CompoundAssignOperator(+=)
+    WalkUpFromStmt CompoundAssignOperator(+=)
+WalkUpFromExpr DeclRefExpr(a)
+  WalkUpFromStmt DeclRefExpr(a)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromExpr DeclRefExpr(a)
+  WalkUpFromStmt DeclRefExpr(a)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromCompoundAssignOperator CompoundAssignOperator(+=)
+  WalkUpFromExpr CompoundAssignOperator(+=)
+    WalkUpFromStmt CompoundAssignOperator(+=)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTVisitor, StmtCallbacks_TraverseBinAddAssign) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseBinAddAssign(CompoundAssignOperator *CAO) {
+      recordCallback(__func__, CAO, [&]() {
+        RecordingVisitorBase::TraverseBinAddAssign(CAO);
+      });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test(int a) {
+  1;
+  a += 2;
+  3;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromStmt IntegerLiteral(1)
+TraverseBinAddAssign CompoundAssignOperator(+=)
+  WalkUpFromStmt CompoundAssignOperator(+=)
+  WalkUpFromStmt DeclRefExpr(a)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromStmt IntegerLiteral(3)
+)txt"));
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromStmt IntegerLiteral(1)
+TraverseBinAddAssign CompoundAssignOperator(+=)
+  WalkUpFromStmt DeclRefExpr(a)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromStmt CompoundAssignOperator(+=)
+WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTVisitor,
+     StmtCallbacks_TraverseBinAddAssign_WalkUpFromBinAddAssign) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool TraverseBinAddAssign(CompoundAssignOperator *CAO) {
+      recordCallback(__func__, CAO, [&]() {
+        RecordingVisitorBase::TraverseBinAddAssign(CAO);
+      });
+      return true;
+    }
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromBinAddAssign(CompoundAssignOperator *CAO) {
+      recordCallback(__func__, CAO, [&]() {
+        RecordingVisitorBase::WalkUpFromBinAddAssign(CAO);
+      });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test(int a) {
+  1;
+  a += 2;
+  3;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+TraverseBinAddAssign CompoundAssignOperator(+=)
+  WalkUpFromBinAddAssign CompoundAssignOperator(+=)
+    WalkUpFromExpr CompoundAssignOperator(+=)
+      WalkUpFromStmt CompoundAssignOperator(+=)
+  WalkUpFromExpr DeclRefExpr(a)
+    WalkUpFromStmt DeclRefExpr(a)
+  WalkUpFromExpr IntegerLiteral(2)
+    WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+)txt"));
+
+  // FIXME: The following log should include a call to WalkUpFromBinAddAssign.
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+TraverseBinAddAssign CompoundAssignOperator(+=)
+  WalkUpFromExpr DeclRefExpr(a)
+    WalkUpFromStmt DeclRefExpr(a)
+  WalkUpFromExpr IntegerLiteral(2)
+    WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr CompoundAssignOperator(+=)
+  WalkUpFromStmt CompoundAssignOperator(+=)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+WalkUpFromStmt CompoundStmt
+)txt"));
+}
+
+TEST(RecursiveASTVisitor, StmtCallbacks_WalkUpFromBinAddAssign) {
+  class RecordingVisitor : public RecordingVisitorBase<RecordingVisitor> {
+  public:
+    RecordingVisitor(ShouldTraversePostOrder ShouldTraversePostOrderValue)
+        : RecordingVisitorBase(ShouldTraversePostOrderValue) {}
+
+    bool WalkUpFromStmt(Stmt *S) {
+      recordCallback(__func__, S,
+                     [&]() { RecordingVisitorBase::WalkUpFromStmt(S); });
+      return true;
+    }
+
+    bool WalkUpFromExpr(Expr *E) {
+      recordCallback(__func__, E,
+                     [&]() { RecordingVisitorBase::WalkUpFromExpr(E); });
+      return true;
+    }
+
+    bool WalkUpFromBinAddAssign(CompoundAssignOperator *CAO) {
+      recordCallback(__func__, CAO, [&]() {
+        RecordingVisitorBase::WalkUpFromBinAddAssign(CAO);
+      });
+      return true;
+    }
+  };
+
+  StringRef Code = R"cpp(
+void test(int a) {
+  1;
+  a += 2;
+  3;
+}
+)cpp";
+
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::No), Code,
+      R"txt(
+WalkUpFromStmt CompoundStmt
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromBinAddAssign CompoundAssignOperator(+=)
+  WalkUpFromExpr CompoundAssignOperator(+=)
+    WalkUpFromStmt CompoundAssignOperator(+=)
+WalkUpFromExpr DeclRefExpr(a)
+  WalkUpFromStmt DeclRefExpr(a)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr IntegerLiteral(3)
+  WalkUpFromStmt IntegerLiteral(3)
+)txt"));
+
+  // FIXME: The following log should include a call to WalkUpFromBinAddAssign.
+  EXPECT_TRUE(visitorCallbackLogEqual(
+      RecordingVisitor(ShouldTraversePostOrder::Yes), Code,
+      R"txt(
+WalkUpFromExpr IntegerLiteral(1)
+  WalkUpFromStmt IntegerLiteral(1)
+WalkUpFromExpr DeclRefExpr(a)
+  WalkUpFromStmt DeclRefExpr(a)
+WalkUpFromExpr IntegerLiteral(2)
+  WalkUpFromStmt IntegerLiteral(2)
+WalkUpFromExpr CompoundAssignOperator(+=)
+  WalkUpFromStmt CompoundAssignOperator(+=)
 WalkUpFromExpr IntegerLiteral(3)
   WalkUpFromStmt IntegerLiteral(3)
-WalkUpFromBinaryOperator BinaryOperator(+)
-  WalkUpFromExpr BinaryOperator(+)
-    WalkUpFromStmt BinaryOperator(+)
-WalkUpFromExpr DeclRefExpr(add)
-  WalkUpFromStmt DeclRefExpr(add)
-WalkUpFromExpr ImplicitCastExpr
-  WalkUpFromStmt ImplicitCastExpr
-WalkUpFromExpr IntegerLiteral(4)
-  WalkUpFromStmt IntegerLiteral(4)
-WalkUpFromExpr IntegerLiteral(5)
-  WalkUpFromStmt IntegerLiteral(5)
-WalkUpFromExpr CallExpr(add)
-  WalkUpFromStmt CallExpr(add)
 WalkUpFromStmt CompoundStmt
 )txt"));
 }


        


More information about the cfe-commits mailing list