naga/front/wgsl/
error.rs

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