diff --git a/sixtyfps_compiler/diagnostics.rs b/sixtyfps_compiler/diagnostics.rs index f68753aec..936ced7ea 100644 --- a/sixtyfps_compiler/diagnostics.rs +++ b/sixtyfps_compiler/diagnostics.rs @@ -1,3 +1,6 @@ +use std::collections::HashMap; +use std::path::PathBuf; + #[derive(Debug, Clone, Default)] pub struct Span { pub offset: usize, @@ -206,3 +209,86 @@ impl quote::ToTokens for FileDiagnostics { quote!(#(#diags)*).to_tokens(tokens); } } + +#[derive(Default)] +pub struct BuildDiagnostics { + per_input_file_diagnostics: HashMap, + internal_errors: Option, +} + +impl BuildDiagnostics { + pub fn add(&mut self, diagnostics: FileDiagnostics) { + match self.per_input_file_diagnostics.get_mut(&diagnostics.current_path) { + Some(existing_diags) => existing_diags.inner.extend(diagnostics.inner), + None => { + self.per_input_file_diagnostics + .insert(diagnostics.current_path.clone(), diagnostics); + } + } + } + + pub fn push_internal_error(&mut self, err: Diagnostic) { + self.internal_errors + .get_or_insert_with(|| FileDiagnostics { + current_path: "[internal error]".into(), + ..Default::default() + }) + .inner + .push(err) + } + + fn iter(&self) -> impl Iterator { + self.per_input_file_diagnostics.values().chain(self.internal_errors.iter()) + } + + fn into_iter(self) -> impl Iterator { + self.per_input_file_diagnostics + .into_iter() + .map(|(_, diag)| diag) + .chain(self.internal_errors.into_iter()) + } + + fn iter_mut(&mut self) -> impl Iterator { + self.per_input_file_diagnostics.values_mut().chain(self.internal_errors.iter_mut()) + } + + pub fn has_error(&self) -> bool { + self.iter().any(|diag| diag.has_error()) + } + + pub fn to_string_vec(&self) -> Vec { + self.iter() + .flat_map(|diag| { + diag.to_string_vec() + .iter() + .map(|err| format!("{}: {}", diag.current_path.to_string_lossy(), err)) + .collect::>() + }) + .collect() + } + + pub fn print(self) { + self.into_iter().for_each(|diag| diag.print()); + } + + #[cfg(feature = "display-diagnostics")] + pub fn check_and_exit_on_error(self) -> Self { + if self.has_error() { + self.print(); + std::process::exit(-1); + } + self + } + + #[cfg(feature = "proc_macro_span")] + pub fn map_offsets_to_span(&mut self, span_map: &[crate::parser::Token]) { + self.iter_mut().for_each(|diag| diag.map_offsets_to_span(span_map)) + } +} + +#[cfg(feature = "proc_macro_span")] +impl quote::ToTokens for BuildDiagnostics { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + self.iter().for_each(|diag| diag.to_tokens(tokens)) + } +} diff --git a/sixtyfps_compiler/generator.rs b/sixtyfps_compiler/generator.rs index bca52ab08..5efeaa87f 100644 --- a/sixtyfps_compiler/generator.rs +++ b/sixtyfps_compiler/generator.rs @@ -4,7 +4,7 @@ The module responsible for the code generation. There is one sub module for every language */ -use crate::diagnostics::FileDiagnostics; +use crate::diagnostics::BuildDiagnostics; use crate::object_tree::{Component, ElementRc}; use std::rc::Rc; @@ -51,7 +51,7 @@ pub fn generate( format: OutputFormat, destination: &mut impl std::io::Write, component: &Rc, - diag: &mut FileDiagnostics, + diag: &mut BuildDiagnostics, ) -> std::io::Result<()> { #![allow(unused_variables)] #![allow(unreachable_code)] diff --git a/sixtyfps_compiler/generator/cpp.rs b/sixtyfps_compiler/generator/cpp.rs index 779a66a7b..e472183b3 100644 --- a/sixtyfps_compiler/generator/cpp.rs +++ b/sixtyfps_compiler/generator/cpp.rs @@ -127,7 +127,7 @@ mod cpp_ast { } } -use crate::diagnostics::{CompilerDiagnostic, FileDiagnostics}; +use crate::diagnostics::{BuildDiagnostics, CompilerDiagnostic}; use crate::expression_tree::Expression; use crate::object_tree::{Component, Element, ElementRc, RepeatedElementInfo}; use crate::parser::Spanned; @@ -360,7 +360,7 @@ fn handle_repeater( /// Returns the text of the C++ code produced by the given root component pub fn generate( component: &Rc, - diag: &mut FileDiagnostics, + diag: &mut BuildDiagnostics, ) -> Option { let mut file = File::default(); @@ -377,7 +377,7 @@ pub fn generate( } } -fn generate_component(file: &mut File, component: &Rc, diag: &mut FileDiagnostics) { +fn generate_component(file: &mut File, component: &Rc, diag: &mut BuildDiagnostics) { let component_id = component_id(component); let mut component_struct = Struct { name: component_id.clone(), ..Default::default() }; @@ -405,7 +405,7 @@ fn generate_component(file: &mut File, component: &Rc, diag: &mut Fil span: property_decl.type_location.clone(), }; - diag.push_compiler_error(err); + diag.push_internal_error(err.into()); "".into() }); @@ -454,15 +454,18 @@ fn generate_component(file: &mut File, component: &Rc, diag: &mut Fil .ty() .cpp_type() .unwrap_or_else(|| { - diag.push_compiler_error(CompilerDiagnostic { - message: "Cannot map property type to C++".into(), - span: parent_element - .borrow() - .node - .as_ref() - .map(|n| n.span()) - .unwrap_or_default(), - }); + diag.push_internal_error( + CompilerDiagnostic { + message: "Cannot map property type to C++".into(), + span: parent_element + .borrow() + .node + .as_ref() + .map(|n| n.span()) + .unwrap_or_default(), + } + .into(), + ); String::default() }) .to_owned(); diff --git a/sixtyfps_compiler/generator/rust.rs b/sixtyfps_compiler/generator/rust.rs index 516ac1e1b..4f4b4e5e3 100644 --- a/sixtyfps_compiler/generator/rust.rs +++ b/sixtyfps_compiler/generator/rust.rs @@ -1,7 +1,7 @@ /*! module for the Rust code generator */ -use crate::diagnostics::{CompilerDiagnostic, FileDiagnostics}; +use crate::diagnostics::{BuildDiagnostics, CompilerDiagnostic}; use crate::expression_tree::{Expression, NamedReference, OperatorClass, Path}; use crate::object_tree::{Component, ElementRc}; use crate::parser::Spanned; @@ -38,7 +38,7 @@ fn rust_type( /// Generate the rust code for the given component. /// /// Fill the diagnostic in case of error. -pub fn generate(component: &Rc, diag: &mut FileDiagnostics) -> Option { +pub fn generate(component: &Rc, diag: &mut BuildDiagnostics) -> Option { let mut extra_components = vec![]; let mut declared_property_vars = vec![]; let mut declared_property_types = vec![]; @@ -66,7 +66,7 @@ pub fn generate(component: &Rc, diag: &mut FileDiagnostics) -> Option let rust_property_type = rust_type(&property_decl.property_type, &property_decl.type_location) .unwrap_or_else(|err| { - diag.push_compiler_error(err); + diag.push_internal_error(err.into()); quote!().into() }); declared_property_types.push(rust_property_type.clone()); @@ -141,7 +141,7 @@ pub fn generate(component: &Rc, diag: &mut FileDiagnostics) -> Option &item.node.as_ref().map_or_else(Default::default, |n| n.span()), ) .unwrap_or_else(|err| { - diag.push_compiler_error(err); + diag.push_internal_error(err.into()); quote!().into() }); @@ -291,7 +291,7 @@ pub fn generate(component: &Rc, diag: &mut FileDiagnostics) -> Option .map_or_else(Default::default, |n| n.span()), ) .unwrap_or_else(|err| { - diag.push_compiler_error(err); + diag.push_internal_error(err.into()); quote!().into() }), ); diff --git a/sixtyfps_compiler/lib.rs b/sixtyfps_compiler/lib.rs index 701aa0b4b..3ccae2a9b 100644 --- a/sixtyfps_compiler/lib.rs +++ b/sixtyfps_compiler/lib.rs @@ -53,14 +53,17 @@ pub fn compile_syntax_node>( doc_node: DocNode, mut diagnostics: diagnostics::FileDiagnostics, compiler_config: &CompilerConfiguration, -) -> (object_tree::Document, diagnostics::FileDiagnostics) { +) -> (object_tree::Document, diagnostics::BuildDiagnostics) { let type_registry = typeregister::TypeRegister::builtin(); let doc = crate::object_tree::Document::from_node(doc_node.into(), &mut diagnostics, &type_registry); run_passes(&doc, &mut diagnostics, compiler_config); - (doc, diagnostics) + let mut all_diagnostics = diagnostics::BuildDiagnostics::default(); + all_diagnostics.add(diagnostics); + + (doc, all_diagnostics) } pub fn run_passes( diff --git a/sixtyfps_runtime/interpreter/dynamic_component.rs b/sixtyfps_runtime/interpreter/dynamic_component.rs index 5d1303c89..06620e04f 100644 --- a/sixtyfps_runtime/interpreter/dynamic_component.rs +++ b/sixtyfps_runtime/interpreter/dynamic_component.rs @@ -222,7 +222,7 @@ fn rtti_for Result, sixtyfps_compilerlib::diagnostics::FileDiagnostics> { +) -> Result, sixtyfps_compilerlib::diagnostics::BuildDiagnostics> { let (syntax_node, diag) = parser::parse(source, Some(path)); let compiler_config = CompilerConfiguration::default(); let (tree, diag) = compile_syntax_node(syntax_node, diag, &compiler_config);