Add aggregating BuildDiagnostics

This type aggregates different per-file diagnostics into a similar interface,
in preparation for reporting diagnostics from multiple source files.
This commit is contained in:
Simon Hausmann 2020-07-17 12:10:25 +02:00
parent 5dc53f0f79
commit ada5f68908
6 changed files with 115 additions and 23 deletions

View file

@ -1,3 +1,6 @@
use std::collections::HashMap;
use std::path::PathBuf;
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct Span { pub struct Span {
pub offset: usize, pub offset: usize,
@ -206,3 +209,86 @@ impl quote::ToTokens for FileDiagnostics {
quote!(#(#diags)*).to_tokens(tokens); quote!(#(#diags)*).to_tokens(tokens);
} }
} }
#[derive(Default)]
pub struct BuildDiagnostics {
per_input_file_diagnostics: HashMap<PathBuf, FileDiagnostics>,
internal_errors: Option<FileDiagnostics>,
}
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<Item = &FileDiagnostics> {
self.per_input_file_diagnostics.values().chain(self.internal_errors.iter())
}
fn into_iter(self) -> impl Iterator<Item = FileDiagnostics> {
self.per_input_file_diagnostics
.into_iter()
.map(|(_, diag)| diag)
.chain(self.internal_errors.into_iter())
}
fn iter_mut(&mut self) -> impl Iterator<Item = &mut FileDiagnostics> {
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<String> {
self.iter()
.flat_map(|diag| {
diag.to_string_vec()
.iter()
.map(|err| format!("{}: {}", diag.current_path.to_string_lossy(), err))
.collect::<Vec<_>>()
})
.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))
}
}

View file

