From 234f40ec587a95b47666e8ab9ae304399cc11305 Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Mon, 26 May 2025 16:24:40 -0500 Subject: [PATCH] Use ast walking to verify that identifiers have saved symbols and linking symbols are resolved. --- src/ast/mod.rs | 2 +- src/name_analysis/gather.rs | 27 +++---- src/name_analysis/mod.rs | 117 +++++++++++++++++++++++++++--- src/name_analysis/symbol_table.rs | 20 +++-- 4 files changed, 132 insertions(+), 34 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index e22f418..9636c8b 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -3,4 +3,4 @@ pub mod children; pub mod node; pub mod pretty_print; pub mod unparse; -mod walk; +pub mod walk; diff --git a/src/name_analysis/gather.rs b/src/name_analysis/gather.rs index 48d8f15..8de7215 100644 --- a/src/name_analysis/gather.rs +++ b/src/name_analysis/gather.rs @@ -250,9 +250,9 @@ fn gather_parameter( match insert_result { Ok(parameter_symbol) => { - parameter - .identifier_mut() - .set_scope_id(symbol_table.current_scope_id()); + let mut identifier = parameter.identifier_mut(); + identifier.set_scope_id(symbol_table.current_scope_id()); + identifier.set_saved_symbol(Symbol::Parameter(parameter_symbol.clone())); gather_type_use( parameter.type_use_mut(), @@ -694,9 +694,9 @@ fn gather_function_definition( match insert_result { Ok(function_symbol) => { - function - .identifier_mut() - .set_scope_id(symbol_table.current_scope_id()); + let mut identifier = function.identifier_mut(); + identifier.set_saved_symbol(Symbol::Function(function_symbol.clone())); + identifier.set_scope_id(symbol_table.current_scope_id()); symbol_table.push_scope(&format!("FunctionParameterScope({})", resolved_name)); @@ -1043,21 +1043,22 @@ fn gather_variable_declaration( Some(identifier), )); - if let Err(err) = insert_result { - handle_insert_error( + match insert_result { + Ok(variable_symbol) => { + let mut identifier = variable_declaration.identifier_mut(); + identifier.set_saved_symbol(Symbol::Variable(variable_symbol)); + identifier.set_scope_id(symbol_table.current_scope_id()); + } + Err(err) => handle_insert_error( err, &variable_name, identifier.file_id(), identifier.range(), "function/variable", diagnostics, - ) + ), } - variable_declaration - .identifier_mut() - .set_scope_id(symbol_table.current_scope_id()); - if let Some(initializer) = variable_declaration.initializer_mut() { gather_expression(initializer, symbol_table, diagnostics); } diff --git a/src/name_analysis/mod.rs b/src/name_analysis/mod.rs index 4db7e1d..1c34b98 100644 --- a/src/name_analysis/mod.rs +++ b/src/name_analysis/mod.rs @@ -55,6 +55,9 @@ pub fn analyze_names( mod tests { use super::*; use crate::ast::build::build_ast; + use crate::ast::children::NodeRef; + use crate::ast::node::use_statement::UseStatementLast; + use crate::ast::walk::walk_depth_first; use crate::parser::{DeimosParser, Rule}; use crate::std_core::add_std_core_symbols; use codespan_reporting::files::SimpleFiles; @@ -68,7 +71,7 @@ mod tests { sources: HashMap<&str, &str>, symbol_table: &mut SymbolTable, n_diagnostics: usize, - ) { + ) -> Vec { let mut files = SimpleFiles::new(); let mut compilation_units = vec![]; @@ -83,12 +86,12 @@ mod tests { panic!("Parsing did not consume entire input."); } - compilation_units.push(build_ast(file_name, file_id, pairs.next().unwrap())) + compilation_units.push(build_ast(file_name, file_id, pairs.next().unwrap())); } let diagnostics = analyze_names(&mut compilation_units, symbol_table); - if !diagnostics.is_empty() { + if diagnostics.len() != n_diagnostics { let writer = StandardStream::stderr(ColorChoice::Always); let config = term::Config::default(); @@ -101,13 +104,88 @@ mod tests { assert_eq!(n_diagnostics, diagnostics.len()); - for compilation_unit in &compilation_units { - dbg!(compilation_unit); - } + compilation_units } - fn assert_no_diagnostics(sources: HashMap<&str, &str>, symbol_table: &mut SymbolTable) { - assert_number_of_diagnostics(sources, symbol_table, 0); + fn assert_no_diagnostics( + sources: HashMap<&str, &str>, + symbol_table: &mut SymbolTable, + ) -> Vec { + assert_number_of_diagnostics(sources, symbol_table, 0) + } + + fn assert_saved_symbols(compilation_unit: &CompilationUnit) { + walk_depth_first(compilation_unit, &mut |node_ref| match node_ref { + NodeRef::Identifier(identifier) => { + if identifier.saved_symbol().is_none() { + panic!("{:?} does not have a saved symbol.", identifier) + } + } + NodeRef::FullyQualifiedName(fqn) => { + if fqn.saved_symbol().is_none() { + panic!("{:?} does not have a saved symbol.", fqn) + } + } + NodeRef::UseStatement(use_statement) => match use_statement.last() { + UseStatementLast::Identifier(identifier) => { + if identifier.saved_symbol().is_none() { + panic!( + "UseStatement {:?} does not have a saved symbol.", + identifier + ) + } + } + UseStatementLast::Identifiers(identifiers) => { + for identifier in identifiers { + if identifier.saved_symbol().is_none() { + panic!( + "UseStatement {:?} does not have a saved symbol.", + identifier + ) + } + } + } + _ => todo!(), + }, + _ => {} + }) + } + + fn assert_resolved_symbols(compilation_unit: &CompilationUnit) { + walk_depth_first(compilation_unit, &mut |node_ref| match node_ref { + NodeRef::UseStatement(use_statement) => match use_statement.last() { + UseStatementLast::Identifier(identifier) => { + let use_statement_symbol = identifier + .saved_symbol() + .unwrap() + .unwrap_use_statement_symbol(); + let borrowed = use_statement_symbol.borrow(); + if borrowed.referenced_symbol().is_none() { + panic!( + "{:?} does not have a referenced symbol.", + use_statement_symbol + ) + } + } + UseStatementLast::Identifiers(identifiers) => { + for identifier in identifiers { + let use_statement_symbol = identifier + .saved_symbol() + .unwrap() + .unwrap_use_statement_symbol(); + let borrowed = use_statement_symbol.borrow(); + if borrowed.referenced_symbol().is_none() { + panic!( + "{:?} does not have a referenced symbol.", + use_statement_symbol + ) + } + } + } + _ => todo!(), + }, + _ => {} + }) } #[test] @@ -120,7 +198,10 @@ mod tests { }"}, )]); - assert_no_diagnostics(sources, &mut SymbolTable::new()); + let cus = assert_no_diagnostics(sources, &mut SymbolTable::new()); + for ref cu in cus { + assert_saved_symbols(cu); + } } #[test] @@ -142,7 +223,11 @@ mod tests { ), ]); - assert_no_diagnostics(sources, &mut SymbolTable::new()); + let cus = assert_no_diagnostics(sources, &mut SymbolTable::new()); + for ref cu in cus { + assert_saved_symbols(cu); + assert_resolved_symbols(cu); + } } #[test] @@ -158,7 +243,11 @@ mod tests { let mut symbol_table = SymbolTable::new(); add_std_core_symbols(&mut symbol_table).expect("Failed to add std::core symbols."); - assert_no_diagnostics(sources, &mut symbol_table); + let cus = assert_no_diagnostics(sources, &mut symbol_table); + for ref cu in cus { + assert_saved_symbols(cu); + assert_resolved_symbols(cu); + } } #[test] @@ -194,7 +283,11 @@ mod tests { ), ]); let mut symbol_table = SymbolTable::new(); - assert_no_diagnostics(sources, &mut symbol_table); + let cus = assert_no_diagnostics(sources, &mut symbol_table); + for ref cu in cus { + assert_saved_symbols(cu); + assert_resolved_symbols(cu); + } } #[test] diff --git a/src/name_analysis/symbol_table.rs b/src/name_analysis/symbol_table.rs index 0917587..7db4ed8 100644 --- a/src/name_analysis/symbol_table.rs +++ b/src/name_analysis/symbol_table.rs @@ -311,27 +311,31 @@ impl SymbolTable { pub fn insert_variable_symbol( &mut self, variable_symbol: VariableSymbol, - ) -> Result<(), SymbolInsertError> { + ) -> Result, SymbolInsertError> { let current_scope = self.scopes.get_mut(self.current_scope_id).unwrap(); if let Some(defined_symbol) = current_scope.get_value_symbol_by_declared_name(variable_symbol.declared_name()) { Err(SymbolAlreadyDefined(defined_symbol)) } else { - current_scope.variable_symbols.insert( - variable_symbol.declared_name().to_string(), - Rc::new(variable_symbol), - ); - Ok(()) + let declared_name = variable_symbol.declared_name().to_string(); + let to_insert = Rc::new(variable_symbol); + let to_return = to_insert.clone(); + current_scope + .variable_symbols + .insert(declared_name, to_insert); + Ok(to_return) } } - + pub fn insert_class_member_symbol( &mut self, class_member_symbol: ClassMemberSymbol, ) -> Result<(), SymbolInsertError> { let current_scope = self.scopes.get_mut(self.current_scope_id).unwrap(); - if let Some(defined_symbol) = current_scope.get_expressible_by_declared_name(class_member_symbol.declared_name()) { + if let Some(defined_symbol) = + current_scope.get_expressible_by_declared_name(class_member_symbol.declared_name()) + { Err(SymbolAlreadyDefined(defined_symbol)) } else { current_scope.class_member_symbols.insert(