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