diff options
-rw-r--r-- | tvix/eval/src/compiler/mod.rs | 16 | ||||
-rw-r--r-- | tvix/eval/src/errors.rs | 150 | ||||
-rw-r--r-- | tvix/eval/src/value/attrs.rs | 6 |
3 files changed, 145 insertions, 27 deletions
diff --git a/tvix/eval/src/compiler/mod.rs b/tvix/eval/src/compiler/mod.rs index 7868bced4653..440ac284c1a6 100644 --- a/tvix/eval/src/compiler/mod.rs +++ b/tvix/eval/src/compiler/mod.rs @@ -1114,21 +1114,17 @@ impl Compiler<'_, '_> { self.scope_mut().poison(global_ident, depth); } - let mut shadowed = false; for other in self.scope().locals.iter().rev() { if other.has_name(&name) && other.depth == depth { - shadowed = true; + self.emit_error( + self.span_for(node), + ErrorKind::VariableAlreadyDefined(other.span), + ); + break; } } - if shadowed { - self.emit_error( - self.span_for(node), - ErrorKind::VariableAlreadyDefined(name.clone()), - ); - } - let span = self.span_for(node); self.scope_mut().declare_local(name, span) } @@ -1264,7 +1260,7 @@ impl Compiler<'_, '_> { N: AstNode<Language = rnix::NixLanguage>, { Error { - kind: ErrorKind::DynamicKeyInLet(node.syntax().clone()), + kind: ErrorKind::DynamicKeyInLet, span: self.span_for(node), } } diff --git a/tvix/eval/src/errors.rs b/tvix/eval/src/errors.rs index b87555d06d52..39b105de9812 100644 --- a/tvix/eval/src/errors.rs +++ b/tvix/eval/src/errors.rs @@ -1,16 +1,24 @@ use std::fmt::Display; +use codemap::{CodeMap, Span}; +use codemap_diagnostic::{Diagnostic, Emitter, Level, SpanLabel, SpanStyle}; + +use crate::Value; + #[derive(Clone, Debug)] pub enum ErrorKind { + /// These are user-generated errors through builtins. + Throw(String), + Abort(String), + AssertionFailed, + DuplicateAttrsKey { key: String, }, /// Attempted to specify an invalid key type (e.g. integer) in a /// dynamic attribute name. - InvalidAttributeName { - given: &'static str, - }, + InvalidAttributeName(Value), AttributeNotFound { name: String, @@ -30,7 +38,7 @@ pub enum ErrorKind { PathResolution(String), /// Dynamic keys are not allowed in let. - DynamicKeyInLet(rnix::SyntaxNode), + DynamicKeyInLet, /// Unknown variable in statically known scope. UnknownStaticVariable, @@ -39,7 +47,7 @@ pub enum ErrorKind { UnknownDynamicVariable(String), /// User is defining the same variable twice at the same depth. - VariableAlreadyDefined(String), + VariableAlreadyDefined(Span), /// Attempt to call something that is not callable. NotCallable, @@ -49,12 +57,6 @@ pub enum ErrorKind { ParseErrors(Vec<rnix::parser::ParseError>), - AssertionFailed, - - /// These are user-generated errors through builtins. - Throw(String), - Abort(String), - /// An error occured while forcing a thunk, and needs to be /// chained up. ThunkForce(Box<Error>), @@ -68,7 +70,7 @@ pub enum ErrorKind { #[derive(Clone, Debug)] pub struct Error { pub kind: ErrorKind, - pub span: codemap::Span, + pub span: Span, } impl Display for Error { @@ -78,3 +80,127 @@ impl Display for Error { } pub type EvalResult<T> = Result<T, Error>; + +impl Error { + pub fn fancy_format_str(&self, codemap: &CodeMap) -> String { + let mut out = vec![]; + Emitter::vec(&mut out, Some(codemap)).emit(&[self.diagnostic(codemap)]); + String::from_utf8_lossy(&out).to_string() + } + + /// Create the optional span label displayed as an annotation on + /// the underlined span of the error. + fn span_label(&self) -> Option<String> { + None + } + + /// Create the primary error message displayed to users. + fn message(&self, codemap: &CodeMap) -> String { + match &self.kind { + ErrorKind::Throw(msg) => format!("error thrown: {}", msg), + ErrorKind::Abort(msg) => format!("evaluation aborted: {}", msg), + ErrorKind::AssertionFailed => "assertion failed".to_string(), + + ErrorKind::DuplicateAttrsKey { key } => { + format!("attribute key '{}' already defined", key) + } + + ErrorKind::InvalidAttributeName(val) => format!( + "found attribute name '{}' of type '{}', but attribute names must be strings", + val, + val.type_of() + ), + + ErrorKind::AttributeNotFound { name } => format!( + "attribute with name '{}' could not be found in the set", + name + ), + + ErrorKind::TypeError { expected, actual } => format!( + "expected value of type '{}', but found a '{}'", + expected, actual + ), + + ErrorKind::Incomparable { lhs, rhs } => { + format!("can not compare a {} with a {}", lhs, rhs) + } + + ErrorKind::PathResolution(err) => format!("could not resolve path: {}", err), + + ErrorKind::DynamicKeyInLet => { + "dynamically evaluated keys are not allowed in let-bindings".to_string() + } + + ErrorKind::UnknownStaticVariable => "variable not found".to_string(), + + ErrorKind::UnknownDynamicVariable(name) => format!( + r#"variable '{}' could not be found + +Note that this occured within a `with`-expression. The problem may be related +to a missing value in the attribute set(s) included via `with`."#, + name + ), + + ErrorKind::VariableAlreadyDefined(_) => "variable has already been defined".to_string(), + + ErrorKind::NotCallable => { + "this value is not callable (i.e. not a function or builtin)".to_string() + } + + ErrorKind::InfiniteRecursion => "infinite recursion encountered".to_string(), + + // TODO(tazjin): these errors should actually end up with + // individual spans etc. + ErrorKind::ParseErrors(errors) => format!("failed to parse Nix code: {:?}", errors), + + // TODO(tazjin): trace through the whole chain of thunk + // forcing errors with secondary spans, instead of just + // delegating to the inner error + ErrorKind::ThunkForce(err) => err.message(codemap), + + ErrorKind::NotImplemented(feature) => { + format!("feature not yet implemented in Tvix: {}", feature) + } + } + } + + /// Return the unique error code for this variant which can be + /// used to refer users to documentation. + fn code(&self) -> &'static str { + match self.kind { + ErrorKind::Throw(_) => "E001", + ErrorKind::Abort(_) => "E002", + ErrorKind::AssertionFailed => "E003", + ErrorKind::InvalidAttributeName { .. } => "E004", + ErrorKind::AttributeNotFound { .. } => "E005", + ErrorKind::TypeError { .. } => "E006", + ErrorKind::Incomparable { .. } => "E007", + ErrorKind::PathResolution(_) => "E008", + ErrorKind::DynamicKeyInLet => "E009", + ErrorKind::UnknownStaticVariable => "E010", + ErrorKind::UnknownDynamicVariable(_) => "E011", + ErrorKind::VariableAlreadyDefined(_) => "E012", + ErrorKind::NotCallable => "E013", + ErrorKind::InfiniteRecursion => "E014", + ErrorKind::ParseErrors(_) => "E015", + ErrorKind::DuplicateAttrsKey { .. } => "E016", + ErrorKind::ThunkForce(_) => "E017", + ErrorKind::NotImplemented(_) => "E999", + } + } + + fn diagnostic(&self, codemap: &CodeMap) -> Diagnostic { + let span_label = SpanLabel { + label: self.span_label(), + span: self.span, + style: SpanStyle::Primary, + }; + + Diagnostic { + level: Level::Error, + message: self.message(codemap), + spans: vec![span_label], + code: Some(self.code().into()), + } + } +} diff --git a/tvix/eval/src/value/attrs.rs b/tvix/eval/src/value/attrs.rs index b8ae51bf48fc..4c18ad2f55b2 100644 --- a/tvix/eval/src/value/attrs.rs +++ b/tvix/eval/src/value/attrs.rs @@ -286,11 +286,7 @@ impl NixAttrs { continue; } - other => { - return Err(ErrorKind::InvalidAttributeName { - given: other.type_of(), - }) - } + other => return Err(ErrorKind::InvalidAttributeName(other)), } } |