Skip to content

Commit 7207921

Browse files
committed
Refactor, fix build, and mark uninit twice
1 parent 0784aa6 commit 7207921

File tree

2 files changed

+64
-33
lines changed

2 files changed

+64
-33
lines changed

pyrefly/lib/binding/stmt.rs

Lines changed: 45 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use crate::binding::binding::KeyExpect;
3535
use crate::binding::binding::LinkedKey;
3636
use crate::binding::binding::RaisedException;
3737
use crate::binding::bindings::BindingsBuilder;
38+
use crate::binding::bindings::LookupKind;
3839
use crate::binding::bindings::MutableCaptureLookupKind;
3940
use crate::binding::expr::Usage;
4041
use crate::binding::narrow::NarrowOps;
@@ -247,6 +248,25 @@ impl<'a> BindingsBuilder<'a> {
247248
self.scopes.mark_flow_termination();
248249
}
249250

251+
fn mark_exception_name_uninitialized(&mut self, name: &Identifier) {
252+
// https://docs.python.org/3/reference/compound_stmts.html#except-clause
253+
// Mark the handler name as uninitialized, so that it can't be used.
254+
let idx = self.lookup_name(
255+
Hashed::new(&name.id),
256+
LookupKind::Regular,
257+
&mut Usage::MutableLookup,
258+
);
259+
if let Ok(idx) = idx {
260+
self.scopes.upsert_flow_info(
261+
Hashed::new(&name.id),
262+
idx,
263+
Some(FlowStyle::Uninitialized),
264+
);
265+
} else {
266+
panic!("Should have found the exception name `{name}` in the current scope");
267+
}
268+
}
269+
250270
/// Evaluate the statements and update the bindings.
251271
/// Every statement should end up in the bindings, perhaps with a location that is never used.
252272
pub fn stmt(&mut self, x: Stmt) {
@@ -731,8 +751,12 @@ impl<'a> BindingsBuilder<'a> {
731751
self.scopes.swap_current_flow_with(&mut base);
732752
branches.push(base);
733753

734-
// Store the last name of the exception, so that we can mark it uninitialized in the next scope.
735-
let mut last_name: Option<Identifier> = None;
754+
// Store the last caught exception name, so that we can mark any
755+
// caught exception to be uninitialized at the end of an
756+
// `except` block, and at the beginning of the next block. This
757+
// allows us to ensure that the caught exception name goes out
758+
// of scope after the corresponding `except` block.
759+
let mut last_caught_exception_name: Option<Identifier> = None;
736760

737761
for h in x.handlers {
738762
base = self.scopes.clone_current_flow();
@@ -758,49 +782,38 @@ impl<'a> BindingsBuilder<'a> {
758782
Binding::ExceptionHandler(type_, x.is_star),
759783
);
760784
}
761-
if let Some(ref name) = last_name {
785+
786+
if let Some(ref name) = last_caught_exception_name {
762787
// https://docs.python.org/3/reference/compound_stmts.html#except-clause
763-
// Mark the previous handler name as uninitialized in
764-
// the current scope, so that it can't be used.
765-
let idx = self.lookup_name(Hashed::new(&name.id), LookupKind::Regular);
766-
if let Ok(idx) = idx {
767-
self.scopes.upsert_flow_info(
768-
Hashed::new(&name.id),
769-
idx,
770-
Some(FlowStyle::Uninitialized),
771-
);
772-
} else {
773-
panic!(
774-
"Should have found the handler name {name} in the current scope"
775-
);
776-
}
788+
// Mark the previous caught exception name as
789+
// uninitialized in the current scope, so that it can't
790+
// be used.
791+
self.mark_exception_name_uninitialized(name);
777792
}
778793
self.stmts(h.body);
794+
795+
if let Some(ref name) = h.name {
796+
// Mark the current caught exception name as
797+
// uninitialized in the current scope, so that the flow
798+
// merges correctly.
799+
self.mark_exception_name_uninitialized(name);
800+
}
801+
779802
self.scopes.swap_current_flow_with(&mut base);
780803

781804
if let Some(name) = h.name {
782-
last_name = Some(name.clone());
805+
last_caught_exception_name = Some(name.clone());
783806
} else {
784-
last_name = None;
807+
last_caught_exception_name = None;
785808
}
786809
branches.push(base);
787810
}
788811

789812
self.set_current_flow_to_merged_branches(branches, range);
790-
if let Some(ref name) = last_name {
791-
// https://docs.python.org/3/reference/compound_stmts.html#except-clause
792-
// Mark the previous handler name as uninitialized in
813+
if let Some(ref name) = last_caught_exception_name {
814+
// Mark the last caught exception name as uninitialized in
793815
// the current scope, so that it can't be used.
794-
let idx = self.lookup_name(Hashed::new(&name.id), LookupKind::Regular);
795-
if let Ok(idx) = idx {
796-
self.scopes.upsert_flow_info(
797-
Hashed::new(&name.id),
798-
idx,
799-
Some(FlowStyle::Uninitialized),
800-
);
801-
} else {
802-
panic!("Should have found the handler name {name} in the current scope");
803-
}
816+
self.mark_exception_name_uninitialized(name);
804817
}
805818
self.stmts(x.finalbody);
806819
}

pyrefly/lib/test/scope.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,14 +468,32 @@ def g():
468468
finally:
469469
e2 # E: `e2` is uninitialized
470470
471+
e2 # E: `e2` is uninitialized
472+
471473
def h():
472474
try:
473475
1 / 0
474476
except OSError as e3:
475477
pass
476478
except Exception:
477479
e3 # E: `e3` is uninitialized
480+
481+
e3 # E: `e3` is uninitialized
482+
483+
def i():
484+
try:
485+
1 / 0
486+
except OSError as e4:
487+
pass
488+
except Exception:
489+
pass
490+
except OSError as e5:
491+
pass
478492
finally:
479-
e3 # E: `e3` may be uninitialized
493+
e4 # E: `e4` is uninitialized
494+
e5 # E: `e5` is uninitialized
495+
496+
e4 # E: `e4` is uninitialized
497+
e5 # E: `e5` is uninitialized
480498
"#,
481499
);

0 commit comments

Comments
 (0)