fix(pylyzer): declarations in control flow blocks

This commit is contained in:
Shunsuke Shibayama 2024-09-21 23:38:26 +09:00
parent ebd7707f77
commit 9f435706a1
2 changed files with 143 additions and 5 deletions

View file

@ -44,7 +44,7 @@ use RegistrationMode::*;
use super::eval::Substituter;
use super::instantiate::TyVarCache;
use super::instantiate_spec::ParamKind;
use super::{MethodContext, ParamSpec, TraitImpl, TypeContext};
use super::{ControlKind, MethodContext, ParamSpec, TraitImpl, TypeContext};
pub fn valid_mod_name(name: &str) -> bool {
!name.is_empty() && !name.starts_with('/') && name.trim() == name
@ -1103,6 +1103,11 @@ impl Context {
total_errs.extend(errs);
}
}
ast::Expr::Call(call) if PYTHON_MODE => {
if let Err(errs) = self.preregister_control_consts(call) {
total_errs.extend(errs);
}
}
_ => {}
}
}
@ -1113,6 +1118,44 @@ impl Context {
}
}
fn preregister_control_consts(&mut self, call: &ast::Call) -> TyCheckResult<()> {
match call
.obj
.get_name()
.and_then(|s| ControlKind::try_from(&s[..]).ok())
{
Some(ControlKind::If) => {
let Some(ast::Expr::Lambda(then)) = call.args.nth_or_key(1, "then") else {
return Ok(());
};
self.preregister_consts(&then.body)?;
if let Some(ast::Expr::Lambda(else_)) = call.args.nth_or_key(2, "else") {
self.preregister_consts(&else_.body)?;
}
}
Some(ControlKind::For) => {
let Some(ast::Expr::Lambda(body)) = call.args.nth_or_key(1, "body") else {
return Ok(());
};
self.preregister_consts(&body.body)?;
}
Some(ControlKind::While) => {
let Some(ast::Expr::Lambda(body)) = call.args.nth_or_key(1, "body") else {
return Ok(());
};
self.preregister_consts(&body.body)?;
}
Some(ControlKind::With) => {
let Some(ast::Expr::Lambda(body)) = call.args.nth_or_key(1, "body") else {
return Ok(());
};
self.preregister_consts(&body.body)?;
}
_ => {}
}
Ok(())
}
pub(crate) fn register_defs(&mut self, block: &ast::Block) -> TyCheckResult<()> {
let mut total_errs = TyCheckErrors::empty();
for expr in block.iter() {
@ -1181,6 +1224,11 @@ impl Context {
total_errs.extend(errs);
}
}
ast::Expr::Call(call) if PYTHON_MODE => {
if let Err(errs) = self.register_control_defs(call) {
total_errs.extend(errs);
}
}
_ => {}
}
}
@ -1191,6 +1239,44 @@ impl Context {
}
}
fn register_control_defs(&mut self, call: &ast::Call) -> TyCheckResult<()> {
match call
.obj
.get_name()
.and_then(|s| ControlKind::try_from(&s[..]).ok())
{
Some(ControlKind::If) => {
let Some(ast::Expr::Lambda(then)) = call.args.nth_or_key(1, "then") else {
return Ok(());
};
self.register_defs(&then.body)?;
if let Some(ast::Expr::Lambda(else_)) = call.args.nth_or_key(2, "else") {
self.register_defs(&else_.body)?;
}
}
Some(ControlKind::For) => {
let Some(ast::Expr::Lambda(body)) = call.args.nth_or_key(1, "body") else {
return Ok(());
};
self.register_defs(&body.body)?;
}
Some(ControlKind::While) => {
let Some(ast::Expr::Lambda(body)) = call.args.nth_or_key(1, "body") else {
return Ok(());
};
self.register_defs(&body.body)?;
}
Some(ControlKind::With) => {
let Some(ast::Expr::Lambda(body)) = call.args.nth_or_key(1, "body") else {
return Ok(());
};
self.register_defs(&body.body)?;
}
_ => {}
}
Ok(())
}
/// HACK: The constant expression evaluator can evaluate attributes when the type of the receiver is known.
/// import/pyimport is not a constant function, but specially assumes that the type of the module is known in the eval phase.
fn pre_import(&mut self, def: &ast::Def) -> TyCheckResult<()> {