naga/front/wgsl/
error.rs

1use crate::front::wgsl::parse::directive::enable_extension::{
2    EnableExtension, UnimplementedEnableExtension,
3};
4use crate::front::wgsl::parse::directive::language_extension::{
5    LanguageExtension, UnimplementedLanguageExtension,
6};
7use crate::front::wgsl::parse::directive::{DirectiveKind, UnimplementedDirectiveKind};
8use crate::front::wgsl::parse::lexer::Token;
9use crate::front::wgsl::Scalar;
10use crate::proc::{Alignment, ConstantEvaluatorError, ResolveError};
11use crate::{SourceLocation, Span};
12use codespan_reporting::diagnostic::{Diagnostic, Label};
13use codespan_reporting::files::SimpleFile;
14use codespan_reporting::term;
15use std::borrow::Cow;
16use std::ops::Range;
17use termcolor::{ColorChoice, NoColor, StandardStream};
18use thiserror::Error;
19
20#[cfg(test)]
21use std::mem::size_of;
22
23#[derive(Clone, Debug)]
24pub struct ParseError {
25    message: String,
26    // The first span should be the primary span, and the other ones should be complementary.
27    labels: Vec<(Span, Cow<'static, str>)>,
28    notes: Vec<String>,
29}
30
31impl ParseError {
32    pub fn labels(&self) -> impl ExactSizeIterator<Item = (Span, &str)> + '_ {
33        self.labels
34            .iter()
35            .map(|&(span, ref msg)| (span, msg.as_ref()))
36    }
37
38    pub fn message(&self) -> &str {
39        &self.message
40    }
41
42    fn diagnostic(&self) -> Diagnostic<()> {
43        let diagnostic = Diagnostic::error()
44            .with_message(self.message.to_string())
45            .with_labels(
46                self.labels
47                    .iter()
48                    .filter_map(|label| label.0.to_range().map(|range| (label, range)))
49                    .map(|(label, range)| {
50                        Label::primary((), range).with_message(label.1.to_string())
51                    })
52                    .collect(),
53            )
54            .with_notes(
55                self.notes
56                    .iter()
57                    .map(|note| format!("note: {note}"))
58                    .collect(),
59            );
60        diagnostic
61    }
62
63    /// Emits a summary of the error to standard error stream.
64    pub fn emit_to_stderr(&self, source: &str) {
65        self.emit_to_stderr_with_path(source, "wgsl")
66    }
67
68    /// Emits a summary of the error to standard error stream.
69    pub fn emit_to_stderr_with_path<P>(&self, source: &str, path: P)
70    where
71        P: AsRef<std::path::Path>,
72    {
73        let path = path.as_ref().display().to_string();
74        let files = SimpleFile::new(path, source);
75        let config = term::Config::default();
76        let writer = StandardStream::stderr(ColorChoice::Auto);
77        term::emit(&mut writer.lock(), &config, &files, &self.diagnostic())
78            .expect("cannot write error");
79    }
80
81    /// Emits a summary of the error to a string.
82    pub fn emit_to_string(&self, source: &str) -> String {
83        self.emit_to_string_with_path(source, "wgsl")
84    }
85
86    /// Emits a summary of the error to a string.
87    pub fn emit_to_string_with_path<P>(&self, source: &str, path: P) -> String
88    where
89        P: AsRef<std::path::Path>,
90    {
91        let path = path.as_ref().display().to_string();
92        let files = SimpleFile::new(path, source);
93        let config = term::Config::default();
94        let mut writer = NoColor::new(Vec::new());
95        term::emit(&mut writer, &config, &files, &self.diagnostic()).expect("cannot write error");
96        String::from_utf8(writer.into_inner()).unwrap()
97    }
98
99    /// Returns a [`SourceLocation`] for the first label in the error message.
100    pub fn location(&self, source: &str) -> Option<SourceLocation> {
101        self.labels.first().map(|label| label.0.location(source))
102    }
103}
104
105impl std::fmt::Display for ParseError {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        write!(f, "{}", self.message)
108    }
109}
110
111impl std::error::Error for ParseError {
112    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
113        None
114    }
115}
116
117#[derive(Copy, Clone, Debug, PartialEq)]
118pub enum ExpectedToken<'a> {
119    Token(Token<'a>),
120    Identifier,
121    AfterIdentListComma,
122    AfterIdentListArg,
123    /// Expected: constant, parenthesized expression, identifier
124    PrimaryExpression,
125    /// Expected: assignment, increment/decrement expression
126    Assignment,
127    /// Expected: 'case', 'default', '}'
128    SwitchItem,
129    /// Expected: ',', ')'
130    WorkgroupSizeSeparator,
131    /// Expected: 'struct', 'let', 'var', 'type', ';', 'fn', eof
132    GlobalItem,
133    /// Expected a type.
134    Type,
135    /// Access of `var`, `let`, `const`.
136    Variable,
137    /// Access of a function
138    Function,
139}
140
141#[derive(Clone, Copy, Debug, Error, PartialEq)]
142pub enum NumberError {
143    #[error("invalid numeric literal format")]
144    Invalid,
145    #[error("numeric literal not representable by target type")]
146    NotRepresentable,
147    #[error("unimplemented f16 type")]
148    UnimplementedF16,
149}
150
151#[derive(Copy, Clone, Debug, PartialEq)]
152pub enum InvalidAssignmentType {
153    Other,
154    Swizzle,
155    ImmutableBinding(Span),
156}
157
158#[derive(Clone, Debug)]
159pub(crate) enum Error<'a> {
160    Unexpected(Span, ExpectedToken<'a>),
161    UnexpectedComponents(Span),
162    UnexpectedOperationInConstContext(Span),
163    BadNumber(Span, NumberError),
164    BadMatrixScalarKind(Span, Scalar),
165    BadAccessor(Span),
166    BadTexture(Span),
167    BadTypeCast {
168        span: Span,
169        from_type: Box<str>,
170        to_type: Box<str>,
171    },
172    BadTextureSampleType {
173        span: Span,
174        scalar: Scalar,
175    },
176    BadIncrDecrReferenceType(Span),
177    InvalidResolve(ResolveError),
178    InvalidForInitializer(Span),
179    /// A break if appeared outside of a continuing block
180    InvalidBreakIf(Span),
181    InvalidGatherComponent(Span),
182    InvalidConstructorComponentType(Span, i32),
183    InvalidIdentifierUnderscore(Span),
184    ReservedIdentifierPrefix(Span),
185    UnknownAddressSpace(Span),
186    RepeatedAttribute(Span),
187    UnknownAttribute(Span),
188    UnknownBuiltin(Span),
189    UnknownAccess(Span),
190    UnknownIdent(Span, &'a str),
191    UnknownScalarType(Span),
192    UnknownType(Span),
193    UnknownStorageFormat(Span),
194    UnknownConservativeDepth(Span),
195    UnknownEnableExtension(Span, &'a str),
196    UnknownLanguageExtension(Span, &'a str),
197    SizeAttributeTooLow(Span, u32),
198    AlignAttributeTooLow(Span, Alignment),
199    NonPowerOfTwoAlignAttribute(Span),
200    InconsistentBinding(Span),
201    TypeNotConstructible(Span),
202    TypeNotInferable(Span),
203    InitializationTypeMismatch {
204        name: Span,
205        expected: Box<str>,
206        got: Box<str>,
207    },
208    DeclMissingTypeAndInit(Span),
209    MissingAttribute(&'static str, Span),
210    InvalidAtomicPointer(Span),
211    InvalidAtomicOperandType(Span),
212    InvalidRayQueryPointer(Span),
213    Pointer(&'static str, Span),
214    NotPointer(Span),
215    NotReference(&'static str, Span),
216    InvalidAssignment {
217        span: Span,
218        ty: InvalidAssignmentType,
219    },
220    ReservedKeyword(Span),
221    /// Redefinition of an identifier (used for both module-scope and local redefinitions).
222    Redefinition {
223        /// Span of the identifier in the previous definition.
224        previous: Span,
225
226        /// Span of the identifier in the new definition.
227        current: Span,
228    },
229    /// A declaration refers to itself directly.
230    RecursiveDeclaration {
231        /// The location of the name of the declaration.
232        ident: Span,
233
234        /// The point at which it is used.
235        usage: Span,
236    },
237    /// A declaration refers to itself indirectly, through one or more other
238    /// definitions.
239    CyclicDeclaration {
240        /// The location of the name of some declaration in the cycle.
241        ident: Span,
242
243        /// The edges of the cycle of references.
244        ///
245        /// Each `(decl, reference)` pair indicates that the declaration whose
246        /// name is `decl` has an identifier at `reference` whose definition is
247        /// the next declaration in the cycle. The last pair's `reference` is
248        /// the same identifier as `ident`, above.
249        path: Box<[(Span, Span)]>,
250    },
251    InvalidSwitchValue {
252        uint: bool,
253        span: Span,
254    },
255    CalledEntryPoint(Span),
256    WrongArgumentCount {
257        span: Span,
258        expected: Range<u32>,
259        found: u32,
260    },
261    FunctionReturnsVoid(Span),
262    InvalidWorkGroupUniformLoad(Span),
263    Internal(&'static str),
264    ExpectedConstExprConcreteIntegerScalar(Span),
265    ExpectedNonNegative(Span),
266    ExpectedPositiveArrayLength(Span),
267    MissingWorkgroupSize(Span),
268    ConstantEvaluatorError(Box<ConstantEvaluatorError>, Span),
269    AutoConversion(Box<AutoConversionError>),
270    AutoConversionLeafScalar(Box<AutoConversionLeafScalarError>),
271    ConcretizationFailed(Box<ConcretizationFailedError>),
272    ExceededLimitForNestedBraces {
273        span: Span,
274        limit: u8,
275    },
276    PipelineConstantIDValue(Span),
277    NotBool(Span),
278    ConstAssertFailed(Span),
279    DirectiveNotYetImplemented {
280        kind: UnimplementedDirectiveKind,
281        span: Span,
282    },
283    DirectiveAfterFirstGlobalDecl {
284        directive_span: Span,
285    },
286    EnableExtensionNotYetImplemented {
287        kind: UnimplementedEnableExtension,
288        span: Span,
289    },
290    EnableExtensionNotEnabled {
291        kind: EnableExtension,
292        span: Span,
293    },
294    LanguageExtensionNotYetImplemented {
295        kind: UnimplementedLanguageExtension,
296        span: Span,
297    },
298}
299
300#[derive(Clone, Debug)]
301pub(crate) struct AutoConversionError {
302    pub dest_span: Span,
303    pub dest_type: Box<str>,
304    pub source_span: Span,
305    pub source_type: Box<str>,
306}
307
308#[derive(Clone, Debug)]
309pub(crate) struct AutoConversionLeafScalarError {
310    pub dest_span: Span,
311    pub dest_scalar: Box<str>,
312    pub source_span: Span,
313    pub source_type: Box<str>,
314}
315
316#[derive(Clone, Debug)]
317pub(crate) struct ConcretizationFailedError {
318    pub expr_span: Span,
319    pub expr_type: Box<str>,
320    pub scalar: Box<str>,
321    pub inner: ConstantEvaluatorError,
322}
323
324impl<'a> Error<'a> {
325    #[cold]
326    #[inline(never)]
327    pub(crate) fn as_parse_error(&self, source: &'a str) -> ParseError {
328        match *self {
329            Error::Unexpected(unexpected_span, expected) => {
330                let expected_str = match expected {
331                    ExpectedToken::Token(token) => match token {
332                        Token::Separator(c) => format!("'{c}'"),
333                        Token::Paren(c) => format!("'{c}'"),
334                        Token::Attribute => "@".to_string(),
335                        Token::Number(_) => "number".to_string(),
336                        Token::Word(s) => s.to_string(),
337                        Token::Operation(c) => format!("operation ('{c}')"),
338                        Token::LogicalOperation(c) => format!("logical operation ('{c}')"),
339                        Token::ShiftOperation(c) => format!("bitshift ('{c}{c}')"),
340                        Token::AssignmentOperation(c) if c == '<' || c == '>' => {
341                            format!("bitshift ('{c}{c}=')")
342                        }
343                        Token::AssignmentOperation(c) => format!("operation ('{c}=')"),
344                        Token::IncrementOperation => "increment operation".to_string(),
345                        Token::DecrementOperation => "decrement operation".to_string(),
346                        Token::Arrow => "->".to_string(),
347                        Token::Unknown(c) => format!("unknown ('{c}')"),
348                        Token::Trivia => "trivia".to_string(),
349                        Token::End => "end".to_string(),
350                    },
351                    ExpectedToken::Identifier => "identifier".to_string(),
352                    ExpectedToken::PrimaryExpression => "expression".to_string(),
353                    ExpectedToken::Assignment => "assignment or increment/decrement".to_string(),
354                    ExpectedToken::SwitchItem => concat!(
355                        "switch item ('case' or 'default') or a closing curly bracket ",
356                        "to signify the end of the switch statement ('}')"
357                    )
358                    .to_string(),
359                    ExpectedToken::WorkgroupSizeSeparator => {
360                        "workgroup size separator (',') or a closing parenthesis".to_string()
361                    }
362                    ExpectedToken::GlobalItem => concat!(
363                        "global item ('struct', 'const', 'var', 'alias', ';', 'fn') ",
364                        "or the end of the file"
365                    )
366                    .to_string(),
367                    ExpectedToken::Type => "type".to_string(),
368                    ExpectedToken::Variable => "variable access".to_string(),
369                    ExpectedToken::Function => "function name".to_string(),
370                    ExpectedToken::AfterIdentListArg => {
371                        "next argument, trailing comma, or end of list (',' or ';')".to_string()
372                    }
373                    ExpectedToken::AfterIdentListComma => {
374                        "next argument or end of list (';')".to_string()
375                    }
376                };
377                ParseError {
378                    message: format!(
379                        "expected {}, found '{}'",
380                        expected_str, &source[unexpected_span],
381                    ),
382                    labels: vec![(unexpected_span, format!("expected {expected_str}").into())],
383                    notes: vec![],
384                }
385            }
386            Error::UnexpectedComponents(bad_span) => ParseError {
387                message: "unexpected components".to_string(),
388                labels: vec![(bad_span, "unexpected components".into())],
389                notes: vec![],
390            },
391            Error::UnexpectedOperationInConstContext(span) => ParseError {
392                message: "this operation is not supported in a const context".to_string(),
393                labels: vec![(span, "operation not supported here".into())],
394                notes: vec![],
395            },
396            Error::BadNumber(bad_span, ref err) => ParseError {
397                message: format!("{}: `{}`", err, &source[bad_span],),
398                labels: vec![(bad_span, err.to_string().into())],
399                notes: vec![],
400            },
401            Error::BadMatrixScalarKind(span, scalar) => ParseError {
402                message: format!(
403                    "matrix scalar type must be floating-point, but found `{}`",
404                    scalar.to_wgsl()
405                ),
406                labels: vec![(span, "must be floating-point (e.g. `f32`)".into())],
407                notes: vec![],
408            },
409            Error::BadAccessor(accessor_span) => ParseError {
410                message: format!("invalid field accessor `{}`", &source[accessor_span],),
411                labels: vec![(accessor_span, "invalid accessor".into())],
412                notes: vec![],
413            },
414            Error::UnknownIdent(ident_span, ident) => ParseError {
415                message: format!("no definition in scope for identifier: '{ident}'"),
416                labels: vec![(ident_span, "unknown identifier".into())],
417                notes: vec![],
418            },
419            Error::UnknownScalarType(bad_span) => ParseError {
420                message: format!("unknown scalar type: '{}'", &source[bad_span]),
421                labels: vec![(bad_span, "unknown scalar type".into())],
422                notes: vec!["Valid scalar types are f32, f64, i32, u32, bool".into()],
423            },
424            Error::BadTextureSampleType { span, scalar } => ParseError {
425                message: format!(
426                    "texture sample type must be one of f32, i32 or u32, but found {}",
427                    scalar.to_wgsl()
428                ),
429                labels: vec![(span, "must be one of f32, i32 or u32".into())],
430                notes: vec![],
431            },
432            Error::BadIncrDecrReferenceType(span) => ParseError {
433                message: concat!(
434                    "increment/decrement operation requires ",
435                    "reference type to be one of i32 or u32"
436                )
437                .to_string(),
438                labels: vec![(span, "must be a reference type of i32 or u32".into())],
439                notes: vec![],
440            },
441            Error::BadTexture(bad_span) => ParseError {
442                message: format!(
443                    "expected an image, but found '{}' which is not an image",
444                    &source[bad_span]
445                ),
446                labels: vec![(bad_span, "not an image".into())],
447                notes: vec![],
448            },
449            Error::BadTypeCast {
450                span,
451                ref from_type,
452                ref to_type,
453            } => {
454                let msg = format!("cannot cast a {from_type} to a {to_type}");
455                ParseError {
456                    message: msg.clone(),
457                    labels: vec![(span, msg.into())],
458                    notes: vec![],
459                }
460            }
461            Error::InvalidResolve(ref resolve_error) => ParseError {
462                message: resolve_error.to_string(),
463                labels: vec![],
464                notes: vec![],
465            },
466            Error::InvalidForInitializer(bad_span) => ParseError {
467                message: format!(
468                    "for(;;) initializer is not an assignment or a function call: '{}'",
469                    &source[bad_span]
470                ),
471                labels: vec![(bad_span, "not an assignment or function call".into())],
472                notes: vec![],
473            },
474            Error::InvalidBreakIf(bad_span) => ParseError {
475                message: "A break if is only allowed in a continuing block".to_string(),
476                labels: vec![(bad_span, "not in a continuing block".into())],
477                notes: vec![],
478            },
479            Error::InvalidGatherComponent(bad_span) => ParseError {
480                message: format!(
481                    "textureGather component '{}' doesn't exist, must be 0, 1, 2, or 3",
482                    &source[bad_span]
483                ),
484                labels: vec![(bad_span, "invalid component".into())],
485                notes: vec![],
486            },
487            Error::InvalidConstructorComponentType(bad_span, component) => ParseError {
488                message: format!("invalid type for constructor component at index [{component}]"),
489                labels: vec![(bad_span, "invalid component type".into())],
490                notes: vec![],
491            },
492            Error::InvalidIdentifierUnderscore(bad_span) => ParseError {
493                message: "Identifier can't be '_'".to_string(),
494                labels: vec![(bad_span, "invalid identifier".into())],
495                notes: vec![
496                    "Use phony assignment instead ('_ =' notice the absence of 'let' or 'var')"
497                        .to_string(),
498                ],
499            },
500            Error::ReservedIdentifierPrefix(bad_span) => ParseError {
501                message: format!(
502                    "Identifier starts with a reserved prefix: '{}'",
503                    &source[bad_span]
504                ),
505                labels: vec![(bad_span, "invalid identifier".into())],
506                notes: vec![],
507            },
508            Error::UnknownAddressSpace(bad_span) => ParseError {
509                message: format!("unknown address space: '{}'", &source[bad_span]),
510                labels: vec![(bad_span, "unknown address space".into())],
511                notes: vec![],
512            },
513            Error::RepeatedAttribute(bad_span) => ParseError {
514                message: format!("repeated attribute: '{}'", &source[bad_span]),
515                labels: vec![(bad_span, "repeated attribute".into())],
516                notes: vec![],
517            },
518            Error::UnknownAttribute(bad_span) => ParseError {
519                message: format!("unknown attribute: '{}'", &source[bad_span]),
520                labels: vec![(bad_span, "unknown attribute".into())],
521                notes: vec![],
522            },
523            Error::UnknownBuiltin(bad_span) => ParseError {
524                message: format!("unknown builtin: '{}'", &source[bad_span]),
525                labels: vec![(bad_span, "unknown builtin".into())],
526                notes: vec![],
527            },
528            Error::UnknownAccess(bad_span) => ParseError {
529                message: format!("unknown access: '{}'", &source[bad_span]),
530                labels: vec![(bad_span, "unknown access".into())],
531                notes: vec![],
532            },
533            Error::UnknownStorageFormat(bad_span) => ParseError {
534                message: format!("unknown storage format: '{}'", &source[bad_span]),
535                labels: vec![(bad_span, "unknown storage format".into())],
536                notes: vec![],
537            },
538            Error::UnknownConservativeDepth(bad_span) => ParseError {
539                message: format!("unknown conservative depth: '{}'", &source[bad_span]),
540                labels: vec![(bad_span, "unknown conservative depth".into())],
541                notes: vec![],
542            },
543            Error::UnknownType(bad_span) => ParseError {
544                message: format!("unknown type: '{}'", &source[bad_span]),
545                labels: vec![(bad_span, "unknown type".into())],
546                notes: vec![],
547            },
548            Error::UnknownEnableExtension(span, word) => ParseError {
549                message: format!("unknown enable-extension `{}`", word),
550                labels: vec![(span, "".into())],
551                notes: vec![
552                    "See available extensions at <https://www.w3.org/TR/WGSL/#enable-extension>."
553                        .into(),
554                ],
555            },
556            Error::UnknownLanguageExtension(span, name) => ParseError {
557                message: format!("unknown language extension `{name}`"),
558                labels: vec![(span, "".into())],
559                notes: vec![concat!(
560                    "See available extensions at ",
561                    "<https://www.w3.org/TR/WGSL/#language-extensions-sec>."
562                )
563                .into()],
564            },
565            Error::SizeAttributeTooLow(bad_span, min_size) => ParseError {
566                message: format!("struct member size must be at least {min_size}"),
567                labels: vec![(bad_span, format!("must be at least {min_size}").into())],
568                notes: vec![],
569            },
570            Error::AlignAttributeTooLow(bad_span, min_align) => ParseError {
571                message: format!("struct member alignment must be at least {min_align}"),
572                labels: vec![(bad_span, format!("must be at least {min_align}").into())],
573                notes: vec![],
574            },
575            Error::NonPowerOfTwoAlignAttribute(bad_span) => ParseError {
576                message: "struct member alignment must be a power of 2".to_string(),
577                labels: vec![(bad_span, "must be a power of 2".into())],
578                notes: vec![],
579            },
580            Error::InconsistentBinding(span) => ParseError {
581                message: "input/output binding is not consistent".to_string(),
582                labels: vec![(span, "input/output binding is not consistent".into())],
583                notes: vec![],
584            },
585            Error::TypeNotConstructible(span) => ParseError {
586                message: format!("type `{}` is not constructible", &source[span]),
587                labels: vec![(span, "type is not constructible".into())],
588                notes: vec![],
589            },
590            Error::TypeNotInferable(span) => ParseError {
591                message: "type can't be inferred".to_string(),
592                labels: vec![(span, "type can't be inferred".into())],
593                notes: vec![],
594            },
595            Error::InitializationTypeMismatch {
596                name,
597                ref expected,
598                ref got,
599            } => ParseError {
600                message: format!(
601                    "the type of `{}` is expected to be `{}`, but got `{}`",
602                    &source[name], expected, got,
603                ),
604                labels: vec![(name, format!("definition of `{}`", &source[name]).into())],
605                notes: vec![],
606            },
607            Error::DeclMissingTypeAndInit(name_span) => ParseError {
608                message: format!(
609                    "declaration of `{}` needs a type specifier or initializer",
610                    &source[name_span]
611                ),
612                labels: vec![(name_span, "needs a type specifier or initializer".into())],
613                notes: vec![],
614            },
615            Error::MissingAttribute(name, name_span) => ParseError {
616                message: format!(
617                    "variable `{}` needs a '{}' attribute",
618                    &source[name_span], name
619                ),
620                labels: vec![(
621                    name_span,
622                    format!("definition of `{}`", &source[name_span]).into(),
623                )],
624                notes: vec![],
625            },
626            Error::InvalidAtomicPointer(span) => ParseError {
627                message: "atomic operation is done on a pointer to a non-atomic".to_string(),
628                labels: vec![(span, "atomic pointer is invalid".into())],
629                notes: vec![],
630            },
631            Error::InvalidAtomicOperandType(span) => ParseError {
632                message: "atomic operand type is inconsistent with the operation".to_string(),
633                labels: vec![(span, "atomic operand type is invalid".into())],
634                notes: vec![],
635            },
636            Error::InvalidRayQueryPointer(span) => ParseError {
637                message: "ray query operation is done on a pointer to a non-ray-query".to_string(),
638                labels: vec![(span, "ray query pointer is invalid".into())],
639                notes: vec![],
640            },
641            Error::NotPointer(span) => ParseError {
642                message: "the operand of the `*` operator must be a pointer".to_string(),
643                labels: vec![(span, "expression is not a pointer".into())],
644                notes: vec![],
645            },
646            Error::NotReference(what, span) => ParseError {
647                message: format!("{what} must be a reference"),
648                labels: vec![(span, "expression is not a reference".into())],
649                notes: vec![],
650            },
651            Error::InvalidAssignment { span, ty } => {
652                let (extra_label, notes) = match ty {
653                    InvalidAssignmentType::Swizzle => (
654                        None,
655                        vec![
656                            "WGSL does not support assignments to swizzles".into(),
657                            "consider assigning each component individually".into(),
658                        ],
659                    ),
660                    InvalidAssignmentType::ImmutableBinding(binding_span) => (
661                        Some((binding_span, "this is an immutable binding".into())),
662                        vec![format!(
663                            "consider declaring '{}' with `var` instead of `let`",
664                            &source[binding_span]
665                        )],
666                    ),
667                    InvalidAssignmentType::Other => (None, vec![]),
668                };
669
670                ParseError {
671                    message: "invalid left-hand side of assignment".into(),
672                    labels: std::iter::once((span, "cannot assign to this expression".into()))
673                        .chain(extra_label)
674                        .collect(),
675                    notes,
676                }
677            }
678            Error::Pointer(what, span) => ParseError {
679                message: format!("{what} must not be a pointer"),
680                labels: vec![(span, "expression is a pointer".into())],
681                notes: vec![],
682            },
683            Error::ReservedKeyword(name_span) => ParseError {
684                message: format!("name `{}` is a reserved keyword", &source[name_span]),
685                labels: vec![(
686                    name_span,
687                    format!("definition of `{}`", &source[name_span]).into(),
688                )],
689                notes: vec![],
690            },
691            Error::Redefinition { previous, current } => ParseError {
692                message: format!("redefinition of `{}`", &source[current]),
693                labels: vec![
694                    (
695                        current,
696                        format!("redefinition of `{}`", &source[current]).into(),
697                    ),
698                    (
699                        previous,
700                        format!("previous definition of `{}`", &source[previous]).into(),
701                    ),
702                ],
703                notes: vec![],
704            },
705            Error::RecursiveDeclaration { ident, usage } => ParseError {
706                message: format!("declaration of `{}` is recursive", &source[ident]),
707                labels: vec![(ident, "".into()), (usage, "uses itself here".into())],
708                notes: vec![],
709            },
710            Error::CyclicDeclaration { ident, ref path } => ParseError {
711                message: format!("declaration of `{}` is cyclic", &source[ident]),
712                labels: path
713                    .iter()
714                    .enumerate()
715                    .flat_map(|(i, &(ident, usage))| {
716                        [
717                            (ident, "".into()),
718                            (
719                                usage,
720                                if i == path.len() - 1 {
721                                    "ending the cycle".into()
722                                } else {
723                                    format!("uses `{}`", &source[ident]).into()
724                                },
725                            ),
726                        ]
727                    })
728                    .collect(),
729                notes: vec![],
730            },
731            Error::InvalidSwitchValue { uint, span } => ParseError {
732                message: "invalid switch value".to_string(),
733                labels: vec![(
734                    span,
735                    if uint {
736                        "expected unsigned integer"
737                    } else {
738                        "expected signed integer"
739                    }
740                    .into(),
741                )],
742                notes: vec![if uint {
743                    format!("suffix the integer with a `u`: '{}u'", &source[span])
744                } else {
745                    let span = span.to_range().unwrap();
746                    format!(
747                        "remove the `u` suffix: '{}'",
748                        &source[span.start..span.end - 1]
749                    )
750                }],
751            },
752            Error::CalledEntryPoint(span) => ParseError {
753                message: "entry point cannot be called".to_string(),
754                labels: vec![(span, "entry point cannot be called".into())],
755                notes: vec![],
756            },
757            Error::WrongArgumentCount {
758                span,
759                ref expected,
760                found,
761            } => ParseError {
762                message: format!(
763                    "wrong number of arguments: expected {}, found {}",
764                    if expected.len() < 2 {
765                        format!("{}", expected.start)
766                    } else {
767                        format!("{}..{}", expected.start, expected.end)
768                    },
769                    found
770                ),
771                labels: vec![(span, "wrong number of arguments".into())],
772                notes: vec![],
773            },
774            Error::FunctionReturnsVoid(span) => ParseError {
775                message: "function does not return any value".to_string(),
776                labels: vec![(span, "".into())],
777                notes: vec![
778                    "perhaps you meant to call the function in a separate statement?".into(),
779                ],
780            },
781            Error::InvalidWorkGroupUniformLoad(span) => ParseError {
782                message: "incorrect type passed to workgroupUniformLoad".into(),
783                labels: vec![(span, "".into())],
784                notes: vec!["passed type must be a workgroup pointer".into()],
785            },
786            Error::Internal(message) => ParseError {
787                message: "internal WGSL front end error".to_string(),
788                labels: vec![],
789                notes: vec![message.into()],
790            },
791            Error::ExpectedConstExprConcreteIntegerScalar(span) => ParseError {
792                message: concat!(
793                    "must be a const-expression that ",
794                    "resolves to a concrete integer scalar (u32 or i32)"
795                )
796                .to_string(),
797                labels: vec![(span, "must resolve to u32 or i32".into())],
798                notes: vec![],
799            },
800            Error::ExpectedNonNegative(span) => ParseError {
801                message: "must be non-negative (>= 0)".to_string(),
802                labels: vec![(span, "must be non-negative".into())],
803                notes: vec![],
804            },
805            Error::ExpectedPositiveArrayLength(span) => ParseError {
806                message: "array element count must be positive (> 0)".to_string(),
807                labels: vec![(span, "must be positive".into())],
808                notes: vec![],
809            },
810            Error::ConstantEvaluatorError(ref e, span) => ParseError {
811                message: e.to_string(),
812                labels: vec![(span, "see msg".into())],
813                notes: vec![],
814            },
815            Error::MissingWorkgroupSize(span) => ParseError {
816                message: "workgroup size is missing on compute shader entry point".to_string(),
817                labels: vec![(
818                    span,
819                    "must be paired with a @workgroup_size attribute".into(),
820                )],
821                notes: vec![],
822            },
823            Error::AutoConversion(ref error) => {
824                // destructuring ensures all fields are handled
825                let AutoConversionError {
826                    dest_span,
827                    ref dest_type,
828                    source_span,
829                    ref source_type,
830                } = **error;
831                ParseError {
832                    message: format!(
833                        "automatic conversions cannot convert `{}` to `{}`",
834                        source_type, dest_type
835                    ),
836                    labels: vec![
837                        (
838                            dest_span,
839                            format!("a value of type {dest_type} is required here").into(),
840                        ),
841                        (
842                            source_span,
843                            format!("this expression has type {source_type}").into(),
844                        ),
845                    ],
846                    notes: vec![],
847                }
848            }
849            Error::AutoConversionLeafScalar(ref error) => {
850                let AutoConversionLeafScalarError {
851                    dest_span,
852                    ref dest_scalar,
853                    source_span,
854                    ref source_type,
855                } = **error;
856                ParseError {
857                    message: format!(
858                        "automatic conversions cannot convert elements of `{}` to `{}`",
859                        source_type, dest_scalar
860                    ),
861                    labels: vec![
862                        (
863                            dest_span,
864                            format!(
865                                "a value with elements of type {} is required here",
866                                dest_scalar
867                            )
868                            .into(),
869                        ),
870                        (
871                            source_span,
872                            format!("this expression has type {source_type}").into(),
873                        ),
874                    ],
875                    notes: vec![],
876                }
877            }
878            Error::ConcretizationFailed(ref error) => {
879                let ConcretizationFailedError {
880                    expr_span,
881                    ref expr_type,
882                    ref scalar,
883                    ref inner,
884                } = **error;
885                ParseError {
886                    message: format!("failed to convert expression to a concrete type: {inner}"),
887                    labels: vec![(
888                        expr_span,
889                        format!("this expression has type {expr_type}").into(),
890                    )],
891                    notes: vec![format!(
892                        "the expression should have been converted to have {} scalar type",
893                        scalar
894                    )],
895                }
896            }
897            Error::ExceededLimitForNestedBraces { span, limit } => ParseError {
898                message: "brace nesting limit reached".into(),
899                labels: vec![(span, "limit reached at this brace".into())],
900                notes: vec![format!("nesting limit is currently set to {limit}")],
901            },
902            Error::PipelineConstantIDValue(span) => ParseError {
903                message: "pipeline constant ID must be between 0 and 65535 inclusive".to_string(),
904                labels: vec![(span, "must be between 0 and 65535 inclusive".into())],
905                notes: vec![],
906            },
907            Error::NotBool(span) => ParseError {
908                message: "must be a const-expression that resolves to a bool".to_string(),
909                labels: vec![(span, "must resolve to bool".into())],
910                notes: vec![],
911            },
912            Error::ConstAssertFailed(span) => ParseError {
913                message: "const_assert failure".to_string(),
914                labels: vec![(span, "evaluates to false".into())],
915                notes: vec![],
916            },
917            Error::DirectiveNotYetImplemented { kind, span } => ParseError {
918                message: format!(
919                    "the `{}` directive is not yet implemented",
920                    DirectiveKind::Unimplemented(kind).to_ident()
921                ),
922                labels: vec![(
923                    span,
924                    "this global directive is standard, but not yet implemented".into(),
925                )],
926                notes: vec![format!(
927                    concat!(
928                        "Let Naga maintainers know that you ran into this at ",
929                        "<https://github.com/gfx-rs/wgpu/issues/{}>, ",
930                        "so they can prioritize it!"
931                    ),
932                    kind.tracking_issue_num()
933                )],
934            },
935            Error::DirectiveAfterFirstGlobalDecl { directive_span } => ParseError {
936                message: "expected global declaration, but found a global directive".into(),
937                labels: vec![(
938                    directive_span,
939                    "written after first global declaration".into(),
940                )],
941                notes: vec![concat!(
942                    "global directives are only allowed before global declarations; ",
943                    "maybe hoist this closer to the top of the shader module?"
944                )
945                .into()],
946            },
947            Error::EnableExtensionNotYetImplemented { kind, span } => ParseError {
948                message: format!(
949                    "the `{}` enable-extension is not yet supported",
950                    EnableExtension::Unimplemented(kind).to_ident()
951                ),
952                labels: vec![(
953                    span,
954                    concat!(
955                        "this enable-extension specifies standard functionality ",
956                        "which is not yet implemented in Naga"
957                    )
958                    .into(),
959                )],
960                notes: vec![format!(
961                    concat!(
962                        "Let Naga maintainers know that you ran into this at ",
963                        "<https://github.com/gfx-rs/wgpu/issues/{}>, ",
964                        "so they can prioritize it!"
965                    ),
966                    kind.tracking_issue_num()
967                )],
968            },
969            Error::EnableExtensionNotEnabled { kind, span } => ParseError {
970                message: format!("`{}` enable-extension is not enabled", kind.to_ident()),
971                labels: vec![(
972                    span,
973                    format!(
974                        concat!(
975                            "the `{}` enable-extension is needed for this functionality, ",
976                            "but it is not currently enabled"
977                        ),
978                        kind.to_ident()
979                    )
980                    .into(),
981                )],
982                notes: if let EnableExtension::Unimplemented(kind) = kind {
983                    vec![format!(
984                        concat!(
985                            "This enable-extension is not yet implemented. ",
986                            "Let Naga maintainers know that you ran into this at ",
987                            "<https://github.com/gfx-rs/wgpu/issues/{}>, ",
988                            "so they can prioritize it!"
989                        ),
990                        kind.tracking_issue_num()
991                    )]
992                } else {
993                    vec![]
994                },
995            },
996            Error::LanguageExtensionNotYetImplemented { kind, span } => ParseError {
997                message: format!(
998                    "the `{}` language extension is not yet supported",
999                    LanguageExtension::Unimplemented(kind).to_ident()
1000                ),
1001                labels: vec![(span, "".into())],
1002                notes: vec![format!(
1003                    concat!(
1004                        "Let Naga maintainers know that you ran into this at ",
1005                        "<https://github.com/gfx-rs/wgpu/issues/{}>, ",
1006                        "so they can prioritize it!"
1007                    ),
1008                    kind.tracking_issue_num()
1009                )],
1010            },
1011        }
1012    }
1013}
1014
1015#[test]
1016fn test_error_size() {
1017    assert!(size_of::<Error<'_>>() <= 48);
1018}