Skip to content

Commit 00b063e

Browse files
committed
When Cpp::Declare is silent the API should retain the error code.
1 parent a02ba96 commit 00b063e

6 files changed

Lines changed: 94 additions & 30 deletions

File tree

lib/CppInterOp/Compatibility.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,51 @@ static inline char* GetEnv(const char* Var_Name) {
5151
#endif
5252
}
5353

54+
/// A very lightweight RAII switcher. It manages diagnostic silencing
55+
/// and provides a unified interface for handling LLVM Errors.
56+
class DiagnosticGuard {
57+
bool m_is_silent;
58+
clang::DiagnosticsEngine& m_diags;
59+
clang::DiagnosticConsumer* m_oldClient;
60+
bool m_oldShouldOwn;
61+
clang::IgnoringDiagConsumer m_ignoring_consumer;
62+
63+
public:
64+
explicit DiagnosticGuard(clang::DiagnosticsEngine& diags, bool silent)
65+
: m_is_silent(silent), m_diags(diags) {
66+
if (m_is_silent) {
67+
m_oldShouldOwn = m_diags.ownsClient();
68+
if (m_oldShouldOwn)
69+
m_oldClient = m_diags.takeClient().release();
70+
else
71+
m_oldClient = m_diags.getClient();
72+
m_diags.setClient(&m_ignoring_consumer, /*ShouldOwnClient=*/false);
73+
}
74+
}
75+
76+
~DiagnosticGuard() {
77+
// Only restore if we actually swapped the client
78+
if (m_is_silent) {
79+
m_diags.setClient(m_oldClient, m_oldShouldOwn);
80+
}
81+
}
82+
83+
// Explicitly non-copyable
84+
DiagnosticGuard(const DiagnosticGuard&) = delete;
85+
DiagnosticGuard& operator=(const DiagnosticGuard&) = delete;
86+
87+
/// Consumes or logs the error based on the scope's silence setting.
88+
void handleOrConsume(llvm::Error err, const char* msg) {
89+
if (!err)
90+
return; // Safety check for "success" errors
91+
92+
if (m_is_silent)
93+
llvm::consumeError(std::move(err));
94+
else
95+
llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), msg);
96+
}
97+
};
98+
5499
#if CLANG_VERSION_MAJOR < 21
55100
#define Print_Canonical_Types PrintCanonicalTypes
56101
#else

lib/CppInterOp/CppInterOp.cpp

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3606,30 +3606,13 @@ void GetIncludePaths(std::vector<std::string>& IncludePaths, bool withSystem,
36063606
IncludePaths.push_back(i);
36073607
}
36083608

3609-
namespace {
3610-
3611-
class clangSilent {
3612-
public:
3613-
clangSilent(clang::DiagnosticsEngine& diag) : fDiagEngine(diag) {
3614-
fOldDiagValue = fDiagEngine.getSuppressAllDiagnostics();
3615-
fDiagEngine.setSuppressAllDiagnostics(true);
3616-
}
3617-
3618-
~clangSilent() { fDiagEngine.setSuppressAllDiagnostics(fOldDiagValue); }
3619-
3620-
protected:
3621-
clang::DiagnosticsEngine& fDiagEngine;
3622-
bool fOldDiagValue;
3623-
};
3624-
} // namespace
3625-
36263609
int Declare(compat::Interpreter& I, const char* code, bool silent) {
3627-
if (silent) {
3628-
clangSilent diagSuppr(I.getSema().getDiagnostics());
3629-
return I.declare(code);
3630-
}
3631-
3610+
#ifdef CPPINTEROP_USE_CLING
3611+
DiagnosticGuard Guard(I.getSema().getDiagnostics(), silent);
36323612
return I.declare(code);
3613+
#else
3614+
return I.declare(code, silent);
3615+
#endif // CPPINTEROP_USE_CLING
36333616
}
36343617

