Refactor diagnostic: Merge BuildDiagnostic and FileDiagnostic

This commit is contained in:
Olivier Goffart 2021-03-12 19:31:18 +01:00
parent 11e0be5130
commit 730b1ccff2
19 changed files with 313 additions and 480 deletions

View file

@ -54,6 +54,8 @@ use std::env;
use std::io::Write; use std::io::Write;
use std::path::Path; use std::path::Path;
use sixtyfps_compilerlib::diagnostics::BuildDiagnostics;
/// The structure for configuring aspects of the compilation of `.60` markup files to Rust. /// The structure for configuring aspects of the compilation of `.60` markup files to Rust.
pub struct CompilerConfiguration { pub struct CompilerConfiguration {
config: sixtyfps_compilerlib::CompilerConfiguration, config: sixtyfps_compilerlib::CompilerConfiguration,
@ -180,8 +182,9 @@ pub fn compile_with_config(
let path = Path::new(&env::var_os("CARGO_MANIFEST_DIR").ok_or(CompileError::NotRunViaCargo)?) let path = Path::new(&env::var_os("CARGO_MANIFEST_DIR").ok_or(CompileError::NotRunViaCargo)?)
.join(path.as_ref()); .join(path.as_ref());
let (syntax_node, diag) = let mut diag = BuildDiagnostics::default();
sixtyfps_compilerlib::parser::parse_file(&path).map_err(CompileError::LoadError)?; let syntax_node = sixtyfps_compilerlib::parser::parse_file(&path, &mut diag)
.map_err(CompileError::LoadError)?;
if diag.has_error() { if diag.has_error() {
let vec = diag.to_string_vec(); let vec = diag.to_string_vec();
@ -222,7 +225,7 @@ pub fn compile_with_config(
let mut code_formater = CodeFormatter { indentation: 0, in_string: false, sink: file }; let mut code_formater = CodeFormatter { indentation: 0, in_string: false, sink: file };
let generated = match sixtyfps_compilerlib::generator::rust::generate(&doc, &mut diag) { let generated = match sixtyfps_compilerlib::generator::rust::generate(&doc, &mut diag) {
Some(code) => { Some(code) => {
for x in diag.files() { for x in &diag.all_loaded_files {
if x.is_absolute() { if x.is_absolute() {
println!("cargo:rerun-if-changed={}", x.display()); println!("cargo:rerun-if-changed={}", x.display());
} }

View file

@ -19,7 +19,8 @@ You should use the `sixtyfps` crate instead.
extern crate proc_macro; extern crate proc_macro;
use proc_macro::{Spacing, TokenStream, TokenTree}; use proc_macro::{Spacing, TokenStream, TokenTree};
use quote::{quote, ToTokens}; use quote::quote;
use sixtyfps_compilerlib::diagnostics::BuildDiagnostics;
use sixtyfps_compilerlib::parser::SyntaxKind; use sixtyfps_compilerlib::parser::SyntaxKind;
use sixtyfps_compilerlib::*; use sixtyfps_compilerlib::*;
@ -319,10 +320,10 @@ pub fn sixtyfps(stream: TokenStream) -> TokenStream {
let mut tokens = vec![]; let mut tokens = vec![];
fill_token_vec(token_iter, &mut tokens); fill_token_vec(token_iter, &mut tokens);
let (syntax_node, mut diag) = parser::parse_tokens(tokens.clone()); let mut diag = BuildDiagnostics::default();
let syntax_node = parser::parse_tokens(tokens.clone(), &mut diag);
if diag.has_error() { if diag.has_error() {
diag.map_offsets_to_span(&tokens); return diag.report_macro_diagnostic(&tokens);
return diag.into_token_stream().into();
} }
let source_file = if let Some(cargo_manifest) = std::env::var_os("CARGO_MANIFEST_DIR") { let source_file = if let Some(cargo_manifest) = std::env::var_os("CARGO_MANIFEST_DIR") {
@ -343,14 +344,15 @@ pub fn sixtyfps(stream: TokenStream) -> TokenStream {
spin_on::spin_on(compile_syntax_node(syntax_node, diag, compiler_config)); spin_on::spin_on(compile_syntax_node(syntax_node, diag, compiler_config));
//println!("{:#?}", tree); //println!("{:#?}", tree);
if diag.has_error() { if diag.has_error() {
return report_diagnostics(diag, &tokens); return diag.report_macro_diagnostic(&tokens);
} }
let mut result = generator::rust::generate(&root_component, &mut diag); let mut result = generator::rust::generate(&root_component, &mut diag);
// Make sure to recompile if any of the external files changes // Make sure to recompile if any of the external files changes
let reload = diag let reload = diag
.files() .all_loaded_files
.iter()
.filter(|path| path.is_absolute() && !path.ends_with("Cargo.toml")) .filter(|path| path.is_absolute() && !path.ends_with("Cargo.toml"))
.filter_map(|p| p.to_str()) .filter_map(|p| p.to_str())
.map(|p| quote! {const _ : &'static [u8] = ::core::include_bytes!(#p);}); .map(|p| quote! {const _ : &'static [u8] = ::core::include_bytes!(#p);});
@ -359,27 +361,6 @@ pub fn sixtyfps(stream: TokenStream) -> TokenStream {
x.extend(quote! {const _ : Option<&'static str> = ::core::option_env!("SIXTYFPS_STYLE");}); x.extend(quote! {const _ : Option<&'static str> = ::core::option_env!("SIXTYFPS_STYLE");});
}); });
let diags = report_diagnostics(diag, &tokens); let diags = diag.report_macro_diagnostic(&tokens);
result.map_or(diags, |r| r.into()) result.map_or(diags, |r| r.into())
} }
fn report_diagnostics(
diag: diagnostics::BuildDiagnostics,
span_map: &[parser::Token],
) -> TokenStream {
let mut result = TokenStream::new();
let mut needs_error = diag.has_error();
for mut file_diag in diag.into_iter() {
if file_diag.current_path.path() == std::path::PathBuf::default() {
file_diag.map_offsets_to_span(span_map);
needs_error &= !file_diag.has_error();
result.extend(TokenStream::from(file_diag.into_token_stream()))
} else {
file_diag.print();
}
}
if needs_error {
result.extend(TokenStream::from(quote!(compile_error! { "Error occured" })))
}
result
}

View file

@ -77,37 +77,33 @@ pub async fn compile_from_string(
let level_key = JsValue::from_str("level"); let level_key = JsValue::from_str("level");
let mut error_as_string = String::new(); let mut error_as_string = String::new();
let array = js_sys::Array::new(); let array = js_sys::Array::new();
for diag in errors.into_iter() { for d in errors.into_iter() {
let filename_js = JsValue::from_str(&diag.current_path.display().to_string()); let filename = d
for d in &diag.inner { .span
if !error_as_string.is_empty() { .source_file
error_as_string.push_str("\n"); .as_ref()
} .map_or(String::new(), |sf| sf.path().to_string_lossy().into());
use std::fmt::Write;
let (line, column) = d.line_column(&diag); let filename_js = JsValue::from_str(&filename);
write!(&mut error_as_string, "{}:{}:{}", diag.current_path.display(), line, d)
.unwrap(); if !error_as_string.is_empty() {
let error_obj = js_sys::Object::new(); error_as_string.push_str("\n");
js_sys::Reflect::set(
&error_obj,
&message_key,
&JsValue::from_str(&d.to_string()),
)?;
js_sys::Reflect::set(&error_obj, &line_key, &JsValue::from_f64(line as f64))?;
js_sys::Reflect::set(
&error_obj,
&column_key,
&JsValue::from_f64(column as f64),
)?;
js_sys::Reflect::set(&error_obj, &file_key, &filename_js)?;
js_sys::Reflect::set(
&error_obj,
&level_key,
&JsValue::from_f64(d.level() as i8 as f64),
)?;
array.push(&error_obj);
} }
use std::fmt::Write;
let (line, column) = d.line_column();
write!(&mut error_as_string, "{}:{}:{}", filename, line, d).unwrap();
let error_obj = js_sys::Object::new();
js_sys::Reflect::set(&error_obj, &message_key, &JsValue::from_str(&d.message))?;
js_sys::Reflect::set(&error_obj, &line_key, &JsValue::from_f64(line as f64))?;
js_sys::Reflect::set(&error_obj, &column_key, &JsValue::from_f64(column as f64))?;
js_sys::Reflect::set(&error_obj, &file_key, &filename_js)?;
js_sys::Reflect::set(
&error_obj,
&level_key,
&JsValue::from_f64(d.level() as i8 as f64),
)?;
array.push(&error_obj);
} }
let error = js_sys::Error::new(&error_as_string); let error = js_sys::Error::new(&error_as_string);

View file

@ -11,12 +11,12 @@ use std::collections::HashMap;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::rc::Rc; use std::rc::Rc;
#[derive(Debug, Clone)]
/// Span represent an error location within a file. /// Span represent an error location within a file.
/// ///
/// Currently, it is just an offset in byte within the file. /// Currently, it is just an offset in byte within the file.
/// ///
/// When the `proc_macro_span` feature is enabled, it may also hold a proc_maco span. /// When the `proc_macro_span` feature is enabled, it may also hold a proc_maco span.
#[derive(Debug, Clone)]
pub struct Span { pub struct Span {
pub offset: usize, pub offset: usize,
#[cfg(feature = "proc_macro_span")] #[cfg(feature = "proc_macro_span")]
@ -108,14 +108,8 @@ pub type SourceFile = Rc<SourceFileInner>;
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct SourceLocation { pub struct SourceLocation {
source_file: Option<SourceFile>, pub source_file: Option<SourceFile>,
span: Span, pub span: Span,
}
impl From<SyntaxNodeWithSourceFile> for SourceLocation {
fn from(node: SyntaxNodeWithSourceFile) -> Self {
SourceLocation { span: node.span(), source_file: node.source_file }
}
} }
impl Spanned for SourceLocation { impl Spanned for SourceLocation {
@ -161,39 +155,23 @@ impl From<DiagnosticLevel> for codemap_diagnostic::Level {
} }
} }
#[derive(thiserror::Error, Default, Debug)] #[derive(Debug, Clone)]
#[error("{message}")] pub struct Diagnostic {
pub struct CompilerDiagnostic {
pub message: String, pub message: String,
pub span: SourceLocation, pub span: SourceLocation,
pub level: DiagnosticLevel, pub level: DiagnosticLevel,
} }
#[derive(thiserror::Error, Debug)]
pub enum Diagnostic {
#[error(transparent)]
FileLoadError(#[from] std::io::Error),
#[error(transparent)]
CompilerDiagnostic(#[from] CompilerDiagnostic),
}
impl Diagnostic { impl Diagnostic {
/// Return the level for this diagnostic /// Return the level for this diagnostic
pub fn level(&self) -> DiagnosticLevel { pub fn level(&self) -> DiagnosticLevel {
match self { self.level
Diagnostic::CompilerDiagnostic(e) => e.level,
_ => DiagnosticLevel::Error,
}
} }
/// Returns a tuple with the line (starting at 1) and column number (starting at 0) /// Returns a tuple with the line (starting at 1) and column number (starting at 0)
pub fn line_column(&self) -> (usize, usize) { pub fn line_column(&self) -> (usize, usize) {
let source_location = match self { let offset = self.span.span.offset;
Diagnostic::CompilerDiagnostic(e) => &e.span, let line_offsets = match &self.span.source_file {
_ => return (0, 0),
};
let offset = source_location.span.offset;
let line_offsets = match &source_location.source_file {
None => return (0, 0), None => return (0, 0),
Some(sl) => sl.line_offsets(), Some(sl) => sl.line_offsets(),
}; };
@ -210,16 +188,29 @@ impl Diagnostic {
} }
} }
/// This structure holds all the diagnostics for a given files impl std::fmt::Display for Diagnostic {
#[derive(Default, Debug)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
pub struct FileDiagnostics { if let Some(sf) = self.span.source_file() {
/// List of diagnostics related to this file let (line, _) = self.line_column();
pub inner: Vec<Diagnostic>, write!(f, "{}:{}: {}", sf.path.display(), line, self.message)
/// file path } else {
pub current_path: SourceFile, write!(f, "{}", self.message)
}
}
} }
impl IntoIterator for FileDiagnostics { #[derive(Default)]
pub struct BuildDiagnostics {
inner: Vec<Diagnostic>,
/// This is the list of all loaded files (with or without diagnostic)
/// does not include the main file.
/// FIXME: this doesn't really belong in the diagnostics, it should be somehow returned in another way
/// (maybe in a compilation state that include the diagnostics?)
pub all_loaded_files: Vec<PathBuf>,
}
impl IntoIterator for BuildDiagnostics {
type Item = Diagnostic; type Item = Diagnostic;
type IntoIter = <Vec<Diagnostic> as IntoIterator>::IntoIter; type IntoIter = <Vec<Diagnostic> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
@ -227,23 +218,22 @@ impl IntoIterator for FileDiagnostics {
} }
} }
impl FileDiagnostics { impl BuildDiagnostics {
pub fn push_diagnostic_with_span( pub fn push_diagnostic_with_span(
&mut self, &mut self,
message: String, message: String,
span: Span, span: SourceLocation,
level: DiagnosticLevel, level: DiagnosticLevel,
) { ) {
let span = SourceLocation { source_file: Some(self.current_path.clone()), span }; self.inner.push(Diagnostic { message, span, level }.into());
self.inner.push(CompilerDiagnostic { message, span, level }.into());
} }
pub fn push_error_with_span(&mut self, message: String, span: Span) { pub fn push_error_with_span(&mut self, message: String, span: SourceLocation) {
self.push_diagnostic_with_span(message, span, DiagnosticLevel::Error) self.push_diagnostic_with_span(message, span, DiagnosticLevel::Error)
} }
pub fn push_error(&mut self, message: String, source: &dyn Spanned) { pub fn push_error(&mut self, message: String, source: &dyn Spanned) {
self.push_error_with_span(message, source.span()); self.push_error_with_span(message, source.to_source_location());
} }
pub fn push_compiler_error(&mut self, error: CompilerDiagnostic) { pub fn push_compiler_error(&mut self, error: Diagnostic) {
self.inner.push(error.into()); self.inner.push(error.into());
} }
@ -258,17 +248,14 @@ impl FileDiagnostics {
"The property '{}' has been deprecated. Please use '{}' instead", "The property '{}' has been deprecated. Please use '{}' instead",
old_property, new_property old_property, new_property
), ),
source.span(), source.to_source_location(),
crate::diagnostics::DiagnosticLevel::Warning, crate::diagnostics::DiagnosticLevel::Warning,
) )
} }
/// Return true if there is at least one compilation error for this file /// Return true if there is at least one compilation error for this file
pub fn has_error(&self) -> bool { pub fn has_error(&self) -> bool {
self.inner.iter().any(|diag| match diag { self.inner.iter().any(|diag| diag.level == DiagnosticLevel::Error)
Diagnostic::FileLoadError(_) => true,
Diagnostic::CompilerDiagnostic(diag) => matches!(diag.level, DiagnosticLevel::Error),
})
} }
/// Return true if there are no diagnostics (warnings or errors); false otherwise. /// Return true if there are no diagnostics (warnings or errors); false otherwise.
@ -280,6 +267,7 @@ impl FileDiagnostics {
fn call_diagnostics<'a, Output>( fn call_diagnostics<'a, Output>(
self, self,
output: &'a mut Output, output: &'a mut Output,
mut handle_no_source: Option<&mut dyn FnMut(Diagnostic)>,
emitter_factory: impl for<'b> FnOnce( emitter_factory: impl for<'b> FnOnce(
&'b mut Output, &'b mut Output,
Option<&'b codemap::CodeMap>, Option<&'b codemap::CodeMap>,
@ -290,42 +278,43 @@ impl FileDiagnostics {
} }
let mut codemap = codemap::CodeMap::new(); let mut codemap = codemap::CodeMap::new();
let internal_errors = self.current_path.source.is_none(); let mut codemap_files = HashMap::new();
let file = codemap.add_file(
self.current_path.path.to_string_lossy().to_string(),
self.current_path.source.clone().unwrap_or_default(),
);
let file_span = file.span;
let diags: Vec<_> = self let diags: Vec<_> = self
.inner .inner
.into_iter() .into_iter()
.map(|diagnostic| match diagnostic { .filter_map(|d| {
Diagnostic::CompilerDiagnostic(CompilerDiagnostic { message, span, level }) => { let spans = if let Some(sf) = &d.span.source_file {
let spans = if !internal_errors && span.span.is_valid() { if let Some(ref mut handle_no_source) = handle_no_source {
let s = codemap_diagnostic::SpanLabel { if sf.source.is_none() {
span: file_span handle_no_source(d);
.subspan(span.span.offset as u64, span.span.offset as u64), return None;
style: codemap_diagnostic::SpanStyle::Primary, }
label: None,
};
vec![s]
} else {
vec![]
};
codemap_diagnostic::Diagnostic {
level: level.into(),
message,
code: None,
spans,
} }
} let path: String = sf.path.to_string_lossy().into();
Diagnostic::FileLoadError(err) => codemap_diagnostic::Diagnostic { let file = codemap_files.entry(path).or_insert_with(|| {
level: codemap_diagnostic::Level::Error, codemap.add_file(
message: err.to_string(), sf.path.to_string_lossy().into(),
sf.source.clone().unwrap_or_default(),
)
});
let file_span = file.span;
let s = codemap_diagnostic::SpanLabel {
span: file_span
.subspan(d.span.span.offset as u64, d.span.span.offset as u64),
style: codemap_diagnostic::SpanStyle::Primary,
label: None,
};
vec![s]
} else {
vec![]
};
Some(codemap_diagnostic::Diagnostic {
level: d.level.into(),
message: d.message,
code: None, code: None,
spans: vec![], spans,
}, })
}) })
.collect(); .collect();
@ -336,7 +325,7 @@ impl FileDiagnostics {
#[cfg(feature = "display-diagnostics")] #[cfg(feature = "display-diagnostics")]
/// Print the diagnostics on the console /// Print the diagnostics on the console
pub fn print(self) { pub fn print(self) {
self.call_diagnostics(&mut (), |_, codemap| { self.call_diagnostics(&mut (), None, |_, codemap| {
codemap_diagnostic::Emitter::stderr(codemap_diagnostic::ColorConfig::Always, codemap) codemap_diagnostic::Emitter::stderr(codemap_diagnostic::ColorConfig::Always, codemap)
}); });
} }
@ -345,7 +334,7 @@ impl FileDiagnostics {
/// Print into a string /// Print into a string
pub fn diagnostics_as_string(self) -> String { pub fn diagnostics_as_string(self) -> String {
let mut output = Vec::new(); let mut output = Vec::new();
self.call_diagnostics(&mut output, |output, codemap| { self.call_diagnostics(&mut output, None, |output, codemap| {
codemap_diagnostic::Emitter::vec(output, codemap) codemap_diagnostic::Emitter::vec(output, codemap)
}); });
@ -354,182 +343,79 @@ impl FileDiagnostics {
) )
} }
#[cfg(feature = "proc_macro_span")] #[cfg(all(feature = "proc_macro_span", feature = "display-diagnostics"))]
/// Will convert the diagnostics that only have offsets to the actual proc_macro::Span /// Will convert the diagnostics that only have offsets to the actual proc_macro::Span
pub fn map_offsets_to_span(&mut self, span_map: &[crate::parser::Token]) { pub fn report_macro_diagnostic(
for d in &mut self.inner { self,
if let Diagnostic::CompilerDiagnostic(d) = d { span_map: &[crate::parser::Token],
if d.span.span.span.is_none() { ) -> proc_macro::TokenStream {
let mut result = proc_macro::TokenStream::default();
let mut needs_error = self.has_error();
self.call_diagnostics(
&mut (),
Some(&mut |diag| {
let span = diag.span.span.span.clone().or_else(|| {
//let pos = //let pos =
//span_map.binary_search_by_key(d.span.offset, |x| x.0).unwrap_or_else(|x| x); //span_map.binary_search_by_key(d.span.offset, |x| x.0).unwrap_or_else(|x| x);
//d.span.span = span_map.get(pos).as_ref().map(|x| x.1); //d.span.span = span_map.get(pos).as_ref().map(|x| x.1);
let mut offset = 0; let mut offset = 0;
d.span.span.span = span_map.iter().find_map(|t| { span_map.iter().find_map(|t| {
if d.span.span.offset <= offset { if diag.span.span.offset <= offset {
t.span t.span
} else { } else {
offset += t.text.len(); offset += t.text.len();
None None
} }
}); })
});
let message = &diag.message;
match diag.level {
DiagnosticLevel::Error => {
needs_error = false;
result.extend(proc_macro::TokenStream::from(if let Some(span) = span {
quote::quote_spanned!(span.into() => compile_error!{ #message })
} else {
quote::quote!(compile_error! { #message })
}));
}
// FIXME: find a way to report warnings.
DiagnosticLevel::Warning => (),
} }
} }),
|_, codemap| {
codemap_diagnostic::Emitter::stderr(
codemap_diagnostic::ColorConfig::Always,
codemap,
)
},
);
if needs_error {
result.extend(proc_macro::TokenStream::from(quote::quote!(
compile_error! { "Error occured" }
)))
} }
result
} }
pub fn to_string_vec(&self) -> Vec<String> { pub fn to_string_vec(&self) -> Vec<String> {
self.inner.iter().map(|d| d.to_string()).collect() self.inner.iter().map(|d| d.to_string()).collect()
} }
pub fn new_from_error(path: std::path::PathBuf, err: std::io::Error) -> Self {
Self { inner: vec![err.into()], current_path: SourceFileInner::from_path(path) }
}
}
#[cfg(feature = "proc_macro_span")]
use quote::quote;
use crate::parser::SyntaxNodeWithSourceFile;
#[cfg(feature = "proc_macro_span")]
impl quote::ToTokens for FileDiagnostics {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let diags: Vec<_> = self
.inner
.iter()
.filter_map(|diag| match diag {
Diagnostic::CompilerDiagnostic(CompilerDiagnostic {
level,
message,
span,
}) => {
match level {
DiagnosticLevel::Error => {
if let Some(span) = span.span.span {
Some(quote::quote_spanned!(span.into() => compile_error!{ #message }))
} else {
Some(quote!(compile_error! { #message }))
}
}
// FIXME: find a way to report warnings.
DiagnosticLevel::Warning => None
}
},
_ => None,
})
.collect();
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.path) {
Some(existing_diags) => existing_diags.inner.extend(diagnostics.inner),
None => {
self.per_input_file_diagnostics
.insert(diagnostics.current_path.path.clone(), diagnostics);
}
}
}
fn file_diagnostics(&mut self, path: &Path) -> &mut FileDiagnostics {
self.per_input_file_diagnostics.entry(path.into()).or_insert_with(|| FileDiagnostics {
current_path: Rc::new(SourceFileInner { path: path.into(), ..Default::default() }),
..Default::default()
})
}
pub fn push_diagnostic( pub fn push_diagnostic(
&mut self, &mut self,
message: String, message: String,
source: &dyn Spanned, source: &dyn Spanned,
level: DiagnosticLevel, level: DiagnosticLevel,
) { ) {
match source.source_file() { self.push_diagnostic_with_span(message, source.to_source_location(), level)
Some(source_file) => self
.file_diagnostics(source_file.path())
.push_diagnostic_with_span(message, source.span(), level),
None => self.push_internal_error(
CompilerDiagnostic { message, span: source.to_source_location(), level }.into(),
),
}
}
pub fn push_error(&mut self, message: String, source: &dyn Spanned) {
self.push_diagnostic(message, source, DiagnosticLevel::Error)
} }
pub fn push_internal_error(&mut self, err: Diagnostic) { pub fn push_internal_error(&mut self, err: Diagnostic) {
self.internal_errors self.inner.push(err)
.get_or_insert_with(|| FileDiagnostics {
current_path: Rc::new(SourceFileInner {
path: "[internal error]".into(),
..Default::default()
}),
..Default::default()
})
.inner
.push(err)
} }
pub fn push_property_deprecation_warning( pub fn iter(&self) -> impl Iterator<Item = &Diagnostic> {
&mut self, self.inner.iter()
old_property: &str,
new_property: &str,
source: &impl Spanned,
) {
self.file_diagnostics(
source.source_file().expect("deprecations cannot be created as internal errors").path(),
)
.push_property_deprecation_warning(old_property, new_property, source);
}
fn iter(&self) -> impl Iterator<Item = &FileDiagnostics> {
self.per_input_file_diagnostics.values().chain(self.internal_errors.iter())
}
pub fn into_iter(self) -> impl Iterator<Item = FileDiagnostics> {
self.per_input_file_diagnostics
.into_iter()
.map(|(_, diag)| diag)
.chain(self.internal_errors.into_iter())
}
#[cfg(feature = "proc_macro_span")]
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.path().to_string_lossy(), err))
.collect::<Vec<_>>()
})
.collect()
}
#[cfg(feature = "display-diagnostics")]
pub fn print(self) {
self.into_iter().for_each(|diag| diag.print());
}
#[cfg(feature = "display-diagnostics")]
pub fn diagnostics_as_string(self) -> String {
self.into_iter().map(|diag| diag.diagnostics_as_string()).collect::<Vec<_>>().join("\n")
} }
#[cfg(feature = "display-diagnostics")] #[cfg(feature = "display-diagnostics")]
@ -549,22 +435,4 @@ impl BuildDiagnostics {
std::process::exit(-1); std::process::exit(-1);
} }
} }
#[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))
}
/// Return an iterator containing all the files
pub fn files(&self) -> impl Iterator<Item = &'_ std::path::Path> + '_ {
self.per_input_file_diagnostics.keys().map(|x| x.as_path())
}
}
impl Extend<FileDiagnostics> for BuildDiagnostics {
fn extend<T: IntoIterator<Item = FileDiagnostics>>(&mut self, iter: T) {
for diag in iter {
self.add(diag)
}
}
} }

View file

@ -1022,7 +1022,7 @@ impl BindingExpression {
pub fn new_uncompiled(node: SyntaxNodeWithSourceFile) -> Self { pub fn new_uncompiled(node: SyntaxNodeWithSourceFile) -> Self {
Self { Self {
expression: Expression::Uncompiled(node.clone()), expression: Expression::Uncompiled(node.clone()),
span: Some(node.into()), span: Some(node.to_source_location()),
priority: 0, priority: 0,
} }
} }

View file

@ -216,7 +216,7 @@ mod cpp_ast {
} }
} }
use crate::diagnostics::{BuildDiagnostics, CompilerDiagnostic, DiagnosticLevel, Spanned}; use crate::diagnostics::{BuildDiagnostics, DiagnosticLevel, Spanned};
use crate::expression_tree::{ use crate::expression_tree::{
BindingExpression, BuiltinFunction, EasingCurve, Expression, NamedReference, BindingExpression, BuiltinFunction, EasingCurve, Expression, NamedReference,
}; };
@ -263,7 +263,7 @@ impl CppType for Type {
fn get_cpp_type(ty: &Type, type_node: &dyn Spanned, diag: &mut BuildDiagnostics) -> String { fn get_cpp_type(ty: &Type, type_node: &dyn Spanned, diag: &mut BuildDiagnostics) -> String {
ty.cpp_type().unwrap_or_else(|| { ty.cpp_type().unwrap_or_else(|| {
let err = CompilerDiagnostic { let err = crate::diagnostics::Diagnostic {
message: "Cannot map property type to C++".into(), message: "Cannot map property type to C++".into(),
span: type_node.to_source_location(), span: type_node.to_source_location(),
level: DiagnosticLevel::Error, level: DiagnosticLevel::Error,
@ -1300,7 +1300,7 @@ fn model_data_type(parent_element: &ElementRc, diag: &mut BuildDiagnostics) -> S
.ty(); .ty();
model_data_type.cpp_type().unwrap_or_else(|| { model_data_type.cpp_type().unwrap_or_else(|| {
diag.push_internal_error( diag.push_internal_error(
CompilerDiagnostic { crate::diagnostics::Diagnostic {
message: format!("Cannot map property type {} to C++", model_data_type), message: format!("Cannot map property type {} to C++", model_data_type),
span: parent_element span: parent_element
.borrow() .borrow()

View file

@ -13,7 +13,7 @@ Some convention used in the generated code:
- `_self` is of type `Pin<&ComponentType>` where ComponentType is the type of the generated component - `_self` is of type `Pin<&ComponentType>` where ComponentType is the type of the generated component
*/ */
use crate::diagnostics::{BuildDiagnostics, CompilerDiagnostic, DiagnosticLevel}; use crate::diagnostics::{BuildDiagnostics, Diagnostic, DiagnosticLevel};
use crate::expression_tree::{ use crate::expression_tree::{
BuiltinFunction, EasingCurve, Expression, NamedReference, OperatorClass, Path, BuiltinFunction, EasingCurve, Expression, NamedReference, OperatorClass, Path,
}; };
@ -27,7 +27,7 @@ use std::{collections::BTreeMap, rc::Rc};
fn rust_type( fn rust_type(
ty: &Type, ty: &Type,
span: Option<&dyn crate::diagnostics::Spanned>, span: Option<&dyn crate::diagnostics::Spanned>,
) -> Result<proc_macro2::TokenStream, CompilerDiagnostic> { ) -> Result<proc_macro2::TokenStream, Diagnostic> {
match ty { match ty {
Type::Int32 => Ok(quote!(i32)), Type::Int32 => Ok(quote!(i32)),
Type::Float32 => Ok(quote!(f32)), Type::Float32 => Ok(quote!(f32)),
@ -56,7 +56,7 @@ fn rust_type(
Ok(quote!(sixtyfps::re_exports::#e)) Ok(quote!(sixtyfps::re_exports::#e))
} }
Type::Brush => Ok(quote!(sixtyfps::Brush)), Type::Brush => Ok(quote!(sixtyfps::Brush)),
_ => Err(CompilerDiagnostic { _ => Err(Diagnostic {
message: format!("Cannot map property type {} to Rust", ty), message: format!("Cannot map property type {} to Rust", ty),
span: span.map(|s| s.to_source_location()).unwrap_or_default(), span: span.map(|s| s.to_source_location()).unwrap_or_default(),
level: DiagnosticLevel::Error, level: DiagnosticLevel::Error,

View file

@ -119,11 +119,9 @@ impl CompilerConfiguration {
pub async fn compile_syntax_node( pub async fn compile_syntax_node(
doc_node: parser::SyntaxNodeWithSourceFile, doc_node: parser::SyntaxNodeWithSourceFile,
mut diagnostics: diagnostics::FileDiagnostics, mut diagnostics: diagnostics::BuildDiagnostics,
compiler_config: CompilerConfiguration, compiler_config: CompilerConfiguration,
) -> (object_tree::Document, diagnostics::BuildDiagnostics) { ) -> (object_tree::Document, diagnostics::BuildDiagnostics) {
let mut build_diagnostics = diagnostics::BuildDiagnostics::default();
let global_type_registry = typeregister::TypeRegister::builtin(); let global_type_registry = typeregister::TypeRegister::builtin();
let type_registry = let type_registry =
Rc::new(RefCell::new(typeregister::TypeRegister::new(&global_type_registry))); Rc::new(RefCell::new(typeregister::TypeRegister::new(&global_type_registry)));
@ -131,33 +129,26 @@ pub async fn compile_syntax_node(
let doc_node: parser::syntax_nodes::Document = doc_node.into(); let doc_node: parser::syntax_nodes::Document = doc_node.into();
let mut loader = let mut loader =
typeloader::TypeLoader::new(global_type_registry, &compiler_config, &mut build_diagnostics); typeloader::TypeLoader::new(global_type_registry, &compiler_config, &mut diagnostics);
if doc_node.source_file.is_some() { if doc_node.source_file.is_some() {
loader loader.load_dependencies_recursively(&doc_node, &mut diagnostics, &type_registry).await;
.load_dependencies_recursively(
&doc_node,
&mut diagnostics,
&mut build_diagnostics,
&type_registry,
)
.await;
} }
let doc = crate::object_tree::Document::from_node(doc_node, &mut diagnostics, &type_registry); let doc = crate::object_tree::Document::from_node(doc_node, &mut diagnostics, &type_registry);
build_diagnostics.add(diagnostics);
if let Some((_, node)) = &*doc.root_component.child_insertion_point.borrow() { if let Some((_, node)) = &*doc.root_component.child_insertion_point.borrow() {
build_diagnostics diagnostics
.push_error("@children placeholder not allowed in the final component".into(), node) .push_error("@children placeholder not allowed in the final component".into(), node)
} }
if !build_diagnostics.has_error() { if !diagnostics.has_error() {
// FIXME: ideally we would be able to run more passes, but currently we panic because invariant are not met. // FIXME: ideally we would be able to run more passes, but currently we panic because invariant are not met.
run_passes(&doc, &mut build_diagnostics, &mut loader, &compiler_config).await; run_passes(&doc, &mut diagnostics, &mut loader, &compiler_config).await;
} }
(doc, build_diagnostics) diagnostics.all_loaded_files = loader.all_files().cloned().collect();
(doc, diagnostics)
} }
pub async fn run_passes( pub async fn run_passes(

View file

@ -27,7 +27,8 @@ use crate::typeregister::TypeRegister;
/// `register` is the register to fill with the builtin types. /// `register` is the register to fill with the builtin types.
/// At this point, it really should already contain the basic Types (string, int, ...) /// At this point, it really should already contain the basic Types (string, int, ...)
pub fn load_builtins(register: &mut TypeRegister) { pub fn load_builtins(register: &mut TypeRegister) {
let (node, mut diag) = crate::parser::parse(include_str!("builtins.60").into(), None); let mut diag = crate::diagnostics::BuildDiagnostics::default();
let node = crate::parser::parse(include_str!("builtins.60").into(), None, &mut diag);
if !diag.is_empty() { if !diag.is_empty() {
let vec = diag.to_string_vec(); let vec = diag.to_string_vec();
#[cfg(feature = "display-diagnostics")] #[cfg(feature = "display-diagnostics")]

View file

@ -11,7 +11,7 @@ LICENSE END */
This module contains the intermediate representation of the code in the form of an object tree This module contains the intermediate representation of the code in the form of an object tree
*/ */
use crate::diagnostics::{FileDiagnostics, Spanned}; use crate::diagnostics::{BuildDiagnostics, Spanned};
use crate::expression_tree::{self, Unit}; use crate::expression_tree::{self, Unit};
use crate::expression_tree::{BindingExpression, Expression, NamedReference}; use crate::expression_tree::{BindingExpression, Expression, NamedReference};
use crate::langtype::PropertyLookupResult; use crate::langtype::PropertyLookupResult;
@ -36,7 +36,7 @@ pub struct Document {
impl Document { impl Document {
pub fn from_node( pub fn from_node(
node: syntax_nodes::Document, node: syntax_nodes::Document,
diag: &mut FileDiagnostics, diag: &mut BuildDiagnostics,
parent_registry: &Rc<RefCell<TypeRegister>>, parent_registry: &Rc<RefCell<TypeRegister>>,
) -> Self { ) -> Self {
debug_assert_eq!(node.kind(), SyntaxKind::Document); debug_assert_eq!(node.kind(), SyntaxKind::Document);
@ -47,7 +47,7 @@ impl Document {
let mut process_component = let mut process_component =
|n: syntax_nodes::Component, |n: syntax_nodes::Component,
diag: &mut FileDiagnostics, diag: &mut BuildDiagnostics,
local_registry: &mut TypeRegister| { local_registry: &mut TypeRegister| {
let compo = Component::from_node(n, diag, local_registry); let compo = Component::from_node(n, diag, local_registry);
local_registry.add(compo.clone()); local_registry.add(compo.clone());
@ -55,7 +55,7 @@ impl Document {
}; };
let mut process_struct = let mut process_struct =
|n: syntax_nodes::StructDeclaration, |n: syntax_nodes::StructDeclaration,
diag: &mut FileDiagnostics, diag: &mut BuildDiagnostics,
local_registry: &mut TypeRegister| { local_registry: &mut TypeRegister| {
let mut ty = type_struct_from_node(n.ObjectType(), diag, local_registry); let mut ty = type_struct_from_node(n.ObjectType(), diag, local_registry);
if let Type::Object { name, .. } = &mut ty { if let Type::Object { name, .. } = &mut ty {
@ -172,7 +172,7 @@ pub struct Component {
impl Component { impl Component {
pub fn from_node( pub fn from_node(
node: syntax_nodes::Component, node: syntax_nodes::Component,
diag: &mut FileDiagnostics, diag: &mut BuildDiagnostics,
tr: &TypeRegister, tr: &TypeRegister,
) -> Rc<Self> { ) -> Rc<Self> {
let mut child_insertion_point = None; let mut child_insertion_point = None;
@ -373,7 +373,7 @@ impl Element {
id: String, id: String,
parent_type: Type, parent_type: Type,
component_child_insertion_point: &mut Option<ChildrenInsertionPoint>, component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
diag: &mut FileDiagnostics, diag: &mut BuildDiagnostics,
tr: &TypeRegister, tr: &TypeRegister,
) -> ElementRc { ) -> ElementRc {
let (base_type, name_for_looup_errors) = if let Some(base_node) = node.QualifiedName() { let (base_type, name_for_looup_errors) = if let Some(base_node) = node.QualifiedName() {
@ -711,7 +711,7 @@ impl Element {
node: syntax_nodes::RepeatedElement, node: syntax_nodes::RepeatedElement,
parent: &ElementRc, parent: &ElementRc,
component_child_insertion_point: &mut Option<ChildrenInsertionPoint>, component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
diag: &mut FileDiagnostics, diag: &mut BuildDiagnostics,
tr: &TypeRegister, tr: &TypeRegister,
) -> ElementRc { ) -> ElementRc {
let is_listview = if parent.borrow().base_type.to_string() == "ListView" { let is_listview = if parent.borrow().base_type.to_string() == "ListView" {
@ -751,7 +751,7 @@ impl Element {
node: syntax_nodes::ConditionalElement, node: syntax_nodes::ConditionalElement,
parent_type: Type, parent_type: Type,
component_child_insertion_point: &mut Option<ChildrenInsertionPoint>, component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
diag: &mut FileDiagnostics, diag: &mut BuildDiagnostics,
tr: &TypeRegister, tr: &TypeRegister,
) -> ElementRc { ) -> ElementRc {
let rei = RepeatedElementInfo { let rei = RepeatedElementInfo {
@ -794,7 +794,7 @@ impl Element {
bindings: impl Iterator< bindings: impl Iterator<
Item = (crate::parser::SyntaxTokenWithSourceFile, SyntaxNodeWithSourceFile), Item = (crate::parser::SyntaxTokenWithSourceFile, SyntaxNodeWithSourceFile),
>, >,
diag: &mut FileDiagnostics, diag: &mut BuildDiagnostics,
) { ) {
for (name_token, b) in bindings { for (name_token, b) in bindings {
let unresolved_name = crate::parser::normalize_identifier(name_token.text()); let unresolved_name = crate::parser::normalize_identifier(name_token.text());
@ -854,7 +854,7 @@ impl Element {
/// Create a Type for this node /// Create a Type for this node
pub fn type_from_node( pub fn type_from_node(
node: syntax_nodes::Type, node: syntax_nodes::Type,
diag: &mut FileDiagnostics, diag: &mut BuildDiagnostics,
tr: &TypeRegister, tr: &TypeRegister,
) -> Type { ) -> Type {
if let Some(qualified_type_node) = node.QualifiedName() { if let Some(qualified_type_node) = node.QualifiedName() {
@ -882,7 +882,7 @@ pub fn type_from_node(
/// Create a Type::Object from a syntax_nodes::ObjectType /// Create a Type::Object from a syntax_nodes::ObjectType
pub fn type_struct_from_node( pub fn type_struct_from_node(
object_node: syntax_nodes::ObjectType, object_node: syntax_nodes::ObjectType,
diag: &mut FileDiagnostics, diag: &mut BuildDiagnostics,
tr: &TypeRegister, tr: &TypeRegister,
) -> Type { ) -> Type {
let fields = object_node let fields = object_node
@ -898,7 +898,7 @@ fn animation_element_from_node(
anim: &syntax_nodes::PropertyAnimation, anim: &syntax_nodes::PropertyAnimation,
prop_name: &syntax_nodes::QualifiedName, prop_name: &syntax_nodes::QualifiedName,
prop_type: Type, prop_type: Type,
diag: &mut FileDiagnostics, diag: &mut BuildDiagnostics,
tr: &TypeRegister, tr: &TypeRegister,
) -> Option<ElementRc> { ) -> Option<ElementRc> {
let anim_type = tr.property_animation_type_for_property(prop_type); let anim_type = tr.property_animation_type_for_property(prop_type);
@ -951,7 +951,7 @@ impl std::fmt::Display for QualifiedTypeName {
fn lookup_property_from_qualified_name( fn lookup_property_from_qualified_name(
node: syntax_nodes::QualifiedName, node: syntax_nodes::QualifiedName,
r: &Rc<RefCell<Element>>, r: &Rc<RefCell<Element>>,
diag: &mut FileDiagnostics, diag: &mut BuildDiagnostics,
) -> (NamedReference, Type) { ) -> (NamedReference, Type) {
let qualname = QualifiedTypeName::from_node(node.clone()); let qualname = QualifiedTypeName::from_node(node.clone());
match qualname.members.as_slice() { match qualname.members.as_slice() {
@ -1261,7 +1261,7 @@ impl Exports {
doc: &syntax_nodes::Document, doc: &syntax_nodes::Document,
inner_components: &[Rc<Component>], inner_components: &[Rc<Component>],
type_registry: &TypeRegister, type_registry: &TypeRegister,
diag: &mut FileDiagnostics, diag: &mut BuildDiagnostics,
) -> Self { ) -> Self {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct NamedExport { struct NamedExport {

View file

@ -18,7 +18,7 @@ This module has different sub modules with the actual parser functions
*/ */
use crate::diagnostics::{FileDiagnostics, SourceFile, Spanned}; use crate::diagnostics::{BuildDiagnostics, SourceFile, Spanned};
pub use smol_str::SmolStr; pub use smol_str::SmolStr;
use std::convert::TryFrom; use std::convert::TryFrom;
@ -506,23 +506,28 @@ mod parser_trait {
#[doc(inline)] #[doc(inline)]
pub use parser_trait::*; pub use parser_trait::*;
pub struct DefaultParser { pub struct DefaultParser<'a> {
builder: rowan::GreenNodeBuilder<'static>, builder: rowan::GreenNodeBuilder<'static>,
tokens: Vec<Token>, tokens: Vec<Token>,
cursor: usize, cursor: usize,
diags: FileDiagnostics, diags: &'a mut BuildDiagnostics,
source_file: SourceFile,
} }
impl From<Vec<Token>> for DefaultParser { impl<'a> DefaultParser<'a> {
fn from(tokens: Vec<Token>) -> Self { fn from_tokens(tokens: Vec<Token>, diags: &'a mut BuildDiagnostics) -> Self {
Self { builder: Default::default(), tokens, cursor: 0, diags: Default::default() } Self {
builder: Default::default(),
tokens,
cursor: 0,
diags,
source_file: Default::default(),
}
} }
}
impl DefaultParser {
/// Constructor that create a parser from the source code /// Constructor that create a parser from the source code
pub fn new(source: &str) -> Self { pub fn new(source: &str, diags: &'a mut BuildDiagnostics) -> Self {
Self::from(crate::lexer::lex(&source)) Self::from_tokens(crate::lexer::lex(&source), diags)
} }
fn current_token(&self) -> Token { fn current_token(&self) -> Token {
@ -537,7 +542,7 @@ impl DefaultParser {
} }
} }
impl Parser for DefaultParser { impl Parser for DefaultParser<'_> {
fn start_node_impl( fn start_node_impl(
&mut self, &mut self,
kind: SyntaxKind, kind: SyntaxKind,
@ -586,7 +591,14 @@ impl Parser for DefaultParser {
{ {
span.span = current_token.span; span.span = current_token.span;
} }
self.diags.push_error_with_span(e.into(), span);
self.diags.push_error_with_span(
e.into(),
crate::diagnostics::SourceLocation {
source_file: Some(self.source_file.clone()),
span,
},
);
} }
type Checkpoint = rowan::Checkpoint; type Checkpoint = rowan::Checkpoint;
@ -808,33 +820,30 @@ pub fn normalize_identifier(ident: &str) -> String {
pub fn parse( pub fn parse(
source: String, source: String,
path: Option<&std::path::Path>, path: Option<&std::path::Path>,
) -> (SyntaxNodeWithSourceFile, FileDiagnostics) { build_diagnostics: &mut BuildDiagnostics,
let mut p = DefaultParser::new(&source); ) -> SyntaxNodeWithSourceFile {
document::parse_document(&mut p); let mut p = DefaultParser::new(&source, build_diagnostics);
let source_file = if let Some(path) = path { let source_file = if let Some(path) = path {
p.diags.current_path = p.source_file =
std::rc::Rc::new(crate::diagnostics::SourceFileInner::new(path.to_path_buf(), source)); std::rc::Rc::new(crate::diagnostics::SourceFileInner::new(path.to_path_buf(), source));
Some(p.diags.current_path.clone()) Some(p.source_file.clone())
} else { } else {
None None
}; };
( document::parse_document(&mut p);
SyntaxNodeWithSourceFile { node: SyntaxNode::new_root(p.builder.finish()), source_file }, SyntaxNodeWithSourceFile { node: SyntaxNode::new_root(p.builder.finish()), source_file }
p.diags,
)
} }
pub fn parse_file<P: AsRef<std::path::Path>>( pub fn parse_file<P: AsRef<std::path::Path>>(
path: P, path: P,
) -> std::io::Result<(SyntaxNodeWithSourceFile, FileDiagnostics)> { build_diagnostics: &mut BuildDiagnostics,
) -> std::io::Result<SyntaxNodeWithSourceFile> {
let source = std::fs::read_to_string(&path)?; let source = std::fs::read_to_string(&path)?;
Ok(parse(source, Some(path.as_ref()), build_diagnostics))
Ok(parse(source, Some(path.as_ref())))
} }
#[allow(dead_code)] pub fn parse_tokens(tokens: Vec<Token>, diags: &mut BuildDiagnostics) -> SyntaxNode {
pub fn parse_tokens(tokens: Vec<Token>) -> (SyntaxNode, FileDiagnostics) { let mut p = DefaultParser::from_tokens(tokens, diags);
let mut p = DefaultParser::from(tokens);
document::parse_document(&mut p); document::parse_document(&mut p);
(SyntaxNode::new_root(p.builder.finish()), p.diags) SyntaxNode::new_root(p.builder.finish())
} }

View file

@ -26,15 +26,9 @@ pub async fn apply_default_properties_from_style<'a>(
_diag: &mut BuildDiagnostics, _diag: &mut BuildDiagnostics,
) { ) {
// Ignore import errors // Ignore import errors
let mut file_diags_to_ignore = crate::diagnostics::FileDiagnostics::default();
let mut build_diags_to_ignore = BuildDiagnostics::default(); let mut build_diags_to_ignore = BuildDiagnostics::default();
let style_metrics = type_loader let style_metrics = type_loader
.import_type( .import_type("sixtyfps_widgets.60", "StyleMetrics", &mut build_diags_to_ignore)
"sixtyfps_widgets.60",
"StyleMetrics",
&mut file_diags_to_ignore,
&mut build_diags_to_ignore,
)
.await; .await;
let style_metrics = if let Some(Type::Component(c)) = style_metrics { c } else { return }; let style_metrics = if let Some(Type::Component(c)) = style_metrics { c } else { return };

View file

@ -246,15 +246,9 @@ pub async fn lower_layouts<'a>(
diag: &mut BuildDiagnostics, diag: &mut BuildDiagnostics,
) { ) {
// Ignore import errors // Ignore import errors
let mut file_diags_to_ignore = crate::diagnostics::FileDiagnostics::default();
let mut build_diags_to_ignore = crate::diagnostics::BuildDiagnostics::default(); let mut build_diags_to_ignore = crate::diagnostics::BuildDiagnostics::default();
let style_metrics = type_loader let style_metrics = type_loader
.import_type( .import_type("sixtyfps_widgets.60", "StyleMetrics", &mut build_diags_to_ignore)
"sixtyfps_widgets.60",
"StyleMetrics",
&mut file_diags_to_ignore,
&mut build_diags_to_ignore,
)
.await; .await;
let style_metrics = let style_metrics =
style_metrics.and_then(|sm| if let Type::Component(c) = sm { Some(c) } else { None }); style_metrics.and_then(|sm| if let Type::Component(c) = sm { Some(c) } else { None });

View file

@ -15,6 +15,8 @@ LICENSE END */
//! // ^error{some_regexp} //! // ^error{some_regexp}
//! ``` //! ```
use std::path::{Path, PathBuf};
#[test] #[test]
fn syntax_tests() -> std::io::Result<()> { fn syntax_tests() -> std::io::Result<()> {
if let Some(specific_test) = std::env::args() if let Some(specific_test) = std::env::args()
@ -57,12 +59,17 @@ fn process_file(path: &std::path::Path) -> std::io::Result<bool> {
} }
fn process_diagnostics( fn process_diagnostics(
mut compile_diagnostics: sixtyfps_compilerlib::diagnostics::FileDiagnostics, compile_diagnostics: &sixtyfps_compilerlib::diagnostics::BuildDiagnostics,
source: String, path: &Path,
source: &str,
silent: bool, silent: bool,
) -> std::io::Result<bool> { ) -> std::io::Result<bool> {
let mut success = true; let mut success = true;
let path = compile_diagnostics.current_path.as_ref();
let mut diags = compile_diagnostics
.iter()
.filter(|d| &cannonical(d.span.source_file.as_ref().unwrap().path()) == path)
.collect::<Vec<_>>();
// Find expected errors in the file. The first caret (^) points to the expected column. The number of // Find expected errors in the file. The first caret (^) points to the expected column. The number of
// carets refers to the number of lines to go back. This is useful when one line of code produces multiple // carets refers to the number of lines to go back. This is useful when one line of code produces multiple
@ -98,14 +105,11 @@ fn process_diagnostics(
_ => panic!("Unsupported diagnostic level {}", warning_or_error), _ => panic!("Unsupported diagnostic level {}", warning_or_error),
}; };
match compile_diagnostics.inner.iter().position(|e| match e { match diags.iter().position(|e| {
sixtyfps_compilerlib::diagnostics::Diagnostic::FileLoadError(_) => false, e.span.span.offset == offset && r.is_match(&e.message) && e.level == expected_diag_level
sixtyfps_compilerlib::diagnostics::Diagnostic::CompilerDiagnostic(e) => {
e.span.offset == offset && r.is_match(&e.message) && e.level == expected_diag_level
}
}) { }) {
Some(idx) => { Some(idx) => {
compile_diagnostics.inner.remove(idx); diags.remove(idx);
} }
None => { None => {
success = false; success = false;
@ -116,28 +120,35 @@ fn process_diagnostics(
} }
} }
} }
if !diags.is_empty() {
println!("{:?}: Unexptected errors/warnings: {:#?}", path, diags);
if !compile_diagnostics.inner.is_empty() { #[cfg(feature = "display-diagnostics")]
println!("{:?}: Unexptected errors/warnings: {:#?}", path, compile_diagnostics.inner);
if !silent { if !silent {
#[cfg(feature = "display-diagnostics")] let mut to_report = sixtyfps_compilerlib::diagnostics::BuildDiagnostics::default();
compile_diagnostics.print(); for d in diags {
to_report.push_compiler_error(d.clone());
}
to_report.print();
} }
success = false; success = false;
} }
Ok(success) Ok(success)
} }
fn cannonical(path: &Path) -> PathBuf {
path.canonicalize().unwrap_or_else(|_| path.to_owned())
}
fn process_file_source( fn process_file_source(
path: &std::path::Path, path: &std::path::Path,
source: String, source: String,
silent: bool, silent: bool,
) -> std::io::Result<bool> { ) -> std::io::Result<bool> {
let (syntax_node, parse_diagnostics) = let mut parse_diagnostics = sixtyfps_compilerlib::diagnostics::BuildDiagnostics::default();
sixtyfps_compilerlib::parser::parse(source.clone(), Some(path)); let syntax_node =
sixtyfps_compilerlib::parser::parse(source.clone(), Some(path), &mut parse_diagnostics);
let compile_diagnostics = if !parse_diagnostics.has_error() { let compile_diagnostics = if !parse_diagnostics.has_error() {
let mut compiler_config = sixtyfps_compilerlib::CompilerConfiguration::new( let mut compiler_config = sixtyfps_compilerlib::CompilerConfiguration::new(
sixtyfps_compilerlib::generator::OutputFormat::Interpreter, sixtyfps_compilerlib::generator::OutputFormat::Interpreter,
@ -148,22 +159,22 @@ fn process_file_source(
parse_diagnostics, parse_diagnostics,
compiler_config, compiler_config,
)); ));
build_diags.into_iter().collect() build_diags
} else { } else {
vec![parse_diagnostics] parse_diagnostics
}; };
assert!(compile_diagnostics.len() >= 1);
let mut success = true; let mut success = true;
success &= process_diagnostics(&compile_diagnostics, &path, &source, silent)?;
for diagnostics in compile_diagnostics.into_iter() { for p in &compile_diagnostics.all_loaded_files {
let source = if diagnostics.current_path.path() == path { let source = if p.is_absolute() {
source.clone() std::fs::read_to_string(&p)?
} else { } else {
std::fs::read_to_string(diagnostics.current_path.path())? // probably sixtyfps_widgets.60
String::new()
}; };
success &= process_diagnostics(diagnostics, source, silent)?; success &= process_diagnostics(&compile_diagnostics, &p, &source, silent)?;
} }
Ok(success) Ok(success)

View file

@ -14,7 +14,7 @@ use std::io::Read;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::rc::Rc; use std::rc::Rc;
use crate::diagnostics::{BuildDiagnostics, CompilerDiagnostic, FileDiagnostics}; use crate::diagnostics::{BuildDiagnostics, Diagnostic};
use crate::object_tree::{self, Document}; use crate::object_tree::{self, Document};
use crate::parser::{syntax_nodes, SyntaxKind, SyntaxTokenWithSourceFile}; use crate::parser::{syntax_nodes, SyntaxKind, SyntaxTokenWithSourceFile};
use crate::typeregister::TypeRegister; use crate::typeregister::TypeRegister;
@ -131,7 +131,7 @@ impl<'a> TypeLoader<'a> {
let is_wasm = cfg!(target_arch = "wasm32") let is_wasm = cfg!(target_arch = "wasm32")
|| std::env::var("TARGET").map_or(false, |t| t.starts_with("wasm")); || std::env::var("TARGET").map_or(false, |t| t.starts_with("wasm"));
if !is_wasm { if !is_wasm {
diag.push_internal_error(CompilerDiagnostic { diag.push_internal_error(Diagnostic {
message: "SIXTYFPS_STYLE not defined, defaulting to 'ugly', see https://github.com/sixtyfpsui/sixtyfps/issues/83 for more info".to_owned(), message: "SIXTYFPS_STYLE not defined, defaulting to 'ugly', see https://github.com/sixtyfpsui/sixtyfps/issues/83 for more info".to_owned(),
span: Default::default(), span: Default::default(),
level: crate::diagnostics::DiagnosticLevel::Warning level: crate::diagnostics::DiagnosticLevel::Warning
@ -154,14 +154,12 @@ impl<'a> TypeLoader<'a> {
pub async fn load_dependencies_recursively( pub async fn load_dependencies_recursively(
&mut self, &mut self,
doc: &syntax_nodes::Document, doc: &syntax_nodes::Document,
mut diagnostics: &mut FileDiagnostics, diagnostics: &mut BuildDiagnostics,
build_diagnostics: &mut BuildDiagnostics,
registry_to_populate: &Rc<RefCell<TypeRegister>>, registry_to_populate: &Rc<RefCell<TypeRegister>>,
) { ) {
let dependencies = self.collect_dependencies(&doc, &mut diagnostics).await; let dependencies = self.collect_dependencies(&doc, diagnostics).await;
for import in dependencies.into_iter() { for import in dependencies.into_iter() {
self.load_dependency(import, registry_to_populate, diagnostics, build_diagnostics) self.load_dependency(import, registry_to_populate, diagnostics).await;
.await;
} }
} }
@ -169,8 +167,7 @@ impl<'a> TypeLoader<'a> {
&mut self, &mut self,
file_to_import: &str, file_to_import: &str,
type_name: &str, type_name: &str,
diagnostics: &mut FileDiagnostics, diagnostics: &mut BuildDiagnostics,
build_diagnostics: &mut BuildDiagnostics,
) -> Option<crate::langtype::Type> { ) -> Option<crate::langtype::Type> {
let file = match self.import_file(None, file_to_import) { let file = match self.import_file(None, file_to_import) {
Some(file) => file, Some(file) => file,
@ -178,13 +175,7 @@ impl<'a> TypeLoader<'a> {
}; };
let doc_path = match self let doc_path = match self
.ensure_document_loaded( .ensure_document_loaded(&file.path, file.source_code_future, None, diagnostics)
&file.path,
file.source_code_future,
None,
Some(diagnostics),
build_diagnostics,
)
.await .await
{ {
Some(doc_path) => doc_path, Some(doc_path) => doc_path,
@ -207,8 +198,7 @@ impl<'a> TypeLoader<'a> {
path: &'b Path, path: &'b Path,
source_code_future: impl std::future::Future<Output = std::io::Result<String>>, source_code_future: impl std::future::Future<Output = std::io::Result<String>>,
import_token: Option<SyntaxTokenWithSourceFile>, import_token: Option<SyntaxTokenWithSourceFile>,
importer_diagnostics: Option<&'b mut FileDiagnostics>, diagnostics: &'b mut BuildDiagnostics,
build_diagnostics: &'b mut BuildDiagnostics,
) -> Option<PathBuf> { ) -> Option<PathBuf> {
let path_canon = path.canonicalize().unwrap_or_else(|_| path.to_owned()); let path_canon = path.canonicalize().unwrap_or_else(|_| path.to_owned());
@ -217,8 +207,7 @@ impl<'a> TypeLoader<'a> {
} }
if !self.all_documents.currently_loading.insert(path_canon.clone()) { if !self.all_documents.currently_loading.insert(path_canon.clone()) {
importer_diagnostics diagnostics
.unwrap()
.push_error(format!("Recursive import of {}", path.display()), &import_token); .push_error(format!("Recursive import of {}", path.display()), &import_token);
return None; return None;
} }
@ -226,7 +215,7 @@ impl<'a> TypeLoader<'a> {
let source_code = match source_code_future.await { let source_code = match source_code_future.await {
Ok(source) => source, Ok(source) => source,
Err(err) => { Err(err) => {
importer_diagnostics.unwrap().push_error( diagnostics.push_error(
format!("Error reading requested import {}: {}", path.display(), err), format!("Error reading requested import {}: {}", path.display(), err),
&import_token, &import_token,
); );
@ -234,7 +223,7 @@ impl<'a> TypeLoader<'a> {
} }
}; };
self.load_file(&path_canon, path, source_code, build_diagnostics).await; self.load_file(&path_canon, path, source_code, diagnostics).await;
let _ok = self.all_documents.currently_loading.remove(path_canon.as_path()); let _ok = self.all_documents.currently_loading.remove(path_canon.as_path());
assert!(_ok); assert!(_ok);
Some(path_canon) Some(path_canon)
@ -248,13 +237,11 @@ impl<'a> TypeLoader<'a> {
path: &Path, path: &Path,
source_path: &Path, source_path: &Path,
source_code: String, source_code: String,
build_diagnostics: &mut BuildDiagnostics, diagnostics: &mut BuildDiagnostics,
) { ) {
let (dependency_doc, mut dependency_diagnostics) = let dependency_doc = crate::parser::parse(source_code, Some(&source_path), diagnostics);
crate::parser::parse(source_code, Some(&source_path));
if dependency_diagnostics.has_error() { if diagnostics.has_error() {
build_diagnostics.add(dependency_diagnostics);
let mut d = Document::default(); let mut d = Document::default();
d.node = Some(dependency_doc.into()); d.node = Some(dependency_doc.into());
self.all_documents.docs.insert(path.to_owned(), d); self.all_documents.docs.insert(path.to_owned(), d);
@ -265,25 +252,15 @@ impl<'a> TypeLoader<'a> {
let dependency_registry = let dependency_registry =
Rc::new(RefCell::new(TypeRegister::new(&self.global_type_registry))); Rc::new(RefCell::new(TypeRegister::new(&self.global_type_registry)));
self.load_dependencies_recursively( self.load_dependencies_recursively(&dependency_doc, diagnostics, &dependency_registry)
&dependency_doc, .await;
&mut dependency_diagnostics,
build_diagnostics,
&dependency_registry,
)
.await;
let doc = crate::object_tree::Document::from_node( let doc = crate::object_tree::Document::from_node(
dependency_doc, dependency_doc,
&mut dependency_diagnostics, diagnostics,
&dependency_registry, &dependency_registry,
); );
crate::passes::resolving::resolve_expressions(&doc, &self, build_diagnostics); crate::passes::resolving::resolve_expressions(&doc, &self, diagnostics);
// Add diagnostics regardless whether they're empty or not. This is used by the syntax_tests to
// also verify that imported files have no errors.
build_diagnostics.add(dependency_diagnostics);
self.all_documents.docs.insert(path.to_owned(), doc); self.all_documents.docs.insert(path.to_owned(), doc);
} }
@ -291,7 +268,6 @@ impl<'a> TypeLoader<'a> {
&'b mut self, &'b mut self,
import: ImportedTypes, import: ImportedTypes,
registry_to_populate: &'b Rc<RefCell<TypeRegister>>, registry_to_populate: &'b Rc<RefCell<TypeRegister>>,
importer_diagnostics: &'b mut FileDiagnostics,
build_diagnostics: &'b mut BuildDiagnostics, build_diagnostics: &'b mut BuildDiagnostics,
) -> core::pin::Pin<Box<dyn std::future::Future<Output = ()> + 'b>> { ) -> core::pin::Pin<Box<dyn std::future::Future<Output = ()> + 'b>> {
Box::pin(async move { Box::pin(async move {
@ -300,7 +276,6 @@ impl<'a> TypeLoader<'a> {
&import.file.path, &import.file.path,
import.file.source_code_future, import.file.source_code_future,
Some(import.import_token.clone()), Some(import.import_token.clone()),
Some(importer_diagnostics),
build_diagnostics, build_diagnostics,
) )
.await .await
@ -324,7 +299,7 @@ impl<'a> TypeLoader<'a> {
let imported_type = match imported_type { let imported_type = match imported_type {
Some(ty) => ty, Some(ty) => ty,
None => { None => {
importer_diagnostics.push_error( build_diagnostics.push_error(
format!( format!(
"No exported type called {} found in {}", "No exported type called {} found in {}",
import_name.external_name, import_name.external_name,
@ -391,7 +366,7 @@ impl<'a> TypeLoader<'a> {
async fn collect_dependencies( async fn collect_dependencies(
&mut self, &mut self,
doc: &syntax_nodes::Document, doc: &syntax_nodes::Document,
doc_diagnostics: &mut FileDiagnostics, doc_diagnostics: &mut BuildDiagnostics,
) -> impl Iterator<Item = ImportedTypes> { ) -> impl Iterator<Item = ImportedTypes> {
let referencing_file = doc.source_file.as_ref().unwrap().path().clone(); let referencing_file = doc.source_file.as_ref().unwrap().path().clone();
@ -445,6 +420,11 @@ impl<'a> TypeLoader<'a> {
|path| self.all_documents.docs.get(&path), |path| self.all_documents.docs.get(&path),
) )
} }
/// Return an iterator over all the loaded file path
pub fn all_files<'b>(&'b self) -> impl Iterator<Item = &PathBuf> + 'b {
self.all_documents.docs.keys()
}
} }
#[test] #[test]
@ -535,7 +515,6 @@ fn test_manual_import() {
let mut compiler_config = let mut compiler_config =
CompilerConfiguration::new(crate::generator::OutputFormat::Interpreter); CompilerConfiguration::new(crate::generator::OutputFormat::Interpreter);
compiler_config.style = Some("ugly".into()); compiler_config.style = Some("ugly".into());
let mut test_diags = FileDiagnostics::default();
let global_registry = TypeRegister::builtin(); let global_registry = TypeRegister::builtin();
let mut build_diagnostics = BuildDiagnostics::default(); let mut build_diagnostics = BuildDiagnostics::default();
let mut loader = TypeLoader::new(global_registry, &compiler_config, &mut build_diagnostics); let mut loader = TypeLoader::new(global_registry, &compiler_config, &mut build_diagnostics);
@ -543,11 +522,9 @@ fn test_manual_import() {
let maybe_button_type = spin_on::spin_on(loader.import_type( let maybe_button_type = spin_on::spin_on(loader.import_type(
"sixtyfps_widgets.60", "sixtyfps_widgets.60",
"Button", "Button",
&mut test_diags,
&mut build_diagnostics, &mut build_diagnostics,
)); ));
assert!(!test_diags.has_error());
assert!(!build_diagnostics.has_error()); assert!(!build_diagnostics.has_error());
assert!(maybe_button_type.is_some()); assert!(maybe_button_type.is_some());
} }

View file

@ -14,6 +14,7 @@ use core::ptr::NonNull;
use dynamic_type::{Instance, InstanceBox}; use dynamic_type::{Instance, InstanceBox};
use expression_tree::NamedReference; use expression_tree::NamedReference;
use object_tree::{Element, ElementRc}; use object_tree::{Element, ElementRc};
use sixtyfps_compilerlib::diagnostics::BuildDiagnostics;
use sixtyfps_compilerlib::langtype::Type; use sixtyfps_compilerlib::langtype::Type;
use sixtyfps_compilerlib::layout::{Layout, LayoutConstraints, LayoutItem, PathLayout}; use sixtyfps_compilerlib::layout::{Layout, LayoutConstraints, LayoutItem, PathLayout};
use sixtyfps_compilerlib::*; use sixtyfps_compilerlib::*;
@ -620,11 +621,10 @@ pub async fn load<'id>(
guard: generativity::Guard<'id>, guard: generativity::Guard<'id>,
) -> (Result<Rc<ComponentDescription<'id>>, ()>, sixtyfps_compilerlib::diagnostics::BuildDiagnostics) ) -> (Result<Rc<ComponentDescription<'id>>, ()>, sixtyfps_compilerlib::diagnostics::BuildDiagnostics)
{ {
let (syntax_node, diag) = parser::parse(source, Some(path.as_path())); let mut diag = BuildDiagnostics::default();
let syntax_node = parser::parse(source, Some(path.as_path()), &mut diag);
if diag.has_error() { if diag.has_error() {
let mut d = sixtyfps_compilerlib::diagnostics::BuildDiagnostics::default(); return (Err(()), diag);
d.add(diag);
return (Err(()), d);
} }
let (doc, diag) = compile_syntax_node(syntax_node, diag, compiler_config).await; let (doc, diag) = compile_syntax_node(syntax_node, diag, compiler_config).await;
if diag.has_error() { if diag.has_error() {

View file

@ -7,6 +7,7 @@
This file is also available under commercial licensing terms. This file is also available under commercial licensing terms.
Please contact info@sixtyfps.io for more information. Please contact info@sixtyfps.io for more information.
LICENSE END */ LICENSE END */
use sixtyfps_compilerlib::diagnostics::BuildDiagnostics;
use sixtyfps_compilerlib::*; use sixtyfps_compilerlib::*;
use std::io::Write; use std::io::Write;
use structopt::StructOpt; use structopt::StructOpt;
@ -41,7 +42,8 @@ struct Cli {
fn main() -> std::io::Result<()> { fn main() -> std::io::Result<()> {
let args = Cli::from_args(); let args = Cli::from_args();
let (syntax_node, diag) = parser::parse_file(&args.path)?; let mut diag = BuildDiagnostics::default();
let syntax_node = parser::parse_file(&args.path, &mut diag)?;
//println!("{:#?}", syntax_node); //println!("{:#?}", syntax_node);
if diag.has_error() { if diag.has_error() {
diag.print(); diag.print();
@ -67,7 +69,7 @@ fn main() -> std::io::Result<()> {
if let Some(depfile) = args.depfile { if let Some(depfile) = args.depfile {
let mut f = std::fs::File::create(depfile)?; let mut f = std::fs::File::create(depfile)?;
write!(f, "{}:", args.output.display())?; write!(f, "{}:", args.output.display())?;
for x in diag.files() { for x in &diag.all_loaded_files {
if x.is_absolute() { if x.is_absolute() {
write!(f, " {}", x.display())?; write!(f, " {}", x.display())?;
} }

View file

@ -196,18 +196,20 @@ fn reload_document(
let mut diag = BuildDiagnostics::default(); let mut diag = BuildDiagnostics::default();
spin_on::spin_on(document_cache.documents.load_file(&path_canon, path, content, &mut diag)); spin_on::spin_on(document_cache.documents.load_file(&path_canon, path, content, &mut diag));
for file_diag in diag.into_iter() { let mut lsp_diags = HashMap::<Url, Vec<lsp_types::Diagnostic>>::new();
if file_diag.current_path.path().is_relative() {
for d in diag.into_iter() {
if d.span.source_file.as_ref().unwrap().path().is_relative() {
continue; continue;
} }
let diagnostics = file_diag.inner.iter().map(|d| to_lsp_diag(d)).collect(); let uri = Url::from_file_path(d.span.source_file.as_ref().unwrap().path()).unwrap();
lsp_diags.entry(uri).or_default().push(to_lsp_diag(&d));
}
for (uri, diagnostics) in lsp_diags {
connection.sender.send(Message::Notification(lsp_server::Notification::new( connection.sender.send(Message::Notification(lsp_server::Notification::new(
"textDocument/publishDiagnostics".into(), "textDocument/publishDiagnostics".into(),
PublishDiagnosticsParams { PublishDiagnosticsParams { uri, diagnostics, version: None },
uri: Url::from_file_path(file_diag.current_path.path()).unwrap(),
diagnostics,
version: None,
},
)))?; )))?;
} }

View file

@ -20,6 +20,7 @@ LICENSE END */
//! cargo run --bin syntax_updater -- -i **/*.md //! cargo run --bin syntax_updater -- -i **/*.md
//! ```` //! ````
use sixtyfps_compilerlib::diagnostics::BuildDiagnostics;
use sixtyfps_compilerlib::parser::{SyntaxKind, SyntaxNode, SyntaxNodeEx}; use sixtyfps_compilerlib::parser::{SyntaxKind, SyntaxNode, SyntaxNodeEx};
use std::io::Write; use std::io::Write;
use structopt::StructOpt; use structopt::StructOpt;
@ -92,7 +93,8 @@ fn process_rust_file(source: String, mut file: impl Write) -> std::io::Result<()
let code = &source_slice[..idx - 1]; let code = &source_slice[..idx - 1];
source_slice = &source_slice[idx - 1..]; source_slice = &source_slice[idx - 1..];
let (syntax_node, diag) = sixtyfps_compilerlib::parser::parse(code.to_owned(), None); let mut diag = BuildDiagnostics::default();
let syntax_node = sixtyfps_compilerlib::parser::parse(code.to_owned(), None, &mut diag);
let len = syntax_node.node.text_range().end().into(); let len = syntax_node.node.text_range().end().into();
visit_node(syntax_node.node, &mut file, &mut State::default())?; visit_node(syntax_node.node, &mut file, &mut State::default())?;
if diag.has_error() { if diag.has_error() {
@ -120,7 +122,8 @@ fn process_markdown_file(source: String, mut file: impl Write) -> std::io::Resul
let code = &source_slice[..code_end]; let code = &source_slice[..code_end];
source_slice = &source_slice[code_end..]; source_slice = &source_slice[code_end..];
let (syntax_node, diag) = sixtyfps_compilerlib::parser::parse(code.to_owned(), None); let mut diag = BuildDiagnostics::default();
let syntax_node = sixtyfps_compilerlib::parser::parse(code.to_owned(), None, &mut diag);
let len = syntax_node.node.text_range().end().into(); let len = syntax_node.node.text_range().end().into();
visit_node(syntax_node.node, &mut file, &mut State::default())?; visit_node(syntax_node.node, &mut file, &mut State::default())?;
if diag.has_error() { if diag.has_error() {
@ -142,7 +145,8 @@ fn process_file(
_ => {} _ => {}
} }
let (syntax_node, diag) = sixtyfps_compilerlib::parser::parse(source.clone(), Some(&path)); let mut diag = BuildDiagnostics::default();
let syntax_node = sixtyfps_compilerlib::parser::parse(source.clone(), Some(&path), &mut diag);
let len = syntax_node.node.text_range().end().into(); let len = syntax_node.node.text_range().end().into();
visit_node(syntax_node.node, &mut file, &mut State::default())?; visit_node(syntax_node.node, &mut file, &mut State::default())?;
if diag.has_error() { if diag.has_error() {