283 lines
8.7 KiB
Rust
283 lines
8.7 KiB
Rust
/*!
|
|
# Name Analysis
|
|
|
|
There are two phases in name analysis.
|
|
|
|
## 1. Gather
|
|
|
|
The gather phase has three responsibilities:
|
|
|
|
1. Add all declared symbols to the symbol table.
|
|
2. Set the `scope_id` property of all identifiers and fully-qualified-names.
|
|
3. For the main identifiers of `UseStatement`s (i.e., the last identifier in the `UseStatement`):
|
|
set a 'linking' symbol on the identifier, which will later be filled in with the linked-to
|
|
symbol (probably in another file, or from the standard library).
|
|
|
|
## 2. Resolve
|
|
|
|
The resolve phase has one main responsibility: resolve all references based on the identifier's
|
|
`scope_id` property.
|
|
*/
|
|
|
|
// use crate::name_analysis::resolve::resolve_compilation_unit;
|
|
use crate::ast::ast_node::AstNode;
|
|
use crate::ast::node::CompilationUnit;
|
|
use crate::diagnostic::DmDiagnostic;
|
|
use crate::name_analysis::gather::gather_compilation_unit;
|
|
use crate::name_analysis::symbol_table::SymbolTable;
|
|
use codespan_reporting::files::Files;
|
|
use std::collections::HashMap;
|
|
use std::hash::Hash;
|
|
use crate::name_analysis::scope_table::ScopeTable;
|
|
|
|
pub(self) mod fqn_context;
|
|
mod gather;
|
|
// mod resolve;
|
|
pub mod symbol;
|
|
pub mod symbol_table;
|
|
mod scope_table;
|
|
|
|
pub fn analyze_names<'a, F: Files<'a, FileId = usize, Name = String>>(
|
|
compilation_units: &[Box<CompilationUnit>],
|
|
files: &'a F,
|
|
symbol_table: &mut SymbolTable,
|
|
) -> Vec<DmDiagnostic> {
|
|
let mut diagnostics = vec![];
|
|
let mut scope_table = ScopeTable::new();
|
|
|
|
// gather symbols
|
|
for compilation_unit in compilation_units {
|
|
let file_name = files.name(compilation_unit.file_id()).unwrap();
|
|
gather_compilation_unit(
|
|
compilation_unit,
|
|
&file_name,
|
|
symbol_table,
|
|
&mut scope_table,
|
|
&mut diagnostics,
|
|
);
|
|
}
|
|
|
|
// resolve symbols
|
|
for compilation_unit in compilation_units {
|
|
// resolve_compilation_unit(compilation_unit, symbol_table, &mut diagnostics);
|
|
}
|
|
|
|
diagnostics.into()
|
|
}
|
|
|
|
// #[cfg(test)]
|
|
// mod tests {
|
|
// use super::*;
|
|
// use crate::ast::build::build_ast;
|
|
// use crate::ast::children::NodeRef;
|
|
// 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;
|
|
// use codespan_reporting::term;
|
|
// use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
|
|
// use indoc::indoc;
|
|
// use pest::Parser;
|
|
// use std::collections::HashMap;
|
|
//
|
|
// fn assert_number_of_diagnostics(
|
|
// sources: HashMap<&str, &str>,
|
|
// symbol_table: &mut SymbolTable,
|
|
// n_diagnostics: usize,
|
|
// ) -> Vec<CompilationUnit> {
|
|
// let mut files = SimpleFiles::new();
|
|
// let mut compilation_units = vec![];
|
|
//
|
|
// for (file_name, source) in sources {
|
|
// let file_id = files.add(file_name, source);
|
|
// let parse_result = DeimosParser::parse(Rule::CompilationUnit, source);
|
|
// if let Err(err) = &parse_result {
|
|
// panic!("{}", err);
|
|
// }
|
|
// let mut pairs = parse_result.unwrap();
|
|
// if pairs.as_str().trim() != source.trim() {
|
|
// panic!("Parsing did not consume entire input.");
|
|
// }
|
|
//
|
|
// compilation_units.push(build_ast(file_name, file_id, pairs.next().unwrap()));
|
|
// }
|
|
//
|
|
// let diagnostics = analyze_names(&mut compilation_units, symbol_table);
|
|
//
|
|
// if diagnostics.len() != n_diagnostics {
|
|
// 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);
|
|
// }
|
|
//
|
|
// assert_eq!(n_diagnostics, diagnostics.len());
|
|
//
|
|
// compilation_units
|
|
// }
|
|
//
|
|
// 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 {
|
|
// _ => 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 {
|
|
// _ => todo!(),
|
|
// },
|
|
// _ => {}
|
|
// })
|
|
// }
|
|
//
|
|
// #[test]
|
|
// fn params_seen() {
|
|
// let sources: HashMap<&str, &str> = HashMap::from([(
|
|
// "main.dm",
|
|
// indoc! {"
|
|
// fn main(args: Array<String>) {
|
|
// let x = args;
|
|
// }"},
|
|
// )]);
|
|
//
|
|
// let cus = assert_no_diagnostics(sources, &mut SymbolTable::new());
|
|
// for ref cu in cus {
|
|
// assert_saved_symbols(cu);
|
|
// }
|
|
// }
|
|
//
|
|
// #[test]
|
|
// fn two_files() {
|
|
// let sources: HashMap<&str, &str> = HashMap::from([
|
|
// (
|
|
// "main.dm",
|
|
// indoc! {"
|
|
// use test::Greeter;
|
|
// "},
|
|
// ),
|
|
// (
|
|
// "deps.dm",
|
|
// indoc! {"
|
|
// ns test;
|
|
//
|
|
// pub class Greeter {}
|
|
// "},
|
|
// ),
|
|
// ]);
|
|
//
|
|
// let cus = assert_no_diagnostics(sources, &mut SymbolTable::new());
|
|
// for ref cu in cus {
|
|
// assert_saved_symbols(cu);
|
|
// assert_resolved_symbols(cu);
|
|
// }
|
|
// }
|
|
//
|
|
// #[test]
|
|
// fn sees_std_core_println() {
|
|
// let sources: HashMap<&str, &str> = HashMap::from([(
|
|
// "main.dm",
|
|
// indoc! {"
|
|
// fn main(args: Array<String>) {
|
|
// println(args)
|
|
// }
|
|
// "},
|
|
// )]);
|
|
//
|
|
// let mut symbol_table = SymbolTable::new();
|
|
// add_std_core_symbols(&mut symbol_table).expect("Failed to add std::core symbols.");
|
|
// let cus = assert_no_diagnostics(sources, &mut symbol_table);
|
|
// for ref cu in cus {
|
|
// assert_saved_symbols(cu);
|
|
// assert_resolved_symbols(cu);
|
|
// }
|
|
// }
|
|
//
|
|
// #[test]
|
|
// fn sees_duplicate_fn() {
|
|
// let sources: HashMap<&str, &str> = HashMap::from([(
|
|
// "main.dm",
|
|
// indoc! {"
|
|
// fn main(args: Array<String>) {}
|
|
// fn main(args: Array<String>) {}
|
|
// "},
|
|
// )]);
|
|
// assert_number_of_diagnostics(sources, &mut SymbolTable::new(), 1);
|
|
// }
|
|
//
|
|
// #[test]
|
|
// fn use_class_from_other_file() {
|
|
// let sources: HashMap<&str, &str> = HashMap::from([
|
|
// (
|
|
// "main.dm",
|
|
// indoc! {"
|
|
// use greeter::Greeter;
|
|
//
|
|
// fn test(greeter: Greeter) {}
|
|
// "},
|
|
// ),
|
|
// (
|
|
// "greeter.dm",
|
|
// indoc! {"
|
|
// ns greeter;
|
|
//
|
|
// class Greeter {}
|
|
// "},
|
|
// ),
|
|
// ]);
|
|
// let mut symbol_table = SymbolTable::new();
|
|
// let cus = assert_no_diagnostics(sources, &mut symbol_table);
|
|
// for ref cu in cus {
|
|
// assert_saved_symbols(cu);
|
|
// assert_resolved_symbols(cu);
|
|
// }
|
|
// }
|
|
//
|
|
// #[test]
|
|
// fn shadow_import() {
|
|
// let sources: HashMap<&str, &str> = HashMap::from([
|
|
// (
|
|
// "main.dm",
|
|
// indoc! {"
|
|
// use greeter::Greeter;
|
|
//
|
|
// class Greeter {}
|
|
// "},
|
|
// ),
|
|
// (
|
|
// "greeter.dm",
|
|
// indoc! {"
|
|
// ns greeter;
|
|
//
|
|
// class Greeter {}
|
|
// "},
|
|
// ),
|
|
// ]);
|
|
// assert_number_of_diagnostics(sources, &mut SymbolTable::new(), 1);
|
|
// }
|
|
// }
|