mirror of https://github.com/mtshiba/pylyzer
feat: ABC implementation check
This commit is contained in:
parent
233b7ee382
commit
2a98535d4c
|
|
@ -150,9 +150,9 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "els"
|
name = "els"
|
||||||
version = "0.1.61"
|
version = "0.1.62"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "be76108fd7329b2130fcc168b00fddf5b7ae44ef4fa91d2246a146d28304d7ac"
|
checksum = "98d4bb4cbce0f519100ba0d6aa5541aa69aa00b0f61bdb862c81124f9cf38cea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"erg_common",
|
"erg_common",
|
||||||
"erg_compiler",
|
"erg_compiler",
|
||||||
|
|
@ -166,9 +166,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "erg_common"
|
name = "erg_common"
|
||||||
version = "0.6.49"
|
version = "0.6.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a6b12918165bc24a49d35897b6b4535360a0dec5b0641d9c1849fe0345875cc1"
|
checksum = "15fcd8b1d8d47238d1488f7a05a8131b77b89adb54c867327b83db272a919344"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace-on-stack-overflow",
|
"backtrace-on-stack-overflow",
|
||||||
"erg_proc_macros",
|
"erg_proc_macros",
|
||||||
|
|
@ -179,9 +179,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "erg_compiler"
|
name = "erg_compiler"
|
||||||
version = "0.6.49"
|
version = "0.6.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "212f2c20609d69579e8f0d860fcad6a6c0aa6434b84a92f24336da339725df52"
|
checksum = "4a5b708d63c430435aac418e0822ef435b6b26c5338e5795130cce308f319505"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"erg_common",
|
"erg_common",
|
||||||
"erg_parser",
|
"erg_parser",
|
||||||
|
|
@ -189,9 +189,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "erg_parser"
|
name = "erg_parser"
|
||||||
version = "0.6.49"
|
version = "0.6.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ae50e2439a5922d664a9a43cd116b4c30eac39b25724ef5af25c719a30b8234"
|
checksum = "6b79c7b5789c93deeeb21cbe9d4e0c62db60712a187a1c260210079d3750b4be"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"erg_common",
|
"erg_common",
|
||||||
"erg_proc_macros",
|
"erg_proc_macros",
|
||||||
|
|
@ -200,9 +200,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "erg_proc_macros"
|
name = "erg_proc_macros"
|
||||||
version = "0.6.49"
|
version = "0.6.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e2799a04d59d8dfd8f4d8a0a495354203d4efb5f48fe2f8effc521e80026714"
|
checksum = "99659bb992c4e9da4af751d63fa126034025ffe79e07bfdbf013d9e165e768fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,9 @@ edition = "2021"
|
||||||
repository = "https://github.com/mtshiba/pylyzer"
|
repository = "https://github.com/mtshiba/pylyzer"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
erg_common = { version = "0.6.49", features = ["py_compat", "els"] }
|
erg_common = { version = "0.6.50", features = ["py_compat", "els"] }
|
||||||
erg_compiler = { version = "0.6.49", features = ["py_compat", "els"] }
|
erg_compiler = { version = "0.6.50", features = ["py_compat", "els"] }
|
||||||
els = { version = "0.1.61", features = ["py_compat"] }
|
els = { version = "0.1.50", features = ["py_compat"] }
|
||||||
# rustpython-parser = { version = "0.3.0", features = ["all-nodes-with-ranges", "location"] }
|
# 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-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"] }
|
rustpython-parser = { git = "https://github.com/RustPython/Parser", version = "0.4.0", features = ["all-nodes-with-ranges", "location"] }
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,7 @@ pylyzer converts Python ASTs to Erg ASTs and passes them to Erg's type checker.
|
||||||
* [x] function/method
|
* [x] function/method
|
||||||
* [x] class
|
* [x] class
|
||||||
* [ ] `async/await`
|
* [ ] `async/await`
|
||||||
|
* [ ] user-defined abstract class
|
||||||
* [x] type inference
|
* [x] type inference
|
||||||
* [x] variable
|
* [x] variable
|
||||||
* [x] operator
|
* [x] operator
|
||||||
|
|
@ -165,10 +166,13 @@ pylyzer converts Python ASTs to Erg ASTs and passes them to Erg's type checker.
|
||||||
* [x] type narrowing
|
* [x] type narrowing
|
||||||
* [ ] others
|
* [ ] others
|
||||||
* [ ] `collections.abc`
|
* [ ] `collections.abc`
|
||||||
|
* [x] `Collection`
|
||||||
|
* [x] `Container`
|
||||||
|
* [x] `Generator`
|
||||||
* [x] `Iterable`
|
* [x] `Iterable`
|
||||||
* [x] `Iterator`
|
* [x] `Iterator`
|
||||||
* [x] `Mapping`
|
* [x] `Mapping`, `MutableMapping`
|
||||||
* [x] `Sequence`
|
* [x] `Sequence`, `MutableSequence`
|
||||||
* [ ] others
|
* [ ] others
|
||||||
* [x] type assertion (`typing.cast`)
|
* [x] type assertion (`typing.cast`)
|
||||||
* [x] type narrowing (`is`, `isinstance`)
|
* [x] type narrowing (`is`, `isinstance`)
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,10 @@ use erg_compiler::erg_parser::ast::{
|
||||||
KeyValue, KwArg, Lambda, LambdaSignature, List, ListComprehension, Literal, Methods, Module,
|
KeyValue, KwArg, Lambda, LambdaSignature, List, ListComprehension, Literal, Methods, Module,
|
||||||
NonDefaultParamSignature, NormalDict, NormalList, NormalRecord, NormalSet, NormalTuple,
|
NonDefaultParamSignature, NormalDict, NormalList, NormalRecord, NormalSet, NormalTuple,
|
||||||
ParamPattern, ParamTySpec, Params, PosArg, PreDeclTypeSpec, ReDef, Record, RecordAttrs, Set,
|
ParamPattern, ParamTySpec, Params, PosArg, PreDeclTypeSpec, ReDef, Record, RecordAttrs, Set,
|
||||||
SetComprehension, Signature, SubrSignature, SubrTypeSpec, Tuple, TupleTypeSpec, TypeAscription,
|
SetComprehension, Signature, SubrSignature, SubrTypeSpec, Tuple, TupleTypeSpec, TypeAppArgs,
|
||||||
TypeBoundSpec, TypeBoundSpecs, TypeSpec, TypeSpecWithOp, UnaryOp, VarName, VarPattern,
|
TypeAppArgsKind, TypeAscription, TypeBoundSpec, TypeBoundSpecs, TypeSpec, TypeSpecWithOp,
|
||||||
VarRecordAttr, VarRecordAttrs, VarRecordPattern, VarSignature, VisModifierSpec,
|
UnaryOp, VarName, VarPattern, VarRecordAttr, VarRecordAttrs, VarRecordPattern, VarSignature,
|
||||||
|
VisModifierSpec,
|
||||||
};
|
};
|
||||||
use erg_compiler::erg_parser::desugar::Desugarer;
|
use erg_compiler::erg_parser::desugar::Desugarer;
|
||||||
use erg_compiler::erg_parser::token::{Token, TokenKind, AS, COLON, DOT, EQUAL};
|
use erg_compiler::erg_parser::token::{Token, TokenKind, AS, COLON, DOT, EQUAL};
|
||||||
|
|
@ -991,43 +992,54 @@ impl ASTConverter {
|
||||||
Lambda::new(sig, op, Block::new(body), DefId(0))
|
Lambda::new(sig, op, Block::new(body), DefId(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_ident_type_spec(&mut self, name: String, loc: PyLocation) -> TypeSpec {
|
fn convert_ident_type_spec(&mut self, name: String, range: PySourceRange) -> TypeSpec {
|
||||||
|
let loc = pyloc_to_ergloc(range);
|
||||||
match &name[..] {
|
match &name[..] {
|
||||||
// Iterable[T] => Iterable(T), Iterable => Iterable(Obj)
|
// Iterable[T] => Iterable(T), Iterable => Iterable(Obj)
|
||||||
global_unary_collections!() => TypeSpec::poly(
|
global_unary_collections!() => TypeSpec::poly(
|
||||||
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("global".into())))
|
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private_with_loc(
|
||||||
.attr(Identifier::private(name.into())),
|
"global".into(),
|
||||||
|
loc,
|
||||||
|
)))
|
||||||
|
.attr(Identifier::private_with_loc(name.into(), loc)),
|
||||||
ConstArgs::single(ConstExpr::Accessor(ConstAccessor::Local(
|
ConstArgs::single(ConstExpr::Accessor(ConstAccessor::Local(
|
||||||
Identifier::private("Obj".into()),
|
Identifier::private_with_loc("Obj".into(), loc),
|
||||||
))),
|
))),
|
||||||
),
|
),
|
||||||
// MutableSequence[T] => Sequence!(T), MutableSequence => Sequence!(Obj)
|
// MutableSequence[T] => Sequence!(T), MutableSequence => Sequence!(Obj)
|
||||||
global_mutable_unary_collections!() => TypeSpec::poly(
|
global_mutable_unary_collections!() => TypeSpec::poly(
|
||||||
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("global".into())))
|
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private_with_loc(
|
||||||
.attr(Identifier::private(
|
"global".into(),
|
||||||
format!("{}!", name.trim_start_matches("Mutable")).into(),
|
loc,
|
||||||
)),
|
)))
|
||||||
|
.attr(Identifier::private_with_loc(
|
||||||
|
format!("{}!", name.trim_start_matches("Mutable")).into(),
|
||||||
|
loc,
|
||||||
|
)),
|
||||||
ConstArgs::single(ConstExpr::Accessor(ConstAccessor::Local(
|
ConstArgs::single(ConstExpr::Accessor(ConstAccessor::Local(
|
||||||
Identifier::private("Obj".into()),
|
Identifier::private_with_loc("Obj".into(), loc),
|
||||||
))),
|
))),
|
||||||
),
|
),
|
||||||
// Mapping => Mapping(Obj, Obj)
|
// Mapping => Mapping(Obj, Obj)
|
||||||
global_binary_collections!() => TypeSpec::poly(
|
global_binary_collections!() => TypeSpec::poly(
|
||||||
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("global".into())))
|
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private_with_loc(
|
||||||
.attr(Identifier::private(name.into())),
|
"global".into(),
|
||||||
|
loc,
|
||||||
|
)))
|
||||||
|
.attr(Identifier::private_with_loc(name.into(), loc)),
|
||||||
ConstArgs::pos_only(
|
ConstArgs::pos_only(
|
||||||
vec![
|
vec![
|
||||||
ConstPosArg::new(ConstExpr::Accessor(ConstAccessor::Local(
|
ConstPosArg::new(ConstExpr::Accessor(ConstAccessor::Local(
|
||||||
Identifier::private("Obj".into()),
|
Identifier::private_with_loc("Obj".into(), loc),
|
||||||
))),
|
))),
|
||||||
ConstPosArg::new(ConstExpr::Accessor(ConstAccessor::Local(
|
ConstPosArg::new(ConstExpr::Accessor(ConstAccessor::Local(
|
||||||
Identifier::private("Obj".into()),
|
Identifier::private_with_loc("Obj".into(), loc),
|
||||||
))),
|
))),
|
||||||
],
|
],
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_ => TypeSpec::mono(self.convert_ident(name, loc)),
|
_ => TypeSpec::mono(self.convert_ident(name, range.start)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1427,13 +1439,13 @@ impl ASTConverter {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.appeared_type_names
|
.appeared_type_names
|
||||||
.insert(name.id.to_string());
|
.insert(name.id.to_string());
|
||||||
self.convert_ident_type_spec(name.id.to_string(), name.location())
|
self.convert_ident_type_spec(name.id.to_string(), name.range)
|
||||||
}
|
}
|
||||||
py_ast::Expr::Constant(cons) => {
|
py_ast::Expr::Constant(cons) => {
|
||||||
if cons.value.is_none() {
|
if cons.value.is_none() {
|
||||||
self.convert_ident_type_spec("NoneType".into(), cons.location())
|
self.convert_ident_type_spec("NoneType".into(), cons.range)
|
||||||
} else if let Some(name) = cons.value.as_str() {
|
} else if let Some(name) = cons.value.as_str() {
|
||||||
self.convert_ident_type_spec(name.into(), cons.location())
|
self.convert_ident_type_spec(name.into(), cons.range)
|
||||||
} else {
|
} else {
|
||||||
let err = CompileError::syntax_error(
|
let err = CompileError::syntax_error(
|
||||||
self.cfg.input.clone(),
|
self.cfg.input.clone(),
|
||||||
|
|
@ -1458,8 +1470,7 @@ impl ASTConverter {
|
||||||
global_unary_collections!()
|
global_unary_collections!()
|
||||||
| global_mutable_unary_collections!()
|
| global_mutable_unary_collections!()
|
||||||
| global_binary_collections!() => {
|
| global_binary_collections!() => {
|
||||||
return self
|
return self.convert_ident_type_spec(attr.attr.to_string(), attr.range)
|
||||||
.convert_ident_type_spec(attr.attr.to_string(), attr.range.start)
|
|
||||||
}
|
}
|
||||||
"Any" => return TypeSpec::PreDeclTy(PreDeclTypeSpec::Mono(t)),
|
"Any" => return TypeSpec::PreDeclTy(PreDeclTypeSpec::Mono(t)),
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
@ -2218,9 +2229,19 @@ impl ASTConverter {
|
||||||
&mut self,
|
&mut self,
|
||||||
ident: Identifier,
|
ident: Identifier,
|
||||||
body: Vec<py_ast::Stmt>,
|
body: Vec<py_ast::Stmt>,
|
||||||
inherit: bool,
|
base: Option<py_ast::Expr>,
|
||||||
) -> (Option<Expr>, Vec<Methods>) {
|
) -> (Option<Expr>, Vec<Methods>) {
|
||||||
let class = TypeSpec::mono(ident.clone());
|
let inherit = base.is_some();
|
||||||
|
let class = if let Some(base) = base {
|
||||||
|
let base_spec = self.convert_type_spec(base.clone());
|
||||||
|
let expr = self.convert_expr(base);
|
||||||
|
let loc = expr.loc();
|
||||||
|
let base = TypeSpecWithOp::new(COLON, base_spec, expr);
|
||||||
|
let args = TypeAppArgs::new(loc, TypeAppArgsKind::SubtypeOf(Box::new(base)), loc);
|
||||||
|
TypeSpec::type_app(TypeSpec::mono(ident.clone()), args)
|
||||||
|
} else {
|
||||||
|
TypeSpec::mono(ident.clone())
|
||||||
|
};
|
||||||
let class_as_expr = Expr::Accessor(Accessor::Ident(ident));
|
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);
|
||||||
self.block_id_counter += 1;
|
self.block_id_counter += 1;
|
||||||
|
|
@ -2398,12 +2419,13 @@ impl ASTConverter {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|deco| self.convert_expr(deco))
|
.map(|deco| self.convert_expr(deco))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
let inherit = class_def.bases.first().cloned();
|
||||||
|
let is_inherit = inherit.is_some();
|
||||||
let mut bases = class_def
|
let mut bases = class_def
|
||||||
.bases
|
.bases
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|base| self.convert_expr(base))
|
.map(|base| self.convert_expr(base))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let inherit = !bases.is_empty();
|
|
||||||
self.register_name_info(&name, NameKind::Class);
|
self.register_name_info(&name, NameKind::Class);
|
||||||
let class_name_loc = PyLocation {
|
let class_name_loc = PyLocation {
|
||||||
row: loc.row,
|
row: loc.row,
|
||||||
|
|
@ -2413,7 +2435,7 @@ impl ASTConverter {
|
||||||
let sig = Signature::Var(VarSignature::new(VarPattern::Ident(ident.clone()), None));
|
let sig = Signature::Var(VarSignature::new(VarPattern::Ident(ident.clone()), None));
|
||||||
self.grow(ident.inspect().to_string(), BlockKind::Class);
|
self.grow(ident.inspect().to_string(), BlockKind::Class);
|
||||||
let (base_type, methods) = self.extract_method_list(ident, class_def.body, inherit);
|
let (base_type, methods) = self.extract_method_list(ident, class_def.body, inherit);
|
||||||
let classdef = if inherit {
|
let classdef = if is_inherit {
|
||||||
// TODO: multiple inheritance
|
// TODO: multiple inheritance
|
||||||
let pos_args = vec![PosArg::new(bases.remove(0))];
|
let pos_args = vec![PosArg::new(bases.remove(0))];
|
||||||
let mut args = Args::pos_only(pos_args, None);
|
let mut args = Args::pos_only(pos_args, None);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
class Vec(Sequence):
|
||||||
|
x: list[int]
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.x = []
|
||||||
|
|
||||||
|
def __getitem__(self, i: int) -> int:
|
||||||
|
return self.x[i]
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.x)
|
||||||
|
|
||||||
|
def __len__(self) -> int:
|
||||||
|
return len(self.x)
|
||||||
|
|
||||||
|
def __contains__(self, i: int) -> bool:
|
||||||
|
return i in self.x
|
||||||
|
|
@ -59,6 +59,11 @@ pub fn expect(file_path: &'static str, warns: usize, errors: usize) -> Result<()
|
||||||
exec_new_thread(move || _expect(file_path, warns, errors), file_path)
|
exec_new_thread(move || _expect(file_path, warns, errors), file_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exec_abc() -> Result<(), String> {
|
||||||
|
expect("tests/abc.py", 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn exec_test() -> Result<(), String> {
|
fn exec_test() -> Result<(), String> {
|
||||||
expect("tests/test.py", 0, 11)
|
expect("tests/test.py", 0, 11)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue