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    // This function is not recursive, so it can exceed our conservatively-set
529    // `stack-frame-limit` without causing problems.
530    #[allow(clippy::large_stack_frames)]
531    pub(crate) fn as_parse_error(&self, source: &'a str) -> ParseError {
532        match *self {
533            Error::Unexpected(unexpected_span, expected) => {
534                let expected_str = match expected {
535                    ExpectedToken::Token(token) => match token {
536                        Token::Separator(c) => format!("`{c}`"),
537                        Token::Paren(c) => format!("`{c}`"),
538                        Token::Attribute => "@".to_string(),
539                        Token::Number(_) => "number".to_string(),
540                        Token::Word(s) => s.to_string(),
541                        Token::Operation(c) => format!("operation (`{c}`)"),
542                        Token::LogicalOperation(c) => format!("logical operation (`{c}`)"),
543                        Token::ShiftOperation(c) => format!("bitshift (`{c}{c}`)"),
544                        Token::AssignmentOperation(c) if c == '<' || c == '>' => {
545                            format!("bitshift (`{c}{c}=`)")
546                        }
547                        Token::AssignmentOperation(c) => format!("operation (`{c}=`)"),
548                        Token::IncrementOperation => "increment operation".to_string(),
549                        Token::DecrementOperation => "decrement operation".to_string(),
550                        Token::Arrow => "->".to_string(),
551                        Token::TemplateArgsStart => "template args start".to_string(),
552                        Token::TemplateArgsEnd => "template args end".to_string(),
553                        Token::Unknown(c) => format!("unknown (`{c}`)"),
554                        Token::Trivia => "trivia".to_string(),
555                        Token::DocComment(s) => format!("doc comment ('{s}')"),
556                        Token::ModuleDocComment(s) => format!("module doc comment ('{s}')"),
557                        Token::End => "end".to_string(),
558                        Token::UnterminatedBlockComment(s) => format!("unterminated doc comment ('{s}'")
559                    },
560                    ExpectedToken::Identifier => "identifier".to_string(),
561                    ExpectedToken::LhsExpression => "LHS expression (identifier component_or_swizzle_specifier?, (`lhs_expression`) component_or_swizzle_specifier?, &`lhs_expression`, *`lhs_expression`)".to_string(),
562                    ExpectedToken::PrimaryExpression => "expression".to_string(),
563                    ExpectedToken::Assignment => "assignment or increment/decrement".to_string(),
564                    ExpectedToken::SwitchItem => concat!(
565                        "switch item (`case` or `default`) or a closing curly bracket ",
566                        "to signify the end of the switch statement (`}`)"
567                    )
568                    .to_string(),
569                    ExpectedToken::WorkgroupSizeSeparator => {
570                        "workgroup size separator (`,`) or a closing parenthesis".to_string()
571                    }
572                    ExpectedToken::GlobalItem => concat!(
573                        "global item (`struct`, `const`, `var`, `alias`, ",
574                        "`fn`, `diagnostic`, `enable`, `requires`, `;`) ",
575                        "or the end of the file"
576                    )
577                    .to_string(),
578                    ExpectedToken::Variable => "variable access".to_string(),
579                    ExpectedToken::Function => "function name".to_string(),
580                    ExpectedToken::AfterIdentListArg => {
581                        "next argument, trailing comma, or end of list (`,` or `;`)".to_string()
582                    }
583                    ExpectedToken::AfterIdentListComma => {
584                        "next argument or end of list (`;`)".to_string()
585                    }
586                    ExpectedToken::DiagnosticAttribute => {
587                        "the `diagnostic` attribute identifier".to_string()
588                    }
589                    ExpectedToken::Statement => "statement".to_string(),
590                    ExpectedToken::ForInit => "for loop initializer statement (`var`/`let`/`const` declaration, assignment, `i++`/`i--` statement, function call)".to_string(),
591                    ExpectedToken::ForUpdate => "for loop update statement (assignment, `i++`/`i--` statement, function call)".to_string(),
592                };
593                ParseError {
594                    message: format!(
595                        "expected {}, found {:?}",
596                        expected_str, &source[unexpected_span],
597                    ),
598                    labels: vec![(unexpected_span, format!("expected {expected_str}").into())],
599                    notes: vec![],
600                }
601            }
602            Error::UnexpectedComponents(bad_span) => ParseError {
603                message: "unexpected components".to_string(),
604                labels: vec![(bad_span, "unexpected components".into())],
605                notes: vec![],
606            },
607            Error::UnexpectedOperationInConstContext(span) => ParseError {
608                message: "this operation is not supported in a const context".to_string(),
609                labels: vec![(span, "operation not supported here".into())],
610                notes: vec![],
611            },
612            Error::BadNumber(bad_span, ref err) => ParseError {
613                message: format!("{}: `{}`", err, &source[bad_span],),
614                labels: vec![(bad_span, err.to_string().into())],
615                notes: vec![],
616            },
617            Error::BadMatrixScalarKind(span, scalar) => ParseError {
618                message: format!(
619                    "matrix scalar type must be floating-point, but found `{}`",
620                    scalar.to_wgsl_for_diagnostics()
621                ),
622                labels: vec![(span, "must be floating-point (e.g. `f32`)".into())],
623                notes: vec![],
624            },
625            Error::BadAccessor(accessor_span) => ParseError {
626                message: format!("invalid field accessor `{}`", &source[accessor_span],),
627                labels: vec![(accessor_span, "invalid accessor".into())],
628                notes: vec![],
629            },
630            Error::UnknownIdent(ident_span, ident) => ParseError {
631                message: format!("no definition in scope for identifier: `{ident}`"),
632                labels: vec![(ident_span, "unknown identifier".into())],
633                notes: vec![],
634            },
635            Error::UnknownScalarType(bad_span) => ParseError {
636                message: format!("unknown scalar type: `{}`", &source[bad_span]),
637                labels: vec![(bad_span, "unknown scalar type".into())],
638                notes: vec!["Valid scalar types are f32, f64, i32, u32, bool".into()],
639            },
640            Error::NotStorageTexture(bad_span) => ParseError {
641                message: "textureStore can only be applied to storage textures".to_string(),
642                labels: vec![(bad_span, "not a storage texture".into())],
643                notes: vec![],
644            },
645            Error::BadTextureSampleType { span, scalar } => ParseError {
646                message: format!(
647                    "texture sample type must be one of f32, i32 or u32, but found {}",
648                    scalar.to_wgsl_for_diagnostics()
649                ),
650                labels: vec![(span, "must be one of f32, i32 or u32".into())],
651                notes: vec![],
652            },
653            Error::BadIncrDecrReferenceType(span) => ParseError {
654                message: concat!(
655                    "increment/decrement operation requires ",
656                    "reference type to be one of i32 or u32"
657                )
658                .to_string(),
659                labels: vec![(span, "must be a reference type of i32 or u32".into())],
660                notes: vec![],
661            },
662            Error::BadTexture(bad_span) => ParseError {
663                message: format!(
664                    "expected an image, but found `{}` which is not an image",
665                    &source[bad_span]
666                ),
667                labels: vec![(bad_span, "not an image".into())],
668                notes: vec![],
669            },
670            Error::BadTypeCast {
671                span,
672                ref from_type,
673                ref to_type,
674            } => {
675                let msg = format!("cannot cast a {from_type} to a {to_type}");
676                ParseError {
677                    message: msg.clone(),
678                    labels: vec![(span, msg.into())],
679                    notes: vec![],
680                }
681            }
682            Error::InvalidResolve(ref resolve_error) => ParseError {
683                message: resolve_error.to_string(),
684                labels: vec![],
685                notes: vec![],
686            },
687            Error::InvalidBreakIf(bad_span) => ParseError {
688                message: "A break if is only allowed in a continuing block".to_string(),
689                labels: vec![(bad_span, "not in a continuing block".into())],
690                notes: vec![],
691            },
692            Error::InvalidGatherComponent(bad_span) => ParseError {
693                message: format!(
694                    "textureGather component `{}` doesn't exist, must be 0, 1, 2, or 3",
695                    &source[bad_span]
696                ),
697                labels: vec![(bad_span, "invalid component".into())],
698                notes: vec![],
699            },
700            Error::InvalidConstructorComponentType(bad_span, component) => ParseError {
701                message: format!("invalid type for constructor component at index [{component}]"),
702                labels: vec![(bad_span, "invalid component type".into())],
703                notes: vec![],
704            },
705            Error::InvalidIdentifierUnderscore(bad_span) => ParseError {
706                message: "Identifier can't be `_`".to_string(),
707                labels: vec![(bad_span, "invalid identifier".into())],
708                notes: vec![
709                    "Use phony assignment instead (`_ =` notice the absence of `let` or `var`)"
710                        .to_string(),
711                ],
712            },
713            Error::ReservedIdentifierPrefix(bad_span) => ParseError {
714                message: format!(
715                    "Identifier starts with a reserved prefix: `{}`",
716                    &source[bad_span]
717                ),
718                labels: vec![(bad_span, "invalid identifier".into())],
719                notes: vec![],
720            },
721            Error::UnknownAddressSpace(bad_span) => ParseError {
722                message: format!("unknown address space: `{}`", &source[bad_span]),
723                labels: vec![(bad_span, "unknown address space".into())],
724                notes: vec![],
725            },
726            Error::InvalidLocalVariableAddressSpace(bad_span) => ParseError {
727                message: format!("invalid address space for local variable: `{}`", &source[bad_span]),
728                labels: vec![(bad_span, "local variables can only use 'function' address space".into())],
729                notes: vec![],
730            },
731            Error::UnknownRayFlag(bad_span) => ParseError {
732                message: format!("unknown ray flag: `{}`", &source[bad_span]),
733                labels: vec![(bad_span, "unknown ray flag".into())],
734                notes: vec![],
735            },
736            Error::RepeatedAttribute(bad_span) => ParseError {
737                message: format!("repeated attribute: `{}`", &source[bad_span]),
738                labels: vec![(bad_span, "repeated attribute".into())],
739                notes: vec![],
740            },
741            Error::UnknownAttribute(bad_span) => ParseError {
742                message: format!("unknown attribute: `{}`", &source[bad_span]),
743                labels: vec![(bad_span, "unknown attribute".into())],
744                notes: vec![],
745            },
746            Error::UnknownBuiltin(bad_span) => ParseError {
747                message: format!("unknown builtin: `{}`", &source[bad_span]),
748                labels: vec![(bad_span, "unknown builtin".into())],
749                notes: vec![],
750            },
751            Error::UnknownAccess(bad_span) => ParseError {
752                message: format!("unknown access: `{}`", &source[bad_span]),
753                labels: vec![(bad_span, "unknown access".into())],
754                notes: vec![],
755            },
756            Error::UnknownStorageFormat(bad_span) => ParseError {
757                message: format!("unknown storage format: `{}`", &source[bad_span]),
758                labels: vec![(bad_span, "unknown storage format".into())],
759                notes: vec![],
760            },
761            Error::UnknownConservativeDepth(bad_span) => ParseError {
762                message: format!("unknown conservative depth: `{}`", &source[bad_span]),
763                labels: vec![(bad_span, "unknown conservative depth".into())],
764                notes: vec![],
765            },
766            Error::UnknownEnableExtension(span, word) => ParseError {
767                message: format!("unknown enable-extension `{word}`"),
768                labels: vec![(span, "".into())],
769                notes: vec![
770                    "See available extensions at <https://www.w3.org/TR/WGSL/#enable-extension>."
771                        .into(),
772                ],
773            },
774            Error::UnknownLanguageExtension(span, name) => ParseError {
775                message: format!("unknown language extension `{name}`"),
776                labels: vec![(span, "".into())],
777                notes: vec![concat!(
778                    "See available extensions at ",
779                    "<https://www.w3.org/TR/WGSL/#language-extensions-sec>."
780                )
781                .into()],
782            },
783            Error::UnknownDiagnosticRuleName(span) => ParseError {
784                message: format!("unknown `diagnostic(…)` rule name `{}`", &source[span]),
785                labels: vec![(span, "not a valid diagnostic rule name".into())],
786                notes: vec![concat!(
787                    "See available trigger rules at ",
788                    "<https://www.w3.org/TR/WGSL/#filterable-triggering-rules>."
789                )
790                .into()],
791            },
792            Error::SizeAttributeTooLow(bad_span, min_size) => ParseError {
793                message: format!("struct member size must be at least {min_size}"),
794                labels: vec![(bad_span, format!("must be at least {min_size}").into())],
795                notes: vec![],
796            },
797            Error::SizeAttributeRequiresFixedFootprint(bad_span) => ParseError {
798                message: "@size attribute requires a type with creation-fixed footprint".to_string(),
799                labels: vec![(bad_span, "type does not have creation-fixed footprint".into())],
800                notes: vec![],
801            },
802            Error::AlignAttributeTooLow(bad_span, min_align) => ParseError {
803                message: format!("struct member alignment must be at least {min_align}"),
804                labels: vec![(bad_span, format!("must be at least {min_align}").into())],
805                notes: vec![],
806            },
807            Error::NonPowerOfTwoAlignAttribute(bad_span) => ParseError {
808                message: "struct member alignment must be a power of 2".to_string(),
809                labels: vec![(bad_span, "must be a power of 2".into())],
810                notes: vec![],
811            },
812            Error::InconsistentBinding(span) => ParseError {
813                message: "input/output binding is not consistent".to_string(),
814                labels: vec![(span, "input/output binding is not consistent".into())],
815                notes: vec![],
816            },
817            Error::TypeNotConstructible(span) => ParseError {
818                message: format!("type `{}` is not constructible", &source[span]),
819                labels: vec![(span, "type is not constructible".into())],
820                notes: vec![],
821            },
822            Error::TypeNotInferable(span) => ParseError {
823                message: "type can't be inferred".to_string(),
824                labels: vec![(span, "type can't be inferred".into())],
825                notes: vec![],
826            },
827            Error::InitializationTypeMismatch {
828                name,
829                ref expected,
830                ref got,
831            } => ParseError {
832                message: format!(
833                    "the type of `{}` is expected to be `{}`, but got `{}`",
834                    &source[name], expected, got,
835                ),
836                labels: vec![(name, format!("definition of `{}`", &source[name]).into())],
837                notes: vec![],
838            },
839            Error::DeclMissingTypeAndInit(name_span) => ParseError {
840                message: format!(
841                    "declaration of `{}` needs a type specifier or initializer",
842                    &source[name_span]
843                ),
844                labels: vec![(name_span, "needs a type specifier or initializer".into())],
845                notes: vec![],
846            },
847            Error::MissingAttribute(name, name_span) => ParseError {
848                message: format!(
849                    "variable `{}` needs a '{}' attribute",
850                    &source[name_span], name
851                ),
852                labels: vec![(
853                    name_span,
854                    format!("definition of `{}`", &source[name_span]).into(),
855                )],
856                notes: vec![],
857            },
858            Error::InvalidAddrOfOperand(span) => ParseError {
859                message: "cannot take the address of a vector component".to_string(),
860                labels: vec![(span, "invalid operand for address-of".into())],
861                notes: vec![],
862            },
863            Error::InvalidAtomicPointer(span) => ParseError {
864                message: "atomic operation is done on a pointer to a non-atomic".to_string(),
865                labels: vec![(span, "atomic pointer is invalid".into())],
866                notes: vec![],
867            },
868            Error::InvalidAtomicOperandType(span) => ParseError {
869                message: "atomic operand type is inconsistent with the operation".to_string(),
870                labels: vec![(span, "atomic operand type is invalid".into())],
871                notes: vec![],
872            },
873            Error::InvalidAtomicAccess(span) => ParseError {
874                message: "atomic variables cannot be accessed directly; use atomic built-in functions".to_string(),
875                labels: vec![(span, "direct access to atomic variable is not allowed".into())],
876                notes: vec![],
877            },
878            Error::InvalidRayQueryPointer(span) => ParseError {
879                message: "ray query operation is done on a pointer to a non-ray-query".to_string(),
880                labels: vec![(span, "ray query pointer is invalid".into())],
881                notes: vec![],
882            },
883            Error::NotPointer(span) => ParseError {
884                message: "the operand of the `*` operator must be a pointer".to_string(),
885                labels: vec![(span, "expression is not a pointer".into())],
886                notes: vec![],
887            },
888            Error::NotReference(what, span) => ParseError {
889                message: format!("{what} must be a reference"),
890                labels: vec![(span, "expression is not a reference".into())],
891                notes: vec![],
892            },
893            Error::InvalidAssignment { span, ty } => {
894                let (extra_label, notes) = match ty {
895                    InvalidAssignmentType::Swizzle => (
896                        None,
897                        vec![
898                            "WGSL does not support assignments to swizzles".into(),
899                            "consider assigning each component individually".into(),
900                        ],
901                    ),
902                    InvalidAssignmentType::ImmutableBinding(binding_span) => (
903                        Some((binding_span, "this is an immutable binding".into())),
904                        vec![format!(
905                            "consider declaring `{}` with `var` instead of `let`",
906                            &source[binding_span]
907                        )],
908                    ),
909                    InvalidAssignmentType::Other => (None, vec![]),
910                };
911
912                ParseError {
913                    message: "invalid left-hand side of assignment".into(),
914                    labels: core::iter::once((span, "cannot assign to this expression".into()))
915                        .chain(extra_label)
916                        .collect(),
917                    notes,
918                }
919            }
920            Error::ReservedKeyword(name_span) => ParseError {
921                message: format!("name `{}` is a reserved keyword", &source[name_span]),
922                labels: vec![(
923                    name_span,
924                    format!("definition of `{}`", &source[name_span]).into(),
925                )],
926                notes: vec![],
927            },
928            Error::Redefinition { previous, current } => ParseError {
929                message: format!("redefinition of `{}`", &source[current]),
930                labels: vec![
931                    (
932                        current,
933                        format!("redefinition of `{}`", &source[current]).into(),
934                    ),
935                    (
936                        previous,
937                        format!("previous definition of `{}`", &source[previous]).into(),
938                    ),
939                ],
940                notes: vec![],
941            },
942            Error::RecursiveDeclaration { ident, usage } => ParseError {
943                message: format!("declaration of `{}` is recursive", &source[ident]),
944                labels: vec![(ident, "".into()), (usage, "uses itself here".into())],
945                notes: vec![],
946            },
947            Error::CyclicDeclaration { ident, ref path } => ParseError {
948                message: format!("declaration of `{}` is cyclic", &source[ident]),
949                labels: path
950                    .iter()
951                    .enumerate()
952                    .flat_map(|(i, &(ident, usage))| {
953                        [
954                            (ident, "".into()),
955                            (
956                                usage,
957                                if i == path.len() - 1 {
958                                    "ending the cycle".into()
959                                } else {
960                                    format!("uses `{}`", &source[ident]).into()
961                                },
962                            ),
963                        ]
964                    })
965                    .collect(),
966                notes: vec![],
967            },
968            Error::InvalidSwitchSelector { span } => ParseError {
969                message: "invalid `switch` selector".to_string(),
970                labels: vec![(
971                    span,
972                    "`switch` selector must be a scalar integer"
973                    .into(),
974                )],
975                notes: vec![],
976            },
977            Error::InvalidSwitchCase { span } => ParseError {
978                message: "invalid `switch` case selector value".to_string(),
979                labels: vec![(
980                    span,
981                    "`switch` case selector must be a scalar integer const expression"
982                    .into(),
983                )],
984                notes: vec![],
985            },
986            Error::SwitchCaseTypeMismatch { span } => ParseError {
987                message: "invalid `switch` case selector value".to_string(),
988                labels: vec![(
989                    span,
990                    "`switch` case selector must have the same type as the `switch` selector expression"
991                    .into(),
992                )],
993                notes: vec![],
994            },
995            Error::CalledEntryPoint(span) => ParseError {
996                message: "entry point cannot be called".to_string(),
997                labels: vec![(span, "entry point cannot be called".into())],
998                notes: vec![],
999            },
1000            Error::CalledLocalDecl(span) => ParseError {
1001                message: "local declaration cannot be called".to_string(),
1002                labels: vec![(span, "local declaration cannot be called".into())],
1003                notes: vec![],
1004            },
1005            Error::WrongArgumentCount {
1006                span,
1007                ref expected,
1008                found,
1009            } => ParseError {
1010                message: format!(
1011                    "wrong number of arguments: expected {}, found {}",
1012                    if expected.len() < 2 {
1013                        format!("{}", expected.start)
1014                    } else {
1015                        format!("{}..{}", expected.start, expected.end)
1016                    },
1017                    found
1018                ),
1019                labels: vec![(span, "wrong number of arguments".into())],
1020                notes: vec![],
1021            },
1022            Error::TooManyArguments {
1023                ref function,
1024                call_span,
1025                arg_span,
1026                max_arguments,
1027            } => ParseError {
1028                message: format!("too many arguments passed to `{function}`"),
1029                labels: vec![
1030                    (call_span, "".into()),
1031                    (arg_span, format!("unexpected argument #{}", max_arguments + 1).into())
1032                ],
1033                notes: vec![
1034                    format!("The `{function}` function accepts at most {max_arguments} argument(s)")
1035                ],
1036            },
1037            Error::WrongArgumentType {
1038                ref function,
1039                call_span,
1040                arg_span,
1041                arg_index,
1042                ref arg_ty,
1043                ref allowed,
1044            } => {
1045                let message = format!(
1046                    "wrong type passed as argument #{} to `{function}`",
1047                    arg_index + 1,
1048                );
1049                let labels = vec![
1050                    (call_span, "".into()),
1051                    (arg_span, format!("argument #{} has type `{arg_ty}`", arg_index + 1).into())
1052                ];
1053
1054                let mut notes = vec![];
1055                notes.push(format!("`{function}` accepts the following types for argument #{}:", arg_index + 1));
1056                notes.extend(allowed.iter().map(|ty| format!("allowed type: {ty}")));
1057
1058                ParseError { message, labels, notes }
1059            },
1060            Error::InconsistentArgumentType {
1061                ref function,
1062                call_span,
1063                arg_span,
1064                arg_index,
1065                ref arg_ty,
1066                inconsistent_span,
1067                inconsistent_index,
1068                ref inconsistent_ty,
1069                ref allowed
1070            } => {
1071                let message = format!(
1072                    "inconsistent type passed as argument #{} to `{function}`",
1073                    arg_index + 1,
1074                );
1075                let labels = vec![
1076                    (call_span, "".into()),
1077                    (arg_span, format!("argument #{} has type {arg_ty}", arg_index + 1).into()),
1078                    (inconsistent_span, format!(
1079                        "this argument has type {inconsistent_ty}, which constrains subsequent arguments"
1080                    ).into()),
1081                ];
1082                let mut notes = vec![
1083                    format!("Because argument #{} has type {inconsistent_ty}, only the following types", inconsistent_index + 1),
1084                    format!("(or types that automatically convert to them) are accepted for argument #{}:", arg_index + 1),
1085                ];
1086                notes.extend(allowed.iter().map(|ty| format!("allowed type: {ty}")));
1087
1088                ParseError { message, labels, notes }
1089            }
1090            Error::FunctionReturnsVoid(span) => ParseError {
1091                message: "function does not return any value".to_string(),
1092                labels: vec![(span, "".into())],
1093                notes: vec![
1094                    "perhaps you meant to call the function in a separate statement?".into(),
1095                ],
1096            },
1097            Error::FunctionMustUseUnused(call) => ParseError {
1098                message: "unused return value from function annotated with @must_use".into(),
1099                labels: vec![(call, "".into())],
1100                notes: vec![
1101                    format!(
1102                        "function '{}' is declared with `@must_use` attribute",
1103                        &source[call],
1104                    ),
1105                    "use a phony assignment or declare a value using the function call as the initializer".into(),
1106                ],
1107            },
1108            Error::FunctionMustUseReturnsVoid(attr, signature) => ParseError {
1109                message: "function annotated with @must_use but does not return any value".into(),
1110                labels: vec![
1111                    (attr, "".into()),
1112                    (signature, "".into()),
1113                ],
1114                notes: vec![
1115                    "declare a return type or remove the attribute".into(),
1116                ],
1117            },
1118            Error::FunctionMustUseOnNonFunction(attr) => ParseError {
1119                message: "attribute `@must_use` is only valid on function declarations".into(),
1120                labels: vec![(attr, "".into())],
1121                notes: vec![
1122                    "place `@must_use` on a function declaration with a return type".into(),
1123                ],
1124            },
1125            Error::InvalidWorkGroupUniformLoad(span) => ParseError {
1126                message: "incorrect type passed to workgroupUniformLoad".into(),
1127                labels: vec![(span, "".into())],
1128                notes: vec!["passed type must be a workgroup pointer".into()],
1129            },
1130            Error::Internal(message) => ParseError {
1131                message: "internal WGSL front end error".to_string(),
1132                labels: vec![],
1133                notes: vec![message.into()],
1134            },
1135            Error::ExpectedConstExprConcreteIntegerScalar(span) => ParseError {
1136                message: concat!(
1137                    "must be a const-expression that ",
1138                    "resolves to a concrete integer scalar (`u32` or `i32`)"
1139                )
1140                .to_string(),
1141                labels: vec![(span, "must resolve to `u32` or `i32`".into())],
1142                notes: vec![],
1143            },
1144            Error::ExpectedNonNegative(span) => ParseError {
1145                message: "must be non-negative (>= 0)".to_string(),
1146                labels: vec![(span, "must be non-negative".into())],
1147                notes: vec![],
1148            },
1149            Error::ExpectedPositiveArrayLength(span) => ParseError {
1150                message: "array element count must be positive (> 0)".to_string(),
1151                labels: vec![(span, "must be positive".into())],
1152                notes: vec![],
1153            },
1154            Error::ConstantEvaluatorError(ref e, span) => ParseError {
1155                message: e.to_string(),
1156                labels: vec![(span, "see msg".into())],
1157                notes: vec![],
1158            },
1159            Error::MissingWorkgroupSize(span) => ParseError {
1160                message: "workgroup size is missing on compute shader entry point".to_string(),
1161                labels: vec![(
1162                    span,
1163                    "must be paired with a `@workgroup_size` attribute".into(),
1164                )],
1165                notes: vec![],
1166            },
1167            Error::AutoConversion(ref error) => {
1168                // destructuring ensures all fields are handled
1169                let AutoConversionError {
1170                    dest_span,
1171                    ref dest_type,
1172                    source_span,
1173                    ref source_type,
1174                } = **error;
1175                ParseError {
1176                    message: format!(
1177                        "automatic conversions cannot convert `{source_type}` to `{dest_type}`"
1178                    ),
1179                    labels: vec![
1180                        (
1181                            dest_span,
1182                            format!("a value of type {dest_type} is required here").into(),
1183                        ),
1184                        (
1185                            source_span,
1186                            format!("this expression has type {source_type}").into(),
1187                        ),
1188                    ],
1189                    notes: vec![],
1190                }
1191            }
1192            Error::AutoConversionLeafScalar(ref error) => {
1193                let AutoConversionLeafScalarError {
1194                    dest_span,
1195                    ref dest_scalar,
1196                    source_span,
1197                    ref source_type,
1198                } = **error;
1199                ParseError {
1200                    message: format!(
1201                        "automatic conversions cannot convert elements of `{source_type}` to `{dest_scalar}`"
1202                    ),
1203                    labels: vec![
1204                        (
1205                            dest_span,
1206                            format!(
1207                                "a value with elements of type {dest_scalar} is required here"
1208                            )
1209                            .into(),
1210                        ),
1211                        (
1212                            source_span,
1213                            format!("this expression has type {source_type}").into(),
1214                        ),
1215                    ],
1216                    notes: vec![],
1217                }
1218            }
1219            Error::ConcretizationFailed(ref error) => {
1220                let ConcretizationFailedError {
1221                    expr_span,
1222                    ref expr_type,
1223                    ref concretization_preferences,
1224                } = **error;
1225                ParseError {
1226                    message: "failed to convert expression to a concrete type".to_string(),
1227                    labels: vec![(
1228                        expr_span,
1229                        format!("this expression has type {expr_type}").into(),
1230                    )],
1231                    notes: concretization_preferences
1232                        .iter()
1233                        .map(|&(ref scalar, ref err)|
1234                            format!("the expression couldn't be converted to have {scalar} scalar type: {err}")
1235                        )
1236                        .collect(),
1237                }
1238            }
1239            Error::ExceededLimitForNestedBraces { span, limit } => ParseError {
1240                message: "brace nesting limit reached".into(),
1241                labels: vec![(span, "limit reached at this brace".into())],
1242                notes: vec![format!("nesting limit is currently set to {limit}")],
1243            },
1244            Error::PipelineConstantIDValue(span) => ParseError {
1245                message: "pipeline constant ID must be between 0 and 65535 inclusive".to_string(),
1246                labels: vec![(span, "must be between 0 and 65535 inclusive".into())],
1247                notes: vec![],
1248            },
1249            Error::NotBool(span) => ParseError {
1250                message: "must be a const-expression that resolves to a `bool`".to_string(),
1251                labels: vec![(span, "must resolve to `bool`".into())],
1252                notes: vec![],
1253            },
1254            Error::ConstAssertFailed(span) => ParseError {
1255                message: "`const_assert` failure".to_string(),
1256                labels: vec![(span, "evaluates to `false`".into())],
1257                notes: vec![],
1258            },
1259            Error::DirectiveAfterFirstGlobalDecl { directive_span } => ParseError {
1260                message: "expected global declaration, but found a global directive".into(),
1261                labels: vec![(
1262                    directive_span,
1263                    "written after first global declaration".into(),
1264                )],
1265                notes: vec![concat!(
1266                    "global directives are only allowed before global declarations; ",
1267                    "maybe hoist this closer to the top of the shader module?"
1268                )
1269                .into()],
1270            },
1271            Error::EnableExtensionNotYetImplemented { kind, span } => ParseError {
1272                message: format!(
1273                    "the `{}` enable-extension is not yet supported",
1274                    EnableExtension::Unimplemented(kind).to_ident()
1275                ),
1276                labels: vec![(
1277                    span,
1278                    concat!(
1279                        "this enable-extension specifies standard functionality ",
1280                        "which is not yet implemented in Naga"
1281                    )
1282                    .into(),
1283                )],
1284                notes: vec![format!(
1285                    concat!(
1286                        "Let Naga maintainers know that you ran into this at ",
1287                        "<https://github.com/gfx-rs/wgpu/issues/{}>, ",
1288                        "so they can prioritize it!"
1289                    ),
1290                    kind.tracking_issue_num()
1291                )],
1292            },
1293            Error::EnableExtensionNotEnabled { kind, span } => ParseError {
1294                message: format!("the `{}` enable extension is not enabled", kind.to_ident()),
1295                labels: vec![(
1296                    span,
1297                    format!(
1298                        concat!(
1299                            "the `{}` \"Enable Extension\" is needed for this functionality, ",
1300                            "but it is not currently enabled."
1301                        ),
1302                        kind.to_ident()
1303                    )
1304                    .into(),
1305                )],
1306                notes: if let EnableExtension::Unimplemented(kind) = kind {
1307                    vec![format!(
1308                        concat!(
1309                            "This \"Enable Extension\" is not yet implemented. ",
1310                            "Let Naga maintainers know that you ran into this at ",
1311                            "<https://github.com/gfx-rs/wgpu/issues/{}>, ",
1312                            "so they can prioritize it!"
1313                        ),
1314                        kind.tracking_issue_num()
1315                    )]
1316                } else {
1317                    vec![
1318                        format!(
1319                            "You can enable this extension by adding `enable {};` at the top of the shader, before any other items.",
1320                            kind.to_ident()
1321                        ),
1322                    ]
1323                },
1324            },
1325            Error::EnableExtensionNotSupported { kind, span } => ParseError {
1326                message: format!(
1327                    "the `{}` extension is not supported in the current environment",
1328                    kind.to_ident()
1329                ),
1330                labels: vec![(
1331                    span,
1332                    "unsupported enable-extension".into(),
1333                )],
1334                notes: vec![],
1335            },
1336            Error::LanguageExtensionNotYetImplemented { kind, span } => ParseError {
1337                message: format!(
1338                    "the `{}` language extension is not yet supported",
1339                    LanguageExtension::Unimplemented(kind).to_ident()
1340                ),
1341                labels: vec![(span, "".into())],
1342                notes: vec![format!(
1343                    concat!(
1344                        "Let Naga maintainers know that you ran into this at ",
1345                        "<https://github.com/gfx-rs/wgpu/issues/{}>, ",
1346                        "so they can prioritize it!"
1347                    ),
1348                    kind.tracking_issue_num()
1349                )],
1350            },
1351            Error::DiagnosticInvalidSeverity {
1352                severity_control_name_span,
1353            } => ParseError {
1354                message: "invalid `diagnostic(…)` severity".into(),
1355                labels: vec![(
1356                    severity_control_name_span,
1357                    "not a valid severity level".into(),
1358                )],
1359                notes: vec![concat!(
1360                    "See available severities at ",
1361                    "<https://www.w3.org/TR/WGSL/#diagnostic-severity>."
1362                )
1363                .into()],
1364            },
1365            Error::DiagnosticDuplicateTriggeringRule(ConflictingDiagnosticRuleError {
1366                triggering_rule_spans,
1367            }) => {
1368                let [first_span, second_span] = triggering_rule_spans;
1369                ParseError {
1370                    message: "found conflicting `diagnostic(…)` rule(s)".into(),
1371                    labels: vec![
1372                        (first_span, "first rule".into()),
1373                        (second_span, "second rule".into()),
1374                    ],
1375                    notes: vec![
1376                        concat!(
1377                            "Multiple `diagnostic(…)` rules with the same rule name ",
1378                            "conflict unless they are directives and the severity is the same.",
1379                        )
1380                        .into(),
1381                        "You should delete the rule you don't want.".into(),
1382                    ],
1383                }
1384            }
1385            Error::DiagnosticAttributeNotYetImplementedAtParseSite {
1386                site_name_plural,
1387                ref spans,
1388            } => ParseError {
1389                message: "`@diagnostic(…)` attribute(s) not yet implemented".into(),
1390                labels: {
1391                    let mut spans = spans.iter().cloned();
1392                    let first = spans
1393                        .next()
1394                        .map(|span| {
1395                            (
1396                                span,
1397                                format!("can't use this on {site_name_plural} (yet)").into(),
1398                            )
1399                        })
1400                        .expect("internal error: diag. attr. rejection on empty map");
1401                    core::iter::once(first)
1402                        .chain(spans.map(|span| (span, "".into())))
1403                        .collect()
1404                },
1405                notes: vec![format!(concat!(
1406                    "Let Naga maintainers know that you ran into this at ",
1407                    "<https://github.com/gfx-rs/wgpu/issues/5320>, ",
1408                    "so they can prioritize it!"
1409                ))],
1410            },
1411            Error::DiagnosticAttributeNotSupported { on_what, ref spans } => {
1412                // In this case the user may have intended to create a global diagnostic filter directive,
1413                // so display a note to them suggesting the correct syntax.
1414                let intended_diagnostic_directive = match on_what {
1415                    DiagnosticAttributeNotSupportedPosition::SemicolonInModulePosition => true,
1416                    DiagnosticAttributeNotSupportedPosition::Other { .. } => false,
1417                };
1418                let on_what_plural = match on_what {
1419                    DiagnosticAttributeNotSupportedPosition::SemicolonInModulePosition => {
1420                        "semicolons"
1421                    }
1422                    DiagnosticAttributeNotSupportedPosition::Other { display_plural } => {
1423                        display_plural
1424                    }
1425                };
1426                ParseError {
1427                    message: format!(
1428                        "`@diagnostic(…)` attribute(s) on {on_what_plural} are not supported",
1429                    ),
1430                    labels: spans
1431                        .iter()
1432                        .cloned()
1433                        .map(|span| (span, "".into()))
1434                        .collect(),
1435                    notes: vec![
1436                        concat!(
1437                            "`@diagnostic(…)` attributes are only permitted on `fn`s, ",
1438                            "some statements, and `switch`/`loop` bodies."
1439                        )
1440                        .into(),
1441                        {
1442                            if intended_diagnostic_directive {
1443                                concat!(
1444                                    "If you meant to declare a diagnostic filter that ",
1445                                    "applies to the entire module, move this line to ",
1446                                    "the top of the file and remove the `@` symbol."
1447                                )
1448                                .into()
1449                            } else {
1450                                concat!(
1451                                    "These attributes are well-formed, ",
1452                                    "you likely just need to move them."
1453                                )
1454                                .into()
1455                            }
1456                        },
1457                    ],
1458                }
1459            }
1460            Error::SelectUnexpectedArgumentType { arg_span, ref arg_type } => ParseError {
1461                message: "unexpected argument type for `select` call".into(),
1462                labels: vec![(arg_span, format!("this value of type {arg_type}").into())],
1463                notes: vec!["expected a scalar or a `vecN` of scalars".into()],
1464            },
1465            Error::SelectRejectAndAcceptHaveNoCommonType {
1466                reject_span,
1467                ref reject_type,
1468                accept_span,
1469                ref accept_type,
1470            } => ParseError {
1471                message: "type mismatch for reject and accept values in `select` call".into(),
1472                labels: vec![
1473                    (reject_span, format!("reject value of type {reject_type}").into()),
1474                    (accept_span, format!("accept value of type {accept_type}").into()),
1475                ],
1476                notes: vec![],
1477            },
1478            Error::ExpectedGlobalVariable { name_span } => ParseError {
1479                message: "expected global variable".to_string(),
1480                labels: vec![(name_span, "variable used here".into())],
1481                notes: vec![],
1482            },
1483            Error::StructMemberTooLarge { member_name_span } => ParseError {
1484                message: "struct member is too large".into(),
1485                labels: vec![(member_name_span, "this member exceeds the maximum size".into())],
1486                notes: vec![format!(
1487                    "the maximum size is {} bytes",
1488                    crate::valid::MAX_TYPE_SIZE
1489                )],
1490            },
1491            Error::TypeTooLarge { span } => ParseError {
1492                message: "type is too large".into(),
1493                labels: vec![(span, "this type exceeds the maximum size".into())],
1494                notes: vec![format!(
1495                    "the maximum size is {} bytes",
1496                    crate::valid::MAX_TYPE_SIZE
1497                )],
1498            },
1499            Error::UnderspecifiedCooperativeMatrix => ParseError {
1500                message: "cooperative matrix constructor is underspecified".into(),
1501                labels: vec![],
1502                notes: vec![format!("must be F32")],
1503            },
1504            Error::InvalidCooperativeLoadType(span) => ParseError {
1505                message: "cooperative load should have a generic type for coop_mat".into(),
1506                labels: vec![(span, "type needs the coop_mat<...>".into())],
1507                notes: vec![format!("must be a valid cooperative type")],
1508            },
1509            Error::UnsupportedCooperativeScalar(span) => ParseError {
1510                message: "cooperative scalar type is not supported".into(),
1511                labels: vec![(span, "type needs the scalar type specified".into())],
1512                notes: vec![format!("must be F32")],
1513            },
1514            Error::UnexpectedIdentForEnumerant(ident_span) => ParseError {
1515                message: format!(
1516                    "identifier `{}` resolves to a declaration",
1517                    &source[ident_span]
1518                ),
1519                labels: vec![(ident_span, "needs to resolve to a predeclared enumerant".into())],
1520                notes: vec![],
1521            },
1522            Error::UnexpectedExprForEnumerant(expr_span) => ParseError {
1523                message: "unexpected expression".to_string(),
1524                labels: vec![(expr_span, "needs to be an identifier resolving to a predeclared enumerant".into())],
1525                notes: vec![],
1526            },
1527            Error::UnusedArgsForTemplate(ref expr_spans) => ParseError {
1528                message: "unused expressions for template".to_string(),
1529                labels: expr_spans.iter().cloned().map(|span| -> (_, _){ (span, "unused".into()) }).collect(),
1530                notes: vec![],
1531            },
1532            Error::UnexpectedTemplate(span) => ParseError {
1533                message: "unexpected template".to_string(),
1534                labels: vec![(span, "expected identifier".into())],
1535                notes: vec![],
1536            },
1537            Error::MissingTemplateArg {
1538                span,
1539                description: arg,
1540            } => ParseError {
1541                message: format!(
1542                    "`{}` needs a template argument specified: {arg}",
1543                    &source[span]
1544                ),
1545                labels: vec![(span, "is missing a template argument".into())],
1546                notes: vec![],
1547            },
1548            Error::UnexpectedExprForTypeExpression(expr_span) => ParseError {
1549                message: "unexpected expression".to_string(),
1550                labels: vec![(expr_span, "needs to be an identifier resolving to a type declaration (alias or struct) or predeclared type(-generator)".into())],
1551                notes: vec![],
1552            },
1553            Error::MissingIncomingPayload(span) => ParseError {
1554                message: "incoming payload is missing on a `closest_hit`, `any_hit` or `miss` shader entry point".to_string(),
1555                labels: vec![(
1556                    span,
1557                    "must be paired with a `@incoming_payload` attribute".into(),
1558                )],
1559                notes: vec![],
1560            },
1561            Error::UnterminatedBlockComment(span) => ParseError {
1562                message: "unterminated block comment".to_string(),
1563                labels: vec![(
1564                    span,
1565                    "must be closed with `*/`".into(),
1566                )],
1567                notes: vec![],
1568            }
1569        }
1570    }
1571}