This revision was automatically updated to reflect the committed changes.
Closed by commit rG79889691430d: Added tests for RecursiveASTVisitor for AST 
nodes that are special cased (authored by gribozavr).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D82875/new/

https://reviews.llvm.org/D82875

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

Index: clang/unittests/Tooling/RecursiveASTVisitorTests/Callbacks.cpp
===================================================================
--- clang/unittests/Tooling/RecursiveASTVisitorTests/Callbacks.cpp
+++ clang/unittests/Tooling/RecursiveASTVisitorTests/Callbacks.cpp
@@ -41,6 +41,11 @@
     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 @@
 )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 @@
   };
 
   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 @@
       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 @@
       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 @@
   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 @@
       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 @@
 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 @@
   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"));
 }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to