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::path::Path;
use sixtyfps_compilerlib::diagnostics::BuildDiagnostics;
/// The structure for configuring aspects of the compilation of `.60` markup files to Rust.
pub struct 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)?)
.join(path.as_ref());
let (syntax_node, diag) =
sixtyfps_compilerlib::parser::parse_file(&path).map_err(CompileError::LoadError)?;
let mut diag = BuildDiagnostics::default();
let syntax_node = sixtyfps_compilerlib::parser::parse_file(&path, &mut diag)
.map_err(CompileError::LoadError)?;
if diag.has_error() {
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 generated = match sixtyfps_compilerlib::generator::rust::generate(&doc, &mut diag) {
Some(code) => {
for x in diag.files() {
for x in &diag.all_loaded_files {
if x.is_absolute() {
println!("cargo:rerun-if-changed={}", x.display());
}

View file

@ -19,7 +19,8 @@ You should use the `sixtyfps` crate instead.
extern crate proc_macro;
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::*;
@ -319,10 +320,10 @@ pub fn sixtyfps(stream: TokenStream) -> TokenStream {
let mut tokens = vec![];
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() {
diag.map_offsets_to_span(&tokens);
return diag.into_token_stream().into();
return diag.report_macro_diagnostic(&tokens);
}
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));
//println!("{:#?}", tree);
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);
// Make sure to recompile if any of the external files changes
let reload = diag
.files()
.all_loaded_files
.iter()
.filter(|path| path.is_absolute() && !path.ends_with("Cargo.toml"))
.filter_map(|p| p.to_str())
.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");});
});
let diags = report_diagnostics(diag, &tokens);
let diags = diag.report_macro_diagnostic(&tokens);
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,29 +77,26 @@ pub async fn compile_from_string(
let level_key = JsValue::from_str("level");
let mut error_as_string = String::new();
let array = js_sys::Array::new();
for diag in errors.into_iter() {
let filename_js = JsValue::from_str(&diag.current_path.display().to_string());
for d in &diag.inner {
for d in errors.into_iter() {
let filename = d
.span
.source_file
.as_ref()
.map_or(String::new(), |sf| sf.path().to_string_lossy().into());
let filename_js = JsValue::from_str(&filename);
if !error_as_string.is_empty() {
error_as_string.push_str("\n");
}
use std::fmt::Write;
let (line, column) = d.line_column(&diag);
write!(&mut error_as_string, "{}:{}:{}", diag.current_path.display(), line, d)
.unwrap();
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.to_string()),
)?;
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, &column_key, &JsValue::from_f64(column as f64))?;
js_sys::Reflect::set(&error_obj, &file_key, &filename_js)?;
js_sys::Reflect::set(
&error_obj,
@ -108,7 +105,6 @@ pub async fn compile_from_string(
)?;
array.push(&error_obj);
}
}
let error = js_sys::Error::new(&error_as_string);
js_sys::Reflect::set(&error, &JsValue::from_str("errors"), &array)?;

View file

@ -11,12 +11,12 @@ use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::rc::Rc;
#[derive(Debug, Clone)]
/// Span represent an error location within a 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.
#[derive(Debug, Clone)]
pub struct Span {
pub offset: usize,
#[cfg(feature = "proc_macro_span")]
@ -108,14 +108,8 @@ pub type SourceFile = Rc<SourceFileInner>;
#[derive(Debug, Clone, Default)]
pub struct SourceLocation {
source_file: Option<SourceFile>,
span: Span,
}
impl From<SyntaxNodeWithSourceFile> for SourceLocation {
fn from(node: SyntaxNodeWithSourceFile) -> Self {
SourceLocation { span: node.span(), source_file: node.source_file }
}
pub source_file: Option<SourceFile>,
pub span: Span,
}
impl Spanned for SourceLocation {
@ -161,39 +155,23 @@ impl From<DiagnosticLevel> for codemap_diagnostic::Level {
}
}
#[derive(thiserror::Error, Default, Debug)]
#[error("{message}")]
pub struct CompilerDiagnostic {
#[derive(Debug, Clone)]
pub struct Diagnostic {
pub message: String,
pub span: SourceLocation,
pub level: DiagnosticLevel,
}
#[derive(thiserror::Error, Debug)]
pub enum Diagnostic {
#[error(transparent)]
FileLoadError(#[from] std::io::Error),
#[error(transparent)]
CompilerDiagnostic(#[from] CompilerDiagnostic),
}
impl Diagnostic {
/// Return the level for this diagnostic
pub fn level(&self) -> DiagnosticLevel {
match self {
Diagnostic::CompilerDiagnostic(e) => e.level,
_ => DiagnosticLevel::Error,
}
self.level
}
/// Returns a tuple with the line (starting at 1) and column number (starting at 0)
pub fn line_column(&self) -> (usize, usize) {
let source_location = match self {
Diagnostic::CompilerDiagnostic(e) => &e.span,
_ => return (0, 0),
};
let offset = source_location.span.offset;
let line_offsets = match &source_location.source_file {
let offset = self.span.span.offset;
let line_offsets = match &self.span.source_file {
None => return (0, 0),
Some(sl) => sl.line_offsets(),
};
@ -210,16 +188,29 @@ impl Diagnostic {
}
}
/// This structure holds all the diagnostics for a given files
#[derive(Default, Debug)]
pub struct FileDiagnostics {
/// List of diagnostics related to this file
pub inner: Vec<Diagnostic>,
/// file path
pub current_path: SourceFile,
impl std::fmt::Display for Diagnostic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(sf) = self.span.source_file() {
let (line, _) = self.line_column();
write!(f, "{}:{}: {}", sf.path.display(), line, self.message)
} else {
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 IntoIter = <Vec<Diagnostic> as IntoIterator>::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(
&mut self,
message: String,
span: Span,
span: SourceLocation,
level: DiagnosticLevel,
) {
let span = SourceLocation { source_file: Some(self.current_path.clone()), span };
self.inner.push(CompilerDiagnostic { message, span, level }.into());
self.inner.push(Diagnostic { 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)
}
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());
}
@ -258,17 +248,14 @@ impl FileDiagnostics {
"The property '{}' has been deprecated. Please use '{}' instead",
old_property, new_property
),
source.span(),
source.to_source_location(),
crate::diagnostics::DiagnosticLevel::Warning,
)
}
/// Return true if there is at least one compilation error for this file
pub fn has_error(&self) -> bool {
self.inner.iter().any(|diag| match diag {
Diagnostic::FileLoadError(_) => true,
Diagnostic::CompilerDiagnostic(diag) => matches!(diag.level, DiagnosticLevel::Error),
})
self.inner.iter().any(|diag| diag.level == DiagnosticLevel::Error)
}
/// Return true if there are no diagnostics (warnings or errors); false otherwise.
@ -280,6 +267,7 @@ impl FileDiagnostics {
fn call_diagnostics<'a, Output>(
self,
output: &'a mut Output,
mut handle_no_source: Option<&mut dyn FnMut(Diagnostic)>,
emitter_factory: impl for<'b> FnOnce(
&'b mut Output,
Option<&'b codemap::CodeMap>,
@ -290,22 +278,30 @@ impl FileDiagnostics {
}
let mut codemap = codemap::CodeMap::new();
let internal_errors = self.current_path.source.is_none();
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 mut codemap_files = HashMap::new();
let diags: Vec<_> = self
.inner
.into_iter()
.map(|diagnostic| match diagnostic {
Diagnostic::CompilerDiagnostic(CompilerDiagnostic { message, span, level }) => {
let spans = if !internal_errors && span.span.is_valid() {
.filter_map(|d| {
let spans = if let Some(sf) = &d.span.source_file {
if let Some(ref mut handle_no_source) = handle_no_source {
if sf.source.is_none() {
handle_no_source(d);
return None;
}
}
let path: String = sf.path.to_string_lossy().into();
let file = codemap_files.entry(path).or_insert_with(|| {
codemap.add_file(
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(span.span.offset as u64, span.span.offset as u64),
.subspan(d.span.span.offset as u64, d.span.span.offset as u64),
style: codemap_diagnostic::SpanStyle::Primary,
label: None,
};
@ -313,19 +309,12 @@ impl FileDiagnostics {
} else {
vec![]
};
codemap_diagnostic::Diagnostic {
level: level.into(),
message,
Some(codemap_diagnostic::Diagnostic {
level: d.level.into(),
message: d.message,
code: None,
spans,
}
}
Diagnostic::FileLoadError(err) => codemap_diagnostic::Diagnostic {
level: codemap_diagnostic::Level::Error,
message: err.to_string(),
code: None,
spans: vec![],
},
})
})
.collect();
@ -336,7 +325,7 @@ impl FileDiagnostics {
#[cfg(feature = "display-diagnostics")]
/// Print the diagnostics on the console
pub fn print(self) {
self.call_diagnostics(&mut (), |_, codemap| {
self.call_diagnostics(&mut (), None, |_, codemap| {
codemap_diagnostic::Emitter::stderr(codemap_diagnostic::ColorConfig::Always, codemap)
});
}
@ -345,7 +334,7 @@ impl FileDiagnostics {
/// Print into a string
pub fn diagnostics_as_string(self) -> String {
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)
});
@ -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
pub fn map_offsets_to_span(&mut self, span_map: &[crate::parser::Token]) {
for d in &mut self.inner {
if let Diagnostic::CompilerDiagnostic(d) = d {
if d.span.span.span.is_none() {
pub fn report_macro_diagnostic(
self,
span_map: &[crate::parser::Token],
) -> 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 =
//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);
let mut offset = 0;
d.span.span.span = span_map.iter().find_map(|t| {
if d.span.span.offset <= offset {
span_map.iter().find_map(|t| {
if diag.span.span.offset <= offset {
t.span
} else {
offset += t.text.len();
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> {
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(
&mut self,
message: String,
source: &dyn Spanned,
level: DiagnosticLevel,
) {
match source.source_file() {
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)
self.push_diagnostic_with_span(message, source.to_source_location(), level)
}
pub fn push_internal_error(&mut self, err: Diagnostic) {
self.internal_errors
.get_or_insert_with(|| FileDiagnostics {
current_path: Rc::new(SourceFileInner {
path: "[internal error]".into(),
..Default::default()
}),
..Default::default()
})
.inner
.push(err)
self.inner.push(err)
}
pub fn push_property_deprecation_warning(
&mut self,
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")
pub fn iter(&self) -> impl Iterator<Item = &Diagnostic> {
self.inner.iter()
}
#[cfg(feature = "display-diagnostics")]
@ -549,22 +435,4 @@ impl BuildDiagnostics {
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 {
Self {
expression: Expression::Uncompiled(node.clone()),
span: Some(node.into()),
span: Some(node.to_source_location()),
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::{
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 {
ty.cpp_type().unwrap_or_else(|| {
let err = CompilerDiagnostic {
let err = crate::diagnostics::Diagnostic {
message: "Cannot map property type to C++".into(),
span: type_node.to_source_location(),
level: DiagnosticLevel::Error,
@ -1300,7 +1300,7 @@ fn model_data_type(parent_element: &ElementRc, diag: &mut BuildDiagnostics) -> S
.ty();
model_data_type.cpp_type().unwrap_or_else(|| {
diag.push_internal_error(
CompilerDiagnostic {
crate::diagnostics::Diagnostic {
message: format!("Cannot map property type {} to C++", model_data_type),
span: parent_element
.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
*/
use crate::diagnostics::{BuildDiagnostics, CompilerDiagnostic, DiagnosticLevel};
use crate::diagnostics::{BuildDiagnostics, Diagnostic, DiagnosticLevel};
use crate::expression_tree::{
BuiltinFunction, EasingCurve, Expression, NamedReference, OperatorClass, Path,
};
@ -27,7 +27,7 @@ use std::{collections::BTreeMap, rc::Rc};
fn rust_type(
ty: &Type,
span: Option<&dyn crate::diagnostics::Spanned>,
) -> Result<proc_macro2::TokenStream, CompilerDiagnostic> {
) -> Result<proc_macro2::TokenStream, Diagnostic> {
match ty {
Type::Int32 => Ok(quote!(i32)),
Type::Float32 => Ok(quote!(f32)),
@ -56,7 +56,7 @@ fn rust_type(
Ok(quote!(sixtyfps::re_exports::#e))
}
Type::Brush => Ok(quote!(sixtyfps::Brush)),
_ => Err(CompilerDiagnostic {
_ => Err(Diagnostic {
message: format!("Cannot map property type {} to Rust", ty),
span: span.map(|s| s.to_source_location()).unwrap_or_default(),
level: DiagnosticLevel::Error,

View file

@ -119,11 +119,9 @@ impl CompilerConfiguration {
pub async fn compile_syntax_node(
doc_node: parser::SyntaxNodeWithSourceFile,
mut diagnostics: diagnostics::FileDiagnostics,
mut diagnostics: diagnostics::BuildDiagnostics,
compiler_config: CompilerConfiguration,
) -> (object_tree::Document, diagnostics::BuildDiagnostics) {
let mut build_diagnostics = diagnostics::BuildDiagnostics::default();
let global_type_registry = typeregister::TypeRegister::builtin();
let 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 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() {
loader
.load_dependencies_recursively(
&doc_node,
&mut diagnostics,
&mut build_diagnostics,
&type_registry,
)
.await;
loader.load_dependencies_recursively(&doc_node, &mut diagnostics, &type_registry).await;
}
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() {
build_diagnostics
diagnostics
.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.
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(

View file

@ -27,7 +27,8 @@ use crate::typeregister::TypeRegister;
/// `register` is the register to fill with the builtin types.
/// At this point, it really should already contain the basic Types (string, int, ...)
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() {
let vec = diag.to_string_vec();
#[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
*/
use crate::diagnostics::{FileDiagnostics, Spanned};
use crate::diagnostics::{BuildDiagnostics, Spanned};
use crate::expression_tree::{self, Unit};
use crate::expression_tree::{BindingExpression, Expression, NamedReference};
use crate::langtype::PropertyLookupResult;
@ -36,7 +36,7 @@ pub struct Document {
impl Document {
pub fn from_node(
node: syntax_nodes::Document,
diag: &mut FileDiagnostics,
diag: &mut BuildDiagnostics,
parent_registry: &Rc<RefCell<TypeRegister>>,
) -> Self {
debug_assert_eq!(node.kind(), SyntaxKind::Document);
@ -47,7 +47,7 @@ impl Document {
let mut process_component =
|n: syntax_nodes::Component,
diag: &mut FileDiagnostics,
diag: &mut BuildDiagnostics,
local_registry: &mut TypeRegister| {
let compo = Component::from_node(n, diag, local_registry);
local_registry.add(compo.clone());
@ -55,7 +55,7 @@ impl Document {
};
let mut process_struct =
|n: syntax_nodes::StructDeclaration,
diag: &mut FileDiagnostics,
diag: &mut BuildDiagnostics,
local_registry: &mut TypeRegister| {
let mut ty = type_struct_from_node(n.ObjectType(), diag, local_registry);
if let Type::Object { name, .. } = &mut ty {
@ -172,7 +172,7 @@ pub struct Component {
impl Component {
pub fn from_node(
node: syntax_nodes::Component,
diag: &mut FileDiagnostics,
diag: &mut BuildDiagnostics,
tr: &TypeRegister,
) -> Rc<Self> {
let mut child_insertion_point = None;
@ -373,7 +373,7 @@ impl Element {
id: String,
parent_type: Type,
component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
diag: &mut FileDiagnostics,
diag: &mut BuildDiagnostics,
tr: &TypeRegister,
) -> ElementRc {
let (base_type, name_for_looup_errors) = if let Some(base_node) = node.QualifiedName() {
@ -711,7 +711,7 @@ impl Element {
node: syntax_nodes::RepeatedElement,
parent: &ElementRc,
component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
diag: &mut FileDiagnostics,
diag: &mut BuildDiagnostics,
tr: &TypeRegister,
) -> ElementRc {
let is_listview = if parent.borrow().base_type.to_string() == "ListView" {
@ -751,7 +751,7 @@ impl Element {
node: syntax_nodes::ConditionalElement,
parent_type: Type,
component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
diag: &mut FileDiagnostics,
diag: &mut BuildDiagnostics,
tr: &TypeRegister,
) -> ElementRc {
let rei = RepeatedElementInfo {
@ -794,7 +794,7 @@ impl Element {
bindings: impl Iterator<
Item = (crate::parser::SyntaxTokenWithSourceFile, SyntaxNodeWithSourceFile),
>,
diag: &mut FileDiagnostics,
diag: &mut BuildDiagnostics,
) {
for (name_token, b) in bindings {
let unresolved_name = crate::parser::normalize_identifier(name_token.text());
@ -854,7 +854,7 @@ impl Element {
/// Create a Type for this node
pub fn type_from_node(
node: syntax_nodes::Type,
diag: &mut FileDiagnostics,
diag: &mut BuildDiagnostics,
tr: &TypeRegister,
) -> Type {
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
pub fn type_struct_from_node(
object_node: syntax_nodes::ObjectType,
diag: &mut FileDiagnostics,
diag: &mut BuildDiagnostics,
tr: &TypeRegister,
) -> Type {
let fields = object_node
@ -898,7 +898,7 @@ fn animation_element_from_node(
anim: &syntax_nodes::PropertyAnimation,
prop_name: &syntax_nodes::QualifiedName,
prop_type: Type,
diag: &mut FileDiagnostics,
diag: &mut BuildDiagnostics,
tr: &TypeRegister,
) -> Option<ElementRc> {
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(
node: syntax_nodes::QualifiedName,
r: &Rc<RefCell<Element>>,
diag: &mut FileDiagnostics,
diag: &mut BuildDiagnostics,
) -> (NamedReference, Type) {
let qualname = QualifiedTypeName::from_node(node.clone());
match qualname.members.as_slice() {
@ -1261,7 +1261,7 @@ impl Exports {
doc: &syntax_nodes::Document,
inner_components: &[Rc<Component>],
type_registry: &TypeRegister,
diag: &mut FileDiagnostics,
diag: &mut BuildDiagnostics,
) -> Self {
#[derive(Debug, Clone)]
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;
use std::convert::TryFrom;
@ -506,23 +506,28 @@ mod parser_trait {
#[doc(inline)]
pub use parser_trait::*;
pub struct DefaultParser {
pub struct DefaultParser<'a> {
builder: rowan::GreenNodeBuilder<'static>,
tokens: Vec<Token>,
cursor: usize,
diags: FileDiagnostics,
diags: &'a mut BuildDiagnostics,
source_file: SourceFile,
}
impl From<Vec<Token>> for DefaultParser {
fn from(tokens: Vec<Token>) -> Self {
Self { builder: Default::default(), tokens, cursor: 0, diags: Default::default() }
impl<'a> DefaultParser<'a> {
fn from_tokens(tokens: Vec<Token>, diags: &'a mut BuildDiagnostics) -> Self {
Self {
builder: Default::default(),
tokens,
cursor: 0,
diags,
source_file: Default::default(),
}
}
}
impl DefaultParser {
/// Constructor that create a parser from the source code
pub fn new(source: &str) -> Self {
Self::from(crate::lexer::lex(&source))
pub fn new(source: &str, diags: &'a mut BuildDiagnostics) -> Self {
Self::from_tokens(crate::lexer::lex(&source), diags)
}
fn current_token(&self) -> Token {
@ -537,7 +542,7 @@ impl DefaultParser {
}
}
impl Parser for DefaultParser {
impl Parser for DefaultParser<'_> {
fn start_node_impl(
&mut self,
kind: SyntaxKind,
@ -586,7 +591,14 @@ impl Parser for DefaultParser {
{
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;
@ -808,33 +820,30 @@ pub fn normalize_identifier(ident: &str) -> String {
pub fn parse(
source: String,
path: Option<&std::path::Path>,
) -> (SyntaxNodeWithSourceFile, FileDiagnostics) {
let mut p = DefaultParser::new(&source);
document::parse_document(&mut p);
build_diagnostics: &mut BuildDiagnostics,
) -> SyntaxNodeWithSourceFile {
let mut p = DefaultParser::new(&source, build_diagnostics);
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));
Some(p.diags.current_path.clone())
Some(p.source_file.clone())
} else {
None
};
(
SyntaxNodeWithSourceFile { node: SyntaxNode::new_root(p.builder.finish()), source_file },
p.diags,
)
document::parse_document(&mut p);
SyntaxNodeWithSourceFile { node: SyntaxNode::new_root(p.builder.finish()), source_file }
}
pub fn parse_file<P: AsRef<std::path::Path>>(
path: P,
) -> std::io::Result<(SyntaxNodeWithSourceFile, FileDiagnostics)> {
build_diagnostics: &mut BuildDiagnostics,
) -> std::io::Result<SyntaxNodeWithSourceFile> {
let source = std::fs::read_to_string(&path)?;
Ok(parse(source, Some(path.as_ref())))
Ok(parse(source, Some(path.as_ref()), build_diagnostics))
}
#[allow(dead_code)]
pub fn parse_tokens(tokens: Vec<Token>) -> (SyntaxNode, FileDiagnostics) {
let mut p = DefaultParser::from(tokens);
pub fn parse_tokens(tokens: Vec<Token>, diags: &mut BuildDiagnostics) -> SyntaxNode {
let mut p = DefaultParser::from_tokens(tokens, diags);
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,
) {
// Ignore import errors
let mut file_diags_to_ignore = crate::diagnostics::FileDiagnostics::default();
let mut build_diags_to_ignore = BuildDiagnostics::default();
let style_metrics = type_loader
.import_type(
"sixtyfps_widgets.60",
"StyleMetrics",
&mut file_diags_to_ignore,
&mut build_diags_to_ignore,
)
.import_type("sixtyfps_widgets.60", "StyleMetrics", &mut build_diags_to_ignore)
.await;
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,
) {
// Ignore import errors
let mut file_diags_to_ignore = crate::diagnostics::FileDiagnostics::default();
let mut build_diags_to_ignore = crate::diagnostics::BuildDiagnostics::default();
let style_metrics = type_loader
.import_type(
"sixtyfps_widgets.60",
"StyleMetrics",
&mut file_diags_to_ignore,
&mut build_diags_to_ignore,
)
.import_type("sixtyfps_widgets.60", "StyleMetrics", &mut build_diags_to_ignore)
.await;
let style_metrics =
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}
//! ```
use std::path::{Path, PathBuf};
#[test]
fn syntax_tests() -> std::io::Result<()> {
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(
mut compile_diagnostics: sixtyfps_compilerlib::diagnostics::FileDiagnostics,
source: String,
compile_diagnostics: &sixtyfps_compilerlib::diagnostics::BuildDiagnostics,
path: &Path,
source: &str,
silent: bool,
) -> std::io::Result<bool> {
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
// 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),
};
match compile_diagnostics.inner.iter().position(|e| match e {
sixtyfps_compilerlib::diagnostics::Diagnostic::FileLoadError(_) => false,
sixtyfps_compilerlib::diagnostics::Diagnostic::CompilerDiagnostic(e) => {
e.span.offset == offset && r.is_match(&e.message) && e.level == expected_diag_level
}
match diags.iter().position(|e| {
e.span.span.offset == offset && r.is_match(&e.message) && e.level == expected_diag_level
}) {
Some(idx) => {
compile_diagnostics.inner.remove(idx);
diags.remove(idx);
}
None => {
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() {
println!("{:?}: Unexptected errors/warnings: {:#?}", path, compile_diagnostics.inner);
if !silent {
#[cfg(feature = "display-diagnostics")]
compile_diagnostics.print();
if !silent {
let mut to_report = sixtyfps_compilerlib::diagnostics::BuildDiagnostics::default();
for d in diags {
to_report.push_compiler_error(d.clone());
}
to_report.print();
}
success = false;
}
Ok(success)
}
fn cannonical(path: &Path) -> PathBuf {
path.canonicalize().unwrap_or_else(|_| path.to_owned())
}
fn process_file_source(
path: &std::path::Path,
source: String,
silent: bool,
) -> std::io::Result<bool> {
let (syntax_node, parse_diagnostics) =
sixtyfps_compilerlib::parser::parse(source.clone(), Some(path));
let mut parse_diagnostics = sixtyfps_compilerlib::diagnostics::BuildDiagnostics::default();
let syntax_node =
sixtyfps_compilerlib::parser::parse(source.clone(), Some(path), &mut parse_diagnostics);
let compile_diagnostics = if !parse_diagnostics.has_error() {
let mut compiler_config = sixtyfps_compilerlib::CompilerConfiguration::new(
sixtyfps_compilerlib::generator::OutputFormat::Interpreter,
@ -148,22 +159,22 @@ fn process_file_source(
parse_diagnostics,
compiler_config,
));
build_diags.into_iter().collect()
build_diags
} else {
vec![parse_diagnostics]
parse_diagnostics
};
assert!(compile_diagnostics.len() >= 1);
let mut success = true;
success &= process_diagnostics(&compile_diagnostics, &path, &source, silent)?;
for diagnostics in compile_diagnostics.into_iter() {
let source = if diagnostics.current_path.path() == path {
source.clone()
for p in &compile_diagnostics.all_loaded_files {
let source = if p.is_absolute() {
std::fs::read_to_string(&p)?
} 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)

View file

@ -14,7 +14,7 @@ use std::io::Read;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use crate::diagnostics::{BuildDiagnostics, CompilerDiagnostic, FileDiagnostics};
use crate::diagnostics::{BuildDiagnostics, Diagnostic};
use crate::object_tree::{self, Document};
use crate::parser::{syntax_nodes, SyntaxKind, SyntaxTokenWithSourceFile};
use crate::typeregister::TypeRegister;
@ -131,7 +131,7 @@ impl<'a> TypeLoader<'a> {
let is_wasm = cfg!(target_arch = "wasm32")
|| std::env::var("TARGET").map_or(false, |t| t.starts_with("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(),
span: Default::default(),
level: crate::diagnostics::DiagnosticLevel::Warning
@ -154,14 +154,12 @@ impl<'a> TypeLoader<'a> {
pub async fn load_dependencies_recursively(
&mut self,
doc: &syntax_nodes::Document,
mut diagnostics: &mut FileDiagnostics,
build_diagnostics: &mut BuildDiagnostics,
diagnostics: &mut BuildDiagnostics,
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() {
self.load_dependency(import, registry_to_populate, diagnostics, build_diagnostics)
.await;
self.load_dependency(import, registry_to_populate, diagnostics).await;
}
}
@ -169,8 +167,7 @@ impl<'a> TypeLoader<'a> {
&mut self,
file_to_import: &str,
type_name: &str,
diagnostics: &mut FileDiagnostics,
build_diagnostics: &mut BuildDiagnostics,
diagnostics: &mut BuildDiagnostics,
) -> Option<crate::langtype::Type> {
let file = match self.import_file(None, file_to_import) {
Some(file) => file,
@ -178,13 +175,7 @@ impl<'a> TypeLoader<'a> {
};
let doc_path = match self
.ensure_document_loaded(
&file.path,
file.source_code_future,
None,
Some(diagnostics),
build_diagnostics,
)
.ensure_document_loaded(&file.path, file.source_code_future, None, diagnostics)
.await
{
Some(doc_path) => doc_path,
@ -207,8 +198,7 @@ impl<'a> TypeLoader<'a> {
path: &'b Path,
source_code_future: impl std::future::Future<Output = std::io::Result<String>>,
import_token: Option<SyntaxTokenWithSourceFile>,
importer_diagnostics: Option<&'b mut FileDiagnostics>,
build_diagnostics: &'b mut BuildDiagnostics,
diagnostics: &'b mut BuildDiagnostics,
) -> Option<PathBuf> {
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()) {
importer_diagnostics
.unwrap()
diagnostics
.push_error(format!("Recursive import of {}", path.display()), &import_token);
return None;
}
@ -226,7 +215,7 @@ impl<'a> TypeLoader<'a> {
let source_code = match source_code_future.await {
Ok(source) => source,
Err(err) => {
importer_diagnostics.unwrap().push_error(
diagnostics.push_error(
format!("Error reading requested import {}: {}", path.display(), err),
&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());
assert!(_ok);
Some(path_canon)
@ -248,13 +237,11 @@ impl<'a> TypeLoader<'a> {
path: &Path,
source_path: &Path,
source_code: String,
build_diagnostics: &mut BuildDiagnostics,
diagnostics: &mut BuildDiagnostics,
) {
let (dependency_doc, mut dependency_diagnostics) =
crate::parser::parse(source_code, Some(&source_path));
let dependency_doc = crate::parser::parse(source_code, Some(&source_path), diagnostics);
if dependency_diagnostics.has_error() {
build_diagnostics.add(dependency_diagnostics);
if diagnostics.has_error() {
let mut d = Document::default();
d.node = Some(dependency_doc.into());
self.all_documents.docs.insert(path.to_owned(), d);
@ -265,25 +252,15 @@ impl<'a> TypeLoader<'a> {
let dependency_registry =
Rc::new(RefCell::new(TypeRegister::new(&self.global_type_registry)));
self.load_dependencies_recursively(
&dependency_doc,
&mut dependency_diagnostics,
build_diagnostics,
&dependency_registry,
)
self.load_dependencies_recursively(&dependency_doc, diagnostics, &dependency_registry)
.await;
let doc = crate::object_tree::Document::from_node(
dependency_doc,
&mut dependency_diagnostics,
diagnostics,
&dependency_registry,
);
crate::passes::resolving::resolve_expressions(&doc, &self, build_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);
crate::passes::resolving::resolve_expressions(&doc, &self, diagnostics);
self.all_documents.docs.insert(path.to_owned(), doc);
}
@ -291,7 +268,6 @@ impl<'a> TypeLoader<'a> {
&'b mut self,
import: ImportedTypes,
registry_to_populate: &'b Rc<RefCell<TypeRegister>>,
importer_diagnostics: &'b mut FileDiagnostics,
build_diagnostics: &'b mut BuildDiagnostics,
) -> core::pin::Pin<Box<dyn std::future::Future<Output = ()> + 'b>> {
Box::pin(async move {
@ -300,7 +276,6 @@ impl<'a> TypeLoader<'a> {
&import.file.path,
import.file.source_code_future,
Some(import.import_token.clone()),
Some(importer_diagnostics),
build_diagnostics,
)
.await
@ -324,7 +299,7 @@ impl<'a> TypeLoader<'a> {
let imported_type = match imported_type {
Some(ty) => ty,
None => {
importer_diagnostics.push_error(
build_diagnostics.push_error(
format!(
"No exported type called {} found in {}",
import_name.external_name,
@ -391,7 +366,7 @@ impl<'a> TypeLoader<'a> {
async fn collect_dependencies(
&mut self,
doc: &syntax_nodes::Document,
doc_diagnostics: &mut FileDiagnostics,
doc_diagnostics: &mut BuildDiagnostics,
) -> impl Iterator<Item = ImportedTypes> {
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),
)
}
/// 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]
@ -535,7 +515,6 @@ fn test_manual_import() {
let mut compiler_config =
CompilerConfiguration::new(crate::generator::OutputFormat::Interpreter);
compiler_config.style = Some("ugly".into());
let mut test_diags = FileDiagnostics::default();
let global_registry = TypeRegister::builtin();
let mut build_diagnostics = BuildDiagnostics::default();
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(
"sixtyfps_widgets.60",
"Button",
&mut test_diags,
&mut build_diagnostics,
));
assert!(!test_diags.has_error());
assert!(!build_diagnostics.has_error());
assert!(maybe_button_type.is_some());
}

View file

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

View file

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

View file

@ -196,18 +196,20 @@ fn reload_document(
let mut diag = BuildDiagnostics::default();
spin_on::spin_on(document_cache.documents.load_file(&path_canon, path, content, &mut diag));
for file_diag in diag.into_iter() {
if file_diag.current_path.path().is_relative() {
let mut lsp_diags = HashMap::<Url, Vec<lsp_types::Diagnostic>>::new();
for d in diag.into_iter() {
if d.span.source_file.as_ref().unwrap().path().is_relative() {
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(
"textDocument/publishDiagnostics".into(),
PublishDiagnosticsParams {
uri: Url::from_file_path(file_diag.current_path.path()).unwrap(),
diagnostics,
version: None,
},
PublishDiagnosticsParams { uri, diagnostics, version: None },
)))?;
}

View file

@ -20,6 +20,7 @@ LICENSE END */
//! cargo run --bin syntax_updater -- -i **/*.md
//! ````
use sixtyfps_compilerlib::diagnostics::BuildDiagnostics;
use sixtyfps_compilerlib::parser::{SyntaxKind, SyntaxNode, SyntaxNodeEx};
use std::io::Write;
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];
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();
visit_node(syntax_node.node, &mut file, &mut State::default())?;
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];
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();
visit_node(syntax_node.node, &mut file, &mut State::default())?;
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();
visit_node(syntax_node.node, &mut file, &mut State::default())?;
if diag.has_error() {