Skip to content

Commit eae567f

Browse files
authored
FutureFeature enum (RustPython#8185)
1 parent e831e24 commit eae567f

2 files changed

Lines changed: 141 additions & 36 deletions

File tree

crates/codegen/src/compile.rs

Lines changed: 109 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,90 @@ enum DoneWithFuture {
201201
Yes,
202202
}
203203

204+
/// A Python `__future__` feature flag imported via `from __future__ import <feature>`.
205+
///
206+
/// # See Also
207+
///
208+
/// - [Python documentation on `__future__`](https://docs.python.org/3.14/library/__future__.html)
209+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
210+
pub enum FutureFeature {
211+
/// ```py
212+
/// from __future__ import absolute_import
213+
/// ```
214+
AbsoluteImport,
215+
216+
/// ```py
217+
/// from __future__ import annotations
218+
/// ```
219+
Annotations,
220+
221+
/// ```py
222+
/// from __future__ import barry_as_FLUFL
223+
/// ```
224+
BarryAsFLUFL,
225+
226+
/// ```py
227+
/// from __future__ import braces
228+
/// ```
229+
Braces,
230+
231+
/// ```py
232+
/// from __future__ import division
233+
/// ```
234+
Division,
235+
236+
/// ```py
237+
/// from __future__ import generator_stop
238+
/// ```
239+
GeneratorStop,
240+
241+
/// ```py
242+
/// from __future__ import generators
243+
/// ```
244+
Generators,
245+
246+
/// ```py
247+
/// from __future__ import nested_scopes
248+
/// ```
249+
NestedScopes,
250+
251+
/// ```py
252+
/// from __future__ import print_function
253+
/// ```
254+
PrintFunction,
255+
256+
/// ```py
257+
/// from __future__ import unicode_literals
258+
/// ```
259+
UnicodeLiterals,
260+
261+
/// ```py
262+
/// from __future__ import with_statement
263+
/// ```
264+
WithStatement,
265+
}
266+
267+
impl TryFrom<&str> for FutureFeature {
268+
type Error = String;
269+
270+
fn try_from(name: &str) -> Result<Self, Self::Error> {
271+
Ok(match name {
272+
"absolute_import" => Self::AbsoluteImport,
273+
"annotations" => Self::Annotations,
274+
"barry_as_FLUFL" => Self::BarryAsFLUFL,
275+
"braces" => Self::Braces,
276+
"division" => Self::Division,
277+
"generator_stop" => Self::GeneratorStop,
278+
"generators" => Self::Generators,
279+
"nested_scopes" => Self::NestedScopes,
280+
"print_function" => Self::PrintFunction,
281+
"unicode_literals" => Self::UnicodeLiterals,
282+
"with_statement" => Self::WithStatement,
283+
_ => return Err(name.into()),
284+
})
285+
}
286+
}
287+
204288
#[derive(Clone, Copy)]
205289
enum ComprehensionSymbolSource {
206290
Child,
@@ -11144,33 +11228,40 @@ impl<'warnings> Compiler<'warnings> {
1114411228
if let DoneWithFuture::Yes = self.done_with_future_stmts {
1114511229
return Err(self.error(CodegenErrorType::InvalidFuturePlacement));
1114611230
}
11231+
1114711232
self.done_with_future_stmts = DoneWithFuture::DoneWithDoc;
11233+
1114811234
for feature in features {
11149-
match feature.name.as_str() {
11150-
// Python 3 features; we've already implemented them by default
11151-
"nested_scopes" | "generators" | "division" | "absolute_import"
11152-
| "with_statement" | "print_function" | "unicode_literals" | "generator_stop" => {}
11153-
// Accept the future feature name, but do not implement
11154-
// Barry-as-BDFL parser mode.
11155-
"barry_as_FLUFL" => {}
11156-
"annotations" => {
11235+
let future_feature = feature.name.as_str().try_into().map_err(|name| {
11236+
self.error_ranged(CodegenErrorType::InvalidFutureFeature(name), feature.range)
11237+
})?;
11238+
11239+
match future_feature {
11240+
FutureFeature::Braces => {
11241+
return Err(
11242+
self.error_ranged(CodegenErrorType::InvalidFutureBraces, feature.range)
11243+
);
11244+
}
11245+
FutureFeature::Annotations => {
1115711246
self.future_annotations = true;
1115811247
self.future_features
1115911248
.insert(bytecode::CodeFlags::FUTURE_ANNOTATIONS);
1116011249
self.current_code_info()
1116111250
.flags
1116211251
.insert(bytecode::CodeFlags::FUTURE_ANNOTATIONS);
1116311252
}
11164-
"braces" => {
11165-
return Err(
11166-
self.error_ranged(CodegenErrorType::InvalidFutureBraces, feature.range)
11167-
);
11168-
}
11169-
other => {
11170-
return Err(self.error_ranged(
11171-
CodegenErrorType::InvalidFutureFeature(other.to_owned()),
11172-
feature.range,
11173-
));
11253+
FutureFeature::BarryAsFLUFL => {
11254+
// We do not support Barry-as-BDFL parser mode yet. This is a nop for now.
11255+
}
11256+
FutureFeature::AbsoluteImport
11257+
| FutureFeature::Division
11258+
| FutureFeature::GeneratorStop
11259+
| FutureFeature::Generators
11260+
| FutureFeature::NestedScopes
11261+
| FutureFeature::PrintFunction
11262+
| FutureFeature::UnicodeLiterals
11263+
| FutureFeature::WithStatement => {
11264+
// Python 3 features. They are already implemented by default.
1117411265
}
1117511266
}
1117611267
}

crates/codegen/src/preprocess.rs

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use ruff_python_ast::{
88
visitor::transformer::{self, Transformer},
99
};
1010
use ruff_text_size::{Ranged, TextRange};
11+
12+
use crate::compile::FutureFeature;
1113
use rustpython_compiler_core::bytecode;
1214

1315
const MAXDIGITS: usize = 3;
@@ -232,30 +234,41 @@ pub fn checked_future_features_in_body(
232234
..
233235
}) if *level == 0 && module.as_ref().map(|id| id.as_str()) == Some("__future__") => {
234236
for alias in names {
235-
match alias.name.as_str() {
236-
"nested_scopes" | "generators" | "division" | "absolute_import"
237-
| "with_statement" | "print_function" | "unicode_literals"
238-
| "generator_stop" => {}
239-
"annotations" => {
240-
future_features.insert(bytecode::CodeFlags::FUTURE_ANNOTATIONS);
241-
}
242-
// Accept the future feature name, but leave it
243-
// as a RustPython no-op.
244-
"barry_as_FLUFL" => {}
245-
"braces" => {
246-
return Err(FutureFeatureError {
237+
let future_feature =
238+
alias
239+
.name
240+
.as_str()
241+
.try_into()
242+
.map_err(|name| FutureFeatureError {
247243
features: future_features,
248244
range: alias.range,
249-
kind: FutureFeatureErrorKind::InvalidBraces,
250-
});
251-
}
252-
other => {
245+
kind: FutureFeatureErrorKind::InvalidFeature(name),
246+
})?;
247+
248+
match future_feature {
249+
FutureFeature::Braces => {
253250
return Err(FutureFeatureError {
254251
features: future_features,
255252
range: alias.range,
256-
kind: FutureFeatureErrorKind::InvalidFeature(other.to_owned()),
253+
kind: FutureFeatureErrorKind::InvalidBraces,
257254
});
258255
}
256+
FutureFeature::Annotations => {
257+
future_features.insert(bytecode::CodeFlags::FUTURE_ANNOTATIONS)
258+
}
259+
FutureFeature::BarryAsFLUFL => {
260+
// We do not support Barry-as-BDFL parser mode yet. This is a nop for now.
261+
}
262+
FutureFeature::AbsoluteImport
263+
| FutureFeature::Division
264+
| FutureFeature::GeneratorStop
265+
| FutureFeature::Generators
266+
| FutureFeature::NestedScopes
267+
| FutureFeature::PrintFunction
268+
| FutureFeature::UnicodeLiterals
269+
| FutureFeature::WithStatement => {
270+
// Python 3 features. They are already implemented by default.
271+
}
259272
}
260273
}
261274
}
@@ -298,14 +311,15 @@ pub fn preprocess_mod(
298311
}
299312
}
300313

314+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
301315
struct AstPreprocessor {
302316
optimize: u8,
303317
future_annotations: bool,
304318
constant_folding: bool,
305319
}
306320

307321
impl AstPreprocessor {
308-
fn visit_astfold_body(&self, body: &mut ast::Suite) {
322+
fn visit_astfold_body(self, body: &mut ast::Suite) {
309323
let mut docstring = body_starts_with_docstring(body);
310324
if docstring && self.optimize >= 2 {
311325
remove_docstring_from_body(body);

0 commit comments

Comments
 (0)