36353618
int Declare(const char* code, bool silent) {

lib/CppInterOp/CppInterOpInterpreter.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -389,30 +389,31 @@ class Interpreter {
389389
return nullptr;
390390
}
391391

392-
CompilationResult declare(const std::string& input,
392+
CompilationResult declare(const std::string& input, bool silent = false,
393393
clang::PartialTranslationUnit** PTU = nullptr) {
394-
return process(input, /*Value=*/nullptr, PTU);
394+
return process(input, /*Value=*/nullptr, silent, PTU);
395395
}
396396

397-
///\brief Maybe transform the input line to implement cint command line
397+
/// Maybe transform the input line to implement cint command line
398398
/// semantics (declarations are global) and compile to produce a module.
399399
///
400400
CompilationResult process(const std::string& input, clang::Value* V = 0,
401+
bool silent = false,
401402
clang::PartialTranslationUnit** PTU = nullptr,
402403
bool disableValuePrinting = false) {
404+
DiagnosticGuard Guard(getSema().getDiagnostics(), silent);
403405
auto PTUOrErr = Parse(input);
404406
if (!PTUOrErr) {
405-
llvm::logAllUnhandledErrors(PTUOrErr.takeError(), llvm::errs(),
406-
"Failed to parse via ::process:");
407+
Guard.handleOrConsume(PTUOrErr.takeError(),
408+
"Failed to parse via ::process:");
407409
return Interpreter::kFailure;
408410
}
409411

410412
if (PTU)
411413
*PTU = &*PTUOrErr;
412414

413415
if (auto Err = Execute(*PTUOrErr)) {
414-
llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
415-
"Failed to execute via ::process:");
416+
Guard.handleOrConsume(std::move(Err), "Failed to execute via ::process:");
416417
return Interpreter::kFailure;
417418
}
418419
return Interpreter::kSuccess;

unittests/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ function(add_cppinterop_unittest name)
3737
${ARGN}
3838
)
3939
add_executable(${name} EXCLUDE_FROM_ALL ${ARG_UNPARSED_ARGUMENTS})
40+
if(NOT LLVM_ENABLE_RTTI)
41+
if(MSVC)
42+
target_compile_options(${name} PRIVATE "/GR-")
43+
else()
44+
target_compile_options(${name} PRIVATE "-fno-rtti")
45+
endif()
46+
endif()
4047
add_dependencies(CppInterOpUnitTests ${name})
4148
target_include_directories(${name} PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${GTEST_INCLUDE_DIR})
4249
set_property(TARGET ${name} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})

unittests/CppInterOp/InterpreterTest.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,34 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, Interpreter_EmscriptenExceptionHandling) {
200200
EXPECT_TRUE(Cpp::Process(tryCatchCode) == 0);
201201
}
202202

203+
TYPED_TEST(CPPINTEROP_TEST_MODE, Silent_Declare_Behavior) {
204+
auto* I = TestFixture::CreateInterpreter();
205+
ASSERT_TRUE(I);
206+
207+
EXPECT_FALSE(Cpp::Declare("int valid_var = 42;", /*silent=*/true));
208+
209+
// Should produce output
210+
{
211+
testing::internal::CaptureStderr();
212+
// Intentionally broken code: "int x = ;"
213+
bool success = Cpp::Declare("int broken_syntax = ;", /*silent=*/false);
214+
std::string output = testing::internal::GetCapturedStderr();
215+
EXPECT_TRUE(success);
216+
EXPECT_FALSE(output.empty()) << "We expect to see a Clang error message\n";
217+
}
218+
219+
// Should NOT produce output
220+
{
221+
testing::internal::CaptureStderr();
222+
// Intentionally broken code
223+
bool success = Cpp::Declare("invalid_type error_var = 0;", /*silent=*/true);
224+
std::string output = testing::internal::GetCapturedStderr();
225+
EXPECT_TRUE(success);
226+
EXPECT_TRUE(output.empty())
227+
<< "Silent Declare leaked an error to stderr: " << output;
228+
}
229+
}
230+
203231
TYPED_TEST(CPPINTEROP_TEST_MODE, Interpreter_CreateInterpreter) {
204232
auto* I = TestFixture::CreateInterpreter();
205233
EXPECT_TRUE(I);

unittests/CppInterOp/Utils.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ void TestUtils::GetAllTopLevelDecls(
7676
}
7777
#else
7878
PartialTranslationUnit *T = nullptr;
79-
Interp->process(code, /*Value*/nullptr, &T);
79+
Interp->process(code, /*Value*/ nullptr, /*silent=*/false, &T);
8080
for (auto *D : T->TUPart->decls()) {
8181
if (filter_implicitGenerated && D->isImplicit())
8282
continue;

0 commit comments

Comments
 (0)