naga/front/wgsl/
error.rs

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