diff --git a/Cargo.lock b/Cargo.lock index ea1e3f4..c105364 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -150,9 +150,9 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "els" -version = "0.1.63-nightly.0" +version = "0.1.63-nightly.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d2ae54e13d256ec9a09554249ccc498c31022836c76bc5dc1b11b072f0a753" +checksum = "499bf0ea920c33a3b8f905450435135a8fbefc6c06c3b53f2b5c0e66d25367b5" dependencies = [ "erg_common", "erg_compiler", @@ -166,9 +166,9 @@ dependencies = [ [[package]] name = "erg_common" -version = "0.6.51-nightly.0" +version = "0.6.51-nightly.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abc654d256215d0d1b2ccb5d1e80c9188148dd59714d0a642f5738bf0271197a" +checksum = "1d112c6737def3a5ee868510f4ce9ef9be64b2a0bdf8899b24576a1e8521d401" dependencies = [ "backtrace-on-stack-overflow", "erg_proc_macros", @@ -179,9 +179,9 @@ dependencies = [ [[package]] name = "erg_compiler" -version = "0.6.51-nightly.0" +version = "0.6.51-nightly.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "223b817462901cfef987f38c21a18f9637f7f796dbe58a3b51a1e09489fd25be" +checksum = "32d241b882842c86c6c3e2aa54c4c8121fdbedd8a15a7b563cf5f7c1c5c0c0bb" dependencies = [ "erg_common", "erg_parser", @@ -189,9 +189,9 @@ dependencies = [ [[package]] name = "erg_parser" -version = "0.6.51-nightly.0" +version = "0.6.51-nightly.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13d9eaf0b3076b05cb7290be98de5c1cbd73ab7a72b090f67d5911e03d062501" +checksum = "91efa61b1495ee680394b6f9d98d84535dc8445329f0310734a3e40671a0061a" dependencies = [ "erg_common", "erg_proc_macros", @@ -200,9 +200,9 @@ dependencies = [ [[package]] name = "erg_proc_macros" -version = "0.6.51-nightly.0" +version = "0.6.51-nightly.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d2e883cddea276b76add108da8d16276d9d6962a0079561515e701121a3e1a" +checksum = "bd78027648c82db68efed1a1789bd84ce09f07ea7630085c9015c4ed6b1a192d" dependencies = [ "quote", "syn 1.0.109", diff --git a/Cargo.toml b/Cargo.toml index cf7847d..d297911 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.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"] } +erg_common = { version = "0.6.51-nightly.1", features = ["py_compat", "els"] } +erg_compiler = { version = "0.6.51-nightly.1", features = ["py_compat", "els"] } +els = { version = "0.1.63-nightly.1", 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 b478f2f..da0c1ea 100644 --- a/crates/py2erg/convert.rs +++ b/crates/py2erg/convert.rs @@ -2261,10 +2261,12 @@ impl ASTConverter { &mut self, body: Vec, inherit: bool, + class_name: Expr, ) -> (Option, ClassAttrs) { let mut base_type = None; let mut attrs = vec![]; let mut init_is_defined = false; + let mut call_params_len = None; for stmt in body { match self.convert_statement(stmt, true) { Expr::Def(mut def) => { @@ -2274,12 +2276,55 @@ impl ASTConverter { .insert(Decorator(Expr::static_local("Override"))); } } - if def + if def.sig.decorators().is_some_and(|decos| { + decos.iter().any(|deco| { + deco.expr() + .get_name() + .is_some_and(|name| name == "property") + }) + }) { + // class Foo: + // @property + // def foo(self): ... + // ↓ + // class Foo: + // def foo_(self): ... + // foo = Foo(*[]).foo_() + let mut args = Args::empty(); + if call_params_len.as_ref().is_some_and(|&len| len >= 1) { + args.set_var_args(PosArg::new(Expr::List(List::Normal( + NormalList::new( + Token::dummy(TokenKind::LSqBr, "["), + Token::dummy(TokenKind::RSqBr, "]"), + Args::empty(), + ), + )))); + } + let instance = class_name.clone().call(args); + let name = def.sig.ident().unwrap().clone(); + def.sig + .ident_mut() + .unwrap() + .name + .rename(format!("{} ", name.inspect()).into()); + let escaped = def.sig.ident().unwrap().clone(); + let call = Expr::Call(instance).method_call_expr(escaped, Args::empty()); + let t_spec = def.sig.t_spec_op_mut().cloned(); + let sig = + Signature::Var(VarSignature::new(VarPattern::Ident(name), t_spec)); + let var_def = + Def::new(sig, DefBody::new(EQUAL, Block::new(vec![call]), DefId(0))); + attrs.push(ClassAttr::Def(def)); + attrs.push(ClassAttr::Def(var_def)); + } else if def .sig .ident() .is_some_and(|id| &id.inspect()[..] == "__init__") { if let Some(call_def) = self.extract_init(&mut base_type, def) { + if let Some(params) = call_def.sig.params() { + call_params_len = Some(params.len()); + } attrs.insert(0, ClassAttr::Def(call_def)); init_is_defined = true; } @@ -2355,7 +2400,7 @@ impl ASTConverter { TypeSpec::mono(ident.clone()) }; let class_as_expr = Expr::Accessor(Accessor::Ident(ident)); - let (base_type, attrs) = self.extract_method(body, inherit); + let (base_type, attrs) = self.extract_method(body, inherit, class_as_expr.clone()); self.block_id_counter += 1; let methods = Methods::new( DefId(self.block_id_counter), diff --git a/tests/err/property.py b/tests/err/property.py new file mode 100644 index 0000000..9168cdb --- /dev/null +++ b/tests/err/property.py @@ -0,0 +1,11 @@ +class Foo: + x: int + def __init__(self, x): + self.x = x + + @property + def foo(self): + return self.x + +f = Foo(1) +print(f.foo + "a") # ERR diff --git a/tests/property.py b/tests/property.py new file mode 100644 index 0000000..4020306 --- /dev/null +++ b/tests/property.py @@ -0,0 +1,11 @@ +class Foo: + x: int + def __init__(self, x): + self.x = x + + @property + def foo(self): + return self.x + +f = Foo(1) +assert f.foo + 1 == 2 diff --git a/tests/test.rs b/tests/test.rs index 9a78079..8b95a89 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -128,6 +128,16 @@ fn exec_projection() -> Result<(), String> { expect("tests/projection.py", 0, 5) } +#[test] +fn exec_property() -> Result<(), String> { + expect("tests/property.py", 0, 0) +} + +#[test] +fn exec_property_err() -> Result<(), String> { + expect("tests/err/property.py", 0, 1) +} + #[test] fn exec_pyi() -> Result<(), String> { expect("tests/pyi.py", 0, 5)