Skip to content

Commit

Permalink
Raise syntax error for mixing except and except* (#14895)
Browse files Browse the repository at this point in the history
This PR adds a syntax error if the parser encounters a `TryStmt` that
has except clauses both with and without a star.

The displayed error points to each except clause that contradicts the
original except clause kind. So, for example,

```python
try:
    ....
except:     #<-- we assume this is the desired except kind
    ....
except*:    #<---  error will point here
    ....
except*:    #<--- and here
    ....
```

Closes #14860
  • Loading branch information
dylwil3 authored Dec 10, 2024
1 parent d4126f6 commit a3bb0cd
Show file tree
Hide file tree
Showing 3 changed files with 301 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
try:
pass
except:
pass
except* ExceptionGroup:
pass
try:
pass
except* ExceptionGroup:
pass
except:
pass
try:
pass
except:
pass
except:
pass
except* ExceptionGroup:
pass
except* ExceptionGroup:
pass
31 changes: 27 additions & 4 deletions crates/ruff_python_parser/src/parser/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1332,14 +1332,12 @@ impl<'src> Parser<'src> {
self.bump(TokenKind::Try);
self.expect(TokenKind::Colon);

let mut is_star = false;
let mut is_star: Option<bool> = None;

let try_body = self.parse_body(Clause::Try);

let has_except = self.at(TokenKind::Except);

// TODO(dhruvmanila): Raise syntax error if there are both 'except' and 'except*'
// on the same 'try'
// test_err try_stmt_mixed_except_kind
// try:
// pass
Expand All @@ -1353,11 +1351,36 @@ impl<'src> Parser<'src> {
// pass
// except:
// pass
// try:
// pass
// except:
// pass
// except:
// pass
// except* ExceptionGroup:
// pass
// except* ExceptionGroup:
// pass
let mut mixed_except_ranges = Vec::new();
let handlers = self.parse_clauses(Clause::Except, |p| {
let (handler, kind) = p.parse_except_clause();
is_star |= kind.is_star();
if is_star.is_none() {
is_star = Some(kind.is_star());
} else if is_star != Some(kind.is_star()) {
mixed_except_ranges.push(handler.range());
}
handler
});
// Empty handler has `is_star` false.
let is_star = is_star.unwrap_or_default();
for handler_err_range in mixed_except_ranges {
self.add_error(
ParseErrorType::OtherError(
"Cannot have both 'except' and 'except*' on the same 'try'".to_string(),
),
handler_err_range,
);
}

// test_err try_stmt_misspelled_except
// try:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/err/try_stmt_mixed_except_kind.py
---
## AST

```
Module(
ModModule {
range: 0..242,
body: [
Try(
StmtTry {
range: 0..63,
body: [
Pass(
StmtPass {
range: 9..13,
},
),
],
handlers: [
ExceptHandler(
ExceptHandlerExceptHandler {
range: 14..30,
type_: None,
name: None,
body: [
Pass(
StmtPass {
range: 26..30,
},
),
],
},
),
ExceptHandler(
ExceptHandlerExceptHandler {
range: 31..63,
type_: Some(
Name(
ExprName {
range: 39..53,
id: Name("ExceptionGroup"),
ctx: Load,
},
),
),
name: None,
body: [
Pass(
StmtPass {
range: 59..63,
},
),
],
},
),
],
orelse: [],
finalbody: [],
is_star: false,
},
),
Try(
StmtTry {
range: 64..127,
body: [
Pass(
StmtPass {
range: 73..77,
},
),
],
handlers: [
ExceptHandler(
ExceptHandlerExceptHandler {
range: 78..110,
type_: Some(
Name(
ExprName {
range: 86..100,
id: Name("ExceptionGroup"),
ctx: Load,
},
),
),
name: None,
body: [
Pass(
StmtPass {
range: 106..110,
},
),
],
},
),
ExceptHandler(
ExceptHandlerExceptHandler {
range: 111..127,
type_: None,
name: None,
body: [
Pass(
StmtPass {
range: 123..127,
},
),
],
},
),
],
orelse: [],
finalbody: [],
is_star: true,
},
),
Try(
StmtTry {
range: 128..241,
body: [
Pass(
StmtPass {
range: 137..141,
},
),
],
handlers: [
ExceptHandler(
ExceptHandlerExceptHandler {
range: 142..158,
type_: None,
name: None,
body: [
Pass(
StmtPass {
range: 154..158,
},
),
],
},
),
ExceptHandler(
ExceptHandlerExceptHandler {
range: 159..175,
type_: None,
name: None,
body: [
Pass(
StmtPass {
range: 171..175,
},
),
],
},
),
ExceptHandler(
ExceptHandlerExceptHandler {
range: 176..208,
type_: Some(
Name(
ExprName {
range: 184..198,
id: Name("ExceptionGroup"),
ctx: Load,
},
),
),
name: None,
body: [
Pass(
StmtPass {
range: 204..208,
},
),
],
},
),
ExceptHandler(
ExceptHandlerExceptHandler {
range: 209..241,
type_: Some(
Name(
ExprName {
range: 217..231,
id: Name("ExceptionGroup"),
ctx: Load,
},
),
),
name: None,
body: [
Pass(
StmtPass {
range: 237..241,
},
),
],
},
),
],
orelse: [],
finalbody: [],
is_star: false,
},
),
],
},
)
```
## Errors

|
3 | except:
4 | pass
5 | / except* ExceptionGroup:
6 | | pass
| |________^ Syntax Error: Cannot have both 'except' and 'except*' on the same 'try'
7 | try:
8 | pass
|


|
9 | except* ExceptionGroup:
10 | pass
11 | / except:
12 | | pass
| |________^ Syntax Error: Cannot have both 'except' and 'except*' on the same 'try'
13 | try:
14 | pass
|


|
17 | except:
18 | pass
19 | / except* ExceptionGroup:
20 | | pass
| |________^ Syntax Error: Cannot have both 'except' and 'except*' on the same 'try'
21 | except* ExceptionGroup:
22 | pass
|


|
19 | except* ExceptionGroup:
20 | pass
21 | / except* ExceptionGroup:
22 | | pass
| |________^ Syntax Error: Cannot have both 'except' and 'except*' on the same 'try'
|

0 comments on commit a3bb0cd

Please sign in to comment.