-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[clang-repl] Remove invalid TopLevelStmtDecl from TU on parse failure #153945
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we extend either There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The state of the AST before and after the failed statement will not be identical (and asserts on AST dump). I could not find a way to emit/dump AST on a failed statement.
cat "test.C" | bin/clang-repl -Xcc -Xclang -Xcc -ast-dump I get:
There are no dumps after the failed statement. Do you know of another way to dump the AST (to trigger the assertion)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you try to |
||
// 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", "-"}; | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we do this here
llvm-project/clang/lib/Interpreter/IncrementalParser.cpp
Line 155 in e44784f
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we could do something like:
Does that work?
I’m wondering, though, if we should fix this at the source: when ParseTopLevelStmtDecl sees !R.isUsable(), we immediately unlink the TopLevelStmtDecl we just attached. That ensures we never see a TU with an invalid TopLevelStmtDecl between parse and cleanup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clang does not have the notion of removing the declarations on error. If we do it in the Parser that would be inconsistent to the rest of the logic in clang. I'd prefer to have all of this happen in a single place eg.
CleanUpPTU
.