diff --git a/Cargo.lock b/Cargo.lock index 6fe2910..7f145f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index d2a9d99..2212590 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] } diff --git a/crates/py2erg/convert.rs b/crates/py2erg/convert.rs index 6f61d9c..b478f2f 100644 --- a/crates/py2erg/convert.rs +++ b/crates/py2erg/convert.rs @@ -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, // e.g. def id(x: T) -> T: ... => appeared_types = {T} appeared_type_names: HashSet, + 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, init_def: Def) -> Option { 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 diff --git a/crates/pylyzer_core/handle_err.rs b/crates/pylyzer_core/handle_err.rs index 840efa6..e6a0879 100644 --- a/crates/pylyzer_core/handle_err.rs +++ b/crates/pylyzer_core/handle_err.rs @@ -37,10 +37,11 @@ fn handle_name_error(error: CompileError) -> Option { || { main.contains(" is not defined") && { let name = StyledStr::destyle(main.trim_end_matches(" is not defined")); - error - .core - .get_hint() - .is_some_and(|hint| hint.contains(name)) + name == "Any" + || error + .core + .get_hint() + .is_some_and(|hint| hint.contains(name)) } } { diff --git a/tests/class.py b/tests/class.py index 24edc40..452359b 100644 --- a/tests/class.py +++ b/tests/class.py @@ -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 diff --git a/tests/err/class.py b/tests/err/class.py new file mode 100644 index 0000000..ee6dfd7 --- /dev/null +++ b/tests/err/class.py @@ -0,0 +1,4 @@ +class Foo: + def invalid_append(self): + paths: list[str] = [] + paths.append(self) # ERR diff --git a/tests/projection.py b/tests/projection.py index 925c8ab..0eec72a 100644 --- a/tests/projection.py +++ b/tests/projection.py @@ -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 diff --git a/tests/test.rs b/tests/test.rs index 8f7ade9..9a78079 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -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)