@ -4,7 +4,7 @@ The module responsible for the code generation.
There is one sub module for every language There is one sub module for every language
*/ */
use crate::diagnostics::FileDiagnostics; use crate::diagnostics::BuildDiagnostics;
use crate::object_tree::{Component, ElementRc}; use crate::object_tree::{Component, ElementRc};
use std::rc::Rc; use std::rc::Rc;
@ -51,7 +51,7 @@ pub fn generate(
format: OutputFormat, format: OutputFormat,
destination: &mut impl std::io::Write, destination: &mut impl std::io::Write,
component: &Rc<Component>, component: &Rc<Component>,
diag: &mut FileDiagnostics, diag: &mut BuildDiagnostics,
) -> std::io::Result<()> { ) -> std::io::Result<()> {
#![allow(unused_variables)] #![allow(unused_variables)]
#![allow(unreachable_code)] #![allow(unreachable_code)]

View file

@ -127,7 +127,7 @@ mod cpp_ast {
} }
} }
use crate::diagnostics::{CompilerDiagnostic, FileDiagnostics}; use crate::diagnostics::{BuildDiagnostics, CompilerDiagnostic};
use crate::expression_tree::Expression; use crate::expression_tree::Expression;
use crate::object_tree::{Component, Element, ElementRc, RepeatedElementInfo}; use crate::object_tree::{Component, Element, ElementRc, RepeatedElementInfo};
use crate::parser::Spanned; use crate::parser::Spanned;
@ -360,7 +360,7 @@ fn handle_repeater(
/// Returns the text of the C++ code produced by the given root component /// Returns the text of the C++ code produced by the given root component
pub fn generate( pub fn generate(
component: &Rc<Component>, component: &Rc<Component>,
diag: &mut FileDiagnostics, diag: &mut BuildDiagnostics,
) -> Option<impl std::fmt::Display> { ) -> Option<impl std::fmt::Display> {
let mut file = File::default(); let mut file = File::default();
@ -377,7 +377,7 @@ pub fn generate(
} }
} }
fn generate_component(file: &mut File, component: &Rc<Component>, diag: &mut FileDiagnostics) { fn generate_component(file: &mut File, component: &Rc<Component>, diag: &mut BuildDiagnostics) {
let component_id = component_id(component); let component_id = component_id(component);
let mut component_struct = Struct { name: component_id.clone(), ..Default::default() }; let mut component_struct = Struct { name: component_id.clone(), ..Default::default() };
@ -405,7 +405,7 @@ fn generate_component(file: &mut File, component: &Rc<Component>, diag: &mut Fil
span: property_decl.type_location.clone(), span: property_decl.type_location.clone(),
}; };
diag.push_compiler_error(err); diag.push_internal_error(err.into());
"".into() "".into()
}); });
@ -454,7 +454,8 @@ fn generate_component(file: &mut File, component: &Rc<Component>, diag: &mut Fil
.ty() .ty()
.cpp_type() .cpp_type()
.unwrap_or_else(|| { .unwrap_or_else(|| {
diag.push_compiler_error(CompilerDiagnostic { diag.push_internal_error(
CompilerDiagnostic {
message: "Cannot map property type to C++".into(), message: "Cannot map property type to C++".into(),
span: parent_element span: parent_element
.borrow() .borrow()
@ -462,7 +463,9 @@ fn generate_component(file: &mut File, component: &Rc<Component>, diag: &mut Fil
.as_ref() .as_ref()
.map(|n| n.span()) .map(|n| n.span())
.unwrap_or_default(), .unwrap_or_default(),
}); }
.into(),
);
String::default() String::default()
}) })
.to_owned(); .to_owned();

View file

@ -1,7 +1,7 @@
/*! module for the Rust code generator /*! 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::expression_tree::{Expression, NamedReference, OperatorClass, Path};
use crate::object_tree::{Component, ElementRc}; use crate::object_tree::{Component, ElementRc};
use crate::parser::Spanned; use crate::parser::Spanned;
@ -38,7 +38,7 @@ fn rust_type(
/// Generate the rust code for the given component. /// Generate the rust code for the given component.
/// ///
/// Fill the diagnostic in case of error. /// Fill the diagnostic in case of error.
pub fn generate(component: &Rc<Component>, diag: &mut FileDiagnostics) -> Option<TokenStream> { pub fn generate(component: &Rc<Component>, diag: &mut BuildDiagnostics) -> Option<TokenStream> {
let mut extra_components = vec![]; let mut extra_components = vec![];
let mut declared_property_vars = vec![]; let mut declared_property_vars = vec![];
let mut declared_property_types = vec![]; let mut declared_property_types = vec![];
@ -66,7 +66,7 @@ pub fn generate(component: &Rc<Component>, diag: &mut FileDiagnostics) -> Option
let rust_property_type = let rust_property_type =
rust_type(&property_decl.property_type, &property_decl.type_location) rust_type(&property_decl.property_type, &property_decl.type_location)
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
diag.push_compiler_error(err); diag.push_internal_error(err.into());
quote!().into() quote!().into()
}); });
declared_property_types.push(rust_property_type.clone()); declared_property_types.push(rust_property_type.clone());
@ -141,7 +141,7 @@ pub fn generate(component: &Rc<Component>, diag: &mut FileDiagnostics) -> Option
&item.node.as_ref().map_or_else(Default::default, |n| n.span()), &item.node.as_ref().map_or_else(Default::default, |n| n.span()),
) )
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
diag.push_compiler_error(err); diag.push_internal_error(err.into());
quote!().into() quote!().into()
}); });
@ -291,7 +291,7 @@ pub fn generate(component: &Rc<Component>, diag: &mut FileDiagnostics) -> Option
.map_or_else(Default::default, |n| n.span()), .map_or_else(Default::default, |n| n.span()),
) )
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
diag.push_compiler_error(err); diag.push_internal_error(err.into());
quote!().into() quote!().into()
}), }),
); );

View file

@ -53,14 +53,17 @@ pub fn compile_syntax_node<DocNode: Into<parser::syntax_nodes::Document>>(
doc_node: DocNode, doc_node: DocNode,
mut diagnostics: diagnostics::FileDiagnostics, mut diagnostics: diagnostics::FileDiagnostics,
compiler_config: &CompilerConfiguration, compiler_config: &CompilerConfiguration,
) -> (object_tree::Document, diagnostics::FileDiagnostics) { ) -> (object_tree::Document, diagnostics::BuildDiagnostics) {
let type_registry = typeregister::TypeRegister::builtin(); let type_registry = typeregister::TypeRegister::builtin();
let doc = let doc =
crate::object_tree::Document::from_node(doc_node.into(), &mut diagnostics, &type_registry); crate::object_tree::Document::from_node(doc_node.into(), &mut diagnostics, &type_registry);
run_passes(&doc, &mut diagnostics, compiler_config); 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( pub fn run_passes(

View file

@ -222,7 +222,7 @@ fn rtti_for<T: 'static + Default + rtti::BuiltinItem + vtable::HasStaticVTable<I
pub fn load( pub fn load(
source: String, source: String,
path: &std::path::Path, path: &std::path::Path,
) -> Result<Rc<ComponentDescription>, sixtyfps_compilerlib::diagnostics::FileDiagnostics> { ) -> Result<Rc<ComponentDescription>, sixtyfps_compilerlib::diagnostics::BuildDiagnostics> {
let (syntax_node, diag) = parser::parse(source, Some(path)); let (syntax_node, diag) = parser::parse(source, Some(path));
let compiler_config = CompilerConfiguration::default(); let compiler_config = CompilerConfiguration::default();
let (tree, diag) = compile_syntax_node(syntax_node, diag, &compiler_config); let (tree, diag) = compile_syntax_node(syntax_node, diag, &compiler_config);