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