fix: `List` type bug

This commit is contained in:
Shunsuke Shibayama 2025-02-21 21:22:13 +09:00
parent fd4b5895f3
commit 5fb0c71a63
6 changed files with 87 additions and 20 deletions

20
Cargo.lock generated
View File

@ -152,9 +152,9 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]] [[package]]
name = "els" name = "els"
version = "0.1.65-nightly.0" version = "0.1.65-nightly.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4e47271869da4eb87b3fe853c99c1c36faf5a2aa42e59df37d4d03683dfa47d" checksum = "93c98062a1591263c7bdb7444ffae5eb7151fb3fc8f09eddeba13163e86706cf"
dependencies = [ dependencies = [
"erg_common", "erg_common",
"erg_compiler", "erg_compiler",
@ -168,9 +168,9 @@ dependencies = [
[[package]] [[package]]
name = "erg_common" name = "erg_common"
version = "0.6.53-nightly.0" version = "0.6.53-nightly.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80f2bf84c599bbfa0186d90cf0e6894cc3d0c05b33b33bebddf4bae9082a386f" checksum = "a6d0fdaad7b1913c304d070a5e4737e7495e2eadca1a67fddca77649995c4d52"
dependencies = [ dependencies = [
"backtrace-on-stack-overflow", "backtrace-on-stack-overflow",
"erg_proc_macros", "erg_proc_macros",
@ -181,9 +181,9 @@ dependencies = [
[[package]] [[package]]
name = "erg_compiler" name = "erg_compiler"
version = "0.6.53-nightly.0" version = "0.6.53-nightly.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59e0f2f1c2e7369bdd9f80dc4c3d3452eedb7f8e7911eae60ab6784449592234" checksum = "26ebe561fa98ff75a6a99167e86826f01baca96e0a696f3fcb4af86101f9c309"
dependencies = [ dependencies = [
"erg_common", "erg_common",
"erg_parser", "erg_parser",
@ -191,9 +191,9 @@ dependencies = [
[[package]] [[package]]
name = "erg_parser" name = "erg_parser"
version = "0.6.53-nightly.0" version = "0.6.53-nightly.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28892f334a901d746c9aeb4e70c20661387b1edcdf126c9f3e3c34d6405428ab" checksum = "d4c06268f345b640bcb6b3f0bf2390b9da072b05b72c697f51502e47e11db667"
dependencies = [ dependencies = [
"erg_common", "erg_common",
"erg_proc_macros", "erg_proc_macros",
@ -202,9 +202,9 @@ dependencies = [
[[package]] [[package]]
name = "erg_proc_macros" name = "erg_proc_macros"
version = "0.6.53-nightly.0" version = "0.6.53-nightly.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c22150bc5756a07cf110e7b66fcd3f7f6fdea8a3b9146b1a2a227e3ede6eb0c" checksum = "2b6f03ae04101a0079ac52948edd33a08b3349d78a5cf632bddc22f020bd52e1"
dependencies = [ dependencies = [
"quote", "quote",
"syn 1.0.109", "syn 1.0.109",

View File

@ -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.53-nightly.0", features = ["py_compat", "els"] } erg_common = { version = "0.6.53-nightly.1", features = ["py_compat", "els"] }
erg_compiler = { version = "0.6.53-nightly.0", features = ["py_compat", "els"] } erg_compiler = { version = "0.6.53-nightly.1", features = ["py_compat", "els"] }
els = { version = "0.1.65-nightly.0", features = ["py_compat"] } els = { version = "0.1.65-nightly.1", 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"] }

View File

@ -144,12 +144,26 @@ fn escape_name(name: String) -> String {
"tuple" => "GenericTuple".into(), "tuple" => "GenericTuple".into(),
"type" => "Type".into(), "type" => "Type".into(),
"ModuleType" => "GeneticModule".into(), "ModuleType" => "GeneticModule".into(),
"MutableSequence" => "Sequence!".into(),
"MutableMapping" => "Mapping!".into(),
_ => name, _ => name,
} }
} }
fn is_global_poly_type(name: &str) -> bool {
matches!(
name,
"list"
| "dict"
| "set"
| "tuple"
| "List"
| "Dict"
| "Tuple"
| global_unary_collections!()
| global_mutable_unary_collections!()
| global_binary_collections!()
)
}
fn quoted_symbol(sym: &str, lineno: u32, col_begin: u32) -> Token { fn quoted_symbol(sym: &str, lineno: u32, col_begin: u32) -> Token {
let col_end = col_begin + sym.chars().count() as u32; let col_end = col_begin + sym.chars().count() as u32;
Token { Token {
@ -241,6 +255,7 @@ impl DefinedPlace {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct NameInfo { pub struct NameInfo {
kind: NameKind,
rename: Option<String>, rename: Option<String>,
defined_in: DefinedPlace, defined_in: DefinedPlace,
defined_block_id: usize, defined_block_id: usize,
@ -250,12 +265,14 @@ pub struct NameInfo {
impl NameInfo { impl NameInfo {
pub fn new( pub fn new(
kind: NameKind,
rename: Option<String>, rename: Option<String>,
defined_in: DefinedPlace, defined_in: DefinedPlace,
defined_block_id: usize, defined_block_id: usize,
defined_times: usize, defined_times: usize,
) -> Self { ) -> Self {
Self { Self {
kind,
rename, rename,
defined_in, defined_in,
defined_block_id, defined_block_id,
@ -636,6 +653,15 @@ impl ASTConverter {
None None
} }
fn is_type_name(&self, name: &str) -> bool {
is_global_poly_type(name)
|| self
.contexts
.iter()
.rev()
.any(|ctx| ctx.names.get(name).is_some_and(|ni| ni.kind.is_class()))
}
fn define_name(&mut self, name: String, info: NameInfo) { fn define_name(&mut self, name: String, info: NameInfo) {
self.contexts.last_mut().unwrap().names.insert(name, info); self.contexts.last_mut().unwrap().names.insert(name, info);
} }
@ -732,7 +758,7 @@ impl ASTConverter {
None None
}; };
let defined_in = DefinedPlace::Known(self.cur_namespace()); let defined_in = DefinedPlace::Known(self.cur_namespace());
let info = NameInfo::new(rename, defined_in, self.cur_block_id(), 1); let info = NameInfo::new(kind, rename, defined_in, self.cur_block_id(), 1);
self.define_name(String::from(name), info); self.define_name(String::from(name), info);
RenameKind::Let RenameKind::Let
} }
@ -773,7 +799,7 @@ impl ASTConverter {
} else { } else {
DefinedPlace::Unknown DefinedPlace::Unknown
}; };
let mut info = NameInfo::new(None, defined_in, cur_block_id, 0); let mut info = NameInfo::new(NameKind::Variable, None, defined_in, cur_block_id, 0);
info.add_referrer(cur_namespace); info.add_referrer(cur_namespace);
self.declare_name(name.clone(), info); self.declare_name(name.clone(), info);
name name
@ -1527,7 +1553,6 @@ impl ASTConverter {
} }
fn convert_type_spec(&mut self, expr: py_ast::Expr) -> TypeSpec { fn convert_type_spec(&mut self, expr: py_ast::Expr) -> TypeSpec {
#[allow(clippy::collapsible_match)]
match expr { match expr {
py_ast::Expr::Name(name) => { py_ast::Expr::Name(name) => {
self.contexts self.contexts
@ -2000,6 +2025,19 @@ impl ASTConverter {
} }
py_ast::Expr::Subscript(subs) => { py_ast::Expr::Subscript(subs) => {
let obj = self.convert_expr(*subs.value); let obj = self.convert_expr(*subs.value);
// List[T] => List!(T)
if obj.get_name().is_some_and(|n| self.is_type_name(n)) {
let obj = if obj.get_name().is_some_and(|n| n == "List") {
let global = self.convert_ident("global".to_string(), subs.range.start);
Expr::from(global).attr_expr(Identifier::private("List!".into()))
} else if obj.get_name().is_some_and(|n| n == "Set") {
let global = self.convert_ident("global".to_string(), subs.range.start);
Expr::from(global).attr_expr(Identifier::private("Set!".into()))
} else {
obj
};
return obj.call1(self.convert_expr(*subs.slice));
}
let method = obj.attr_expr( let method = obj.attr_expr(
self.convert_ident("__getitem__".to_string(), subs.slice.location()), self.convert_ident("__getitem__".to_string(), subs.slice.location()),
); );

View File

@ -1,4 +1,4 @@
from typing import Self from typing import Self, List
class Empty: pass class Empty: pass
emp = Empty() emp = Empty()
@ -105,3 +105,18 @@ class Implicit:
def set_foo(self): def set_foo(self):
self.foo = True self.foo = True
class Cs:
cs: list[C]
cs2: List[C]
cs_list: list[list[C]]
def __init__(self, cs: list[C]):
self.cs = cs
self.cs2 = cs
self.cs_list = []
def add(self, c: C):
self.cs.append(c)
self.cs2.append(c)
self.cs_list.append([c])

View File

@ -2,3 +2,17 @@ class Foo:
def invalid_append(self): def invalid_append(self):
paths: list[str] = [] paths: list[str] = []
paths.append(self) # ERR paths.append(self) # ERR
class Bar:
foos: list[Foo]
def __init__(self, foos: list[Foo]) -> None:
self.foos = foos
def add_foo(self, foo: Foo):
self.foos.append(foo)
def invalid_add_foo(self):
self.foos.append(1) # ERR
_ = Bar([Bar([])]) # ERR

View File

@ -105,7 +105,7 @@ fn exec_class() -> Result<(), String> {
#[test] #[test]
fn exec_class_err() -> Result<(), String> { fn exec_class_err() -> Result<(), String> {
expect("tests/err/class.py", 0, 1) expect("tests/err/class.py", 0, 3)
} }
#[test] #[test]