Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

gleam / compiler-core / src / parse / error.rs
36 kB 880 lines
1use crate::ast::{SrcSpan, TypeAst}; 2use crate::diagnostic::{ExtraLabel, Label}; 3use crate::error::wrap; 4use crate::parse::Token; 5use ecow::EcoString; 6use itertools::Itertools; 7 8#[derive(Debug, PartialEq, Eq, Clone, Copy)] 9pub struct LexicalError { 10 pub error: LexicalErrorType, 11 pub location: SrcSpan, 12} 13 14#[derive(Debug, PartialEq, Eq, Clone, Copy)] 15pub enum InvalidUnicodeEscapeError { 16 /// Expected '{' 17 MissingOpeningBrace, 18 /// Expected hex digit or '}' 19 ExpectedHexDigitOrCloseBrace, 20 /// Expected between 1 and 6 hex digits 21 InvalidNumberOfHexDigits, 22 /// Invalid Unicode codepoint 23 InvalidCodepoint, 24} 25 26#[derive(Debug, PartialEq, Eq, Clone, Copy)] 27pub enum LexicalErrorType { 28 // String contains an unescaped slash 29 BadStringEscape, 30 // \u{...} escape sequence is invalid 31 InvalidUnicodeEscape(InvalidUnicodeEscapeError), 32 // 0x012 , 2 is out of radix 33 DigitOutOfRadix, 34 // 1_000_ is not allowed 35 NumTrailingUnderscore, 36 // 0x, 0b, 0o without a value 37 RadixIntNoValue, 38 // // 1.0e, for example, where there is no exponent 39 MissingExponent, 40 // Unterminated string literal 41 UnexpectedStringEnd, 42 UnrecognizedToken { 43 tok: char, 44 }, 45 InvalidTripleEqual, 46 /// A character was encountered that visually looks like a correct 47 /// character, but in reality it's some other unicode characters. 48 /// For example, a non-breaking-space instead of a regular space. 49 VisuallySimilarInvalidCharacter { 50 name: &'static str, 51 correct: &'static str, 52 }, 53} 54 55#[derive(Debug, Clone, PartialEq, Eq)] 56pub struct ParseError { 57 pub error: ParseErrorType, 58 pub location: SrcSpan, 59} 60 61#[derive(Debug, Clone, PartialEq, Eq)] 62pub enum ParseErrorType { 63 ExpectedEqual, // expect "=" 64 ExpectedExpr, // after "->" in a case clause 65 ExpectedName, // any token used when a Name was expected 66 ExpectedPattern, // after ':' where a pattern is expected 67 ExpectedType, // after ':' or '->' where a type annotation is expected 68 ExpectedUpName, // any token used when a UpName was expected 69 ExpectedValue, // no value after "=" 70 ExpectedDefinition, // after attributes 71 ExpectedDeprecationMessage, // after "deprecated" 72 ExpectedFunctionDefinition, // after function-only attributes 73 ExpectedTargetName, // after "@target(" 74 ExprLparStart, // it seems "(" was used to start an expression 75 ExtraSeparator, // #(1,,) <- the 2nd comma is an extra separator 76 IncorrectName, // UpName or DiscardName used when Name was expected 77 IncorrectUpName, // Name or DiscardName used when UpName was expected 78 InvalidBitArraySegment, // <<7:hello>> `hello` is an invalid BitArray segment 79 InvalidBitArrayUnit, // in <<1:unit(x)>> x must be 1 <= x <= 256 80 InvalidTailPattern, // only name and _name are allowed after ".." in list pattern 81 InvalidTupleAccess, // only positive int literals for tuple access 82 LexError { 83 error: LexicalError, 84 }, 85 NestedBitArrayPattern, // <<<<1>>, 2>>, <<1>> is not allowed in there 86 NoLetBinding, // Bindings and rebinds always require let and must always bind to a value. 87 NoValueAfterEqual, // = <something other than a value> 88 NotConstType, // :fn(), name, _ are not valid const types 89 OpNakedRight, // Operator with no value to the right 90 OpaqueTypeAlias, // Type aliases cannot be opaque 91 TooManyArgHoles, // a function call can have at most 1 arg hole 92 DuplicateAttribute, // an attribute was used more than once 93 UnknownAttribute, // an attribute was used that is not known 94 UnknownTarget, // an unknown target was used 95 ListSpreadWithoutElements, // Pointless spread: `[..xs]` 96 ListSpreadFollowedByElements, // trying to append something after the spread: `[..xs, x]` 97 ListSpreadWithAnotherSpread { 98 first_spread_location: SrcSpan, 99 }, // trying to use multiple spreads: `[..xs, ..ys]` 100 UnexpectedLabel, // argument labels were provided, but are not supported in this context 101 UnexpectedEof, 102 UnexpectedReservedWord, // reserved word used when a name was expected 103 UnexpectedToken { 104 token: Token, 105 expected: Vec<EcoString>, 106 hint: Option<EcoString>, 107 }, 108 UnexpectedFunction, // a function was used called outside of another function 109 // A variable was assigned or discarded on the left hand side of a <> pattern 110 ConcatPatternVariableLeftHandSide, 111 ListSpreadWithoutTail, // let x = [1, ..] 112 ExpectedFunctionBody, // let x = fn() 113 RedundantInternalAttribute, // for a private definition marked as internal 114 InvalidModuleTypePattern, // for patterns that have a dot like: `name.thing` 115 ListPatternSpreadFollowedByElements, // When there is a pattern after a spread [..rest, pattern] 116 ExpectedRecordConstructor { 117 name: EcoString, 118 public: bool, 119 opaque: bool, 120 field: EcoString, 121 field_type: Option<Box<TypeAst>>, 122 }, 123 CallInClauseGuard, // case x { _ if f() -> 1 } 124 IfExpression, 125 ConstantRecordConstructorNoArguments, // const x = Record() 126 TypeDefinitionNoArguments, // pub type Wibble() { ... } 127 UnknownAttributeRecordVariant, // an attribute was used that is not know for a custom type variant 128 // a Python-like import was written, such as `import gleam.io`, instead of `import gleam/io` 129 IncorrectImportModuleSeparator { 130 module: EcoString, 131 item: EcoString, 132 }, 133 /// This can happen when there's an empty block in a case clause guard. 134 /// For example: `_ if a == {}` 135 EmptyGuardBlock, 136 // When the use tries to define a constant inside a function 137 ConstantInsideFunction, 138 FunctionDefinitionAngleGenerics, // fn something<T>() { ... } 139 // let a: List<String> = [] 140 TypeUsageAngleGenerics { 141 module: Option<EcoString>, 142 name: EcoString, 143 arguments: Vec<TypeAst>, 144 }, 145 // type Something<T> { 146 TypeDefinitionAngleGenerics { 147 name: EcoString, 148 arguments: Vec<EcoString>, 149 }, 150} 151 152pub(crate) struct ParseErrorDetails { 153 pub text: String, 154 pub label_text: EcoString, 155 pub extra_labels: Vec<ExtraLabel>, 156 pub hint: Option<String>, 157} 158 159impl ParseErrorType { 160 pub(crate) fn details(&self) -> ParseErrorDetails { 161 match self { 162 ParseErrorType::ExpectedEqual => ParseErrorDetails { 163 text: "".into(), 164 hint: None, 165 label_text: "I was expecting a '=' after this".into(), 166 extra_labels: vec![], 167 }, 168 169 ParseErrorType::ExpectedExpr => ParseErrorDetails { 170 text: "".into(), 171 hint: None, 172 label_text: "I was expecting an expression after this".into(), 173 extra_labels: vec![], 174 }, 175 176 ParseErrorType::ExpectedName => ParseErrorDetails { 177 text: "".into(), 178 hint: None, 179 label_text: "I was expecting a name here".into(), 180 extra_labels: vec![], 181 }, 182 183 ParseErrorType::ExpectedPattern => ParseErrorDetails { 184 text: "".into(), 185 hint: None, 186 label_text: "I was expecting a pattern after this".into(), 187 extra_labels: vec![], 188 }, 189 190 ParseErrorType::ExpectedType => ParseErrorDetails { 191 text: "See: https://tour.gleam.run/basics/assignments/".into(), 192 hint: None, 193 label_text: "I was expecting a type after this".into(), 194 extra_labels: vec![], 195 }, 196 197 ParseErrorType::ExpectedUpName => ParseErrorDetails { 198 text: "".into(), 199 hint: None, 200 label_text: "I was expecting a type name here".into(), 201 extra_labels: vec![], 202 }, 203 204 ParseErrorType::ExpectedValue => ParseErrorDetails { 205 text: "".into(), 206 hint: None, 207 label_text: "I was expecting a value after this".into(), 208 extra_labels: vec![], 209 }, 210 211 ParseErrorType::ExpectedDefinition => ParseErrorDetails { 212 text: "".into(), 213 hint: None, 214 label_text: "I was expecting a definition after this".into(), 215 extra_labels: vec![], 216 }, 217 218 ParseErrorType::ExpectedDeprecationMessage => ParseErrorDetails { 219 text: "See: https://tour.gleam.run/functions/deprecations/".into(), 220 hint: None, 221 label_text: "A deprecation attribute must have a string message.".into(), 222 extra_labels: vec![], 223 }, 224 225 ParseErrorType::ExpectedFunctionDefinition => ParseErrorDetails { 226 text: "".into(), 227 hint: None, 228 label_text: "I was expecting a function definition after this".into(), 229 extra_labels: vec![], 230 }, 231 232 ParseErrorType::ExpectedTargetName => ParseErrorDetails { 233 text: "Try `erlang`, `javascript`.".into(), 234 hint: None, 235 label_text: "I was expecting a target name after this".into(), 236 extra_labels: vec![], 237 }, 238 239 ParseErrorType::ExtraSeparator => ParseErrorDetails { 240 text: "".into(), 241 hint: Some("Try removing it?".into()), 242 label_text: "This is an extra delimiter".into(), 243 extra_labels: vec![], 244 }, 245 246 ParseErrorType::ExprLparStart => ParseErrorDetails { 247 text: "".into(), 248 hint: Some( 249 "To group expressions in Gleam, use \"{\" and \"}\"; \ 250tuples are created with `#(` and `)`." 251 .into(), 252 ), 253 label_text: "This parenthesis cannot be understood here".into(), 254 extra_labels: vec![], 255 }, 256 257 ParseErrorType::IncorrectName => ParseErrorDetails { 258 text: "".into(), 259 hint: Some(wrap( 260 "Variable and module names start with a lowercase letter, \ 261and can contain a-z, 0-9, or _.", 262 )), 263 label_text: "I'm expecting a lowercase name here".into(), 264 extra_labels: vec![], 265 }, 266 267 ParseErrorType::IncorrectUpName => ParseErrorDetails { 268 text: "".into(), 269 hint: Some(wrap( 270 "Type names start with a uppercase letter, and can \ 271contain a-z, A-Z, or 0-9.", 272 )), 273 label_text: "I'm expecting a type name here".into(), 274 extra_labels: vec![], 275 }, 276 277 ParseErrorType::InvalidBitArraySegment => ParseErrorDetails { 278 text: "See: https://tour.gleam.run/data-types/bit-arrays/".into(), 279 hint: Some(format!( 280 "Valid BitArray segment options are:\n{}", 281 wrap( 282 "bits, bytes, int, float, utf8, utf16, utf32, utf8_codepoint, \ 283utf16_codepoint, utf32_codepoint, signed, unsigned, big, little, native, size, unit.", 284 ) 285 )), 286 label_text: "This is not a valid BitArray segment option".into(), 287 extra_labels: vec![], 288 }, 289 290 ParseErrorType::InvalidBitArrayUnit => ParseErrorDetails { 291 text: "See: https://tour.gleam.run/data-types/bit-arrays/".into(), 292 hint: Some("Unit must be an integer literal >= 1 and <= 256.".into()), 293 label_text: "This is not a valid BitArray unit value".into(), 294 extra_labels: vec![], 295 }, 296 297 ParseErrorType::InvalidTailPattern => ParseErrorDetails { 298 text: "".into(), 299 hint: None, 300 label_text: "This part of a list pattern can only be a name or a discard".into(), 301 extra_labels: vec![], 302 }, 303 304 ParseErrorType::InvalidTupleAccess => ParseErrorDetails { 305 text: "".into(), 306 hint: Some( 307 "Only non negative integer literals like 0, or 1_000 can be used.".into(), 308 ), 309 label_text: "This integer is not valid for tuple access".into(), 310 extra_labels: vec![], 311 }, 312 313 ParseErrorType::LexError { error: lex_err } => { 314 let (label_text, text_lines) = lex_err.to_parse_error_info(); 315 let text = text_lines.join("\n"); 316 ParseErrorDetails { 317 text, 318 hint: None, 319 label_text: label_text.into(), 320 extra_labels: vec![], 321 } 322 } 323 324 ParseErrorType::NestedBitArrayPattern => ParseErrorDetails { 325 text: "".into(), 326 hint: None, 327 label_text: "BitArray patterns cannot be nested".into(), 328 extra_labels: vec![], 329 }, 330 331 ParseErrorType::NotConstType => ParseErrorDetails { 332 text: "See: https://tour.gleam.run/basics/constants/".into(), 333 hint: None, 334 label_text: "This type is not allowed in module constants".into(), 335 extra_labels: vec![], 336 }, 337 338 ParseErrorType::NoLetBinding => ParseErrorDetails { 339 text: "See: https://tour.gleam.run/basics/assignments/".into(), 340 hint: Some("Use let for binding.".into()), 341 label_text: "There must be a 'let' to bind variable to value".into(), 342 extra_labels: vec![], 343 }, 344 345 ParseErrorType::NoValueAfterEqual => ParseErrorDetails { 346 text: "".into(), 347 hint: None, 348 label_text: "I was expecting to see a value after this equals sign".into(), 349 extra_labels: vec![], 350 }, 351 352 ParseErrorType::OpaqueTypeAlias => ParseErrorDetails { 353 text: "See: https://tour.gleam.run/basics/type-aliases/".into(), 354 hint: None, 355 label_text: "Type Aliases cannot be opaque".into(), 356 extra_labels: vec![], 357 }, 358 359 ParseErrorType::OpNakedRight => ParseErrorDetails { 360 text: "".into(), 361 hint: Some("Remove it or put a value after it.".into()), 362 label_text: "This operator has no value on its right side".into(), 363 extra_labels: vec![], 364 }, 365 366 ParseErrorType::TooManyArgHoles => ParseErrorDetails { 367 text: "See: https://tour.gleam.run/functions/functions/".into(), 368 hint: Some("Function calls can have at most one argument hole.".into()), 369 label_text: "There is more than 1 argument hole in this function call".into(), 370 extra_labels: vec![], 371 }, 372 373 ParseErrorType::UnexpectedEof => ParseErrorDetails { 374 text: "".into(), 375 hint: None, 376 label_text: "The module ended unexpectedly".into(), 377 extra_labels: vec![], 378 }, 379 380 ParseErrorType::ListSpreadWithoutElements => ParseErrorDetails { 381 text: "See: https://tour.gleam.run/basics/lists/".into(), 382 hint: Some("Try prepending some elements [1, 2, ..list].".into()), 383 label_text: "This spread does nothing".into(), 384 extra_labels: vec![], 385 }, 386 387 ParseErrorType::ListSpreadWithAnotherSpread { 388 first_spread_location, 389 } => ParseErrorDetails { 390 text: [ 391 "Lists are immutable and singly-linked, so to join two or more lists", 392 "all the elements of the lists would need to be copied into a new list.", 393 "This would be slow, so there is no built-in syntax for it.", 394 ] 395 .join("\n"), 396 hint: None, 397 label_text: "I wasn't expecting a second list here".into(), 398 extra_labels: vec![ExtraLabel { 399 src_info: None, 400 label: Label { 401 text: Some("You're using a list here".into()), 402 span: *first_spread_location, 403 }, 404 }], 405 }, 406 407 ParseErrorType::ListSpreadFollowedByElements => ParseErrorDetails { 408 text: [ 409 "Lists are immutable and singly-linked, so to append items to them", 410 "all the elements of a list would need to be copied into a new list.", 411 "This would be slow, so there is no built-in syntax for it.", 412 ] 413 .join("\n"), 414 hint: Some( 415 "Prepend items to the list and then reverse it once you are done.".into(), 416 ), 417 label_text: "I wasn't expecting elements after this".into(), 418 extra_labels: vec![], 419 }, 420 421 ParseErrorType::ListPatternSpreadFollowedByElements => ParseErrorDetails { 422 text: [ 423 "Lists are immutable and singly-linked, so to match on the end", 424 "of a list would require the whole list to be traversed. This", 425 "would be slow, so there is no built-in syntax for it. Pattern", 426 "match on the start of the list instead.", 427 ] 428 .join("\n"), 429 hint: None, 430 label_text: "I wasn't expecting elements after this".into(), 431 extra_labels: vec![], 432 }, 433 434 ParseErrorType::UnexpectedReservedWord => ParseErrorDetails { 435 text: "".into(), 436 hint: Some("I was expecting to see a name here.".into()), 437 label_text: "This is a reserved word".into(), 438 extra_labels: vec![], 439 }, 440 441 ParseErrorType::UnexpectedLabel => ParseErrorDetails { 442 text: "Please remove the argument label.".into(), 443 hint: None, 444 label_text: "Argument labels are not allowed for anonymous functions".into(), 445 extra_labels: vec![], 446 }, 447 448 ParseErrorType::UnexpectedToken { 449 token, 450 expected, 451 hint, 452 } => { 453 let found = match token { 454 Token::Int { .. } => "an Int".to_string(), 455 Token::Float { .. } => "a Float".to_string(), 456 Token::String { .. } => "a String".to_string(), 457 Token::CommentDoc { .. } => "a comment".to_string(), 458 Token::DiscardName { .. } => "a discard name".to_string(), 459 Token::Name { .. } | Token::UpName { .. } => "a name".to_string(), 460 _ if token.is_reserved_word() => format!("the keyword {token}"), 461 Token::LeftParen 462 | Token::RightParen 463 | Token::LeftSquare 464 | Token::RightSquare 465 | Token::LeftBrace 466 | Token::RightBrace 467 | Token::Plus 468 | Token::Minus 469 | Token::Star 470 | Token::Slash 471 | Token::Less 472 | Token::Greater 473 | Token::LessEqual 474 | Token::GreaterEqual 475 | Token::Percent 476 | Token::PlusDot 477 | Token::MinusDot 478 | Token::StarDot 479 | Token::SlashDot 480 | Token::LessDot 481 | Token::GreaterDot 482 | Token::LessEqualDot 483 | Token::GreaterEqualDot 484 | Token::Concatenate 485 | Token::Colon 486 | Token::Comma 487 | Token::Hash 488 | Token::Bang 489 | Token::Equal 490 | Token::EqualEqual 491 | Token::NotEqual 492 | Token::Vbar 493 | Token::VbarVbar 494 | Token::AmperAmper 495 | Token::LtLt 496 | Token::GtGt 497 | Token::Pipe 498 | Token::Dot 499 | Token::RArrow 500 | Token::LArrow 501 | Token::DotDot 502 | Token::At 503 | Token::EndOfFile 504 | Token::CommentNormal 505 | Token::CommentModule 506 | Token::NewLine 507 | Token::As 508 | Token::Assert 509 | Token::Auto 510 | Token::Case 511 | Token::Const 512 | Token::Delegate 513 | Token::Derive 514 | Token::Echo 515 | Token::Else 516 | Token::Fn 517 | Token::If 518 | Token::Implement 519 | Token::Import 520 | Token::Let 521 | Token::Macro 522 | Token::Opaque 523 | Token::Panic 524 | Token::Pub 525 | Token::Test 526 | Token::Todo 527 | Token::Type 528 | Token::Use => token.to_string(), 529 }; 530 531 let mut messages = std::iter::once(format!("Found {found}, expected one of: ")) 532 .chain(expected.iter().map(|s| format!("- {s}"))); 533 534 ParseErrorDetails { 535 text: messages.join("\n"), 536 hint: hint.as_ref().map(|hint| hint.to_string()), 537 label_text: "I was not expecting this".into(), 538 extra_labels: vec![], 539 } 540 } 541 542 ParseErrorType::ConcatPatternVariableLeftHandSide => ParseErrorDetails { 543 text: [ 544 "We can't tell what size this prefix should be so we don't know", 545 "how to handle this pattern.", 546 "", 547 "If you want to match one character consider using `pop_grapheme`", 548 "from the stdlib's `gleam/string` module.", 549 ] 550 .join("\n"), 551 hint: None, 552 label_text: "This must be a string literal".into(), 553 extra_labels: vec![], 554 }, 555 556 ParseErrorType::UnexpectedFunction => ParseErrorDetails { 557 text: "".into(), 558 hint: None, 559 label_text: "Functions can only be called within other functions".into(), 560 extra_labels: vec![], 561 }, 562 563 ParseErrorType::ListSpreadWithoutTail => ParseErrorDetails { 564 text: "If a list expression has a spread then a tail must also be given.".into(), 565 hint: None, 566 label_text: "I was expecting a value after this spread".into(), 567 extra_labels: vec![], 568 }, 569 570 ParseErrorType::UnknownAttribute => ParseErrorDetails { 571 text: "".into(), 572 hint: Some("Try `deprecated`, `external` or `internal` instead.".into()), 573 label_text: "I don't recognise this attribute".into(), 574 extra_labels: vec![], 575 }, 576 577 ParseErrorType::DuplicateAttribute => ParseErrorDetails { 578 text: "This attribute has already been given.".into(), 579 hint: None, 580 label_text: "Duplicate attribute".into(), 581 extra_labels: vec![], 582 }, 583 584 ParseErrorType::UnknownTarget => ParseErrorDetails { 585 text: "Try `erlang`, `javascript`.".into(), 586 hint: None, 587 label_text: "I don't recognise this target".into(), 588 extra_labels: vec![], 589 }, 590 591 ParseErrorType::ExpectedFunctionBody => ParseErrorDetails { 592 text: "".into(), 593 hint: None, 594 label_text: "This function does not have a body".into(), 595 extra_labels: vec![], 596 }, 597 598 ParseErrorType::RedundantInternalAttribute => ParseErrorDetails { 599 text: "Only a public definition can be annotated as internal.".into(), 600 hint: Some("Remove the `@internal` annotation.".into()), 601 label_text: "Redundant internal attribute".into(), 602 extra_labels: vec![], 603 }, 604 605 ParseErrorType::InvalidModuleTypePattern => ParseErrorDetails { 606 text: [ 607 "I'm expecting a pattern here,", 608 "or a variable to bind a value to, etc.", 609 ] 610 .join("\n"), 611 hint: Some( 612 "A pattern can be a constructor name, a literal value 613See: https://tour.gleam.run/flow-control/case-expressions/" 614 .into(), 615 ), 616 label_text: "Invalid pattern".into(), 617 extra_labels: vec![], 618 }, 619 620 ParseErrorType::ExpectedRecordConstructor { 621 name, 622 public, 623 opaque, 624 field, 625 field_type, 626 } => { 627 let (accessor, opaque) = match *public { 628 true if *opaque => ("pub ", "opaque "), 629 true => ("pub ", ""), 630 false => ("", ""), 631 }; 632 633 let mut annotation = EcoString::new(); 634 match field_type { 635 Some(t) => t.print(&mut annotation), 636 None => annotation.push_str("Type"), 637 }; 638 639 ParseErrorDetails { 640 text: [ 641 "Each custom type variant must have a constructor:\n".into(), 642 format!("{accessor}{opaque}type {name} {{"), 643 format!(" {name}("), 644 format!(" {field}: {annotation},"), 645 " )".into(), 646 "}".into(), 647 ] 648 .join("\n"), 649 hint: None, 650 label_text: "I was not expecting this".into(), 651 extra_labels: vec![], 652 } 653 } 654 655 ParseErrorType::CallInClauseGuard => ParseErrorDetails { 656 text: "Functions cannot be called in clause guards.".into(), 657 hint: None, 658 label_text: "Unsupported expression".into(), 659 extra_labels: vec![], 660 }, 661 662 ParseErrorType::IfExpression => ParseErrorDetails { 663 text: [ 664 "If you want to write a conditional expression you can use a `case`:", 665 "", 666 " case condition {", 667 " True -> todo", 668 " False -> todo", 669 " }", 670 "", 671 "See: https://tour.gleam.run/flow-control/case-expressions/", 672 ] 673 .join("\n"), 674 hint: None, 675 label_text: "Gleam doesn't have if expressions".into(), 676 extra_labels: vec![], 677 }, 678 679 ParseErrorType::ConstantRecordConstructorNoArguments => ParseErrorDetails { 680 text: "A record must be passed arguments when constructed.".into(), 681 hint: None, 682 label_text: "I was expecting arguments here".into(), 683 extra_labels: vec![], 684 }, 685 686 ParseErrorType::TypeDefinitionNoArguments => ParseErrorDetails { 687 text: "A generic type must have at least a generic parameter.".into(), 688 hint: Some("If a type is not generic you should omit the `()`.".into()), 689 label_text: "I was expecting generic parameters here".into(), 690 extra_labels: vec![], 691 }, 692 693 ParseErrorType::UnknownAttributeRecordVariant => ParseErrorDetails { 694 text: "".into(), 695 hint: Some("Did you mean `@deprecated`?".into()), 696 label_text: "This attribute cannot be used on a variant.".into(), 697 extra_labels: vec![], 698 }, 699 700 ParseErrorType::IncorrectImportModuleSeparator { module, item } => ParseErrorDetails { 701 text: [ 702 "Perhaps you meant one of:".into(), 703 "".into(), 704 format!(" import {module}/{item}"), 705 format!(" import {module}.{{item}}"), 706 ] 707 .join("\n"), 708 hint: None, 709 label_text: "I was expecting either `/` or `.{` here.".into(), 710 extra_labels: vec![], 711 }, 712 713 ParseErrorType::EmptyGuardBlock => ParseErrorDetails { 714 text: "".into(), 715 hint: None, 716 label_text: "A clause guard block cannot be empty".into(), 717 extra_labels: vec![], 718 }, 719 720 ParseErrorType::ConstantInsideFunction => ParseErrorDetails { 721 text: wrap( 722 "All variables are immutable in Gleam, so constants inside \ 723functions are not necessary.", 724 ), 725 hint: Some( 726 "Either move this into the global scope or use `let` binding instead.".into(), 727 ), 728 label_text: "Constants are not allowed inside functions".into(), 729 extra_labels: vec![], 730 }, 731 732 ParseErrorType::FunctionDefinitionAngleGenerics => ParseErrorDetails { 733 text: "\ 734Generic function type variables do not need to be predeclared like they 735would be in some other languages, instead they are written with lowercase 736names. 737 738 fn example(argument: generic) -> generic 739 740See: https://tour.gleam.run/functions/generic-functions/" 741 .into(), 742 hint: None, 743 label_text: "I was expecting `(` here.".into(), 744 extra_labels: vec![], 745 }, 746 747 ParseErrorType::TypeUsageAngleGenerics { 748 module, 749 name, 750 arguments, 751 } => { 752 let type_arguments = arguments 753 .iter() 754 .map(|argument| { 755 let mut argument_string = EcoString::new(); 756 argument.print(&mut argument_string); 757 argument_string 758 }) 759 .join(", "); 760 let replacement_type = match module { 761 Some(module) => format!("{module}.{name}({type_arguments})"), 762 None => format!("{name}({type_arguments})"), 763 }; 764 765 ParseErrorDetails { 766 text: format!( 767 "\ 768Type parameters use lowercase names and are surrounded by parentheses. 769 770 {replacement_type} 771 772See: https://tour.gleam.run/data-types/generic-custom-types/" 773 ), 774 hint: None, 775 label_text: "I was expecting `(` here.".into(), 776 extra_labels: vec![], 777 } 778 } 779 780 ParseErrorType::TypeDefinitionAngleGenerics { name, arguments } => { 781 let comma_separated_arguments = arguments.join(", "); 782 783 ParseErrorDetails { 784 text: format!( 785 "\ 786Type parameters use lowercase names and are surrounded by parentheses. 787 788 type {name}({comma_separated_arguments}) {{ 789 790See: https://tour.gleam.run/data-types/generic-custom-types/" 791 ), 792 hint: None, 793 label_text: "I was expecting `(` here.".into(), 794 extra_labels: vec![], 795 } 796 } 797 } 798 } 799} 800 801impl LexicalError { 802 pub fn to_parse_error_info(&self) -> (&'static str, Vec<String>) { 803 match &self.error { 804 LexicalErrorType::BadStringEscape => ( 805 "I don't understand this escape code", 806 vec![ 807 "Hint: Add another backslash before it.".into(), 808 "See: https://tour.gleam.run/basics/strings".into(), 809 ], 810 ), 811 LexicalErrorType::DigitOutOfRadix => { 812 ("This digit is too big for the specified radix", vec![]) 813 } 814 LexicalErrorType::NumTrailingUnderscore => ( 815 "Numbers cannot have a trailing underscore", 816 vec!["Hint: remove it.".into()], 817 ), 818 LexicalErrorType::RadixIntNoValue => ("This integer has no value", vec![]), 819 LexicalErrorType::MissingExponent => ( 820 "This float is missing an exponent", 821 vec!["Hint: Add an exponent or remove the trailing `e`".into()], 822 ), 823 LexicalErrorType::UnexpectedStringEnd => { 824 ("The string starting here was left open", vec![]) 825 } 826 LexicalErrorType::UnrecognizedToken { tok } if *tok == ';' => ( 827 "Remove this semicolon", 828 vec![ 829 "Hint: Semicolons used to be whitespace and did nothing.".into(), 830 "You can safely remove them without your program changing.".into(), 831 ], 832 ), 833 LexicalErrorType::UnrecognizedToken { tok } if *tok == '\'' => ( 834 "Unexpected single quote", 835 vec!["Hint: Strings are written with double quotes.".into()], 836 ), 837 LexicalErrorType::UnrecognizedToken { .. } => ( 838 "I can't figure out what to do with this character", 839 vec!["Hint: Is it a typo?".into()], 840 ), 841 LexicalErrorType::InvalidUnicodeEscape( 842 InvalidUnicodeEscapeError::MissingOpeningBrace, 843 ) => ( 844 "Expected '{' in Unicode escape sequence", 845 vec!["Hint: Add it.".into()], 846 ), 847 LexicalErrorType::InvalidUnicodeEscape( 848 InvalidUnicodeEscapeError::ExpectedHexDigitOrCloseBrace, 849 ) => ( 850 "Expected hex digit or '}' in Unicode escape sequence", 851 vec![ 852 "Hint: Hex digits are digits from 0 to 9 and letters from a to f or A to F." 853 .into(), 854 ], 855 ), 856 LexicalErrorType::InvalidUnicodeEscape( 857 InvalidUnicodeEscapeError::InvalidNumberOfHexDigits, 858 ) => ( 859 "Expected between 1 and 6 hex digits in Unicode escape sequence", 860 vec![], 861 ), 862 LexicalErrorType::InvalidUnicodeEscape(InvalidUnicodeEscapeError::InvalidCodepoint) => { 863 ("Invalid Unicode codepoint", vec![]) 864 } 865 LexicalErrorType::InvalidTripleEqual => ( 866 "Did you mean `==`?", 867 vec![ 868 "Gleam uses `==` to check for equality between two values.".into(), 869 "See: https://tour.gleam.run/basics/equality".into(), 870 ], 871 ), 872 LexicalErrorType::VisuallySimilarInvalidCharacter { name, correct } => ( 873 "Unexpected character", 874 vec![wrap(&format!( 875 "This looks like ascii {correct}, but it is actually the unicode {name}." 876 ))], 877 ), 878 } 879 } 880}