From a97f0fec4937672756578896a35d3f679d469e6f Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sun, 28 Jun 2026 12:30:50 +0300 Subject: [PATCH 1/3] `FutureFeature` enum --- crates/codegen/src/compile.rs | 127 +++++++++++++++++++++++++++++----- 1 file changed, 109 insertions(+), 18 deletions(-) diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index 711ac392694..39500f1ad31 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -201,6 +201,90 @@ enum DoneWithFuture { Yes, } +/// A Python `__future__` feature flag imported via `from __future__ import `. +/// +/// # See Also +/// +/// - [Python documentation on `__future__`](https://docs.python.org/3.14/library/__future__.html) +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum FutureFeature { + /// ```py + /// from __future__ import absolute_import + /// ``` + AbsoluteImport, + + /// ```py + /// from __future__ import annotations + /// ``` + Annotations, + + /// ```py + /// from __future__ import barry_as_FLUFL + /// ``` + BarryAsFLUFL, + + /// ```py + /// from __future__ import braces + /// ``` + Braces, + + /// ```py + /// from __future__ import division + /// ``` + Division, + + /// ```py + /// from __future__ import generator_stop + /// ``` + GeneratorStop, + + /// ```py + /// from __future__ import generators + /// ``` + Generators, + + /// ```py + /// from __future__ import nested_scopes + /// ``` + NestedScopes, + + /// ```py + /// from __future__ import print_function + /// ``` + PrintFunction, + + /// ```py + /// from __future__ import unicode_literals + /// ``` + UnicodeLiterals, + + /// ```py + /// from __future__ import with_statement + /// ``` + WithStatement, +} + +impl TryFrom<&str> for FutureFeature { + type Error = String; + + fn try_from(name: &str) -> Result { + Ok(match name { + "absolute_import" => Self::AbsoluteImport, + "annotations" => Self::Annotations, + "barry_as_FLUFL" => Self::BarryAsFLUFL, + "braces" => Self::Braces, + "division" => Self::Division, + "generator_stop" => Self::GeneratorStop, + "generators" => Self::Generators, + "nested_scopes" => Self::NestedScopes, + "print_function" => Self::PrintFunction, + "unicode_literals" => Self::UnicodeLiterals, + "with_statement" => Self::WithStatement, + _ => return Err(name.into()), + }) + } +} + #[derive(Clone, Copy)] enum ComprehensionSymbolSource { Child, @@ -11144,16 +11228,21 @@ impl<'warnings> Compiler<'warnings> { if let DoneWithFuture::Yes = self.done_with_future_stmts { return Err(self.error(CodegenErrorType::InvalidFuturePlacement)); } + self.done_with_future_stmts = DoneWithFuture::DoneWithDoc; + for feature in features { - match feature.name.as_str() { - // Python 3 features; we've already implemented them by default - "nested_scopes" | "generators" | "division" | "absolute_import" - | "with_statement" | "print_function" | "unicode_literals" | "generator_stop" => {} - // Accept the future feature name, but do not implement - // Barry-as-BDFL parser mode. - "barry_as_FLUFL" => {} - "annotations" => { + let future_feature = feature.name.as_str().try_into().map_err(|name| { + self.error_ranged(CodegenErrorType::InvalidFutureFeature(name), feature.range) + })?; + + match future_feature { + FutureFeature::Braces => { + return Err( + self.error_ranged(CodegenErrorType::InvalidFutureBraces, feature.range) + ); + } + FutureFeature::Annotations => { self.future_annotations = true; self.future_features .insert(bytecode::CodeFlags::FUTURE_ANNOTATIONS); @@ -11161,16 +11250,18 @@ impl<'warnings> Compiler<'warnings> { .flags .insert(bytecode::CodeFlags::FUTURE_ANNOTATIONS); } - "braces" => { - return Err( - self.error_ranged(CodegenErrorType::InvalidFutureBraces, feature.range) - ); - } - other => { - return Err(self.error_ranged( - CodegenErrorType::InvalidFutureFeature(other.to_owned()), - feature.range, - )); + FutureFeature::BarryAsFLUFL => { + // We do not support Barry-as-BDFL parser mode yet. This is a nop for now. + } + FutureFeature::AbsoluteImport + | FutureFeature::Division + | FutureFeature::GeneratorStop + | FutureFeature::Generators + | FutureFeature::NestedScopes + | FutureFeature::PrintFunction + | FutureFeature::UnicodeLiterals + | FutureFeature::WithStatement => { + // Python 3 features. They are already implemented by default. } } } From b68f0e2c2d1450b06414559219057bd3a5f1f089 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sun, 28 Jun 2026 14:21:13 +0300 Subject: [PATCH 2/3] Use it in preprocess as well --- crates/codegen/src/preprocess.rs | 48 +++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/crates/codegen/src/preprocess.rs b/crates/codegen/src/preprocess.rs index f6ca18b67ba..fac41866002 100644 --- a/crates/codegen/src/preprocess.rs +++ b/crates/codegen/src/preprocess.rs @@ -8,6 +8,8 @@ use ruff_python_ast::{ visitor::transformer::{self, Transformer}, }; use ruff_text_size::{Ranged, TextRange}; + +use crate::compile::FutureFeature; use rustpython_compiler_core::bytecode; const MAXDIGITS: usize = 3; @@ -232,30 +234,41 @@ pub fn checked_future_features_in_body( .. }) if *level == 0 && module.as_ref().map(|id| id.as_str()) == Some("__future__") => { for alias in names { - match alias.name.as_str() { - "nested_scopes" | "generators" | "division" | "absolute_import" - | "with_statement" | "print_function" | "unicode_literals" - | "generator_stop" => {} - "annotations" => { - future_features.insert(bytecode::CodeFlags::FUTURE_ANNOTATIONS); - } - // Accept the future feature name, but leave it - // as a RustPython no-op. - "barry_as_FLUFL" => {} - "braces" => { - return Err(FutureFeatureError { + let future_feature = + alias + .name + .as_str() + .try_into() + .map_err(|name| FutureFeatureError { features: future_features, range: alias.range, - kind: FutureFeatureErrorKind::InvalidBraces, - }); - } - other => { + kind: FutureFeatureErrorKind::InvalidFeature(name), + })?; + + match future_feature { + FutureFeature::Braces => { return Err(FutureFeatureError { features: future_features, range: alias.range, - kind: FutureFeatureErrorKind::InvalidFeature(other.to_owned()), + kind: FutureFeatureErrorKind::InvalidBraces, }); } + FutureFeature::Annotations => { + future_features.insert(bytecode::CodeFlags::FUTURE_ANNOTATIONS) + } + FutureFeature::BarryAsFLUFL => { + // We do not support Barry-as-BDFL parser mode yet. This is a nop for now. + } + FutureFeature::AbsoluteImport + | FutureFeature::Division + | FutureFeature::GeneratorStop + | FutureFeature::Generators + | FutureFeature::NestedScopes + | FutureFeature::PrintFunction + | FutureFeature::UnicodeLiterals + | FutureFeature::WithStatement => { + // Python 3 features. They are already implemented by default. + } } } } @@ -298,6 +311,7 @@ pub fn preprocess_mod( } } +#[derive(Clone, Copy, Debug, Eq, PartialEq)] struct AstPreprocessor { optimize: u8, future_annotations: bool, From 1bb8fa969e03238cb396f615d0e79a05e627b2f6 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sun, 28 Jun 2026 14:22:26 +0300 Subject: [PATCH 3/3] clippy --- crates/codegen/src/preprocess.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/codegen/src/preprocess.rs b/crates/codegen/src/preprocess.rs index fac41866002..084b72c87f7 100644 --- a/crates/codegen/src/preprocess.rs +++ b/crates/codegen/src/preprocess.rs @@ -319,7 +319,7 @@ struct AstPreprocessor { } impl AstPreprocessor { - fn visit_astfold_body(&self, body: &mut ast::Suite) { + fn visit_astfold_body(self, body: &mut ast::Suite) { let mut docstring = body_starts_with_docstring(body); if docstring && self.optimize >= 2 { remove_docstring_from_body(body);