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