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