use crate::{ config::{attributes::AttrError, config::Include, validate::ValidationError}, format_diagnostic::ToDiagnostic, parser::{ ast::{Ast, AstType}, lexer, parse_error, }, }; use codespan_reporting::{diagnostic, files}; use eww_shared_util::{AttrName, Span, Spanned, VarName}; use simplexpr::dynval; use thiserror::Error; pub type AstResult = Result; #[derive(Debug, Error)] pub enum AstError { #[error("Unknown toplevel declaration `{1}`")] UnknownToplevel(Span, String), #[error("Expected another element, but got nothing")] MissingNode(Span), #[error("Too many elements, must be exactly {1}")] TooManyNodes(Span, i32), #[error("Did not expect any further elements here. Make sure your format is correct")] NoMoreElementsExpected(Span), #[error(transparent)] FormFormatError(#[from] FormFormatError), #[error("Wrong type of expression: Expected {1} but got {2}")] WrongExprType(Span, AstType, AstType), #[error("Expected to get a value, but got {1}")] NotAValue(Span, AstType), #[error("Expected element {1}, but read {2}")] MismatchedElementName(Span, String, String), #[error("Keyword `{1}` is missing a value")] DanglingKeyword(Span, String), #[error("Included file not found {}", .0.path)] IncludedFileNotFound(Include), #[error("{}", .main_err.to_message())] ErrorContext { label_span: Span, context: String, main_err: Box }, #[error("{1}")] ErrorNote(String, #[source] Box), #[error(transparent)] SimplExpr(#[from] simplexpr::error::Error), #[error(transparent)] ConversionError(#[from] dynval::ConversionError), #[error("{1}")] Other(Span, Box), #[error(transparent)] AttrError(#[from] AttrError), #[error(transparent)] ValidationError(#[from] ValidationError), #[error("Parse error: {source}")] ParseError { file_id: usize, source: lalrpop_util::ParseError }, } static_assertions::assert_impl_all!(AstError: Send, Sync); static_assertions::assert_impl_all!(dynval::ConversionError: Send, Sync); static_assertions::assert_impl_all!(lalrpop_util::ParseError < usize, lexer::Token, parse_error::ParseError>: Send, Sync); impl AstError { pub fn note(self, note: &str) -> Self { AstError::ErrorNote(note.to_string(), Box::new(self)) } pub fn context_label(self, label_span: Span, context: &str) -> Self { AstError::ErrorContext { label_span, context: context.to_string(), main_err: Box::new(self) } } pub fn from_parse_error( file_id: usize, err: lalrpop_util::ParseError, ) -> AstError { AstError::ParseError { file_id, source: err } } pub fn wrong_expr_type_to>(self, f: impl FnOnce(Span, AstType) -> Option) -> AstError { match self { AstError::WrongExprType(span, expected, got) => { f(span.point_span(), got).map(|x| x.into()).unwrap_or_else(|| AstError::WrongExprType(span, expected, got)) } AstError::ErrorNote(s, err) => AstError::ErrorNote(s, Box::new(err.wrong_expr_type_to(f))), other => other, } } } impl Spanned for AstError { fn span(&self) -> Span { match self { AstError::UnknownToplevel(span, _) => *span, AstError::MissingNode(span) => *span, AstError::WrongExprType(span, ..) => *span, AstError::NotAValue(span, ..) => *span, AstError::MismatchedElementName(span, ..) => *span, AstError::DanglingKeyword(span, _) => *span, AstError::AttrError(err) => err.span(), AstError::Other(span, ..) => *span, AstError::ConversionError(err) => err.value.span(), AstError::IncludedFileNotFound(include) => include.path_span, AstError::TooManyNodes(span, ..) => *span, AstError::ErrorContext { label_span, .. } => *label_span, AstError::ValidationError(error) => error.span(), AstError::ParseError { file_id, source } => get_parse_error_span(*file_id, source), AstError::ErrorNote(_, err) => err.span(), AstError::NoMoreElementsExpected(span) => *span, AstError::SimplExpr(err) => err.span(), AstError::FormFormatError(err) => err.span(), } } } pub fn get_parse_error_span(file_id: usize, err: &lalrpop_util::ParseError) -> Span { use lalrpop_util::ParseError::*; match err { InvalidToken { location } => Span(*location, *location, file_id), UnrecognizedEOF { location, expected } => Span(*location, *location, file_id), UnrecognizedToken { token, expected } => Span(token.0, token.2, file_id), ExtraToken { token } => Span(token.0, token.2, file_id), User { error } => error.span(), } } pub trait OptionAstErrorExt { fn or_missing(self, span: Span) -> Result; } impl OptionAstErrorExt for Option { fn or_missing(self, span: Span) -> Result { self.ok_or(AstError::MissingNode(span)) } } pub trait AstResultExt { fn context_label(self, label_span: Span, context: &str) -> AstResult; fn note(self, note: &str) -> AstResult; /// Map any [AstError::WrongExprType]s error to any other Into (such as a [FormFormatError]) /// If the provided closure returns `None`, the error will be kept unmodified fn wrong_expr_type_to>(self, f: impl FnOnce(Span, AstType) -> Option) -> AstResult; } impl AstResultExt for AstResult { fn context_label(self, label_span: Span, context: &str) -> AstResult { self.map_err(|e| AstError::ErrorContext { label_span, context: context.to_string(), main_err: Box::new(e) }) } fn note(self, note: &str) -> AstResult { self.map_err(|e| e.note(note)) } fn wrong_expr_type_to>(self, f: impl FnOnce(Span, AstType) -> Option) -> AstResult { self.map_err(|err| err.wrong_expr_type_to(f)) } } #[derive(Debug, Error)] pub enum FormFormatError { #[error("Widget definition missing argument list")] WidgetDefArglistMissing(Span), #[error("Widget definition has more than one child widget")] WidgetDefMultipleChildren(Span), } impl Spanned for FormFormatError { fn span(&self) -> Span { match self { FormFormatError::WidgetDefArglistMissing(span) => *span, FormFormatError::WidgetDefMultipleChildren(span) => *span, } } }