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