Repository: compiler-research/CppInterOp
Pull request #513: Added undo command for CppInterOp
Author: login: kr-2003
Created: 2025-03-03T11:44:10Z | Merged: 2025-03-24T14:12:00Z

# Description

Added ```Undo``` command for Clang-REPL. Reverts the last N operations performed by the interpreter.

## Type of change

- [x] New feature


## Testing
1. Tests in ```unittests/CppInterOp/FunctionReflectionTest.cpp```.
2. Demo - https://res.cloudinary.com/abhi9av/video/upload/v1740997292/wqgfbgqefrwpx2o7ko95.mp4


## Checklist

- [x] I have read the contribution guide recently



Diff:
diff --git a/include/clang/Interpreter/CppInterOp.h b/include/clang/Interpreter/CppInterOp.h
index 4f4f05c64..875d1227e 100644
--- a/include/clang/Interpreter/CppInterOp.h
+++ b/include/clang/Interpreter/CppInterOp.h
@@ -785,6 +785,11 @@ namespace Cpp {
                                    unsigned complete_line = 1U,
                                    unsigned complete_column = 1U);
 
+  /// Reverts the last N operations performed by the interpreter.
+  ///\param[in] N The number of operations to undo. Defaults to 1.
+  ///\returns 0 on success, non-zero on failure.
+  CPPINTEROP_API int Undo(unsigned N = 1);
+
 } // end namespace Cpp
 
 #endif // CPPINTEROP_CPPINTEROP_H
diff --git a/lib/Interpreter/CXCppInterOp.cpp b/lib/Interpreter/CXCppInterOp.cpp
index 2b5569915..0f4566016 100644
--- a/lib/Interpreter/CXCppInterOp.cpp
+++ b/lib/Interpreter/CXCppInterOp.cpp
@@ -306,7 +306,10 @@ TInterp_t clang_Interpreter_takeInterpreterAsPtr(CXInterpreter I) {
 
 enum CXErrorCode clang_Interpreter_undo(CXInterpreter I, unsigned int N) {
 #ifdef CPPINTEROP_USE_CLING
-  return CXError_Failure;
+  auto* interp = getInterpreter(I);
+  cling::Interpreter::PushTransactionRAII RAII(interp);
+  interp->unload(N);
+  return CXError_Success;
 #else
   return getInterpreter(I)->Undo(N) ? CXError_Failure : CXError_Success;
 #endif // CPPINTEROP_USE_CLING
diff --git a/lib/Interpreter/CppInterOp.cpp b/lib/Interpreter/CppInterOp.cpp
index 861a610be..2c00d79ba 100755
--- a/lib/Interpreter/CppInterOp.cpp
+++ b/lib/Interpreter/CppInterOp.cpp
@@ -3684,4 +3684,15 @@ namespace Cpp {
                          complete_column);
   }
 
+  int Undo(unsigned N) {
+#ifdef CPPINTEROP_USE_CLING
+    auto& I = getInterp();
+    cling::Interpreter::PushTransactionRAII RAII(&I);
+    I.unload(N);
+    return compat::Interpreter::kSuccess;
+#else
+    return getInterp().undo(N);
+#endif
+  }
+
   } // end namespace Cpp
diff --git a/lib/Interpreter/CppInterOpInterpreter.h b/lib/Interpreter/CppInterOpInterpreter.h
index e35840633..b5b8e7e96 100644
--- a/lib/Interpreter/CppInterOpInterpreter.h
+++ b/lib/Interpreter/CppInterOpInterpreter.h
@@ -429,6 +429,15 @@ class Interpreter {
     return ret; // TODO: Implement
   }
 
+  CompilationResult undo(unsigned N = 1) {
+    if (llvm::Error Err = Undo(N)) {
+      llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
+                                  "Failed to undo via ::undo");
+      return kFailure;
+    }
+    return kSuccess;
+  }
+
 }; // Interpreter
 } // namespace Cpp
 
diff --git a/unittests/CppInterOp/FunctionReflectionTest.cpp b/unittests/CppInterOp/FunctionReflectionTest.cpp
index a8c12ef52..42ab8cb6a 100644
--- a/unittests/CppInterOp/FunctionReflectionTest.cpp
+++ b/unittests/CppInterOp/FunctionReflectionTest.cpp
@@ -1615,3 +1615,30 @@ TEST(FunctionReflectionTest, Destruct) {
   clang_Interpreter_takeInterpreterAsPtr(I);
   clang_Interpreter_dispose(I);
 }
