Use ast walking to verify that identifiers have saved symbols and linking symbols are resolved.
This commit is contained in:
parent
d38b30b755
commit
234f40ec58
@ -3,4 +3,4 @@ pub mod children;
|
||||
pub mod node;
|
||||
pub mod pretty_print;
|
||||
pub mod unparse;
|
||||
mod walk;
|
||||
pub mod walk;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -311,27 +311,31 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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(
|
||||
|
Loading…
Reference in New Issue
Block a user