diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7c56fc620..7bf9efea7 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -97,6 +97,13 @@ jobs: - uses: dtolnay/rust-toolchain@stable + - name: Use a short Cargo home on Windows + if: runner.os == 'Windows' + shell: pwsh + run: | + "CARGO_HOME=$env:RUNNER_TEMP\c" >> $env:GITHUB_ENV + git config --global core.longpaths true + - name: Restore cache uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -105,6 +112,9 @@ jobs: ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ + ${{ runner.temp }}/c/registry/index/ + ${{ runner.temp }}/c/registry/cache/ + ${{ runner.temp }}/c/git/db/ target/ key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml') }}- restore-keys: | @@ -338,6 +348,13 @@ jobs: - uses: dtolnay/rust-toolchain@stable + - name: Use a short Cargo home on Windows + if: runner.os == 'Windows' + shell: pwsh + run: | + "CARGO_HOME=$env:RUNNER_TEMP\c" >> $env:GITHUB_ENV + git config --global core.longpaths true + - name: Restore cache uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -346,6 +363,9 @@ jobs: ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ + ${{ runner.temp }}/c/registry/index/ + ${{ runner.temp }}/c/registry/cache/ + ${{ runner.temp }}/c/git/db/ target/ key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml') }}- restore-keys: | @@ -499,6 +519,13 @@ jobs: with: components: clippy + - name: Use a short Cargo home on Windows + if: runner.os == 'Windows' + shell: pwsh + run: | + "CARGO_HOME=$env:RUNNER_TEMP\c" >> $env:GITHUB_ENV + git config --global core.longpaths true + - name: Restore cache uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -507,6 +534,9 @@ jobs: ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ + ${{ runner.temp }}/c/registry/index/ + ${{ runner.temp }}/c/registry/cache/ + ${{ runner.temp }}/c/git/db/ target/ key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml') }}- restore-keys: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5957666de..6fa03af23 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -60,6 +60,13 @@ jobs: with: target: ${{ matrix.target }} + - name: Use a short Cargo home on Windows + if: runner.os == 'Windows' + shell: pwsh + run: | + "CARGO_HOME=$env:RUNNER_TEMP\c" >> $env:GITHUB_ENV + git config --global core.longpaths true + - name: Install macOS dependencies uses: ./.github/actions/install-macos-deps with: diff --git a/.github/workflows/update-caches.yml b/.github/workflows/update-caches.yml index 4667c07fe..d3f5da9c1 100644 --- a/.github/workflows/update-caches.yml +++ b/.github/workflows/update-caches.yml @@ -52,6 +52,13 @@ jobs: toolchain: ${{ matrix.toolchain }} target: ${{ matrix.target }} + - name: Use a short Cargo home on Windows + if: runner.os == 'Windows' + shell: pwsh + run: | + "CARGO_HOME=$env:RUNNER_TEMP\c" >> $env:GITHUB_ENV + git config --global core.longpaths true + - name: Install macos dependencies uses: ./.github/actions/install-macos-deps with: @@ -74,5 +81,8 @@ jobs: ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ + ${{ runner.temp }}/c/registry/index/ + ${{ runner.temp }}/c/registry/cache/ + ${{ runner.temp }}/c/git/db/ target/ key: ${{ runner.os }}-${{ matrix.toolchain }}-${{ matrix.target }}-${{ hashFiles('**/Cargo.toml') }}-${{ hashFiles('Cargo.lock') }}-${{ github.sha }} diff --git a/Cargo.lock b/Cargo.lock index 0b0487982..2f99dfac9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -502,6 +502,18 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "char_str" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "576ba56f6ca18ebb069d0d07260407171712aff4dfe15ee3f8982dc16a455bcf" +dependencies = [ + "castaway", + "get-size2", + "itoa", + "ryu", +] + [[package]] name = "ciborium" version = "0.2.2" @@ -691,16 +703,15 @@ dependencies = [ [[package]] name = "compact_str" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dfdd1c2274d9aa354115b09dc9a901d6c5576818cdf70d14cae2bdb47df00ab" +checksum = "79fcda08c33bb58b97008b2cdada6622500e949e060f5913361763121abd2416" dependencies = [ "castaway", "cfg-if", "itoa", - "rustversion", - "ryu", "static_assertions", + "zmij", ] [[package]] @@ -1190,6 +1201,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "drop_bomb" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1" + [[package]] name = "dunce" version = "1.0.5" @@ -1378,9 +1395,9 @@ dependencies = [ [[package]] name = "get-size-derive2" -version = "0.7.4" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b6d1e2f75c16bfbcd0f95d84f99858a6e2f885c2287d1f5c3a96e8444a34b4" +checksum = "c736d226c32e496b8377813b52269e11ad3a48d8373b68862d0364f04fd1229d" dependencies = [ "attribute-derive", "quote", @@ -1389,15 +1406,16 @@ dependencies = [ [[package]] name = "get-size2" -version = "0.7.4" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49cf31a6d70300cf81461098f7797571362387ef4bf85d32ac47eaa59b3a5a1a" +checksum = "b411f34418305908ab15a82ff78958c2a9aee9a272b2a2e663836b20a4e4b9d3" dependencies = [ "compact_str", "get-size-derive2", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "ordermap", "smallvec", + "thin-vec", ] [[package]] @@ -3510,11 +3528,13 @@ dependencies = [ [[package]] name = "rustpython-ruff_python_ast" -version = "0.15.9" -source = "git+https://github.com/RustPython/ruff.git?tag=0.15.19-rustpython#3c1ab2dd9987f190f3df94d28452ed5e65e106af" +version = "0.16.5" +source = "git+https://github.com/RustPython/ruff.git?tag=0.16.5-rustpython#df59fe4c720b48a29fe8726a37e2ef06322096f8" dependencies = [ "aho-corasick", + "arrayvec", "bitflags 2.13.1", + "char_str", "compact_str", "get-size2", "is-macro", @@ -3523,24 +3543,27 @@ dependencies = [ "rustpython-ruff_python_trivia", "rustpython-ruff_source_file", "rustpython-ruff_text_size", + "thin-vec", "thiserror", ] [[package]] name = "rustpython-ruff_python_parser" -version = "0.15.9" -source = "git+https://github.com/RustPython/ruff.git?tag=0.15.19-rustpython#3c1ab2dd9987f190f3df94d28452ed5e65e106af" +version = "0.16.5" +source = "git+https://github.com/RustPython/ruff.git?tag=0.16.5-rustpython#df59fe4c720b48a29fe8726a37e2ef06322096f8" dependencies = [ "bitflags 2.13.1", "bstr", - "compact_str", + "drop_bomb", "get-size2", "memchr", "rustc-hash", "rustpython-ruff_python_ast", "rustpython-ruff_python_trivia", "rustpython-ruff_text_size", + "stacker", "static_assertions", + "thin-vec", "unicode-ident", "unicode-normalization", "unicode_names2 1.3.0", @@ -3548,10 +3571,11 @@ dependencies = [ [[package]] name = "rustpython-ruff_python_trivia" -version = "0.15.9" -source = "git+https://github.com/RustPython/ruff.git?tag=0.15.19-rustpython#3c1ab2dd9987f190f3df94d28452ed5e65e106af" +version = "0.16.5" +source = "git+https://github.com/RustPython/ruff.git?tag=0.16.5-rustpython#df59fe4c720b48a29fe8726a37e2ef06322096f8" dependencies = [ - "itertools 0.14.0", + "itertools 0.15.0", + "rustc-hash", "rustpython-ruff_source_file", "rustpython-ruff_text_size", "unicode-ident", @@ -3559,8 +3583,8 @@ dependencies = [ [[package]] name = "rustpython-ruff_source_file" -version = "0.15.9" -source = "git+https://github.com/RustPython/ruff.git?tag=0.15.19-rustpython#3c1ab2dd9987f190f3df94d28452ed5e65e106af" +version = "0.16.5" +source = "git+https://github.com/RustPython/ruff.git?tag=0.16.5-rustpython#df59fe4c720b48a29fe8726a37e2ef06322096f8" dependencies = [ "memchr", "rustpython-ruff_text_size", @@ -3568,8 +3592,8 @@ dependencies = [ [[package]] name = "rustpython-ruff_text_size" -version = "0.15.9" -source = "git+https://github.com/RustPython/ruff.git?tag=0.15.19-rustpython#3c1ab2dd9987f190f3df94d28452ed5e65e106af" +version = "0.16.5" +source = "git+https://github.com/RustPython/ruff.git?tag=0.16.5-rustpython#df59fe4c720b48a29fe8726a37e2ef06322096f8" dependencies = [ "get-size2", ] @@ -4077,6 +4101,19 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "stacker" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707f49d46706bacf8a2b00d51dace3f9de527c13eec3778f570c411f89e69967" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.61.2", +] + [[package]] name = "static_assertions" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 3fd59e088..7f5a0b64a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -184,10 +184,10 @@ rustpython-wtf8 = { path = "crates/wtf8", version = "0.5.0" } rustpython-doc = { path = "crates/doc", version = "0.5.0" } # Use the RustPython Ruff fork for RustPython public `_ast` metadata. -ruff_python_parser = { package = "rustpython-ruff_python_parser", git = "https://github.com/RustPython/ruff.git", tag = "0.15.19-rustpython" } -ruff_python_ast = { package = "rustpython-ruff_python_ast", git = "https://github.com/RustPython/ruff.git", tag = "0.15.19-rustpython" } -ruff_text_size = { package = "rustpython-ruff_text_size", git = "https://github.com/RustPython/ruff.git", tag = "0.15.19-rustpython" } -ruff_source_file = { package = "rustpython-ruff_source_file", git = "https://github.com/RustPython/ruff.git", tag = "0.15.19-rustpython" } +ruff_python_parser = { package = "rustpython-ruff_python_parser", git = "https://github.com/RustPython/ruff.git", tag = "0.16.5-rustpython" } +ruff_python_ast = { package = "rustpython-ruff_python_ast", git = "https://github.com/RustPython/ruff.git", tag = "0.16.5-rustpython" } +ruff_text_size = { package = "rustpython-ruff_text_size", git = "https://github.com/RustPython/ruff.git", tag = "0.16.5-rustpython" } +ruff_source_file = { package = "rustpython-ruff_source_file", git = "https://github.com/RustPython/ruff.git", tag = "0.16.5-rustpython" } der = { version = "0.8", features = ["alloc", "oid", "pem", "zeroize"] } phf = { version = "0.14.0", default-features = false, features = ["macros"]} diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 6dab7dfa1..44d0cc7aa 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -1468,7 +1468,7 @@ Ensure that early = are not matched by the parser as invalid comparisons Traceback (most recent call last): SyntaxError: invalid syntax - >>> dict(x=34, x=1, y=2); x $ y # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> dict(x=34, x=1, y=2); x $ y Traceback (most recent call last): SyntaxError: invalid syntax diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index 8a4a37319..6f06eb4ee 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -24,7 +24,7 @@ use malachite_bigint::BigInt; use num_complex::Complex; use num_traits::{Num, ToPrimitive, Zero}; use ruff_python_ast::{self as ast, name::Name}; -use ruff_text_size::{Ranged, TextRange, TextSize}; +use ruff_text_size::{Ranged, TextRange, TextSize, TextSlice}; use rustpython_compiler_core::{ Mode, OneIndexed, PositionEncoding, SourceFile, SourceLocation, bytecode::{ @@ -9129,7 +9129,12 @@ impl<'warnings> Compiler<'warnings> { range, .. }) => { - let key = key.as_ref(); + let Some(key) = key.as_deref() else { + self.set_source_range(*range); + return Err(self.error(CodegenErrorType::SyntaxError( + "dict unpacking cannot be used in dict comprehension".to_owned(), + ))); + }; self.compile_comprehension( "", Some( @@ -9644,7 +9649,11 @@ impl<'warnings> Compiler<'warnings> { continue; }; for other in &keywords[i + 1..] { - if other.arg.as_ref() == Some(arg) { + if other + .arg + .as_ref() + .is_some_and(|other| other.as_str() == arg.as_str()) + { return Err(self.error_ranged( CodegenErrorType::SyntaxError(format!("keyword argument repeated: {arg}")), other.range, @@ -10067,7 +10076,11 @@ impl<'warnings> Compiler<'warnings> { self.visit_expr(&first.iter); } if self.consume_inlined_comprehension_scope() { - self.visit_comprehension_tail(key, Some(value), generators); + if let Some(key) = key.as_deref() { + self.visit_comprehension_tail(key, Some(value), generators); + } else { + self.visit_comprehension_tail(value, None, generators); + } } else { self.consume_scope(); } @@ -13249,9 +13262,10 @@ impl<'warnings> Compiler<'warnings> { { let after_brace = interp.range.start() + TextSize::new(1); self.source_file + .source_text() .slice(TextRange::new(after_brace, expr_range.end())) } else { - self.source_file.slice(expr_range) + self.source_file.source_text().slice(expr_range) } .to_string(); self.set_source_range(interp.range); @@ -13843,7 +13857,7 @@ mod tests { fn frozenset_call_expr() -> ast::Expr { ast::Expr::Call(ast::ExprCall { node_index: ast::AtomicNodeIndex::NONE, - range: TextRange::default(), + range_start: TextSize::default(), func: Box::new(ast::Expr::Name(ast::ExprName { node_index: ast::AtomicNodeIndex::NONE, range: TextRange::default(), @@ -13996,7 +14010,7 @@ mod tests { ); let message = first_ast_constant_warning(ast::Expr::Call(ast::ExprCall { node_index: ast::AtomicNodeIndex::NONE, - range: TextRange::default(), + range_start: TextSize::default(), func: Box::new(func), arguments: ast::Arguments { node_index: ast::AtomicNodeIndex::NONE, diff --git a/crates/codegen/src/lib.rs b/crates/codegen/src/lib.rs index b7ddb357e..0bc16d72d 100644 --- a/crates/codegen/src/lib.rs +++ b/crates/codegen/src/lib.rs @@ -24,7 +24,7 @@ mod unparse; pub use compile::CompileOpts; use ruff_python_ast as ast; -use ruff_text_size::{Ranged, TextRange, TextSize}; +use ruff_text_size::{Ranged, TextRange, TextSize, TextSlice}; use rustpython_compiler_core::SourceFile; use rustpython_wtf8::Wtf8Buf; @@ -116,7 +116,7 @@ pub fn decorated_definition_range( return statement_range; } let search_range = TextRange::new(search_start, statement_range.end()); - let source = source_file.slice(search_range); + let source = source_file.source_text().slice(search_range); let Some(keyword_offset) = source.find(keyword) else { return statement_range; }; @@ -151,7 +151,7 @@ pub fn string_literal_value(source_file: &SourceFile, string: &ast::StringLitera #[must_use] pub fn string_literal_part_value(source_file: &SourceFile, string: &ast::StringLiteral) -> Wtf8Buf { if string.value.contains(char::REPLACEMENT_CHARACTER) { - let source = source_file.slice(string.range); + let source = source_file.source_text().slice(string.range); string_parser::parse_string_literal(source, string.flags.into()).into() } else { string.value.to_string().into() @@ -167,7 +167,7 @@ pub fn interpolated_string_literal_value( flags: ast::AnyStringFlags, ) -> Wtf8Buf { if element.value.contains(char::REPLACEMENT_CHARACTER) { - let source = source_file.slice(element.range); + let source = source_file.source_text().slice(element.range); string_parser::parse_fstring_literal_element(source.into(), flags).into() } else { element.value.to_string().into() @@ -186,11 +186,11 @@ pub fn interpolation_debug_text( debug_text: &ast::DebugText, expression_range: TextRange, ) -> (String, TextRange) { - let leading = debug_text.leading.as_str(); - let trailing = debug_text.trailing.as_str(); + let leading = debug_text.leading(); + let trailing = debug_text.trailing(); let text = [ strip_python_comments(leading).as_str(), - source_file.slice(expression_range), + source_file.source_text().slice(expression_range), strip_python_comments(trailing).as_str(), ] .concat(); diff --git a/crates/codegen/src/symboltable.rs b/crates/codegen/src/symboltable.rs index 173921654..d936bfc1a 100644 --- a/crates/codegen/src/symboltable.rs +++ b/crates/codegen/src/symboltable.rs @@ -2376,7 +2376,12 @@ impl SymbolTableBuilder { self.in_iter_def_exp = true; } // Dict comprehension - is_generator = false (can be inlined) - let key = key.as_ref(); + let Some(key) = key.as_deref() else { + return Err(self.error_ranged( + "dict unpacking cannot be used in dict comprehension".to_owned(), + *range, + )); + }; self.scan_comprehension( &"".into(), key, diff --git a/crates/codegen/src/unparse.rs b/crates/codegen/src/unparse.rs index f890edf37..86744a601 100644 --- a/crates/codegen/src/unparse.rs +++ b/crates/codegen/src/unparse.rs @@ -2,7 +2,7 @@ use crate::strip_python_comments; use alloc::fmt; use core::fmt::Display as _; use ruff_python_ast as ast; -use ruff_text_size::{Ranged, TextSize}; +use ruff_text_size::{Ranged, TextSize, TextSlice}; use rustpython_compiler_core::SourceFile; use rustpython_literal::escape::{AsciiEscape, UnicodeEscape}; @@ -322,8 +322,12 @@ impl<'a, 'b, 'c> Unparser<'a, 'b, 'c> { .. }) => { self.p("{")?; - self.unparse_expr(key, precedence::TEST)?; - self.p(": ")?; + if let Some(key) = key { + self.unparse_expr(key, precedence::TEST)?; + self.p(": ")?; + } else { + self.p("**")?; + } self.unparse_expr(value, precedence::TEST)?; self.unparse_comp(generators)?; self.p("}")?; @@ -399,7 +403,7 @@ impl<'a, 'b, 'c> Unparser<'a, 'b, 'c> { func, arguments: ast::Arguments { args, keywords, .. }, node_index: _, - range: _range, + range_start: _range_start, .. }) => { self.unparse_expr(func, precedence::ATOM)?; @@ -618,10 +622,10 @@ impl<'a, 'b, 'c> Unparser<'a, 'b, 'c> { fmt::from_fn(|f| Unparser::new(f, self.source).unparse_expr(val, precedence::TEST + 1)) .to_string(); if let Some(debug_text) = debug_text { - let leading = debug_text.leading.as_str(); - let trailing = debug_text.trailing.as_str(); + let leading = debug_text.leading(); + let trailing = debug_text.trailing(); self.p(leading)?; - self.p(self.source.slice(val.range()))?; + self.p(self.source.source_text().slice(val.range()))?; self.p(trailing)?; if conversion == ast::ConversionFlag::None && spec.is_none() { conversion = ast::ConversionFlag::Repr; @@ -744,9 +748,13 @@ impl<'a, 'b, 'c> Unparser<'a, 'b, 'c> { let mut conversion = source_conversion; let debug_parts = interpolation.debug_text.as_ref().map(|debug_text| { ( - strip_python_comments(debug_text.leading.as_str()), - strip_python_comments(self.source.slice(interpolation.expression.range())), - strip_python_comments(debug_text.trailing.as_str()), + strip_python_comments(debug_text.leading()), + strip_python_comments( + self.source + .source_text() + .slice(interpolation.expression.range()), + ), + strip_python_comments(debug_text.trailing()), ) }); if let Some((leading, source, trailing)) = &debug_parts { @@ -784,6 +792,7 @@ impl<'a, 'b, 'c> Unparser<'a, 'b, 'c> { { strip_python_comments( self.source + .source_text() .slice(ruff_text_size::TextRange::new(after_brace, expression_end)), ) .trim_end() diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs index 633587020..e8b24a632 100644 --- a/crates/compiler/src/lib.rs +++ b/crates/compiler/src/lib.rs @@ -6520,11 +6520,11 @@ mod tests { ), ( "t\"x\" b\"y\"", - "cannot mix t-string literals with string or bytes literals", + "Cannot mix t-string literals with string or bytes literals", ), ( "b\"x\" t\"y\"", - "cannot mix t-string literals with string or bytes literals", + "Cannot mix t-string literals with string or bytes literals", ), // The literals ahead of the first t-string already mix, so CPython's first pass // raises from `_PyPegen_concatenate_strings` before the t-string rule is reached. diff --git a/crates/vm/src/stdlib/_ast.rs b/crates/vm/src/stdlib/_ast.rs index 887c3cc7d..7ce719da0 100644 --- a/crates/vm/src/stdlib/_ast.rs +++ b/crates/vm/src/stdlib/_ast.rs @@ -21,7 +21,7 @@ use crate::{ }; use node::Node; use ruff_python_ast as ast; -use ruff_text_size::{Ranged, TextRange, TextSize}; +use ruff_text_size::{Ranged, TextRange, TextSize, TextSlice}; use rustpython_compiler_core::{ LineIndex, OneIndexed, PositionEncoding, SourceFile, SourceFileBuilder, SourceLocation, }; diff --git a/crates/vm/src/stdlib/_ast/argument.rs b/crates/vm/src/stdlib/_ast/argument.rs index 5019c4366..0ea9725a1 100644 --- a/crates/vm/src/stdlib/_ast/argument.rs +++ b/crates/vm/src/stdlib/_ast/argument.rs @@ -137,7 +137,7 @@ pub(super) fn merge_function_call_arguments( node_index: Default::default(), range, args, - keywords: key_args.keywords, + keywords: key_args.keywords.into(), runtime_args, runtime_bases: None, } @@ -173,7 +173,7 @@ pub(super) fn split_function_call_arguments( // debug_assert!(range.contains_range(keyword_arguments_range)); let keyword_arguments = KeywordArguments { range: keyword_arguments_range, - keywords, + keywords: keywords.into(), }; (positional_arguments, keyword_arguments) @@ -214,7 +214,7 @@ pub(super) fn split_class_def_args( // debug_assert!(range.contains_range(keyword_arguments_range)); let keyword_arguments = KeywordArguments { range: keyword_arguments_range, - keywords, + keywords: keywords.into(), }; (Some(positional_arguments), Some(keyword_arguments)) @@ -243,7 +243,7 @@ pub(super) fn merge_class_def_args( node_index: Default::default(), range: Default::default(), // TODO args, - keywords, + keywords: keywords.into(), runtime_args: None, runtime_bases, })) diff --git a/crates/vm/src/stdlib/_ast/expression.rs b/crates/vm/src/stdlib/_ast/expression.rs index 97a3519ea..7c64e4f79 100644 --- a/crates/vm/src/stdlib/_ast/expression.rs +++ b/crates/vm/src/stdlib/_ast/expression.rs @@ -855,7 +855,13 @@ fn expr_dict_comp_from_object_with_range( ) -> PyResult { Ok(ast::ExprDictComp { node_index: Default::default(), - key: get_required_node_field(vm, source_file, &object, "key", "DictComp")?, + key: Some(get_required_node_field( + vm, + source_file, + &object, + "key", + "DictComp", + )?), value: get_required_node_field(vm, source_file, &object, "value", "DictComp")?, generators: get_node_list_field(vm, source_file, &object, "generators", "DictComp")?, range, @@ -1130,24 +1136,27 @@ fn expr_call_from_object_with_range( object: PyObjectRef, range: TextRange, ) -> PyResult { + let mut arguments = merge_function_call_arguments( + PositionalArguments::ast_from_field(vm, source_file, &object, "args", "Call")?, + KeywordArguments::ast_from_field(vm, source_file, &object, "keywords", "Call")?, + ); + arguments.range = range; Ok(ast::ExprCall { node_index: Default::default(), func: get_required_node_field(vm, source_file, &object, "func", "Call")?, - arguments: merge_function_call_arguments( - PositionalArguments::ast_from_field(vm, source_file, &object, "args", "Call")?, - KeywordArguments::ast_from_field(vm, source_file, &object, "keywords", "Call")?, - ), - range, + arguments, + range_start: range.start(), }) } impl Node for ast::ExprCall { fn ast_to_object(self, vm: &VirtualMachine, source_file: &SourceFile) -> PyObjectRef { + let range = self.range(); let Self { node_index: _, func, arguments, - range, + range_start: _, } = self; let (positional_arguments, keyword_arguments) = split_function_call_arguments(arguments); let node = NodeAst diff --git a/crates/vm/src/stdlib/_ast/parameter.rs b/crates/vm/src/stdlib/_ast/parameter.rs index 1ce4973b4..03ff20f12 100644 --- a/crates/vm/src/stdlib/_ast/parameter.rs +++ b/crates/vm/src/stdlib/_ast/parameter.rs @@ -14,10 +14,13 @@ impl Node for ast::Parameters { range: _, runtime_defaults, } = self; - let (posonlyargs, args, mut defaults) = - extract_positional_parameter_defaults(posonlyargs, args); + let (posonlyargs, args, mut defaults) = extract_positional_parameter_defaults( + posonlyargs.into_iter().collect(), + args.into_iter().collect(), + ); defaults.runtime_defaults = runtime_defaults; - let (kwonlyargs, kw_defaults) = extract_keyword_parameter_defaults(kwonlyargs); + let (kwonlyargs, kw_defaults) = + extract_keyword_parameter_defaults(kwonlyargs.into_iter().collect()); let node = NodeAst .into_ref_with_type(vm, pyast::NodeArguments::static_type().to_owned()) .unwrap(); @@ -108,10 +111,10 @@ impl Node for ast::Parameters { Ok(Self { node_index: Default::default(), - posonlyargs, - args, + posonlyargs: posonlyargs.into(), + args: args.into(), vararg, - kwonlyargs, + kwonlyargs: kwonlyargs.into(), kwarg, range: Default::default(), runtime_defaults, diff --git a/crates/vm/src/stdlib/_ast/pattern.rs b/crates/vm/src/stdlib/_ast/pattern.rs index 387fb5900..7c834178b 100644 --- a/crates/vm/src/stdlib/_ast/pattern.rs +++ b/crates/vm/src/stdlib/_ast/pattern.rs @@ -384,8 +384,8 @@ fn pattern_match_mapping_from_object_with_range( .then(|| patterns.clone()); Ok(ast::PatternMatchMapping { node_index: Default::default(), - keys: lower_nullable_exprs(&keys, range), - patterns: lower_nullable_patterns(&patterns, range), + keys: lower_nullable_exprs(&keys, range).into(), + patterns: lower_nullable_patterns(&patterns, range).into(), rest: get_node_field_opt(vm, &object, "rest")? .map(|obj| Node::ast_from_object(vm, source_file, obj)) .transpose()?, @@ -473,7 +473,7 @@ fn pattern_match_class_from_object_with_range( arguments: ast::PatternArguments { node_index: Default::default(), range: Default::default(), - patterns, + patterns: patterns.into(), keywords, }, runtime_patterns, @@ -744,7 +744,7 @@ fn split_pattern_match_class( PatternMatchClassKeywordAttributes, PatternMatchClassKeywordPatterns, ) { - let patterns = PatternMatchClassPatterns(arguments.patterns); + let patterns = PatternMatchClassPatterns(arguments.patterns.into_iter().collect()); let kwd_attrs = PatternMatchClassKeywordAttributes( arguments.keywords.iter().map(|k| k.attr.clone()).collect(), ); diff --git a/crates/vm/src/stdlib/_ast/statement.rs b/crates/vm/src/stdlib/_ast/statement.rs index bf8b03476..167f68f63 100644 --- a/crates/vm/src/stdlib/_ast/statement.rs +++ b/crates/vm/src/stdlib/_ast/statement.rs @@ -365,7 +365,7 @@ fn stmt_function_def_from_object_with_range( name, parameters, body, - decorator_list, + decorator_list: decorator_list.into(), returns, type_params, range, @@ -491,7 +491,7 @@ fn stmt_class_def_from_object_with_range( name, arguments: merge_class_def_args(Some(bases), Some(keywords)), body, - decorator_list, + decorator_list: decorator_list.into(), type_params, range, runtime_decorator_list, @@ -1342,7 +1342,7 @@ fn except_handler_list_from_field( range, type_: None, name: None, - body: Vec::new(), + body: Vec::new().into(), runtime_body: None, }) }); diff --git a/crates/vm/src/stdlib/_ast/string.rs b/crates/vm/src/stdlib/_ast/string.rs index 2e44ce48f..ec7d6eb5a 100644 --- a/crates/vm/src/stdlib/_ast/string.rs +++ b/crates/vm/src/stdlib/_ast/string.rs @@ -801,13 +801,13 @@ fn push_ruff_tstring_element( expression.range(), ))); conversion = debug_conversion(conversion, format_spec.is_some()); - let expr_source = source_file.slice(expr_range); + let expr_source = source_file.source_text().slice(expr_range); let mut expr_with_debug = String::with_capacity( - debug_text.leading.len() + expr_source.len() + debug_text.trailing.len(), + debug_text.leading().len() + expr_source.len() + debug_text.trailing().len(), ); - expr_with_debug.push_str(&debug_text.leading); + expr_with_debug.push_str(debug_text.leading()); expr_with_debug.push_str(expr_source); - expr_with_debug.push_str(&debug_text.trailing); + expr_with_debug.push_str(debug_text.trailing()); strip_interpolation_expr(&expr_with_debug) } else { tstring_interpolation_expr_str(source_file, range, expr_range) @@ -849,7 +849,9 @@ fn tstring_interpolation_expr_str( } else { start }; - let expr_source = source_file.slice(TextRange::new(start, expr_range.end())); + let expr_source = source_file + .source_text() + .slice(TextRange::new(start, expr_range.end())); strip_interpolation_expr(expr_source) } @@ -858,7 +860,7 @@ fn extend_expr_range_with_wrapping_parens( interpolation_range: TextRange, expr_range: TextRange, ) -> Option { - let left_slice = source_file.slice(TextRange::new( + let left_slice = source_file.source_text().slice(TextRange::new( interpolation_range.start(), expr_range.start(), )); @@ -879,8 +881,9 @@ fn extend_expr_range_with_wrapping_parens( return None; } - let right_slice = - source_file.slice(TextRange::new(expr_range.end(), interpolation_range.end())); + let right_slice = source_file + .source_text() + .slice(TextRange::new(expr_range.end(), interpolation_range.end())); let mut right_char: Option<(usize, char)> = None; for (idx, ch) in right_slice.char_indices() { if !ch.is_whitespace() { diff --git a/crates/vm/src/stdlib/_ast/validate.rs b/crates/vm/src/stdlib/_ast/validate.rs index b4dd4a76d..cbdc15b53 100644 --- a/crates/vm/src/stdlib/_ast/validate.rs +++ b/crates/vm/src/stdlib/_ast/validate.rs @@ -593,7 +593,10 @@ fn validate_expr(vm: &VirtualMachine, expr: &ast::Expr, ctx: ast::ExprContext) - } ast::Expr::DictComp(dict) => { validate_comprehension(vm, &dict.generators)?; - validate_expr(vm, &dict.key, ast::ExprContext::Load)?; + let key = dict.key.as_deref().ok_or_else(|| { + vm.new_value_error("field 'key' is required for DictComp".to_owned()) + })?; + validate_expr(vm, key, ast::ExprContext::Load)?; validate_expr(vm, &dict.value, ast::ExprContext::Load) } ast::Expr::Generator(generator) => { diff --git a/crates/vm/src/vm/vm_new.rs b/crates/vm/src/vm/vm_new.rs index d1890bbc0..e2c73daf5 100644 --- a/crates/vm/src/vm/vm_new.rs +++ b/crates/vm/src/vm/vm_new.rs @@ -152,10 +152,6 @@ impl SyntaxErrorInfo { ParseErrorType::EmptyImportNames => "Expected one or more names after 'import'".into(), - ParseErrorType::DuplicateKeywordArgumentError(arg_name) => { - format!("keyword argument repeated: {arg_name}") - } - ParseErrorType::UnparenthesizedGeneratorExpression => { "Generator expression must be parenthesized".into() }