mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-16 09:35:17 +00:00
[syntax-errors] Positional-only parameters before Python 3.8 (#16481)
Summary -- Detect positional-only parameters before Python 3.8, as marked by the `/` separator in a parameter list. Test Plan -- Inline tests.
This commit is contained in:
parent
23fd4927ae
commit
d0623888b3
6 changed files with 404 additions and 0 deletions
|
@ -0,0 +1,5 @@
|
||||||
|
# parse_options: {"target-version": "3.7"}
|
||||||
|
def foo(a, /): ...
|
||||||
|
def foo(a, /, b, /): ...
|
||||||
|
def foo(a, *args, /, b): ...
|
||||||
|
def foo(a, //): ...
|
|
@ -0,0 +1,2 @@
|
||||||
|
# parse_options: {"target-version": "3.8"}
|
||||||
|
def foo(a, /): ...
|
|
@ -449,6 +449,32 @@ pub enum UnsupportedSyntaxErrorKind {
|
||||||
Match,
|
Match,
|
||||||
Walrus,
|
Walrus,
|
||||||
ExceptStar,
|
ExceptStar,
|
||||||
|
/// Represents the use of a [PEP 570] positional-only parameter before Python 3.8.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// Python 3.8 added the `/` syntax for marking preceding parameters as positional-only:
|
||||||
|
///
|
||||||
|
/// ```python
|
||||||
|
/// def foo(a, b, /, c): ...
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This means `a` and `b` in this case can only be provided by position, not by name. In other
|
||||||
|
/// words, this code results in a `TypeError` at runtime:
|
||||||
|
///
|
||||||
|
/// ```pycon
|
||||||
|
/// >>> def foo(a, b, /, c): ...
|
||||||
|
/// ...
|
||||||
|
/// >>> foo(a=1, b=2, c=3)
|
||||||
|
/// Traceback (most recent call last):
|
||||||
|
/// File "<python-input-3>", line 1, in <module>
|
||||||
|
/// foo(a=1, b=2, c=3)
|
||||||
|
/// ~~~^^^^^^^^^^^^^^^
|
||||||
|
/// TypeError: foo() got some positional-only arguments passed as keyword arguments: 'a, b'
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [PEP 570]: https://peps.python.org/pep-0570/
|
||||||
|
PositionalOnlyParameter,
|
||||||
/// Represents the use of a [type parameter list] before Python 3.12.
|
/// Represents the use of a [type parameter list] before Python 3.12.
|
||||||
///
|
///
|
||||||
/// ## Examples
|
/// ## Examples
|
||||||
|
@ -487,6 +513,9 @@ impl Display for UnsupportedSyntaxError {
|
||||||
UnsupportedSyntaxErrorKind::Match => "Cannot use `match` statement",
|
UnsupportedSyntaxErrorKind::Match => "Cannot use `match` statement",
|
||||||
UnsupportedSyntaxErrorKind::Walrus => "Cannot use named assignment expression (`:=`)",
|
UnsupportedSyntaxErrorKind::Walrus => "Cannot use named assignment expression (`:=`)",
|
||||||
UnsupportedSyntaxErrorKind::ExceptStar => "Cannot use `except*`",
|
UnsupportedSyntaxErrorKind::ExceptStar => "Cannot use `except*`",
|
||||||
|
UnsupportedSyntaxErrorKind::PositionalOnlyParameter => {
|
||||||
|
"Cannot use positional-only parameter separator"
|
||||||
|
}
|
||||||
UnsupportedSyntaxErrorKind::TypeParameterList => "Cannot use type parameter lists",
|
UnsupportedSyntaxErrorKind::TypeParameterList => "Cannot use type parameter lists",
|
||||||
UnsupportedSyntaxErrorKind::TypeAliasStatement => "Cannot use `type` alias statement",
|
UnsupportedSyntaxErrorKind::TypeAliasStatement => "Cannot use `type` alias statement",
|
||||||
UnsupportedSyntaxErrorKind::TypeParamDefault => {
|
UnsupportedSyntaxErrorKind::TypeParamDefault => {
|
||||||
|
@ -509,6 +538,7 @@ impl UnsupportedSyntaxErrorKind {
|
||||||
UnsupportedSyntaxErrorKind::Match => PythonVersion::PY310,
|
UnsupportedSyntaxErrorKind::Match => PythonVersion::PY310,
|
||||||
UnsupportedSyntaxErrorKind::Walrus => PythonVersion::PY38,
|
UnsupportedSyntaxErrorKind::Walrus => PythonVersion::PY38,
|
||||||
UnsupportedSyntaxErrorKind::ExceptStar => PythonVersion::PY311,
|
UnsupportedSyntaxErrorKind::ExceptStar => PythonVersion::PY311,
|
||||||
|
UnsupportedSyntaxErrorKind::PositionalOnlyParameter => PythonVersion::PY38,
|
||||||
UnsupportedSyntaxErrorKind::TypeParameterList => PythonVersion::PY312,
|
UnsupportedSyntaxErrorKind::TypeParameterList => PythonVersion::PY312,
|
||||||
UnsupportedSyntaxErrorKind::TypeAliasStatement => PythonVersion::PY312,
|
UnsupportedSyntaxErrorKind::TypeAliasStatement => PythonVersion::PY312,
|
||||||
UnsupportedSyntaxErrorKind::TypeParamDefault => PythonVersion::PY313,
|
UnsupportedSyntaxErrorKind::TypeParamDefault => PythonVersion::PY313,
|
||||||
|
|
|
@ -3050,6 +3050,21 @@ impl<'src> Parser<'src> {
|
||||||
// first time, otherwise it's a user error.
|
// first time, otherwise it's a user error.
|
||||||
std::mem::swap(&mut parameters.args, &mut parameters.posonlyargs);
|
std::mem::swap(&mut parameters.args, &mut parameters.posonlyargs);
|
||||||
seen_positional_only_separator = true;
|
seen_positional_only_separator = true;
|
||||||
|
|
||||||
|
// test_ok pos_only_py38
|
||||||
|
// # parse_options: {"target-version": "3.8"}
|
||||||
|
// def foo(a, /): ...
|
||||||
|
|
||||||
|
// test_err pos_only_py37
|
||||||
|
// # parse_options: {"target-version": "3.7"}
|
||||||
|
// def foo(a, /): ...
|
||||||
|
// def foo(a, /, b, /): ...
|
||||||
|
// def foo(a, *args, /, b): ...
|
||||||
|
// def foo(a, //): ...
|
||||||
|
parser.add_unsupported_syntax_error(
|
||||||
|
UnsupportedSyntaxErrorKind::PositionalOnlyParameter,
|
||||||
|
slash_range,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
last_keyword_only_separator_range = None;
|
last_keyword_only_separator_range = None;
|
||||||
|
|
|
@ -0,0 +1,291 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_parser/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_parser/resources/inline/err/pos_only_py37.py
|
||||||
|
---
|
||||||
|
## AST
|
||||||
|
|
||||||
|
```
|
||||||
|
Module(
|
||||||
|
ModModule {
|
||||||
|
range: 0..136,
|
||||||
|
body: [
|
||||||
|
FunctionDef(
|
||||||
|
StmtFunctionDef {
|
||||||
|
range: 43..61,
|
||||||
|
is_async: false,
|
||||||
|
decorator_list: [],
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("foo"),
|
||||||
|
range: 47..50,
|
||||||
|
},
|
||||||
|
type_params: None,
|
||||||
|
parameters: Parameters {
|
||||||
|
range: 50..56,
|
||||||
|
posonlyargs: [
|
||||||
|
ParameterWithDefault {
|
||||||
|
range: 51..52,
|
||||||
|
parameter: Parameter {
|
||||||
|
range: 51..52,
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("a"),
|
||||||
|
range: 51..52,
|
||||||
|
},
|
||||||
|
annotation: None,
|
||||||
|
},
|
||||||
|
default: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
args: [],
|
||||||
|
vararg: None,
|
||||||
|
kwonlyargs: [],
|
||||||
|
kwarg: None,
|
||||||
|
},
|
||||||
|
returns: None,
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 58..61,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 58..61,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
FunctionDef(
|
||||||
|
StmtFunctionDef {
|
||||||
|
range: 62..86,
|
||||||
|
is_async: false,
|
||||||
|
decorator_list: [],
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("foo"),
|
||||||
|
range: 66..69,
|
||||||
|
},
|
||||||
|
type_params: None,
|
||||||
|
parameters: Parameters {
|
||||||
|
range: 69..81,
|
||||||
|
posonlyargs: [
|
||||||
|
ParameterWithDefault {
|
||||||
|
range: 70..71,
|
||||||
|
parameter: Parameter {
|
||||||
|
range: 70..71,
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("a"),
|
||||||
|
range: 70..71,
|
||||||
|
},
|
||||||
|
annotation: None,
|
||||||
|
},
|
||||||
|
default: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
args: [
|
||||||
|
ParameterWithDefault {
|
||||||
|
range: 76..77,
|
||||||
|
parameter: Parameter {
|
||||||
|
range: 76..77,
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("b"),
|
||||||
|
range: 76..77,
|
||||||
|
},
|
||||||
|
annotation: None,
|
||||||
|
},
|
||||||
|
default: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
vararg: None,
|
||||||
|
kwonlyargs: [],
|
||||||
|
kwarg: None,
|
||||||
|
},
|
||||||
|
returns: None,
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 83..86,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 83..86,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
FunctionDef(
|
||||||
|
StmtFunctionDef {
|
||||||
|
range: 87..115,
|
||||||
|
is_async: false,
|
||||||
|
decorator_list: [],
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("foo"),
|
||||||
|
range: 91..94,
|
||||||
|
},
|
||||||
|
type_params: None,
|
||||||
|
parameters: Parameters {
|
||||||
|
range: 94..110,
|
||||||
|
posonlyargs: [
|
||||||
|
ParameterWithDefault {
|
||||||
|
range: 95..96,
|
||||||
|
parameter: Parameter {
|
||||||
|
range: 95..96,
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("a"),
|
||||||
|
range: 95..96,
|
||||||
|
},
|
||||||
|
annotation: None,
|
||||||
|
},
|
||||||
|
default: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
args: [],
|
||||||
|
vararg: Some(
|
||||||
|
Parameter {
|
||||||
|
range: 98..103,
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("args"),
|
||||||
|
range: 99..103,
|
||||||
|
},
|
||||||
|
annotation: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
kwonlyargs: [
|
||||||
|
ParameterWithDefault {
|
||||||
|
range: 108..109,
|
||||||
|
parameter: Parameter {
|
||||||
|
range: 108..109,
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("b"),
|
||||||
|
range: 108..109,
|
||||||
|
},
|
||||||
|
annotation: None,
|
||||||
|
},
|
||||||
|
default: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
kwarg: None,
|
||||||
|
},
|
||||||
|
returns: None,
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 112..115,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 112..115,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
FunctionDef(
|
||||||
|
StmtFunctionDef {
|
||||||
|
range: 116..135,
|
||||||
|
is_async: false,
|
||||||
|
decorator_list: [],
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("foo"),
|
||||||
|
range: 120..123,
|
||||||
|
},
|
||||||
|
type_params: None,
|
||||||
|
parameters: Parameters {
|
||||||
|
range: 123..130,
|
||||||
|
posonlyargs: [],
|
||||||
|
args: [
|
||||||
|
ParameterWithDefault {
|
||||||
|
range: 124..125,
|
||||||
|
parameter: Parameter {
|
||||||
|
range: 124..125,
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("a"),
|
||||||
|
range: 124..125,
|
||||||
|
},
|
||||||
|
annotation: None,
|
||||||
|
},
|
||||||
|
default: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
vararg: None,
|
||||||
|
kwonlyargs: [],
|
||||||
|
kwarg: None,
|
||||||
|
},
|
||||||
|
returns: None,
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 132..135,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 132..135,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | # parse_options: {"target-version": "3.7"}
|
||||||
|
2 | def foo(a, /): ...
|
||||||
|
3 | def foo(a, /, b, /): ...
|
||||||
|
| ^ Syntax Error: Only one '/' separator allowed
|
||||||
|
4 | def foo(a, *args, /, b): ...
|
||||||
|
5 | def foo(a, //): ...
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
2 | def foo(a, /): ...
|
||||||
|
3 | def foo(a, /, b, /): ...
|
||||||
|
4 | def foo(a, *args, /, b): ...
|
||||||
|
| ^ Syntax Error: '/' parameter must appear before '*' parameter
|
||||||
|
5 | def foo(a, //): ...
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
3 | def foo(a, /, b, /): ...
|
||||||
|
4 | def foo(a, *args, /, b): ...
|
||||||
|
5 | def foo(a, //): ...
|
||||||
|
| ^^ Syntax Error: Expected ',', found '//'
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
## Unsupported Syntax Errors
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | # parse_options: {"target-version": "3.7"}
|
||||||
|
2 | def foo(a, /): ...
|
||||||
|
| ^ Syntax Error: Cannot use positional-only parameter separator on Python 3.7 (syntax was added in Python 3.8)
|
||||||
|
3 | def foo(a, /, b, /): ...
|
||||||
|
4 | def foo(a, *args, /, b): ...
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | # parse_options: {"target-version": "3.7"}
|
||||||
|
2 | def foo(a, /): ...
|
||||||
|
3 | def foo(a, /, b, /): ...
|
||||||
|
| ^ Syntax Error: Cannot use positional-only parameter separator on Python 3.7 (syntax was added in Python 3.8)
|
||||||
|
4 | def foo(a, *args, /, b): ...
|
||||||
|
5 | def foo(a, //): ...
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
2 | def foo(a, /): ...
|
||||||
|
3 | def foo(a, /, b, /): ...
|
||||||
|
4 | def foo(a, *args, /, b): ...
|
||||||
|
| ^ Syntax Error: Cannot use positional-only parameter separator on Python 3.7 (syntax was added in Python 3.8)
|
||||||
|
5 | def foo(a, //): ...
|
||||||
|
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_parser/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_parser/resources/inline/ok/pos_only_py38.py
|
||||||
|
---
|
||||||
|
## AST
|
||||||
|
|
||||||
|
```
|
||||||
|
Module(
|
||||||
|
ModModule {
|
||||||
|
range: 0..62,
|
||||||
|
body: [
|
||||||
|
FunctionDef(
|
||||||
|
StmtFunctionDef {
|
||||||
|
range: 43..61,
|
||||||
|
is_async: false,
|
||||||
|
decorator_list: [],
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("foo"),
|
||||||
|
range: 47..50,
|
||||||
|
},
|
||||||
|
type_params: None,
|
||||||
|
parameters: Parameters {
|
||||||
|
range: 50..56,
|
||||||
|
posonlyargs: [
|
||||||
|
ParameterWithDefault {
|
||||||
|
range: 51..52,
|
||||||
|
parameter: Parameter {
|
||||||
|
range: 51..52,
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("a"),
|
||||||
|
range: 51..52,
|
||||||
|
},
|
||||||
|
annotation: None,
|
||||||
|
},
|
||||||
|
default: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
args: [],
|
||||||
|
vararg: None,
|
||||||
|
kwonlyargs: [],
|
||||||
|
kwarg: None,
|
||||||
|
},
|
||||||
|
returns: None,
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 58..61,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 58..61,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
Loading…
Add table
Add a link
Reference in a new issue