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