diff --git a/Cargo.lock b/Cargo.lock index c85aee3..652763d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -252,8 +252,9 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "els" -version = "0.1.19-nightly.2" -source = "git+https://github.com/erg-lang/erg?branch=main#228a74d29d83907ffcb62dd849036200bd167c06" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94694dcfb56e12207fb27ff73158b9fb759497485790c71de98aa4b0136c72f6" dependencies = [ "erg_common", "erg_compiler", @@ -273,8 +274,9 @@ dependencies = [ [[package]] name = "erg_common" -version = "0.6.7-nightly.2" -source = "git+https://github.com/erg-lang/erg?branch=main#228a74d29d83907ffcb62dd849036200bd167c06" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88c9d058b405901b41dbc282f9927fd5295dc46c50fa2f8fc75f2c3d7783ac77" dependencies = [ "backtrace-on-stack-overflow", "hermit-abi", @@ -284,8 +286,9 @@ dependencies = [ [[package]] name = "erg_compiler" -version = "0.6.7-nightly.2" -source = "git+https://github.com/erg-lang/erg?branch=main#228a74d29d83907ffcb62dd849036200bd167c06" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb25a8403805eb864bb3f56c920dfafadca30bf2ff8be5c733d3449e16ae224" dependencies = [ "erg_common", "erg_parser", @@ -293,8 +296,9 @@ dependencies = [ [[package]] name = "erg_parser" -version = "0.6.7-nightly.2" -source = "git+https://github.com/erg-lang/erg?branch=main#228a74d29d83907ffcb62dd849036200bd167c06" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea28285bc487cc9807beafd63664c0903bf8bca86c5ac692dd8a3b783c03bdb7" dependencies = [ "erg_common", "unicode-xid 0.2.4", diff --git a/Cargo.toml b/Cargo.toml index 6474449..f14d6a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,13 +22,13 @@ edition = "2021" repository = "https://github.com/mtshiba/pylyzer" [workspace.dependencies] -# erg_common = { version = "0.6.7-nightly.2", features = ["py_compatible", "els"] } -# erg_compiler = { version = "0.6.7-nightly.2", features = ["py_compatible", "els"] } -# els = { version = "0.1.19-nightly.2", features = ["py_compatible"] } +erg_common = { version = "0.6.7", features = ["py_compatible", "els"] } +erg_compiler = { version = "0.6.7", features = ["py_compatible", "els"] } +els = { version = "0.1.19", features = ["py_compatible"] } rustpython-parser = "0.1.2" -erg_compiler = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compatible", "els"] } -erg_common = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compatible", "els"] } -els = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compatible"] } +# erg_compiler = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compatible", "els"] } +# erg_common = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compatible", "els"] } +# els = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compatible"] } [features] debug = ["erg_compiler/debug", "erg_common/debug", "py2erg/debug"] diff --git a/README.md b/README.md index d679d01..d012ab7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Build status Build status -`pylyzer` is a static code analyzer / language server for Python written in Rust. +`pylyzer` is a static code analyzer / language server for Python, written in Rust. ## Installation @@ -20,8 +20,7 @@ cargo install pylyzer pip install pylyzer ``` -If installed this way, type resolution for the standard libraries is not available. -If you want to use it, you need to [install Erg](https://github.com/mtshiba/ergup). +If installed this way, you also need to [install Erg](https://github.com/mtshiba/ergup). ```bash curl -L https://github.com/mtshiba/ergup/raw/main/ergup.py | python3 diff --git a/crates/py2erg/convert.rs b/crates/py2erg/convert.rs index 7e4cd63..78bf1ce 100644 --- a/crates/py2erg/convert.rs +++ b/crates/py2erg/convert.rs @@ -1019,7 +1019,7 @@ impl ASTConverter { ConstArgs::empty(), ))); let sig = Signature::Subr(SubrSignature::new( - HashSet::new(), + set! { Decorator(Expr::static_local("Override")) }, call_ident, TypeBoundSpecs::empty(), params, @@ -1046,7 +1046,7 @@ impl ASTConverter { ConstArgs::empty(), ))); let sig = Signature::Subr(SubrSignature::new( - HashSet::new(), + set! { Decorator(Expr::static_local("Override")) }, call_ident, TypeBoundSpecs::empty(), params, @@ -1059,13 +1059,23 @@ impl ASTConverter { Def::new(sig, body) } - fn extract_method(&mut self, body: Vec>) -> (Option, ClassAttrs) { + fn extract_method( + &mut self, + body: Vec>, + inherit: bool, + ) -> (Option, ClassAttrs) { let mut base_type = None; let mut attrs = vec![]; let mut init_is_defined = false; for stmt in body { match self.convert_statement(stmt, true) { - Expr::Def(def) => { + Expr::Def(mut def) => { + if inherit { + if let Signature::Subr(subr) = &mut def.sig { + subr.decorators + .insert(Decorator(Expr::static_local("Override"))); + } + } if def .sig .ident() @@ -1124,7 +1134,7 @@ impl ASTConverter { _other => {} // TODO: } } - if !init_is_defined { + if !init_is_defined && !inherit { attrs.insert(0, ClassAttr::Def(self.gen_default_init(0))); } (base_type, ClassAttrs::new(attrs)) @@ -1134,13 +1144,14 @@ impl ASTConverter { &mut self, ident: Identifier, body: Vec>, + inherit: bool, ) -> (Option, Vec) { let class = TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(SimpleTypeSpec::new( ident.clone(), ConstArgs::empty(), ))); let class_as_expr = Expr::Accessor(Accessor::Ident(ident)); - let (base_type, attrs) = self.extract_method(body); + let (base_type, attrs) = self.extract_method(body, inherit); let methods = Methods::new(class, class_as_expr, VisModifierSpec::Public(DOT), attrs); (base_type, vec![methods]) } @@ -1205,6 +1216,13 @@ impl ASTConverter { /// ```erg /// Foo = Inheritable Class() /// ``` + /// ```python + /// class Foo(Bar): pass + /// ``` + /// ↓ + /// ```erg + /// Foo = Inherit Bar + /// ``` fn convert_classdef( &mut self, name: String, @@ -1217,34 +1235,48 @@ impl ASTConverter { .into_iter() .map(|deco| self.convert_expr(deco)) .collect::>(); - let _bases = bases + let mut bases = bases .into_iter() .map(|base| self.convert_expr(base)) .collect::>(); + let inherit = !bases.is_empty(); self.register_name_info(&name, NameKind::Class); let class_name_loc = PyLocation::new(loc.row(), loc.column() + 6); let ident = self.convert_ident(name, class_name_loc); let sig = Signature::Var(VarSignature::new(VarPattern::Ident(ident.clone()), None)); self.namespace.push(ident.inspect().to_string()); - let (base_type, methods) = self.extract_method_list(ident, body); - let pos_args = if let Some(base) = base_type { - vec![PosArg::new(base)] + let (base_type, methods) = self.extract_method_list(ident, body, inherit); + let classdef = if inherit { + // TODO: multiple inheritance + let pos_args = vec![PosArg::new(bases.remove(0))]; + let args = Args::pos_only(pos_args, None); + let inherit_acc = Expr::Accessor(Accessor::Ident( + self.convert_ident("Inherit".to_string(), loc), + )); + let inherit_call = inherit_acc.call_expr(args); + let body = DefBody::new(EQUAL, Block::new(vec![inherit_call]), DefId(0)); + let def = Def::new(sig, body); + ClassDef::new(def, methods) } else { - vec![] + let pos_args = if let Some(base) = base_type { + vec![PosArg::new(base)] + } else { + vec![] + }; + let args = Args::pos_only(pos_args, None); + let class_acc = Expr::Accessor(Accessor::Ident( + self.convert_ident("Class".to_string(), loc), + )); + let class_call = class_acc.call_expr(args); + let inheritable_acc = Expr::Accessor(Accessor::Ident( + self.convert_ident("Inheritable".to_string(), loc), + )); + let inheritable_call = + inheritable_acc.call_expr(Args::pos_only(vec![PosArg::new(class_call)], None)); + let body = DefBody::new(EQUAL, Block::new(vec![inheritable_call]), DefId(0)); + let def = Def::new(sig, body); + ClassDef::new(def, methods) }; - let args = Args::pos_only(pos_args, None); - let class_acc = Expr::Accessor(Accessor::Ident( - self.convert_ident("Class".to_string(), loc), - )); - let class_call = class_acc.call_expr(args); - let inheritable_acc = Expr::Accessor(Accessor::Ident( - self.convert_ident("Inheritable".to_string(), loc), - )); - let inheritable_call = - inheritable_acc.call_expr(Args::pos_only(vec![PosArg::new(class_call)], None)); - let body = DefBody::new(EQUAL, Block::new(vec![inheritable_call]), DefId(0)); - let def = Def::new(sig, body); - let classdef = ClassDef::new(def, methods); self.namespace.pop(); Expr::ClassDef(classdef) } diff --git a/tests/class.py b/tests/class.py index c73515a..0ec9174 100644 --- a/tests/class.py +++ b/tests/class.py @@ -36,11 +36,19 @@ class D: return D(self.c - other.x) def __neg__(self): return D(-self.c) + def __gt__(self, other: D): + return self.c > other.c def __init__(self, c): self.c = c +class E(D): + def __add__(self, other: E): + return E(self.c + other.c) + c1 = D(1).c + 1 d = D(1) + D(2) err = C(1, 2) + D(1) # ERR ok = D(1) - C(1, 2) # OK +assert D(1) > D(0) c = -d # OK +e = E(1)