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