Skip to content

Commit 969b630

Browse files
authored
Fix panics while searching type comments in function with syntax error (#78)
This fixes panics found in python/mypy#21823
1 parent fc05930 commit 969b630

2 files changed

Lines changed: 58 additions & 2 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ in PATH.
7474

7575
## Making a release
7676

77-
1. Bump the version number in `pyproject.toml` and `Cargo.toml` in this repository.
77+
1. Bump the version number in `pyproject.toml` and `Cargo.toml` (including vendored crates)
78+
in this repository.
7879
2. Update `test_ast_serialize.py` (optional but recommended if the release includes major features).
7980
3. Commit and push (pushing directly to master is fine).
8081
4. Wait until all [builds](https://github.com/mypyc/ast_serialize/actions) complete successfully

src/serialize_ast.rs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1169,13 +1169,16 @@ fn find_func_type_comment(
11691169
return (arg_types, ret_type);
11701170
}
11711171
let first_stmt = func.body[0].start();
1172+
if ser.tokens.is_none() {
1173+
return (arg_types, ret_type);
1174+
}
11721175
let tokens = ser.tokens.unwrap();
11731176
let tokens_before = tokens.before(first_stmt);
11741177
let mut idx = tokens_before.len() - 1;
11751178
loop {
11761179
// Look for function type comments between colon in `def foo(...):` anr first statement.
11771180
let token = tokens_before[idx].kind();
1178-
if token == TokenKind::Colon {
1181+
if idx == 0 || token == TokenKind::Colon {
11791182
break;
11801183
}
11811184
if token == TokenKind::Comment {
@@ -3579,6 +3582,58 @@ mod tests {
35793582
assert!(ser.lines_with_non_ascii.is_empty()); // No per-line tracking
35803583
}
35813584

3585+
#[test]
3586+
fn test_syntax_error_empty_func() {
3587+
// Test that we do not panic on invalid function definition.
3588+
let text = "def hello()\n pass\n";
3589+
let mut ser = make_ser(text);
3590+
let opt = ParseOptions::from(PySourceType::Python);
3591+
let parsed = parse_unchecked(text, opt);
3592+
let ast = parsed.syntax();
3593+
3594+
// Should not panic
3595+
ast.serialize(&mut ser);
3596+
3597+
// Syntax error is reported normally
3598+
assert!(ser.extra_errors.is_empty());
3599+
}
3600+
3601+
#[test]
3602+
fn test_syntax_error_empty_func_2() {
3603+
// Same as above, but after a valid function.
3604+
let text = "def ok():\n pass\ndef hello()\n pass\n";
3605+
let mut ser = make_ser(text);
3606+
let opt = ParseOptions::from(PySourceType::Python);
3607+
let parsed = parse_unchecked(text, opt);
3608+
let ast = parsed.syntax();
3609+
3610+
// Should not panic
3611+
ast.serialize(&mut ser);
3612+
3613+
// Syntax error is reported normally
3614+
assert!(ser.extra_errors.is_empty());
3615+
}
3616+
3617+
#[test]
3618+
fn test_syntax_error_broken_args() {
3619+
// Test that we do not panic on invalid function arguments.
3620+
let source = "def f(x, (y, z)): pass\n";
3621+
let path = write_temp_py("test_source", source);
3622+
// Simply check that we do not panic
3623+
let _ = serialize_python_file(&path, None, false, Options::default()).unwrap();
3624+
let _ = std::fs::remove_file(&path);
3625+
}
3626+
3627+
#[test]
3628+
fn test_syntax_error_broken_args_2() {
3629+
// Same as above, but after a valid function.
3630+
let source = "def ok(x): pass\ndef f(x, (y, z)): pass\n";
3631+
let path = write_temp_py("test_source", source);
3632+
// Simply check that we do not panic
3633+
let _ = serialize_python_file(&path, None, false, Options::default()).unwrap();
3634+
let _ = std::fs::remove_file(&path);
3635+
}
3636+
35823637
#[test]
35833638
fn test_unicode_with_crlf_line_endings() {
35843639
// Test that Unicode handling works correctly with Windows (CRLF) line endings

0 commit comments

Comments
 (0)