From 70eede4fd012f49dfab0e2e27fb3a4e4bbff6325 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 1 Feb 2020 22:04:34 +0000 Subject: Implement once nice error using annotate_snippets --- dhall/src/error/builder.rs | 100 +++++++++++++++++++++++++++++++++++++++++++++ dhall/src/error/mod.rs | 3 ++ 2 files changed, 103 insertions(+) create mode 100644 dhall/src/error/builder.rs (limited to 'dhall/src/error') diff --git a/dhall/src/error/builder.rs b/dhall/src/error/builder.rs new file mode 100644 index 0000000..e293220 --- /dev/null +++ b/dhall/src/error/builder.rs @@ -0,0 +1,100 @@ +use annotate_snippets::{ + display_list::DisplayList, + formatter::DisplayListFormatter, + snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation}, +}; + +use crate::syntax::Span; + +#[derive(Debug, Clone)] +pub struct ErrorBuilder { + message: String, + annotation_type: AnnotationType, + annotations: Vec, +} + +#[derive(Debug, Clone)] +struct BuilderAnnotation { + span: Span, + message: String, + annotation_type: AnnotationType, +} + +/// A builder that uses the annotate_snippets library to display nice error messages about source +/// code locations. +impl ErrorBuilder { + pub fn new(message: String) -> Self { + ErrorBuilder { + message, + annotation_type: AnnotationType::Error, + annotations: Vec::new(), + } + } + pub fn span_err(span: &Span, message: String) -> Self { + let mut builder = Self::new(message.clone()); + builder.annotate_err(span, message); + builder + } + + pub fn annotate_err(&mut self, span: &Span, message: String) { + self.annotations.push(BuilderAnnotation { + span: span.clone(), + message, + annotation_type: AnnotationType::Error, + }) + } + pub fn annotate_info(&mut self, span: &Span, message: String) { + self.annotations.push(BuilderAnnotation { + span: span.clone(), + message, + annotation_type: AnnotationType::Help, + }) + } + + // TODO: handle multiple files + pub fn format(self) -> String { + let mut input = None; + let annotations = self + .annotations + .into_iter() + .filter_map(|annot| { + let span = match annot.span { + Span::Parsed(span) => span, + _ => return None, + }; + if input.is_none() { + input = Some(span.to_input()); + } + Some(SourceAnnotation { + label: annot.message, + annotation_type: annot.annotation_type, + range: span.as_char_range(), + }) + }) + .collect(); + + let input = match input { + Some(input) => input, + None => return format!("[unknown location] {}", self.message), + }; + + let snippet = Snippet { + title: Some(Annotation { + label: Some(self.message), + id: None, + annotation_type: self.annotation_type, + }), + footer: vec![], + slices: vec![Slice { + source: input, + line_start: 1, // TODO + origin: Some("".to_string()), + fold: true, + annotations, + }], + }; + let dl = DisplayList::from(snippet); + let dlf = DisplayListFormatter::new(true, false); + format!("{}", dlf.format(&dl)) + } +} diff --git a/dhall/src/error/mod.rs b/dhall/src/error/mod.rs index 4df018d..5b7693e 100644 --- a/dhall/src/error/mod.rs +++ b/dhall/src/error/mod.rs @@ -5,6 +5,9 @@ use crate::semantics::Value; use crate::syntax::{Import, ParseError}; use crate::NormalizedExpr; +mod builder; +pub(crate) use builder::*; + pub type Result = std::result::Result; #[derive(Debug)] -- cgit v1.2.3 From 92bbea48f9a0380a614f2687c73d55a67ff9294e Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 2 Feb 2020 15:02:23 +0000 Subject: More nice errors plus some refactor --- dhall/src/error/builder.rs | 169 +++++++++++++++++++++++++++++++-------------- dhall/src/error/mod.rs | 9 --- 2 files changed, 117 insertions(+), 61 deletions(-) (limited to 'dhall/src/error') diff --git a/dhall/src/error/builder.rs b/dhall/src/error/builder.rs index e293220..22b0d77 100644 --- a/dhall/src/error/builder.rs +++ b/dhall/src/error/builder.rs @@ -4,97 +4,162 @@ use annotate_snippets::{ snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation}, }; -use crate::syntax::Span; +use crate::syntax::{ParsedSpan, Span}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct ErrorBuilder { + title: FreeAnnotation, + annotations: Vec, + footer: Vec, + /// Inducate that the current builder has already been consumed and consuming it again should + /// panic. + consumed: bool, +} + +#[derive(Debug, Clone)] +struct SpannedAnnotation { + span: ParsedSpan, message: String, annotation_type: AnnotationType, - annotations: Vec, } #[derive(Debug, Clone)] -struct BuilderAnnotation { - span: Span, +struct FreeAnnotation { message: String, annotation_type: AnnotationType, } +impl SpannedAnnotation { + fn into_annotation(self) -> SourceAnnotation { + SourceAnnotation { + label: self.message, + annotation_type: self.annotation_type, + range: self.span.as_char_range(), + } + } +} + +impl FreeAnnotation { + fn into_annotation(self) -> Annotation { + Annotation { + label: Some(self.message), + id: None, + annotation_type: self.annotation_type, + } + } +} + /// A builder that uses the annotate_snippets library to display nice error messages about source /// code locations. impl ErrorBuilder { - pub fn new(message: String) -> Self { + pub fn new(message: impl ToString) -> Self { ErrorBuilder { - message, - annotation_type: AnnotationType::Error, + title: FreeAnnotation { + message: message.to_string(), + annotation_type: AnnotationType::Error, + }, annotations: Vec::new(), + footer: Vec::new(), + consumed: false, } } - pub fn span_err(span: &Span, message: String) -> Self { + pub fn new_span_err(span: &Span, message: impl ToString) -> Self { + let message = message.to_string(); let mut builder = Self::new(message.clone()); - builder.annotate_err(span, message); + builder.span_err(span, message); builder } - pub fn annotate_err(&mut self, span: &Span, message: String) { - self.annotations.push(BuilderAnnotation { + pub fn span_err( + &mut self, + span: &Span, + message: impl ToString, + ) -> &mut Self { + // Ignore spans not coming from a source file + let span = match span { + Span::Parsed(span) => span, + _ => return self, + }; + self.annotations.push(SpannedAnnotation { span: span.clone(), - message, + message: message.to_string(), annotation_type: AnnotationType::Error, - }) + }); + self } - pub fn annotate_info(&mut self, span: &Span, message: String) { - self.annotations.push(BuilderAnnotation { + pub fn span_help( + &mut self, + span: &Span, + message: impl ToString, + ) -> &mut Self { + // Ignore spans not coming from a source file + let span = match span { + Span::Parsed(span) => span, + _ => return self, + }; + self.annotations.push(SpannedAnnotation { span: span.clone(), - message, + message: message.to_string(), + annotation_type: AnnotationType::Help, + }); + self + } + pub fn help(&mut self, message: impl ToString) -> &mut Self { + self.footer.push(FreeAnnotation { + message: message.to_string(), annotation_type: AnnotationType::Help, - }) + }); + self } // TODO: handle multiple files - pub fn format(self) -> String { - let mut input = None; - let annotations = self - .annotations - .into_iter() - .filter_map(|annot| { - let span = match annot.span { - Span::Parsed(span) => span, - _ => return None, - }; - if input.is_none() { - input = Some(span.to_input()); - } - Some(SourceAnnotation { - label: annot.message, - annotation_type: annot.annotation_type, - range: span.as_char_range(), - }) - }) - .collect(); - - let input = match input { - Some(input) => input, - None => return format!("[unknown location] {}", self.message), - }; + pub fn format(&mut self) -> String { + if self.consumed { + panic!("tried to format the same ErrorBuilder twice") + } + let this = std::mem::replace(self, ErrorBuilder::default()); + self.consumed = true; + drop(self); // Get rid of the self reference so we don't use it by mistake. - let snippet = Snippet { - title: Some(Annotation { - label: Some(self.message), - id: None, - annotation_type: self.annotation_type, - }), - footer: vec![], - slices: vec![Slice { + let slices = if this.annotations.is_empty() { + Vec::new() + } else { + let input = this.annotations[0].span.to_input(); + let annotations = this + .annotations + .into_iter() + .map(|annot| annot.into_annotation()) + .collect(); + vec![Slice { source: input, line_start: 1, // TODO origin: Some("".to_string()), fold: true, annotations, - }], + }] + }; + let footer = this + .footer + .into_iter() + .map(|annot| annot.into_annotation()) + .collect(); + + let snippet = Snippet { + title: Some(this.title.into_annotation()), + slices, + footer, }; let dl = DisplayList::from(snippet); let dlf = DisplayListFormatter::new(true, false); format!("{}", dlf.format(&dl)) } } + +impl Default for FreeAnnotation { + fn default() -> Self { + FreeAnnotation { + message: String::new(), + annotation_type: AnnotationType::Error, + } + } +} diff --git a/dhall/src/error/mod.rs b/dhall/src/error/mod.rs index 5b7693e..6ea7d0c 100644 --- a/dhall/src/error/mod.rs +++ b/dhall/src/error/mod.rs @@ -1,7 +1,6 @@ use std::io::Error as IOError; use crate::semantics::resolve::ImportStack; -use crate::semantics::Value; use crate::syntax::{Import, ParseError}; use crate::NormalizedExpr; @@ -49,8 +48,6 @@ pub struct TypeError { #[derive(Debug)] pub(crate) enum TypeMessage { // UnboundVariable(Span), - InvalidInputType(Value), - InvalidOutputType(Value), // NotAFunction(Value), // TypeMismatch(Value, Value, Value), // AnnotMismatch(Value, Value), @@ -100,12 +97,6 @@ impl std::fmt::Display for TypeError { use TypeMessage::*; let msg = match &self.message { // UnboundVariable(var) => var.error("Type error: Unbound variable"), - InvalidInputType(v) => { - v.span().error("Type error: Invalid function input") - } - InvalidOutputType(v) => { - v.span().error("Type error: Invalid function output") - } // NotAFunction(v) => v.span().error("Type error: Not a function"), // TypeMismatch(x, y, z) => { // x.span() -- cgit v1.2.3 From d8c3ced0f1acb1924c801fadbfd3077dede9b0dd Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 2 Feb 2020 17:22:27 +0000 Subject: More errors --- dhall/src/error/builder.rs | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) (limited to 'dhall/src/error') diff --git a/dhall/src/error/builder.rs b/dhall/src/error/builder.rs index 22b0d77..29d0d35 100644 --- a/dhall/src/error/builder.rs +++ b/dhall/src/error/builder.rs @@ -70,10 +70,11 @@ impl ErrorBuilder { builder } - pub fn span_err( + pub fn span_annot( &mut self, span: &Span, message: impl ToString, + annotation_type: AnnotationType, ) -> &mut Self { // Ignore spans not coming from a source file let span = match span { @@ -83,33 +84,38 @@ impl ErrorBuilder { self.annotations.push(SpannedAnnotation { span: span.clone(), message: message.to_string(), - annotation_type: AnnotationType::Error, + annotation_type, }); self } - pub fn span_help( + pub fn footer_annot( &mut self, - span: &Span, message: impl ToString, + annotation_type: AnnotationType, ) -> &mut Self { - // Ignore spans not coming from a source file - let span = match span { - Span::Parsed(span) => span, - _ => return self, - }; - self.annotations.push(SpannedAnnotation { - span: span.clone(), + self.footer.push(FreeAnnotation { message: message.to_string(), - annotation_type: AnnotationType::Help, + annotation_type, }); self } + + pub fn span_err( + &mut self, + span: &Span, + message: impl ToString, + ) -> &mut Self { + self.span_annot(span, message, AnnotationType::Error) + } + pub fn span_help( + &mut self, + span: &Span, + message: impl ToString, + ) -> &mut Self { + self.span_annot(span, message, AnnotationType::Help) + } pub fn help(&mut self, message: impl ToString) -> &mut Self { - self.footer.push(FreeAnnotation { - message: message.to_string(), - annotation_type: AnnotationType::Help, - }); - self + self.footer_annot(message, AnnotationType::Help) } // TODO: handle multiple files -- cgit v1.2.3 From b6625eccbf3f2d1bcfe1a88f4d556439281e91de Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 2 Feb 2020 17:49:49 +0000 Subject: Use Spans consistently by value --- dhall/src/error/builder.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'dhall/src/error') diff --git a/dhall/src/error/builder.rs b/dhall/src/error/builder.rs index 29d0d35..8c7eef3 100644 --- a/dhall/src/error/builder.rs +++ b/dhall/src/error/builder.rs @@ -63,7 +63,7 @@ impl ErrorBuilder { consumed: false, } } - pub fn new_span_err(span: &Span, message: impl ToString) -> Self { + pub fn new_span_err(span: Span, message: impl ToString) -> Self { let message = message.to_string(); let mut builder = Self::new(message.clone()); builder.span_err(span, message); @@ -72,7 +72,7 @@ impl ErrorBuilder { pub fn span_annot( &mut self, - span: &Span, + span: Span, message: impl ToString, annotation_type: AnnotationType, ) -> &mut Self { @@ -82,7 +82,7 @@ impl ErrorBuilder { _ => return self, }; self.annotations.push(SpannedAnnotation { - span: span.clone(), + span, message: message.to_string(), annotation_type, }); @@ -102,14 +102,14 @@ impl ErrorBuilder { pub fn span_err( &mut self, - span: &Span, + span: Span, message: impl ToString, ) -> &mut Self { self.span_annot(span, message, AnnotationType::Error) } pub fn span_help( &mut self, - span: &Span, + span: Span, message: impl ToString, ) -> &mut Self { self.span_annot(span, message, AnnotationType::Help) -- cgit v1.2.3 From f3681f7a32ddb78db4d564769b50b697c54ebeac Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 2 Feb 2020 18:03:03 +0000 Subject: Tweak errors --- dhall/src/error/builder.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'dhall/src/error') diff --git a/dhall/src/error/builder.rs b/dhall/src/error/builder.rs index 8c7eef3..39f8dfb 100644 --- a/dhall/src/error/builder.rs +++ b/dhall/src/error/builder.rs @@ -63,12 +63,6 @@ impl ErrorBuilder { consumed: false, } } - pub fn new_span_err(span: Span, message: impl ToString) -> Self { - let message = message.to_string(); - let mut builder = Self::new(message.clone()); - builder.span_err(span, message); - builder - } pub fn span_annot( &mut self, @@ -117,6 +111,9 @@ impl ErrorBuilder { pub fn help(&mut self, message: impl ToString) -> &mut Self { self.footer_annot(message, AnnotationType::Help) } + pub fn note(&mut self, message: impl ToString) -> &mut Self { + self.footer_annot(message, AnnotationType::Note) + } // TODO: handle multiple files pub fn format(&mut self) -> String { -- cgit v1.2.3