From 8cb2598a7644c046ac956cc82e0ed9507b5290cd Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Wed, 27 May 2026 15:41:26 +0300 Subject: [PATCH 1/7] Cannot assign to True/False/None --- Lib/test/test_syntax.py | 20 +++---- crates/compiler/src/lib.rs | 117 +++++++++++++++++++++++-------------- 2 files changed, 82 insertions(+), 55 deletions(-) diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 1c6e536c783..adbd8ef0bb5 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -31,7 +31,7 @@ Traceback (most recent call last): SyntaxError: invalid syntax ->>> None = 1 # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> None = 1 Traceback (most recent call last): SyntaxError: cannot assign to None @@ -39,7 +39,7 @@ Traceback (most recent call last): SyntaxError: invalid syntax ->>> True = 1 # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> True = 1 Traceback (most recent call last): SyntaxError: cannot assign to True @@ -124,7 +124,7 @@ Traceback (most recent call last): SyntaxError: cannot assign to literal ->>> (a, True, c) = (1, 2, 3) # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> (a, True, c) = (1, 2, 3) Traceback (most recent call last): SyntaxError: cannot assign to True @@ -132,7 +132,7 @@ Traceback (most recent call last): SyntaxError: cannot assign to __debug__ ->>> (a, *True, c) = (1, 2, 3) # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> (a, *True, c) = (1, 2, 3) Traceback (most recent call last): SyntaxError: cannot assign to True @@ -188,11 +188,11 @@ Traceback (most recent call last): SyntaxError: invalid syntax ->>> True = True = 3 # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> True = True = 3 Traceback (most recent call last): SyntaxError: cannot assign to True ->>> x = y = True = z = 3 # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> x = y = True = z = 3 Traceback (most recent call last): SyntaxError: cannot assign to True @@ -2114,19 +2114,19 @@ # Check that we don't raise a "cannot use name as import target" error # if there is an error in an unrelated statement after ';' ->>> import a as b; None = 1 # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> import a as b; None = 1 Traceback (most recent call last): SyntaxError: cannot assign to None ->>> import a, b as c; d = 1; None = 1 # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> import a, b as c; d = 1; None = 1 Traceback (most recent call last): SyntaxError: cannot assign to None ->>> from a import b as c; None = 1 # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> from a import b as c; None = 1 Traceback (most recent call last): SyntaxError: cannot assign to None ->>> from a import b, c as d; e = 1; None = 1 # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> from a import b, c as d; e = 1; None = 1 Traceback (most recent call last): SyntaxError: cannot assign to None diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs index bc0a2363e33..60c9a6414b6 100644 --- a/crates/compiler/src/lib.rs +++ b/crates/compiler/src/lib.rs @@ -1,4 +1,7 @@ +use ruff_python_parser::{LexicalErrorType, ParseErrorType}; use ruff_source_file::{PositionEncoding, SourceFile, SourceFileBuilder, SourceLocation}; +use ruff_text_size::TextSlice; + use rustpython_codegen::{compile, symboltable}; pub use rustpython_codegen::compile::CompileOpts; @@ -16,13 +19,13 @@ pub enum CompileErrorType { #[error(transparent)] Codegen(#[from] codegen::error::CodegenErrorType), #[error(transparent)] - Parse(#[from] parser::ParseErrorType), + Parse(#[from] ParseErrorType), } #[derive(Error, Debug)] pub struct ParseError { #[source] - pub error: parser::ParseErrorType, + pub error: ParseErrorType, pub raw_location: ruff_text_size::TextRange, pub location: SourceLocation, pub end_location: SourceLocation, @@ -48,63 +51,87 @@ pub enum CompileError { impl CompileError { #[must_use] pub fn from_ruff_parse_error(error: parser::ParseError, source_file: &SourceFile) -> Self { + //dbg!(&error); + let source_code = source_file.to_source_code(); let source_text = source_file.source_text(); // For EOF errors (unclosed brackets), find the unclosed bracket position // and adjust both the error location and message let mut is_unclosed_bracket = false; - let (error_type, location, end_location) = if matches!( - &error.error, - parser::ParseErrorType::Lexical(parser::LexicalErrorType::Eof) - ) { - if let Some((bracket_char, bracket_offset)) = find_unclosed_bracket(source_text) { - let bracket_text_size = ruff_text_size::TextSize::new(bracket_offset as u32); - let loc = source_code.source_location(bracket_text_size, PositionEncoding::Utf8); + let (error_type, location, end_location) = match &error.error { + ParseErrorType::Lexical(LexicalErrorType::Eof) => { + if let Some((bracket_char, bracket_offset)) = find_unclosed_bracket(source_text) { + let bracket_text_size = ruff_text_size::TextSize::new(bracket_offset as u32); + let loc = + source_code.source_location(bracket_text_size, PositionEncoding::Utf8); + let end_loc = SourceLocation { + line: loc.line, + character_offset: loc.character_offset.saturating_add(1), + }; + let msg = format!("'{bracket_char}' was never closed"); + is_unclosed_bracket = true; + (ParseErrorType::OtherError(msg), loc, end_loc) + } else { + let loc = + source_code.source_location(error.location.start(), PositionEncoding::Utf8); + let end_loc = + source_code.source_location(error.location.end(), PositionEncoding::Utf8); + (error.error, loc, end_loc) + } + } + + ParseErrorType::Lexical(LexicalErrorType::IndentationError) => { + // For IndentationError, point the offset to the end of the line content + // instead of the beginning + let loc = + source_code.source_location(error.location.start(), PositionEncoding::Utf8); + let line_idx = loc.line.to_zero_indexed(); + let line = source_text.split('\n').nth(line_idx).unwrap_or(""); + let line_end_col = line.chars().count() + 1; // 1-indexed, past last char let end_loc = SourceLocation { line: loc.line, - character_offset: loc.character_offset.saturating_add(1), + character_offset: ruff_source_file::OneIndexed::new(line_end_col) + .unwrap_or(loc.character_offset), }; - let msg = format!("'{bracket_char}' was never closed"); - is_unclosed_bracket = true; - (parser::ParseErrorType::OtherError(msg), loc, end_loc) - } else { + (error.error, end_loc, end_loc) + } + + ParseErrorType::InvalidAssignmentTarget => { let loc = source_code.source_location(error.location.start(), PositionEncoding::Utf8); - let end_loc = + let mut end_loc = source_code.source_location(error.location.end(), PositionEncoding::Utf8); - (error.error, loc, end_loc) - } - } else if matches!( - &error.error, - parser::ParseErrorType::Lexical(parser::LexicalErrorType::IndentationError) - ) { - // For IndentationError, point the offset to the end of the line content - // instead of the beginning - let loc = source_code.source_location(error.location.start(), PositionEncoding::Utf8); - let line_idx = loc.line.to_zero_indexed(); - let line = source_text.split('\n').nth(line_idx).unwrap_or(""); - let line_end_col = line.chars().count() + 1; // 1-indexed, past last char - let end_loc = SourceLocation { - line: loc.line, - character_offset: ruff_source_file::OneIndexed::new(line_end_col) - .unwrap_or(loc.character_offset), - }; - (error.error, end_loc, end_loc) - } else { - let loc = source_code.source_location(error.location.start(), PositionEncoding::Utf8); - let mut end_loc = - source_code.source_location(error.location.end(), PositionEncoding::Utf8); - - // If the error range ends at the start of a new line (column 1), - // adjust it to the end of the previous line - if end_loc.character_offset.get() == 1 && end_loc.line > loc.line { - let prev_line_end = error.location.end() - ruff_text_size::TextSize::from(1); - end_loc = source_code.source_location(prev_line_end, PositionEncoding::Utf8); - end_loc.character_offset = end_loc.character_offset.saturating_add(1); + + // If the error range ends at the start of a new line (column 1), + // adjust it to the end of the previous line + if end_loc.character_offset.get() == 1 && end_loc.line > loc.line { + let prev_line_end = error.location.end() - ruff_text_size::TextSize::from(1); + end_loc = source_code.source_location(prev_line_end, PositionEncoding::Utf8); + end_loc.character_offset = end_loc.character_offset.saturating_add(1); + } + + let target = source_file.source_text().slice(&error.location); + let msg = format!("cannot assign to {target}"); + (ParseErrorType::OtherError(msg), loc, end_loc) } - (error.error, loc, end_loc) + _ => { + let loc = + source_code.source_location(error.location.start(), PositionEncoding::Utf8); + let mut end_loc = + source_code.source_location(error.location.end(), PositionEncoding::Utf8); + + // If the error range ends at the start of a new line (column 1), + // adjust it to the end of the previous line + if end_loc.character_offset.get() == 1 && end_loc.line > loc.line { + let prev_line_end = error.location.end() - ruff_text_size::TextSize::from(1); + end_loc = source_code.source_location(prev_line_end, PositionEncoding::Utf8); + end_loc.character_offset = end_loc.character_offset.saturating_add(1); + } + + (error.error, loc, end_loc) + } }; Self::Parse(ParseError { From 328b21791050a87c36a185315f73a1414f4541e4 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Wed, 27 May 2026 15:50:14 +0300 Subject: [PATCH 2/7] cannot use assignment expressions with ... --- Lib/test/test_syntax.py | 2 +- crates/compiler/src/lib.rs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index adbd8ef0bb5..ec63dc1e172 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -43,7 +43,7 @@ Traceback (most recent call last): SyntaxError: cannot assign to True ->>> (True := 1) # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> (True := 1) Traceback (most recent call last): SyntaxError: cannot use assignment expressions with True diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs index 60c9a6414b6..612dc59403f 100644 --- a/crates/compiler/src/lib.rs +++ b/crates/compiler/src/lib.rs @@ -116,6 +116,25 @@ impl CompileError { (ParseErrorType::OtherError(msg), loc, end_loc) } + ParseErrorType::InvalidNamedAssignmentTarget => { + let loc = + source_code.source_location(error.location.start(), PositionEncoding::Utf8); + let mut end_loc = + source_code.source_location(error.location.end(), PositionEncoding::Utf8); + + // If the error range ends at the start of a new line (column 1), + // adjust it to the end of the previous line + if end_loc.character_offset.get() == 1 && end_loc.line > loc.line { + let prev_line_end = error.location.end() - ruff_text_size::TextSize::from(1); + end_loc = source_code.source_location(prev_line_end, PositionEncoding::Utf8); + end_loc.character_offset = end_loc.character_offset.saturating_add(1); + } + + let target = source_file.source_text().slice(&error.location); + let msg = format!("cannot use assignment expressions with {target}"); + (ParseErrorType::OtherError(msg), loc, end_loc) + } + _ => { let loc = source_code.source_location(error.location.start(), PositionEncoding::Utf8); From dfd48e0c70240c49bac9f4760c7d6fc7a12343f8 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Wed, 27 May 2026 16:20:47 +0300 Subject: [PATCH 3/7] cannot assign to function call/expression --- Lib/test/test_syntax.py | 40 +++++++++++++++++++------------------- crates/compiler/src/lib.rs | 16 +++++++++++---- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index ec63dc1e172..60a30ce2bb1 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -140,15 +140,15 @@ Traceback (most recent call last): SyntaxError: cannot assign to __debug__ ->>> [a, b, c + 1] = [1, 2, 3] # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> [a, b, c + 1] = [1, 2, 3] Traceback (most recent call last): SyntaxError: cannot assign to expression ->>> [a, b[1], c + 1] = [1, 2, 3] # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> [a, b[1], c + 1] = [1, 2, 3] Traceback (most recent call last): SyntaxError: cannot assign to expression ->>> [a, b.c.d, c + 1] = [1, 2, 3] # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> [a, b.c.d, c + 1] = [1, 2, 3] Traceback (most recent call last): SyntaxError: cannot assign to expression @@ -215,31 +215,31 @@ Invalid targets in `for` loops and `with` statements should also produce a specialized error message ->>> for a() in b: pass # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> for a() in b: pass Traceback (most recent call last): SyntaxError: cannot assign to function call ->>> for (a, b()) in b: pass # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> for (a, b()) in b: pass Traceback (most recent call last): SyntaxError: cannot assign to function call ->>> for [a, b()] in b: pass # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> for [a, b()] in b: pass Traceback (most recent call last): SyntaxError: cannot assign to function call ->>> for (*a, b, c+1) in b: pass # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> for (*a, b, c+1) in b: pass Traceback (most recent call last): SyntaxError: cannot assign to expression ->>> for (x, *(y, z.d())) in b: pass # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> for (x, *(y, z.d())) in b: pass Traceback (most recent call last): SyntaxError: cannot assign to function call ->>> for a, b() in c: pass # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> for a, b() in c: pass Traceback (most recent call last): SyntaxError: cannot assign to function call ->>> for a, b, (c + 1, d()): pass # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> for a, b, (c + 1, d()): pass Traceback (most recent call last): SyntaxError: cannot assign to expression @@ -251,27 +251,27 @@ Traceback (most recent call last): SyntaxError: invalid syntax ->>> with a as b(): pass # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> with a as b(): pass Traceback (most recent call last): SyntaxError: cannot assign to function call ->>> with a as (b, c()): pass # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> with a as (b, c()): pass Traceback (most recent call last): SyntaxError: cannot assign to function call ->>> with a as [b, c()]: pass # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> with a as [b, c()]: pass Traceback (most recent call last): SyntaxError: cannot assign to function call ->>> with a as (*b, c, d+1): pass # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> with a as (*b, c, d+1): pass Traceback (most recent call last): SyntaxError: cannot assign to expression ->>> with a as (x, *(y, z.d())): pass # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> with a as (x, *(y, z.d())): pass Traceback (most recent call last): SyntaxError: cannot assign to function call ->>> with a as b, c as d(): pass # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> with a as b, c as d(): pass Traceback (most recent call last): SyntaxError: cannot assign to function call @@ -293,11 +293,11 @@ Traceback (most recent call last): SyntaxError: 'in' expected after for-loop variables ->>> [x for x() in a] # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> [x for x() in a] Traceback (most recent call last): SyntaxError: cannot assign to function call ->>> [x for a, b, (c + 1, d()) in y] # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> [x for a, b, (c + 1, d()) in y] Traceback (most recent call last): SyntaxError: cannot assign to expression @@ -305,11 +305,11 @@ Traceback (most recent call last): SyntaxError: 'in' expected after for-loop variables ->>> [x for x+1 in y] # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> [x for x+1 in y] Traceback (most recent call last): SyntaxError: cannot assign to expression ->>> [x for x+1, x() in y] # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> [x for x+1, x() in y] Traceback (most recent call last): SyntaxError: cannot assign to expression diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs index 612dc59403f..24bf93f126b 100644 --- a/crates/compiler/src/lib.rs +++ b/crates/compiler/src/lib.rs @@ -1,6 +1,7 @@ use ruff_python_parser::{LexicalErrorType, ParseErrorType}; use ruff_source_file::{PositionEncoding, SourceFile, SourceFileBuilder, SourceLocation}; use ruff_text_size::TextSlice; +use thiserror::Error; use rustpython_codegen::{compile, symboltable}; @@ -12,7 +13,6 @@ pub use ruff_python_ast as ast; pub use ruff_python_parser as parser; pub use rustpython_codegen as codegen; pub use rustpython_compiler_core as core; -use thiserror::Error; #[derive(Error, Debug)] pub enum CompileErrorType { @@ -51,7 +51,7 @@ pub enum CompileError { impl CompileError { #[must_use] pub fn from_ruff_parse_error(error: parser::ParseError, source_file: &SourceFile) -> Self { - //dbg!(&error); + dbg!(&error); let source_code = source_file.to_source_code(); let source_text = source_file.source_text(); @@ -111,8 +111,16 @@ impl CompileError { end_loc.character_offset = end_loc.character_offset.saturating_add(1); } - let target = source_file.source_text().slice(&error.location); - let msg = format!("cannot assign to {target}"); + let expr_str = source_file.source_text().slice(&error.location); + + let msg = parser::parse_expression(expr_str) + .ok() + .map(|parsed| match dbg!(&*parsed.syntax().body) { + ast::Expr::Call(_) => "cannot assign to function call".into(), + ast::Expr::BinOp(_) => "cannot assign to expression".into(), + _ => format!("cannot assign to {expr_str}"), + }) + .unwrap_or_else(|| format!("cannot assign to {expr_str}")); (ParseErrorType::OtherError(msg), loc, end_loc) } From 90ce4df147b4f17363593e6d7ac9de45627d083c Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Wed, 27 May 2026 16:32:28 +0300 Subject: [PATCH 4/7] Fix more --- Lib/test/test_syntax.py | 24 +++++++++++------------ crates/compiler/src/lib.rs | 40 ++++++++++++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 60a30ce2bb1..0934f22d470 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -79,7 +79,7 @@ Traceback (most recent call last): SyntaxError: cannot assign to function call here. Maybe you meant '==' instead of '='? ->>> yield = 1 # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> yield = 1 Traceback (most recent call last): SyntaxError: assignment to yield expression not possible @@ -91,23 +91,23 @@ Traceback (most recent call last): SyntaxError: cannot assign to expression here. Maybe you meant '==' instead of '='? ->>> (x for x in x) = 1 # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> (x for x in x) = 1 Traceback (most recent call last): SyntaxError: cannot assign to generator expression ->>> 1 = 1 # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> 1 = 1 Traceback (most recent call last): SyntaxError: cannot assign to literal here. Maybe you meant '==' instead of '='? ->>> "abc" = 1 # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> "abc" = 1 Traceback (most recent call last): SyntaxError: cannot assign to literal here. Maybe you meant '==' instead of '='? ->>> b"" = 1 # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> b"" = 1 Traceback (most recent call last): SyntaxError: cannot assign to literal here. Maybe you meant '==' instead of '='? ->>> ... = 1 # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> ... = 1 Traceback (most recent call last): SyntaxError: cannot assign to ellipsis here. Maybe you meant '==' instead of '='? @@ -152,7 +152,7 @@ Traceback (most recent call last): SyntaxError: cannot assign to expression ->>> a if 1 else b = 1 # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> a if 1 else b = 1 Traceback (most recent call last): SyntaxError: cannot assign to conditional expression @@ -196,7 +196,7 @@ Traceback (most recent call last): SyntaxError: cannot assign to True ->>> x = y = yield = 1 # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> x = y = yield = 1 Traceback (most recent call last): SyntaxError: assignment to yield expression not possible @@ -334,19 +334,19 @@ # produce special error messages regarding missing # parentheses, but about missing commas instead ->>> [1, 2 3] # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> [1, 2 3] Traceback (most recent call last): SyntaxError: invalid syntax. Perhaps you forgot a comma? ->>> {1, 2 3} # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> {1, 2 3} Traceback (most recent call last): SyntaxError: invalid syntax. Perhaps you forgot a comma? ->>> {1:2, 2:5 3:12} # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> {1:2, 2:5 3:12} Traceback (most recent call last): SyntaxError: invalid syntax. Perhaps you forgot a comma? ->>> (1, 2 3) # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> (1, 2 3) Traceback (most recent call last): SyntaxError: invalid syntax. Perhaps you forgot a comma? diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs index 24bf93f126b..a40a1e51b79 100644 --- a/crates/compiler/src/lib.rs +++ b/crates/compiler/src/lib.rs @@ -1,3 +1,4 @@ +pub use ruff_python_ast::token::TokenKind; use ruff_python_parser::{LexicalErrorType, ParseErrorType}; use ruff_source_file::{PositionEncoding, SourceFile, SourceFileBuilder, SourceLocation}; use ruff_text_size::TextSlice; @@ -51,8 +52,6 @@ pub enum CompileError { impl CompileError { #[must_use] pub fn from_ruff_parse_error(error: parser::ParseError, source_file: &SourceFile) -> Self { - dbg!(&error); - let source_code = source_file.to_source_code(); let source_text = source_file.source_text(); @@ -96,6 +95,24 @@ impl CompileError { }; (error.error, end_loc, end_loc) } + ParseErrorType::ExpectedToken { expected, found } + if matches!((expected, found), (TokenKind::Comma, TokenKind::Int)) => + { + let loc = + source_code.source_location(error.location.start(), PositionEncoding::Utf8); + let mut end_loc = + source_code.source_location(error.location.end(), PositionEncoding::Utf8); + + // If the error range ends at the start of a new line (column 1), + // adjust it to the end of the previous line + if end_loc.character_offset.get() == 1 && end_loc.line > loc.line { + let prev_line_end = error.location.end() - ruff_text_size::TextSize::from(1); + end_loc = source_code.source_location(prev_line_end, PositionEncoding::Utf8); + end_loc.character_offset = end_loc.character_offset.saturating_add(1); + } + let msg = "invalid syntax. Perhaps you forgot a comma?".into(); + (ParseErrorType::OtherError(msg), loc, end_loc) + } ParseErrorType::InvalidAssignmentTarget => { let loc = @@ -115,12 +132,27 @@ impl CompileError { let msg = parser::parse_expression(expr_str) .ok() - .map(|parsed| match dbg!(&*parsed.syntax().body) { + .map(|parsed| match *parsed.syntax().body { ast::Expr::Call(_) => "cannot assign to function call".into(), ast::Expr::BinOp(_) => "cannot assign to expression".into(), + ast::Expr::If(_) => "cannot assign to conditional expression".into(), + ast::Expr::Generator(_) => "cannot assign to generator expression".into(), + ast::Expr::StringLiteral(_) + | ast::Expr::BytesLiteral(_) + | ast::Expr::NumberLiteral(_) => { + "cannot assign to literal here. Maybe you meant '==' instead of '='?" + .into() + } + ast::Expr::EllipsisLiteral(_) => { + "cannot assign to ellipsis here. Maybe you meant '==' instead of '='?" + .into() + } _ => format!("cannot assign to {expr_str}"), }) - .unwrap_or_else(|| format!("cannot assign to {expr_str}")); + .unwrap_or_else(|| match expr_str { + "yield" => "assignment to yield expression not possible".into(), + _ => format!("cannot assign to {expr_str}"), + }); (ParseErrorType::OtherError(msg), loc, end_loc) } From d456ed9c12e2da2ec81ac4bf94f9bcafc7f53f57 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Wed, 27 May 2026 17:07:10 +0300 Subject: [PATCH 5/7] Clippy --- crates/compiler/src/lib.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs index a40a1e51b79..4c0c4c0fc05 100644 --- a/crates/compiler/src/lib.rs +++ b/crates/compiler/src/lib.rs @@ -130,9 +130,12 @@ impl CompileError { let expr_str = source_file.source_text().slice(&error.location); - let msg = parser::parse_expression(expr_str) - .ok() - .map(|parsed| match *parsed.syntax().body { + let msg = parser::parse_expression(expr_str).map_or_else( + |_| match expr_str { + "yield" => "assignment to yield expression not possible".into(), + _ => format!("cannot assign to {expr_str}"), + }, + |parsed| match *parsed.syntax().body { ast::Expr::Call(_) => "cannot assign to function call".into(), ast::Expr::BinOp(_) => "cannot assign to expression".into(), ast::Expr::If(_) => "cannot assign to conditional expression".into(), @@ -148,11 +151,9 @@ impl CompileError { .into() } _ => format!("cannot assign to {expr_str}"), - }) - .unwrap_or_else(|| match expr_str { - "yield" => "assignment to yield expression not possible".into(), - _ => format!("cannot assign to {expr_str}"), - }); + }, + ); + (ParseErrorType::OtherError(msg), loc, end_loc) } From e6bf997391cd8d58a8809c2519745804449c9c06 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Wed, 27 May 2026 20:03:45 +0300 Subject: [PATCH 6/7] clippy --- crates/compiler/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs index 4c0c4c0fc05..9c0884c7520 100644 --- a/crates/compiler/src/lib.rs +++ b/crates/compiler/src/lib.rs @@ -128,7 +128,7 @@ impl CompileError { end_loc.character_offset = end_loc.character_offset.saturating_add(1); } - let expr_str = source_file.source_text().slice(&error.location); + let expr_str = source_file.source_text().slice(error.location); let msg = parser::parse_expression(expr_str).map_or_else( |_| match expr_str { @@ -171,7 +171,7 @@ impl CompileError { end_loc.character_offset = end_loc.character_offset.saturating_add(1); } - let target = source_file.source_text().slice(&error.location); + let target = source_file.source_text().slice(error.location); let msg = format!("cannot use assignment expressions with {target}"); (ParseErrorType::OtherError(msg), loc, end_loc) } From 86984a04b1f287f919b11e30d38805f28f3a86f9 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Wed, 27 May 2026 20:10:33 +0300 Subject: [PATCH 7/7] Unmark passing tests --- Lib/test/test_generators.py | 2 +- Lib/test/test_genexps.py | 2 +- Lib/test/test_named_expressions.py | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index 49bf5bc6aea..8ede6e22fab 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -2525,7 +2525,7 @@ def printsolution(self, x): ... SyntaxError: 'yield from' outside function ->>> def f(): x = yield = y # TODO: RUSTPYTHON # doctest: +EXPECTED_FAILURE +>>> def f(): x = yield = y Traceback (most recent call last): ... SyntaxError: assignment to yield expression not possible diff --git a/Lib/test/test_genexps.py b/Lib/test/test_genexps.py index ae16318ff45..fde12f13cdc 100644 --- a/Lib/test/test_genexps.py +++ b/Lib/test/test_genexps.py @@ -154,7 +154,7 @@ Verify that syntax error's are raised for genexps used as lvalues - >>> (y for y in (1,2)) = 10 # TODO: RUSTPYTHON # doctest: +EXPECTED_FAILURE + >>> (y for y in (1,2)) = 10 Traceback (most recent call last): ... SyntaxError: cannot assign to generator expression diff --git a/Lib/test/test_named_expressions.py b/Lib/test/test_named_expressions.py index 617107e108e..4f92176b301 100644 --- a/Lib/test/test_named_expressions.py +++ b/Lib/test/test_named_expressions.py @@ -90,7 +90,6 @@ def test_named_expression_invalid_14(self): with self.assertRaisesRegex(SyntaxError, "invalid syntax"): exec(code, {}, {}) - @unittest.expectedFailure # TODO: RUSTPYTHON; wrong error message def test_named_expression_invalid_15(self): code = """(lambda: x := 1)"""