Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 29 additions & 3 deletions pyrefly/lib/binding/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use crate::binding::binding::KeyExpect;
use crate::binding::binding::LinkedKey;
use crate::binding::binding::RaisedException;
use crate::binding::bindings::BindingsBuilder;
use crate::binding::bindings::LookupKind;
use crate::binding::bindings::MutableCaptureLookupKind;
use crate::binding::expr::Usage;
use crate::binding::narrow::NarrowOps;
Expand Down Expand Up @@ -735,14 +736,14 @@ impl<'a> BindingsBuilder<'a> {
base = self.scopes.clone_current_flow();
let range = h.range();
let h = h.except_handler().unwrap(); // Only one variant for now
if let Some(name) = h.name
if let Some(ref name) = h.name
&& let Some(mut type_) = h.type_
{
let mut handler =
self.declare_current_idx(Key::Definition(ShortIdentifier::new(&name)));
self.declare_current_idx(Key::Definition(ShortIdentifier::new(name)));
self.ensure_expr(&mut type_, handler.usage());
self.bind_definition_current(
&name,
name,
handler,
Binding::ExceptionHandler(type_, x.is_star),
FlowStyle::Other,
Expand All @@ -755,7 +756,32 @@ impl<'a> BindingsBuilder<'a> {
Binding::ExceptionHandler(type_, x.is_star),
);
}

self.stmts(h.body);

if let Some(ref name) = h.name {
// Mark the current caught exception name as
// uninitialized in the current scope, so that it cannot
// be used later.
// https://docs.python.org/3/reference/compound_stmts.html#except-clause
let idx = self.lookup_name(
Hashed::new(&name.id),
LookupKind::Regular,
&mut Usage::MutableLookup,
);
if let Ok(idx) = idx {
self.scopes.upsert_flow_info(
Hashed::new(&name.id),
idx,
Some(FlowStyle::Uninitialized),
);
} else {
panic!(
"Should have found the exception name `{name}` in the current scope"
);
}
}

self.scopes.swap_current_flow_with(&mut base);
branches.push(base);
}
Expand Down
76 changes: 76 additions & 0 deletions pyrefly/lib/test/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,3 +447,79 @@ def foo():
y = 42
"#,
);

// https://github.com/facebook/pyrefly/issues/154
testcase!(
test_exception_not_in_scope,
r#"
def try_except():
try:
1 / 0
except Exception as e1:
pass

e1 # E: `e1` is uninitialized

def try_except_finally():
try:
1 / 0
except Exception as e2:
pass
finally:
e2 # E: `e2` is uninitialized

e2 # E: `e2` is uninitialized

def try_except_twice():
try:
1 / 0
except OSError as e3:
pass
except Exception:
e3 # E: `e3` is uninitialized

e3 # E: `e3` is uninitialized

def try_except_multiple_finally():
try:
1 / 0
except OSError as e4:
pass
except Exception:
pass
except OSError as e5:
e4 # E: `e4` is uninitialized
finally:
e4 # E: `e4` is uninitialized
e5 # E: `e5` is uninitialized

e4 # E: `e4` is uninitialized
e5 # E: `e5` is uninitialized

def try_except_else():
try:
1 / 0
except OSError as e6:
pass
except Exception:
pass
else:
e6 # E: `e6` is uninitialized

e6 # E: `e6` is uninitialized

def try_except_else_finally():
try:
1 / 0
except OSError as e7:
pass
except Exception:
pass
else:
e7 # E: `e7` is uninitialized
finally:
e7 # E: `e7` is uninitialized

e7 # E: `e7` is uninitialized
"#,
);
Loading