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