Use ast walking to verify that identifiers have saved symbols and linking symbols are resolved.

This commit is contained in:
Jesse Brault 2025-05-26 16:24:40 -05:00
parent d38b30b755
commit 234f40ec58
4 changed files with 132 additions and 34 deletions

View File

@ -3,4 +3,4 @@ pub mod children;
pub mod node;
pub mod pretty_print;
pub mod unparse;
mod walk;
pub mod walk;

View File

@ -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);
}

View File

@ -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<CompilationUnit> {
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<CompilationUnit> {
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]

View File

@ -311,18 +311,20 @@ impl SymbolTable {
pub fn insert_variable_symbol(
&mut self,
variable_symbol: VariableSymbol,
) -> Result<(), SymbolInsertError> {
) -> Result<Rc<VariableSymbol>, 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)
}
}
@ -331,7 +333,9 @@ impl SymbolTable {
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(