From 97f53b8a722713cb5834fb376829952fbbf4d16f Mon Sep 17 00:00:00 2001 From: mumallaeng Date: Mon, 17 Aug 2026 16:17:07 +0900 Subject: [PATCH] Collapse bare ExpectedExpression to 'invalid syntax'; fix '<>' diagnostic offset `ParseErrorType::ExpectedExpression` currently surfaces as the raw ruff parser message (e.g. "Expected an expression") to callers that only depend on `rustpython-compiler` (no `rustpython-vm`). `rustpython-vm`'s `vm_new.rs` already collapses this to CPython's generic "invalid syntax" for its own callers; mirror that same collapse inside `cpython_parse_diagnostic_override` so non-vm consumers get the same CPython-compatible message. A bare `<>` outside Barry-as-BDFL mode (`2 <> 3`) lexes as `Less` then an unexpected `Greater`, so the resulting `ExpectedExpression` location points at the `>` -- one character past where CPython's tokenizer (which treats `<>` as a single obsolete token) reports the error. Detect the `<` immediately preceding the location and shift the reported range back over it. Assisted-by: Claude Code:claude-sonnet-5 --- crates/compiler/src/lib.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs index 7562e8939b9..062a705d4d7 100644 --- a/crates/compiler/src/lib.rs +++ b/crates/compiler/src/lib.rs @@ -362,9 +362,42 @@ fn cpython_parse_diagnostic_override( )); } + // `2 <> 3` outside Barry mode: ruff lexes `<` then an unexpected `>` and + // reports `ExpectedExpression` starting at the `>`. CPython's tokenizer + // treats `<>` as a single obsolete token and points at its start (the + // `<`) instead, so shift the reported location back over it. + source_error!(barry_flufl_obsolete_operator_error(error, source_text)); + + // CPython's PEG parser collapses a bare "expected an expression" failure + // into the generic "invalid syntax" message. rustpython-vm's `vm_new.rs` + // does this same collapse for its own callers; rustpython-compiler has no + // vm dependency, so mirror it here. + if matches!(&error.error, parser::ParseErrorType::ExpectedExpression) { + let (loc, end_loc) = adjusted_error_locations(source_file, error.location); + return Some(NormalizedParseDiagnostic::new( + parser::ParseErrorType::OtherError("invalid syntax".into()), + loc, + end_loc, + )); + } + None } +fn barry_flufl_obsolete_operator_error( + error: &parser::ParseError, + source: &str, +) -> Option<(String, usize, usize)> { + if !matches!(&error.error, parser::ParseErrorType::ExpectedExpression) { + return None; + } + let start = error.location.start().to_usize(); + if start == 0 || source.as_bytes().get(start - 1) != Some(&b'<') { + return None; + } + Some(("invalid syntax".to_string(), start - 1, start + 1)) +} + fn eof_parse_diagnostic( error: &parser::ParseError, source_file: &SourceFile,