1use 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 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 #[cfg(feature = "stderr")]
71 pub fn emit_to_stderr(&self, source: &str) {
72 self.emit_to_stderr_with_path(source, "wgsl")
73 }
74
75 #[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 pub fn emit_to_string(&self, source: &str) -> String {
99 self.emit_to_string_with_path(source, "wgsl")
100 }
101
102 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 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 PrimaryExpression,
143 Assignment,
145 SwitchItem,
147 WorkgroupSizeSeparator,
149 GlobalItem,
151 Type,
153 Variable,
155 Function,
157 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 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 {
243 previous: Span,
245
246 current: Span,
248 },
249 RecursiveDeclaration {
251 ident: Span,
253
254 usage: Span,
256 },
257 CyclicDeclaration {
260 ident: Span,
262
263 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 TooManyArguments {
288 function: String,
290
291 call_span: Span,
293
294 arg_span: Span,
296
297 max_arguments: u32,
300 },
301 WrongArgumentType {
304 function: String,
306
307 call_span: Span,
309
310 arg_span: Span,
312
313 arg_index: u32,
315
316 arg_ty: String,
318
319 allowed: Vec<String>,
322 },
323 InconsistentArgumentType {
326 function: String,
328
329 call_span: Span,
331
332 arg_span: Span,
334
335 arg_index: u32,
337
338 arg_ty: String,
340
341 inconsistent_span: Span,
344
345 inconsistent_index: u32,
347
348 inconsistent_ty: String,
350
351 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#[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 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 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}