#![cfg(test)] use assert_matches::assert_matches; use super::*; use crate::util::quick_ast; fn add_symbols(src: &str) -> (SymbolTable, Result<(), Vec>) { let ast = quick_ast(src); let mut symbol_table = SymbolTable::new(); let mut type_context = crate::type_inference::TypeContext::new(); let result = symbol_table.process_ast(&ast, &mut type_context); (symbol_table, result) } fn make_fqsn(strs: &[&str]) -> Fqsn { Fqsn::from_strs(strs) } #[test] fn basic_symbol_table() { let src = "let a = 10; fn b() { 20 }"; let (symbols, _) = add_symbols(src); fn make_fqsn(strs: &[&str]) -> Fqsn { Fqsn::from_strs(strs) } symbols.fq_names.table.get(&make_fqsn(&["b"])).unwrap(); let src = "type Option = Some(T) | None"; let (symbols, _) = add_symbols(src); symbols.types.table.get(&make_fqsn(&["Option"])).unwrap(); } #[test] fn no_function_definition_duplicates() { let source = r#" fn a() { 1 } fn b() { 2 } fn a() { 3 } "#; let (_, output) = add_symbols(source); let errs = output.unwrap_err(); assert_matches!(&errs[..], [ SymbolError::DuplicateName { prev_name, ..} ] if prev_name == &Fqsn::from_strs(&["a"]) ); } #[test] fn no_variable_definition_duplicates() { let source = r#" let x = 9 let a = 20 let q = 39 let a = 30 let x = 34 "#; let (_, output) = add_symbols(source); let errs = output.unwrap_err(); assert_matches!(&errs[..], [ SymbolError::DuplicateName { prev_name: pn1, ..}, SymbolError::DuplicateName { prev_name: pn2, ..} ] if pn1 == &Fqsn::from_strs(&["a"]) && pn2 == &Fqsn::from_strs(&["x"]) ); } #[test] fn no_type_definition_duplicates() { let source = r#" let x = 9 type Food = Japchae | Burrito | Other type Food = GoodJapchae | Breadfruit "#; let (_, output) = add_symbols(source); let errs = output.unwrap_err(); let err = &errs[0]; match err { SymbolError::DuplicateName { location: _, prev_name } => { assert_eq!(prev_name, &Fqsn::from_strs(&["Food"])); //TODO restore this Location test //assert_eq!(location, &Location { line_num: 2, char_num: 2 }); } _ => panic!(), } } #[test] fn no_variant_duplicates() { let source = r#" type Panda = FoolsGold | Kappa(i32) | Remix | Kappa | Thursday | Remix "#; let (_, output) = add_symbols(source); let errs = output.unwrap_err(); assert_eq!(errs.len(), 2); assert_matches!(&errs[0], SymbolError::DuplicateVariant { type_fqsn, name } if *type_fqsn == Fqsn::from_strs(&["Panda"]) && name == "Kappa"); assert_matches!(&errs[1], SymbolError::DuplicateVariant { type_fqsn, name } if *type_fqsn == Fqsn::from_strs(&["Panda"]) && name == "Remix"); } #[test] fn no_variable_definition_duplicates_in_function() { let source = r#" fn a() { let a = 20 let b = 40 a + b } fn q() { let a = 29 let x = 30 let x = 33 } "#; let (_, output) = add_symbols(source); let errs = output.unwrap_err(); assert_matches!(&errs[..], [ SymbolError::DuplicateName { prev_name: pn1, ..}, ] if pn1 == &Fqsn::from_strs(&["q", "x"]) ); } #[test] fn dont_falsely_detect_duplicates() { let source = r#" let a = 20; fn some_func() { let a = 40; 77 } let q = 39 "#; let (symbols, _) = add_symbols(source); assert!(symbols.fq_names.table.get(&make_fqsn(&["a"])).is_some()); assert!(symbols.fq_names.table.get(&make_fqsn(&["some_func", "a"])).is_some()); } #[test] fn enclosing_scopes() { let source = r#" fn outer_func(x) { fn inner_func(arg) { arg } x + inner_func(x) }"#; let (symbols, _) = add_symbols(source); assert!(symbols.fq_names.table.get(&make_fqsn(&["outer_func"])).is_some()); assert!(symbols.fq_names.table.get(&make_fqsn(&["outer_func", "inner_func"])).is_some()); } #[test] fn enclosing_scopes_2() { let source = r#" fn outer_func(x) { fn inner_func(arg) { arg } fn second_inner_func() { fn another_inner_func() { } } inner_func(x) } "#; let (symbols, _) = add_symbols(source); assert!(symbols.fq_names.table.get(&make_fqsn(&["outer_func"])).is_some()); assert!(symbols.fq_names.table.get(&make_fqsn(&["outer_func", "inner_func"])).is_some()); assert!(symbols.fq_names.table.get(&make_fqsn(&["outer_func", "second_inner_func"])).is_some()); assert!(symbols .fq_names .table .get(&make_fqsn(&["outer_func", "second_inner_func", "another_inner_func"])) .is_some()); } #[test] fn enclosing_scopes_3() { let source = r#" fn outer_func(x) { fn inner_func(arg) { arg } fn second_inner_func() { fn another_inner_func() { } fn another_inner_func() { } } inner_func(x) }"#; let (_, output) = add_symbols(source); let _err = output.unwrap_err(); } #[test] fn modules() { let source = r#" module stuff { fn item() { } } fn item() "#; let (symbols, _) = add_symbols(source); symbols.fq_names.table.get(&make_fqsn(&["stuff"])).unwrap(); symbols.fq_names.table.get(&make_fqsn(&["item"])).unwrap(); symbols.fq_names.table.get(&make_fqsn(&["stuff", "item"])).unwrap(); } #[test] fn duplicate_modules() { let source = r#" module q { fn foo() { 4 } } module a { fn foo() { 334 } } module a { fn sarat() { 39 } fn foo() { 256.1 } } "#; let (_, output) = add_symbols(source); let errs = output.unwrap_err(); assert_matches!(&errs[..], [ SymbolError::DuplicateName { prev_name: pn1, ..}, ] if pn1 == &Fqsn::from_strs(&["a"]) ); } #[test] fn duplicate_struct_members() { let source = r#" type Tarak = Tarak { loujet: i32 , mets: i32, mets: i32 , } "#; let (_, output) = add_symbols(source); let errs = dbg!(output.unwrap_err()); assert_matches!(&errs[..], [ SymbolError::DuplicateRecord { type_fqsn, member, record, ..}, ] if type_fqsn == &Fqsn::from_strs(&["Tarak"]) && member == "mets" && record == "Tarak" ); } #[test] fn methods() { let source = r#" type Foo = { x: Int, y: Int } impl Foo { fn hella() { self.x + 50 } } "#; let (symbols, _) = add_symbols(source); symbols.debug(); assert!(symbols.fq_names.table.get(&make_fqsn(&["hella"])).is_some()); }