+
+TEST(FunctionReflectionTest, UndoTest) {
+#ifdef _WIN32
+  GTEST_SKIP() << "Disabled on Windows. Needs fixing.";
+#endif
+#ifdef EMSCRIPTEN
+  GTEST_SKIP() << "Test fails for Emscipten builds";
+#else
+  Cpp::CreateInterpreter();
+  EXPECT_EQ(Cpp::Process("int a = 5;"), 0);
+  EXPECT_EQ(Cpp::Process("int b = 10;"), 0);
+  EXPECT_EQ(Cpp::Process("int x = 5;"), 0);
+  Cpp::Undo();
+  EXPECT_NE(Cpp::Process("int y = x;"), 0);
+  EXPECT_EQ(Cpp::Process("int x = 10;"), 0);
+  EXPECT_EQ(Cpp::Process("int y = 10;"), 0);
+  Cpp::Undo(2);
+  EXPECT_EQ(Cpp::Process("int x = 20;"), 0);
+  EXPECT_EQ(Cpp::Process("int y = 20;"), 0);
+  int ret = Cpp::Undo(100);
+#ifdef CPPINTEROP_USE_CLING
+  EXPECT_EQ(ret, 0);
+#else
+  EXPECT_EQ(ret, 1);
+#endif
+#endif
+  }
\ No newline at end of file


Reviews:
user: login: Vipul-Cariappa
body: @anutosh491 asked me to look at this.

LGTM! If you can just update the test with the changes I specified.
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-03T12:02:28Z
user: login: mcbarton
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T12:10:39Z
user: login: anutosh491
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T12:14:02Z
user: login: anutosh491
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T12:15:25Z
user: login: vgvassilev
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T12:23:37Z
user: login: anutosh491
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T12:32:40Z
user: login: kr-2003
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T19:16:37Z
user: login: kr-2003
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T19:17:14Z
user: login: anutosh491
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-04T05:48:25Z
user: login: mcbarton
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-04T09:33:52Z
user: login: kr-2003
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T10:07:41Z
user: login: mcbarton
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-04T19:07:12Z
user: login: kr-2003
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T08:27:09Z
user: login: kr-2003
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T09:52:57Z
user: login: vgvassilev
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T11:11:36Z
user: login: anutosh491
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-07T12:21:15Z
user: login: anutosh491
body: I think this looks okay to me. 

Maybe the two lines that are not being covered (see comment below) can be addressed.

https://github.com/compiler-research/CppInterOp/pull/513#issuecomment-2694131812

This demands for a negation case (testing a case where undo fails)
Probably can be done through trying undo(1000) when only few operations have taken place. Let's try something relevant out and then move this in ?
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-07T12:26:45Z
user: login: aaronj0
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-13T15:28:46Z
user: login: mcbarton
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-17T14:34:45Z
user: login: mcbarton
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-17T14:36:46Z
user: login: vgvassilev
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T06:48:23Z
user: login: kr-2003
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T19:28:10Z
user: login: kr-2003
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T19:30:52Z
user: login: vgvassilev
body: Lgtm!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T12:41:40Z

Review comments:
diff_hunk: @@ -1251,3 +1251,17 @@ TEST(FunctionReflectionTest, Destruct) {
   output = testing::internal::GetCapturedStdout();
   EXPECT_EQ(output, "Destructor Executed");
 }
+
+TEST(FunctionReflectionTest, UndoTest) {
+  Cpp::CreateInterpreter();
+  std::string cerrs;
+  testing::internal::CaptureStderr();
+  Cpp::Process("int x = 5;");
+  cerrs = testing::internal::GetCapturedStderr();
+  EXPECT_STREQ(cerrs.c_str(), "");
+  Cpp::Undo();
+  testing::internal::CaptureStderr();
+  Cpp::Process("int x = 10;");
path: unittests/CppInterOp/FunctionReflectionTest.cpp
user: login: Vipul-Cariappa
body: Could you check if accessing `x` gives an error here?

Also, extend this test case with `Undo(n)` where `n > 1`.
created_at: 2025-03-03T12:00:22Z
author_association: COLLABORATOR
diff_hunk: @@ -3616,6 +3616,14 @@ namespace Cpp {
                          complete_column);
   }
 
