#![cfg(test)] use std::cell::RefCell; use std::rc::Rc; use super::*; use crate::util::quick_ast; fn add_symbols_from_source(src: &str) -> (SymbolTable, Result<(), String>) { let (ast, source_map) = quick_ast(src); let source_map = Rc::new(RefCell::new(source_map)); let mut symbol_table = SymbolTable::new(source_map); let result = symbol_table.add_top_level_symbols(&ast); (symbol_table, result) } macro_rules! values_in_table { ($source:expr, $single_value:expr) => { values_in_table!($source => $single_value); }; ($source:expr => $( $value:expr ),* ) => { { let (symbol_table, _) = add_symbols_from_source($source); $( match symbol_table.lookup_by_fqsn($value) { Some(_spec) => (), None => panic!(), }; )* } }; } #[test] fn basic_symbol_table() { values_in_table! { "let a = 10; fn b() { 20 }", &fqsn!("b"; tr) }; values_in_table! { "type Option = Some(T) | None" => &fqsn!("Option"; tr), &fqsn!("Option"; ty, "Some"; tr), &fqsn!("Option"; ty, "None"; tr) }; } #[test] fn no_function_definition_duplicates() { let source = r#" fn a() { 1 } fn b() { 2 } fn a() { 3 } "#; let (_, output) = add_symbols_from_source(source); assert!(output.unwrap_err().contains("Duplicate function definition: a")) } #[test] fn no_variable_definition_duplicates() { let source = r#" let x = 9 let a = 20 let q = 39 let a = 30 "#; let (_, output) = add_symbols_from_source(source); let output = output.unwrap_err(); assert!(output.contains("Duplicate variable definition: a")); assert!(output.contains("already defined at 2")); } #[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_from_source(source); assert!(output.unwrap_err().contains("Duplicate variable definition: x")) } #[test] fn dont_falsely_detect_duplicates() { let source = r#" let a = 20; fn some_func() { let a = 40; 77 } let q = 39; "#; let (symbol_table, _) = add_symbols_from_source(source); assert!(symbol_table.lookup_by_fqsn(&fqsn!["a"; tr]).is_some()); assert!(symbol_table.lookup_by_fqsn(&fqsn!["some_func"; fn, "a";tr]).is_some()); } #[test] fn enclosing_scopes() { let source = r#" fn outer_func(x) { fn inner_func(arg) { arg } x + inner_func(x) }"#; let (symbol_table, _) = add_symbols_from_source(source); assert!(symbol_table.lookup_by_fqsn(&fqsn!("outer_func"; tr)).is_some()); assert!(symbol_table.lookup_by_fqsn(&fqsn!("outer_func"; fn, "inner_func"; tr)).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 (symbol_table, _) = add_symbols_from_source(source); assert!(symbol_table.lookup_by_fqsn(&fqsn!("outer_func"; tr)).is_some()); assert!(symbol_table.lookup_by_fqsn(&fqsn!("outer_func"; fn, "inner_func"; tr)).is_some()); assert!(symbol_table.lookup_by_fqsn(&fqsn!("outer_func"; fn, "second_inner_func"; tr)).is_some()); assert!(symbol_table.lookup_by_fqsn(&fqsn!("outer_func"; fn, "second_inner_func"; fn, "another_inner_func"; tr)).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_from_source(source); assert!(output.unwrap_err().contains("Duplicate")) } #[test] fn modules() { let source = r#" module stuff { fn item() { } } fn item() "#; values_in_table! { source => &fqsn!("item"; tr), &fqsn!("stuff"; tr, "item"; tr) }; } #[test] fn duplicate_modules() { let source = r#" module q { fn foo() { 4 } } module a { fn foo() { 334 } } module a { fn foo() { 256.1 } } "#; let (_, output) = add_symbols_from_source(source); let output = output.unwrap_err(); assert!(output.contains("Duplicate module")); assert!(output.contains("already defined at 5")); }