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