diff options
author | Nadrieril | 2019-11-11 12:43:09 +0000 |
---|---|---|
committer | Nadrieril | 2019-11-11 13:50:36 +0000 |
commit | 330f063e80a51f8f399864f9d01412e1bff34fe9 (patch) | |
tree | d008005efc57222c0b33f5069c6ce8576cc7bd43 | |
parent | 5d5a356b8eb36e277c312e5550d1cb0a2f82e9fa (diff) |
Display first pretty type error
-rw-r--r-- | dhall/src/error/mod.rs | 8 | ||||
-rw-r--r-- | dhall/src/phase/typecheck.rs | 2 | ||||
-rw-r--r-- | dhall/src/tests.rs | 25 | ||||
-rw-r--r-- | dhall/tests/type-errors/unit/AssertAlphaTrap.txt | 7 | ||||
-rw-r--r-- | dhall/tests/type-errors/unit/UnionDeprecatedConstructorsKeyword.txt | 7 | ||||
-rw-r--r-- | dhall/tests/type-errors/unit/VariableFree.txt | 7 | ||||
-rw-r--r-- | dhall_syntax/src/core/span.rs | 14 |
7 files changed, 61 insertions, 9 deletions
diff --git a/dhall/src/error/mod.rs b/dhall/src/error/mod.rs index 5338e2d..efbd578 100644 --- a/dhall/src/error/mod.rs +++ b/dhall/src/error/mod.rs @@ -1,6 +1,6 @@ use std::io::Error as IOError; -use dhall_syntax::{BinOp, Import, Label, ParseError, V}; +use dhall_syntax::{BinOp, Import, Label, ParseError, Span}; use crate::core::context::TypecheckContext; use crate::core::value::Value; @@ -48,7 +48,7 @@ pub struct TypeError { /// The specific type error #[derive(Debug)] pub(crate) enum TypeMessage { - UnboundVariable(V<Label>), + UnboundVariable(Span), InvalidInputType(Value), InvalidOutputType(Value), NotAFunction(Value), @@ -102,8 +102,8 @@ impl TypeError { impl std::fmt::Display for TypeError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { use TypeMessage::*; - let msg = match self.message { - UnboundVariable(_) => "Unbound variable".to_string(), + let msg = match &self.message { + UnboundVariable(span) => span.error("Unbound variable"), InvalidInputType(_) => "Invalid function input".to_string(), InvalidOutputType(_) => "Invalid function output".to_string(), NotAFunction(_) => "Not a function".to_string(), diff --git a/dhall/src/phase/typecheck.rs b/dhall/src/phase/typecheck.rs index 63f5157..9d2c69b 100644 --- a/dhall/src/phase/typecheck.rs +++ b/dhall/src/phase/typecheck.rs @@ -339,7 +339,7 @@ fn type_with( None => { return Err(TypeError::new( ctx, - TypeMessage::UnboundVariable(var.clone()), + TypeMessage::UnboundVariable(span), )) } }, diff --git a/dhall/src/tests.rs b/dhall/src/tests.rs index 9790b95..cde3b03 100644 --- a/dhall/src/tests.rs +++ b/dhall/src/tests.rs @@ -20,6 +20,29 @@ right: `{}`"#, }}; } +/// Wrapper around string slice that makes debug output `{:?}` to print string same way as `{}`. +/// Used in different `assert*!` macros in combination with `pretty_assertions` crate to make +/// test failures to show nice diffs. +#[derive(PartialEq, Eq)] +#[doc(hidden)] +pub struct PrettyString(String); + +/// Make diff to display string as multi-line string +impl std::fmt::Debug for PrettyString { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str(&self.0) + } +} + +macro_rules! assert_eq_pretty_str { + ($left:expr, $right:expr) => { + assert_eq_pretty!( + PrettyString($left.to_string()), + PrettyString($right.to_string()) + ); + }; +} + use std::fs::File; use std::io::{Read, Write}; use std::path::PathBuf; @@ -172,7 +195,7 @@ pub fn run_test(test: Test<'_>) -> Result<()> { if error_file_path.is_file() { let expected_msg = std::fs::read_to_string(error_file_path)?; let msg = format!("{}\n", err); - assert_eq_pretty!(msg, expected_msg); + assert_eq_pretty_str!(msg, expected_msg); } else { std::fs::create_dir_all(error_file_path.parent().unwrap())?; let mut file = File::create(error_file_path)?; diff --git a/dhall/tests/type-errors/unit/AssertAlphaTrap.txt b/dhall/tests/type-errors/unit/AssertAlphaTrap.txt index df2b6c1..f2fc8ce 100644 --- a/dhall/tests/type-errors/unit/AssertAlphaTrap.txt +++ b/dhall/tests/type-errors/unit/AssertAlphaTrap.txt @@ -1 +1,6 @@ -Type error: Unbound variable +Type error: --> 1:47 + | +1 | assert : (\(_: Bool) -> _) === (\(x: Bool) -> _)␊ + | ^ + | + = Unbound variable diff --git a/dhall/tests/type-errors/unit/UnionDeprecatedConstructorsKeyword.txt b/dhall/tests/type-errors/unit/UnionDeprecatedConstructorsKeyword.txt index df2b6c1..f7903de 100644 --- a/dhall/tests/type-errors/unit/UnionDeprecatedConstructorsKeyword.txt +++ b/dhall/tests/type-errors/unit/UnionDeprecatedConstructorsKeyword.txt @@ -1 +1,6 @@ -Type error: Unbound variable +Type error: --> 1:1 + | +1 | constructors < Left : Natural | Right : Bool >␊ + | ^----------^ + | + = Unbound variable diff --git a/dhall/tests/type-errors/unit/VariableFree.txt b/dhall/tests/type-errors/unit/VariableFree.txt index df2b6c1..ef1d16e 100644 --- a/dhall/tests/type-errors/unit/VariableFree.txt +++ b/dhall/tests/type-errors/unit/VariableFree.txt @@ -1 +1,6 @@ -Type error: Unbound variable +Type error: --> 1:1 + | +1 | x␊ + | ^ + | + = Unbound variable diff --git a/dhall_syntax/src/core/span.rs b/dhall_syntax/src/core/span.rs index fa89c30..e2bf26d 100644 --- a/dhall_syntax/src/core/span.rs +++ b/dhall_syntax/src/core/span.rs @@ -53,4 +53,18 @@ impl Span { ), } } + + pub fn error(&self, message: impl Into<String>) -> String { + use pest::error::{Error, ErrorVariant}; + use pest::Span; + let message: String = message.into(); + let span = match self { + self::Span::Parsed(span) => span, + _ => return format!("Unknown location: {}", message), + }; + let span = Span::new(&*span.input, span.start, span.end).unwrap(); + let err: ErrorVariant<!> = ErrorVariant::CustomError { message }; + let err = Error::new_from_span(err, span); + format!("{}", err) + } } |