use crate::ast::named::Named; use crate::ast::{CompilationUnit, Identifier}; use crate::diagnostic::DmDiagnostic; use crate::name_analysis::fqn_context::FqnContext; use crate::name_analysis::gather::gather_module_level_declaration; use crate::name_analysis::resolve::resolve_module_level_declaration; use crate::name_analysis::symbol_table::SymbolTable; use codespan_reporting::diagnostic::{Diagnostic, Label}; mod fqn_context; mod gather; mod resolve; pub mod symbol; pub mod symbol_table; pub(self) struct DiagnosticsContainer { file_id: usize, diagnostics: Vec, } impl DiagnosticsContainer { pub fn new(file_id: usize) -> DiagnosticsContainer { DiagnosticsContainer { file_id, diagnostics: vec![], } } pub fn add(&mut self, f: impl FnOnce(usize) -> DmDiagnostic) { self.diagnostics.push(f(self.file_id)); } pub fn add_error_at_identifier(&mut self, msg: &str, identifier: &Identifier) { self.diagnostics.push( Diagnostic::error() .with_message(msg) .with_label(Label::primary(self.file_id, identifier.range)), ); } } impl Into> for DiagnosticsContainer { fn into(self) -> Vec { self.diagnostics } } pub fn analyze_names( file_id: usize, compilation_unit: &mut CompilationUnit, symbol_table: &mut SymbolTable, ) -> Vec { let mut diagnostics = DiagnosticsContainer::new(file_id); let mut fqn_context = FqnContext::new(); if let Some(namespace) = &compilation_unit.namespace { fqn_context.push(namespace.name().to_string()); } for declaration in &mut compilation_unit.declarations { gather_module_level_declaration( declaration, symbol_table, &mut fqn_context, &mut diagnostics, ); } assert_eq!(symbol_table.current_scope_id(), 0); for declaration in &mut compilation_unit.declarations { resolve_module_level_declaration(declaration, symbol_table, &mut diagnostics); } diagnostics.into() } #[cfg(test)] mod tests { use super::*; use crate::ast::build::build_ast; use crate::parser::{DeimosParser, Rule}; use codespan_reporting::files::SimpleFiles; use codespan_reporting::term; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use pest::Parser; fn assert_no_diagnostics(src: &str) { let parse_result = DeimosParser::parse(Rule::CompilationUnit, src); if let Err(err) = &parse_result { panic!("{:?}", err); } let compilation_unit_pair = parse_result.unwrap().next().unwrap(); let mut ast = build_ast(compilation_unit_pair); let mut files = SimpleFiles::new(); let test_file_id = files.add("test.dm", src); let mut symbol_table = SymbolTable::new(); let diagnostics = analyze_names(test_file_id, &mut ast, &mut symbol_table); if !diagnostics.is_empty() { let writer = StandardStream::stderr(ColorChoice::Always); let config = term::Config::default(); for diagnostic in &diagnostics { term::emit(&mut writer.lock(), &config, &files, &diagnostic).unwrap(); } eprintln!("{}", symbol_table); panic!("Diagnostics was not empty!"); } } #[test] fn params_seen() { assert_no_diagnostics( r#" fn main(args: Array) { let x = args; } "#, ) } }