Skip to content

Handle syntax errors in parser - #48

Open
gf712 wants to merge 2 commits into
mainfrom
parser-syntax-errors
Open

gf712 wants to merge 2 commits into
mainfrom
parser-syntax-errors

Conversation

@gf712

@gf712 gf712 commented Sep 9, 2026

Copy link
Copy Markdown
Owner

No description provided.

parser::Parser p{ lexer };
p.parse();
auto module_ = p.parse();
if (module_.is_err()) { TODO(); }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The compile() builtin aborts the whole process instead of raising a SyntaxError (bug)


module_.is_err() calls TODO(), which aborts via std::abort(). Since p.parse() now actually returns a SyntaxError on failure, compile("def foo(:", "<string>", "exec") will crash the interpreter instead of raising a catchable SyntaxError, unlike every other caller in this PR (repl.cpp, freeze.cpp) which was updated to propagate the error. The enclosing function returns PyResult<PyObject *>, so this should be return Err(module_.unwrap_err());.

Comment on lines +146 to +147
const auto column = std::min(line.size(), offset->as_size_t());
auto prefix = line.substr(0, column > 0 ? column - 1 : 0);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caret rendered one column too far left when the error is at end-of-line (bug)


offset is 1-based over 1 .. line.size() + 1 (an offset of line.size() + 1 means "past the last character", which Parser::parse() deliberately produces for the ran-off-end-of-input case). Clamping with std::min(line.size(), offset->as_size_t()) caps column at line.size(), so when offset == line.size() + 1 the computed column is one short, and prefix/the caret end up one column left of the intended position. The clamp should be std::min(line.size() + 1, offset->as_size_t()).

}
if (filename && lineno) {
return PyString::create(
std::format("{} ({}, line {})", msg, basename, lineno->as_size_t()));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unvalidated user-supplied lineno/offset abort the process instead of raising (bug)


m_lineno/m_offset are set directly from the user-supplied info tuple in __init__ with no range/sign checks, and are also writable via the plain .attr("lineno", ...)/.attr("offset", ...) descriptors. __str__ and format_exception_only both call ->as_size_t() on them unconditionally, and PyInteger::as_size_t() asserts fits_ulong_p() and calls std::abort() otherwise. So e.g. str(SyntaxError("m", ("f.py", -1, 1, "code"))), or an uncaught raise SyntaxError("m", ("f.py", -1, 1, "code")), aborts the interpreter (SIGABRT) instead of behaving like CPython, which just prints the value.

@claude

claude Bot commented Sep 9, 2026

Copy link
Copy Markdown

Code review

Found 3 new issues — see the inline comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant