https://github.com/devajithvs updated https://github.com/llvm/llvm-project/pull/153945
>From 4ba8f5a6cd94a7c77da3ae4301d57f11c103d8ae Mon Sep 17 00:00:00 2001 From: Devajith Valaparambil Sreeramaswamy <devajith.valaparambil.sreeramasw...@cern.ch> Date: Fri, 15 Aug 2025 17:08:07 +0200 Subject: [PATCH] [clang-repl] Remove invalid TopLevelStmtDecl from TU on parse failure `ActOnStartTopLevelStmtDecl` immediately adds the new `TopLevelStmtDecl` to the current `DeclContext`. If parsing fails, the decl remains attached to the TU even though it has no valid `Stmt`, leaving the AST in an invalid state. --- clang/lib/Parse/ParseDecl.cpp | 5 +- .../unittests/Interpreter/InterpreterTest.cpp | 49 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index fd53cca5a13ff..59c5f4cacc36e 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -5696,8 +5696,11 @@ Parser::DeclGroupPtrTy Parser::ParseTopLevelStmtDecl() { TopLevelStmtDecl *TLSD = Actions.ActOnStartTopLevelStmtDecl(getCurScope()); StmtResult R = ParseStatementOrDeclaration(Stmts, SubStmtCtx); Actions.ActOnFinishTopLevelStmtDecl(TLSD, R.get()); - if (!R.isUsable()) + if (!R.isUsable()) { + if (DeclContext *DC = TLSD->getDeclContext()) + DC->removeDecl(TLSD); // unlink from TU R = Actions.ActOnNullStmt(Tok.getLocation()); + } if (Tok.is(tok::annot_repl_input_end) && Tok.getAnnotationValue() != nullptr) { diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp index 8639fb668f1fe..e0a007f68227c 100644 --- a/clang/unittests/Interpreter/InterpreterTest.cpp +++ b/clang/unittests/Interpreter/InterpreterTest.cpp @@ -21,6 +21,8 @@ #include "clang/Interpreter/Value.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Sema.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" #include "llvm/TargetParser/Host.h" @@ -91,6 +93,53 @@ TEST_F(InterpreterTest, IncrementalInputTopLevelDecls) { EXPECT_EQ("var2", DeclToString(*R2DeclRange.begin())); } +TEST_F(InterpreterTest, BadIncludeDoesNotCorruptTU) { + // Create a temporary header that triggers an error at top level. + llvm::SmallString<256> HeaderPath; + ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("interp-bad-include", "h", + HeaderPath)); + { + std::error_code EC; + llvm::raw_fd_ostream OS(HeaderPath, EC, llvm::sys::fs::OF_Text); + ASSERT_FALSE(EC); + OS << R"(error_here; // expected-error {{use of undeclared identifier}})"; + } + + llvm::SmallString<256> HeaderDir = llvm::sys::path::parent_path(HeaderPath); + llvm::SmallString<256> HeaderFile = llvm::sys::path::filename(HeaderPath); + + // Set up the interpreter with the include path. + using Args = std::vector<const char *>; + Args ExtraArgs = {"-I", HeaderDir.c_str(), + "-Xclang", "-diagnostic-log-file", + "-Xclang", "-"}; + + // Create the diagnostic engine with unowned consumer. + std::string DiagnosticOutput; + llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput); + DiagnosticOptions DiagOpts; + auto DiagPrinter = + std::make_unique<TextDiagnosticPrinter>(DiagnosticsOS, DiagOpts); + + auto Interp = createInterpreter(ExtraArgs, DiagPrinter.get()); + // This parse should fail because of an undeclared identifier, but should + // leave TU intact. + auto Err = + Interp->Parse("#include \"" + HeaderFile.str().str() + "\"").takeError(); + using ::testing::HasSubstr; + EXPECT_THAT(DiagnosticOutput, + HasSubstr("use of undeclared identifier 'error_here'")); + EXPECT_EQ("Parsing failed.", llvm::toString(std::move(Err))); + + // Inspect the TU, there must not be any TopLevelStmtDecl left. + TranslationUnitDecl *TU = + Interp->getCompilerInstance()->getASTContext().getTranslationUnitDecl(); + EXPECT_EQ(0U, DeclsSize(TU)); + + // Cleanup temp file. + llvm::sys::fs::remove(HeaderPath); +} + TEST_F(InterpreterTest, Errors) { Args ExtraArgs = {"-Xclang", "-diagnostic-log-file", "-Xclang", "-"}; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits