mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 11:59:10 +00:00
[syntax-errors] except*
before Python 3.11 (#16446)
Summary -- One of the simpler ones, just detect the use of `except*` before 3.11. Test Plan -- New inline tests.
This commit is contained in:
parent
0d615b8765
commit
e924ecbdac
9 changed files with 180 additions and 23 deletions
|
@ -613,7 +613,7 @@ mod tests {
|
||||||
let settings = Settings {
|
let settings = Settings {
|
||||||
cache_dir,
|
cache_dir,
|
||||||
linter: LinterSettings {
|
linter: LinterSettings {
|
||||||
unresolved_target_version: PythonVersion::PY310,
|
unresolved_target_version: PythonVersion::latest(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
..Settings::default()
|
..Settings::default()
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# parse_options: {"target-version": "3.10"}
|
||||||
|
try: ...
|
||||||
|
except* ValueError: ...
|
|
@ -0,0 +1,3 @@
|
||||||
|
# parse_options: {"target-version": "3.11"}
|
||||||
|
try: ...
|
||||||
|
except* ValueError: ...
|
|
@ -438,9 +438,9 @@ pub struct UnsupportedSyntaxError {
|
||||||
/// The target [`PythonVersion`] for which this error was detected.
|
/// The target [`PythonVersion`] for which this error was detected.
|
||||||
///
|
///
|
||||||
/// This is different from the version reported by the
|
/// This is different from the version reported by the
|
||||||
/// [`minimum_version`](UnsupportedSyntaxError::minimum_version) method, which is the earliest
|
/// [`minimum_version`](UnsupportedSyntaxErrorKind::minimum_version) method, which is the
|
||||||
/// allowed version for this piece of syntax. The `target_version` is primarily used for
|
/// earliest allowed version for this piece of syntax. The `target_version` is primarily used
|
||||||
/// user-facing error messages.
|
/// for user-facing error messages.
|
||||||
pub target_version: PythonVersion,
|
pub target_version: PythonVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,6 +448,7 @@ pub struct UnsupportedSyntaxError {
|
||||||
pub enum UnsupportedSyntaxErrorKind {
|
pub enum UnsupportedSyntaxErrorKind {
|
||||||
Match,
|
Match,
|
||||||
Walrus,
|
Walrus,
|
||||||
|
ExceptStar,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for UnsupportedSyntaxError {
|
impl Display for UnsupportedSyntaxError {
|
||||||
|
@ -455,22 +456,24 @@ impl Display for UnsupportedSyntaxError {
|
||||||
let kind = match self.kind {
|
let kind = match self.kind {
|
||||||
UnsupportedSyntaxErrorKind::Match => "`match` statement",
|
UnsupportedSyntaxErrorKind::Match => "`match` statement",
|
||||||
UnsupportedSyntaxErrorKind::Walrus => "named assignment expression (`:=`)",
|
UnsupportedSyntaxErrorKind::Walrus => "named assignment expression (`:=`)",
|
||||||
|
UnsupportedSyntaxErrorKind::ExceptStar => "`except*`",
|
||||||
};
|
};
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Cannot use {kind} on Python {} (syntax was added in Python {})",
|
"Cannot use {kind} on Python {} (syntax was added in Python {})",
|
||||||
self.target_version,
|
self.target_version,
|
||||||
self.minimum_version(),
|
self.kind.minimum_version(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnsupportedSyntaxError {
|
impl UnsupportedSyntaxErrorKind {
|
||||||
/// The earliest allowed version for the syntax associated with this error.
|
/// The earliest allowed version for the syntax associated with this error.
|
||||||
pub const fn minimum_version(&self) -> PythonVersion {
|
pub const fn minimum_version(&self) -> PythonVersion {
|
||||||
match self.kind {
|
match self {
|
||||||
UnsupportedSyntaxErrorKind::Match => PythonVersion::PY310,
|
UnsupportedSyntaxErrorKind::Match => PythonVersion::PY310,
|
||||||
UnsupportedSyntaxErrorKind::Walrus => PythonVersion::PY38,
|
UnsupportedSyntaxErrorKind::Walrus => PythonVersion::PY38,
|
||||||
|
UnsupportedSyntaxErrorKind::ExceptStar => PythonVersion::PY311,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use rustc_hash::{FxBuildHasher, FxHashSet};
|
||||||
use ruff_python_ast::name::Name;
|
use ruff_python_ast::name::Name;
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
self as ast, BoolOp, CmpOp, ConversionFlag, Expr, ExprContext, FStringElement, FStringElements,
|
self as ast, BoolOp, CmpOp, ConversionFlag, Expr, ExprContext, FStringElement, FStringElements,
|
||||||
IpyEscapeKind, Number, Operator, PythonVersion, StringFlags, UnaryOp,
|
IpyEscapeKind, Number, Operator, StringFlags, UnaryOp,
|
||||||
};
|
};
|
||||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||||
|
|
||||||
|
@ -2171,9 +2171,7 @@ impl<'src> Parser<'src> {
|
||||||
// # parse_options: { "target-version": "3.8" }
|
// # parse_options: { "target-version": "3.8" }
|
||||||
// (x := 1)
|
// (x := 1)
|
||||||
|
|
||||||
if self.options.target_version < PythonVersion::PY38 {
|
self.add_unsupported_syntax_error(UnsupportedSyntaxErrorKind::Walrus, range);
|
||||||
self.add_unsupported_syntax_error(UnsupportedSyntaxErrorKind::Walrus, range);
|
|
||||||
}
|
|
||||||
|
|
||||||
ast::ExprNamed {
|
ast::ExprNamed {
|
||||||
target: Box::new(target),
|
target: Box::new(target),
|
||||||
|
|
|
@ -439,13 +439,15 @@ impl<'src> Parser<'src> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an [`UnsupportedSyntaxError`] with the given [`UnsupportedSyntaxErrorKind`] and
|
/// Add an [`UnsupportedSyntaxError`] with the given [`UnsupportedSyntaxErrorKind`] and
|
||||||
/// [`TextRange`].
|
/// [`TextRange`] if its minimum version is less than [`Parser::target_version`].
|
||||||
fn add_unsupported_syntax_error(&mut self, kind: UnsupportedSyntaxErrorKind, range: TextRange) {
|
fn add_unsupported_syntax_error(&mut self, kind: UnsupportedSyntaxErrorKind, range: TextRange) {
|
||||||
self.unsupported_syntax_errors.push(UnsupportedSyntaxError {
|
if self.options.target_version < kind.minimum_version() {
|
||||||
kind,
|
self.unsupported_syntax_errors.push(UnsupportedSyntaxError {
|
||||||
range,
|
kind,
|
||||||
target_version: self.options.target_version,
|
range,
|
||||||
});
|
target_version: self.options.target_version,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the current token is of the given kind.
|
/// Returns `true` if the current token is of the given kind.
|
||||||
|
|
|
@ -5,8 +5,7 @@ use rustc_hash::{FxBuildHasher, FxHashSet};
|
||||||
|
|
||||||
use ruff_python_ast::name::Name;
|
use ruff_python_ast::name::Name;
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
self as ast, ExceptHandler, Expr, ExprContext, IpyEscapeKind, Operator, PythonVersion, Stmt,
|
self as ast, ExceptHandler, Expr, ExprContext, IpyEscapeKind, Operator, Stmt, WithItem,
|
||||||
WithItem,
|
|
||||||
};
|
};
|
||||||
use ruff_text_size::{Ranged, TextSize};
|
use ruff_text_size::{Ranged, TextSize};
|
||||||
|
|
||||||
|
@ -1458,13 +1457,28 @@ impl<'src> Parser<'src> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test_ok except_star_py311
|
||||||
|
// # parse_options: {"target-version": "3.11"}
|
||||||
|
// try: ...
|
||||||
|
// except* ValueError: ...
|
||||||
|
|
||||||
|
// test_err except_star_py310
|
||||||
|
// # parse_options: {"target-version": "3.10"}
|
||||||
|
// try: ...
|
||||||
|
// except* ValueError: ...
|
||||||
|
|
||||||
|
let range = self.node_range(try_start);
|
||||||
|
if is_star {
|
||||||
|
self.add_unsupported_syntax_error(UnsupportedSyntaxErrorKind::ExceptStar, range);
|
||||||
|
}
|
||||||
|
|
||||||
ast::StmtTry {
|
ast::StmtTry {
|
||||||
body: try_body,
|
body: try_body,
|
||||||
handlers,
|
handlers,
|
||||||
orelse,
|
orelse,
|
||||||
finalbody,
|
finalbody,
|
||||||
is_star,
|
is_star,
|
||||||
range: self.node_range(try_start),
|
range,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2277,9 +2291,7 @@ impl<'src> Parser<'src> {
|
||||||
// case 1:
|
// case 1:
|
||||||
// pass
|
// pass
|
||||||
|
|
||||||
if self.options.target_version < PythonVersion::PY310 {
|
self.add_unsupported_syntax_error(UnsupportedSyntaxErrorKind::Match, match_range);
|
||||||
self.add_unsupported_syntax_error(UnsupportedSyntaxErrorKind::Match, match_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
ast::StmtMatch {
|
ast::StmtMatch {
|
||||||
subject: Box::new(subject),
|
subject: Box::new(subject),
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_parser/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_parser/resources/inline/err/except_star_py310.py
|
||||||
|
---
|
||||||
|
## AST
|
||||||
|
|
||||||
|
```
|
||||||
|
Module(
|
||||||
|
ModModule {
|
||||||
|
range: 0..77,
|
||||||
|
body: [
|
||||||
|
Try(
|
||||||
|
StmtTry {
|
||||||
|
range: 44..76,
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 49..52,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 49..52,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
handlers: [
|
||||||
|
ExceptHandler(
|
||||||
|
ExceptHandlerExceptHandler {
|
||||||
|
range: 53..76,
|
||||||
|
type_: Some(
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 61..71,
|
||||||
|
id: Name("ValueError"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
name: None,
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 73..76,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 73..76,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
orelse: [],
|
||||||
|
finalbody: [],
|
||||||
|
is_star: true,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
||||||
|
## Unsupported Syntax Errors
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | # parse_options: {"target-version": "3.10"}
|
||||||
|
2 | / try: ...
|
||||||
|
3 | | except* ValueError: ...
|
||||||
|
| |_______________________^ Syntax Error: Cannot use `except*` on Python 3.10 (syntax was added in Python 3.11)
|
||||||
|
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_parser/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_parser/resources/inline/ok/except_star_py311.py
|
||||||
|
---
|
||||||
|
## AST
|
||||||
|
|
||||||
|
```
|
||||||
|
Module(
|
||||||
|
ModModule {
|
||||||
|
range: 0..77,
|
||||||
|
body: [
|
||||||
|
Try(
|
||||||
|
StmtTry {
|
||||||
|
range: 44..76,
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 49..52,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 49..52,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
handlers: [
|
||||||
|
ExceptHandler(
|
||||||
|
ExceptHandlerExceptHandler {
|
||||||
|
range: 53..76,
|
||||||
|
type_: Some(
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 61..71,
|
||||||
|
id: Name("ValueError"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
name: None,
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 73..76,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 73..76,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
orelse: [],
|
||||||
|
finalbody: [],
|
||||||
|
is_star: true,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
Loading…
Add table
Add a link
Reference in a new issue