mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-02 09:52:32 +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 {
|
||||
cache_dir,
|
||||
linter: LinterSettings {
|
||||
unresolved_target_version: PythonVersion::PY310,
|
||||
unresolved_target_version: PythonVersion::latest(),
|
||||
..Default::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.
|
||||
///
|
||||
/// This is different from the version reported by the
|
||||
/// [`minimum_version`](UnsupportedSyntaxError::minimum_version) method, which is the earliest
|
||||
/// allowed version for this piece of syntax. The `target_version` is primarily used for
|
||||
/// user-facing error messages.
|
||||
/// [`minimum_version`](UnsupportedSyntaxErrorKind::minimum_version) method, which is the
|
||||
/// earliest allowed version for this piece of syntax. The `target_version` is primarily used
|
||||
/// for user-facing error messages.
|
||||
pub target_version: PythonVersion,
|
||||
}
|
||||
|
||||
|
@ -448,6 +448,7 @@ pub struct UnsupportedSyntaxError {
|
|||
pub enum UnsupportedSyntaxErrorKind {
|
||||
Match,
|
||||
Walrus,
|
||||
ExceptStar,
|
||||
}
|
||||
|
||||
impl Display for UnsupportedSyntaxError {
|
||||
|
@ -455,22 +456,24 @@ impl Display for UnsupportedSyntaxError {
|
|||
let kind = match self.kind {
|
||||
UnsupportedSyntaxErrorKind::Match => "`match` statement",
|
||||
UnsupportedSyntaxErrorKind::Walrus => "named assignment expression (`:=`)",
|
||||
UnsupportedSyntaxErrorKind::ExceptStar => "`except*`",
|
||||
};
|
||||
write!(
|
||||
f,
|
||||
"Cannot use {kind} on Python {} (syntax was added in Python {})",
|
||||
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.
|
||||
pub const fn minimum_version(&self) -> PythonVersion {
|
||||
match self.kind {
|
||||
match self {
|
||||
UnsupportedSyntaxErrorKind::Match => PythonVersion::PY310,
|
||||
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::{
|
||||
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};
|
||||
|
||||
|
@ -2171,9 +2171,7 @@ impl<'src> Parser<'src> {
|
|||
// # parse_options: { "target-version": "3.8" }
|
||||
// (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 {
|
||||
target: Box::new(target),
|
||||
|
|
|
@ -439,13 +439,15 @@ impl<'src> Parser<'src> {
|
|||
}
|
||||
|
||||
/// 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) {
|
||||
self.unsupported_syntax_errors.push(UnsupportedSyntaxError {
|
||||
kind,
|
||||
range,
|
||||
target_version: self.options.target_version,
|
||||
});
|
||||
if self.options.target_version < kind.minimum_version() {
|
||||
self.unsupported_syntax_errors.push(UnsupportedSyntaxError {
|
||||
kind,
|
||||
range,
|
||||
target_version: self.options.target_version,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 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::{
|
||||
self as ast, ExceptHandler, Expr, ExprContext, IpyEscapeKind, Operator, PythonVersion, Stmt,
|
||||
WithItem,
|
||||
self as ast, ExceptHandler, Expr, ExprContext, IpyEscapeKind, Operator, Stmt, WithItem,
|
||||
};
|
||||
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 {
|
||||
body: try_body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
is_star,
|
||||
range: self.node_range(try_start),
|
||||
range,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2277,9 +2291,7 @@ impl<'src> Parser<'src> {
|
|||
// case 1:
|
||||
// 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 {
|
||||
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