From e5893e0a65af9fff5f46fdbc6f2b2867caeb9eb3 Mon Sep 17 00:00:00 2001 From: abdul124 Date: Thu, 29 Aug 2024 17:38:24 +0800 Subject: [PATCH] WIP --- flake.nix | 2 + nac3core/src/symbol_resolver.rs | 7 +- nac3core/src/toplevel/composer.rs | 247 ++++++++++- ..._test_analyze__list_tuple_generic.snap.new | 15 + ...plevel__test__test_analyze__self1.snap.new | 15 + ...est_analyze__simple_pass_in_class.snap.new | 9 + nac3core/src/toplevel/test.rs | 389 ++++++------------ nac3core/src/toplevel/type_annotation.rs | 14 +- nac3standalone/demo/src/_test.py | 27 ++ 9 files changed, 443 insertions(+), 282 deletions(-) create mode 100644 nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__list_tuple_generic.snap.new create mode 100644 nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__self1.snap.new create mode 100644 nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__simple_pass_in_class.snap.new create mode 100644 nac3standalone/demo/src/_test.py diff --git a/flake.nix b/flake.nix index 07cea776..7987b259 100644 --- a/flake.nix +++ b/flake.nix @@ -180,7 +180,9 @@ clippy pre-commit rustfmt + rust-analyzer ]; + RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; shellHook = '' export DEMO_LINALG_STUB=${packages.x86_64-linux.demo-linalg-stub}/lib/liblinalg.a diff --git a/nac3core/src/symbol_resolver.rs b/nac3core/src/symbol_resolver.rs index 9d7084b9..5bc84411 100644 --- a/nac3core/src/symbol_resolver.rs +++ b/nac3core/src/symbol_resolver.rs @@ -439,7 +439,12 @@ pub fn parse_type_annotation( } else { let obj_id = resolver.get_identifier_def(*id); if let Ok(obj_id) = obj_id { - let def = top_level_defs[obj_id.0].read(); + let Some(top_level_def) = top_level_defs.get(obj_id.0) else { + return Err(HashSet::from([format!( + "NameError: '{id}' not found at {}", expr.location + )])); + }; + let def = top_level_def.read(); if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def { if !type_vars.is_empty() { return Err(HashSet::from([format!( diff --git a/nac3core/src/toplevel/composer.rs b/nac3core/src/toplevel/composer.rs index 603a508e..92e5653c 100644 --- a/nac3core/src/toplevel/composer.rs +++ b/nac3core/src/toplevel/composer.rs @@ -52,6 +52,10 @@ pub type BuiltinFuncSpec = (StrRef, FunSignature, Arc); /// [`Unifier`]. pub type BuiltinFuncCreator = dyn Fn(&PrimitiveStore, &mut Unifier) -> BuiltinFuncSpec; +fn report_error(msg: &str, location: Location) -> Result> { + Err(HashSet::from([format!("{msg} at {location}")])) +} + impl TopLevelComposer { /// return a composer and things to make a "primitive" symbol resolver, so that the symbol /// resolver can later figure out primitive tye definitions when passed a primitive type name @@ -389,8 +393,21 @@ impl TopLevelComposer { } pub fn start_analysis(&mut self, inference: bool) -> Result<(), HashSet> { - self.analyze_top_level_class_type_var()?; - self.analyze_top_level_class_bases()?; + let unifier = self.unifier.borrow_mut(); + let primitives_store = &self.primitives_ty; + let def_list = &self.definition_ast_list; + // let class_def = def_list.iter().filter(|f| matches!(&*f.0.read(), TopLevelDef::Class { .. })).collect_vec(); + + + Self::analyze_top_level_class_bases( + def_list, + unifier, + primitives_store, + self.builtin_num, + )?; + + // self.analyze_top_level_class_type_var()?; + self.analyze_top_level_class_bases2()?; self.analyze_top_level_class_fields_methods()?; self.analyze_top_level_function()?; if inference { @@ -398,6 +415,206 @@ impl TopLevelComposer { } Ok(()) } + + fn analyze_bases( + class_def: &Arc>, + class_ast: &Option, + temp_def_list:&[Arc>], + unifier: &mut Unifier, + primitives_store: &PrimitiveStore, + ) -> Result<(), HashSet> { + let mut class_def = class_def.write(); + let (class_def_id, class_ancestors, class_bases_ast, class_type_vars, class_resolver) = { + let TopLevelDef::Class { object_id, ancestors, type_vars, resolver, .. } = + &mut *class_def + else { + unreachable!() + }; + let Some(ast::Located { node: ast::StmtKind::ClassDef { bases, .. }, .. }) = class_ast + else { + unreachable!() + }; + (object_id, ancestors, bases, type_vars, resolver.as_ref().unwrap().as_ref()) + }; + + let mut is_generic = false; + let mut has_base = false; + // Check class bases for typevars + for b in class_bases_ast { + match &b.node { + // analyze typevars bounded to the class, + // only support things like `class A(Generic[T, V])`, + // things like `class A(Generic[T, V, ImportedModule.T])` is not supported + // i.e. only simple names are allowed in the subscript + // should update the TopLevelDef::Class.typevars and the TypeEnum::TObj.params + ast::ExprKind::Subscript { value, slice, .. } if matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"Generic".into()) => + { + if is_generic { + return report_error("only single Generic[...] is allowed", b.location); + } + is_generic = true; + + let type_var_list: Vec<&ast::Expr<()>>; + // if `class A(Generic[T, V, G])` + if let ast::ExprKind::Tuple { elts, .. } = &slice.node { + type_var_list = elts.iter().collect_vec(); + // `class A(Generic[T])` + } else { + type_var_list = vec![&**slice]; + } + + let type_vars = type_var_list + .into_iter() + .map(|e| { + class_resolver.parse_type_annotation( + &temp_def_list, + unifier, + primitives_store, + e, + ) + }) + .collect::, _>>()?; + + class_type_vars.extend(type_vars); + } + ast::ExprKind::Name { .. } | ast::ExprKind::Subscript { .. } => { + if has_base { + return report_error("a class definition can only have at most one base class declaration and one generic declaration", b.location); + } + has_base = true; + // the function parse_ast_to make sure that no type var occurred in + // bast_ty if it is a CustomClassKind + let base_ty = parse_ast_to_type_annotation_kinds( + class_resolver, + temp_def_list, + unifier, + primitives_store, + b, + vec![(*class_def_id, class_type_vars.clone())] + .into_iter() + .collect::>(), + )?; + if let TypeAnnotation::CustomClass { .. } = &base_ty { + class_ancestors.push(base_ty); + } else { + return report_error( + "class base declaration can only be custom class", + b.location, + ); + } + } + // TODO: Report Error here + _ => { + println!("Type was => {}", b.node.name()); + } + } + } + + Ok(()) + } + + fn analyze_ancestors( + class_def: &Arc>, + temp_def_list: &[Arc>], + unifier: &mut Unifier, + ) { + // Check if class has a direct parent + { + let mut class_def = class_def.write(); + let TopLevelDef::Class { ancestors, type_vars, object_id, .. } = &mut *class_def else { + unreachable!() + }; + println!("Type Vars: {:?}", type_vars); + let mut anc_set = HashMap::new(); + + + if let Some(ancestor) = ancestors.first() { + let TypeAnnotation::CustomClass { id, .. } = ancestor else { unreachable!() }; + let TopLevelDef::Class { ancestors: parent_ancestors, .. } = + &*temp_def_list[id.0].read() + else { + unreachable!() + }; + for anc in parent_ancestors.iter().skip(1) { + let TypeAnnotation::CustomClass { id, .. } = anc else { unreachable!() }; + anc_set.insert(id, anc.clone()); + } + ancestors.extend(anc_set.into_iter().map(|f| f.1).collect::>()); + + } + ancestors.insert(0, make_self_type_annotation(type_vars.as_slice(), *object_id)); + + } + let class_def = class_def.read(); + println!("{:?}", class_def.to_string(unifier)); + + } + + fn analyze_top_level_class_bases( + def_list: &[DefAst], + unifier: &mut Unifier, + primitives_store: &PrimitiveStore, + builtin_num: usize, + ) -> Result<(), HashSet> { + let mut errors = HashSet::new(); + let mut temp_def_list: Vec>> = def_list.iter().take(builtin_num).map(|f| f.0.clone()).collect_vec(); + + // Analyze class_bases + for (class_def, class_ast) in def_list.iter().skip(builtin_num) { + if class_ast.is_some() && matches!(&*class_def.read(), TopLevelDef::Class { .. }) { + println!("Class Bases Analysis start"); + if let Err(e) = Self::analyze_bases( + class_def, + class_ast, + &temp_def_list, + unifier, + primitives_store, + ) { + errors.extend(e); + } + // Self::analyze_ancestors(class_def, &temp_def_list, unifier); + + // special case classes that inherit from Exception + let TopLevelDef::Class { ancestors: class_ancestors, loc, .. } = &*class_def.read() + else { + unreachable!() + }; + if class_ancestors + .iter() + .any(|ann| matches!(ann, TypeAnnotation::CustomClass { id, .. } if id.0 == 7)) + { + // if inherited from Exception, the body should be a pass + let ast::StmtKind::ClassDef { body, .. } = &class_ast.as_ref().unwrap().node + else { + unreachable!() + }; + for stmt in body { + if matches!( + stmt.node, + ast::StmtKind::FunctionDef { .. } | ast::StmtKind::AnnAssign { .. } + ) { + errors.extend(report_error("Classes inherited from exception should have no custom fields/methods", loc.unwrap())); + } + } + } + + } + temp_def_list.push(class_def.clone()); + } + println!("Bases Analyzed"); + if !errors.is_empty() { + return Err(errors) + } + println!("Startgin Ancestor Analysis"); + for (class_def, class_ast) in def_list.iter().skip(builtin_num) { + if class_ast.is_some() && matches!(&*class_def.read(), TopLevelDef::Class { .. }) { + // Self::analyze_ancestors(class_def, &temp_def_list); + } + } + println!("Ancestors added"); + + Ok(()) + } /// step 1, analyze the type vars associated with top level class fn analyze_top_level_class_type_var(&mut self) -> Result<(), HashSet> { @@ -509,6 +726,13 @@ impl TopLevelComposer { errors.extend(e); } } + for (def, _) in def_list.iter().skip(self.builtin_num){ + let def = &*def.read(); + println!("{}", format!("{}\n", def.to_string(unifier.borrow_mut()))) + } + + + if !errors.is_empty() { return Err(errors); } @@ -518,7 +742,7 @@ impl TopLevelComposer { /// step 2, base classes. /// now that the type vars of all classes are done, handle base classes and /// put Self class into the ancestors list. We only allow single inheritance - fn analyze_top_level_class_bases(&mut self) -> Result<(), HashSet> { + fn analyze_top_level_class_bases2(&mut self) -> Result<(), HashSet> { if self.unifier.top_level.is_none() { let ctx = Arc::new(self.make_top_level_context()); self.unifier.top_level = Some(ctx); @@ -605,9 +829,9 @@ impl TopLevelComposer { if class_ast.is_none() { continue; } - if let Err(e) = get_direct_parents(class_def, class_ast) { - errors.extend(e); - } + // if let Err(e) = get_direct_parents(class_def, class_ast) { + // errors.extend(e); + // } } if !errors.is_empty() { return Err(errors); @@ -657,6 +881,7 @@ impl TopLevelComposer { continue; } let mut class_def = class_def.write(); + println!("1) {}", class_def.to_string(unifier)); let (class_ancestors, class_id, class_type_vars) = { if let TopLevelDef::Class { ancestors, object_id, type_vars, .. } = &mut *class_def { @@ -669,6 +894,8 @@ impl TopLevelComposer { let ans = ancestors_store.get_mut(&class_id).unwrap(); class_ancestors.append(ans); + // println!("1) {}", class_def.to_string(unifier)); + // insert self type annotation to the front of the vector to maintain the order class_ancestors .insert(0, make_self_type_annotation(class_type_vars.as_slice(), class_id)); @@ -697,6 +924,14 @@ impl TopLevelComposer { } } + println!("\n\nSecond Phase:\n"); + for (def, _) in self.definition_ast_list.iter().skip(self.builtin_num){ + if matches!(&*def.read(), TopLevelDef::Class{..}) { + let class_def = def.read(); + println!("{:?}", class_def.to_string(unifier)); + } + } + // deal with ancestor of Exception object let TopLevelDef::Class { name, ancestors, object_id, .. } = &mut *self.definition_ast_list[7].0.write() diff --git a/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__list_tuple_generic.snap.new b/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__list_tuple_generic.snap.new new file mode 100644 index 00000000..60cea96a --- /dev/null +++ b/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__list_tuple_generic.snap.new @@ -0,0 +1,15 @@ +--- +source: nac3core/src/toplevel/test.rs +assertion_line: 576 +expression: res_vec + +--- +[ + "Function {\nname: \"foo\",\nsig: \"fn[[a:11[0], b:tuple[T, 2]], 105[109, 3]]\",\nvar_id: []\n}\n", + "Class {\nname: \"A\",\nancestors: [\"class_def_105[T, V]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[v:V], 4]\"), (\"fun\", \"fn[[a:T], V]\")],\ntype_vars: [\"T\", \"V\"]\n}\n", + "Function {\nname: \"A.__init__\",\nsig: \"fn[[v:V], 4]\",\nvar_id: [TypeVarId(243)]\n}\n", + "Function {\nname: \"A.fun\",\nsig: \"fn[[a:T], V]\",\nvar_id: [TypeVarId(248)]\n}\n", + "Function {\nname: \"gfun\",\nsig: \"fn[[a:105[11[2], 0]], 4]\",\nvar_id: []\n}\n", + "Class {\nname: \"B\",\nancestors: [\"class_def_109\"],\nfields: [],\nmethods: [(\"__init__\", \"fn[[], 4]\")],\ntype_vars: []\n}\n", + "Function {\nname: \"B.__init__\",\nsig: \"fn[[], 4]\",\nvar_id: []\n}\n", +] diff --git a/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__self1.snap.new b/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__self1.snap.new new file mode 100644 index 00000000..0da9920f --- /dev/null +++ b/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__self1.snap.new @@ -0,0 +1,15 @@ +--- +source: nac3core/src/toplevel/test.rs +assertion_line: 576 +expression: res_vec + +--- +[ + "Class {\nname: \"A\",\nancestors: [\"class_def_104[typevar229, typevar230]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[a:104[2, 3], b:107], 4]\"), (\"fun\", \"fn[[a:104[2, 3]], 104[3, 0]]\")],\ntype_vars: [\"typevar229\", \"typevar230\"]\n}\n", + "Function {\nname: \"A.__init__\",\nsig: \"fn[[a:104[2, 3], b:107], 4]\",\nvar_id: []\n}\n", + "Function {\nname: \"A.fun\",\nsig: \"fn[[a:104[2, 3]], 104[3, 0]]\",\nvar_id: []\n}\n", + "Class {\nname: \"B\",\nancestors: [\"class_def_107\", \"class_def_104[1, 3]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[], 4]\"), (\"fun\", \"fn[[a:104[2, 3]], 104[3, 0]]\"), (\"foo\", \"fn[[b:107], 107]\"), (\"bar\", \"fn[[a:104[11[107], 0]], tuple[104[virtual[104[107, 0]], 3], 107]]\")],\ntype_vars: []\n}\n", + "Function {\nname: \"B.__init__\",\nsig: \"fn[[], 4]\",\nvar_id: []\n}\n", + "Function {\nname: \"B.foo\",\nsig: \"fn[[b:107], 107]\",\nvar_id: []\n}\n", + "Function {\nname: \"B.bar\",\nsig: \"fn[[a:104[11[107], 0]], tuple[104[virtual[104[107, 0]], 3], 107]]\",\nvar_id: []\n}\n", +] diff --git a/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__simple_pass_in_class.snap.new b/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__simple_pass_in_class.snap.new new file mode 100644 index 00000000..4071300c --- /dev/null +++ b/nac3core/src/toplevel/snapshots/nac3core__toplevel__test__test_analyze__simple_pass_in_class.snap.new @@ -0,0 +1,9 @@ +--- +source: nac3core/src/toplevel/test.rs +assertion_line: 576 +expression: res_vec + +--- +[ + "Class {\nname: \"A\",\nancestors: [\"class_def_104\"],\nfields: [],\nmethods: [],\ntype_vars: []\n}\n", +] diff --git a/nac3core/src/toplevel/test.rs b/nac3core/src/toplevel/test.rs index 522cb23d..b403cab5 100644 --- a/nac3core/src/toplevel/test.rs +++ b/nac3core/src/toplevel/test.rs @@ -214,119 +214,119 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) { } } -#[test_case( - &[ - indoc! {" - class A(): - a: int32 - def __init__(self): - self.a = 3 - def fun(self, b: B): - pass - def foo(self, a: T, b: V): - pass - "}, - indoc! {" - class B(C): - def __init__(self): - pass - "}, - indoc! {" - class C(A): - def __init__(self): - pass - def fun(self, b: B): - a = 1 - pass - "}, - indoc! {" - def foo(a: A): - pass - "}, - indoc! {" - def ff(a: T) -> V: - pass - "} - ], - &[]; - "simple class compose" -)] -#[test_case( - &[ - indoc! {" - class Generic_A(Generic[V], B): - a: int64 - def __init__(self): - self.a = 123123123123 - def fun(self, a: int32) -> V: - pass - "}, - indoc! {" - class B: - aa: bool - def __init__(self): - self.aa = False - def foo(self, b: T): - pass - "} - ], - &[]; - "generic class" -)] -#[test_case( - &[ - indoc! {" - def foo(a: list[int32], b: tuple[T, float]) -> A[B, bool]: - pass - "}, - indoc! {" - class A(Generic[T, V]): - a: T - b: V - def __init__(self, v: V): - self.a = 1 - self.b = v - def fun(self, a: T) -> V: - pass - "}, - indoc! {" - def gfun(a: A[list[float], int32]): - pass - "}, - indoc! {" - class B: - def __init__(self): - pass - "} - ], - &[]; - "list tuple generic" -)] -#[test_case( - &[ - indoc! {" - class A(Generic[T, V]): - a: A[float, bool] - b: B - def __init__(self, a: A[float, bool], b: B): - self.a = a - self.b = b - def fun(self, a: A[float, bool]) -> A[bool, int32]: - pass - "}, - indoc! {" - class B(A[int64, bool]): - def __init__(self): - pass - def foo(self, b: B) -> B: - pass - def bar(self, a: A[list[B], int32]) -> tuple[A[virtual[A[B, int32]], bool], B]: - pass - "} - ], - &[]; - "self1" -)] +// #[test_case( +// &[ +// indoc! {" +// class A(): +// a: int32 +// def __init__(self): +// self.a = 3 +// def fun(self, b: B): +// pass +// def foo(self, a: T, b: V): +// pass +// "}, +// indoc! {" +// class B(C): +// def __init__(self): +// pass +// "}, +// indoc! {" +// class C(A): +// def __init__(self): +// pass +// def fun(self, b: B): +// a = 1 +// pass +// "}, +// indoc! {" +// def foo(a: A): +// pass +// "}, +// indoc! {" +// def ff(a: T) -> V: +// pass +// "} +// ], +// &[]; +// "simple class compose" +// )] +// #[test_case( +// &[ +// indoc! {" +// class Generic_A(Generic[V], B): +// a: int64 +// def __init__(self): +// self.a = 123123123123 +// def fun(self, a: int32) -> V: +// pass +// "}, +// indoc! {" +// class B: +// aa: bool +// def __init__(self): +// self.aa = False +// def foo(self, b: T): +// pass +// "} +// ], +// &[]; +// "generic class" +// )] +// #[test_case( +// &[ +// indoc! {" +// def foo(a: list[int32], b: tuple[T, float]) -> A[B, bool]: +// pass +// "}, +// indoc! {" +// class A(Generic[T, V]): +// a: T +// b: V +// def __init__(self, v: V): +// self.a = 1 +// self.b = v +// def fun(self, a: T) -> V: +// pass +// "}, +// indoc! {" +// def gfun(a: A[list[float], int32]): +// pass +// "}, +// indoc! {" +// class B: +// def __init__(self): +// pass +// "} +// ], +// &[]; +// "list tuple generic" +// )] +// #[test_case( +// &[ +// indoc! {" +// class A(Generic[T, V]): +// a: A[float, bool] +// b: B +// def __init__(self, a: A[float, bool], b: B): +// self.a = a +// self.b = b +// def fun(self, a: A[float, bool]) -> A[bool, int32]: +// pass +// "}, +// indoc! {" +// class B(A[int64, bool]): +// def __init__(self): +// pass +// def foo(self, b: B) -> B: +// pass +// def bar(self, a: A[list[B], int32]) -> tuple[A[virtual[A[B, int32]], bool], B]: +// pass +// "} +// ], +// &[]; +// "self1" +// )] #[test_case( &[ indoc! {" @@ -361,167 +361,8 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) { &[]; "inheritance_override" )] -#[test_case( - &[ - indoc! {" - class A(Generic[T]): - def __init__(self): - pass - def fun(self, a: A[T]) -> A[T]: - pass - "} - ], - &["application of type vars to generic class is not currently supported (at unknown:4:24)"]; - "err no type var in generic app" -)] -#[test_case( - &[ - indoc! {" - class A(B): - def __init__(self): - pass - "}, - indoc! {" - class B(A): - def __init__(self): - pass - "} - ], - &["cyclic inheritance detected"]; - "cyclic1" -)] -#[test_case( - &[ - indoc! {" - class A(B[bool, int64]): - def __init__(self): - pass - "}, - indoc! {" - class B(Generic[V, T], C[int32]): - def __init__(self): - pass - "}, - indoc! {" - class C(Generic[T], A): - def __init__(self): - pass - "}, - ], - &["cyclic inheritance detected"]; - "cyclic2" -)] -#[test_case( - &[ - indoc! {" - class A: - pass - "} - ], - &["5: Class {\nname: \"A\",\ndef_id: DefinitionId(5),\nancestors: [CustomClassKind { id: DefinitionId(5), params: [] }],\nfields: [],\nmethods: [],\ntype_vars: []\n}"]; - "simple pass in class" -)] -#[test_case( - &[indoc! {" - class A: - def __init__(): - pass - "}], - &["__init__ method must have a `self` parameter (at unknown:2:5)"]; - "err no self_1" -)] -#[test_case( - &[ - indoc! {" - class A(B, Generic[T], C): - def __init__(self): - pass - "}, - indoc! {" - class B: - def __init__(self): - pass - "}, - indoc! {" - class C: - def __init__(self): - pass - "} - - ], - &["a class definition can only have at most one base class declaration and one generic declaration (at unknown:1:24)"]; - "err multiple inheritance" -)] -#[test_case( - &[ - indoc! {" - class A(Generic[T]): - a: int32 - b: T - c: A[int64] - def __init__(self, t: T): - self.a = 3 - self.b = T - def fun(self, a: int32, b: T) -> list[virtual[B[bool]]]: - pass - "}, - indoc! {" - class B(Generic[V], A[float]): - def __init__(self): - pass - def fun(self, a: int32, b: T) -> list[virtual[B[int32]]]: - # override - pass - "} - ], - &["method fun has same name as ancestors' method, but incompatible type"]; - "err_incompatible_inheritance_method" -)] -#[test_case( - &[ - indoc! {" - class A(Generic[T]): - a: int32 - b: T - c: A[int64] - def __init__(self, t: T): - self.a = 3 - self.b = T - def fun(self, a: int32, b: T) -> list[virtual[B[bool]]]: - pass - "}, - indoc! {" - class B(Generic[V], A[float]): - a: int32 - def __init__(self): - pass - def fun(self, a: int32, b: T) -> list[virtual[B[bool]]]: - # override - pass - "} - ], - &["field `a` has already declared in the ancestor classes"]; - "err_incompatible_inheritance_field" -)] -#[test_case( - &[ - indoc! {" - class A: - def __init__(self): - pass - "}, - indoc! {" - class A: - a: int32 - def __init__(self): - pass - "} - ], - &["duplicate definition of class `A` (at unknown:1:1)"]; - "class same name" -)] fn test_analyze(source: &[&str], res: &[&str]) { - let print = false; + let print = true; let mut composer = TopLevelComposer::new(Vec::new(), Vec::new(), ComposerConfig::default(), 64).0; @@ -545,6 +386,7 @@ fn test_analyze(source: &[&str], res: &[&str]) { match composer.register_top_level(ast, Some(resolver.clone()), "", false) { Ok(x) => x, Err(msg) => { + println!("\nError in registry\n\n"); if print { println!("{msg}"); } else { @@ -561,6 +403,7 @@ fn test_analyze(source: &[&str], res: &[&str]) { } if let Err(msg) = composer.start_analysis(false) { + println!("Error message\n"); if print { println!("{}", msg.iter().sorted().join("\n----------\n")); } else { diff --git a/nac3core/src/toplevel/type_annotation.rs b/nac3core/src/toplevel/type_annotation.rs index d997f176..3ef3eb92 100644 --- a/nac3core/src/toplevel/type_annotation.rs +++ b/nac3core/src/toplevel/type_annotation.rs @@ -97,7 +97,12 @@ pub fn parse_ast_to_type_annotation_kinds( Ok(TypeAnnotation::CustomClass { id: PrimDef::Exception.id(), params: Vec::default() }) } else if let Ok(obj_id) = resolver.get_identifier_def(*id) { let type_vars = { - let def_read = top_level_defs[obj_id.0].try_read(); + let Some(top_level_def) = top_level_defs.get(obj_id.0) else { + return Err(HashSet::from([format!( + "NameError: '{id}' not found at {}", expr.location + )])); + }; + let def_read = top_level_def.try_read(); if let Some(def_read) = def_read { if let TopLevelDef::Class { type_vars, .. } = &*def_read { type_vars.clone() @@ -152,7 +157,12 @@ pub fn parse_ast_to_type_annotation_kinds( } let obj_id = resolver.get_identifier_def(*id)?; let type_vars = { - let def_read = top_level_defs[obj_id.0].try_read(); + let Some(top_level_def) = top_level_defs.get(obj_id.0) else { + return Err(HashSet::from([format!( + "NameError: '{id}' not found at {}", expr.location + )])); + }; + let def_read = top_level_def.try_read(); if let Some(def_read) = def_read { let TopLevelDef::Class { type_vars, .. } = &*def_read else { unreachable!("must be class here") diff --git a/nac3standalone/demo/src/_test.py b/nac3standalone/demo/src/_test.py new file mode 100644 index 00000000..928b15b4 --- /dev/null +++ b/nac3standalone/demo/src/_test.py @@ -0,0 +1,27 @@ +from __future__ import annotations +T = TypeVar("T") +V = TypeVar("V") +class A(Generic[T]): + a: int32 + b: T + c: A[int64] + def __init__(self, t: T): + self.a = 3 + self.b = T + def fun(self, a: int32, b: T) -> list[virtual[B[bool]]]: + pass + def foo(self, c: C): + pass + +class B(Generic[V], A[float]): + d: C + def __init__(self): + pass + def fun(self, a: int32, b: T) -> list[virtual[B[bool]]]: + # override + pass + +class C(B[bool]): + e: int64 + def __init__(self): + pass \ No newline at end of file