-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Closed
Description
RustPython's AST structure for the following two programs is identical (the ranges differ)
# Program 1
if True:
pass
elif False:
pass
# Program 2
if True:
pass
else:
if False:
passIf(
StmtIf {
range: 0..38,
test: Constant(
ExprConstant {
range: 3..7,
value: Bool(
true,
),
kind: None,
},
),
body: [
Pass(
StmtPass {
range: 13..17,
},
),
],
orelse: [
If(
StmtIf {
range: 18..38,
test: Constant(
ExprConstant {
range: 23..28,
value: Bool(
false,
),
kind: None,
},
),
body: [
Pass(
StmtPass {
range: 34..38,
},
),
],
orelse: [],
},
),
],
},
),
This is problematic because the formatter incorrectly assumes that the nested if in the second program is an elif and collapses the nested if. This isn't something a formatter should do. This representation does make sense for an interpreter. It's actually a neat little optimisation that the parser performs.
The solution for this is to change Suite from a Vec<Statement> to its own SuiteStatement AST node and change the orelse type (and any other field that represents a body) from Suite to Option<Stmt>.
This would change the representation of the first program to:
If(
StmtIf {
range: 0..38,
test: Constant(
ExprConstant {
range: 3..7,
value: Bool(
true,
),
kind: None,
},
),
body: [
Pass(
StmtPass {
range: 13..17,
},
),
],
orelse: Some(If(
StmtIf {
range: 18..38,
test: Constant(
ExprConstant {
range: 23..28,
value: Bool(
false,
),
kind: None,
},
),
body: [
Pass(
StmtPass {
range: 34..38,
},
),
],
orelse: None
},
),
}),
),
and of the second program:
If(
StmtIf {
range: 0..38,
test: Constant(
ExprConstant {
range: 3..7,
value: Bool(
true,
),
kind: None,
},
),
body: [
Pass(
StmtPass {
range: 13..17,
},
),
],
orelse: Some(SuiteStatement(SuiteStatement([
If(
StmtIf {
range: 18..38,
test: Constant(
ExprConstant {
range: 23..28,
value: Bool(
false,
),
kind: None,
},
),
body: [
Pass(
StmtPass {
range: 34..38,
},
),
],
orelse: None,
},
),
])),
},
),
which is unambiguous
This requires changes to:
- RustPython's parser
- Ruff
- The visitor implementations
- The formatter
Alternatives
- Extract the indent from the source to distinguish the two cases. That's what we currently do when extracting comments but it is so easy to get wrong. We would need to do the same in the
unparseof the linter
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels