mirror of https://github.com/mtshiba/pylyzer
fix: type inference bugs
This commit is contained in:
parent
faec281fa9
commit
9cd1846216
|
|
@ -150,9 +150,9 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
|||
|
||||
[[package]]
|
||||
name = "els"
|
||||
version = "0.1.62"
|
||||
version = "0.1.63-nightly.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98d4bb4cbce0f519100ba0d6aa5541aa69aa00b0f61bdb862c81124f9cf38cea"
|
||||
checksum = "99d2ae54e13d256ec9a09554249ccc498c31022836c76bc5dc1b11b072f0a753"
|
||||
dependencies = [
|
||||
"erg_common",
|
||||
"erg_compiler",
|
||||
|
|
@ -166,9 +166,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "erg_common"
|
||||
version = "0.6.50"
|
||||
version = "0.6.51-nightly.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15fcd8b1d8d47238d1488f7a05a8131b77b89adb54c867327b83db272a919344"
|
||||
checksum = "abc654d256215d0d1b2ccb5d1e80c9188148dd59714d0a642f5738bf0271197a"
|
||||
dependencies = [
|
||||
"backtrace-on-stack-overflow",
|
||||
"erg_proc_macros",
|
||||
|
|
@ -179,9 +179,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "erg_compiler"
|
||||
version = "0.6.50"
|
||||
version = "0.6.51-nightly.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5b708d63c430435aac418e0822ef435b6b26c5338e5795130cce308f319505"
|
||||
checksum = "223b817462901cfef987f38c21a18f9637f7f796dbe58a3b51a1e09489fd25be"
|
||||
dependencies = [
|
||||
"erg_common",
|
||||
"erg_parser",
|
||||
|
|
@ -189,9 +189,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "erg_parser"
|
||||
version = "0.6.50"
|
||||
version = "0.6.51-nightly.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b79c7b5789c93deeeb21cbe9d4e0c62db60712a187a1c260210079d3750b4be"
|
||||
checksum = "13d9eaf0b3076b05cb7290be98de5c1cbd73ab7a72b090f67d5911e03d062501"
|
||||
dependencies = [
|
||||
"erg_common",
|
||||
"erg_proc_macros",
|
||||
|
|
@ -200,9 +200,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "erg_proc_macros"
|
||||
version = "0.6.50"
|
||||
version = "0.6.51-nightly.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99659bb992c4e9da4af751d63fa126034025ffe79e07bfdbf013d9e165e768fc"
|
||||
checksum = "e3d2e883cddea276b76add108da8d16276d9d6962a0079561515e701121a3e1a"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ edition = "2021"
|
|||
repository = "https://github.com/mtshiba/pylyzer"
|
||||
|
||||
[workspace.dependencies]
|
||||
erg_common = { version = "0.6.50", features = ["py_compat", "els"] }
|
||||
erg_compiler = { version = "0.6.50", features = ["py_compat", "els"] }
|
||||
els = { version = "0.1.50", features = ["py_compat"] }
|
||||
erg_common = { version = "0.6.51-nightly.0", features = ["py_compat", "els"] }
|
||||
erg_compiler = { version = "0.6.51-nightly.0", features = ["py_compat", "els"] }
|
||||
els = { version = "0.1.63-nightly.0", features = ["py_compat"] }
|
||||
# rustpython-parser = { version = "0.3.0", features = ["all-nodes-with-ranges", "location"] }
|
||||
# rustpython-ast = { version = "0.3.0", features = ["all-nodes-with-ranges", "location"] }
|
||||
rustpython-parser = { git = "https://github.com/RustPython/Parser", version = "0.4.0", features = ["all-nodes-with-ranges", "location"] }
|
||||
|
|
|
|||
|
|
@ -320,6 +320,19 @@ pub struct BlockInfo {
|
|||
pub kind: BlockKind,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ReturnKind {
|
||||
None,
|
||||
Return,
|
||||
Yield,
|
||||
}
|
||||
|
||||
impl ReturnKind {
|
||||
pub const fn is_none(&self) -> bool {
|
||||
matches!(self, Self::None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LocalContext {
|
||||
pub name: String,
|
||||
|
|
@ -329,6 +342,7 @@ pub struct LocalContext {
|
|||
type_vars: HashMap<String, TypeVarInfo>,
|
||||
// e.g. def id(x: T) -> T: ... => appeared_types = {T}
|
||||
appeared_type_names: HashSet<String>,
|
||||
return_kind: ReturnKind,
|
||||
}
|
||||
|
||||
impl LocalContext {
|
||||
|
|
@ -339,6 +353,7 @@ impl LocalContext {
|
|||
names: HashMap::new(),
|
||||
type_vars: HashMap::new(),
|
||||
appeared_type_names: HashSet::new(),
|
||||
return_kind: ReturnKind::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -667,6 +682,14 @@ impl ASTConverter {
|
|||
&self.contexts.last().unwrap().name
|
||||
}
|
||||
|
||||
fn cur_context(&self) -> &LocalContext {
|
||||
self.contexts.last().unwrap()
|
||||
}
|
||||
|
||||
fn cur_context_mut(&mut self) -> &mut LocalContext {
|
||||
self.contexts.last_mut().unwrap()
|
||||
}
|
||||
|
||||
fn parent_name(&self) -> &str {
|
||||
&self.contexts[self.contexts.len().saturating_sub(2)].name
|
||||
}
|
||||
|
|
@ -2033,6 +2056,11 @@ impl ASTConverter {
|
|||
);
|
||||
Expr::Compound(Compound::new(vec![Expr::Def(def), target]))
|
||||
}
|
||||
py_ast::Expr::Yield(_) => {
|
||||
self.cur_context_mut().return_kind = ReturnKind::Yield;
|
||||
log!(err "unimplemented: {:?}", expr);
|
||||
Expr::Dummy(Dummy::new(None, vec![]))
|
||||
}
|
||||
_other => {
|
||||
log!(err "unimplemented: {:?}", _other);
|
||||
Expr::Dummy(Dummy::new(None, vec![]))
|
||||
|
|
@ -2087,7 +2115,7 @@ impl ASTConverter {
|
|||
// self.y = y
|
||||
// self.z = z
|
||||
// ↓
|
||||
// requirement : {x: Int, y: Int, z: Never}
|
||||
// requirement : {x: Int, y: Int, z: Any}
|
||||
// returns : .__call__(x: Int, y: Int, z: Obj): Self = .unreachable()
|
||||
fn extract_init(&mut self, base_type: &mut Option<Expr>, init_def: Def) -> Option<Def> {
|
||||
self.check_init_sig(&init_def.sig)?;
|
||||
|
|
@ -2134,9 +2162,8 @@ impl ASTConverter {
|
|||
} else if let Some(typ) = redef.t_spec.map(|t_spec| t_spec.t_spec_as_expr) {
|
||||
*typ
|
||||
} else {
|
||||
Expr::from(Accessor::Ident(Identifier::public_with_line(
|
||||
DOT,
|
||||
"Never".into(),
|
||||
Expr::from(Accessor::Ident(Identifier::private_with_line(
|
||||
"Any".into(),
|
||||
attr.obj.ln_begin().unwrap_or(0),
|
||||
)))
|
||||
};
|
||||
|
|
@ -2473,8 +2500,22 @@ impl ASTConverter {
|
|||
.unwrap_or(func_def.type_params)
|
||||
};
|
||||
let bounds = self.get_type_bounds(type_params);
|
||||
let sig = Signature::Subr(SubrSignature::new(decos, ident, bounds, params, return_t));
|
||||
let mut sig =
|
||||
Signature::Subr(SubrSignature::new(decos, ident, bounds, params, return_t));
|
||||
let block = self.convert_block(func_def.body, BlockKind::Function);
|
||||
if self.cur_context().return_kind.is_none() {
|
||||
let Signature::Subr(subr) = &mut sig else {
|
||||
unreachable!()
|
||||
};
|
||||
if subr.return_t_spec.is_none() {
|
||||
let none = TypeSpecWithOp::new(
|
||||
Token::dummy(TokenKind::Colon, ":"),
|
||||
TypeSpec::mono(Identifier::private("NoneType".into())),
|
||||
Expr::static_local("NoneType"),
|
||||
);
|
||||
subr.return_t_spec = Some(Box::new(none));
|
||||
}
|
||||
}
|
||||
let body = DefBody::new(EQUAL, block, DefId(0));
|
||||
let def = Def::new(sig, body);
|
||||
self.pop();
|
||||
|
|
@ -3030,6 +3071,7 @@ impl ASTConverter {
|
|||
}
|
||||
}
|
||||
py_ast::Stmt::Return(return_) => {
|
||||
self.cur_context_mut().return_kind = ReturnKind::Return;
|
||||
let loc = return_.location();
|
||||
let value = return_
|
||||
.value
|
||||
|
|
|
|||
|
|
@ -37,7 +37,8 @@ fn handle_name_error(error: CompileError) -> Option<CompileError> {
|
|||
|| {
|
||||
main.contains(" is not defined") && {
|
||||
let name = StyledStr::destyle(main.trim_end_matches(" is not defined"));
|
||||
error
|
||||
name == "Any"
|
||||
|| error
|
||||
.core
|
||||
.get_hint()
|
||||
.is_some_and(|hint| hint.contains(name))
|
||||
|
|
|
|||
|
|
@ -98,3 +98,10 @@ class MyList(list):
|
|||
return MyList(lis)
|
||||
else:
|
||||
return None
|
||||
|
||||
class Implicit:
|
||||
def __init__(self):
|
||||
self.foo = False
|
||||
|
||||
def set_foo(self):
|
||||
self.foo = True
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
class Foo:
|
||||
def invalid_append(self):
|
||||
paths: list[str] = []
|
||||
paths.append(self) # ERR
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
def imaginary(x):
|
||||
x.imag
|
||||
return x.imag
|
||||
|
||||
assert imaginary(1) == 0
|
||||
assert imaginary(1.0) <= 0.0
|
||||
|
|
@ -8,7 +8,7 @@ print(imaginary("a")) # ERR
|
|||
class C:
|
||||
def method(self, x): return x
|
||||
def call_method(obj, x):
|
||||
obj.method(x)
|
||||
return obj.method(x)
|
||||
|
||||
c = C()
|
||||
assert call_method(c, 1) == 1
|
||||
|
|
|
|||
|
|
@ -103,6 +103,11 @@ fn exec_class() -> Result<(), String> {
|
|||
expect("tests/class.py", 0, 6)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_class_err() -> Result<(), String> {
|
||||
expect("tests/err/class.py", 0, 1)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_errors() -> Result<(), String> {
|
||||
expect("tests/errors.py", 0, 3)
|
||||
|
|
|
|||
Loading…
Reference in New Issue