Revision control
Copy as Markdown
Other Tools
use crate::diagnostic_filter::ConflictingDiagnosticRuleError;
use crate::front::wgsl::parse::directive::enable_extension::{
EnableExtension, UnimplementedEnableExtension,
};
use crate::front::wgsl::parse::directive::language_extension::{
LanguageExtension, UnimplementedLanguageExtension,
};
use crate::front::wgsl::parse::lexer::Token;
use crate::front::wgsl::Scalar;
use crate::proc::{Alignment, ConstantEvaluatorError, ResolveError};
use crate::{SourceLocation, Span};
use codespan_reporting::diagnostic::{Diagnostic, Label};
use codespan_reporting::files::SimpleFile;
use codespan_reporting::term;
use std::borrow::Cow;
use std::ops::Range;
use termcolor::{ColorChoice, NoColor, StandardStream};
use thiserror::Error;
#[cfg(test)]
use std::mem::size_of;
#[derive(Clone, Debug)]
pub struct ParseError {
message: String,
// The first span should be the primary span, and the other ones should be complementary.
labels: Vec<(Span, Cow<'static, str>)>,
notes: Vec<String>,
}
impl ParseError {
pub fn labels(&self) -> impl ExactSizeIterator<Item = (Span, &str)> + '_ {
self.labels
.iter()
.map(|&(span, ref msg)| (span, msg.as_ref()))
}
pub fn message(&self) -> &str {
&self.message
}
fn diagnostic(&self) -> Diagnostic<()> {
let diagnostic = Diagnostic::error()
.with_message(self.message.to_string())
.with_labels(
self.labels
.iter()
.filter_map(|label| label.0.to_range().map(|range| (label, range)))
.map(|(label, range)| {
Label::primary((), range).with_message(label.1.to_string())
})
.collect(),
)
.with_notes(
self.notes
.iter()
.map(|note| format!("note: {note}"))
.collect(),
);
diagnostic
}
/// Emits a summary of the error to standard error stream.
pub fn emit_to_stderr(&self, source: &str) {
self.emit_to_stderr_with_path(source, "wgsl")
}
/// Emits a summary of the error to standard error stream.
pub fn emit_to_stderr_with_path<P>(&self, source: &str, path: P)
where
P: AsRef<std::path::Path>,
{
let path = path.as_ref().display().to_string();
let files = SimpleFile::new(path, source);
let config = term::Config::default();
let writer = StandardStream::stderr(ColorChoice::Auto);
term::emit(&mut writer.lock(), &config, &files, &self.diagnostic())
.expect("cannot write error");
}
/// Emits a summary of the error to a string.
pub fn emit_to_string(&self, source: &str) -> String {
self.emit_to_string_with_path(source, "wgsl")
}
/// Emits a summary of the error to a string.
pub fn emit_to_string_with_path<P>(&self, source: &str, path: P) -> String
where
P: AsRef<std::path::Path>,
{
let path = path.as_ref().display().to_string();
let files = SimpleFile::new(path, source);
let config = term::Config::default();
let mut writer = NoColor::new(Vec::new());
term::emit(&mut writer, &config, &files, &self.diagnostic()).expect("cannot write error");
String::from_utf8(writer.into_inner()).unwrap()
}
/// Returns a [`SourceLocation`] for the first label in the error message.
pub fn location(&self, source: &str) -> Option<SourceLocation> {
self.labels.first().map(|label| label.0.location(source))
}
}
impl std::fmt::Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}
impl std::error::Error for ParseError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ExpectedToken<'a> {
Token(Token<'a>),
Identifier,
AfterIdentListComma,
AfterIdentListArg,
/// Expected: constant, parenthesized expression, identifier
PrimaryExpression,
/// Expected: assignment, increment/decrement expression
Assignment,
/// Expected: 'case', 'default', '}'
SwitchItem,
/// Expected: ',', ')'
WorkgroupSizeSeparator,
/// Expected: 'struct', 'let', 'var', 'type', ';', 'fn', eof
GlobalItem,
/// Expected a type.
Type,
/// Access of `var`, `let`, `const`.
Variable,
/// Access of a function
Function,
/// The `diagnostic` identifier of the `@diagnostic(…)` attribute.
DiagnosticAttribute,
}
#[derive(Clone, Copy, Debug, Error, PartialEq)]
pub enum NumberError {
#[error("invalid numeric literal format")]
Invalid,
#[error("numeric literal not representable by target type")]
NotRepresentable,
#[error("unimplemented f16 type")]
UnimplementedF16,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum InvalidAssignmentType {
Other,
Swizzle,
ImmutableBinding(Span),
}
#[derive(Clone, Debug)]
pub(crate) enum Error<'a> {
Unexpected(Span, ExpectedToken<'a>),
UnexpectedComponents(Span),
UnexpectedOperationInConstContext(Span),
BadNumber(Span, NumberError),
BadMatrixScalarKind(Span, Scalar),
BadAccessor(Span),
BadTexture(Span),
BadTypeCast {
span: Span,
from_type: Box<str>,
to_type: Box<str>,
},
BadTextureSampleType {
span: Span,
scalar: Scalar,
},
BadIncrDecrReferenceType(Span),
InvalidResolve(ResolveError),
InvalidForInitializer(Span),
/// A break if appeared outside of a continuing block
InvalidBreakIf(Span),
InvalidGatherComponent(Span),
InvalidConstructorComponentType(Span, i32),
InvalidIdentifierUnderscore(Span),
ReservedIdentifierPrefix(Span),
UnknownAddressSpace(Span),
RepeatedAttribute(Span),
UnknownAttribute(Span),
UnknownBuiltin(Span),
UnknownAccess(Span),
UnknownIdent(Span, &'a str),
UnknownScalarType(Span),
UnknownType(Span),
UnknownStorageFormat(Span),
UnknownConservativeDepth(Span),
UnknownEnableExtension(Span, &'a str),
UnknownLanguageExtension(Span, &'a str),
UnknownDiagnosticRuleName(Span),
SizeAttributeTooLow(Span, u32),
AlignAttributeTooLow(Span, Alignment),
NonPowerOfTwoAlignAttribute(Span),
InconsistentBinding(Span),
TypeNotConstructible(Span),
TypeNotInferable(Span),
InitializationTypeMismatch {
name: Span,
expected: Box<str>,
got: Box<str>,
},
DeclMissingTypeAndInit(Span),
MissingAttribute(&'static str, Span),
InvalidAtomicPointer(Span),
InvalidAtomicOperandType(Span),
InvalidRayQueryPointer(Span),
Pointer(&'static str, Span),
NotPointer(Span),
NotReference(&'static str, Span),
InvalidAssignment {
span: Span,
ty: InvalidAssignmentType,
},
ReservedKeyword(Span),
/// Redefinition of an identifier (used for both module-scope and local redefinitions).
Redefinition {
/// Span of the identifier in the previous definition.
previous: Span,
/// Span of the identifier in the new definition.
current: Span,
},
/// A declaration refers to itself directly.
RecursiveDeclaration {
/// The location of the name of the declaration.
ident: Span,
/// The point at which it is used.
usage: Span,
},
/// A declaration refers to itself indirectly, through one or more other
/// definitions.
CyclicDeclaration {
/// The location of the name of some declaration in the cycle.
ident: Span,
/// The edges of the cycle of references.
///
/// Each `(decl, reference)` pair indicates that the declaration whose
/// name is `decl` has an identifier at `reference` whose definition is
/// the next declaration in the cycle. The last pair's `reference` is
/// the same identifier as `ident`, above.
path: Box<[(Span, Span)]>,
},
InvalidSwitchValue {
uint: bool,
span: Span,
},
CalledEntryPoint(Span),
WrongArgumentCount {
span: Span,
expected: Range<u32>,
found: u32,
},
FunctionReturnsVoid(Span),
InvalidWorkGroupUniformLoad(Span),
Internal(&'static str),
ExpectedConstExprConcreteIntegerScalar(Span),
ExpectedNonNegative(Span),
ExpectedPositiveArrayLength(Span),
MissingWorkgroupSize(Span),
ConstantEvaluatorError(Box<ConstantEvaluatorError>, Span),
AutoConversion(Box<AutoConversionError>),
AutoConversionLeafScalar(Box<AutoConversionLeafScalarError>),
ConcretizationFailed(Box<ConcretizationFailedError>),
ExceededLimitForNestedBraces {
span: Span,
limit: u8,
},
PipelineConstantIDValue(Span),
NotBool(Span),
ConstAssertFailed(Span),
DirectiveAfterFirstGlobalDecl {
directive_span: Span,
},
EnableExtensionNotYetImplemented {
kind: UnimplementedEnableExtension,
span: Span,
},
EnableExtensionNotEnabled {
kind: EnableExtension,
span: Span,
},
LanguageExtensionNotYetImplemented {
kind: UnimplementedLanguageExtension,
span: Span,
},
DiagnosticInvalidSeverity {
severity_control_name_span: Span,
},
DiagnosticDuplicateTriggeringRule(ConflictingDiagnosticRuleError),
DiagnosticAttributeNotYetImplementedAtParseSite {
site_name_plural: &'static str,
spans: Vec<Span>,
},
DiagnosticAttributeNotSupported {
on_what_plural: &'static str,
spans: Vec<Span>,
},
}
impl<'a> From<ConflictingDiagnosticRuleError> for Error<'a> {
fn from(value: ConflictingDiagnosticRuleError) -> Self {
Self::DiagnosticDuplicateTriggeringRule(value)
}
}
#[derive(Clone, Debug)]
pub(crate) struct AutoConversionError {
pub dest_span: Span,
pub dest_type: Box<str>,
pub source_span: Span,
pub source_type: Box<str>,
}
#[derive(Clone, Debug)]
pub(crate) struct AutoConversionLeafScalarError {
pub dest_span: Span,
pub dest_scalar: Box<str>,
pub source_span: Span,
pub source_type: Box<str>,
}
#[derive(Clone, Debug)]
pub(crate) struct ConcretizationFailedError {
pub expr_span: Span,
pub expr_type: Box<str>,
pub scalar: Box<str>,
pub inner: ConstantEvaluatorError,
}
impl<'a> Error<'a> {
#[cold]
#[inline(never)]
pub(crate) fn as_parse_error(&self, source: &'a str) -> ParseError {
match *self {
Error::Unexpected(unexpected_span, expected) => {
let expected_str = match expected {
ExpectedToken::Token(token) => match token {
Token::Separator(c) => format!("'{c}'"),
Token::Paren(c) => format!("'{c}'"),
Token::Attribute => "@".to_string(),
Token::Number(_) => "number".to_string(),
Token::Word(s) => s.to_string(),
Token::Operation(c) => format!("operation ('{c}')"),
Token::LogicalOperation(c) => format!("logical operation ('{c}')"),
Token::ShiftOperation(c) => format!("bitshift ('{c}{c}')"),
Token::AssignmentOperation(c) if c == '<' || c == '>' => {
format!("bitshift ('{c}{c}=')")
}
Token::AssignmentOperation(c) => format!("operation ('{c}=')"),
Token::IncrementOperation => "increment operation".to_string(),
Token::DecrementOperation => "decrement operation".to_string(),
Token::Arrow => "->".to_string(),
Token::Unknown(c) => format!("unknown ('{c}')"),
Token::Trivia => "trivia".to_string(),
Token::End => "end".to_string(),
},
ExpectedToken::Identifier => "identifier".to_string(),
ExpectedToken::PrimaryExpression => "expression".to_string(),
ExpectedToken::Assignment => "assignment or increment/decrement".to_string(),
ExpectedToken::SwitchItem => concat!(
"switch item ('case' or 'default') or a closing curly bracket ",
"to signify the end of the switch statement ('}')"
)
.to_string(),
ExpectedToken::WorkgroupSizeSeparator => {
"workgroup size separator (',') or a closing parenthesis".to_string()
}
ExpectedToken::GlobalItem => concat!(
"global item ('struct', 'const', 'var', 'alias', ';', 'fn') ",
"or the end of the file"
)
.to_string(),
ExpectedToken::Type => "type".to_string(),
ExpectedToken::Variable => "variable access".to_string(),
ExpectedToken::Function => "function name".to_string(),
ExpectedToken::AfterIdentListArg => {
"next argument, trailing comma, or end of list (',' or ';')".to_string()
}
ExpectedToken::AfterIdentListComma => {
"next argument or end of list (';')".to_string()
}
ExpectedToken::DiagnosticAttribute => {
"the 'diagnostic' attribute identifier".to_string()
}
};
ParseError {
message: format!(
"expected {}, found '{}'",
expected_str, &source[unexpected_span],
),
labels: vec![(unexpected_span, format!("expected {expected_str}").into())],
notes: vec![],
}
}
Error::UnexpectedComponents(bad_span) => ParseError {
message: "unexpected components".to_string(),
labels: vec![(bad_span, "unexpected components".into())],
notes: vec![],
},
Error::UnexpectedOperationInConstContext(span) => ParseError {
message: "this operation is not supported in a const context".to_string(),
labels: vec![(span, "operation not supported here".into())],
notes: vec![],
},
Error::BadNumber(bad_span, ref err) => ParseError {
message: format!("{}: `{}`", err, &source[bad_span],),
labels: vec![(bad_span, err.to_string().into())],
notes: vec![],
},
Error::BadMatrixScalarKind(span, scalar) => ParseError {
message: format!(
"matrix scalar type must be floating-point, but found `{}`",
scalar.to_wgsl()
),
labels: vec![(span, "must be floating-point (e.g. `f32`)".into())],
notes: vec![],
},
Error::BadAccessor(accessor_span) => ParseError {
message: format!("invalid field accessor `{}`", &source[accessor_span],),
labels: vec![(accessor_span, "invalid accessor".into())],
notes: vec![],
},
Error::UnknownIdent(ident_span, ident) => ParseError {
message: format!("no definition in scope for identifier: '{ident}'"),
labels: vec![(ident_span, "unknown identifier".into())],
notes: vec![],
},
Error::UnknownScalarType(bad_span) => ParseError {
message: format!("unknown scalar type: '{}'", &source[bad_span]),
labels: vec![(bad_span, "unknown scalar type".into())],
notes: vec!["Valid scalar types are f32, f64, i32, u32, bool".into()],
},
Error::BadTextureSampleType { span, scalar } => ParseError {
message: format!(
"texture sample type must be one of f32, i32 or u32, but found {}",
scalar.to_wgsl()
),
labels: vec![(span, "must be one of f32, i32 or u32".into())],
notes: vec![],
},
Error::BadIncrDecrReferenceType(span) => ParseError {
message: concat!(
"increment/decrement operation requires ",
"reference type to be one of i32 or u32"
)
.to_string(),
labels: vec![(span, "must be a reference type of i32 or u32".into())],
notes: vec![],
},
Error::BadTexture(bad_span) => ParseError {
message: format!(
"expected an image, but found '{}' which is not an image",
&source[bad_span]
),
labels: vec![(bad_span, "not an image".into())],
notes: vec![],
},
Error::BadTypeCast {
span,
ref from_type,
ref to_type,
} => {
let msg = format!("cannot cast a {from_type} to a {to_type}");
ParseError {
message: msg.clone(),
labels: vec![(span, msg.into())],
notes: vec![],
}
}
Error::InvalidResolve(ref resolve_error) => ParseError {
message: resolve_error.to_string(),
labels: vec![],
notes: vec![],
},
Error::InvalidForInitializer(bad_span) => ParseError {
message: format!(
"for(;;) initializer is not an assignment or a function call: '{}'",
&source[bad_span]
),
labels: vec![(bad_span, "not an assignment or function call".into())],
notes: vec![],
},
Error::InvalidBreakIf(bad_span) => ParseError {
message: "A break if is only allowed in a continuing block".to_string(),
labels: vec![(bad_span, "not in a continuing block".into())],
notes: vec![],
},
Error::InvalidGatherComponent(bad_span) => ParseError {
message: format!(
"textureGather component '{}' doesn't exist, must be 0, 1, 2, or 3",
&source[bad_span]
),
labels: vec![(bad_span, "invalid component".into())],
notes: vec![],
},
Error::InvalidConstructorComponentType(bad_span, component) => ParseError {
message: format!("invalid type for constructor component at index [{component}]"),
labels: vec![(bad_span, "invalid component type".into())],
notes: vec![],
},
Error::InvalidIdentifierUnderscore(bad_span) => ParseError {
message: "Identifier can't be '_'".to_string(),
labels: vec![(bad_span, "invalid identifier".into())],
notes: vec![
"Use phony assignment instead ('_ =' notice the absence of 'let' or 'var')"
.to_string(),
],
},
Error::ReservedIdentifierPrefix(bad_span) => ParseError {
message: format!(
"Identifier starts with a reserved prefix: '{}'",
&source[bad_span]
),
labels: vec![(bad_span, "invalid identifier".into())],
notes: vec![],
},
Error::UnknownAddressSpace(bad_span) => ParseError {
message: format!("unknown address space: '{}'", &source[bad_span]),
labels: vec![(bad_span, "unknown address space".into())],
notes: vec![],
},
Error::RepeatedAttribute(bad_span) => ParseError {
message: format!("repeated attribute: '{}'", &source[bad_span]),
labels: vec![(bad_span, "repeated attribute".into())],
notes: vec![],
},
Error::UnknownAttribute(bad_span) => ParseError {
message: format!("unknown attribute: '{}'", &source[bad_span]),
labels: vec![(bad_span, "unknown attribute".into())],
notes: vec![],
},
Error::UnknownBuiltin(bad_span) => ParseError {
message: format!("unknown builtin: '{}'", &source[bad_span]),
labels: vec![(bad_span, "unknown builtin".into())],
notes: vec![],
},
Error::UnknownAccess(bad_span) => ParseError {
message: format!("unknown access: '{}'", &source[bad_span]),
labels: vec![(bad_span, "unknown access".into())],
notes: vec![],
},
Error::UnknownStorageFormat(bad_span) => ParseError {
message: format!("unknown storage format: '{}'", &source[bad_span]),
labels: vec![(bad_span, "unknown storage format".into())],
notes: vec![],
},
Error::UnknownConservativeDepth(bad_span) => ParseError {
message: format!("unknown conservative depth: '{}'", &source[bad_span]),
labels: vec![(bad_span, "unknown conservative depth".into())],
notes: vec![],
},
Error::UnknownType(bad_span) => ParseError {
message: format!("unknown type: '{}'", &source[bad_span]),
labels: vec![(bad_span, "unknown type".into())],
notes: vec![],
},
Error::UnknownEnableExtension(span, word) => ParseError {
message: format!("unknown enable-extension `{}`", word),
labels: vec![(span, "".into())],
notes: vec![
.into(),
],
},
Error::UnknownLanguageExtension(span, name) => ParseError {
message: format!("unknown language extension `{name}`"),
labels: vec![(span, "".into())],
notes: vec![concat!(
"See available extensions at ",
)
.into()],
},
Error::UnknownDiagnosticRuleName(span) => ParseError {
message: format!("unknown `diagnostic(…)` rule name `{}`", &source[span]),
labels: vec![(span, "not a valid diagnostic rule name".into())],
notes: vec![concat!(
"See available trigger rules at ",
)
.into()],
},
Error::SizeAttributeTooLow(bad_span, min_size) => ParseError {
message: format!("struct member size must be at least {min_size}"),
labels: vec![(bad_span, format!("must be at least {min_size}").into())],
notes: vec![],
},
Error::AlignAttributeTooLow(bad_span, min_align) => ParseError {
message: format!("struct member alignment must be at least {min_align}"),
labels: vec![(bad_span, format!("must be at least {min_align}").into())],
notes: vec![],
},
Error::NonPowerOfTwoAlignAttribute(bad_span) => ParseError {
message: "struct member alignment must be a power of 2".to_string(),
labels: vec![(bad_span, "must be a power of 2".into())],
notes: vec![],
},
Error::InconsistentBinding(span) => ParseError {
message: "input/output binding is not consistent".to_string(),
labels: vec![(span, "input/output binding is not consistent".into())],
notes: vec![],
},
Error::TypeNotConstructible(span) => ParseError {
message: format!("type `{}` is not constructible", &source[span]),
labels: vec![(span, "type is not constructible".into())],
notes: vec![],
},
Error::TypeNotInferable(span) => ParseError {
message: "type can't be inferred".to_string(),
labels: vec![(span, "type can't be inferred".into())],
notes: vec![],
},
Error::InitializationTypeMismatch {
name,
ref expected,
ref got,
} => ParseError {
message: format!(
"the type of `{}` is expected to be `{}`, but got `{}`",
&source[name], expected, got,
),
labels: vec![(name, format!("definition of `{}`", &source[name]).into())],
notes: vec![],
},
Error::DeclMissingTypeAndInit(name_span) => ParseError {
message: format!(
"declaration of `{}` needs a type specifier or initializer",
&source[name_span]
),
labels: vec![(name_span, "needs a type specifier or initializer".into())],
notes: vec![],
},
Error::MissingAttribute(name, name_span) => ParseError {
message: format!(
"variable `{}` needs a '{}' attribute",
&source[name_span], name
),
labels: vec![(
name_span,
format!("definition of `{}`", &source[name_span]).into(),
)],
notes: vec![],
},
Error::InvalidAtomicPointer(span) => ParseError {
message: "atomic operation is done on a pointer to a non-atomic".to_string(),
labels: vec![(span, "atomic pointer is invalid".into())],
notes: vec![],
},
Error::InvalidAtomicOperandType(span) => ParseError {
message: "atomic operand type is inconsistent with the operation".to_string(),
labels: vec![(span, "atomic operand type is invalid".into())],
notes: vec![],
},
Error::InvalidRayQueryPointer(span) => ParseError {
message: "ray query operation is done on a pointer to a non-ray-query".to_string(),
labels: vec![(span, "ray query pointer is invalid".into())],
notes: vec![],
},
Error::NotPointer(span) => ParseError {
message: "the operand of the `*` operator must be a pointer".to_string(),
labels: vec![(span, "expression is not a pointer".into())],
notes: vec![],
},
Error::NotReference(what, span) => ParseError {
message: format!("{what} must be a reference"),
labels: vec![(span, "expression is not a reference".into())],
notes: vec![],
},
Error::InvalidAssignment { span, ty } => {
let (extra_label, notes) = match ty {
InvalidAssignmentType::Swizzle => (
None,
vec![
"WGSL does not support assignments to swizzles".into(),
"consider assigning each component individually".into(),
],
),
InvalidAssignmentType::ImmutableBinding(binding_span) => (
Some((binding_span, "this is an immutable binding".into())),
vec![format!(
"consider declaring '{}' with `var` instead of `let`",
&source[binding_span]
)],
),
InvalidAssignmentType::Other => (None, vec![]),
};
ParseError {
message: "invalid left-hand side of assignment".into(),
labels: std::iter::once((span, "cannot assign to this expression".into()))
.chain(extra_label)
.collect(),
notes,
}
}
Error::Pointer(what, span) => ParseError {
message: format!("{what} must not be a pointer"),
labels: vec![(span, "expression is a pointer".into())],
notes: vec![],
},
Error::ReservedKeyword(name_span) => ParseError {
message: format!("name `{}` is a reserved keyword", &source[name_span]),
labels: vec![(
name_span,
format!("definition of `{}`", &source[name_span]).into(),
)],
notes: vec![],
},
Error::Redefinition { previous, current } => ParseError {
message: format!("redefinition of `{}`", &source[current]),
labels: vec![
(
current,
format!("redefinition of `{}`", &source[current]).into(),
),
(
previous,
format!("previous definition of `{}`", &source[previous]).into(),
),
],
notes: vec![],
},
Error::RecursiveDeclaration { ident, usage } => ParseError {
message: format!("declaration of `{}` is recursive", &source[ident]),
labels: vec![(ident, "".into()), (usage, "uses itself here".into())],
notes: vec![],
},
Error::CyclicDeclaration { ident, ref path } => ParseError {
message: format!("declaration of `{}` is cyclic", &source[ident]),
labels: path
.iter()
.enumerate()
.flat_map(|(i, &(ident, usage))| {
[
(ident, "".into()),
(
usage,
if i == path.len() - 1 {
"ending the cycle".into()
} else {
format!("uses `{}`", &source[ident]).into()
},
),
]
})
.collect(),
notes: vec![],
},
Error::InvalidSwitchValue { uint, span } => ParseError {
message: "invalid switch value".to_string(),
labels: vec![(
span,
if uint {
"expected unsigned integer"
} else {
"expected signed integer"
}
.into(),
)],
notes: vec![if uint {
format!("suffix the integer with a `u`: '{}u'", &source[span])
} else {
let span = span.to_range().unwrap();
format!(
"remove the `u` suffix: '{}'",
&source[span.start..span.end - 1]
)
}],
},
Error::CalledEntryPoint(span) => ParseError {
message: "entry point cannot be called".to_string(),
labels: vec![(span, "entry point cannot be called".into())],
notes: vec![],
},
Error::WrongArgumentCount {
span,
ref expected,
found,
} => ParseError {
message: format!(
"wrong number of arguments: expected {}, found {}",
if expected.len() < 2 {
format!("{}", expected.start)
} else {
format!("{}..{}", expected.start, expected.end)
},
found
),
labels: vec![(span, "wrong number of arguments".into())],
notes: vec![],
},
Error::FunctionReturnsVoid(span) => ParseError {
message: "function does not return any value".to_string(),
labels: vec![(span, "".into())],
notes: vec![
"perhaps you meant to call the function in a separate statement?".into(),
],
},
Error::InvalidWorkGroupUniformLoad(span) => ParseError {
message: "incorrect type passed to workgroupUniformLoad".into(),
labels: vec![(span, "".into())],
notes: vec!["passed type must be a workgroup pointer".into()],
},
Error::Internal(message) => ParseError {
message: "internal WGSL front end error".to_string(),
labels: vec![],
notes: vec![message.into()],
},
Error::ExpectedConstExprConcreteIntegerScalar(span) => ParseError {
message: concat!(
"must be a const-expression that ",
"resolves to a concrete integer scalar (u32 or i32)"
)
.to_string(),
labels: vec![(span, "must resolve to u32 or i32".into())],
notes: vec![],
},
Error::ExpectedNonNegative(span) => ParseError {
message: "must be non-negative (>= 0)".to_string(),
labels: vec![(span, "must be non-negative".into())],
notes: vec![],
},
Error::ExpectedPositiveArrayLength(span) => ParseError {
message: "array element count must be positive (> 0)".to_string(),
labels: vec![(span, "must be positive".into())],
notes: vec![],
},
Error::ConstantEvaluatorError(ref e, span) => ParseError {
message: e.to_string(),
labels: vec![(span, "see msg".into())],
notes: vec![],
},
Error::MissingWorkgroupSize(span) => ParseError {
message: "workgroup size is missing on compute shader entry point".to_string(),
labels: vec![(
span,
"must be paired with a @workgroup_size attribute".into(),
)],
notes: vec![],
},
Error::AutoConversion(ref error) => {
// destructuring ensures all fields are handled
let AutoConversionError {
dest_span,
ref dest_type,
source_span,
ref source_type,
} = **error;
ParseError {
message: format!(
"automatic conversions cannot convert `{}` to `{}`",
source_type, dest_type
),
labels: vec![
(
dest_span,
format!("a value of type {dest_type} is required here").into(),
),
(
source_span,
format!("this expression has type {source_type}").into(),
),
],
notes: vec![],
}
}
Error::AutoConversionLeafScalar(ref error) => {
let AutoConversionLeafScalarError {
dest_span,
ref dest_scalar,
source_span,
ref source_type,
} = **error;
ParseError {
message: format!(
"automatic conversions cannot convert elements of `{}` to `{}`",
source_type, dest_scalar
),
labels: vec![
(
dest_span,
format!(
"a value with elements of type {} is required here",
dest_scalar
)
.into(),
),
(
source_span,
format!("this expression has type {source_type}").into(),
),
],
notes: vec![],
}
}
Error::ConcretizationFailed(ref error) => {
let ConcretizationFailedError {
expr_span,
ref expr_type,
ref scalar,
ref inner,
} = **error;
ParseError {
message: format!("failed to convert expression to a concrete type: {inner}"),
labels: vec![(
expr_span,
format!("this expression has type {expr_type}").into(),
)],
notes: vec![format!(
"the expression should have been converted to have {} scalar type",
scalar
)],
}
}
Error::ExceededLimitForNestedBraces { span, limit } => ParseError {
message: "brace nesting limit reached".into(),
labels: vec![(span, "limit reached at this brace".into())],
notes: vec![format!("nesting limit is currently set to {limit}")],
},
Error::PipelineConstantIDValue(span) => ParseError {
message: "pipeline constant ID must be between 0 and 65535 inclusive".to_string(),
labels: vec![(span, "must be between 0 and 65535 inclusive".into())],
notes: vec![],
},
Error::NotBool(span) => ParseError {
message: "must be a const-expression that resolves to a bool".to_string(),
labels: vec![(span, "must resolve to bool".into())],
notes: vec![],
},
Error::ConstAssertFailed(span) => ParseError {
message: "const_assert failure".to_string(),
labels: vec![(span, "evaluates to false".into())],
notes: vec![],
},
Error::DirectiveAfterFirstGlobalDecl { directive_span } => ParseError {
message: "expected global declaration, but found a global directive".into(),
labels: vec![(
directive_span,
"written after first global declaration".into(),
)],
notes: vec![concat!(
"global directives are only allowed before global declarations; ",
"maybe hoist this closer to the top of the shader module?"
)
.into()],
},
Error::EnableExtensionNotYetImplemented { kind, span } => ParseError {
message: format!(
"the `{}` enable-extension is not yet supported",
EnableExtension::Unimplemented(kind).to_ident()
),
labels: vec![(
span,
concat!(
"this enable-extension specifies standard functionality ",
"which is not yet implemented in Naga"
)
.into(),
)],
notes: vec![format!(
concat!(
"Let Naga maintainers know that you ran into this at ",
"so they can prioritize it!"
),
kind.tracking_issue_num()
)],
},
Error::EnableExtensionNotEnabled { kind, span } => ParseError {
message: format!("`{}` enable-extension is not enabled", kind.to_ident()),
labels: vec![(
span,
format!(
concat!(
"the `{}` enable-extension is needed for this functionality, ",
"but it is not currently enabled"
),
kind.to_ident()
)
.into(),
)],
notes: if let EnableExtension::Unimplemented(kind) = kind {
vec![format!(
concat!(
"This enable-extension is not yet implemented. ",
"Let Naga maintainers know that you ran into this at ",
"so they can prioritize it!"
),
kind.tracking_issue_num()
)]
} else {
vec![]
},
},
Error::LanguageExtensionNotYetImplemented { kind, span } => ParseError {
message: format!(
"the `{}` language extension is not yet supported",
LanguageExtension::Unimplemented(kind).to_ident()
),
labels: vec![(span, "".into())],
notes: vec![format!(
concat!(
"Let Naga maintainers know that you ran into this at ",
"so they can prioritize it!"
),
kind.tracking_issue_num()
)],
},
Error::DiagnosticInvalidSeverity {
severity_control_name_span,
} => ParseError {
message: "invalid `diagnostic(…)` severity".into(),
labels: vec![(
severity_control_name_span,
"not a valid severity level".into(),
)],
notes: vec![concat!(
"See available severities at ",
)
.into()],
},
Error::DiagnosticDuplicateTriggeringRule(ConflictingDiagnosticRuleError {
triggering_rule_spans,
}) => {
let [first_span, second_span] = triggering_rule_spans;
ParseError {
message: "found conflicting `diagnostic(…)` rule(s)".into(),
labels: vec![
(first_span, "first rule".into()),
(second_span, "second rule".into()),
],
notes: vec![
concat!(
"Multiple `diagnostic(…)` rules with the same rule name ",
"conflict unless they are directives and the severity is the same.",
)
.into(),
"You should delete the rule you don't want.".into(),
],
}
}
Error::DiagnosticAttributeNotYetImplementedAtParseSite {
site_name_plural,
ref spans,
} => ParseError {
message: "`@diagnostic(…)` attribute(s) not yet implemented".into(),
labels: {
let mut spans = spans.iter().cloned();
let first = spans
.next()
.map(|span| {
(
span,
format!("can't use this on {site_name_plural} (yet)").into(),
)
})
.expect("internal error: diag. attr. rejection on empty map");
std::iter::once(first)
.chain(spans.map(|span| (span, "".into())))
.collect()
},
notes: vec![format!(concat!(
"Let Naga maintainers know that you ran into this at ",
"so they can prioritize it!"
))],
},
Error::DiagnosticAttributeNotSupported {
on_what_plural,
ref spans,
} => ParseError {
message: format!(
"`@diagnostic(…)` attribute(s) on {on_what_plural} are not supported",
),
labels: spans
.iter()
.cloned()
.map(|span| (span, "".into()))
.collect(),
notes: vec![
concat!(
"`@diagnostic(…)` attributes are only permitted on `fn`s, ",
"some statements, and `switch`/`loop` bodies."
)
.into(),
"These attributes are well-formed, you likely just need to move them.".into(),
],
},
}
}
}
#[test]
fn test_error_size() {
assert!(size_of::<Error<'_>>() <= 48);
}