-  int Undo(unsigned N) { return getInterp().undo(N); }
+  int Undo(unsigned N) {
+#ifdef CPPINTEROP_USE_CLING
path: lib/Interpreter/CppInterOp.cpp
user: login: mcbarton
body: @kr-2003 @anutosh491 Are you 100% sure undo is not supported through Cling?
created_at: 2025-03-03T12:10:32Z
author_association: COLLABORATOR
diff_hunk: @@ -3616,6 +3616,14 @@ namespace Cpp {
                          complete_column);
   }
 
-  int Undo(unsigned N) { return getInterp().undo(N); }
+  int Undo(unsigned N) {
+#ifdef CPPINTEROP_USE_CLING
path: lib/Interpreter/CppInterOp.cpp
user: login: anutosh491
body: Hmmm, I mean we're technically looking out for our use case here 

Cause

A) the C API is not having it 

https://github.com/compiler-research/CppInterOp/blob/7caf10b1ccae74e62600b1b5fe05dc4e71db93bb/lib/Interpreter/CXCppInterOp.cpp#L307-L313

B) cling has `unload` but I am not a 100% sure about how it is defined or if it is tested enough. I had opened this for the same 

https://github.com/root-project/cling/issues/544


created_at: 2025-03-03T12:14:02Z
author_association: COLLABORATOR
diff_hunk: @@ -3616,4 +3616,14 @@ namespace Cpp {
                          complete_column);
   }
 
