Skip to main content

naga/front/wgsl/
error.rs

1//! Formatting WGSL front end error messages.
2
3#![expect(
4    clippy::pattern_type_mismatch,
5    reason = "There are matches on references, since it produces less LLVM IR than dereferencing"
6)]
7
8use crate::common::wgsl::TryToWgsl;
9use crate::diagnostic_filter::ConflictingDiagnosticRuleError;
10use crate::error::replace_control_chars;
11use crate::proc::{Alignment, ConstantEvaluatorError, ResolveError};
12use crate::{Scalar, SourceLocation, Span, UnaryOperator};
13
14use super::parse::directive::enable_extension::{EnableExtension, UnimplementedEnableExtension};
15use super::parse::directive::language_extension::{
16    LanguageExtension, UnimplementedLanguageExtension,
17};
18use super::parse::lexer::Token;
19
20use codespan_reporting::diagnostic::{Diagnostic, Label};
21use codespan_reporting::files::SimpleFile;
22use codespan_reporting::term;
23use thiserror::Error;
24
25use alloc::{
26    borrow::Cow,
27    boxed::Box,
28    format,
29    string::{String, ToString},
30    vec,
31    vec::Vec,
32};
33use core::fmt::Write as _;
34use core::ops::Range;
35
36#[derive(Clone, Debug)]
37pub struct ParseError {
38    message: Cow<'static, str>,
39    // The first span should be the primary span, and the other ones should be complementary.
40    labels: Vec<(Span, Cow<'static, str>)>,
41    notes: Vec<Cow<'static, str>>,
42}
43
44impl ParseError {
45    pub fn labels(&self) -> impl ExactSizeIterator<Item = (Span, &str)> + '_ {
46        self.labels
47            .iter()
48            .map(|&(span, ref msg)| (span, msg.as_ref()))
49    }
50
51    pub fn message(&self) -> &str {
52        &self.message
53    }
54
55    pub fn notes(&self) -> impl ExactSizeIterator<Item = &str> + '_ {
56        self.notes.iter().map(Cow::as_ref)
57    }
58
59    fn diagnostic(&self) -> Diagnostic<()> {
60        let diagnostic = Diagnostic::error()
61            .with_message(self.message.to_string())
62            .with_labels(
63                self.labels
64                    .iter()
65                    .filter_map(|label| label.0.to_range().map(|range| (label, range)))
66                    .map(|(label, range)| {
67                        Label::primary((), range).with_message(label.1.to_string())
68                    })
69                    .collect(),
70            )
71            .with_notes(
72                self.notes
73                    .iter()
74                    .map(|note| format!("note: {note}"))
75                    .collect(),
76            );
77        diagnostic
78    }
79
80    /// Emits a summary of the error to standard error stream.
81    #[cfg(feature = "stderr")]
82    pub fn emit_to_stderr(&self, source: &str) {
83        self.emit_to_stderr_with_path(source, "wgsl")
84    }
85
86    /// Emits a summary of the error to standard error stream.
87    #[cfg(feature = "stderr")]
88    pub fn emit_to_stderr_with_path<P>(&self, source: &str, path: P)
89    where
90        P: AsRef<std::path::Path>,
91    {
92        let path = path.as_ref().display().to_string();
93        let files = SimpleFile::new(path, replace_control_chars(source));
94        let config = term::Config::default();
95
96        cfg_if::cfg_if! {
97            if #[cfg(feature = "termcolor")] {
98                let writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Auto);
99                term::emit_to_write_style(&mut writer.lock(), &config, &files, &self.diagnostic())
100                    .expect("cannot write error");
101            } else {
102                let writer = std::io::stderr();
103                term::emit_to_io_write(&mut writer.lock(), &config, &files, &self.diagnostic())
104                    .expect("cannot write error");
105            }
106        }
107    }
108
109    /// Emits a summary of the error to a string.
110    pub fn emit_to_string(&self, source: &str) -> String {
111        self.emit_to_string_with_path(source, "wgsl")
112    }
113
114    /// Emits a summary of the error to a string.
115    ///
116    /// `path` gives the filename to attribute the error to in the
117    /// output; this function does not try to access the file.
118    pub fn emit_to_string_with_path(&self, source: &str, path: &str) -> String {
119        let files = SimpleFile::new(path, replace_control_chars(source));
120        let config = term::Config::default();
121
122        let mut writer = crate::error::DiagnosticBuffer::new();
123        writer
124            .emit_to_self(&config, &files, &self.diagnostic())
125            .expect("cannot write error");
126        writer.into_string()
127    }
128
129    /// Returns a [`SourceLocation`] for the first label in the error message.
130    pub fn location(&self, source: &str) -> Option<SourceLocation> {
131        self.labels.first().map(|label| label.0.location(source))
132    }
133}
134
135impl core::fmt::Display for ParseError {
136    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
137        write!(f, "{}", self.message)
138    }
139}
140
141impl core::error::Error for ParseError {}
142
143#[cfg(test)]
144mod parse_error_tests {
145
146    #[test]
147    fn test_notes() {
148        use crate::front::wgsl::parse_str;
149        // wgsl code and notes taken from: `cross_vec2()` in naga/tests/naga/wgsl_errors.rs
150        assert_eq!(
151            parse_str(
152                r#"
153            fn x() -> f32 {
154                return cross(vec2(0., 1.), vec2(0., 1.));
155            }
156        "#,
157            )
158            .unwrap_err()
159            .notes()
160            .collect::<super::Vec<_>>(),
161            [
162                "`cross` accepts the following types for argument #1:",
163                "allowed type: vec3<{AbstractFloat}>",
164                "allowed type: vec3<f32>",
165                "allowed type: vec3<f16>",
166                "allowed type: vec3<f64>"
167            ]
168            .to_vec()
169        );
170    }
171}
172
173#[derive(Copy, Clone, Debug, PartialEq)]
174pub enum ExpectedToken<'a> {
175    Token(Token<'a>),
176    Identifier,
177    AfterIdentListComma,
178    AfterIdentListArg,
179    /// LHS expression (identifier component_or_swizzle_specifier?, (`lhs_expression`) component_or_swizzle_specifier?, &`lhs_expression`, *`lhs_expression`)
180    LhsExpression,
181    /// Expected: constant, parenthesized expression, identifier
182    PrimaryExpression,
183    /// Expected: assignment, increment/decrement expression
184    Assignment,
185    /// Expected: 'case', 'default', '}'
186    SwitchItem,
187    /// Expected: ',', ')'
188    WorkgroupSizeSeparator,
189    /// Expected: 'struct', 'let', 'var', 'type', ';', 'fn', eof
190    GlobalItem,
191    /// Access of `var`, `let`, `const`.
192    Variable,
193    /// Access of a function
194    Function,
195    /// The `diagnostic` identifier of the `@diagnostic(…)` attribute.
196    DiagnosticAttribute,
197    /// statement
198    Statement,
199    /// for loop init statement (variable_or_value_statement, variable_updating_statement, func_call_statement)
200    ForInit,
201    /// for loop update statement (variable_updating_statement, func_call_statement)
202    ForUpdate,
203}
204
205impl core::fmt::Display for ExpectedToken<'_> {
206    #[expect(
207        unused,
208        reason = "This ignores write errors, since this should only be called to write into a \
209                  String, which is infallible. Ignoring errors lowers binary bloat from this \
210                  function."
211    )]
212    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
213        // This function uses a temporary enum here to lower binary bloat by merging calls to
214        // write_str.
215        //
216        // Tokens where the output is already a single string (such as Token::Number) use Kind::Str.
217        // Tokens where the output is made up of two static strings split by a char (such as
218        // Token::Operation) use Kind::FormatChar
219        // Tokens where the output is 3 strings concatenated (such as Token::DocComment) use
220        // Kind::FormatStr
221        //
222        // Any exceptions (such as Token::ShiftOperation) handle their cases explicitly and return
223        enum Kind<'a> {
224            Str(&'a str),
225            FormatChar(&'static str, char, &'static str),
226            FormatStr([&'a str; 3]),
227        }
228
229        let kind = match *self {
230            ExpectedToken::Token(token) => match token {
231                Token::Separator(c) | Token::Paren(c) => Kind::FormatChar("`", c, "`"),
232                Token::Attribute => Kind::Str("@"),
233                Token::Number(_) => Kind::Str("number"),
234                Token::Word(s) => Kind::Str(s),
235                Token::String(_) => Kind::Str("string literal"),
236                Token::Operation(c) => Kind::FormatChar("operation (`", c, "`)"),
237                Token::LogicalOperation(c) => Kind::FormatChar("logical operation (`", c, "`)"),
238                Token::ShiftOperation(c) => {
239                    f.write_str("bitshift (`");
240                    f.write_char(c);
241                    f.write_char(c);
242                    f.write_str("`)");
243                    return Ok(())
244                },
245                Token::AssignmentOperation(c) if c == '<' || c == '>' => {
246                    f.write_str("bitshift (`");
247                    f.write_char(c);
248                    f.write_char(c);
249                    f.write_str("=`)");
250                    return Ok(())
251                }
252                Token::AssignmentOperation(c) => Kind::FormatChar("operation (`", c, "=`)"),
253                Token::IncrementOperation => Kind::Str("increment operation"),
254                Token::DecrementOperation => Kind::Str("decrement operation"),
255                Token::Arrow => Kind::Str("->"),
256                Token::TemplateArgsStart => Kind::Str("template args start"),
257                Token::TemplateArgsEnd => Kind::Str("template args end"),
258                Token::Unknown(c) => Kind::FormatChar("unknown (`", c, "`)"),
259                Token::Trivia => Kind::Str("trivia"),
260                Token::DocComment(s) => Kind::FormatStr(["doc comment ('", s, "')"]),
261                Token::ModuleDocComment(s) => Kind::FormatStr(["module doc comment ('", s, "')"]),
262                Token::End => Kind::Str("end"),
263                Token::UnterminatedBlockComment(s) => {
264                    Kind::FormatStr(["unterminated doc comment ('", s, "')"])
265                }
266            },
267            ExpectedToken::Identifier => Kind::Str("identifier"),
268            ExpectedToken::LhsExpression => Kind::Str("LHS expression (identifier component_or_swizzle_specifier?, (`lhs_expression`) component_or_swizzle_specifier?, &`lhs_expression`, *`lhs_expression`)"),
269            ExpectedToken::PrimaryExpression => Kind::Str("expression"),
270            ExpectedToken::Assignment => Kind::Str("assignment or increment/decrement"),
271            ExpectedToken::SwitchItem => Kind::Str(concat!(
272                "switch item (`case` or `default`) or a closing curly bracket ",
273                "to signify the end of the switch statement (`}`)"
274            )),
275            ExpectedToken::WorkgroupSizeSeparator => {
276                Kind::Str("workgroup size separator (`,`) or a closing parenthesis")
277            }
278            ExpectedToken::GlobalItem => Kind::Str(concat!(
279                "global item (`struct`, `const`, `var`, `alias`, ",
280                "`fn`, `diagnostic`, `enable`, `requires`, `;`) ",
281                "or the end of the file"
282            )),
283            ExpectedToken::Variable => Kind::Str("variable access"),
284            ExpectedToken::Function => Kind::Str("function name"),
285            ExpectedToken::AfterIdentListArg => {
286                Kind::Str("next argument, trailing comma, or end of list (`,` or `;`)")
287            }
288            ExpectedToken::AfterIdentListComma => {
289                Kind::Str("next argument or end of list (`;`)")
290            }
291            ExpectedToken::DiagnosticAttribute => {
292                Kind::Str("the `diagnostic` attribute identifier")
293            }
294            ExpectedToken::Statement => Kind::Str("statement"),
295            ExpectedToken::ForInit => Kind::Str("for loop initializer statement (`var`/`let`/`const` declaration, assignment, `i++`/`i--` statement, function call)"),
296            ExpectedToken::ForUpdate => Kind::Str("for loop update statement (assignment, `i++`/`i--` statement, function call)"),
297        };
298
299        match kind {
300            Kind::Str(s) => {
301                f.write_str(s);
302            }
303            Kind::FormatChar(a, b, c) => {
304                f.write_str(a);
305                f.write_char(b);
306                f.write_str(c);
307            }
308            Kind::FormatStr(strings) => {
309                for s in strings {
310                    f.write_str(s);
311                }
312            }
313        }
314
315        Ok(())
316    }
317}
318
319#[derive(Clone, Copy, Debug, Error, PartialEq)]
320pub enum NumberError {
321    #[error("invalid numeric literal format")]
322    Invalid,
323    #[error("numeric literal not representable by target type")]
324    NotRepresentable,
325}
326
327#[derive(Copy, Clone, Debug, PartialEq)]
328pub enum InvalidAssignmentType {
329    Other,
330    Swizzle,
331    ImmutableBinding(Span),
332}
333
334#[derive(Clone, Debug)]
335pub(crate) enum Error<'a> {
336    Unexpected(Span, ExpectedToken<'a>),
337    UnexpectedComponents(Span),
338    UnexpectedOperationInConstContext(Span),
339    BadNumber(Span, NumberError),
340    BadMatrixScalarKind(Span, Scalar),
341    BadAccessor(Span),
342    BadTexture(Span),
343    BadTypeCast {
344        span: Span,
345        from_type: String,
346        to_type: String,
347    },
348    NotStorageTexture(Span),
349    BadTextureSampleType {
350        span: Span,
351        scalar: Scalar,
352    },
353    BadIncrDecrReferenceType(Span),
354    InvalidResolve(ResolveError),
355    /// A break if appeared outside of a continuing block
356    InvalidBreakIf(Span),
357    InvalidGatherComponent(Span),
358    InvalidConstructorComponentType(Span, i32),
359    InvalidIdentifierUnderscore(Span),
360    ReservedIdentifierPrefix(Span),
361    UnknownAddressSpace(Span),
362    InvalidLocalVariableAddressSpace(Span),
363    UnknownRayFlag(Span),
364    RepeatedAttribute(Span),
365    UnknownAttribute(Span),
366    UnknownBuiltin(Span),
367    UnknownAccess(Span),
368    UnknownIdent(Span, &'a str),
369    UnknownScalarType(Span),
370    UnknownStorageFormat(Span),
371    UnknownConservativeDepth(Span),
372    UnknownEnableExtension(Span, &'a str),
373    UnknownLanguageExtension(Span, &'a str),
374    UnknownDiagnosticRuleName(Span),
375    SizeAttributeTooLow(Span, u32),
376    SizeAttributeRequiresFixedFootprint(Span),
377    AlignAttributeTooLow(Span, Alignment),
378    NonPowerOfTwoAlignAttribute(Span),
379    InconsistentBinding(Span),
380    TypeNotConstructible(Span),
381    TypeNotInferable(Span),
382    InitializationTypeMismatch {
383        name: Span,
384        expected: String,
385        got: String,
386    },
387    DeclMissingTypeAndInit(Span),
388    MissingAttribute(&'static str, Span),
389    InvalidUnaryOperandType {
390        span: Span,
391        op: UnaryOperator,
392        operand_type: String,
393    },
394
395    InvalidAddrOfOperand(Span),
396    InvalidAtomicPointer(Span),
397    InvalidAtomicOperandType(Span),
398    InvalidAtomicAccess(Span),
399    InvalidRayQueryPointer(Span),
400    NotPointer(Span),
401    NotReference(&'static str, Span),
402    InvalidAssignment {
403        span: Span,
404        ty: InvalidAssignmentType,
405    },
406    ReservedKeyword(Span),
407    /// Redefinition of an identifier (used for both module-scope and local redefinitions).
408    Redefinition {
409        /// Span of the identifier in the previous definition.
410        previous: Span,
411
412        /// Span of the identifier in the new definition.
413        current: Span,
414    },
415    /// A declaration refers to itself directly.
416    RecursiveDeclaration {
417        /// The location of the name of the declaration.
418        ident: Span,
419
420        /// The point at which it is used.
421        usage: Span,
422    },
423    /// A declaration refers to itself indirectly, through one or more other
424    /// definitions.
425    CyclicDeclaration {
426        /// The location of the name of some declaration in the cycle.
427        ident: Span,
428
429        /// The edges of the cycle of references.
430        ///
431        /// Each `(decl, reference)` pair indicates that the declaration whose
432        /// name is `decl` has an identifier at `reference` whose definition is
433        /// the next declaration in the cycle. The last pair's `reference` is
434        /// the same identifier as `ident`, above.
435        path: Box<[(Span, Span)]>,
436    },
437    InvalidSwitchSelector {
438        span: Span,
439    },
440    InvalidSwitchCase {
441        span: Span,
442    },
443    SwitchCaseTypeMismatch {
444        span: Span,
445    },
446    CalledEntryPoint(Span),
447    CalledLocalDecl(Span),
448    WrongArgumentCount {
449        span: Span,
450        expected: Range<u32>,
451        found: u32,
452    },
453    /// No overload of this function accepts this many arguments.
454    TooManyArguments {
455        /// The name of the function being called.
456        function: String,
457
458        /// The function name in the call expression.
459        call_span: Span,
460
461        /// The first argument that is unacceptable.
462        arg_span: Span,
463
464        /// Maximum number of arguments accepted by any overload of
465        /// this function.
466        max_arguments: u32,
467    },
468    /// A value passed to a builtin function has a type that is not
469    /// accepted by any overload of the function.
470    WrongArgumentType {
471        /// The name of the function being called.
472        function: String,
473
474        /// The function name in the call expression.
475        call_span: Span,
476
477        /// The first argument whose type is unacceptable.
478        arg_span: Span,
479
480        /// The index of the first argument whose type is unacceptable.
481        arg_index: u32,
482
483        /// That argument's actual type.
484        arg_ty: String,
485
486        /// The set of argument types that would have been accepted for
487        /// this argument, given the prior arguments.
488        allowed: Vec<String>,
489    },
490    /// A value passed to a builtin function has a type that is not
491    /// accepted, given the earlier arguments' types.
492    InconsistentArgumentType {
493        /// The name of the function being called.
494        function: String,
495
496        /// The function name in the call expression.
497        call_span: Span,
498
499        /// The first unacceptable argument.
500        arg_span: Span,
501
502        /// The index of the first unacceptable argument.
503        arg_index: u32,
504
505        /// The actual type of the first unacceptable argument.
506        arg_ty: String,
507
508        /// The prior argument whose type made the `arg_span` argument
509        /// unacceptable.
510        inconsistent_span: Span,
511
512        /// The index of the `inconsistent_span` argument.
513        inconsistent_index: u32,
514
515        /// The type of the `inconsistent_span` argument.
516        inconsistent_ty: String,
517
518        /// The types that would have been accepted instead of the
519        /// first unacceptable argument.
520        allowed: Vec<String>,
521    },
522    FunctionReturnsVoid(Span),
523    FunctionMustUseUnused(Span),
524    FunctionMustUseReturnsVoid(Span, Span),
525    FunctionMustUseOnNonFunction(Span),
526    InvalidWorkGroupUniformLoad(Span),
527    InvalidStringLiteral {
528        span: Span,
529        description: &'static str,
530    },
531    ExpectedStringLiteral {
532        span: Span,
533        description: &'static str,
534    },
535    Internal(&'static str),
536    ExpectedConstExprConcreteIntegerScalar(Span),
537    ExpectedNonNegative(Span),
538    ExpectedPositiveArrayLength(Span),
539    MissingWorkgroupSize(Span),
540    ConstantEvaluatorError(Box<ConstantEvaluatorError>, Span),
541    TypeMismatch(Box<TypeMismatchError>),
542    AutoConversion(Box<AutoConversionError>),
543    AutoConversionLeafScalar(Box<AutoConversionLeafScalarError>),
544    ConcretizationFailed(Box<ConcretizationFailedError>),
545    ExceededLimitForNestedBraces {
546        span: Span,
547        limit: u8,
548    },
549    PipelineConstantIDValue(Span),
550    NotBool(Span),
551    ConstAssertFailed(Span),
552    DirectiveAfterFirstGlobalDecl {
553        directive_span: Span,
554    },
555    EnableExtensionNotYetImplemented {
556        kind: UnimplementedEnableExtension,
557        span: Span,
558    },
559    EnableExtensionNotEnabled {
560        kind: EnableExtension,
561        span: Span,
562    },
563    EnableExtensionNotSupported {
564        kind: EnableExtension,
565        span: Span,
566    },
567    LanguageExtensionNotYetImplemented {
568        kind: UnimplementedLanguageExtension,
569        span: Span,
570    },
571    DiagnosticInvalidSeverity {
572        severity_control_name_span: Span,
573    },
574    DiagnosticDuplicateTriggeringRule(ConflictingDiagnosticRuleError),
575    DiagnosticAttributeNotYetImplementedAtParseSite {
576        site_name_plural: &'static str,
577        spans: Vec<Span>,
578    },
579    DiagnosticAttributeNotSupported {
580        on_what: DiagnosticAttributeNotSupportedPosition,
581        spans: Vec<Span>,
582    },
583    SelectUnexpectedArgumentType {
584        arg_span: Span,
585        arg_type: String,
586    },
587    SelectRejectAndAcceptHaveNoCommonType {
588        reject_span: Span,
589        reject_type: String,
590        accept_span: Span,
591        accept_type: String,
592    },
593    ExpectedGlobalVariable {
594        name_span: Span,
595    },
596    StructMemberTooLarge {
597        member_name_span: Span,
598    },
599    TypeTooLarge {
600        span: Span,
601    },
602    UnderspecifiedCooperativeMatrix,
603    InvalidCooperativeLoadType(Span),
604    UnsupportedCooperativeScalar(Span),
605    UnexpectedIdentForEnumerant(Span),
606    UnexpectedExprForEnumerant(Span),
607    UnusedArgsForTemplate(Vec<Span>),
608    UnexpectedTemplate(Span),
609    MissingTemplateArg {
610        span: Span,
611        description: &'static str,
612    },
613    UnexpectedExprForTypeExpression(Span),
614    MissingIncomingPayload(Span),
615    UnterminatedBlockComment(Span),
616    RayQueryWithInitializer(Span),
617}
618
619impl From<ConflictingDiagnosticRuleError> for Error<'_> {
620    fn from(value: ConflictingDiagnosticRuleError) -> Self {
621        Self::DiagnosticDuplicateTriggeringRule(value)
622    }
623}
624
625/// Used for diagnostic refinement in [`Error::DiagnosticAttributeNotSupported`].
626#[derive(Clone, Copy, Debug)]
627pub(crate) enum DiagnosticAttributeNotSupportedPosition {
628    SemicolonInModulePosition,
629    Other { display_plural: &'static str },
630}
631
632impl From<&'static str> for DiagnosticAttributeNotSupportedPosition {
633    fn from(display_plural: &'static str) -> Self {
634        Self::Other { display_plural }
635    }
636}
637
638/// A value's concrete type differs from the type required by its context.
639#[derive(Clone, Debug)]
640pub(crate) struct TypeMismatchError {
641    /// Where the required type comes from.
642    pub dest_span: Span,
643    pub dest_type: String,
644    /// The value whose type is wrong.
645    pub source_span: Span,
646    pub source_type: String,
647}
648
649#[derive(Clone, Debug)]
650pub(crate) struct AutoConversionError {
651    pub dest_span: Span,
652    pub dest_type: String,
653    pub source_span: Span,
654    pub source_type: String,
655}
656
657#[derive(Clone, Debug)]
658pub(crate) struct AutoConversionLeafScalarError {
659    pub dest_span: Span,
660    pub dest_scalar: String,
661    pub source_span: Span,
662    pub source_type: String,
663}
664
665#[derive(Clone, Debug)]
666pub(crate) struct ConcretizationFailedError {
667    pub expr_span: Span,
668    pub expr_type: String,
669    pub concretization_preferences: Vec<(String, ConstantEvaluatorError)>,
670}
671
672impl<'a> Error<'a> {
673    #[cold]
674    #[inline(never)]
675    // This function is not recursive, so it can exceed our conservatively-set
676    // `stack-frame-limit` without causing problems.
677    #[allow(clippy::large_stack_frames)]
678    pub(crate) fn as_parse_error(&self, source: &'a str) -> ParseError {
679        // This function normally produces a lot of binary bloat, so has been structured in a
680        // non-idiomatic way to minimize its size.
681        //
682        // Errors where the output has a static message and a single, static label are handled in
683        // the block starting with UnexpectedComponents.
684        //
685        // Errors where the output has a single static label, and a message in the form
686        // "..{&source[span]}.." are handled in the block starting with BadAccessor.
687        //
688        // Other errors with a single static label, and a non-static message are handled in the
689        // block starting with BadMatrixScalarKind.
690        //
691        // Other errors are handled on their own.
692        //
693        // If you're adding a new error type, and you're not sure where to put it, you can put it
694        // on its own; there'll only be a small difference in size from one variant :)
695
696        match self {
697            Error::Unexpected(unexpected_span, expected) => {
698                ParseError {
699                    message: format!(
700                        "expected {expected}, found {:?}",
701                        &source[*unexpected_span]
702                    ).into(),
703                    labels: vec![(*unexpected_span, format!("expected {expected}").into())],
704                    notes: vec![],
705                }
706            }
707            // Variants where message and label are static strings
708            Error::UnexpectedComponents(span)
709                | Error::UnexpectedOperationInConstContext(span)
710                | Error::NotStorageTexture(span)
711                | Error::BadIncrDecrReferenceType(span)
712                | Error::InvalidBreakIf(span)
713                | Error::NonPowerOfTwoAlignAttribute(span)
714                | Error::InconsistentBinding(span)
715                | Error::TypeNotInferable(span)
716                | Error::InvalidAddrOfOperand(span)
717                | Error::InvalidAtomicPointer(span)
718                | Error::InvalidAtomicOperandType(span)
719                | Error::InvalidAtomicAccess(span)
720                | Error::InvalidRayQueryPointer(span)
721                | Error::NotPointer(span)
722                | Error::InvalidSwitchSelector { span }
723                | Error::InvalidSwitchCase { span }
724                | Error::SwitchCaseTypeMismatch { span }
725                | Error::CalledEntryPoint(span)
726                | Error::CalledLocalDecl(span)
727                | Error::ExpectedConstExprConcreteIntegerScalar(span)
728                | Error::ExpectedNonNegative(span)
729                | Error::ExpectedPositiveArrayLength(span)
730                | Error::MissingWorkgroupSize(span)
731                | Error::PipelineConstantIDValue(span)
732                | Error::NotBool(span)
733                | Error::ConstAssertFailed(span)
734                | Error::ExpectedGlobalVariable { name_span: span }
735                | Error::UnexpectedExprForEnumerant(span)
736                | Error::UnexpectedTemplate(span)
737                | Error::UnexpectedExprForTypeExpression(span)
738                | Error::MissingIncomingPayload(span) => {
739                let (message, label) = match self {
740                    Error::UnexpectedComponents(_) => (
741                        "unexpected components",
742                        "unexpected components"
743                    ),
744                    Error::UnexpectedOperationInConstContext(_) => (
745                        "this operation is not supported in a const context",
746                        "operation not supported here"
747                    ),
748                    Error::NotStorageTexture(_) => (
749                        "textureStore can only be applied to storage textures",
750                        "not a storage texture"
751                    ),
752                    Error::BadIncrDecrReferenceType(_) => (
753                        concat!(
754                            "increment/decrement operation requires ",
755                            "reference type to be one of i32 or u32"
756                        ),
757                        "must be a reference type of i32 or u32"
758                    ),
759                    Error::InvalidBreakIf(_) => (
760                        "A break if is only allowed in a continuing block",
761                        "not in a continuing block"
762                    ),
763                    Error::NonPowerOfTwoAlignAttribute(_) => (
764                        "struct member alignment must be a power of 2",
765                        "must be a power of 2"
766                    ),
767                    Error::InconsistentBinding(_) => (
768                        "input/output binding is not consistent",
769                        "input/output binding is not consistent"
770                    ),
771                    Error::TypeNotInferable(_) => (
772                        "type can't be inferred",
773                        "type can't be inferred"
774                    ),
775                    Error::InvalidAddrOfOperand(_) => (
776                        "cannot take the address of a vector component",
777                        "invalid operand for address-of"
778                    ),
779                    Error::InvalidAtomicPointer(_) => (
780                        "atomic operation is done on a pointer to a non-atomic",
781                        "atomic pointer is invalid"
782                    ),
783                    Error::InvalidAtomicOperandType(_) => (
784                        "atomic operand type is inconsistent with the operation",
785                        "atomic operand type is invalid"
786                    ),
787                    Error::InvalidAtomicAccess(_) => (
788                        "direct access to atomic variable is not allowed",
789                        "atomic variables cannot be accessed directly; use atomic built-in functions",
790                    ),
791                    Error::InvalidRayQueryPointer(_) => (
792                        "ray query operation is done on a pointer to a non-ray-query",
793                        "ray query pointer is invalid"
794                    ),
795                    Error::NotPointer(_) => (
796                        "the operand of the `*` operator must be a pointer",
797                        "expression is not a pointer"
798                    ),
799                    Error::InvalidSwitchSelector { .. } => (
800                        "invalid `switch` selector",
801                        "`switch` selector must be a scalar integer"
802                    ),
803                    Error::InvalidSwitchCase { .. } => (
804                        "invalid `switch` case selector value",
805                        "`switch` case selector must be a scalar integer const expression"
806                    ),
807                    Error::SwitchCaseTypeMismatch { .. } => (
808                        "invalid `switch` case selector value",
809                        "`switch` case selector must have the same type as the `switch` selector expression"
810                    ),
811                    Error::CalledEntryPoint(_) => (
812                        "entry point cannot be called",
813                        "entry point cannot be called"
814                    ),
815                    Error::CalledLocalDecl(_) => (
816                        "local declaration cannot be called",
817                        "local declaration cannot be called"
818                    ),
819                    Error::ExpectedConstExprConcreteIntegerScalar(_) => (
820                        concat!(
821                            "must be a const-expression that ",
822                            "resolves to a concrete integer scalar (`u32` or `i32`)"
823                        ),
824                        "must resolve to `u32` or `i32`"
825                    ),
826                    Error::ExpectedNonNegative(_) => (
827                        "must be non-negative (>= 0)",
828                        "must be non-negative"
829                    ),
830                    Error::ExpectedPositiveArrayLength(_) => (
831                        "array element count must be positive (> 0)",
832                        "must be positive"
833                    ),
834                    Error::MissingWorkgroupSize(_) => (
835                        "workgroup size is missing on compute shader entry point",
836                        "must be paired with a `@workgroup_size` attribute"
837                    ),
838                    Error::PipelineConstantIDValue(_) => (
839                        "pipeline constant ID must be between 0 and 65535 inclusive",
840                        "must be between 0 and 65535 inclusive"
841                    ),
842                    Error::NotBool(_) => (
843                        "must be a const-expression that resolves to a `bool`",
844                        "must resolve to `bool`"
845                    ),
846                    Error::ConstAssertFailed(_) => (
847                        "`const_assert` failure",
848                        "evaluates to `false`"
849                    ),
850                    Error::ExpectedGlobalVariable { .. } => (
851                        "expected global variable",
852                        "variable used here"
853                    ),
854                    Error::UnexpectedExprForEnumerant(_) => (
855                        "unexpected expression",
856                        "needs to be an identifier resolving to a predeclared enumerant"
857                    ),
858                    Error::UnexpectedTemplate(_) => (
859                        "unexpected template",
860                        "expected identifier"
861                    ),
862                    Error::UnexpectedExprForTypeExpression(_) => (
863                        "unexpected expression",
864                        "needs to be an identifier resolving to a type declaration (alias or struct) or predeclared type(-generator)"
865                    ),
866                    Error::MissingIncomingPayload(_) => (
867                        "incoming payload is missing on a `closest_hit`, `any_hit` or `miss` shader entry point",
868                        "must be paired with a `@incoming_payload` attribute"
869                    ),
870                    _ => unreachable!()
871                };
872
873                ParseError {
874                    labels: vec![(*span, label.into())],
875                    message: message.into(),
876                    notes: vec![],
877                }
878            },
879            // Variants with a static label, and a message in the form "..{&source[span]}.."
880            Error::BadAccessor(span)
881                | Error::BadTexture(span)
882                | Error::InvalidGatherComponent(span)
883                | Error::ReservedIdentifierPrefix(span)
884                | Error::UnknownAddressSpace(span)
885                | Error::InvalidLocalVariableAddressSpace(span)
886                | Error::UnknownRayFlag(span)
887                | Error::RepeatedAttribute(span)
888                | Error::UnknownAttribute(span)
889                | Error::UnknownBuiltin(span)
890                | Error::UnknownAccess(span)
891                | Error::UnknownStorageFormat(span)
892                | Error::UnknownConservativeDepth(span)
893                | Error::TypeNotConstructible(span)
894                | Error::DeclMissingTypeAndInit(span)
895                | Error::UnexpectedIdentForEnumerant(span) => {
896                let (message_a, message_b, label) = match self {
897                    Error::BadAccessor(_) => (
898                        "invalid field accessor `",
899                        "`",
900                        "invalid accessor"
901                    ),
902                    Error::BadTexture(_) => (
903                        "expected an image, but found `",
904                        "` which is not an image",
905                        "not an image"
906                    ),
907                    Error::InvalidGatherComponent(_) => (
908                        "textureGather component `",
909                        "` doesn't exist, must be 0, 1, 2, or 3",
910                        "invalid component"
911                    ),
912                    Error::ReservedIdentifierPrefix(_) => (
913                        "Identifier starts with a reserved prefix: `",
914                        "`",
915                        "invalid identifier"
916                    ),
917                    Error::UnknownAddressSpace(_) => (
918                        "unknown address space: `",
919                        "`",
920                        "unknown address space"
921                    ),
922                    Error::InvalidLocalVariableAddressSpace(_) => (
923                        "invalid address space for local variable: `",
924                        "`",
925                        "local variables can only use 'function' address space"
926                    ),
927                    Error::UnknownRayFlag(_) => (
928                        "unknown ray flag: `",
929                        "`",
930                        "unknown ray flag"
931                    ),
932                    Error::RepeatedAttribute(_) => (
933                        "repeated attribute: `",
934                        "`",
935                        "repeated attribute"
936                    ),
937                    Error::UnknownAttribute(_) => (
938                        "unknown attribute: `",
939                        "`",
940                        "unknown attribute"
941                    ),
942                    Error::UnknownBuiltin(_) => (
943                        "unknown builtin: `",
944                        "`",
945                        "unknown builtin"
946                    ),
947                    Error::UnknownAccess(_) => (
948                        "unknown access: `",
949                        "`",
950                        "unknown access"
951                    ),
952                    Error::UnknownStorageFormat(_) => (
953                        "unknown storage format: `",
954                        "`",
955                        "unknown storage format"
956                    ),
957                    Error::UnknownConservativeDepth(_) => (
958                        "unknown conservative depth: `",
959                        "`",
960                        "unknown conservative depth"
961                    ),
962                    Error::TypeNotConstructible(_) => (
963                        "type `",
964                        "` is not constructible",
965                        "type is not constructible"
966                    ),
967                    Error::DeclMissingTypeAndInit(_) => (
968                        "declaration of `",
969                        "` needs a type specifier or initializer",
970                        "needs a type specifier or initializer"
971                    ),
972                    Error::UnexpectedIdentForEnumerant(_) => (
973                        "identifier `",
974                        "` resolves to a declaration",
975                        "needs to resolve to a predeclared enumerant"
976                    ),
977                    _ => unreachable!()
978                };
979
980                ParseError {
981                    message: [message_a, &source[*span], message_b].concat().into(),
982                    labels: vec![(*span, label.into())],
983                    notes: vec![]
984                }
985            },
986            // Other variants with a single static label and no notes
987            Error::BadMatrixScalarKind(span, _)
988                | Error::UnknownIdent(span, _)
989                | Error::BadTextureSampleType { span, .. }
990                | Error::InvalidConstructorComponentType(span, _)
991                | Error::NotReference(_, span)
992                | Error::WrongArgumentCount { span, .. }
993                | Error::ConstantEvaluatorError(_, span)
994                | Error::EnableExtensionNotSupported { span, .. }
995                | Error::InvalidUnaryOperandType { span, .. }
996                | Error::MissingTemplateArg { span, .. } => {
997                let (message, label) = match self {
998                    Error::BadMatrixScalarKind(_, scalar) => (
999                        format!(
1000                            "matrix scalar type must be floating-point, but found `{}`",
1001                            scalar.to_wgsl_for_diagnostics()
1002                        ),
1003                        "must be floating-point (e.g. `f32`)"
1004                    ),
1005                    Error::UnknownIdent(_, ident) => (
1006                        format!("no definition in scope for identifier: `{ident}`"),
1007                        "unknown identifier"
1008                    ),
1009                    Error::BadTextureSampleType { scalar, .. } => (
1010                        format!(
1011                            "texture sample type must be one of f32, i32 or u32, but found {}",
1012                            scalar.to_wgsl_for_diagnostics()
1013                        ),
1014                        "must be one of f32, i32 or u32"
1015                    ),
1016                    Error::InvalidConstructorComponentType(_, component) => (
1017                        format!("invalid type for constructor component at index [{component}]"),
1018                        "invalid component type"
1019                    ),
1020                    Error::NotReference(what, _) => (
1021                        format!("{what} must be a reference"),
1022                        "expression is not a reference"
1023                    ),
1024                    Error::WrongArgumentCount {
1025                        ref expected,
1026                        found,
1027                        ..
1028                    } => (
1029                        format!(
1030                            "wrong number of arguments: expected {}, found {}",
1031                            if expected.len() < 2 {
1032                                format!("{}", expected.start)
1033                            } else {
1034                                format!("{}..{}", expected.start, expected.end)
1035                            },
1036                            found
1037                        ),
1038                        "wrong number of arguments"
1039                    ),
1040                    Error::ConstantEvaluatorError(ref e, _) => (e.to_string(), "see msg"),
1041                    Error::EnableExtensionNotSupported { kind, .. } => (
1042                        format!(
1043                            "the `{}` extension is not supported in the current environment",
1044                            kind.to_ident()
1045                        ),
1046                        "unsupported enable-extension"
1047                    ),
1048                    Error::MissingTemplateArg { description, .. } => (
1049                        format!(
1050                            "`{}` needs a template argument specified: {description}",
1051                            &source[*span]
1052                        ),
1053                        "is missing a template argument"
1054                    ),
1055                    Error::InvalidUnaryOperandType { op, operand_type, .. } => {
1056                        let operator = match op {
1057                            UnaryOperator::Negate => "-",
1058                            UnaryOperator::LogicalNot => "!",
1059                            UnaryOperator::BitwiseNot => "~",
1060                        };
1061                        (
1062                            format!(
1063                                "unary operator `{operator}` is not defined for operand type `{}`",
1064                                operand_type
1065                            ),
1066                            "invalid operand type for this operator"
1067                        )
1068                    },
1069                    _ => unreachable!()
1070                };
1071
1072                ParseError {
1073                    message: message.into(),
1074                    labels: vec![(*span, label.into())],
1075                    notes: vec![]
1076                }
1077            },
1078            Error::BadNumber(bad_span, ref err) => ParseError {
1079                message: format!("{}: `{}`", err, &source[*bad_span]).into(),
1080                labels: vec![(*bad_span, err.to_string().into())],
1081                notes: vec![],
1082            },
1083            Error::UnknownScalarType(bad_span) => ParseError {
1084                message: format!("unknown scalar type: `{}`", &source[*bad_span]).into(),
1085                labels: vec![(*bad_span, "unknown scalar type".into())],
1086                notes: vec!["Valid scalar types are f32, f64, i32, u32, bool".into()],
1087            },
1088            Error::BadTypeCast {
1089                span,
1090                ref from_type,
1091                ref to_type,
1092            } => {
1093                let msg = format!("cannot cast a {from_type} to a {to_type}");
1094                ParseError {
1095                    message: msg.clone().into(),
1096                    labels: vec![(*span, msg.into())],
1097                    notes: vec![],
1098                }
1099            }
1100            Error::InvalidResolve(ref resolve_error) => ParseError {
1101                message: resolve_error.to_string().into(),
1102                labels: vec![],
1103                notes: vec![],
1104            },
1105            Error::InvalidIdentifierUnderscore(bad_span) => ParseError {
1106                labels: vec![(*bad_span, "invalid identifier".into())],
1107                notes: vec![
1108                    "Use phony assignment instead (`_ =` notice the absence of `let` or `var`)"
1109                        .into(),
1110                ],
1111                message: "Identifier can't be `_`".into(),
1112            },
1113            Error::UnknownEnableExtension(span, word) => ParseError {
1114                message: format!("unknown enable-extension `{word}`").into(),
1115                labels: vec![(*span, "".into())],
1116                notes: vec![
1117                    "See available extensions at <https://www.w3.org/TR/WGSL/#enable-extension>."
1118                        .into(),
1119                ],
1120            },
1121            Error::UnknownLanguageExtension(span, name) => ParseError {
1122                message: format!("unknown language extension `{name}`").into(),
1123                labels: vec![(*span, "".into())],
1124                notes: vec![concat!(
1125                    "See available extensions at ",
1126                    "<https://www.w3.org/TR/WGSL/#language-extensions-sec>."
1127                )
1128                .into()],
1129            },
1130            Error::UnknownDiagnosticRuleName(span) => ParseError {
1131                message: format!("unknown `diagnostic(…)` rule name `{}`", &source[*span]).into(),
1132                labels: vec![(*span, "not a valid diagnostic rule name".into())],
1133                notes: vec![concat!(
1134                    "See available trigger rules at ",
1135                    "<https://www.w3.org/TR/WGSL/#filterable-triggering-rules>."
1136                )
1137                .into()],
1138            },
1139            Error::SizeAttributeTooLow(bad_span, min_size) => ParseError {
1140                message: format!("struct member size must be at least {min_size}").into(),
1141                labels: vec![(*bad_span, format!("must be at least {min_size}").into())],
1142                notes: vec![],
1143            },
1144            Error::SizeAttributeRequiresFixedFootprint(bad_span) => ParseError {
1145                labels: vec![(*bad_span, "type does not have creation-fixed footprint".into())],
1146                message: "@size attribute requires a type with creation-fixed footprint".into(),
1147                notes: vec![],
1148            },
1149            Error::AlignAttributeTooLow(bad_span, min_align) => ParseError {
1150                message: format!("struct member alignment must be at least {min_align}").into(),
1151                labels: vec![(*bad_span, format!("must be at least {min_align}").into())],
1152                notes: vec![],
1153            },
1154            Error::InitializationTypeMismatch {
1155                name,
1156                ref expected,
1157                ref got,
1158            } => {
1159                let name_str = &source[*name];
1160                ParseError {
1161                    message: format!(
1162                        "the type of `{name_str}` is expected to be `{expected}`, but got `{got}`"
1163                    )
1164                        .into(),
1165                    labels: vec![(*name, format!("definition of `{name_str}`").into())],
1166                    notes: vec![],
1167                }
1168            },
1169            Error::MissingAttribute(name, name_span) => {
1170                let variable = &source[*name_span];
1171                ParseError {
1172                    message: format!(
1173                        "variable `{variable}` needs a '{name}' attribute",
1174                    )
1175                        .into(),
1176                    labels: vec![(
1177                        *name_span,
1178                        format!("definition of `{variable}`").into(),
1179                    )],
1180                    notes: vec![],
1181                }
1182            },
1183            Error::InvalidAssignment { span, ty } => {
1184                let (notes, extra_label) = match ty {
1185                    InvalidAssignmentType::Swizzle => (
1186                        vec![
1187                            "WGSL does not support assignments to swizzles".into(),
1188                            "consider assigning each component individually".into(),
1189                        ],
1190                        None,
1191                    ),
1192                    InvalidAssignmentType::ImmutableBinding(binding_span) => (
1193                        vec![format!(
1194                            "consider declaring `{}` with `var` instead of `let`",
1195                            &source[*binding_span]
1196                        ).into()],
1197                        Some((*binding_span, "this is an immutable binding".into())),
1198                    ),
1199                    InvalidAssignmentType::Other => (vec![], None),
1200                };
1201
1202                let label = (*span, "cannot assign to this expression".into());
1203
1204                ParseError {
1205                    labels: if let Some(extra_label) = extra_label {
1206                        vec![label, extra_label]
1207                    } else {
1208                        vec![label]
1209                    },
1210                    message: "invalid left-hand side of assignment".into(),
1211                    notes,
1212                }
1213            }
1214            Error::ReservedKeyword(name_span) => {
1215                let name = &source[*name_span];
1216                ParseError {
1217                    message: format!("name `{name}` is a reserved keyword").into(),
1218                    labels: vec![(
1219                        *name_span,
1220                        format!("definition of `{name}`").into(),
1221                    )],
1222                    notes: vec![],
1223                }
1224            },
1225            Error::Redefinition { previous, current } => {
1226                let message = format!("redefinition of `{}`", &source[*current]);
1227                ParseError {
1228                    message: message.clone().into(),
1229                    labels: vec![
1230                        (*current, message.into()),
1231                        (
1232                            *previous,
1233                            format!("previous definition of `{}`", &source[*previous]).into(),
1234                        ),
1235                    ],
1236                    notes: vec![],
1237                }
1238            },
1239            Error::RecursiveDeclaration { ident, usage } => ParseError {
1240                message: format!("declaration of `{}` is recursive", &source[*ident]).into(),
1241                labels: vec![(*ident, "".into()), (*usage, "uses itself here".into())],
1242                notes: vec![],
1243            },
1244            Error::CyclicDeclaration { ident, ref path } => ParseError {
1245                message: format!("declaration of `{}` is cyclic", &source[*ident]).into(),
1246                labels: path
1247                    .iter()
1248                    .enumerate()
1249                    .flat_map(|(i, &(ident, usage))| {
1250                        [
1251                            (ident, "".into()),
1252                            (
1253                                usage,
1254                                if i == path.len() - 1 {
1255                                    "ending the cycle".into()
1256                                } else {
1257                                    format!("uses `{}`", &source[ident]).into()
1258                                },
1259                            ),
1260                        ]
1261                    })
1262                    .collect(),
1263                notes: vec![],
1264            },
1265            Error::TooManyArguments {
1266                ref function,
1267                call_span,
1268                arg_span,
1269                max_arguments,
1270            } => ParseError {
1271                message: format!("too many arguments passed to `{function}`").into(),
1272                labels: vec![
1273                    (*call_span, "".into()),
1274                    (*arg_span, format!("unexpected argument #{}", max_arguments + 1).into())
1275                ],
1276                notes: vec![
1277                    format!("The `{function}` function accepts at most {max_arguments} argument(s)").into()
1278                ],
1279            },
1280            Error::WrongArgumentType {
1281                ref function,
1282                call_span,
1283                arg_span,
1284                arg_index,
1285                ref arg_ty,
1286                ref allowed,
1287            } => {
1288                let message = format!(
1289                    "wrong type passed as argument #{} to `{function}`",
1290                    arg_index + 1,
1291                ).into();
1292                let labels = vec![
1293                    (*call_span, "".into()),
1294                    (*arg_span, format!("argument #{} has type `{arg_ty}`", arg_index + 1).into())
1295                ];
1296
1297                let mut notes = vec![];
1298                notes.push(format!("`{function}` accepts the following types for argument #{}:", arg_index + 1).into());
1299                notes.extend(allowed.iter().map(|ty| format!("allowed type: {ty}").into()));
1300
1301                ParseError { message, labels, notes }
1302            },
1303            Error::InconsistentArgumentType {
1304                ref function,
1305                call_span,
1306                arg_span,
1307                arg_index,
1308                ref arg_ty,
1309                inconsistent_span,
1310                inconsistent_index,
1311                ref inconsistent_ty,
1312                ref allowed
1313            } => {
1314                let message = format!(
1315                    "inconsistent type passed as argument #{} to `{function}`",
1316                    arg_index + 1,
1317                ).into();
1318                let labels = vec![
1319                    (*call_span, "".into()),
1320                    (*arg_span, format!("argument #{} has type {arg_ty}", arg_index + 1).into()),
1321                    (*inconsistent_span, format!(
1322                        "this argument has type {inconsistent_ty}, which constrains subsequent arguments"
1323                    ).into()),
1324                ];
1325                let mut notes = vec![
1326                    format!("Because argument #{} has type {inconsistent_ty}, only the following types", inconsistent_index + 1).into(),
1327                    format!("(or types that automatically convert to them) are accepted for argument #{}:", arg_index + 1).into(),
1328                ];
1329                notes.extend(allowed.iter().map(|ty| format!("allowed type: {ty}").into()));
1330
1331                ParseError { message, labels, notes }
1332            }
1333            Error::FunctionReturnsVoid(span) => ParseError {
1334                labels: vec![(*span, "".into())],
1335                notes: vec![
1336                    "perhaps you meant to call the function in a separate statement?".into(),
1337                ],
1338                message: "function does not return any value".into(),
1339            },
1340            Error::FunctionMustUseUnused(call) => ParseError {
1341                labels: vec![(*call, "".into())],
1342                notes: vec![
1343                    format!(
1344                        "function '{}' is declared with `@must_use` attribute",
1345                        &source[*call],
1346                    ).into(),
1347                    "use a phony assignment or declare a value using the function call as the initializer".into(),
1348                ],
1349                message: "unused return value from function annotated with @must_use".into(),
1350            },
1351            Error::FunctionMustUseReturnsVoid(attr, signature) => ParseError {
1352                labels: vec![
1353                    (*attr, "".into()),
1354                    (*signature, "".into()),
1355                ],
1356                notes: vec![
1357                    "declare a return type or remove the attribute".into(),
1358                ],
1359                message: "function annotated with @must_use but does not return any value".into(),
1360            },
1361            Error::FunctionMustUseOnNonFunction(attr) => ParseError {
1362                labels: vec![(*attr, "".into())],
1363                notes: vec![
1364                    "place `@must_use` on a function declaration with a return type".into(),
1365                ],
1366                message: "attribute `@must_use` is only valid on function declarations".into(),
1367            },
1368            Error::InvalidWorkGroupUniformLoad(span) => ParseError {
1369                labels: vec![(*span, "".into())],
1370                notes: vec!["passed type must be a workgroup pointer".into()],
1371                message: "incorrect type passed to workgroupUniformLoad".into(),
1372            },
1373            Error::InvalidStringLiteral { span, description } => ParseError {
1374                message: description.to_string().into(),
1375                labels: vec![(*span, "invalid string literal".into())],
1376                notes: vec![],
1377            },
1378            Error::ExpectedStringLiteral { span, description } => ParseError {
1379                message: description.to_string().into(),
1380                labels: vec![(*span, "expected a string literal".into())],
1381                notes: vec![],
1382            },
1383            Error::Internal(message) => ParseError {
1384                notes: vec![(*message).into()],
1385                message: "internal WGSL front end error".into(),
1386                labels: vec![],
1387            },
1388            Error::TypeMismatch(ref error) => {
1389                let TypeMismatchError {
1390                    dest_span,
1391                    ref dest_type,
1392                    source_span,
1393                    ref source_type,
1394                } = **error;
1395                let mut labels = vec![(
1396                    source_span,
1397                    format!("this expression has type `{source_type}`").into(),
1398                )];
1399                if dest_span != source_span {
1400                    labels.push((
1401                        dest_span,
1402                        format!("a value of type `{dest_type}` is required here").into(),
1403                    ));
1404                }
1405                ParseError {
1406                    message: format!("expected `{dest_type}`, found `{source_type}`").into(),
1407                    labels,
1408                    notes: vec![],
1409                }
1410            }
1411            Error::AutoConversion(ref error) => {
1412                // destructuring ensures all fields are handled
1413                let AutoConversionError {
1414                    dest_span,
1415                    ref dest_type,
1416                    source_span,
1417                    ref source_type,
1418                } = **error;
1419                ParseError {
1420                    message: format!(
1421                        "automatic conversions cannot convert `{source_type}` to `{dest_type}`"
1422                    ).into(),
1423                    labels: vec![
1424                        (
1425                            dest_span,
1426                            format!("a value of type {dest_type} is required here").into(),
1427                        ),
1428                        (
1429                            source_span,
1430                            format!("this expression has type {source_type}").into(),
1431                        ),
1432                    ],
1433                    notes: vec![],
1434                }
1435            }
1436            Error::AutoConversionLeafScalar(ref error) => {
1437                let AutoConversionLeafScalarError {
1438                    dest_span,
1439                    ref dest_scalar,
1440                    source_span,
1441                    ref source_type,
1442                } = **error;
1443                ParseError {
1444                    message: format!(
1445                        "automatic conversions cannot convert elements of `{source_type}` to `{dest_scalar}`"
1446                    ).into(),
1447                    labels: vec![
1448                        (
1449                            dest_span,
1450                            format!(
1451                                "a value with elements of type {dest_scalar} is required here"
1452                            )
1453                            .into(),
1454                        ),
1455                        (
1456                            source_span,
1457                            format!("this expression has type {source_type}").into(),
1458                        ),
1459                    ],
1460                    notes: vec![],
1461                }
1462            }
1463            Error::ConcretizationFailed(ref error) => {
1464                let ConcretizationFailedError {
1465                    expr_span,
1466                    ref expr_type,
1467                    ref concretization_preferences,
1468                } = **error;
1469                ParseError {
1470                    labels: vec![(
1471                        expr_span,
1472                        format!("this expression has type {expr_type}").into(),
1473                    )],
1474                    notes: concretization_preferences
1475                        .iter()
1476                        .map(|&(ref scalar, ref err)|
1477                            format!("the expression couldn't be converted to have {scalar} scalar type: {err}").into()
1478                        )
1479                        .collect(),
1480                    message: "failed to convert expression to a concrete type".into(),
1481                }
1482            }
1483            Error::ExceededLimitForNestedBraces { span, limit } => ParseError {
1484                labels: vec![(*span, "limit reached at this brace".into())],
1485                notes: vec![format!("nesting limit is currently set to {limit}").into()],
1486                message: "brace nesting limit reached".into(),
1487            },
1488            Error::DirectiveAfterFirstGlobalDecl { directive_span } => ParseError {
1489                labels: vec![(
1490                    *directive_span,
1491                    "written after first global declaration".into(),
1492                )],
1493                notes: vec![concat!(
1494                    "global directives are only allowed before global declarations; ",
1495                    "maybe hoist this closer to the top of the shader module?"
1496                )
1497                .into()],
1498                message: "expected global declaration, but found a global directive".into(),
1499            },
1500            Error::EnableExtensionNotYetImplemented { kind, span } => ParseError {
1501                message: format!(
1502                    "the `{}` enable-extension is not yet supported",
1503                    EnableExtension::Unimplemented(*kind).to_ident()
1504                ).into(),
1505                labels: vec![(
1506                    *span,
1507                    concat!(
1508                        "this enable-extension specifies standard functionality ",
1509                        "which is not yet implemented in Naga"
1510                    )
1511                    .into(),
1512                )],
1513                notes: vec![format!(
1514                    concat!(
1515                        "Let Naga maintainers know that you ran into this at ",
1516                        "<https://github.com/gfx-rs/wgpu/issues/{}>, ",
1517                        "so they can prioritize it!"
1518                    ),
1519                    kind.tracking_issue_num()
1520                ).into()],
1521            },
1522            Error::EnableExtensionNotEnabled { kind, span } => ParseError {
1523                message: format!("the `{}` enable extension is not enabled", kind.to_ident()).into(),
1524                labels: vec![(
1525                    *span,
1526                    format!(
1527                        concat!(
1528                            "the `{}` \"Enable Extension\" is needed for this functionality, ",
1529                            "but it is not currently enabled."
1530                        ),
1531                        kind.to_ident()
1532                    )
1533                    .into(),
1534                )],
1535                notes: if let EnableExtension::Unimplemented(kind) = kind {
1536                    vec![format!(
1537                        concat!(
1538                            "This \"Enable Extension\" is not yet implemented. ",
1539                            "Let Naga maintainers know that you ran into this at ",
1540                            "<https://github.com/gfx-rs/wgpu/issues/{}>, ",
1541                            "so they can prioritize it!"
1542                        ),
1543                        kind.tracking_issue_num()
1544                    ).into()]
1545                } else {
1546                    vec![
1547                        format!(
1548                            "You can enable this extension by adding `enable {};` at the top of the shader, before any other items.",
1549                            kind.to_ident()
1550                        ).into(),
1551                    ]
1552                },
1553            },
1554            Error::LanguageExtensionNotYetImplemented { kind, span } => ParseError {
1555                message: format!(
1556                    "the `{}` language extension is not yet supported",
1557                    LanguageExtension::Unimplemented(*kind).to_ident()
1558                ).into(),
1559                labels: vec![(*span, "".into())],
1560                notes: vec![format!(
1561                    concat!(
1562                        "Let Naga maintainers know that you ran into this at ",
1563                        "<https://github.com/gfx-rs/wgpu/issues/{}>, ",
1564                        "so they can prioritize it!"
1565                    ),
1566                    kind.tracking_issue_num()
1567                ).into()],
1568            },
1569            Error::DiagnosticInvalidSeverity {
1570                severity_control_name_span,
1571            } => ParseError {
1572                labels: vec![(
1573                    *severity_control_name_span,
1574                    "not a valid severity level".into(),
1575                )],
1576                notes: vec![concat!(
1577                    "See available severities at ",
1578                    "<https://www.w3.org/TR/WGSL/#diagnostic-severity>."
1579                )
1580                .into()],
1581                message: "invalid `diagnostic(…)` severity".into(),
1582            },
1583            Error::DiagnosticDuplicateTriggeringRule(ConflictingDiagnosticRuleError {
1584                triggering_rule_spans,
1585            }) => {
1586                let [first_span, second_span] = triggering_rule_spans;
1587                ParseError {
1588                    labels: vec![
1589                        (*first_span, "first rule".into()),
1590                        (*second_span, "second rule".into()),
1591                    ],
1592                    notes: vec![
1593                        concat!(
1594                            "Multiple `diagnostic(…)` rules with the same rule name ",
1595                            "conflict unless they are directives and the severity is the same.",
1596                        )
1597                        .into(),
1598                        "You should delete the rule you don't want.".into(),
1599                    ],
1600                    message: "found conflicting `diagnostic(…)` rule(s)".into(),
1601                }
1602            }
1603            Error::DiagnosticAttributeNotYetImplementedAtParseSite {
1604                site_name_plural,
1605                ref spans,
1606            } => ParseError {
1607                labels: {
1608                    let mut spans = spans.iter().cloned();
1609                    let first = spans
1610                        .next()
1611                        .map(|span| {
1612                            (
1613                                span,
1614                                format!("can't use this on {site_name_plural} (yet)").into(),
1615                            )
1616                        })
1617                        .expect("internal error: diag. attr. rejection on empty map");
1618                    core::iter::once(first)
1619                        .chain(spans.map(|span| (span, "".into())))
1620                        .collect()
1621                },
1622                notes: vec![concat!(
1623                    "Let Naga maintainers know that you ran into this at ",
1624                    "<https://github.com/gfx-rs/wgpu/issues/5320>, ",
1625                    "so they can prioritize it!"
1626                ).into()],
1627                message: "`@diagnostic(…)` attribute(s) not yet implemented".into(),
1628            },
1629            Error::DiagnosticAttributeNotSupported { on_what, ref spans } => {
1630                // In this case the user may have intended to create a global diagnostic filter directive,
1631                // so display a note to them suggesting the correct syntax.
1632                let intended_diagnostic_directive = match on_what {
1633                    DiagnosticAttributeNotSupportedPosition::SemicolonInModulePosition => true,
1634                    DiagnosticAttributeNotSupportedPosition::Other { .. } => false,
1635                };
1636                let on_what_plural = match on_what {
1637                    DiagnosticAttributeNotSupportedPosition::SemicolonInModulePosition => {
1638                        "semicolons"
1639                    }
1640                    DiagnosticAttributeNotSupportedPosition::Other { display_plural } => {
1641                        display_plural
1642                    }
1643                };
1644                ParseError {
1645                    message: format!(
1646                        "`@diagnostic(…)` attribute(s) on {on_what_plural} are not supported",
1647                    ).into(),
1648                    labels: spans
1649                        .iter()
1650                        .cloned()
1651                        .map(|span| (span, "".into()))
1652                        .collect(),
1653                    notes: vec![
1654                        concat!(
1655                            "`@diagnostic(…)` attributes are only permitted on `fn`s, ",
1656                            "some statements, and `switch`/`loop` bodies."
1657                        )
1658                        .into(),
1659                        {
1660                            if intended_diagnostic_directive {
1661                                concat!(
1662                                    "If you meant to declare a diagnostic filter that ",
1663                                    "applies to the entire module, move this line to ",
1664                                    "the top of the file and remove the `@` symbol."
1665                                )
1666                                .into()
1667                            } else {
1668                                concat!(
1669                                    "These attributes are well-formed, ",
1670                                    "you likely just need to move them."
1671                                )
1672                                .into()
1673                            }
1674                        },
1675                    ],
1676                }
1677            }
1678            Error::SelectUnexpectedArgumentType { arg_span, ref arg_type } => ParseError {
1679                labels: vec![(*arg_span, format!("this value of type {arg_type}").into())],
1680                notes: vec!["expected a scalar or a `vecN` of scalars".into()],
1681                message: "unexpected argument type for `select` call".into(),
1682            },
1683            Error::SelectRejectAndAcceptHaveNoCommonType {
1684                reject_span,
1685                ref reject_type,
1686                accept_span,
1687                ref accept_type,
1688            } => ParseError {
1689                labels: vec![
1690                    (*reject_span, format!("reject value of type {reject_type}").into()),
1691                    (*accept_span, format!("accept value of type {accept_type}").into()),
1692                ],
1693                message: "type mismatch for reject and accept values in `select` call".into(),
1694                notes: vec![],
1695            },
1696            Error::StructMemberTooLarge { member_name_span } => ParseError {
1697                labels: vec![(*member_name_span, "this member exceeds the maximum size".into())],
1698                notes: vec![format!(
1699                    "the maximum size is {} bytes",
1700                    crate::valid::MAX_TYPE_SIZE
1701                ).into()],
1702                message: "struct member is too large".into(),
1703            },
1704            Error::TypeTooLarge { span } => ParseError {
1705                labels: vec![(*span, "this type exceeds the maximum size".into())],
1706                notes: vec![format!(
1707                    "the maximum size is {} bytes",
1708                    crate::valid::MAX_TYPE_SIZE
1709                ).into()],
1710                message: "type is too large".into(),
1711            },
1712            Error::UnderspecifiedCooperativeMatrix => ParseError {
1713                labels: vec![],
1714                notes: vec!["must be F32".into()],
1715                message: "cooperative matrix constructor is underspecified".into(),
1716            },
1717            Error::InvalidCooperativeLoadType(span) => ParseError {
1718                labels: vec![(*span, "type needs the coop_mat<...>".into())],
1719                notes: vec!["must be a valid cooperative type".into()],
1720                message: "cooperative load should have a generic type for coop_mat".into(),
1721            },
1722            Error::UnsupportedCooperativeScalar(span) => ParseError {
1723                labels: vec![(*span, "type needs the scalar type specified".into())],
1724                notes: vec!["must be F32".into()],
1725                message: "cooperative scalar type is not supported".into(),
1726            },
1727            Error::UnusedArgsForTemplate(ref expr_spans) => ParseError {
1728                labels: expr_spans.iter().cloned().map(|span| -> (_, _){ (span, "unused".into()) }).collect(),
1729                message: "unused expressions for template".into(),
1730                notes: vec![],
1731            },
1732            Error::UnterminatedBlockComment(span) => ParseError {
1733                labels: vec![(
1734                    *span,
1735                    "must be closed with `*/`".into(),
1736                )],
1737                message: "unterminated block comment".into(),
1738                notes: vec![],
1739            },
1740            Error::RayQueryWithInitializer(span) => ParseError {
1741                message: "Ray query with initialize".into(),
1742                labels: vec![(
1743                    *span,
1744                    "variables with type `ray_query` are special and so cannot have initializers".into(),
1745                )],
1746                notes: vec![],
1747            },
1748        }
1749    }
1750}