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