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