mirror of
https://github.com/mtshiba/pylyzer.git
synced 2025-08-04 14:28:24 +00:00
feat: ABC implementation check
This commit is contained in:
parent
233b7ee382
commit
2a98535d4c
6 changed files with 91 additions and 41 deletions
20
Cargo.lock
generated
20
Cargo.lock
generated
|
@ -150,9 +150,9 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
|||
|
||||
[[package]]
|
||||
name = "els"
|
||||
version = "0.1.61"
|
||||
version = "0.1.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be76108fd7329b2130fcc168b00fddf5b7ae44ef4fa91d2246a146d28304d7ac"
|
||||
checksum = "98d4bb4cbce0f519100ba0d6aa5541aa69aa00b0f61bdb862c81124f9cf38cea"
|
||||
dependencies = [
|
||||
"erg_common",
|
||||
"erg_compiler",
|
||||
|
@ -166,9 +166,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "erg_common"
|
||||
version = "0.6.49"
|
||||
version = "0.6.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6b12918165bc24a49d35897b6b4535360a0dec5b0641d9c1849fe0345875cc1"
|
||||
checksum = "15fcd8b1d8d47238d1488f7a05a8131b77b89adb54c867327b83db272a919344"
|
||||
dependencies = [
|
||||
"backtrace-on-stack-overflow",
|
||||
"erg_proc_macros",
|
||||
|
@ -179,9 +179,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "erg_compiler"
|
||||
version = "0.6.49"
|
||||
version = "0.6.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "212f2c20609d69579e8f0d860fcad6a6c0aa6434b84a92f24336da339725df52"
|
||||
checksum = "4a5b708d63c430435aac418e0822ef435b6b26c5338e5795130cce308f319505"
|
||||
dependencies = [
|
||||
"erg_common",
|
||||
"erg_parser",
|
||||
|
@ -189,9 +189,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "erg_parser"
|
||||
version = "0.6.49"
|
||||
version = "0.6.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ae50e2439a5922d664a9a43cd116b4c30eac39b25724ef5af25c719a30b8234"
|
||||
checksum = "6b79c7b5789c93deeeb21cbe9d4e0c62db60712a187a1c260210079d3750b4be"
|
||||
dependencies = [
|
||||
"erg_common",
|
||||
"erg_proc_macros",
|
||||
|
@ -200,9 +200,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "erg_proc_macros"
|
||||
version = "0.6.49"
|
||||
version = "0.6.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e2799a04d59d8dfd8f4d8a0a495354203d4efb5f48fe2f8effc521e80026714"
|
||||
checksum = "99659bb992c4e9da4af751d63fa126034025ffe79e07bfdbf013d9e165e768fc"
|
||||
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.49", features = ["py_compat", "els"] }
|
||||
erg_compiler = { version = "0.6.49", features = ["py_compat", "els"] }
|
||||
els = { version = "0.1.61", features = ["py_compat"] }
|
||||
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"] }
|
||||
# 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"] }
|
||||
|
|
|
@ -123,6 +123,7 @@ pylyzer converts Python ASTs to Erg ASTs and passes them to Erg's type checker.
|
|||
* [x] function/method
|
||||
* [x] class
|
||||
* [ ] `async/await`
|
||||
* [ ] user-defined abstract class
|
||||
* [x] type inference
|
||||
* [x] variable
|
||||
* [x] operator
|
||||
|
@ -165,10 +166,13 @@ pylyzer converts Python ASTs to Erg ASTs and passes them to Erg's type checker.
|
|||
* [x] type narrowing
|
||||
* [ ] others
|
||||
* [ ] `collections.abc`
|
||||
* [x] `Collection`
|
||||
* [x] `Container`
|
||||
* [x] `Generator`
|
||||
* [x] `Iterable`
|
||||
* [x] `Iterator`
|
||||
* [x] `Mapping`
|
||||
* [x] `Sequence`
|
||||
* [x] `Mapping`, `MutableMapping`
|
||||
* [x] `Sequence`, `MutableSequence`
|
||||
* [ ] others
|
||||
* [x] type assertion (`typing.cast`)
|
||||
* [x] type narrowing (`is`, `isinstance`)
|
||||
|
|
|
@ -17,9 +17,10 @@ use erg_compiler::erg_parser::ast::{
|
|||
KeyValue, KwArg, Lambda, LambdaSignature, List, ListComprehension, Literal, Methods, Module,
|
||||
NonDefaultParamSignature, NormalDict, NormalList, NormalRecord, NormalSet, NormalTuple,
|
||||
ParamPattern, ParamTySpec, Params, PosArg, PreDeclTypeSpec, ReDef, Record, RecordAttrs, Set,
|
||||
SetComprehension, Signature, SubrSignature, SubrTypeSpec, Tuple, TupleTypeSpec, TypeAscription,
|
||||
TypeBoundSpec, TypeBoundSpecs, TypeSpec, TypeSpecWithOp, UnaryOp, VarName, VarPattern,
|
||||
VarRecordAttr, VarRecordAttrs, VarRecordPattern, VarSignature, VisModifierSpec,
|
||||
SetComprehension, Signature, SubrSignature, SubrTypeSpec, Tuple, TupleTypeSpec, TypeAppArgs,
|
||||
TypeAppArgsKind, TypeAscription, TypeBoundSpec, TypeBoundSpecs, TypeSpec, TypeSpecWithOp,
|
||||
UnaryOp, VarName, VarPattern, VarRecordAttr, VarRecordAttrs, VarRecordPattern, VarSignature,
|
||||
VisModifierSpec,
|
||||
};
|
||||
use erg_compiler::erg_parser::desugar::Desugarer;
|
||||
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))
|
||||
}
|
||||
|
||||
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[..] {
|
||||
// Iterable[T] => Iterable(T), Iterable => Iterable(Obj)
|
||||
global_unary_collections!() => TypeSpec::poly(
|
||||
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("global".into())))
|
||||
.attr(Identifier::private(name.into())),
|
||||
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private_with_loc(
|
||||
"global".into(),
|
||||
loc,
|
||||
)))
|
||||
.attr(Identifier::private_with_loc(name.into(), loc)),
|
||||
ConstArgs::single(ConstExpr::Accessor(ConstAccessor::Local(
|
||||
Identifier::private("Obj".into()),
|
||||
Identifier::private_with_loc("Obj".into(), loc),
|
||||
))),
|
||||
),
|
||||
// MutableSequence[T] => Sequence!(T), MutableSequence => Sequence!(Obj)
|
||||
global_mutable_unary_collections!() => TypeSpec::poly(
|
||||
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("global".into())))
|
||||
.attr(Identifier::private(
|
||||
format!("{}!", name.trim_start_matches("Mutable")).into(),
|
||||
)),
|
||||
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private_with_loc(
|
||||
"global".into(),
|
||||
loc,
|
||||
)))
|
||||
.attr(Identifier::private_with_loc(
|
||||
format!("{}!", name.trim_start_matches("Mutable")).into(),
|
||||
loc,
|
||||
)),
|
||||
ConstArgs::single(ConstExpr::Accessor(ConstAccessor::Local(
|
||||
Identifier::private("Obj".into()),
|
||||
Identifier::private_with_loc("Obj".into(), loc),
|
||||
))),
|
||||
),
|
||||
// Mapping => Mapping(Obj, Obj)
|
||||
global_binary_collections!() => TypeSpec::poly(
|
||||
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private("global".into())))
|
||||
.attr(Identifier::private(name.into())),
|
||||
ConstExpr::Accessor(ConstAccessor::Local(Identifier::private_with_loc(
|
||||
"global".into(),
|
||||
loc,
|
||||
)))
|
||||
.attr(Identifier::private_with_loc(name.into(), loc)),
|
||||
ConstArgs::pos_only(
|
||||
vec![
|
||||
ConstPosArg::new(ConstExpr::Accessor(ConstAccessor::Local(
|
||||
Identifier::private("Obj".into()),
|
||||
Identifier::private_with_loc("Obj".into(), loc),
|
||||
))),
|
||||
ConstPosArg::new(ConstExpr::Accessor(ConstAccessor::Local(
|
||||
Identifier::private("Obj".into()),
|
||||
Identifier::private_with_loc("Obj".into(), loc),
|
||||
))),
|
||||
],
|
||||
None,
|
||||
),
|
||||
),
|
||||
_ => TypeSpec::mono(self.convert_ident(name, loc)),
|
||||
_ => TypeSpec::mono(self.convert_ident(name, range.start)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1427,13 +1439,13 @@ impl ASTConverter {
|
|||
.unwrap()
|
||||
.appeared_type_names
|
||||
.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) => {
|
||||
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() {
|
||||
self.convert_ident_type_spec(name.into(), cons.location())
|
||||
self.convert_ident_type_spec(name.into(), cons.range)
|
||||
} else {
|
||||
let err = CompileError::syntax_error(
|
||||
self.cfg.input.clone(),
|
||||
|
@ -1458,8 +1470,7 @@ impl ASTConverter {
|
|||
global_unary_collections!()
|
||||
| global_mutable_unary_collections!()
|
||||
| global_binary_collections!() => {
|
||||
return self
|
||||
.convert_ident_type_spec(attr.attr.to_string(), attr.range.start)
|
||||
return self.convert_ident_type_spec(attr.attr.to_string(), attr.range)
|
||||
}
|
||||
"Any" => return TypeSpec::PreDeclTy(PreDeclTypeSpec::Mono(t)),
|
||||
_ => {}
|
||||
|
@ -2218,9 +2229,19 @@ impl ASTConverter {
|
|||
&mut self,
|
||||
ident: Identifier,
|
||||
body: Vec<py_ast::Stmt>,
|
||||
inherit: bool,
|
||||
base: Option<py_ast::Expr>,
|
||||
) -> (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 (base_type, attrs) = self.extract_method(body, inherit);
|
||||
self.block_id_counter += 1;
|
||||
|
@ -2398,12 +2419,13 @@ impl ASTConverter {
|
|||
.into_iter()
|
||||
.map(|deco| self.convert_expr(deco))
|
||||
.collect::<Vec<_>>();
|
||||
let inherit = class_def.bases.first().cloned();
|
||||
let is_inherit = inherit.is_some();
|
||||
let mut bases = class_def
|
||||
.bases
|
||||
.into_iter()
|
||||
.map(|base| self.convert_expr(base))
|
||||
.collect::<Vec<_>>();
|
||||
let inherit = !bases.is_empty();
|
||||
self.register_name_info(&name, NameKind::Class);
|
||||
let class_name_loc = PyLocation {
|
||||
row: loc.row,
|
||||
|
@ -2413,7 +2435,7 @@ impl ASTConverter {
|
|||
let sig = Signature::Var(VarSignature::new(VarPattern::Ident(ident.clone()), None));
|
||||
self.grow(ident.inspect().to_string(), BlockKind::Class);
|
||||
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
|
||||
let pos_args = vec![PosArg::new(bases.remove(0))];
|
||||
let mut args = Args::pos_only(pos_args, None);
|
||||
|
|
19
tests/abc.py
Normal file
19
tests/abc.py
Normal file
|
@ -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)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_abc() -> Result<(), String> {
|
||||
expect("tests/abc.py", 0, 0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_test() -> Result<(), String> {
|
||||
expect("tests/test.py", 0, 11)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue