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