[syntax-errors] nonlocal declaration at module level (#17559)

## Summary

Part of #17412

Add a new compile-time syntax error for detecting `nonlocal`
declarations at a module level.

## Test Plan

- Added new inline tests for the syntax error
- Updated existing tests for `nonlocal` statement parsing to be inside a
function scope

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
This commit is contained in:
Abhijeet Prasad Bodas 2025-04-25 01:41:46 +05:30 committed by GitHub
parent 538393d1f3
commit cf59cee928
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 363 additions and 131 deletions

View file

@ -0,0 +1,55 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/err/nonlocal_declaration_at_module_level.py
---
## AST
```
Module(
ModModule {
range: 0..25,
body: [
Nonlocal(
StmtNonlocal {
range: 0..10,
names: [
Identifier {
id: Name("x"),
range: 9..10,
},
],
},
),
Nonlocal(
StmtNonlocal {
range: 11..24,
names: [
Identifier {
id: Name("x"),
range: 20..21,
},
Identifier {
id: Name("y"),
range: 23..24,
},
],
},
),
],
},
)
```
## Semantic Syntax Errors
|
1 | nonlocal x
| ^^^^^^^^^^ Syntax Error: nonlocal declaration not allowed at module level
2 | nonlocal x, y
|
|
1 | nonlocal x
2 | nonlocal x, y
| ^^^^^^^^^^^^^ Syntax Error: nonlocal declaration not allowed at module level
|

View file

@ -1,19 +1,41 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/err/nonlocal_stmt_empty.py
snapshot_kind: text
---
## AST
```
Module(
ModModule {
range: 0..9,
range: 0..22,
body: [
Nonlocal(
StmtNonlocal {
range: 0..8,
names: [],
FunctionDef(
StmtFunctionDef {
range: 0..21,
is_async: false,
decorator_list: [],
name: Identifier {
id: Name("_"),
range: 4..5,
},
type_params: None,
parameters: Parameters {
range: 5..7,
posonlyargs: [],
args: [],
vararg: None,
kwonlyargs: [],
kwarg: None,
},
returns: None,
body: [
Nonlocal(
StmtNonlocal {
range: 13..21,
names: [],
},
),
],
},
),
],
@ -23,6 +45,7 @@ Module(
## Errors
|
1 | nonlocal
| ^ Syntax Error: Nonlocal statement must have at least one name
1 | def _():
2 | nonlocal
| ^ Syntax Error: Nonlocal statement must have at least one name
|

View file

@ -1,45 +1,67 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/err/nonlocal_stmt_expression.py
snapshot_kind: text
---
## AST
```
Module(
ModModule {
range: 0..15,
range: 0..28,
body: [
Nonlocal(
StmtNonlocal {
range: 0..10,
names: [
Identifier {
id: Name("x"),
range: 9..10,
},
FunctionDef(
StmtFunctionDef {
range: 0..27,
is_async: false,
decorator_list: [],
name: Identifier {
id: Name("_"),
range: 4..5,
},
type_params: None,
parameters: Parameters {
range: 5..7,
posonlyargs: [],
args: [],
vararg: None,
kwonlyargs: [],
kwarg: None,
},
returns: None,
body: [
Nonlocal(
StmtNonlocal {
range: 13..23,
names: [
Identifier {
id: Name("x"),
range: 22..23,
},
],
},
),
Expr(
StmtExpr {
range: 24..27,
value: UnaryOp(
ExprUnaryOp {
range: 24..27,
op: UAdd,
operand: NumberLiteral(
ExprNumberLiteral {
range: 26..27,
value: Int(
1,
),
},
),
},
),
},
),
],
},
),
Expr(
StmtExpr {
range: 11..14,
value: UnaryOp(
ExprUnaryOp {
range: 11..14,
op: UAdd,
operand: NumberLiteral(
ExprNumberLiteral {
range: 13..14,
value: Int(
1,
),
},
),
},
),
},
),
],
},
)
@ -47,6 +69,7 @@ Module(
## Errors
|
1 | nonlocal x + 1
| ^ Syntax Error: Simple statements must be separated by newlines or semicolons
1 | def _():
2 | nonlocal x + 1
| ^ Syntax Error: Simple statements must be separated by newlines or semicolons
|

View file

@ -1,44 +1,66 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/err/nonlocal_stmt_trailing_comma.py
snapshot_kind: text
---
## AST
```
Module(
ModModule {
range: 0..38,
range: 0..59,
body: [
Nonlocal(
StmtNonlocal {
range: 0..10,
names: [],
},
),
Nonlocal(
StmtNonlocal {
range: 11..22,
names: [
Identifier {
id: Name("x"),
range: 20..21,
},
],
},
),
Nonlocal(
StmtNonlocal {
range: 23..37,
names: [
Identifier {
id: Name("x"),
range: 32..33,
},
Identifier {
id: Name("y"),
range: 35..36,
},
FunctionDef(
StmtFunctionDef {
range: 0..58,
is_async: false,
decorator_list: [],
name: Identifier {
id: Name("_"),
range: 4..5,
},
type_params: None,
parameters: Parameters {
range: 5..7,
posonlyargs: [],
args: [],
vararg: None,
kwonlyargs: [],
kwarg: None,
},
returns: None,
body: [
Nonlocal(
StmtNonlocal {
range: 13..23,
names: [],
},
),
Nonlocal(
StmtNonlocal {
range: 28..39,
names: [
Identifier {
id: Name("x"),
range: 37..38,
},
],
},
),
Nonlocal(
StmtNonlocal {
range: 44..58,
names: [
Identifier {
id: Name("x"),
range: 53..54,
},
Identifier {
id: Name("y"),
range: 56..57,
},
],
},
),
],
},
),
@ -49,32 +71,35 @@ Module(
## Errors
|
1 | nonlocal ,
| ^ Syntax Error: Expected an identifier
2 | nonlocal x,
3 | nonlocal x, y,
1 | def _():
2 | nonlocal ,
| ^ Syntax Error: Expected an identifier
3 | nonlocal x,
4 | nonlocal x, y,
|
|
1 | nonlocal ,
| ^ Syntax Error: Nonlocal statement must have at least one name
2 | nonlocal x,
3 | nonlocal x, y,
1 | def _():
2 | nonlocal ,
| ^ Syntax Error: Nonlocal statement must have at least one name
3 | nonlocal x,
4 | nonlocal x, y,
|
|
1 | nonlocal ,
2 | nonlocal x,
| ^ Syntax Error: Trailing comma not allowed
3 | nonlocal x, y,
1 | def _():
2 | nonlocal ,
3 | nonlocal x,
| ^ Syntax Error: Trailing comma not allowed
4 | nonlocal x, y,
|
|
1 | nonlocal ,
2 | nonlocal x,
3 | nonlocal x, y,
| ^ Syntax Error: Trailing comma not allowed
2 | nonlocal ,
3 | nonlocal x,
4 | nonlocal x, y,
| ^ Syntax Error: Trailing comma not allowed
|

View file

@ -0,0 +1,49 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/ok/nonlocal_declaration_at_module_level.py
---
## AST
```
Module(
ModModule {
range: 0..24,
body: [
FunctionDef(
StmtFunctionDef {
range: 0..23,
is_async: false,
decorator_list: [],
name: Identifier {
id: Name("_"),
range: 4..5,
},
type_params: None,
parameters: Parameters {
range: 5..7,
posonlyargs: [],
args: [],
vararg: None,
kwonlyargs: [],
kwarg: None,
},
returns: None,
body: [
Nonlocal(
StmtNonlocal {
range: 13..23,
names: [
Identifier {
id: Name("x"),
range: 22..23,
},
],
},
),
],
},
),
],
},
)
```

View file

@ -1,42 +1,64 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/ok/nonlocal_stmt.py
snapshot_kind: text
---
## AST
```
Module(
ModModule {
range: 0..28,
range: 0..45,
body: [
Nonlocal(
StmtNonlocal {
range: 0..10,
names: [
Identifier {
id: Name("x"),
range: 9..10,
},
],
},
),
Nonlocal(
StmtNonlocal {
range: 11..27,
names: [
Identifier {
id: Name("x"),
range: 20..21,
},
Identifier {
id: Name("y"),
range: 23..24,
},
Identifier {
id: Name("z"),
range: 26..27,
},
FunctionDef(
StmtFunctionDef {
range: 0..44,
is_async: false,
decorator_list: [],
name: Identifier {
id: Name("_"),
range: 4..5,
},
type_params: None,
parameters: Parameters {
range: 5..7,
posonlyargs: [],
args: [],
vararg: None,
kwonlyargs: [],
kwarg: None,
},
returns: None,
body: [
Nonlocal(
StmtNonlocal {
range: 13..23,
names: [
Identifier {
id: Name("x"),
range: 22..23,
},
],
},
),
Nonlocal(
StmtNonlocal {
range: 28..44,
names: [
Identifier {
id: Name("x"),
range: 37..38,
},
Identifier {
id: Name("y"),
range: 40..41,
},
Identifier {
id: Name("z"),
range: 43..44,
},
],
},
),
],
},
),