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