diff --git a/crates/red_knot_python_semantic/src/semantic_index/builder.rs b/crates/red_knot_python_semantic/src/semantic_index/builder.rs index 6bf06f92026c3..c11de70879e9f 100644 --- a/crates/red_knot_python_semantic/src/semantic_index/builder.rs +++ b/crates/red_knot_python_semantic/src/semantic_index/builder.rs @@ -753,6 +753,14 @@ where self.flow_merge(state); } let pre_except_state = self.flow_snapshot(); + + // Account for the fact that we could be visiting a nested `try` block, + // in which case the outer `try` block must be kept informed about all the possible + // between-definition states we could have encountered in the inner `try` block(!) + if self.visiting_try_block { + self.try_block_definition_states.push(self.flow_snapshot()); + } + let mut post_except_states = vec![]; let num_handlers = handlers.len(); diff --git a/crates/red_knot_python_semantic/src/types/infer.rs b/crates/red_knot_python_semantic/src/types/infer.rs index 992b32d89052f..1b53f20ba88e0 100644 --- a/crates/red_knot_python_semantic/src/types/infer.rs +++ b/crates/red_knot_python_semantic/src/types/infer.rs @@ -4939,6 +4939,37 @@ mod tests { Ok(()) } + #[test] + fn exception_raised_in_nested_try_block_caught_in_outer() -> anyhow::Result<()> { + let mut db = setup_db(); + + db.write_dedented( + "src/a.py", + " + try: + try: + x = 1 + x = 2 + except TypeError: + x = 3 + else: + x = 4 + except: + pass + ", + )?; + + assert_file_diagnostics(&db, "src/a.py", &[]); + + // We might have left the inner `try` or the inner `except TypeError` + // block due to another exception, in which case the assignments in the inner `try` block + // would not necessarily have been overridden by the otherwise-exhaustive + // `except`/`else` branches in the inner block. + assert_public_ty(&db, "src/a.py", "x", "Unbound | Literal[1, 2, 3, 4]"); + + Ok(()) + } + #[test] fn basic_comprehension() -> anyhow::Result<()> { let mut db = setup_db();