mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 05:44:56 +00:00
[syntax-errors]: future-feature-not-defined (F407) (#20554)
<!-- Thank you for contributing to Ruff/ty! To help us out with reviewing, please consider the following: - Does this pull request include a summary of the change? (See below.) - Does this pull request include a descriptive title? (Please prefix with `[ty]` for ty pull requests.) - Does this pull request include references to any relevant issues? --> ## Summary <!-- What's the purpose of the change? What does it do, and why? --> This PR implements https://docs.astral.sh/ruff/rules/future-feature-not-defined/ (F407) as a semantic syntax error. ## Test Plan <!-- How was it tested? --> I have written inline tests as directed in #17412 --------- Signed-off-by: 11happy <soni5happy@gmail.com>
This commit is contained in:
parent
6b7a9dc2f2
commit
cfc64d1707
10 changed files with 246 additions and 45 deletions
|
@ -803,11 +803,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for alias in names {
|
for alias in names {
|
||||||
if let Some("__future__") = module {
|
if module != Some("__future__") && &alias.name == "*" {
|
||||||
if checker.is_rule_enabled(Rule::FutureFeatureNotDefined) {
|
|
||||||
pyflakes::rules::future_feature_not_defined(checker, alias);
|
|
||||||
}
|
|
||||||
} else if &alias.name == "*" {
|
|
||||||
// F403
|
// F403
|
||||||
checker.report_diagnostic_if_enabled(
|
checker.report_diagnostic_if_enabled(
|
||||||
pyflakes::rules::UndefinedLocalWithImportStar {
|
pyflakes::rules::UndefinedLocalWithImportStar {
|
||||||
|
|
|
@ -696,6 +696,14 @@ impl SemanticSyntaxContext for Checker<'_> {
|
||||||
self.report_diagnostic(MultipleStarredExpressions, error.range);
|
self.report_diagnostic(MultipleStarredExpressions, error.range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SemanticSyntaxErrorKind::FutureFeatureNotDefined(name) => {
|
||||||
|
if self.is_rule_enabled(Rule::FutureFeatureNotDefined) {
|
||||||
|
self.report_diagnostic(
|
||||||
|
pyflakes::rules::FutureFeatureNotDefined { name },
|
||||||
|
error.range,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
SemanticSyntaxErrorKind::ReboundComprehensionVariable
|
SemanticSyntaxErrorKind::ReboundComprehensionVariable
|
||||||
| SemanticSyntaxErrorKind::DuplicateTypeParameter
|
| SemanticSyntaxErrorKind::DuplicateTypeParameter
|
||||||
| SemanticSyntaxErrorKind::MultipleCaseAssignment(_)
|
| SemanticSyntaxErrorKind::MultipleCaseAssignment(_)
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
use ruff_python_ast::Alias;
|
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_stdlib::future::is_feature_name;
|
|
||||||
use ruff_text_size::Ranged;
|
|
||||||
|
|
||||||
use crate::Violation;
|
use crate::Violation;
|
||||||
use crate::checkers::ast::Checker;
|
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for `__future__` imports that are not defined in the current Python
|
/// Checks for `__future__` imports that are not defined in the current Python
|
||||||
|
@ -19,7 +14,7 @@ use crate::checkers::ast::Checker;
|
||||||
/// - [Python documentation: `__future__`](https://docs.python.org/3/library/__future__.html)
|
/// - [Python documentation: `__future__`](https://docs.python.org/3/library/__future__.html)
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
pub(crate) struct FutureFeatureNotDefined {
|
pub(crate) struct FutureFeatureNotDefined {
|
||||||
name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Violation for FutureFeatureNotDefined {
|
impl Violation for FutureFeatureNotDefined {
|
||||||
|
@ -29,17 +24,3 @@ impl Violation for FutureFeatureNotDefined {
|
||||||
format!("Future feature `{name}` is not defined")
|
format!("Future feature `{name}` is not defined")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// F407
|
|
||||||
pub(crate) fn future_feature_not_defined(checker: &Checker, alias: &Alias) {
|
|
||||||
if is_feature_name(&alias.name) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
checker.report_diagnostic(
|
|
||||||
FutureFeatureNotDefined {
|
|
||||||
name: alias.name.to_string(),
|
|
||||||
},
|
|
||||||
alias.range(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
from __future__ import invalid_feature
|
||||||
|
from __future__ import annotations, invalid_feature
|
||||||
|
from __future__ import invalid_feature_1, invalid_feature_2
|
|
@ -0,0 +1 @@
|
||||||
|
from __future__ import annotations
|
|
@ -65,9 +65,29 @@ impl SemanticSyntaxChecker {
|
||||||
names,
|
names,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
if self.seen_futures_boundary && matches!(module.as_deref(), Some("__future__")) {
|
if matches!(module.as_deref(), Some("__future__")) {
|
||||||
|
for name in names {
|
||||||
|
if !is_known_future_feature(&name.name) {
|
||||||
|
// test_ok valid_future_feature
|
||||||
|
// from __future__ import annotations
|
||||||
|
|
||||||
|
// test_err invalid_future_feature
|
||||||
|
// from __future__ import invalid_feature
|
||||||
|
// from __future__ import annotations, invalid_feature
|
||||||
|
// from __future__ import invalid_feature_1, invalid_feature_2
|
||||||
|
Self::add_error(
|
||||||
|
ctx,
|
||||||
|
SemanticSyntaxErrorKind::FutureFeatureNotDefined(
|
||||||
|
name.name.to_string(),
|
||||||
|
),
|
||||||
|
name.range,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.seen_futures_boundary {
|
||||||
Self::add_error(ctx, SemanticSyntaxErrorKind::LateFutureImport, *range);
|
Self::add_error(ctx, SemanticSyntaxErrorKind::LateFutureImport, *range);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
for alias in names {
|
for alias in names {
|
||||||
if alias.name.as_str() == "*" && !ctx.in_module_scope() {
|
if alias.name.as_str() == "*" && !ctx.in_module_scope() {
|
||||||
// test_err import_from_star
|
// test_err import_from_star
|
||||||
|
@ -978,6 +998,22 @@ impl SemanticSyntaxChecker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_known_future_feature(name: &str) -> bool {
|
||||||
|
matches!(
|
||||||
|
name,
|
||||||
|
"nested_scopes"
|
||||||
|
| "generators"
|
||||||
|
| "division"
|
||||||
|
| "absolute_import"
|
||||||
|
| "with_statement"
|
||||||
|
| "print_function"
|
||||||
|
| "unicode_literals"
|
||||||
|
| "barry_as_FLUFL"
|
||||||
|
| "generator_stop"
|
||||||
|
| "annotations"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||||
pub struct SemanticSyntaxError {
|
pub struct SemanticSyntaxError {
|
||||||
pub kind: SemanticSyntaxErrorKind,
|
pub kind: SemanticSyntaxErrorKind,
|
||||||
|
@ -1086,6 +1122,9 @@ impl Display for SemanticSyntaxError {
|
||||||
SemanticSyntaxErrorKind::MultipleStarredExpressions => {
|
SemanticSyntaxErrorKind::MultipleStarredExpressions => {
|
||||||
write!(f, "Two starred expressions in assignment")
|
write!(f, "Two starred expressions in assignment")
|
||||||
}
|
}
|
||||||
|
SemanticSyntaxErrorKind::FutureFeatureNotDefined(name) => {
|
||||||
|
write!(f, "Future feature `{name}` is not defined")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1456,6 +1495,9 @@ pub enum SemanticSyntaxErrorKind {
|
||||||
/// left-hand side of an assignment. Using multiple starred expressions makes
|
/// left-hand side of an assignment. Using multiple starred expressions makes
|
||||||
/// the statement invalid and results in a `SyntaxError`.
|
/// the statement invalid and results in a `SyntaxError`.
|
||||||
MultipleStarredExpressions,
|
MultipleStarredExpressions,
|
||||||
|
|
||||||
|
/// Represents the use of a `__future__` feature that is not defined.
|
||||||
|
FutureFeatureNotDefined(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_parser/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_parser/resources/inline/err/invalid_future_feature.py
|
||||||
|
---
|
||||||
|
## AST
|
||||||
|
|
||||||
|
```
|
||||||
|
Module(
|
||||||
|
ModModule {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 0..151,
|
||||||
|
body: [
|
||||||
|
ImportFrom(
|
||||||
|
StmtImportFrom {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 0..38,
|
||||||
|
module: Some(
|
||||||
|
Identifier {
|
||||||
|
id: Name("__future__"),
|
||||||
|
range: 5..15,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
names: [
|
||||||
|
Alias {
|
||||||
|
range: 23..38,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("invalid_feature"),
|
||||||
|
range: 23..38,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
asname: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
level: 0,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ImportFrom(
|
||||||
|
StmtImportFrom {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 39..90,
|
||||||
|
module: Some(
|
||||||
|
Identifier {
|
||||||
|
id: Name("__future__"),
|
||||||
|
range: 44..54,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
names: [
|
||||||
|
Alias {
|
||||||
|
range: 62..73,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("annotations"),
|
||||||
|
range: 62..73,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
asname: None,
|
||||||
|
},
|
||||||
|
Alias {
|
||||||
|
range: 75..90,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("invalid_feature"),
|
||||||
|
range: 75..90,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
asname: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
level: 0,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ImportFrom(
|
||||||
|
StmtImportFrom {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 91..150,
|
||||||
|
module: Some(
|
||||||
|
Identifier {
|
||||||
|
id: Name("__future__"),
|
||||||
|
range: 96..106,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
names: [
|
||||||
|
Alias {
|
||||||
|
range: 114..131,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("invalid_feature_1"),
|
||||||
|
range: 114..131,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
asname: None,
|
||||||
|
},
|
||||||
|
Alias {
|
||||||
|
range: 133..150,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("invalid_feature_2"),
|
||||||
|
range: 133..150,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
asname: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
level: 0,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
||||||
|
## Semantic Syntax Errors
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | from __future__ import invalid_feature
|
||||||
|
| ^^^^^^^^^^^^^^^ Syntax Error: Future feature `invalid_feature` is not defined
|
||||||
|
2 | from __future__ import annotations, invalid_feature
|
||||||
|
3 | from __future__ import invalid_feature_1, invalid_feature_2
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | from __future__ import invalid_feature
|
||||||
|
2 | from __future__ import annotations, invalid_feature
|
||||||
|
| ^^^^^^^^^^^^^^^ Syntax Error: Future feature `invalid_feature` is not defined
|
||||||
|
3 | from __future__ import invalid_feature_1, invalid_feature_2
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | from __future__ import invalid_feature
|
||||||
|
2 | from __future__ import annotations, invalid_feature
|
||||||
|
3 | from __future__ import invalid_feature_1, invalid_feature_2
|
||||||
|
| ^^^^^^^^^^^^^^^^^ Syntax Error: Future feature `invalid_feature_1` is not defined
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | from __future__ import invalid_feature
|
||||||
|
2 | from __future__ import annotations, invalid_feature
|
||||||
|
3 | from __future__ import invalid_feature_1, invalid_feature_2
|
||||||
|
| ^^^^^^^^^^^^^^^^^ Syntax Error: Future feature `invalid_feature_2` is not defined
|
||||||
|
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_parser/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_parser/resources/inline/ok/valid_future_feature.py
|
||||||
|
---
|
||||||
|
## AST
|
||||||
|
|
||||||
|
```
|
||||||
|
Module(
|
||||||
|
ModModule {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 0..35,
|
||||||
|
body: [
|
||||||
|
ImportFrom(
|
||||||
|
StmtImportFrom {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 0..34,
|
||||||
|
module: Some(
|
||||||
|
Identifier {
|
||||||
|
id: Name("__future__"),
|
||||||
|
range: 5..15,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
names: [
|
||||||
|
Alias {
|
||||||
|
range: 23..34,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("annotations"),
|
||||||
|
range: 23..34,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
asname: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
level: 0,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
|
@ -1,17 +0,0 @@
|
||||||
/// Returns `true` if `name` is a valid `__future__` feature name, as defined by
|
|
||||||
/// `__future__.all_feature_names`.
|
|
||||||
pub fn is_feature_name(name: &str) -> bool {
|
|
||||||
matches!(
|
|
||||||
name,
|
|
||||||
"nested_scopes"
|
|
||||||
| "generators"
|
|
||||||
| "division"
|
|
||||||
| "absolute_import"
|
|
||||||
| "with_statement"
|
|
||||||
| "print_function"
|
|
||||||
| "unicode_literals"
|
|
||||||
| "barry_as_FLUFL"
|
|
||||||
| "generator_stop"
|
|
||||||
| "annotations"
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,5 +1,4 @@
|
||||||
pub mod builtins;
|
pub mod builtins;
|
||||||
pub mod future;
|
|
||||||
pub mod identifiers;
|
pub mod identifiers;
|
||||||
pub mod keyword;
|
pub mod keyword;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue