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 InvalidLocalVariableAddressSpace(Span),
203 RepeatedAttribute(Span),
204 UnknownAttribute(Span),
205 UnknownBuiltin(Span),
206 UnknownAccess(Span),
207 UnknownIdent(Span, &'a str),
208 UnknownScalarType(Span),
209 UnknownType(Span),
210 UnknownStorageFormat(Span),
211 UnknownConservativeDepth(Span),
212 UnknownEnableExtension(Span, &'a str),
213 UnknownLanguageExtension(Span, &'a str),
214 UnknownDiagnosticRuleName(Span),
215 SizeAttributeTooLow(Span, u32),
216 AlignAttributeTooLow(Span, Alignment),
217 NonPowerOfTwoAlignAttribute(Span),
218 InconsistentBinding(Span),
219 TypeNotConstructible(Span),
220 TypeNotInferable(Span),
221 InitializationTypeMismatch {
222 name: Span,
223 expected: String,
224 got: String,
225 },
226 DeclMissingTypeAndInit(Span),
227 MissingAttribute(&'static str, Span),
228 InvalidAddrOfOperand(Span),
229 InvalidAtomicPointer(Span),
230 InvalidAtomicOperandType(Span),
231 InvalidRayQueryPointer(Span),
232 NotPointer(Span),
233 NotReference(&'static str, Span),
234 InvalidAssignment {
235 span: Span,
236 ty: InvalidAssignmentType,
237 },
238 ReservedKeyword(Span),
239 Redefinition {
241 previous: Span,
243
244 current: Span,
246 },
247 RecursiveDeclaration {
249 ident: Span,
251
252 usage: Span,
254 },
255 CyclicDeclaration {
258 ident: Span,
260
261 path: Box<[(Span, Span)]>,
268 },
269 InvalidSwitchSelector {
270 span: Span,
271 },
272 InvalidSwitchCase {
273 span: Span,
274 },
275 SwitchCaseTypeMismatch {
276 span: Span,
277 },
278 CalledEntryPoint(Span),
279 WrongArgumentCount {
280 span: Span,
281 expected: Range<u32>,
282 found: u32,
283 },
284 TooManyArguments {
286 function: String,
288
289 call_span: Span,
291
292 arg_span: Span,
294
295 max_arguments: u32,
298 },
299 WrongArgumentType {
302 function: String,
304
305 call_span: Span,
307
308 arg_span: Span,
310
311 arg_index: u32,
313
314 arg_ty: String,
316
317 allowed: Vec<String>,
320 },
321 InconsistentArgumentType {
324 function: String,
326
327 call_span: Span,
329
330 arg_span: Span,
332
333 arg_index: u32,
335
336 arg_ty: String,
338
339 inconsistent_span: Span,
342
343 inconsistent_index: u32,
345
346 inconsistent_ty: String,
348
349 allowed: Vec<String>,
352 },
353 FunctionReturnsVoid(Span),
354 FunctionMustUseUnused(Span),
355 FunctionMustUseReturnsVoid(Span, Span),
356 InvalidWorkGroupUniformLoad(Span),
357 Internal(&'static str),
358 ExpectedConstExprConcreteIntegerScalar(Span),
359 ExpectedNonNegative(Span),
360 ExpectedPositiveArrayLength(Span),
361 MissingWorkgroupSize(Span),
362 ConstantEvaluatorError(Box<ConstantEvaluatorError>, Span),
363 AutoConversion(Box<AutoConversionError>),
364 AutoConversionLeafScalar(Box<AutoConversionLeafScalarError>),
365 ConcretizationFailed(Box<ConcretizationFailedError>),
366 ExceededLimitForNestedBraces {
367 span: Span,
368 limit: u8,
369 },
370 PipelineConstantIDValue(Span),
371 NotBool(Span),
372 ConstAssertFailed(Span),
373 DirectiveAfterFirstGlobalDecl {
374 directive_span: Span,
375 },
376 EnableExtensionNotYetImplemented {
377 kind: UnimplementedEnableExtension,
378 span: Span,
379 },
380 EnableExtensionNotEnabled {
381 kind: EnableExtension,
382 span: Span,
383 },
384 LanguageExtensionNotYetImplemented {
385 kind: UnimplementedLanguageExtension,
386 span: Span,
387 },
388 DiagnosticInvalidSeverity {
389 severity_control_name_span: Span,
390 },
391 DiagnosticDuplicateTriggeringRule(ConflictingDiagnosticRuleError),
392 DiagnosticAttributeNotYetImplementedAtParseSite {
393 site_name_plural: &'static str,
394 spans: Vec<Span>,
395 },
396 DiagnosticAttributeNotSupported {
397 on_what: DiagnosticAttributeNotSupportedPosition,
398 spans: Vec<Span>,
399 },
400 SelectUnexpectedArgumentType {
401 arg_span: Span,
402 arg_type: String,
403 },
404 SelectRejectAndAcceptHaveNoCommonType {
405 reject_span: Span,
406 reject_type: String,
407 accept_span: Span,
408 accept_type: String,
409 },
410 ExpectedGlobalVariable {
411 name_span: Span,
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::InvalidLocalVariableAddressSpace(bad_span) => ParseError {
665 message: format!("invalid address space for local variable: `{}`", &source[bad_span]),
666 labels: vec![(bad_span, "local variables can only use 'function' address space".into())],
667 notes: vec![],
668 },
669 Error::RepeatedAttribute(bad_span) => ParseError {
670 message: format!("repeated attribute: `{}`", &source[bad_span]),
671 labels: vec![(bad_span, "repeated attribute".into())],
672 notes: vec![],
673 },
674 Error::UnknownAttribute(bad_span) => ParseError {
675 message: format!("unknown attribute: `{}`", &source[bad_span]),
676 labels: vec![(bad_span, "unknown attribute".into())],
677 notes: vec![],
678 },
679 Error::UnknownBuiltin(bad_span) => ParseError {
680 message: format!("unknown builtin: `{}`", &source[bad_span]),
681 labels: vec![(bad_span, "unknown builtin".into())],
682 notes: vec![],
683 },
684 Error::UnknownAccess(bad_span) => ParseError {
685 message: format!("unknown access: `{}`", &source[bad_span]),
686 labels: vec![(bad_span, "unknown access".into())],
687 notes: vec![],
688 },
689 Error::UnknownStorageFormat(bad_span) => ParseError {
690 message: format!("unknown storage format: `{}`", &source[bad_span]),
691 labels: vec![(bad_span, "unknown storage format".into())],
692 notes: vec![],
693 },
694 Error::UnknownConservativeDepth(bad_span) => ParseError {
695 message: format!("unknown conservative depth: `{}`", &source[bad_span]),
696 labels: vec![(bad_span, "unknown conservative depth".into())],
697 notes: vec![],
698 },
699 Error::UnknownType(bad_span) => ParseError {
700 message: format!("unknown type: `{}`", &source[bad_span]),
701 labels: vec![(bad_span, "unknown type".into())],
702 notes: vec![],
703 },
704 Error::UnknownEnableExtension(span, word) => ParseError {
705 message: format!("unknown enable-extension `{word}`"),
706 labels: vec![(span, "".into())],
707 notes: vec![
708 "See available extensions at <https://www.w3.org/TR/WGSL/#enable-extension>."
709 .into(),
710 ],
711 },
712 Error::UnknownLanguageExtension(span, name) => ParseError {
713 message: format!("unknown language extension `{name}`"),
714 labels: vec![(span, "".into())],
715 notes: vec![concat!(
716 "See available extensions at ",
717 "<https://www.w3.org/TR/WGSL/#language-extensions-sec>."
718 )
719 .into()],
720 },
721 Error::UnknownDiagnosticRuleName(span) => ParseError {
722 message: format!("unknown `diagnostic(…)` rule name `{}`", &source[span]),
723 labels: vec![(span, "not a valid diagnostic rule name".into())],
724 notes: vec![concat!(
725 "See available trigger rules at ",
726 "<https://www.w3.org/TR/WGSL/#filterable-triggering-rules>."
727 )
728 .into()],
729 },
730 Error::SizeAttributeTooLow(bad_span, min_size) => ParseError {
731 message: format!("struct member size must be at least {min_size}"),
732 labels: vec![(bad_span, format!("must be at least {min_size}").into())],
733 notes: vec![],
734 },
735 Error::AlignAttributeTooLow(bad_span, min_align) => ParseError {
736 message: format!("struct member alignment must be at least {min_align}"),
737 labels: vec![(bad_span, format!("must be at least {min_align}").into())],
738 notes: vec![],
739 },
740 Error::NonPowerOfTwoAlignAttribute(bad_span) => ParseError {
741 message: "struct member alignment must be a power of 2".to_string(),
742 labels: vec![(bad_span, "must be a power of 2".into())],
743 notes: vec![],
744 },
745 Error::InconsistentBinding(span) => ParseError {
746 message: "input/output binding is not consistent".to_string(),
747 labels: vec![(span, "input/output binding is not consistent".into())],
748 notes: vec![],
749 },
750 Error::TypeNotConstructible(span) => ParseError {
751 message: format!("type `{}` is not constructible", &source[span]),
752 labels: vec![(span, "type is not constructible".into())],
753 notes: vec![],
754 },
755 Error::TypeNotInferable(span) => ParseError {
756 message: "type can't be inferred".to_string(),
757 labels: vec![(span, "type can't be inferred".into())],
758 notes: vec![],
759 },
760 Error::InitializationTypeMismatch {
761 name,
762 ref expected,
763 ref got,
764 } => ParseError {
765 message: format!(
766 "the type of `{}` is expected to be `{}`, but got `{}`",
767 &source[name], expected, got,
768 ),
769 labels: vec![(name, format!("definition of `{}`", &source[name]).into())],
770 notes: vec![],
771 },
772 Error::DeclMissingTypeAndInit(name_span) => ParseError {
773 message: format!(
774 "declaration of `{}` needs a type specifier or initializer",
775 &source[name_span]
776 ),
777 labels: vec![(name_span, "needs a type specifier or initializer".into())],
778 notes: vec![],
779 },
780 Error::MissingAttribute(name, name_span) => ParseError {
781 message: format!(
782 "variable `{}` needs a '{}' attribute",
783 &source[name_span], name
784 ),
785 labels: vec![(
786 name_span,
787 format!("definition of `{}`", &source[name_span]).into(),
788 )],
789 notes: vec![],
790 },
791 Error::InvalidAddrOfOperand(span) => ParseError {
792 message: "cannot take the address of a vector component".to_string(),
793 labels: vec![(span, "invalid operand for address-of".into())],
794 notes: vec![],
795 },
796 Error::InvalidAtomicPointer(span) => ParseError {
797 message: "atomic operation is done on a pointer to a non-atomic".to_string(),
798 labels: vec![(span, "atomic pointer is invalid".into())],
799 notes: vec![],
800 },
801 Error::InvalidAtomicOperandType(span) => ParseError {
802 message: "atomic operand type is inconsistent with the operation".to_string(),
803 labels: vec![(span, "atomic operand type is invalid".into())],
804 notes: vec![],
805 },
806 Error::InvalidRayQueryPointer(span) => ParseError {
807 message: "ray query operation is done on a pointer to a non-ray-query".to_string(),
808 labels: vec![(span, "ray query pointer is invalid".into())],
809 notes: vec![],
810 },
811 Error::NotPointer(span) => ParseError {
812 message: "the operand of the `*` operator must be a pointer".to_string(),
813 labels: vec![(span, "expression is not a pointer".into())],
814 notes: vec![],
815 },
816 Error::NotReference(what, span) => ParseError {
817 message: format!("{what} must be a reference"),
818 labels: vec![(span, "expression is not a reference".into())],
819 notes: vec![],
820 },
821 Error::InvalidAssignment { span, ty } => {
822 let (extra_label, notes) = match ty {
823 InvalidAssignmentType::Swizzle => (
824 None,
825 vec![
826 "WGSL does not support assignments to swizzles".into(),
827 "consider assigning each component individually".into(),
828 ],
829 ),
830 InvalidAssignmentType::ImmutableBinding(binding_span) => (
831 Some((binding_span, "this is an immutable binding".into())),
832 vec![format!(
833 "consider declaring `{}` with `var` instead of `let`",
834 &source[binding_span]
835 )],
836 ),
837 InvalidAssignmentType::Other => (None, vec![]),
838 };
839
840 ParseError {
841 message: "invalid left-hand side of assignment".into(),
842 labels: core::iter::once((span, "cannot assign to this expression".into()))
843 .chain(extra_label)
844 .collect(),
845 notes,
846 }
847 }
848 Error::ReservedKeyword(name_span) => ParseError {
849 message: format!("name `{}` is a reserved keyword", &source[name_span]),
850 labels: vec![(
851 name_span,
852 format!("definition of `{}`", &source[name_span]).into(),
853 )],
854 notes: vec![],
855 },
856 Error::Redefinition { previous, current } => ParseError {
857 message: format!("redefinition of `{}`", &source[current]),
858 labels: vec![
859 (
860 current,
861 format!("redefinition of `{}`", &source[current]).into(),
862 ),
863 (
864 previous,
865 format!("previous definition of `{}`", &source[previous]).into(),
866 ),
867 ],
868 notes: vec![],
869 },
870 Error::RecursiveDeclaration { ident, usage } => ParseError {
871 message: format!("declaration of `{}` is recursive", &source[ident]),
872 labels: vec![(ident, "".into()), (usage, "uses itself here".into())],
873 notes: vec![],
874 },
875 Error::CyclicDeclaration { ident, ref path } => ParseError {
876 message: format!("declaration of `{}` is cyclic", &source[ident]),
877 labels: path
878 .iter()
879 .enumerate()
880 .flat_map(|(i, &(ident, usage))| {
881 [
882 (ident, "".into()),
883 (
884 usage,
885 if i == path.len() - 1 {
886 "ending the cycle".into()
887 } else {
888 format!("uses `{}`", &source[ident]).into()
889 },
890 ),
891 ]
892 })
893 .collect(),
894 notes: vec![],
895 },
896 Error::InvalidSwitchSelector { span } => ParseError {
897 message: "invalid `switch` selector".to_string(),
898 labels: vec![(
899 span,
900 "`switch` selector must be a scalar integer"
901 .into(),
902 )],
903 notes: vec![],
904 },
905 Error::InvalidSwitchCase { span } => ParseError {
906 message: "invalid `switch` case selector value".to_string(),
907 labels: vec![(
908 span,
909 "`switch` case selector must be a scalar integer const expression"
910 .into(),
911 )],
912 notes: vec![],
913 },
914 Error::SwitchCaseTypeMismatch { span } => ParseError {
915 message: "invalid `switch` case selector value".to_string(),
916 labels: vec![(
917 span,
918 "`switch` case selector must have the same type as the `switch` selector expression"
919 .into(),
920 )],
921 notes: vec![],
922 },
923 Error::CalledEntryPoint(span) => ParseError {
924 message: "entry point cannot be called".to_string(),
925 labels: vec![(span, "entry point cannot be called".into())],
926 notes: vec![],
927 },
928 Error::WrongArgumentCount {
929 span,
930 ref expected,
931 found,
932 } => ParseError {
933 message: format!(
934 "wrong number of arguments: expected {}, found {}",
935 if expected.len() < 2 {
936 format!("{}", expected.start)
937 } else {
938 format!("{}..{}", expected.start, expected.end)
939 },
940 found
941 ),
942 labels: vec![(span, "wrong number of arguments".into())],
943 notes: vec![],
944 },
945 Error::TooManyArguments {
946 ref function,
947 call_span,
948 arg_span,
949 max_arguments,
950 } => ParseError {
951 message: format!("too many arguments passed to `{function}`"),
952 labels: vec![
953 (call_span, "".into()),
954 (arg_span, format!("unexpected argument #{}", max_arguments + 1).into())
955 ],
956 notes: vec![
957 format!("The `{function}` function accepts at most {max_arguments} argument(s)")
958 ],
959 },
960 Error::WrongArgumentType {
961 ref function,
962 call_span,
963 arg_span,
964 arg_index,
965 ref arg_ty,
966 ref allowed,
967 } => {
968 let message = format!(
969 "wrong type passed as argument #{} to `{function}`",
970 arg_index + 1,
971 );
972 let labels = vec![
973 (call_span, "".into()),
974 (arg_span, format!("argument #{} has type `{arg_ty}`", arg_index + 1).into())
975 ];
976
977 let mut notes = vec![];
978 notes.push(format!("`{function}` accepts the following types for argument #{}:", arg_index + 1));
979 notes.extend(allowed.iter().map(|ty| format!("allowed type: {ty}")));
980
981 ParseError { message, labels, notes }
982 },
983 Error::InconsistentArgumentType {
984 ref function,
985 call_span,
986 arg_span,
987 arg_index,
988 ref arg_ty,
989 inconsistent_span,
990 inconsistent_index,
991 ref inconsistent_ty,
992 ref allowed
993 } => {
994 let message = format!(
995 "inconsistent type passed as argument #{} to `{function}`",
996 arg_index + 1,
997 );
998 let labels = vec![
999 (call_span, "".into()),
1000 (arg_span, format!("argument #{} has type {arg_ty}", arg_index + 1).into()),
1001 (inconsistent_span, format!(
1002 "this argument has type {inconsistent_ty}, which constrains subsequent arguments"
1003 ).into()),
1004 ];
1005 let mut notes = vec![
1006 format!("Because argument #{} has type {inconsistent_ty}, only the following types", inconsistent_index + 1),
1007 format!("(or types that automatically convert to them) are accepted for argument #{}:", arg_index + 1),
1008 ];
1009 notes.extend(allowed.iter().map(|ty| format!("allowed type: {ty}")));
1010
1011 ParseError { message, labels, notes }
1012 }
1013 Error::FunctionReturnsVoid(span) => ParseError {
1014 message: "function does not return any value".to_string(),
1015 labels: vec![(span, "".into())],
1016 notes: vec![
1017 "perhaps you meant to call the function in a separate statement?".into(),
1018 ],
1019 },
1020 Error::FunctionMustUseUnused(call) => ParseError {
1021 message: "unused return value from function annotated with @must_use".into(),
1022 labels: vec![(call, "".into())],
1023 notes: vec![
1024 format!(
1025 "function '{}' is declared with `@must_use` attribute",
1026 &source[call],
1027 ),
1028 "use a phony assignment or declare a value using the function call as the initializer".into(),
1029 ],
1030 },
1031 Error::FunctionMustUseReturnsVoid(attr, signature) => ParseError {
1032 message: "function annotated with @must_use but does not return any value".into(),
1033 labels: vec![
1034 (attr, "".into()),
1035 (signature, "".into()),
1036 ],
1037 notes: vec![
1038 "declare a return type or remove the attribute".into(),
1039 ],
1040 },
1041 Error::InvalidWorkGroupUniformLoad(span) => ParseError {
1042 message: "incorrect type passed to workgroupUniformLoad".into(),
1043 labels: vec![(span, "".into())],
1044 notes: vec!["passed type must be a workgroup pointer".into()],
1045 },
1046 Error::Internal(message) => ParseError {
1047 message: "internal WGSL front end error".to_string(),
1048 labels: vec![],
1049 notes: vec![message.into()],
1050 },
1051 Error::ExpectedConstExprConcreteIntegerScalar(span) => ParseError {
1052 message: concat!(
1053 "must be a const-expression that ",
1054 "resolves to a concrete integer scalar (`u32` or `i32`)"
1055 )
1056 .to_string(),
1057 labels: vec![(span, "must resolve to `u32` or `i32`".into())],
1058 notes: vec![],
1059 },
1060 Error::ExpectedNonNegative(span) => ParseError {
1061 message: "must be non-negative (>= 0)".to_string(),
1062 labels: vec![(span, "must be non-negative".into())],
1063 notes: vec![],
1064 },
1065 Error::ExpectedPositiveArrayLength(span) => ParseError {
1066 message: "array element count must be positive (> 0)".to_string(),
1067 labels: vec![(span, "must be positive".into())],
1068 notes: vec![],
1069 },
1070 Error::ConstantEvaluatorError(ref e, span) => ParseError {
1071 message: e.to_string(),
1072 labels: vec![(span, "see msg".into())],
1073 notes: vec![],
1074 },
1075 Error::MissingWorkgroupSize(span) => ParseError {
1076 message: "workgroup size is missing on compute shader entry point".to_string(),
1077 labels: vec![(
1078 span,
1079 "must be paired with a `@workgroup_size` attribute".into(),
1080 )],
1081 notes: vec![],
1082 },
1083 Error::AutoConversion(ref error) => {
1084 let AutoConversionError {
1086 dest_span,
1087 ref dest_type,
1088 source_span,
1089 ref source_type,
1090 } = **error;
1091 ParseError {
1092 message: format!(
1093 "automatic conversions cannot convert `{source_type}` to `{dest_type}`"
1094 ),
1095 labels: vec![
1096 (
1097 dest_span,
1098 format!("a value of type {dest_type} is required here").into(),
1099 ),
1100 (
1101 source_span,
1102 format!("this expression has type {source_type}").into(),
1103 ),
1104 ],
1105 notes: vec![],
1106 }
1107 }
1108 Error::AutoConversionLeafScalar(ref error) => {
1109 let AutoConversionLeafScalarError {
1110 dest_span,
1111 ref dest_scalar,
1112 source_span,
1113 ref source_type,
1114 } = **error;
1115 ParseError {
1116 message: format!(
1117 "automatic conversions cannot convert elements of `{source_type}` to `{dest_scalar}`"
1118 ),
1119 labels: vec![
1120 (
1121 dest_span,
1122 format!(
1123 "a value with elements of type {dest_scalar} is required here"
1124 )
1125 .into(),
1126 ),
1127 (
1128 source_span,
1129 format!("this expression has type {source_type}").into(),
1130 ),
1131 ],
1132 notes: vec![],
1133 }
1134 }
1135 Error::ConcretizationFailed(ref error) => {
1136 let ConcretizationFailedError {
1137 expr_span,
1138 ref expr_type,
1139 ref scalar,
1140 ref inner,
1141 } = **error;
1142 ParseError {
1143 message: format!("failed to convert expression to a concrete type: {inner}"),
1144 labels: vec![(
1145 expr_span,
1146 format!("this expression has type {expr_type}").into(),
1147 )],
1148 notes: vec![format!(
1149 "the expression should have been converted to have {} scalar type",
1150 scalar
1151 )],
1152 }
1153 }
1154 Error::ExceededLimitForNestedBraces { span, limit } => ParseError {
1155 message: "brace nesting limit reached".into(),
1156 labels: vec![(span, "limit reached at this brace".into())],
1157 notes: vec![format!("nesting limit is currently set to {limit}")],
1158 },
1159 Error::PipelineConstantIDValue(span) => ParseError {
1160 message: "pipeline constant ID must be between 0 and 65535 inclusive".to_string(),
1161 labels: vec![(span, "must be between 0 and 65535 inclusive".into())],
1162 notes: vec![],
1163 },
1164 Error::NotBool(span) => ParseError {
1165 message: "must be a const-expression that resolves to a `bool`".to_string(),
1166 labels: vec![(span, "must resolve to `bool`".into())],
1167 notes: vec![],
1168 },
1169 Error::ConstAssertFailed(span) => ParseError {
1170 message: "`const_assert` failure".to_string(),
1171 labels: vec![(span, "evaluates to `false`".into())],
1172 notes: vec![],
1173 },
1174 Error::DirectiveAfterFirstGlobalDecl { directive_span } => ParseError {
1175 message: "expected global declaration, but found a global directive".into(),
1176 labels: vec![(
1177 directive_span,
1178 "written after first global declaration".into(),
1179 )],
1180 notes: vec![concat!(
1181 "global directives are only allowed before global declarations; ",
1182 "maybe hoist this closer to the top of the shader module?"
1183 )
1184 .into()],
1185 },
1186 Error::EnableExtensionNotYetImplemented { kind, span } => ParseError {
1187 message: format!(
1188 "the `{}` enable-extension is not yet supported",
1189 EnableExtension::Unimplemented(kind).to_ident()
1190 ),
1191 labels: vec![(
1192 span,
1193 concat!(
1194 "this enable-extension specifies standard functionality ",
1195 "which is not yet implemented in Naga"
1196 )
1197 .into(),
1198 )],
1199 notes: vec![format!(
1200 concat!(
1201 "Let Naga maintainers know that you ran into this at ",
1202 "<https://github.com/gfx-rs/wgpu/issues/{}>, ",
1203 "so they can prioritize it!"
1204 ),
1205 kind.tracking_issue_num()
1206 )],
1207 },
1208 Error::EnableExtensionNotEnabled { kind, span } => ParseError {
1209 message: format!("the `{}` enable extension is not enabled", kind.to_ident()),
1210 labels: vec![(
1211 span,
1212 format!(
1213 concat!(
1214 "the `{}` \"Enable Extension\" is needed for this functionality, ",
1215 "but it is not currently enabled."
1216 ),
1217 kind.to_ident()
1218 )
1219 .into(),
1220 )],
1221 notes: if let EnableExtension::Unimplemented(kind) = kind {
1222 vec![format!(
1223 concat!(
1224 "This \"Enable Extension\" is not yet implemented. ",
1225 "Let Naga maintainers know that you ran into this at ",
1226 "<https://github.com/gfx-rs/wgpu/issues/{}>, ",
1227 "so they can prioritize it!"
1228 ),
1229 kind.tracking_issue_num()
1230 )]
1231 } else {
1232 vec![
1233 format!(
1234 "You can enable this extension by adding `enable {};` at the top of the shader, before any other items.",
1235 kind.to_ident()
1236 ),
1237 ]
1238 },
1239 },
1240 Error::LanguageExtensionNotYetImplemented { kind, span } => ParseError {
1241 message: format!(
1242 "the `{}` language extension is not yet supported",
1243 LanguageExtension::Unimplemented(kind).to_ident()
1244 ),
1245 labels: vec![(span, "".into())],
1246 notes: vec![format!(
1247 concat!(
1248 "Let Naga maintainers know that you ran into this at ",
1249 "<https://github.com/gfx-rs/wgpu/issues/{}>, ",
1250 "so they can prioritize it!"
1251 ),
1252 kind.tracking_issue_num()
1253 )],
1254 },
1255 Error::DiagnosticInvalidSeverity {
1256 severity_control_name_span,
1257 } => ParseError {
1258 message: "invalid `diagnostic(…)` severity".into(),
1259 labels: vec![(
1260 severity_control_name_span,
1261 "not a valid severity level".into(),
1262 )],
1263 notes: vec![concat!(
1264 "See available severities at ",
1265 "<https://www.w3.org/TR/WGSL/#diagnostic-severity>."
1266 )
1267 .into()],
1268 },
1269 Error::DiagnosticDuplicateTriggeringRule(ConflictingDiagnosticRuleError {
1270 triggering_rule_spans,
1271 }) => {
1272 let [first_span, second_span] = triggering_rule_spans;
1273 ParseError {
1274 message: "found conflicting `diagnostic(…)` rule(s)".into(),
1275 labels: vec![
1276 (first_span, "first rule".into()),
1277 (second_span, "second rule".into()),
1278 ],
1279 notes: vec![
1280 concat!(
1281 "Multiple `diagnostic(…)` rules with the same rule name ",
1282 "conflict unless they are directives and the severity is the same.",
1283 )
1284 .into(),
1285 "You should delete the rule you don't want.".into(),
1286 ],
1287 }
1288 }
1289 Error::DiagnosticAttributeNotYetImplementedAtParseSite {
1290 site_name_plural,
1291 ref spans,
1292 } => ParseError {
1293 message: "`@diagnostic(…)` attribute(s) not yet implemented".into(),
1294 labels: {
1295 let mut spans = spans.iter().cloned();
1296 let first = spans
1297 .next()
1298 .map(|span| {
1299 (
1300 span,
1301 format!("can't use this on {site_name_plural} (yet)").into(),
1302 )
1303 })
1304 .expect("internal error: diag. attr. rejection on empty map");
1305 core::iter::once(first)
1306 .chain(spans.map(|span| (span, "".into())))
1307 .collect()
1308 },
1309 notes: vec![format!(concat!(
1310 "Let Naga maintainers know that you ran into this at ",
1311 "<https://github.com/gfx-rs/wgpu/issues/5320>, ",
1312 "so they can prioritize it!"
1313 ))],
1314 },
1315 Error::DiagnosticAttributeNotSupported { on_what, ref spans } => {
1316 let intended_diagnostic_directive = match on_what {
1319 DiagnosticAttributeNotSupportedPosition::SemicolonInModulePosition => true,
1320 DiagnosticAttributeNotSupportedPosition::Other { .. } => false,
1321 };
1322 let on_what_plural = match on_what {
1323 DiagnosticAttributeNotSupportedPosition::SemicolonInModulePosition => {
1324 "semicolons"
1325 }
1326 DiagnosticAttributeNotSupportedPosition::Other { display_plural } => {
1327 display_plural
1328 }
1329 };
1330 ParseError {
1331 message: format!(
1332 "`@diagnostic(…)` attribute(s) on {on_what_plural} are not supported",
1333 ),
1334 labels: spans
1335 .iter()
1336 .cloned()
1337 .map(|span| (span, "".into()))
1338 .collect(),
1339 notes: vec![
1340 concat!(
1341 "`@diagnostic(…)` attributes are only permitted on `fn`s, ",
1342 "some statements, and `switch`/`loop` bodies."
1343 )
1344 .into(),
1345 {
1346 if intended_diagnostic_directive {
1347 concat!(
1348 "If you meant to declare a diagnostic filter that ",
1349 "applies to the entire module, move this line to ",
1350 "the top of the file and remove the `@` symbol."
1351 )
1352 .into()
1353 } else {
1354 concat!(
1355 "These attributes are well-formed, ",
1356 "you likely just need to move them."
1357 )
1358 .into()
1359 }
1360 },
1361 ],
1362 }
1363 }
1364 Error::SelectUnexpectedArgumentType { arg_span, ref arg_type } => ParseError {
1365 message: "unexpected argument type for `select` call".into(),
1366 labels: vec![(arg_span, format!("this value of type {arg_type}").into())],
1367 notes: vec!["expected a scalar or a `vecN` of scalars".into()],
1368 },
1369 Error::SelectRejectAndAcceptHaveNoCommonType {
1370 reject_span,
1371 ref reject_type,
1372 accept_span,
1373 ref accept_type,
1374 } => ParseError {
1375 message: "type mismatch for reject and accept values in `select` call".into(),
1376 labels: vec![
1377 (reject_span, format!("reject value of type {reject_type}").into()),
1378 (accept_span, format!("accept value of type {accept_type}").into()),
1379 ],
1380 notes: vec![],
1381 },
1382 Error::ExpectedGlobalVariable { name_span } => ParseError {
1383 message: "expected global variable".to_string(),
1384 labels: vec![(name_span, "variable used here".into())],
1385 notes: vec![],
1386 },
1387 Error::StructMemberTooLarge { member_name_span } => ParseError {
1388 message: "struct member is too large".into(),
1389 labels: vec![(member_name_span, "this member exceeds the maximum size".into())],
1390 notes: vec![format!(
1391 "the maximum size is {} bytes",
1392 crate::valid::MAX_TYPE_SIZE
1393 )],
1394 },
1395 Error::TypeTooLarge { span } => ParseError {
1396 message: "type is too large".into(),
1397 labels: vec![(span, "this type exceeds the maximum size".into())],
1398 notes: vec![format!(
1399 "the maximum size is {} bytes",
1400 crate::valid::MAX_TYPE_SIZE
1401 )],
1402 },
1403 }
1404 }
1405}