From 667f01ce17cb96c2adaff6aefc87e9ee48dbb1cf Mon Sep 17 00:00:00 2001 From: Joshua Groves Date: Wed, 3 Mar 2021 23:58:54 -0330 Subject: [PATCH] [wgsl-in] Add descriptions for more errors --- src/front/wgsl/lexer.rs | 26 +++++---- src/front/wgsl/mod.rs | 115 +++++++++++++++++++++++++++++++--------- tests/errors.rs | 72 +++++++++++++++++++++++++ 3 files changed, 178 insertions(+), 35 deletions(-) diff --git a/src/front/wgsl/lexer.rs b/src/front/wgsl/lexer.rs index bc6e9fe05c..7e7c3019ec 100644 --- a/src/front/wgsl/lexer.rs +++ b/src/front/wgsl/lexer.rs @@ -1,4 +1,5 @@ use super::{conv, Error, Token, TokenSpan}; +use std::ops::Range; fn _consume_str<'a>(input: &'a str, what: &str) -> Option<&'a str> { if input.starts_with(what) { @@ -255,6 +256,13 @@ impl<'a> Lexer<'a> { } } + pub(super) fn next_ident_with_span(&mut self) -> Result<(&'a str, Range), Error<'a>> { + match self.next() { + (Token::Word(word), span) => Ok((word, span)), + other => Err(Error::Unexpected(other, "identifier")), + } + } + pub(super) fn next_ident(&mut self) -> Result<&'a str, Error<'a>> { match self.next() { (Token::Word(word), _) => Ok(word), @@ -264,28 +272,26 @@ impl<'a> Lexer<'a> { fn _next_float_literal(&mut self) -> Result> { match self.next() { - (Token::Number { value, .. }, _) => { - value.parse().map_err(|err| Error::BadFloat(value, err)) - } - other => Err(Error::Unexpected(other, "float literal")), + (Token::Number { value, .. }, span) => value.parse().map_err(|_| Error::BadFloat(span)), + other => Err(Error::Unexpected(other, "floating-point literal")), } } pub(super) fn next_uint_literal(&mut self) -> Result> { match self.next() { - (Token::Number { value, .. }, _) => { - value.parse().map_err(|err| Error::BadInteger(value, err)) + (Token::Number { value, .. }, span) => { + value.parse().map_err(|_| Error::BadInteger(span)) } - other => Err(Error::Unexpected(other, "uint literal")), + other => Err(Error::Unexpected(other, "unsigned integer literal")), } } pub(super) fn next_sint_literal(&mut self) -> Result> { match self.next() { - (Token::Number { value, .. }, _) => { - value.parse().map_err(|err| Error::BadInteger(value, err)) + (Token::Number { value, .. }, span) => { + value.parse().map_err(|_| Error::BadInteger(span)) } - other => Err(Error::Unexpected(other, "sint literal")), + other => Err(Error::Unexpected(other, "signed integer literal")), } } diff --git a/src/front/wgsl/mod.rs b/src/front/wgsl/mod.rs index 681afc0596..5b64c90bbf 100644 --- a/src/front/wgsl/mod.rs +++ b/src/front/wgsl/mod.rs @@ -62,14 +62,52 @@ impl<'a> Error<'a> { message: format!( "expected {}, found '{}'", expected, - &source[unexpected_span.clone()] + &source[unexpected_span.clone()], ), labels: vec![(unexpected_span.clone(), format!("expected {}", expected))], + notes: vec![], + source, + }, + Error::BadInteger(bad_span) => ParseError { + message: format!( + "expected integer literal, found `{}`", + &source[bad_span.clone()], + ), + labels: vec![(bad_span.clone(), "expected integer".to_string())], + notes: vec![], + source, + }, + Error::BadFloat(bad_span) => ParseError { + message: format!( + "expected floating-point literal, found `{}`", + &source[bad_span.clone()], + ), + labels: vec![( + bad_span.clone(), + "expected floating-point literal".to_string(), + )], + notes: vec![], + source, + }, + Error::BadScalarWidth(bad_span, width) => ParseError { + message: format!("invalid width of `{}` for literal", width,), + labels: vec![(bad_span.clone(), "invalid width".to_string())], + notes: vec!["valid width is 32".to_string()], + source, + }, + Error::BadAccessor(accessor_span) => ParseError { + message: format!( + "invalid field accessor `{}`", + &source[accessor_span.clone()], + ), + labels: vec![(accessor_span.clone(), "invalid accessor".to_string())], + notes: vec![], source, }, error => ParseError { message: error.to_string(), - labels: Vec::new(), + labels: vec![], + notes: vec![], source, }, } @@ -78,16 +116,16 @@ impl<'a> Error<'a> { #[derive(Clone, Debug, Error)] pub enum Error<'a> { - #[error("unexpected token {:?}, expected {1}", (.0).0)] + #[error("")] Unexpected(TokenSpan<'a>, &'a str), - #[error("unable to parse `{0}` as integer: {1}")] - BadInteger(&'a str, std::num::ParseIntError), - #[error("unable to parse `{1}` as float: {1}")] - BadFloat(&'a str, std::num::ParseFloatError), - #[error("unable to parse `{0}{1}{2}` as scalar width: {3}")] - BadScalarWidth(&'a str, char, &'a str, std::num::ParseIntError), - #[error("bad field accessor `{0}`")] - BadAccessor(&'a str), + #[error("")] + BadInteger(Range), + #[error("")] + BadFloat(Range), + #[error("")] + BadScalarWidth(Range, &'a str), + #[error("")] + BadAccessor(Range), #[error("bad texture {0}`")] BadTexture(&'a str), #[error("bad texture coordinate")] @@ -309,6 +347,7 @@ impl Composition { base: Handle, base_size: crate::VectorSize, name: &'a str, + name_span: Range, expressions: &mut Arena, ) -> Result> { Ok(if name.len() > 1 { @@ -316,7 +355,7 @@ impl Composition { for ch in name.chars() { let index = Self::letter_pos(ch); if index >= base_size as u32 { - return Err(Error::BadAccessor(name)); + return Err(Error::BadAccessor(name_span)); } let expr = crate::Expression::AccessIndex { base, index }; components.push(expressions.append(expr)); @@ -326,14 +365,17 @@ impl Composition { 2 => crate::VectorSize::Bi, 3 => crate::VectorSize::Tri, 4 => crate::VectorSize::Quad, - _ => return Err(Error::BadAccessor(name)), + _ => return Err(Error::BadAccessor(name_span)), }; Composition::Multi(size, components) } else { - let ch = name.chars().next().ok_or(Error::BadAccessor(name))?; + let ch = name + .chars() + .next() + .ok_or_else(|| Error::BadAccessor(name_span.clone()))?; let index = Self::letter_pos(ch); if index >= base_size as u32 { - return Err(Error::BadAccessor(name)); + return Err(Error::BadAccessor(name_span)); } Composition::Single(crate::Expression::AccessIndex { base, index }) }) @@ -375,6 +417,7 @@ struct ParsedVariable<'a> { pub struct ParseError<'a> { message: String, labels: Vec<(Range, String)>, + notes: Vec, source: &'a str, } @@ -389,6 +432,12 @@ impl<'a> ParseError<'a> { Label::primary((), label.0.clone()).with_message(label.1.to_string()) }) .collect(), + ) + .with_notes( + self.notes + .iter() + .map(|note| format!("note: {}", note)) + .collect(), ); diagnostic } @@ -456,20 +505,22 @@ impl Parser { word: &'a str, ty: char, width: &'a str, + token: TokenSpan<'a>, ) -> Result> { + let span = token.1; let value = match ty { 'i' => word .parse() .map(crate::ScalarValue::Sint) - .map_err(|err| Error::BadInteger(word, err))?, + .map_err(|_| Error::BadInteger(span.clone()))?, 'u' => word .parse() .map(crate::ScalarValue::Uint) - .map_err(|err| Error::BadInteger(word, err))?, + .map_err(|_| Error::BadInteger(span.clone()))?, 'f' => word .parse() .map(crate::ScalarValue::Float) - .map_err(|err| Error::BadFloat(word, err))?, + .map_err(|_| Error::BadFloat(span.clone()))?, _ => unreachable!(), }; Ok(crate::ConstantInner::Scalar { @@ -479,7 +530,7 @@ impl Parser { } else { match width.parse::() { Ok(bits) => bits / 8, - Err(e) => return Err(Error::BadScalarWidth(word, ty, width, e)), + Err(_) => return Err(Error::BadScalarWidth(span, width)), } }, }) @@ -851,7 +902,14 @@ impl Parser { width: 1, value: crate::ScalarValue::Bool(false), }, - (Token::Number { value, ty, width }, _) => Self::get_constant_inner(value, ty, width)?, + ( + Token::Number { + ref value, + ref ty, + ref width, + }, + _, + ) => Self::get_constant_inner(*value, *ty, *width, first_token_span)?, (Token::Word(name), _) => { // look for an existing constant first for (handle, var) in const_arena.iter() { @@ -1021,13 +1079,13 @@ impl Parser { let expression = match lexer.peek().0 { Token::Separator('.') => { let _ = lexer.next(); - let name = lexer.next_ident()?; + let (name, name_span) = lexer.next_ident_with_span()?; match *ctx.resolve_type(handle)? { crate::TypeInner::Struct { ref members, .. } => { let index = members .iter() .position(|m| m.name.as_deref() == Some(name)) - .ok_or(Error::BadAccessor(name))? + .ok_or(Error::BadAccessor(name_span))? as u32; crate::Expression::AccessIndex { base: handle, @@ -1035,7 +1093,8 @@ impl Parser { } } crate::TypeInner::Vector { size, kind, width } => { - match Composition::make(handle, size, name, ctx.expressions)? { + match Composition::make(handle, size, name, name_span, ctx.expressions)? + { Composition::Multi(size, components) => { let inner = crate::TypeInner::Vector { size, kind, width }; crate::Expression::Compose { @@ -1052,7 +1111,13 @@ impl Parser { columns, rows, width, - } => match Composition::make(handle, columns, name, ctx.expressions)? { + } => match Composition::make( + handle, + columns, + name, + name_span, + ctx.expressions, + )? { Composition::Multi(columns, components) => { let inner = crate::TypeInner::Matrix { columns, @@ -1068,7 +1133,7 @@ impl Parser { } Composition::Single(expr) => expr, }, - _ => return Err(Error::BadAccessor(name)), + _ => return Err(Error::BadAccessor(name_span)), } } Token::Paren('[') => { diff --git a/tests/errors.rs b/tests/errors.rs index d8d355e3e7..97611b8c62 100644 --- a/tests/errors.rs +++ b/tests/errors.rs @@ -25,3 +25,75 @@ fn function_without_identifier() { "### ); } + +#[cfg(feature = "wgsl-in")] +#[test] +fn invalid_integer() { + err!( + "[[location(1.)]] var pos : vec2;", + @r###" + error: expected integer literal, found `1.` + ┌─ wgsl:1:12 + │ + 1 │ [[location(1.)]] var pos : vec2; + │ ^^ expected integer + + "### + ); +} + +#[cfg(feature = "wgsl-in")] +#[test] +fn invalid_float() { + err!( + "const scale: f32 = 1.1.;", + @r###" + error: expected floating-point literal, found `1.1.` + ┌─ wgsl:1:20 + │ + 1 │ const scale: f32 = 1.1.; + │ ^^^^ expected floating-point literal + + "### + ); +} + +#[cfg(feature = "wgsl-in")] +#[test] +fn invalid_scalar_width() { + err!( + "const scale: f32 = 1.1f1000;", + @r###" + error: invalid width of `1000` for literal + ┌─ wgsl:1:20 + │ + 1 │ const scale: f32 = 1.1f1000; + │ ^^^^^^^^ invalid width + │ + = note: valid width is 32 + + "### + ); +} + +#[cfg(feature = "wgsl-in")] +#[test] +fn invalid_accessor() { + err!( + r###" + [[stage(vertex)]] + fn vs_main() { + var color: vec3 = vec3(1.0, 2.0, 3.0); + var i: f32 = color.a; + } + "###, + @r###" + error: invalid field accessor `a` + ┌─ wgsl:5:32 + │ + 5 │ var i: f32 = color.a; + │ ^ invalid accessor + + "### + ); +}