+  int Undo(unsigned N) {
+#ifdef CPPINTEROP_USE_CLING
+    llvm::logAllUnhandledErrors(Undo(N), llvm::errs(),
path: lib/Interpreter/CppInterOp.cpp
user: login: anutosh491
body: Undo(N) looks wrong here :\
created_at: 2025-03-03T12:15:25Z
author_association: COLLABORATOR
diff_hunk: @@ -3616,6 +3616,14 @@ namespace Cpp {
                          complete_column);
   }
 
-  int Undo(unsigned N) { return getInterp().undo(N); }
+  int Undo(unsigned N) {
+#ifdef CPPINTEROP_USE_CLING
path: lib/Interpreter/CppInterOp.cpp
user: login: vgvassilev
body: It is supported by cling.
created_at: 2025-03-03T12:23:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3616,6 +3616,14 @@ namespace Cpp {
                          complete_column);
   }
 
-  int Undo(unsigned N) { return getInterp().undo(N); }
+  int Undo(unsigned N) {
+#ifdef CPPINTEROP_USE_CLING
path: lib/Interpreter/CppInterOp.cpp
user: login: anutosh491
body: Okay I guess it is the `unload` function we're talking about here. Not sure I saw an `undo` through cling.

@kr-2003 could you read about this and try integrating it ?
created_at: 2025-03-03T12:32:40Z
author_association: COLLABORATOR
diff_hunk: @@ -3616,6 +3616,14 @@ namespace Cpp {
                          complete_column);
   }
 
-  int Undo(unsigned N) { return getInterp().undo(N); }
+  int Undo(unsigned N) {
+#ifdef CPPINTEROP_USE_CLING
path: lib/Interpreter/CppInterOp.cpp
user: login: kr-2003
body: Done. Also added unload function for cling
created_at: 2025-03-03T19:16:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1251,3 +1251,17 @@ TEST(FunctionReflectionTest, Destruct) {
   output = testing::internal::GetCapturedStdout();
   EXPECT_EQ(output, "Destructor Executed");
 }
+
+TEST(FunctionReflectionTest, UndoTest) {
+  Cpp::CreateInterpreter();
+  std::string cerrs;
+  testing::internal::CaptureStderr();
+  Cpp::Process("int x = 5;");
+  cerrs = testing::internal::GetCapturedStderr();
+  EXPECT_STREQ(cerrs.c_str(), "");
+  Cpp::Undo();
+  testing::internal::CaptureStderr();
+  Cpp::Process("int x = 10;");
path: unittests/CppInterOp/FunctionReflectionTest.cpp
user: login: kr-2003
body: done
created_at: 2025-03-03T19:17:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3616,6 +3616,14 @@ namespace Cpp {
                          complete_column);
   }
 
-  int Undo(unsigned N) { return getInterp().undo(N); }
+  int Undo(unsigned N) {
+#ifdef CPPINTEROP_USE_CLING
path: lib/Interpreter/CppInterOp.cpp
user: login: anutosh491
body: Thank you !
created_at: 2025-03-04T05:48:24Z
author_association: COLLABORATOR
diff_hunk: @@ -3616,6 +3616,14 @@ namespace Cpp {
                          complete_column);
   }
 
-  int Undo(unsigned N) { return getInterp().undo(N); }
+  int Undo(unsigned N) {
+#ifdef CPPINTEROP_USE_CLING
path: lib/Interpreter/CppInterOp.cpp
user: login: mcbarton
body: I'm going to make a request. Can the undo feature in the c api be fixed as part of this PR?
created_at: 2025-03-04T09:33:52Z
author_association: COLLABORATOR
diff_hunk: @@ -3616,6 +3616,14 @@ namespace Cpp {
                          complete_column);
   }
 
-  int Undo(unsigned N) { return getInterp().undo(N); }
+  int Undo(unsigned N) {
+#ifdef CPPINTEROP_USE_CLING
path: lib/Interpreter/CppInterOp.cpp
user: login: kr-2003
body: cc @mcbarton  
added unload for cling through c-api too. can you review it?
created_at: 2025-03-04T10:07:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -306,7 +306,10 @@ TInterp_t clang_Interpreter_takeInterpreterAsPtr(CXInterpreter I) {
 
 enum CXErrorCode clang_Interpreter_undo(CXInterpreter I, unsigned int N) {
 #ifdef CPPINTEROP_USE_CLING
-  return CXError_Failure;
+  auto* interp = getInterpreter(I);
path: lib/Interpreter/CXCppInterOp.cpp
user: login: mcbarton
body: If using clang_repl you can return CXError_Failure if it fails. With cling and the c api currently as it stands you cannot. Can you fix this @kr-2003 
created_at: 2025-03-04T19:06:08Z
author_association: COLLABORATOR
diff_hunk: @@ -3616,4 +3616,15 @@ namespace Cpp {
                          complete_column);
   }
 
+  int Undo(unsigned N) {
+#ifdef CPPINTEROP_USE_CLING
+    auto& I = getInterp();
+    cling::Interpreter::PushTransactionRAII RAII(&I);
+    I.unload(N);
path: lib/Interpreter/CppInterOp.cpp
user: login: kr-2003
body: @vgvassilev How do I even check if unload function logs any errors, since its a void function? Should I use try-catch block(but it is disabled in the project) or use system level redirection of the output to stream using pipes(this might be an overkill for capturing)? unload function logs ```cling::errs()```, if it fails to run.
created_at: 2025-03-05T08:27:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3616,4 +3616,15 @@ namespace Cpp {
                          complete_column);
   }
 
+  int Undo(unsigned N) {
+#ifdef CPPINTEROP_USE_CLING
+    auto& I = getInterp();
+    cling::Interpreter::PushTransactionRAII RAII(&I);
+    I.unload(N);
path: lib/Interpreter/CppInterOp.cpp
user: login: kr-2003
body: @vgvassilev Could you please guide me on how to proceed here?

created_at: 2025-03-07T09:52:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3616,4 +3616,15 @@ namespace Cpp {
                          complete_column);
   }
 
+  int Undo(unsigned N) {
+#ifdef CPPINTEROP_USE_CLING
+    auto& I = getInterp();
+    cling::Interpreter::PushTransactionRAII RAII(&I);
+    I.unload(N);
path: lib/Interpreter/CppInterOp.cpp
user: login: vgvassilev
body: You should assume that the operation was successful.
created_at: 2025-03-07T11:11:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3616,4 +3616,15 @@ namespace Cpp {
                          complete_column);
   }
 
+  int Undo(unsigned N) {
+#ifdef CPPINTEROP_USE_CLING
+    auto& I = getInterp();
+    cling::Interpreter::PushTransactionRAII RAII(&I);
+    I.unload(N);
path: lib/Interpreter/CppInterOp.cpp
user: login: anutosh491
body: Hmmm, I think that's what is being done here https://github.com/compiler-research/CppInterOp/pull/513/files#diff-fb574ca9d2633ca33be71dfa5c0d9f437cfa98768f0e828d5eb789bfcfcef688R3624 so nothing to address here correct ?
created_at: 2025-03-07T12:21:15Z
author_association: COLLABORATOR
diff_hunk: @@ -1502,3 +1502,40 @@ TEST(FunctionReflectionTest, Destruct) {
   output = testing::internal::GetCapturedStdout();
   EXPECT_EQ(output, "Destructor Executed");
 }
+
+TEST(FunctionReflectionTest, UndoTest) {
+  Cpp::CreateInterpreter();
+  std::string cerrs;
+  testing::internal::CaptureStderr();
+  Cpp::Process("int x = 5;");
+  cerrs = testing::internal::GetCapturedStderr();
+  EXPECT_STREQ(cerrs.c_str(), "");
+  Cpp::Undo();
path: unittests/CppInterOp/FunctionReflectionTest.cpp
user: login: aaronj0
body: I believe unloading this first transaction here is problematic for destructors, and is unsupported by Cling, which can be seen in the error thrown in the logs. We should drop this call to `Cpp::Undo()`
created_at: 2025-03-13T13:44:34Z
author_association: COLLABORATOR
diff_hunk: @@ -1502,3 +1502,40 @@ TEST(FunctionReflectionTest, Destruct) {
   output = testing::internal::GetCapturedStdout();
   EXPECT_EQ(output, "Destructor Executed");
 }
+
+TEST(FunctionReflectionTest, UndoTest) {
+  Cpp::CreateInterpreter();
+  std::string cerrs;
+  testing::internal::CaptureStderr();
+  Cpp::Process("int x = 5;");
+  cerrs = testing::internal::GetCapturedStderr();
+  EXPECT_STREQ(cerrs.c_str(), "");
+  Cpp::Undo();
path: unittests/CppInterOp/FunctionReflectionTest.cpp
user: login: mcbarton
body: @kr-2003 can you address this review comment?
created_at: 2025-03-17T14:34:45Z
author_association: COLLABORATOR
diff_hunk: @@ -1520,3 +1520,45 @@ TEST(FunctionReflectionTest, Destruct) {
   output = testing::internal::GetCapturedStdout();
   EXPECT_EQ(output, "Destructor Executed");
 }
+
+TEST(FunctionReflectionTest, UndoTest) {
+#ifdef EMSCRIPTEN
+  GTEST_SKIP()
+      << "Currently not implemented while running clang-repl in the broswer";
path: unittests/CppInterOp/FunctionReflectionTest.cpp
user: login: mcbarton
body: @kr-2003 CppInterOp can be have cling as an interpreter too, so maybe change this to 'Test fails for Emscipten builds' like we have for other tests.
created_at: 2025-03-17T14:36:40Z
author_association: COLLABORATOR
diff_hunk: @@ -1517,3 +1517,45 @@ TEST(FunctionReflectionTest, Destruct) {
   output = testing::internal::GetCapturedStdout();
   EXPECT_EQ(output, "Destructor Executed");
 }
+
+TEST(FunctionReflectionTest, UndoTest) {
+#ifdef EMSCRIPTEN
+  GTEST_SKIP()
+      << "Test fails for Emscipten builds";
+#else
+  Cpp::CreateInterpreter();
+  std::string cerrs;
+  testing::internal::CaptureStderr();
+  Cpp::Process("int x = 5;");
+  cerrs = testing::internal::GetCapturedStderr();
+  EXPECT_STREQ(cerrs.c_str(), "");
+  Cpp::Undo();
+  testing::internal::CaptureStderr();
+  Cpp::Process("int y = x;");
+  cerrs = testing::internal::GetCapturedStderr();
+  EXPECT_TRUE(strstr(cerrs.c_str(), "use of undeclared identifier 'x'") != nullptr);
+  testing::internal::CaptureStderr();
+  Cpp::Process("int x = 10;");
+  cerrs = testing::internal::GetCapturedStderr();
+  EXPECT_STREQ(cerrs.c_str(), "");
path: unittests/CppInterOp/FunctionReflectionTest.cpp
user: login: vgvassilev
body: We don't need this pattern. We can use the exit code of `Process`.
created_at: 2025-03-18T06:48:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1517,3 +1517,45 @@ TEST(FunctionReflectionTest, Destruct) {
   output = testing::internal::GetCapturedStdout();
   EXPECT_EQ(output, "Destructor Executed");
 }
+
+TEST(FunctionReflectionTest, UndoTest) {
+#ifdef EMSCRIPTEN
+  GTEST_SKIP()
+      << "Test fails for Emscipten builds";
+#else
+  Cpp::CreateInterpreter();
+  std::string cerrs;
+  testing::internal::CaptureStderr();
+  Cpp::Process("int x = 5;");
+  cerrs = testing::internal::GetCapturedStderr();
+  EXPECT_STREQ(cerrs.c_str(), "");
+  Cpp::Undo();
+  testing::internal::CaptureStderr();
+  Cpp::Process("int y = x;");
+  cerrs = testing::internal::GetCapturedStderr();
+  EXPECT_TRUE(strstr(cerrs.c_str(), "use of undeclared identifier 'x'") != nullptr);
+  testing::internal::CaptureStderr();
+  Cpp::Process("int x = 10;");
+  cerrs = testing::internal::GetCapturedStderr();
+  EXPECT_STREQ(cerrs.c_str(), "");
path: unittests/CppInterOp/FunctionReflectionTest.cpp
user: login: kr-2003
body: done
created_at: 2025-03-18T19:28:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1502,3 +1502,40 @@ TEST(FunctionReflectionTest, Destruct) {
   output = testing::internal::GetCapturedStdout();
   EXPECT_EQ(output, "Destructor Executed");
 }
+
+TEST(FunctionReflectionTest, UndoTest) {
+  Cpp::CreateInterpreter();
+  std::string cerrs;
+  testing::internal::CaptureStderr();
+  Cpp::Process("int x = 5;");
+  cerrs = testing::internal::GetCapturedStderr();
+  EXPECT_STREQ(cerrs.c_str(), "");
+  Cpp::Undo();
path: unittests/CppInterOp/FunctionReflectionTest.cpp
user: login: kr-2003
body: i added two more instructions before calling the first Cpp::Undo().
created_at: 2025-03-18T19:30:52Z
author_association: CONTRIBUTOR

PR comments:
user: login: anutosh491
created_at: 2025-03-03T11:46:31Z
body: Hi, 

@kr-2003 and I were interested in supporting %undo through xeus-cpp/xeus-cpp-lite but we realize it is not being exposed through cppinterop.

Hence I asked him to add the same here.
author_association: COLLABORATOR
user: login: anutosh491
created_at: 2025-03-03T11:49:32Z
body: Weird that we found it being exposed through the libclang inspired C API but not through the standard one.

https://github.com/compiler-research/CppInterOp/blob/7caf10b1ccae74e62600b1b5fe05dc4e71db93bb/lib/Interpreter/CXCppInterOp.cpp#L307-L313
author_association: COLLABORATOR
user: login: codecov[bot]
created_at: 2025-03-03T11:57:24Z
body: ## [Codecov](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 75.46%. Comparing base [(`a22df96`)](https://app.codecov.io/gh/compiler-research/CppInterOp/commit/a22df968590709eb2d3957fb6c88feb61639621e?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research) to head [(`77489d7`)](https://app.codecov.io/gh/compiler-research/CppInterOp/commit/77489d79fcba7865901b76f7b096f2872e7462b1?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research).
> Report is 1 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513/graphs/tree.svg?width=650&height=150&src=pr&token=7UWTYSVVT5&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research)](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research)

```diff
@@            Coverage Diff             @@
##             main     #513      +/-   ##
==========================================
+ Coverage   75.33%   75.46%   +0.13%     
==========================================
  Files           9        9              
  Lines        3620     3628       +8     
==========================================
+ Hits         2727     2738      +11     
+ Misses        893      890       -3     
```

| [Files with missing lines](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research) | Coverage Δ | |
|---|---|---|
| [include/clang/Interpreter/CppInterOp.h](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513?src=pr&el=tree&filepath=include%2Fclang%2FInterpreter%2FCppInterOp.h&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research#diff-aW5jbHVkZS9jbGFuZy9JbnRlcnByZXRlci9DcHBJbnRlck9wLmg=) | `96.29% <ø> (ø)` | |
| [lib/Interpreter/CXCppInterOp.cpp](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513?src=pr&el=tree&filepath=lib%2FInterpreter%2FCXCppInterOp.cpp&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research#diff-bGliL0ludGVycHJldGVyL0NYQ3BwSW50ZXJPcC5jcHA=) | `47.27% <ø> (ø)` | |
| [lib/Interpreter/CppInterOp.cpp](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513?src=pr&el=tree&filepath=lib%2FInterpreter%2FCppInterOp.cpp&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research#diff-bGliL0ludGVycHJldGVyL0NwcEludGVyT3AuY3Bw) | `83.07% <100.00%> (+0.01%)` | :arrow_up: |
| [lib/Interpreter/CppInterOpInterpreter.h](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513?src=pr&el=tree&filepath=lib%2FInterpreter%2FCppInterOpInterpreter.h&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research#diff-bGliL0ludGVycHJldGVyL0NwcEludGVyT3BJbnRlcnByZXRlci5o) | `80.32% <100.00%> (+1.23%)` | :arrow_up: |

... and [1 file with indirect coverage changes](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research)

| [Files with missing lines](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research) | Coverage Δ | |
|---|---|---|
| [include/clang/Interpreter/CppInterOp.h](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513?src=pr&el=tree&filepath=include%2Fclang%2FInterpreter%2FCppInterOp.h&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research#diff-aW5jbHVkZS9jbGFuZy9JbnRlcnByZXRlci9DcHBJbnRlck9wLmg=) | `96.29% <ø> (ø)` | |
| [lib/Interpreter/CXCppInterOp.cpp](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513?src=pr&el=tree&filepath=lib%2FInterpreter%2FCXCppInterOp.cpp&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research#diff-bGliL0ludGVycHJldGVyL0NYQ3BwSW50ZXJPcC5jcHA=) | `47.27% <ø> (ø)` | |
| [lib/Interpreter/CppInterOp.cpp](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513?src=pr&el=tree&filepath=lib%2FInterpreter%2FCppInterOp.cpp&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research#diff-bGliL0ludGVycHJldGVyL0NwcEludGVyT3AuY3Bw) | `83.07% <100.00%> (+0.01%)` | :arrow_up: |
| [lib/Interpreter/CppInterOpInterpreter.h](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513?src=pr&el=tree&filepath=lib%2FInterpreter%2FCppInterOpInterpreter.h&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research#diff-bGliL0ludGVycHJldGVyL0NwcEludGVyT3BJbnRlcnByZXRlci5o) | `80.32% <100.00%> (+1.23%)` | :arrow_up: |

... and [1 file with indirect coverage changes](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research)

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-03T12:02:16Z
body: clang-tidy review says "All clean, LGTM! :+1:"
author_association: CONTRIBUTOR
user: login: kr-2003
created_at: 2025-03-04T07:00:12Z
body: @vgvassilev I have included both cling and clang-REPL for undo feature. All tests are passing successfully. Is there any other work pending?
author_association: CONTRIBUTOR
user: login: anutosh491
created_at: 2025-03-04T08:55:35Z
body: I was actually hopeful we could have this before/in 1.6.0 (which was released on the same day we started discussing undo in xeus-cpp and lite  😅  . We actually made good progress here as xeus-cpp would just naturally have it and I was able to get undo to quite some extent for lite too)

Can we have it through a patch release (1.6.1) or something ?




author_association: COLLABORATOR
user: login: vgvassilev
created_at: 2025-03-04T17:00:21Z
body: > I was actually hopeful we could have this before/in 1.6.0 (which was released on the same day we started discussing undo in xeus-cpp and lite 😅 . We actually made good progress here as xeus-cpp would just naturally have it and I was able to get undo to quite some extent for lite too)
> 
> Can we have it through a patch release (1.6.1) or something ?

We do not foresee a patch version for now. It will wait for the next shipping vehicle. 
author_association: CONTRIBUTOR
user: login: anutosh491
created_at: 2025-03-04T17:17:56Z
body: > It will wait for the next shipping vehicle.

No issues. Let's move it in if we consider it ready though ?

Also hopefully till the next release, I shall have enough context on how to get this working through lite. I was able to implement it (**and if you go through the video carefully you will see the flaw in the video**)

https://github.com/user-attachments/assets/48e8cf6c-103d-45e8-9b3e-dcb78daf47b3

Have been discussing why this goes wrong with Sam Clegg who says symbols from subsequent dlopen() operations don't override existing global symbols from previous dlopen() calls. So yeah an error in emscripten is what we think this is !


author_association: COLLABORATOR
user: login: mcbarton
created_at: 2025-03-04T19:07:47Z
body: > ## [Codecov](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research) Report
> 
> Attention: Patch coverage is `75.00000%` with `2 lines` in your changes missing coverage. Please review.
> 
> > Project coverage is 72.54%. Comparing base [(`7caf10b`)](https://app.codecov.io/gh/compiler-research/CppInterOp/commit/7caf10b1ccae74e62600b1b5fe05dc4e71db93bb?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research) to head [(`87114a5`)](https://app.codecov.io/gh/compiler-research/CppInterOp/commit/87114a5da2b71642c87ba7b6be3fd43aa82e0482?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research).
> 
> [Files with missing lines](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research) 	Patch % 	Lines
> [lib/Interpreter/CppInterOpInterpreter.h](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513?src=pr&el=tree&filepath=lib%2FInterpreter%2FCppInterOpInterpreter.h&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research#diff-bGliL0ludGVycHJldGVyL0NwcEludGVyT3BJbnRlcnByZXRlci5o) 	66.66% 	[2 Missing ⚠️ ](https://app.codecov.io/gh/compiler-research/CppInterOp/pull/513?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=compiler-research)
> Additional details and impacted files

@kr-2003 can you update the tests so your tests cover the 2 missing lines from the patch?
author_association: COLLABORATOR
user: login: kr-2003
created_at: 2025-03-10T07:07:43Z
body: @mcbarton Test coverage review has been addressed. Can we take this in now?
author_association: CONTRIBUTOR
user: login: mcbarton
created_at: 2025-03-14T17:40:22Z
body: @kr-2003 can you rebase? Now that we have automated wasm tests we need to check that the new test passes for an Emscripten build.
author_association: COLLABORATOR
user: login: mcbarton
created_at: 2025-03-17T10:51:12Z
body: @anutosh491 Given you want this PR in can you help @kr-2003 fix the test which fails for the Emscripten case?
author_association: COLLABORATOR
user: login: anutosh491
created_at: 2025-03-17T10:57:53Z
body: Perfect. So undo isn't expected to work with clang-repl-lite untill emscripten supports dlclose correctly. 

Abhinav only run these tests when emscripten is not defined

So 
```
#ifndef EMSCRIPTEN
  place your tests here
#endif
```
or if you place this construct inside your test, you can do 

```
TEST(FunctionReflectionTest, UndoTest) {
#ifdef EMSCRIPTEN
  GTEST_SKIP() << "Currently not implemented while running clang-repl in the broswer";
#endif
```
author_association: COLLABORATOR
user: login: kr-2003
created_at: 2025-03-19T05:37:36Z
body: @mcbarton 
https://github.com/compiler-research/CppInterOp/actions/runs/13938614873/job/39011414822?pr=513#step:8:623
All tests are passing but after that the workflow is exiting with code -1 in windows. Can you help me out here?
author_association: CONTRIBUTOR
user: login: mcbarton
created_at: 2025-03-19T08:21:39Z
body: > @mcbarton https://github.com/compiler-research/CppInterOp/actions/runs/13938614873/job/39011414822?pr=513#step:8:623 All tests are passing but after that the workflow is exiting with code -1 in windows. Can you help me out here?

@kr-2003 I suspect (but won't know for sure until you skip the section of the test causing this message) that you are getting the error from here https://github.com/compiler-research/CppInterOp/actions/runs/13938614873/job/39011414822?pr=513#step:8:408 . Basically almost every test which causes the interpreter to write anything to the screen in Windows is currently skipped. Its a known issue, which we are hoping will get fixed soon. Whether this PR is acceptable with this test skipped on Windows will not be upto me though. You would need the approval of @vgvassilev or maybe @aaronj0  .
author_association: COLLABORATOR
user: login: kr-2003
created_at: 2025-03-19T10:43:23Z
body: > > @mcbarton https://github.com/compiler-research/CppInterOp/actions/runs/13938614873/job/39011414822?pr=513#step:8:623 All tests are passing but after that the workflow is exiting with code -1 in windows. Can you help me out here?
> 
> @kr-2003 I suspect (but won't know for sure until you skip the section of the test causing this message) that you are getting the error from here https://github.com/compiler-research/CppInterOp/actions/runs/13938614873/job/39011414822?pr=513#step:8:408 . Basically almost every test which causes the interpreter to write anything to the screen in Windows is currently skipped. Its a known issue, which we are hoping will get fixed soon. Whether this PR is acceptable with this test skipped on Windows will not be upto me though. You would need the approval of @vgvassilev or maybe @aaronj0 .

So, should I skip these tests for windows? @vgvassilev @aaronj0 
author_association: CONTRIBUTOR
user: login: kr-2003
created_at: 2025-03-22T04:49:23Z
body: For now I have skipped these tests for windows.
author_association: CONTRIBUTOR
user: login: vgvassilev
created_at: 2025-03-24T12:42:23Z
body: Make sure the ci passes and you squash on merge. 
author_association: CONTRIBUTOR
user: login: anutosh491
created_at: 2025-03-24T12:42:55Z
body: > Make sure the ci passes and you squash on merge.

Absolutely 
Yeah 15 commits is a bit too much.
author_association: COLLABORATOR
user: login: Vipul-Cariappa
created_at: 2025-03-25T04:23:56Z
body: @kr-2003 Congratulations on getting your first PR merged 🚀.
author_association: COLLABORATOR
user: login: kr-2003
created_at: 2025-03-25T04:49:22Z
body: > @kr-2003 Congratulations on getting your first PR merged 🚀.

Thank you 😊
author_association: CONTRIBUTOR