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 / error.rs
123 kB 3265 lines
1#![allow(clippy::unwrap_used, clippy::expect_used)] 2use crate::build::{Runtime, Target}; 3use crate::diagnostic::{Diagnostic, Label, Location}; 4use crate::type_::error::RecordVariants; 5use crate::type_::error::{MissingAnnotation, UnknownTypeHint}; 6use crate::type_::{error::PatternMatchKind, FieldAccessUsage}; 7use crate::{ast::BinOp, parse::error::ParseErrorType, type_::Type}; 8use crate::{ 9 bit_array, 10 diagnostic::Level, 11 javascript, 12 type_::{pretty::Printer, UnifyErrorSituation}, 13}; 14use ecow::EcoString; 15use hexpm::version::pubgrub_report::{DefaultStringReporter, Reporter}; 16use hexpm::version::ResolutionError; 17use itertools::Itertools; 18use std::env; 19use std::fmt::Debug; 20use std::path::PathBuf; 21use termcolor::Buffer; 22use thiserror::Error; 23 24use camino::{Utf8Path, Utf8PathBuf}; 25 26pub type Name = EcoString; 27 28pub type Result<Ok, Err = Error> = std::result::Result<Ok, Err>; 29 30macro_rules! wrap_format { 31 ($($tts:tt)*) => { 32 wrap(&format!($($tts)*)) 33 } 34} 35 36#[derive(Debug, Clone, Eq, PartialEq)] 37pub struct UnknownImportDetails { 38 pub module: Name, 39 pub location: crate::ast::SrcSpan, 40 pub path: Utf8PathBuf, 41 pub src: EcoString, 42 pub modules: Vec<EcoString>, 43} 44 45#[derive(Debug, Eq, PartialEq, Error, Clone)] 46pub enum Error { 47 #[error("failed to parse Gleam source code")] 48 Parse { 49 path: Utf8PathBuf, 50 src: EcoString, 51 error: crate::parse::error::ParseError, 52 }, 53 54 #[error("type checking failed")] 55 Type { 56 path: Utf8PathBuf, 57 src: EcoString, 58 error: crate::type_::Error, 59 }, 60 61 #[error("unknown import {import}")] 62 UnknownImport { 63 import: EcoString, 64 // Boxed to prevent this variant from being overly large 65 details: Box<UnknownImportDetails>, 66 }, 67 68 #[error("duplicate module {module}")] 69 DuplicateModule { 70 module: Name, 71 first: Utf8PathBuf, 72 second: Utf8PathBuf, 73 }, 74 75 #[error("duplicate source file {file}")] 76 DuplicateSourceFile { file: String }, 77 78 #[error("cyclical module imports")] 79 ImportCycle { modules: Vec<EcoString> }, 80 81 #[error("cyclical package dependencies")] 82 PackageCycle { packages: Vec<EcoString> }, 83 84 #[error("file operation failed")] 85 FileIo { 86 kind: FileKind, 87 action: FileIoAction, 88 path: Utf8PathBuf, 89 err: Option<String>, 90 }, 91 92 #[error("Non Utf-8 Path: {path}")] 93 NonUtf8Path { path: PathBuf }, 94 95 #[error("{error}")] 96 GitInitialization { error: String }, 97 98 #[error("io operation failed")] 99 StandardIo { 100 action: StandardIoAction, 101 err: Option<std::io::ErrorKind>, 102 }, 103 104 #[error("source code incorrectly formatted")] 105 Format { problem_files: Vec<Unformatted> }, 106 107 #[error("Hex error: {0}")] 108 Hex(String), 109 110 #[error("{error}")] 111 ExpandTar { error: String }, 112 113 #[error("{err}")] 114 AddTar { path: Utf8PathBuf, err: String }, 115 116 #[error("{0}")] 117 TarFinish(String), 118 119 #[error("{0}")] 120 Gzip(String), 121 122 #[error("shell program `{program}` not found")] 123 ShellProgramNotFound { program: String }, 124 125 #[error("shell program `{program}` failed")] 126 ShellCommand { 127 program: String, 128 err: Option<std::io::ErrorKind>, 129 }, 130 131 #[error("{name} is not a valid project name")] 132 InvalidProjectName { 133 name: String, 134 reason: InvalidProjectNameReason, 135 }, 136 137 #[error("{module} is not a valid module name")] 138 InvalidModuleName { module: String }, 139 140 #[error("{module} is not module")] 141 ModuleDoesNotExist { 142 module: EcoString, 143 suggestion: Option<EcoString>, 144 }, 145 146 #[error("{module} does not have a main function")] 147 ModuleDoesNotHaveMainFunction { module: EcoString }, 148 149 #[error("{module}'s main function has the wrong arity so it can not be run")] 150 MainFunctionHasWrongArity { module: EcoString, arity: usize }, 151 152 #[error("{module}'s main function does not support the current target")] 153 MainFunctionDoesNotSupportTarget { module: EcoString, target: Target }, 154 155 #[error("{input} is not a valid version. {error}")] 156 InvalidVersionFormat { input: String, error: String }, 157 158 #[error("project root already exists")] 159 ProjectRootAlreadyExist { path: String }, 160 161 #[error("File(s) already exist in {}", 162file_names.iter().map(|x| x.as_str()).join(", "))] 163 OutputFilesAlreadyExist { file_names: Vec<Utf8PathBuf> }, 164 165 #[error("unable to find project root")] 166 UnableToFindProjectRoot { path: String }, 167 168 #[error("gleam.toml version {toml_ver} does not match .app version {app_ver}")] 169 VersionDoesNotMatch { toml_ver: String, app_ver: String }, 170 171 #[error("metadata decoding failed")] 172 MetadataDecodeError { error: Option<String> }, 173 174 #[error("warnings are not permitted")] 175 ForbiddenWarnings { count: usize }, 176 177 #[error("javascript codegen failed")] 178 JavaScript { 179 path: Utf8PathBuf, 180 src: EcoString, 181 error: crate::javascript::Error, 182 }, 183 184 #[error("Invalid runtime for {target} target: {invalid_runtime}")] 185 InvalidRuntime { 186 target: Target, 187 invalid_runtime: Runtime, 188 }, 189 190 #[error("package downloading failed: {error}")] 191 DownloadPackageError { 192 package_name: String, 193 package_version: String, 194 error: String, 195 }, 196 197 #[error("{0}")] 198 Http(String), 199 200 #[error("Git dependencies are currently unsupported")] 201 GitDependencyUnsupported, 202 203 #[error("Failed to create canonical path for package {0}")] 204 DependencyCanonicalizationFailed(String), 205 206 #[error("Dependency tree resolution failed: {0}")] 207 DependencyResolutionFailed(String), 208 209 #[error("The package {0} is listed in dependencies and dev-dependencies")] 210 DuplicateDependency(EcoString), 211 212 #[error("Expected package {expected} at path {path} but found {found} instead")] 213 WrongDependencyProvided { 214 path: Utf8PathBuf, 215 expected: String, 216 found: String, 217 }, 218 219 #[error("The package {package} is provided multiple times, as {source_1} and {source_2}")] 220 ProvidedDependencyConflict { 221 package: String, 222 source_1: String, 223 source_2: String, 224 }, 225 226 #[error("The package was missing required fields for publishing")] 227 MissingHexPublishFields { 228 description_missing: bool, 229 licence_missing: bool, 230 }, 231 232 #[error("Dependency {package:?} has not been published to Hex")] 233 PublishNonHexDependencies { package: String }, 234 235 #[error("The package {package} uses unsupported build tools {build_tools:?}")] 236 UnsupportedBuildTool { 237 package: String, 238 build_tools: Vec<EcoString>, 239 }, 240 241 #[error("Opening docs at {path} failed: {error}")] 242 FailedToOpenDocs { path: Utf8PathBuf, error: String }, 243 244 #[error("The package {package} requires a Gleam version satisfying {required_version} and you are using v{gleam_version}")] 245 IncompatibleCompilerVersion { 246 package: String, 247 required_version: String, 248 gleam_version: String, 249 }, 250 251 #[error("The --javascript-prelude flag must be given when compiling to JavaScript")] 252 JavaScriptPreludeRequired, 253 254 #[error("The modules {unfinished:?} contain todo expressions and so cannot be published")] 255 CannotPublishTodo { unfinished: Vec<EcoString> }, 256 257 #[error("The modules {unfinished:?} contain internal types in their public API so cannot be published")] 258 CannotPublishLeakedInternalType { unfinished: Vec<EcoString> }, 259 260 #[error("Publishing packages to reserve names is not permitted")] 261 HexPackageSquatting, 262} 263 264impl Error { 265 pub fn http<E>(error: E) -> Error 266 where 267 E: std::error::Error, 268 { 269 Self::Http(error.to_string()) 270 } 271 272 pub fn hex<E>(error: E) -> Error 273 where 274 E: std::error::Error, 275 { 276 Self::Hex(error.to_string()) 277 } 278 279 pub fn add_tar<P, E>(path: P, error: E) -> Error 280 where 281 P: AsRef<Utf8Path>, 282 E: std::error::Error, 283 { 284 Self::AddTar { 285 path: path.as_ref().to_path_buf(), 286 err: error.to_string(), 287 } 288 } 289 290 pub fn finish_tar<E>(error: E) -> Error 291 where 292 E: std::error::Error, 293 { 294 Self::TarFinish(error.to_string()) 295 } 296 297 pub fn dependency_resolution_failed(error: ResolutionError) -> Error { 298 Self::DependencyResolutionFailed(match error { 299 ResolutionError::NoSolution(mut derivation_tree) => { 300 derivation_tree.collapse_no_versions(); 301 let report = DefaultStringReporter::report(&derivation_tree); 302 wrap(&report) 303 } 304 305 ResolutionError::ErrorRetrievingDependencies { 306 package, 307 version, 308 source, 309 } => format!( 310 "An error occurred while trying to retrieve dependencies of {package}@{version}: {source}", 311 ), 312 313 ResolutionError::DependencyOnTheEmptySet { 314 package, 315 version, 316 dependent, 317 } => format!( 318 "{package}@{version} has an impossible dependency on {dependent}", 319 ), 320 321 ResolutionError::SelfDependency { package, version } => { 322 format!("{package}@{version} somehow depends on itself.") 323 } 324 325 ResolutionError::ErrorChoosingPackageVersion(err) => { 326 format!("Unable to determine package versions: {err}") 327 } 328 329 ResolutionError::ErrorInShouldCancel(err) => { 330 format!("Dependency resolution was cancelled. {err}") 331 } 332 333 ResolutionError::Failure(err) => format!( 334 "An unrecoverable error happened while solving dependencies: {err}" 335 ), 336 }) 337 } 338 339 pub fn expand_tar<E>(error: E) -> Error 340 where 341 E: std::error::Error, 342 { 343 Self::ExpandTar { 344 error: error.to_string(), 345 } 346 } 347} 348 349impl From<capnp::Error> for Error { 350 fn from(error: capnp::Error) -> Self { 351 Error::MetadataDecodeError { 352 error: Some(error.to_string()), 353 } 354 } 355} 356 357impl From<capnp::NotInSchema> for Error { 358 fn from(error: capnp::NotInSchema) -> Self { 359 Error::MetadataDecodeError { 360 error: Some(error.to_string()), 361 } 362 } 363} 364 365#[derive(Debug, PartialEq, Eq, Clone, Copy)] 366pub enum InvalidProjectNameReason { 367 Format, 368 GleamPrefix, 369 ErlangReservedWord, 370 ErlangStandardLibraryModule, 371 GleamReservedWord, 372 GleamReservedModule, 373} 374 375#[derive(Debug, PartialEq, Eq, Clone, Copy)] 376pub enum StandardIoAction { 377 Read, 378 Write, 379} 380 381impl StandardIoAction { 382 fn text(&self) -> &'static str { 383 match self { 384 StandardIoAction::Read => "read from", 385 StandardIoAction::Write => "write to", 386 } 387 } 388} 389 390#[derive(Debug, PartialEq, Eq, Clone, Copy)] 391pub enum FileIoAction { 392 Link, 393 Open, 394 Copy, 395 Read, 396 Parse, 397 Delete, 398 // Rename, 399 Create, 400 WriteTo, 401 Canonicalise, 402 UpdatePermissions, 403 FindParent, 404 ReadMetadata, 405} 406 407impl FileIoAction { 408 fn text(&self) -> &'static str { 409 match self { 410 FileIoAction::Link => "link", 411 FileIoAction::Open => "open", 412 FileIoAction::Copy => "copy", 413 FileIoAction::Read => "read", 414 FileIoAction::Parse => "parse", 415 FileIoAction::Delete => "delete", 416 // FileIoAction::Rename => "rename", 417 FileIoAction::Create => "create", 418 FileIoAction::WriteTo => "write to", 419 FileIoAction::FindParent => "find the parent of", 420 FileIoAction::Canonicalise => "canonicalise", 421 FileIoAction::UpdatePermissions => "update permissions of", 422 FileIoAction::ReadMetadata => "read metadata of", 423 } 424 } 425} 426 427#[derive(Debug, Clone, Copy, PartialEq, Eq)] 428pub enum FileKind { 429 File, 430 Directory, 431} 432 433impl FileKind { 434 fn text(&self) -> &'static str { 435 match self { 436 FileKind::File => "file", 437 FileKind::Directory => "directory", 438 } 439 } 440} 441 442// https://github.com/rust-lang/rust/blob/03994e498df79aa1f97f7bbcfd52d57c8e865049/compiler/rustc_span/src/edit_distance.rs 443fn edit_distance(a: &str, b: &str, limit: usize) -> Option<usize> { 444 let mut a = &a.chars().collect::<Vec<_>>()[..]; 445 let mut b = &b.chars().collect::<Vec<_>>()[..]; 446 447 if a.len() < b.len() { 448 std::mem::swap(&mut a, &mut b); 449 } 450 451 let min_dist = a.len() - b.len(); 452 // If we know the limit will be exceeded, we can return early. 453 if min_dist > limit { 454 return None; 455 } 456 457 // Strip common prefix. 458 while !b.is_empty() && !a.is_empty() { 459 let (b_first, b_rest) = b.split_last().expect("Failed to split 'b' slice"); 460 let (a_first, a_rest) = a.split_last().expect("Failed to split 'a' slice"); 461 462 if b_first == a_first { 463 a = a_rest; 464 b = b_rest; 465 } else { 466 break; 467 } 468 } 469 470 // If either string is empty, the distance is the length of the other. 471 // We know that `b` is the shorter string, so we don't need to check `a`. 472 if b.is_empty() { 473 return Some(min_dist); 474 } 475 476 let mut prev_prev = vec![usize::MAX; b.len() + 1]; 477 let mut prev = (0..=b.len()).collect::<Vec<_>>(); 478 let mut current = vec![0; b.len() + 1]; 479 480 // row by row 481 for i in 1..=a.len() { 482 if let Some(elem) = current.get_mut(0) { 483 *elem = i; 484 } 485 let a_idx = i - 1; 486 487 // column by column 488 for j in 1..=b.len() { 489 let b_idx = j - 1; 490 491 // There is no cost to substitute a character with itself. 492 let substitution_cost = match (a.get(a_idx), b.get(b_idx)) { 493 (Some(&a_char), Some(&b_char)) => { 494 if a_char == b_char { 495 0 496 } else { 497 1 498 } 499 } 500 _ => panic!("Index out of bounds"), 501 }; 502 503 let insertion = current.get(j - 1).map_or(std::usize::MAX, |&x| x + 1); 504 505 if let Some(value) = current.get_mut(j) { 506 *value = std::cmp::min( 507 // deletion 508 prev.get(j).map_or(std::usize::MAX, |&x| x + 1), 509 std::cmp::min( 510 // insertion 511 insertion, 512 // substitution 513 prev.get(j - 1) 514 .map_or(std::usize::MAX, |&x| x + substitution_cost), 515 ), 516 ); 517 } 518 519 if (i > 1) && (j > 1) { 520 if let (Some(&a_val), Some(&b_val_prev), Some(&a_val_prev), Some(&b_val)) = ( 521 a.get(a_idx), 522 b.get(b_idx - 1), 523 a.get(a_idx - 1), 524 b.get(b_idx), 525 ) { 526 if (a_val == b_val_prev) && (a_val_prev == b_val) { 527 // transposition 528 if let Some(curr) = current.get_mut(j) { 529 if let Some(&prev_prev_val) = prev_prev.get(j - 2) { 530 *curr = std::cmp::min(*curr, prev_prev_val + 1); 531 } 532 } 533 } 534 } 535 } 536 } 537 538 // Rotate the buffers, reusing the memory. 539 [prev_prev, prev, current] = [prev, current, prev_prev]; 540 } 541 542 // `prev` because we already rotated the buffers. 543 let distance = match prev.get(b.len()) { 544 Some(&d) => d, 545 None => std::usize::MAX, 546 }; 547 (distance <= limit).then_some(distance) 548} 549 550fn edit_distance_with_substrings(a: &str, b: &str, limit: usize) -> Option<usize> { 551 let n = a.chars().count(); 552 let m = b.chars().count(); 553 554 // Check one isn't less than half the length of the other. If this is true then there is a 555 // big difference in length. 556 let big_len_diff = (n * 2) < m || (m * 2) < n; 557 let len_diff = if n < m { m - n } else { n - m }; 558 let distance = edit_distance(a, b, limit + len_diff)?; 559 560 // This is the crux, subtracting length difference means exact substring matches will now be 0 561 let score = distance - len_diff; 562 563 // If the score is 0 but the words have different lengths then it's a substring match not a full 564 // word match 565 let score = if score == 0 && len_diff > 0 && !big_len_diff { 566 1 // Exact substring match, but not a total word match so return non-zero 567 } else if !big_len_diff { 568 // Not a big difference in length, discount cost of length difference 569 score + (len_diff + 1) / 2 570 } else { 571 // A big difference in length, add back the difference in length to the score 572 score + len_diff 573 }; 574 575 (score <= limit).then_some(score) 576} 577 578fn did_you_mean(name: &str, options: &[EcoString]) -> Option<String> { 579 // If only one option is given, return that option. 580 // This seems to solve the `unknown_variable_3` test. 581 if options.len() == 1 { 582 return options 583 .first() 584 .map(|option| format!("Did you mean `{}`?", option)); 585 } 586 587 // Check for case-insensitive matches. 588 // This solves the comparison to small and single character terms, 589 // such as the test on `type_vars_must_be_declared`. 590 if let Some(exact_match) = options 591 .iter() 592 .find(|&option| option.eq_ignore_ascii_case(name)) 593 { 594 return Some(format!("Did you mean `{}`?", exact_match)); 595 } 596 597 // Calculate the threshold as one third of the name's length, with a minimum of 1. 598 let threshold = std::cmp::max(name.chars().count() / 3, 1); 599 600 // Filter and sort options based on edit distance. 601 options 602 .iter() 603 .filter(|&option| option != crate::ast::CAPTURE_VARIABLE) 604 .sorted() 605 .filter_map(|option| { 606 edit_distance_with_substrings(option, name, threshold) 607 .map(|distance| (option, distance)) 608 }) 609 .min_by_key(|&(_, distance)| distance) 610 .map(|(option, _)| format!("Did you mean `{}`?", option)) 611} 612 613impl Error { 614 pub fn pretty_string(&self) -> String { 615 self.to_diagnostic().pretty_string() 616 } 617 618 pub fn pretty(&self, buffer: &mut Buffer) { 619 self.to_diagnostic().write(buffer) 620 } 621 622 pub fn to_diagnostic(&self) -> Diagnostic { 623 use crate::type_::Error as TypeError; 624 match self { 625 Error::HexPackageSquatting => { 626 let text = 627 "You appear to be attempting to reserve a name on Hex rather than publishing a 628working package. This is against the Hex terms of service and can result in 629package deletion or account suspension. 630" 631 .into(); 632 633 Diagnostic { 634 title: "Invalid Hex package".into(), 635 text, 636 level: Level::Error, 637 location: None, 638 hint: None, 639 } 640 } 641 642 Error::MetadataDecodeError { error } => { 643 let mut text = "A problem was encountered when decoding the metadata for one \ 644of the Gleam dependency modules." 645 .to_string(); 646 if let Some(error) = error { 647 text.push_str("\nThe error from the decoder library was:\n\n"); 648 text.push_str(error); 649 } 650 651 Diagnostic { 652 title: "Failed to decode module metadata".into(), 653 text, 654 level: Level::Error, 655 location: None, 656 hint: None, 657 } 658 } 659 660 Error::InvalidProjectName { name, reason } => { 661 let text = wrap_format!( 662 "We were not able to create your project as `{}` {} 663 664Please try again with a different project name.", 665 name, 666 match reason { 667 InvalidProjectNameReason::ErlangReservedWord => 668 "is a reserved word in Erlang.", 669 InvalidProjectNameReason::ErlangStandardLibraryModule => 670 "is a standard library module in Erlang.", 671 InvalidProjectNameReason::GleamReservedWord => 672 "is a reserved word in Gleam.", 673 InvalidProjectNameReason::GleamReservedModule => 674 "is a reserved module name in Gleam.", 675 InvalidProjectNameReason::Format => 676 "does not have the correct format. Project names \ 677must start with a lowercase letter and may only contain lowercase letters, \ 678numbers and underscores.", 679 InvalidProjectNameReason::GleamPrefix => 680 "has the reserved prefix `gleam_`. \ 681This prefix is intended for official Gleam packages only.", 682 } 683 ); 684 685 Diagnostic { 686 title: "Invalid project name".into(), 687 text, 688 hint: None, 689 level: Level::Error, 690 location: None, 691 } 692 } 693 694 Error::InvalidModuleName { module } => Diagnostic { 695 title: "Invalid module name".into(), 696 text: format!( 697 "`{module}` is not a valid module name. 698Module names can only contain lowercase letters, underscore, and 699forward slash and must not end with a slash." 700 ), 701 level: Level::Error, 702 location: None, 703 hint: None, 704 }, 705 706 Error::ModuleDoesNotExist { module, suggestion } => { 707 let hint = match suggestion { 708 Some(suggestion) => format!("Did you mean `{suggestion}`?"), 709 None => format!("Try creating the file `src/{module}.gleam`."), 710 }; 711 Diagnostic { 712 title: "Module does not exist".into(), 713 text: format!("Module `{module}` was not found."), 714 level: Level::Error, 715 location: None, 716 hint: Some(hint), 717 } 718 } 719 720 Error::ModuleDoesNotHaveMainFunction { module } => Diagnostic { 721 title: "Module does not have a main function".into(), 722 text: format!( 723 "`{module}` does not have a main function so the module can not be run." 724 ), 725 level: Level::Error, 726 location: None, 727 hint: Some(format!( 728 "Add a public `main` function to \ 729to `src/{module}.gleam`." 730 )), 731 }, 732 733 Error::MainFunctionDoesNotSupportTarget { module, target } => Diagnostic { 734 title: "Target not supported".into(), 735 text: wrap_format!( 736 "`{module}` has a main function, but it does not support the {target} \ 737target, so it cannot be run." 738 ), 739 level: Level::Error, 740 location: None, 741 hint: None, 742 }, 743 744 Error::MainFunctionHasWrongArity { module, arity } => Diagnostic { 745 title: "Main function has wrong arity".into(), 746 text: format!( 747 "`{module}:main` should have an arity of 0 to be run but its arity is {arity}." 748 ), 749 level: Level::Error, 750 location: None, 751 hint: Some("Change the function signature of main to `pub fn main() {}`.".into()), 752 }, 753 754 Error::ProjectRootAlreadyExist { path } => Diagnostic { 755 title: "Project folder already exists".into(), 756 text: format!("Project folder root:\n\n {path}"), 757 level: Level::Error, 758 hint: None, 759 location: None, 760 }, 761 762 Error::OutputFilesAlreadyExist { file_names } => Diagnostic { 763 title: format!( 764 "{} already exist{} in target directory", 765 if file_names.len() == 1 { 766 "File" 767 } else { 768 "Files" 769 }, 770 if file_names.len() == 1 { "" } else { "s" } 771 ), 772 text: format!( 773 "{} 774If you want to overwrite these files, delete them and run the command again. 775", 776 file_names 777 .iter() 778 .map(|name| format!(" - {}", name.as_str())) 779 .join("\n") 780 ), 781 level: Level::Error, 782 hint: None, 783 location: None, 784 }, 785 786 Error::CannotPublishTodo { unfinished } => Diagnostic { 787 title: "Cannot publish unfinished code".into(), 788 text: format!( 789 "These modules contain todo expressions and cannot be published: 790 791{} 792 793Please remove them and try again. 794", 795 unfinished 796 .iter() 797 .map(|name| format!(" - {}", name.as_str())) 798 .join("\n") 799 ), 800 level: Level::Error, 801 hint: None, 802 location: None, 803 }, 804 805 Error::CannotPublishLeakedInternalType { unfinished } => Diagnostic { 806 title: "Cannot publish unfinished code".into(), 807 text: format!( 808 "These modules leak internal types in their public API and cannot be published: 809 810{} 811 812Please make sure internal types do not appear in public functions and try again. 813", 814 unfinished 815 .iter() 816 .map(|name| format!(" - {}", name.as_str())) 817 .join("\n") 818 ), 819 level: Level::Error, 820 hint: None, 821 location: None, 822 }, 823 824 Error::UnableToFindProjectRoot { path } => { 825 let text = wrap_format!( 826 "We were unable to find gleam.toml. 827 828We searched in {path} and all parent directories." 829 ); 830 Diagnostic { 831 title: "Project not found".into(), 832 text, 833 hint: None, 834 level: Level::Error, 835 location: None, 836 } 837 } 838 839 Error::VersionDoesNotMatch { toml_ver, app_ver } => { 840 let text = format!( 841 "The version in gleam.toml \"{toml_ver}\" does not match the version in 842your app.src file \"{app_ver}\"." 843 ); 844 Diagnostic { 845 title: "Version does not match".into(), 846 hint: None, 847 text, 848 level: Level::Error, 849 location: None, 850 } 851 } 852 853 Error::ShellProgramNotFound { program } => { 854 let mut text = format!("The program `{program}` was not found. Is it installed?"); 855 856 match program.as_str() { 857 "erl" | "erlc" | "escript" => text.push_str( 858 " 859Documentation for installing Erlang can be viewed here: 860https://gleam.run/getting-started/installing/", 861 ), 862 "rebar3" => text.push_str( 863 " 864Documentation for installing rebar3 can be viewed here: 865https://gleam.run/getting-started/installing/", 866 ), 867 _ => (), 868 } 869 match (program.as_str(), env::consts::OS) { 870 // TODO: Further suggestions for other OSes? 871 ("erl" | "erlc" | "escript", "macos") => text.push_str( 872 " 873You can also install Erlang via homebrew using \"brew install erlang\"", 874 ), 875 ("rebar3", "macos") => text.push_str( 876 " 877You can also install rebar3 via homebrew using \"brew install rebar3\"", 878 ), 879 _ => (), 880 }; 881 882 Diagnostic { 883 title: "Program not found".into(), 884 text, 885 hint: None, 886 level: Level::Error, 887 location: None, 888 } 889 } 890 891 Error::ShellCommand { 892 program: command, 893 err: None, 894 } => { 895 let text = 896 format!("There was a problem when running the shell command `{command}`."); 897 Diagnostic { 898 title: "Shell command failure".into(), 899 text, 900 hint: None, 901 level: Level::Error, 902 location: None, 903 } 904 } 905 906 Error::ShellCommand { 907 program: command, 908 err: Some(err), 909 } => { 910 let text = format!( 911 "There was a problem when running the shell command `{}`. 912 913The error from the shell command library was: 914 915 {}", 916 command, 917 std_io_error_kind_text(err) 918 ); 919 Diagnostic { 920 title: "Shell command failure".into(), 921 text, 922 hint: None, 923 level: Level::Error, 924 location: None, 925 } 926 } 927 928 Error::Gzip(detail) => { 929 let text = format!( 930 "There was a problem when applying gzip compression. 931 932This was error from the gzip library: 933 934 {detail}" 935 ); 936 Diagnostic { 937 title: "Gzip compression failure".into(), 938 text, 939 hint: None, 940 level: Level::Error, 941 location: None, 942 } 943 } 944 945 Error::AddTar { path, err } => { 946 let text = format!( 947 "There was a problem when attempting to add the file {path} 948to a tar archive. 949 950This was error from the tar library: 951 952 {err}" 953 ); 954 Diagnostic { 955 title: "Failure creating tar archive".into(), 956 text, 957 hint: None, 958 level: Level::Error, 959 location: None, 960 } 961 } 962 963 Error::ExpandTar { error } => { 964 let text = format!( 965 "There was a problem when attempting to expand a to a tar archive. 966 967This was error from the tar library: 968 969 {error}" 970 ); 971 Diagnostic { 972 title: "Failure opening tar archive".into(), 973 text, 974 hint: None, 975 level: Level::Error, 976 location: None, 977 } 978 } 979 980 Error::TarFinish(detail) => { 981 let text = format!( 982 "There was a problem when creating a tar archive. 983 984This was error from the tar library: 985 986 {detail}" 987 ); 988 Diagnostic { 989 title: "Failure creating tar archive".into(), 990 text, 991 hint: None, 992 level: Level::Error, 993 location: None, 994 } 995 } 996 997 Error::Hex(detail) => { 998 let text = format!( 999 "There was a problem when using the Hex API. 1000 1001This was error from the Hex client library: 1002 1003 {detail}" 1004 ); 1005 Diagnostic { 1006 title: "Hex API failure".into(), 1007 text, 1008 hint: None, 1009 level: Level::Error, 1010 location: None, 1011 } 1012 } 1013 1014 Error::DuplicateModule { 1015 module, 1016 first, 1017 second, 1018 } => { 1019 let text = format!( 1020 "The module `{module}` is defined multiple times. 1021 1022First: {first} 1023Second: {second}" 1024 ); 1025 1026 Diagnostic { 1027 title: "Duplicate module".into(), 1028 text, 1029 hint: None, 1030 level: Level::Error, 1031 location: None, 1032 } 1033 } 1034 1035 Error::DuplicateSourceFile { file } => Diagnostic { 1036 title: "Duplicate Source file".into(), 1037 text: format!("The file `{file}` is defined multiple times."), 1038 hint: None, 1039 level: Level::Error, 1040 location: None, 1041 }, 1042 1043 Error::FileIo { 1044 kind, 1045 action, 1046 path, 1047 err, 1048 } => { 1049 let err = match err { 1050 Some(e) => { 1051 format!("\nThe error message from the file IO library was:\n\n {e}\n") 1052 } 1053 None => "".into(), 1054 }; 1055 let text = format!( 1056 "An error occurred while trying to {} this {}: 1057 1058 {} 1059{}", 1060 action.text(), 1061 kind.text(), 1062 path, 1063 err, 1064 ); 1065 Diagnostic { 1066 title: "File IO failure".into(), 1067 text, 1068 hint: None, 1069 level: Level::Error, 1070 location: None, 1071 } 1072 } 1073 1074 Error::NonUtf8Path { path } => { 1075 let text = format!( 1076 "Encountered a non UTF-8 path '{}', but only UTF-8 paths are supported.", 1077 path.to_string_lossy() 1078 ); 1079 Diagnostic { 1080 title: "Non UTF-8 Path Encountered".into(), 1081 text, 1082 level: Level::Error, 1083 location: None, 1084 hint: None, 1085 } 1086 } 1087 1088 Error::GitInitialization { error } => { 1089 let text = format!( 1090 "An error occurred while trying make a git repository for this project: 1091 1092 {error}" 1093 ); 1094 Diagnostic { 1095 title: "Failed to initialize git repository".into(), 1096 text, 1097 hint: None, 1098 level: Level::Error, 1099 location: None, 1100 } 1101 } 1102 1103 Error::Type { path, src, error } => match error { 1104 TypeError::SrcImportingTest { 1105 location, 1106 src_module, 1107 test_module, 1108 } => { 1109 let text = wrap_format!( 1110 "The application module `{src_module}` is importing the test module `{test_module}`. 1111 1112Test modules are not included in production builds so test \ 1113modules cannot import them. Perhaps move the `{test_module}` module to the src directory.", 1114 ); 1115 1116 Diagnostic { 1117 title: "App importing test module".into(), 1118 text, 1119 hint: None, 1120 level: Level::Error, 1121 location: Some(Location { 1122 label: Label { 1123 text: Some("Imported here".into()), 1124 span: *location, 1125 }, 1126 path: path.clone(), 1127 src: src.clone(), 1128 extra_labels: vec![], 1129 }), 1130 } 1131 } 1132 1133 TypeError::UnknownLabels { 1134 unknown, 1135 valid, 1136 supplied, 1137 } => { 1138 let other_labels: Vec<_> = valid 1139 .iter() 1140 .filter(|label| !supplied.contains(label)) 1141 .cloned() 1142 .collect(); 1143 1144 let title = if unknown.len() > 1 { 1145 "Unknown labels" 1146 } else { 1147 "Unknown label" 1148 } 1149 .into(); 1150 1151 let mut labels = unknown.iter().map(|(label, location)| { 1152 let text = did_you_mean(label, &other_labels) 1153 .unwrap_or_else(|| "Unexpected label".into()); 1154 Label { 1155 text: Some(text), 1156 span: *location, 1157 } 1158 }); 1159 let label = labels.next().expect("Unknown labels first label"); 1160 let extra_labels = labels.collect(); 1161 let text = if valid.is_empty() { 1162 "This constructor does not accept any labelled arguments.".into() 1163 } else if other_labels.is_empty() { 1164 "You have already supplied all the labelled arguments that this 1165constructor accepts." 1166 .into() 1167 } else { 1168 let mut label_text = String::from("It accepts these labels:\n"); 1169 for label in other_labels.iter().sorted() { 1170 label_text.push_str("\n "); 1171 label_text.push_str(label); 1172 } 1173 label_text 1174 }; 1175 Diagnostic { 1176 title, 1177 text, 1178 hint: None, 1179 level: Level::Error, 1180 location: Some(Location { 1181 label, 1182 path: path.clone(), 1183 src: src.clone(), 1184 extra_labels, 1185 }), 1186 } 1187 } 1188 1189 TypeError::UnexpectedLabelledArg { location, label } => { 1190 let text = format!( 1191 "This argument has been given a label but the constructor does 1192not expect any. Please remove the label `{label}`." 1193 ); 1194 Diagnostic { 1195 title: "Unexpected labelled argument".into(), 1196 text, 1197 hint: None, 1198 level: Level::Error, 1199 location: Some(Location { 1200 label: Label { 1201 text: None, 1202 span: *location, 1203 }, 1204 path: path.clone(), 1205 src: src.clone(), 1206 extra_labels: vec![], 1207 }), 1208 } 1209 } 1210 1211 TypeError::PositionalArgumentAfterLabelled { location } => { 1212 let text = 1213 "This unlabeled argument has been supplied after a labelled argument. 1214Once a labelled argument has been supplied all following arguments must 1215also be labelled." 1216 .into(); 1217 Diagnostic { 1218 title: "Unexpected positional argument".into(), 1219 text, 1220 hint: None, 1221 level: Level::Error, 1222 location: Some(Location { 1223 label: Label { 1224 text: None, 1225 span: *location, 1226 }, 1227 path: path.clone(), 1228 src: src.clone(), 1229 extra_labels: vec![], 1230 }), 1231 } 1232 } 1233 1234 TypeError::DuplicateImport { 1235 location, 1236 previous_location, 1237 name, 1238 } => { 1239 let text = format!( 1240 "`{name}` has been imported multiple times. 1241Names in a Gleam module must be unique so one will need to be renamed." 1242 ); 1243 Diagnostic { 1244 title: "Duplicate import".into(), 1245 text, 1246 hint: None, 1247 level: Level::Error, 1248 location: Some(Location { 1249 label: Label { 1250 text: Some("Reimported here".into()), 1251 span: *location, 1252 }, 1253 path: path.clone(), 1254 src: src.clone(), 1255 extra_labels: vec![Label { 1256 text: Some("First imported here".into()), 1257 span: *previous_location, 1258 }], 1259 }), 1260 } 1261 } 1262 1263 TypeError::DuplicateName { 1264 location_a, 1265 location_b, 1266 name, 1267 .. 1268 } => { 1269 let (first_location, second_location) = if location_a.start < location_b.start { 1270 (location_a, location_b) 1271 } else { 1272 (location_b, location_a) 1273 }; 1274 let text = format!( 1275 "`{name}` has been defined multiple times. 1276Names in a Gleam module must be unique so one will need to be renamed." 1277 ); 1278 Diagnostic { 1279 title: "Duplicate definition".into(), 1280 text, 1281 hint: None, 1282 level: Level::Error, 1283 location: Some(Location { 1284 label: Label { 1285 text: Some("Redefined here".into()), 1286 span: *second_location, 1287 }, 1288 path: path.clone(), 1289 src: src.clone(), 1290 extra_labels: vec![Label { 1291 text: Some("First defined here".into()), 1292 span: *first_location, 1293 }], 1294 }), 1295 } 1296 } 1297 1298 TypeError::DuplicateTypeName { 1299 name, 1300 location, 1301 previous_location, 1302 .. 1303 } => { 1304 let text = format!( 1305 "The type `{name}` has been defined multiple times. 1306Names in a Gleam module must be unique so one will need to be renamed." 1307 ); 1308 Diagnostic { 1309 title: "Duplicate type definition".into(), 1310 text, 1311 hint: None, 1312 level: Level::Error, 1313 location: Some(Location { 1314 label: Label { 1315 text: Some("Redefined here".into()), 1316 span: *location, 1317 }, 1318 path: path.clone(), 1319 src: src.clone(), 1320 extra_labels: vec![Label { 1321 text: Some("First defined here".into()), 1322 span: *previous_location, 1323 }], 1324 }), 1325 } 1326 } 1327 1328 TypeError::DuplicateField { location, label } => { 1329 let text = 1330 format!("The field `{label}` has already been defined. Rename this field."); 1331 Diagnostic { 1332 title: "Duplicate field".into(), 1333 text, 1334 hint: None, 1335 level: Level::Error, 1336 location: Some(Location { 1337 label: Label { 1338 text: None, 1339 span: *location, 1340 }, 1341 path: path.clone(), 1342 src: src.clone(), 1343 extra_labels: vec![], 1344 }), 1345 } 1346 } 1347 1348 TypeError::DuplicateArgument { location, label } => { 1349 let text = 1350 format!("The labelled argument `{label}` has already been supplied."); 1351 Diagnostic { 1352 title: "Duplicate argument".into(), 1353 text, 1354 hint: None, 1355 level: Level::Error, 1356 location: Some(Location { 1357 label: Label { 1358 text: None, 1359 span: *location, 1360 }, 1361 path: path.clone(), 1362 src: src.clone(), 1363 extra_labels: vec![], 1364 }), 1365 } 1366 } 1367 1368 TypeError::RecursiveType { location } => { 1369 let text = "I don't know how to work out what type this value has. It seems 1370to be defined in terms of itself. 1371 1372Hint: Add some type annotations and try again." 1373 .into(); 1374 Diagnostic { 1375 title: "Recursive type".into(), 1376 text, 1377 hint: None, 1378 level: Level::Error, 1379 location: Some(Location { 1380 label: Label { 1381 text: None, 1382 span: *location, 1383 }, 1384 path: path.clone(), 1385 src: src.clone(), 1386 extra_labels: vec![], 1387 }), 1388 } 1389 } 1390 1391 TypeError::NotFn { location, typ } => { 1392 let mut printer = Printer::new(); 1393 let text = format!( 1394 "This value is being called as a function but its type is:\n\n{}", 1395 printer.pretty_print(typ, 4) 1396 ); 1397 Diagnostic { 1398 title: "Type mismatch".into(), 1399 text, 1400 hint: None, 1401 level: Level::Error, 1402 location: Some(Location { 1403 label: Label { 1404 text: None, 1405 span: *location, 1406 }, 1407 path: path.clone(), 1408 src: src.clone(), 1409 extra_labels: vec![], 1410 }), 1411 } 1412 } 1413 1414 TypeError::UnknownRecordField { 1415 usage, 1416 location, 1417 typ, 1418 label, 1419 fields, 1420 variants, 1421 } => { 1422 let mut printer = Printer::new(); 1423 1424 // Give a hint about what type this value has. 1425 let mut text = format!( 1426 "The value being accessed has this type:\n\n{}\n", 1427 printer.pretty_print(typ, 4) 1428 ); 1429 1430 // Give a hint about what record fields this value has, if any. 1431 if fields.is_empty() { 1432 text.push_str("\nIt does not have any fields."); 1433 } else { 1434 text.push_str("\nIt has these fields:\n"); 1435 } 1436 for field in fields.iter().sorted() { 1437 text.push_str("\n ."); 1438 text.push_str(field); 1439 } 1440 1441 match variants { 1442 RecordVariants::HasVariants => { 1443 let msg = wrap( 1444 "Note: The field you are trying to \ 1445access might not be consistently present or positioned across the custom \ 1446type's variants, preventing reliable access. Ensure the field exists in the \ 1447same position and has the same type in all variants to enable direct accessor syntax.", 1448 ); 1449 text.push_str("\n\n"); 1450 text.push_str(&msg); 1451 } 1452 RecordVariants::NoVariants => (), 1453 } 1454 1455 // Give a hint about Gleam not having OOP methods if it 1456 // looks like they might be trying to call one. 1457 match usage { 1458 FieldAccessUsage::MethodCall => { 1459 let msg = wrap( 1460 "Gleam is not object oriented, so if you are trying \ 1461to call a method on this value you may want to use the function syntax instead.", 1462 ); 1463 text.push_str("\n\n"); 1464 text.push_str(&msg); 1465 text.push_str("\n\n "); 1466 text.push_str(label); 1467 text.push_str("(value)"); 1468 } 1469 FieldAccessUsage::Other => (), 1470 } 1471 1472 let label = did_you_mean(label, fields) 1473 .unwrap_or_else(|| "This field does not exist".into()); 1474 Diagnostic { 1475 title: "Unknown record field".into(), 1476 text, 1477 hint: None, 1478 level: Level::Error, 1479 location: Some(Location { 1480 label: Label { 1481 text: Some(label), 1482 span: *location, 1483 }, 1484 path: path.clone(), 1485 src: src.clone(), 1486 extra_labels: vec![], 1487 }), 1488 } 1489 } 1490 1491 TypeError::CouldNotUnify { 1492 location, 1493 expected, 1494 given, 1495 situation: Some(UnifyErrorSituation::Operator(op)), 1496 rigid_type_names: annotated_names, 1497 } => { 1498 let mut printer = Printer::new(); 1499 printer.with_names(annotated_names.clone()); 1500 let mut text = format!( 1501 "The {op} operator expects arguments of this type: 1502 1503{expected} 1504 1505But this argument has this type: 1506 1507{given}\n", 1508 op = op.name(), 1509 expected = printer.pretty_print(expected, 4), 1510 given = printer.pretty_print(given, 4), 1511 ); 1512 if let Some(hint) = hint_alternative_operator(op, given) { 1513 text.push('\n'); 1514 text.push_str("Hint: "); 1515 text.push_str(&hint); 1516 } 1517 Diagnostic { 1518 title: "Type mismatch".into(), 1519 text, 1520 hint: None, 1521 level: Level::Error, 1522 location: Some(Location { 1523 label: Label { 1524 text: None, 1525 span: *location, 1526 }, 1527 path: path.clone(), 1528 src: src.clone(), 1529 extra_labels: vec![], 1530 }), 1531 } 1532 } 1533 1534 TypeError::CouldNotUnify { 1535 location, 1536 expected, 1537 given, 1538 situation: Some(UnifyErrorSituation::PipeTypeMismatch), 1539 rigid_type_names: annotated_names, 1540 } => { 1541 // Remap the pipe function type into just the type expected by the pipe. 1542 let expected = expected 1543 .fn_types() 1544 .and_then(|(args, _)| args.first().cloned()); 1545 1546 // Remap the argument as well, if it's a function. 1547 let given = given 1548 .fn_types() 1549 .and_then(|(args, _)| args.first().cloned()) 1550 .unwrap_or_else(|| given.clone()); 1551 1552 let mut printer = Printer::new(); 1553 printer.with_names(annotated_names.clone()); 1554 let text = format!( 1555 "The argument is: 1556 1557{given} 1558 1559But function expects: 1560 1561{expected}", 1562 expected = expected 1563 .map(|v| printer.pretty_print(&v, 4)) 1564 .unwrap_or_else(|| " No arguments".into()), 1565 given = printer.pretty_print(&given, 4) 1566 ); 1567 1568 Diagnostic { 1569 title: "Type mismatch".into(), 1570 text, 1571 hint: None, 1572 level: Level::Error, 1573 location: Some(Location { 1574 label: Label { 1575 text: Some("This function does not accept the piped type".into()), 1576 span: *location, 1577 }, 1578 path: path.clone(), 1579 src: src.clone(), 1580 extra_labels: vec![], 1581 }), 1582 } 1583 } 1584 1585 TypeError::CouldNotUnify { 1586 location, 1587 expected, 1588 given, 1589 situation, 1590 rigid_type_names: annotated_names, 1591 } => { 1592 let mut printer = Printer::new(); 1593 printer.with_names(annotated_names.clone()); 1594 let mut text = 1595 if let Some(description) = situation.and_then(|s| s.description()) { 1596 let mut text = description.to_string(); 1597 text.push('\n'); 1598 text.push('\n'); 1599 text 1600 } else { 1601 "".into() 1602 }; 1603 text.push_str("Expected type:\n\n"); 1604 text.push_str(&printer.pretty_print(expected, 4)); 1605 text.push_str("\n\nFound type:\n\n"); 1606 text.push_str(&printer.pretty_print(given, 4)); 1607 Diagnostic { 1608 title: "Type mismatch".into(), 1609 text, 1610 hint: None, 1611 level: Level::Error, 1612 location: Some(Location { 1613 label: Label { 1614 text: None, 1615 span: *location, 1616 }, 1617 path: path.clone(), 1618 src: src.clone(), 1619 extra_labels: vec![], 1620 }), 1621 } 1622 } 1623 1624 TypeError::IncorrectTypeArity { 1625 location, 1626 expected, 1627 given, 1628 .. 1629 } => { 1630 let text = "Functions and constructors have to be called with their expected 1631number of arguments." 1632 .into(); 1633 let expected = match expected { 1634 0 => "no arguments".into(), 1635 1 => "1 argument".into(), 1636 _ => format!("{expected} arguments"), 1637 }; 1638 Diagnostic { 1639 title: "Incorrect arity".into(), 1640 text, 1641 hint: None, 1642 level: Level::Error, 1643 location: Some(Location { 1644 label: Label { 1645 text: Some(format!("Expected {expected}, got {given}")), 1646 span: *location, 1647 }, 1648 path: path.clone(), 1649 src: src.clone(), 1650 extra_labels: vec![], 1651 }), 1652 } 1653 } 1654 1655 TypeError::IncorrectArity { 1656 labels, 1657 location, 1658 expected, 1659 given, 1660 } => { 1661 let text = if labels.is_empty() { 1662 "".into() 1663 } else { 1664 let labels = labels 1665 .iter() 1666 .map(|p| format!(" - {p}")) 1667 .sorted() 1668 .join("\n"); 1669 format!( 1670 "This call accepts these additional labelled arguments:\n\n{labels}", 1671 ) 1672 }; 1673 let expected = match expected { 1674 0 => "no arguments".into(), 1675 1 => "1 argument".into(), 1676 _ => format!("{expected} arguments"), 1677 }; 1678 let label = format!("Expected {expected}, got {given}"); 1679 Diagnostic { 1680 title: "Incorrect arity".into(), 1681 text, 1682 hint: None, 1683 level: Level::Error, 1684 location: Some(Location { 1685 label: Label { 1686 text: Some(label), 1687 span: *location, 1688 }, 1689 path: path.clone(), 1690 src: src.clone(), 1691 extra_labels: vec![], 1692 }), 1693 } 1694 } 1695 1696 TypeError::UnnecessarySpreadOperator { location, arity } => { 1697 let text = wrap_format!( 1698 "This record has {arity} fields and you have already \ 1699assigned variables to all of them." 1700 ); 1701 Diagnostic { 1702 title: "Unnecessary spread operator".into(), 1703 text, 1704 hint: None, 1705 level: Level::Error, 1706 location: Some(Location { 1707 label: Label { 1708 text: None, 1709 span: *location, 1710 }, 1711 path: path.clone(), 1712 src: src.clone(), 1713 extra_labels: vec![], 1714 }), 1715 } 1716 } 1717 1718 TypeError::UpdateMultiConstructorType { location } => { 1719 let text = "This type has multiple constructors so it cannot be safely updated. 1720If this value was one of the other variants then the update would be 1721produce incorrect results. 1722 1723Consider pattern matching on it with a case expression and then 1724constructing a new record with its values." 1725 .into(); 1726 1727 Diagnostic { 1728 title: "Unsafe record update".into(), 1729 text, 1730 hint: None, 1731 level: Level::Error, 1732 location: Some(Location { 1733 label: Label { 1734 text: Some( 1735 "I can't tell this is always the right constructor".into(), 1736 ), 1737 span: *location, 1738 }, 1739 path: path.clone(), 1740 src: src.clone(), 1741 extra_labels: vec![], 1742 }), 1743 } 1744 } 1745 1746 TypeError::UnknownType { 1747 location, 1748 name, 1749 hint, 1750 } => { 1751 let label_text = match hint { 1752 UnknownTypeHint::AlternativeTypes(types) => did_you_mean(name, types), 1753 UnknownTypeHint::ValueInScopeWithSameName => None, 1754 }; 1755 1756 let mut text = wrap_format!( 1757 "The type `{name}` is not defined or imported in this module." 1758 ); 1759 1760 match hint { 1761 UnknownTypeHint::ValueInScopeWithSameName => { 1762 let hint = wrap_format!( 1763 "There is a value in scope with the name `{name}`, but no type in scope with that name." 1764 ); 1765 text.push('\n'); 1766 text.push_str(hint.as_str()); 1767 } 1768 UnknownTypeHint::AlternativeTypes(_) => {} 1769 }; 1770 1771 Diagnostic { 1772 title: "Unknown type".into(), 1773 text, 1774 hint: None, 1775 level: Level::Error, 1776 location: Some(Location { 1777 label: Label { 1778 text: label_text, 1779 span: *location, 1780 }, 1781 path: path.clone(), 1782 src: src.clone(), 1783 extra_labels: vec![], 1784 }), 1785 } 1786 } 1787 1788 TypeError::UnknownVariable { 1789 location, 1790 variables, 1791 name, 1792 type_with_name_in_scope, 1793 } => { 1794 let text = if *type_with_name_in_scope { 1795 wrap_format!("`{name}` is a type, it cannot be used as a value.") 1796 } else { 1797 wrap_format!("The name `{name}` is not in scope here.") 1798 }; 1799 Diagnostic { 1800 title: "Unknown variable".into(), 1801 text, 1802 hint: None, 1803 level: Level::Error, 1804 location: Some(Location { 1805 label: Label { 1806 text: did_you_mean(name, variables), 1807 span: *location, 1808 }, 1809 path: path.clone(), 1810 src: src.clone(), 1811 extra_labels: vec![], 1812 }), 1813 } 1814 } 1815 1816 TypeError::PrivateTypeLeak { location, leaked } => { 1817 let mut printer = Printer::new(); 1818 1819 // TODO: be more precise. 1820 // - is being returned by this public function 1821 // - is taken as an argument by this public function 1822 // - is taken as an argument by this public enum constructor 1823 // etc 1824 let text = format!( 1825 "The following type is private, but is being used by this public export. 1826 1827{} 1828 1829Private types can only be used within the module that defines them.", 1830 printer.pretty_print(leaked, 4), 1831 ); 1832 Diagnostic { 1833 title: "Private type used in public interface".into(), 1834 text, 1835 hint: None, 1836 level: Level::Error, 1837 location: Some(Location { 1838 label: Label { 1839 text: None, 1840 span: *location, 1841 }, 1842 path: path.clone(), 1843 src: src.clone(), 1844 extra_labels: vec![], 1845 }), 1846 } 1847 } 1848 1849 TypeError::UnknownModule { 1850 location, 1851 name, 1852 imported_modules, 1853 } => Diagnostic { 1854 title: "Unknown module".into(), 1855 text: format!("No module has been found with the name `{name}`."), 1856 hint: None, 1857 level: Level::Error, 1858 location: Some(Location { 1859 label: Label { 1860 text: did_you_mean(name, imported_modules), 1861 span: *location, 1862 }, 1863 path: path.clone(), 1864 src: src.clone(), 1865 extra_labels: vec![], 1866 }), 1867 }, 1868 1869 TypeError::UnknownModuleType { 1870 location, 1871 name, 1872 module_name, 1873 type_constructors, 1874 } => { 1875 let text = 1876 format!("The module `{module_name}` does not have a `{name}` type.",); 1877 Diagnostic { 1878 title: "Unknown module type".into(), 1879 text, 1880 hint: None, 1881 level: Level::Error, 1882 location: Some(Location { 1883 label: Label { 1884 text: did_you_mean(name, type_constructors), 1885 span: *location, 1886 }, 1887 path: path.clone(), 1888 src: src.clone(), 1889 extra_labels: vec![], 1890 }), 1891 } 1892 } 1893 1894 TypeError::UnknownModuleValue { 1895 location, 1896 name, 1897 module_name, 1898 value_constructors, 1899 } => { 1900 let text = 1901 format!("The module `{module_name}` does not have a `{name}` value.",); 1902 Diagnostic { 1903 title: "Unknown module field".into(), 1904 text, 1905 hint: None, 1906 level: Level::Error, 1907 location: Some(Location { 1908 label: Label { 1909 text: did_you_mean(name, value_constructors), 1910 span: *location, 1911 }, 1912 path: path.clone(), 1913 src: src.clone(), 1914 extra_labels: vec![], 1915 }), 1916 } 1917 } 1918 1919 TypeError::UnknownModuleField { 1920 location, 1921 name, 1922 module_name, 1923 type_constructors, 1924 value_constructors, 1925 } => { 1926 let options: Vec<_> = type_constructors 1927 .iter() 1928 .chain(value_constructors) 1929 .cloned() 1930 .collect(); 1931 let text = 1932 format!("The module `{module_name}` does not have a `{name}` field.",); 1933 Diagnostic { 1934 title: "Unknown module field".into(), 1935 text, 1936 hint: None, 1937 level: Level::Error, 1938 location: Some(Location { 1939 label: Label { 1940 text: did_you_mean(name, &options), 1941 span: *location, 1942 }, 1943 path: path.clone(), 1944 src: src.clone(), 1945 extra_labels: vec![], 1946 }), 1947 } 1948 } 1949 1950 TypeError::IncorrectNumClausePatterns { 1951 location, 1952 expected, 1953 given, 1954 } => { 1955 let text = wrap_format!( 1956 "This case expression has {expected} subjects, but this pattern matches {given}. 1957Each clause must have a pattern for every subject value.", 1958 ); 1959 Diagnostic { 1960 title: "Incorrect number of patterns".into(), 1961 text, 1962 hint: None, 1963 level: Level::Error, 1964 location: Some(Location { 1965 label: Label { 1966 text: Some(format!("Expected {expected} patterns, got {given}")), 1967 span: *location, 1968 }, 1969 path: path.clone(), 1970 src: src.clone(), 1971 extra_labels: vec![], 1972 }), 1973 } 1974 } 1975 1976 TypeError::NonLocalClauseGuardVariable { location, name } => { 1977 let text = wrap_format!( 1978 "Variables used in guards must be either defined in the \ 1979function, or be an argument to the function. The variable `{name}` is not defined locally.", 1980 ); 1981 Diagnostic { 1982 title: "Invalid guard variable".into(), 1983 text, 1984 hint: None, 1985 level: Level::Error, 1986 location: Some(Location { 1987 label: Label { 1988 text: Some("Is not locally defined".into()), 1989 span: *location, 1990 }, 1991 path: path.clone(), 1992 src: src.clone(), 1993 extra_labels: vec![], 1994 }), 1995 } 1996 } 1997 1998 TypeError::ExtraVarInAlternativePattern { location, name } => { 1999 let text = wrap_format!( 2000"All alternative patterns must define the same variables as the initial pattern. \ 2001This variable `{name}` has not been previously defined.", 2002 ); 2003 Diagnostic { 2004 title: "Extra alternative pattern variable".into(), 2005 text, 2006 hint: None, 2007 level: Level::Error, 2008 location: Some(Location { 2009 label: Label { 2010 text: Some("Has not been previously defined".into()), 2011 span: *location, 2012 }, 2013 path: path.clone(), 2014 src: src.clone(), 2015 extra_labels: vec![], 2016 }), 2017 } 2018 } 2019 2020 TypeError::MissingVarInAlternativePattern { location, name } => { 2021 let text = wrap_format!( 2022 "All alternative patterns must define the same variables \ 2023as the initial pattern, but the `{name}` variable is missing.", 2024 ); 2025 Diagnostic { 2026 title: "Missing alternative pattern variable".into(), 2027 text, 2028 hint: None, 2029 level: Level::Error, 2030 location: Some(Location { 2031 label: Label { 2032 text: Some("This does not define all required variables".into()), 2033 span: *location, 2034 }, 2035 path: path.clone(), 2036 src: src.clone(), 2037 extra_labels: vec![], 2038 }), 2039 } 2040 } 2041 2042 TypeError::DuplicateVarInPattern { location, name } => { 2043 let text = wrap_format!( 2044 "Variables can only be used once per pattern. This \ 2045variable `{name}` appears multiple times. 2046If you used the same variable twice deliberately in order to check for equality \ 2047please use a guard clause instead. 2048e.g. (x, y) if x == y -> ...", 2049 ); 2050 Diagnostic { 2051 title: "Duplicate variable in pattern".into(), 2052 text, 2053 hint: None, 2054 level: Level::Error, 2055 location: Some(Location { 2056 label: Label { 2057 text: Some("This has already been used".into()), 2058 span: *location, 2059 }, 2060 path: path.clone(), 2061 src: src.clone(), 2062 extra_labels: vec![], 2063 }), 2064 } 2065 } 2066 2067 TypeError::OutOfBoundsTupleIndex { 2068 location, size: 0, .. 2069 } => Diagnostic { 2070 title: "Out of bounds tuple index".into(), 2071 text: "This tuple has no elements so it cannot be indexed at all.".into(), 2072 hint: None, 2073 level: Level::Error, 2074 location: Some(Location { 2075 label: Label { 2076 text: None, 2077 span: *location, 2078 }, 2079 path: path.clone(), 2080 src: src.clone(), 2081 extra_labels: vec![], 2082 }), 2083 }, 2084 2085 TypeError::OutOfBoundsTupleIndex { 2086 location, 2087 index, 2088 size, 2089 } => { 2090 let text = wrap_format!( 2091 "The index being accessed for this tuple is {}, but this \ 2092tuple has {} elements so the highest valid index is {}.", 2093 index, 2094 size, 2095 size - 1, 2096 ); 2097 Diagnostic { 2098 title: "Out of bounds tuple index".into(), 2099 text, 2100 hint: None, 2101 level: Level::Error, 2102 location: Some(Location { 2103 label: Label { 2104 text: Some("This index is too large".into()), 2105 span: *location, 2106 }, 2107 path: path.clone(), 2108 src: src.clone(), 2109 extra_labels: vec![], 2110 }), 2111 } 2112 } 2113 2114 TypeError::NotATuple { location, given } => { 2115 let mut printer = Printer::new(); 2116 let text = format!( 2117 "To index into this value it needs to be a tuple, however it has this type: 2118 2119{}", 2120 printer.pretty_print(given, 4), 2121 ); 2122 Diagnostic { 2123 title: "Type mismatch".into(), 2124 text, 2125 hint: None, 2126 level: Level::Error, 2127 location: Some(Location { 2128 label: Label { 2129 text: Some("This is not a tuple".into()), 2130 span: *location, 2131 }, 2132 path: path.clone(), 2133 src: src.clone(), 2134 extra_labels: vec![], 2135 }), 2136 } 2137 } 2138 2139 TypeError::NotATupleUnbound { location } => { 2140 let text = "To index into a tuple we need to know it size, but we don't know 2141anything about this type yet. Please add some type annotations so 2142we can continue." 2143 .into(); 2144 Diagnostic { 2145 title: "Type mismatch".into(), 2146 text, 2147 hint: None, 2148 level: Level::Error, 2149 location: Some(Location { 2150 label: Label { 2151 text: Some("What type is this?".into()), 2152 span: *location, 2153 }, 2154 path: path.clone(), 2155 src: src.clone(), 2156 extra_labels: vec![], 2157 }), 2158 } 2159 } 2160 2161 TypeError::RecordAccessUnknownType { location } => { 2162 let text = "In order to access a record field we need to know what type it is, 2163but I can't tell the type here. Try adding type annotations to your 2164function and try again." 2165 .into(); 2166 Diagnostic { 2167 title: "Unknown type for record access".into(), 2168 text, 2169 hint: None, 2170 level: Level::Error, 2171 location: Some(Location { 2172 label: Label { 2173 text: Some("I don't know what type this is".into()), 2174 span: *location, 2175 }, 2176 path: path.clone(), 2177 src: src.clone(), 2178 extra_labels: vec![], 2179 }), 2180 } 2181 } 2182 2183 TypeError::BitArraySegmentError { error, location } => { 2184 let (label, mut extra) = match error { 2185 bit_array::ErrorType::ConflictingTypeOptions { existing_type } => ( 2186 "This is an extra type specifier", 2187 vec![format!("Hint: This segment already has the type {existing_type}.")], 2188 ), 2189 2190 bit_array::ErrorType::ConflictingSignednessOptions { 2191 existing_signed 2192 } => ( 2193 "This is an extra signedness specifier", 2194 vec![format!( 2195 "Hint: This segment already has a signedness of {existing_signed}." 2196 )], 2197 ), 2198 2199 bit_array::ErrorType::ConflictingEndiannessOptions { 2200 existing_endianness 2201 } => ( 2202 "This is an extra endianness specifier", 2203 vec![format!( 2204 "Hint: This segment already has an endianness of {existing_endianness}." 2205 )], 2206 ), 2207 2208 bit_array::ErrorType::ConflictingSizeOptions => ( 2209 "This is an extra size specifier", 2210 vec!["Hint: This segment already has a size.".into()], 2211 ), 2212 2213 bit_array::ErrorType::ConflictingUnitOptions => ( 2214 "This is an extra unit specifier", 2215 vec!["Hint: A BitArray segment can have at most 1 unit.".into()], 2216 ), 2217 2218 bit_array::ErrorType::FloatWithSize => ( 2219 "Invalid float size", 2220 vec!["Hint: floats have an exact size of 16/32/64 bits.".into()], 2221 ), 2222 2223 bit_array::ErrorType::InvalidEndianness => ( 2224 "This option is invalid here", 2225 vec![wrap("Hint: signed and unsigned can only be used with \ 2226int, float, utf16 and utf32 types.")], 2227 ), 2228 2229 bit_array::ErrorType::OptionNotAllowedInValue => ( 2230 "This option is only allowed in BitArray patterns", 2231 vec!["Hint: This option has no effect in BitArray values.".into()], 2232 ), 2233 2234 bit_array::ErrorType::SignednessUsedOnNonInt { typ } => ( 2235 "Signedness is only valid with int types", 2236 vec![format!("Hint: This segment has a type of {typ}")], 2237 ), 2238 bit_array::ErrorType::TypeDoesNotAllowSize { typ } => ( 2239 "Size cannot be specified here", 2240 vec![format!("Hint: {typ} segments have an automatic size.")], 2241 ), 2242 bit_array::ErrorType::TypeDoesNotAllowUnit { typ } => ( 2243 "Unit cannot be specified here", 2244 vec![wrap(&format!("Hint: {typ} segments are sized based on their value \ 2245and cannot have a unit."))], 2246 ), 2247 bit_array::ErrorType::VariableUtfSegmentInPattern => ( 2248 "This cannot be a variable", 2249 vec![wrap("Hint: in patterns utf8, utf16, and utf32 must be an exact string.")], 2250 ), 2251 bit_array::ErrorType::SegmentMustHaveSize => ( 2252 "This segment has no size", 2253 vec![wrap("Hint: Bit array segments without a size are only \ 2254allowed at the end of a bin pattern.")], 2255 ), 2256 bit_array::ErrorType::UnitMustHaveSize => ( 2257 "This needs an explicit size", 2258 vec!["Hint: If you specify unit() you must also specify size().".into()], 2259 ), 2260 }; 2261 extra.push("See: https://tour.gleam.run/data-types/bit-arrays/".into()); 2262 let text = extra.join("\n"); 2263 Diagnostic { 2264 title: "Invalid bit array segment".into(), 2265 text, 2266 hint: None, 2267 level: Level::Error, 2268 location: Some(Location { 2269 label: Label { 2270 text: Some(label.into()), 2271 span: *location, 2272 }, 2273 path: path.clone(), 2274 src: src.clone(), 2275 extra_labels: vec![], 2276 }), 2277 } 2278 } 2279 2280 TypeError::RecordUpdateInvalidConstructor { location } => Diagnostic { 2281 title: "Invalid record constructor".into(), 2282 text: "Only record constructors can be used with the update syntax.".into(), 2283 hint: None, 2284 level: Level::Error, 2285 location: Some(Location { 2286 label: Label { 2287 text: Some("This is not a record constructor".into()), 2288 span: *location, 2289 }, 2290 path: path.clone(), 2291 src: src.clone(), 2292 extra_labels: vec![], 2293 }), 2294 }, 2295 2296 TypeError::UnexpectedTypeHole { location } => Diagnostic { 2297 title: "Unexpected type hole".into(), 2298 text: "We need to know the exact type here so type holes cannot be used." 2299 .into(), 2300 hint: None, 2301 level: Level::Error, 2302 location: Some(Location { 2303 label: Label { 2304 text: Some("I need to know what this is".into()), 2305 span: *location, 2306 }, 2307 path: path.clone(), 2308 src: src.clone(), 2309 extra_labels: vec![], 2310 }), 2311 }, 2312 2313 TypeError::ReservedModuleName { name } => { 2314 let text = format!( 2315 "The module name `{name}` is reserved. 2316Try a different name for this module." 2317 ); 2318 Diagnostic { 2319 title: "Reserved module name".into(), 2320 text, 2321 hint: None, 2322 location: None, 2323 level: Level::Error, 2324 } 2325 } 2326 2327 TypeError::KeywordInModuleName { name, keyword } => { 2328 let text = wrap(&format!( 2329 "The module name `{name}` contains the keyword `{keyword}`, so importing \ 2330it would be a syntax error. 2331Try a different name for this module." 2332 )); 2333 Diagnostic { 2334 title: "Invalid module name".into(), 2335 text, 2336 hint: None, 2337 location: None, 2338 level: Level::Error, 2339 } 2340 } 2341 2342 TypeError::NotExhaustivePatternMatch { 2343 location, 2344 unmatched, 2345 kind, 2346 } => { 2347 let mut text = match kind { 2348 PatternMatchKind::Case => { 2349 "This case expression does not match all possibilities. 2350Each constructor must have a pattern that matches it or 2351else it could crash." 2352 } 2353 PatternMatchKind::Assignment => { 2354 "This assignment does not match all possibilities. 2355Either use a case expression with patterns for each possible 2356value, or use `let assert` rather than `let`." 2357 } 2358 } 2359 .to_string(); 2360 2361 text.push_str("\n\nThese values are not matched:\n\n"); 2362 for unmatched in unmatched { 2363 text.push_str(" - "); 2364 text.push_str(unmatched); 2365 text.push('\n'); 2366 } 2367 Diagnostic { 2368 title: "Not exhaustive pattern match".into(), 2369 text, 2370 hint: None, 2371 level: Level::Error, 2372 location: Some(Location { 2373 label: Label { 2374 text: None, 2375 span: *location, 2376 }, 2377 path: path.clone(), 2378 src: src.clone(), 2379 extra_labels: vec![], 2380 }), 2381 } 2382 } 2383 2384 TypeError::ArgumentNameAlreadyUsed { location, name } => Diagnostic { 2385 title: "Argument name already used".into(), 2386 text: format!("Two `{name}` arguments have been defined for this function."), 2387 hint: None, 2388 level: Level::Error, 2389 location: Some(Location { 2390 label: Label { 2391 text: None, 2392 span: *location, 2393 }, 2394 path: path.clone(), 2395 src: src.clone(), 2396 extra_labels: vec![], 2397 }), 2398 }, 2399 2400 TypeError::UnlabelledAfterlabelled { location } => Diagnostic { 2401 title: "Unlabelled argument after labelled argument".into(), 2402 text: wrap("All unlabelled arguments must come before any labelled arguments."), 2403 hint: None, 2404 level: Level::Error, 2405 location: Some(Location { 2406 label: Label { 2407 text: None, 2408 span: *location, 2409 }, 2410 path: path.clone(), 2411 src: src.clone(), 2412 extra_labels: vec![], 2413 }), 2414 }, 2415 2416 TypeError::RecursiveTypeAlias { location, cycle } => { 2417 let mut text = "This type alias is defined in terms of itself.\n".into(); 2418 write_cycle(&mut text, cycle); 2419 text.push_str( 2420 "If we tried to compile this recursive type it would expand 2421forever in a loop, and we'd never get the final type.", 2422 ); 2423 Diagnostic { 2424 title: "Type cycle".into(), 2425 text, 2426 hint: None, 2427 level: Level::Error, 2428 location: Some(Location { 2429 label: Label { 2430 text: None, 2431 span: *location, 2432 }, 2433 path: path.clone(), 2434 src: src.clone(), 2435 extra_labels: vec![], 2436 }), 2437 } 2438 } 2439 2440 TypeError::ExternalMissingAnnotation { location, kind } => { 2441 let kind = match kind { 2442 MissingAnnotation::Parameter => "parameter", 2443 MissingAnnotation::Return => "return", 2444 }; 2445 let text = format!( 2446 "A {kind} annotation is missing from this function. 2447 2448Functions with external implementations must have type annotations 2449so we can tell what type of values they accept and return.", 2450 ); 2451 Diagnostic { 2452 title: "Missing type annotation".into(), 2453 text, 2454 hint: None, 2455 level: Level::Error, 2456 location: Some(Location { 2457 label: Label { 2458 text: None, 2459 span: *location, 2460 }, 2461 path: path.clone(), 2462 src: src.clone(), 2463 extra_labels: vec![], 2464 }), 2465 } 2466 } 2467 2468 TypeError::NoImplementation { location } => { 2469 let text = "We can't compile this function as it doesn't have an 2470implementation. Add a body or an external implementation 2471using the `@external` attribute." 2472 .into(); 2473 Diagnostic { 2474 title: "Function without an implementation".into(), 2475 text, 2476 hint: None, 2477 level: Level::Error, 2478 location: Some(Location { 2479 label: Label { 2480 text: None, 2481 span: *location, 2482 }, 2483 path: path.clone(), 2484 src: src.clone(), 2485 extra_labels: vec![], 2486 }), 2487 } 2488 } 2489 2490 TypeError::InvalidExternalJavascriptModule { 2491 location, 2492 name, 2493 module, 2494 } => { 2495 let text = wrap_format!( 2496 "The function `{name}` has an external JavaScript \ 2497implementation but the module path `{module}` is not valid." 2498 ); 2499 Diagnostic { 2500 title: "Invalid JavaScript module".into(), 2501 text, 2502 hint: None, 2503 level: Level::Error, 2504 location: Some(Location { 2505 label: Label { 2506 text: None, 2507 span: *location, 2508 }, 2509 path: path.clone(), 2510 src: src.clone(), 2511 extra_labels: vec![], 2512 }), 2513 } 2514 } 2515 2516 TypeError::InvalidExternalJavascriptFunction { 2517 location, 2518 name, 2519 function, 2520 } => { 2521 let text = wrap_format!( 2522 "The function `{name}` has an external JavaScript \ 2523implementation but the function name `{function}` is not valid." 2524 ); 2525 Diagnostic { 2526 title: "Invalid JavaScript function".into(), 2527 text, 2528 hint: None, 2529 level: Level::Error, 2530 location: Some(Location { 2531 label: Label { 2532 text: None, 2533 span: *location, 2534 }, 2535 path: path.clone(), 2536 src: src.clone(), 2537 extra_labels: vec![], 2538 }), 2539 } 2540 } 2541 2542 TypeError::InexhaustiveLetAssignment { location, missing } => { 2543 let mut text: String = 2544 "This assignment uses a pattern that does not match all possible 2545values. If one of the other values is used then the assignment 2546will crash. 2547 2548The missing patterns are:\n" 2549 .into(); 2550 for missing in missing { 2551 text.push_str("\n "); 2552 text.push_str(missing); 2553 } 2554 text.push('\n'); 2555 2556 Diagnostic { 2557 title: "Inexhaustive pattern".into(), 2558 text, 2559 hint: Some( 2560 "Use a more general pattern or use `let assert` instead.".into(), 2561 ), 2562 level: Level::Error, 2563 location: Some(Location { 2564 src: src.clone(), 2565 path: path.to_path_buf(), 2566 label: Label { 2567 text: None, 2568 span: *location, 2569 }, 2570 extra_labels: Vec::new(), 2571 }), 2572 } 2573 } 2574 2575 TypeError::EmptyCaseExpression { location } => { 2576 let text: String = "This case expression has no clauses.".into(); 2577 2578 Diagnostic { 2579 title: "Empty case expression".into(), 2580 text, 2581 hint: None, 2582 level: Level::Error, 2583 location: Some(Location { 2584 src: src.clone(), 2585 path: path.to_path_buf(), 2586 label: Label { 2587 text: None, 2588 span: *location, 2589 }, 2590 extra_labels: Vec::new(), 2591 }), 2592 } 2593 } 2594 2595 TypeError::InexhaustiveCaseExpression { location, missing } => { 2596 let mut text: String = 2597 "This case expression does not have a pattern for all possible values. 2598If it is run on one of the values without a pattern then it will crash. 2599 2600The missing patterns are:\n" 2601 .into(); 2602 for missing in missing { 2603 text.push_str("\n "); 2604 text.push_str(missing); 2605 } 2606 Diagnostic { 2607 title: "Inexhaustive patterns".into(), 2608 text, 2609 hint: None, 2610 level: Level::Error, 2611 location: Some(Location { 2612 src: src.clone(), 2613 path: path.to_path_buf(), 2614 label: Label { 2615 text: None, 2616 span: *location, 2617 }, 2618 extra_labels: Vec::new(), 2619 }), 2620 } 2621 } 2622 2623 TypeError::UnsupportedExpressionTarget { 2624 location, 2625 target: current_target, 2626 } => { 2627 let text = wrap_format!( 2628 "This value is not available as it is defined using externals, \ 2629and there is no implementation for the {} target.\n", 2630 match current_target { 2631 Target::Erlang => "Erlang", 2632 Target::JavaScript => "JavaScript", 2633 } 2634 ); 2635 let hint = wrap("Did you mean to build for a different target?"); 2636 Diagnostic { 2637 title: "Unsupported target".into(), 2638 text, 2639 hint: Some(hint), 2640 level: Level::Error, 2641 location: Some(Location { 2642 path: path.clone(), 2643 src: src.clone(), 2644 label: Label { 2645 text: None, 2646 span: *location, 2647 }, 2648 extra_labels: vec![], 2649 }), 2650 } 2651 } 2652 2653 TypeError::UnsupportedPublicFunctionTarget { 2654 location, 2655 name, 2656 target, 2657 } => { 2658 let target = match target { 2659 Target::Erlang => "Erlang", 2660 Target::JavaScript => "JavaScript", 2661 }; 2662 let text = wrap_format!( 2663 "The `{name}` function is public but doesn't have an \ 2664implementation for the {target} target. All public functions of a package \ 2665must be able to compile for a module to be valid." 2666 ); 2667 Diagnostic { 2668 title: "Unsupported target".into(), 2669 text, 2670 hint: None, 2671 level: Level::Error, 2672 location: Some(Location { 2673 path: path.clone(), 2674 src: src.clone(), 2675 label: Label { 2676 text: None, 2677 span: *location, 2678 }, 2679 extra_labels: vec![], 2680 }), 2681 } 2682 } 2683 2684 TypeError::UnusedTypeAliasParameter { location, name } => { 2685 let text = wrap_format!( 2686 "The type variable `{name}` is unused. It can be safely removed.", 2687 ); 2688 Diagnostic { 2689 title: "Unused type parameter".into(), 2690 text, 2691 hint: None, 2692 level: Level::Error, 2693 location: Some(Location { 2694 path: path.clone(), 2695 src: src.clone(), 2696 label: Label { 2697 text: None, 2698 span: *location, 2699 }, 2700 extra_labels: vec![], 2701 }), 2702 } 2703 } 2704 2705 TypeError::DuplicateTypeParameter { location, name } => { 2706 let text = wrap_format!( 2707 "This definition has multiple type parameters named `{name}`. 2708Rename or remove one of them.", 2709 ); 2710 Diagnostic { 2711 title: "Duplicate type parameter".into(), 2712 text, 2713 hint: None, 2714 level: Level::Error, 2715 location: Some(Location { 2716 path: path.clone(), 2717 src: src.clone(), 2718 label: Label { 2719 text: None, 2720 span: *location, 2721 }, 2722 extra_labels: vec![], 2723 }), 2724 } 2725 } 2726 }, 2727 2728 Error::Parse { path, src, error } => { 2729 let (label, extra) = error.details(); 2730 let text = extra.join("\n"); 2731 2732 let adjusted_location = if error.error == ParseErrorType::UnexpectedEof { 2733 crate::ast::SrcSpan { 2734 start: (src.len() - 1) as u32, 2735 end: (src.len() - 1) as u32, 2736 } 2737 } else { 2738 error.location 2739 }; 2740 2741 Diagnostic { 2742 title: "Syntax error".into(), 2743 text, 2744 hint: None, 2745 level: Level::Error, 2746 location: Some(Location { 2747 label: Label { 2748 text: Some(label.to_string()), 2749 span: adjusted_location, 2750 }, 2751 path: path.clone(), 2752 src: src.clone(), 2753 extra_labels: vec![], 2754 }), 2755 } 2756 } 2757 2758 Error::ImportCycle { modules } => { 2759 let mut text = "The import statements for these modules form a cycle: 2760" 2761 .into(); 2762 write_cycle(&mut text, modules); 2763 text.push_str( 2764 "Gleam doesn't support dependency cycles like these, please break the 2765cycle to continue.", 2766 ); 2767 Diagnostic { 2768 title: "Import cycle".into(), 2769 text, 2770 hint: None, 2771 level: Level::Error, 2772 location: None, 2773 } 2774 } 2775 2776 Error::PackageCycle { packages } => { 2777 let mut text = "The dependencies for these packages form a cycle: 2778" 2779 .into(); 2780 write_cycle(&mut text, packages); 2781 text.push_str( 2782 "Gleam doesn't support dependency cycles like these, please break the 2783cycle to continue.", 2784 ); 2785 Diagnostic { 2786 title: "Dependency cycle".into(), 2787 text, 2788 hint: None, 2789 level: Level::Error, 2790 location: None, 2791 } 2792 } 2793 2794 Error::UnknownImport { import, details } => { 2795 let UnknownImportDetails { 2796 module, 2797 location, 2798 path, 2799 src, 2800 modules, 2801 } = details.as_ref(); 2802 let text = wrap(&format!( 2803 "The module `{module}` is trying to import the module `{import}`, \ 2804but it cannot be found." 2805 )); 2806 Diagnostic { 2807 title: "Unknown import".into(), 2808 text, 2809 hint: None, 2810 level: Level::Error, 2811 location: Some(Location { 2812 label: Label { 2813 text: did_you_mean(import, modules), 2814 span: *location, 2815 }, 2816 path: path.clone(), 2817 src: src.clone(), 2818 extra_labels: vec![], 2819 }), 2820 } 2821 } 2822 2823 Error::StandardIo { action, err } => { 2824 let err = match err { 2825 Some(e) => format!( 2826 "\nThe error message from the stdio library was:\n\n {}\n", 2827 std_io_error_kind_text(e) 2828 ), 2829 None => "".into(), 2830 }; 2831 Diagnostic { 2832 title: "Standard IO failure".into(), 2833 text: format!( 2834 "An error occurred while trying to {}: 2835 2836{}", 2837 action.text(), 2838 err, 2839 ), 2840 hint: None, 2841 location: None, 2842 level: Level::Error, 2843 } 2844 } 2845 2846 Error::Format { problem_files } => { 2847 let files: Vec<_> = problem_files 2848 .iter() 2849 .map(|formatted| formatted.source.as_str()) 2850 .map(|p| format!(" - {p}")) 2851 .sorted() 2852 .collect(); 2853 let mut text = files.iter().join("\n"); 2854 text.push('\n'); 2855 Diagnostic { 2856 title: "These files have not been formatted".into(), 2857 text, 2858 hint: None, 2859 location: None, 2860 level: Level::Error, 2861 } 2862 } 2863 2864 Error::ForbiddenWarnings { count } => { 2865 let word_warning = match count { 2866 1 => "warning", 2867 _ => "warnings", 2868 }; 2869 let text = "Your project was compiled with the `--warnings-as-errors` flag. 2870Fix the warnings and try again." 2871 .into(); 2872 Diagnostic { 2873 title: format!("{count} {word_warning} generated."), 2874 text, 2875 hint: None, 2876 location: None, 2877 level: Level::Error, 2878 } 2879 } 2880 2881 Error::JavaScript { src, path, error } => match error { 2882 javascript::Error::Unsupported { feature, location } => Diagnostic { 2883 title: "Unsupported feature for compilation target".into(), 2884 text: format!("{feature} is not supported for JavaScript compilation."), 2885 hint: None, 2886 level: Level::Error, 2887 location: Some(Location { 2888 label: Label { 2889 text: None, 2890 span: *location, 2891 }, 2892 path: path.clone(), 2893 src: src.clone(), 2894 extra_labels: vec![], 2895 }), 2896 }, 2897 }, 2898 2899 Error::DownloadPackageError { 2900 package_name, 2901 package_version, 2902 error, 2903 } => { 2904 let text = format!( 2905 "A problem was encountered when downloading `{package_name}` {package_version}. 2906The error from the package manager client was: 2907 2908 {error}" 2909 ); 2910 Diagnostic { 2911 title: "Failed to download package".into(), 2912 text, 2913 hint: None, 2914 location: None, 2915 level: Level::Error, 2916 } 2917 } 2918 2919 Error::Http(error) => { 2920 let text = format!( 2921 "A HTTP request failed. 2922The error from the HTTP client was: 2923 2924 {error}" 2925 ); 2926 Diagnostic { 2927 title: "HTTP error".into(), 2928 text, 2929 hint: None, 2930 location: None, 2931 level: Level::Error, 2932 } 2933 } 2934 2935 Error::InvalidVersionFormat { input, error } => { 2936 let text = format!( 2937 "I was unable to parse the version \"{input}\". 2938The error from the parser was: 2939 2940 {error}" 2941 ); 2942 Diagnostic { 2943 title: "Invalid version format".into(), 2944 text, 2945 hint: None, 2946 location: None, 2947 level: Level::Error, 2948 } 2949 } 2950 2951 Error::DependencyCanonicalizationFailed(package) => { 2952 let text = format!("Local package `{package}` has no canonical path"); 2953 2954 Diagnostic { 2955 title: "Failed to create canonical path".into(), 2956 text, 2957 hint: None, 2958 location: None, 2959 level: Level::Error, 2960 } 2961 } 2962 2963 Error::DependencyResolutionFailed(error) => { 2964 let text = format!( 2965 "An error occurred while determining what dependency packages and 2966versions should be downloaded. 2967The error from the version resolver library was: 2968 2969{}", 2970 wrap(error) 2971 ); 2972 Diagnostic { 2973 title: "Dependency resolution failed".into(), 2974 text, 2975 hint: None, 2976 location: None, 2977 level: Level::Error, 2978 } 2979 } 2980 2981 Error::GitDependencyUnsupported => Diagnostic { 2982 title: "Git dependencies are not currently supported".into(), 2983 text: "Please remove all git dependencies from the gleam.toml file".into(), 2984 hint: None, 2985 location: None, 2986 level: Level::Error, 2987 }, 2988 2989 Error::WrongDependencyProvided { 2990 path, 2991 expected, 2992 found, 2993 } => { 2994 let text = format!( 2995 "Expected package `{expected}` at path `{path}` but found `{found}` instead.", 2996 ); 2997 2998 Diagnostic { 2999 title: "Wrong dependency provided".into(), 3000 text, 3001 hint: None, 3002 location: None, 3003 level: Level::Error, 3004 } 3005 } 3006 3007 Error::ProvidedDependencyConflict { 3008 package, 3009 source_1, 3010 source_2, 3011 } => { 3012 let text = format!( 3013 "The package `{package}` is provided as both `{source_1}` and `{source_2}`.", 3014 ); 3015 3016 Diagnostic { 3017 title: "Conflicting provided dependencies".into(), 3018 text, 3019 hint: None, 3020 location: None, 3021 level: Level::Error, 3022 } 3023 } 3024 3025 Error::DuplicateDependency(name) => { 3026 let text = format!( 3027 "The package `{name}` is specified in both the dependencies and 3028dev-dependencies sections of the gleam.toml file." 3029 ); 3030 Diagnostic { 3031 title: "Dependency duplicated".into(), 3032 text, 3033 hint: None, 3034 location: None, 3035 level: Level::Error, 3036 } 3037 } 3038 3039 Error::MissingHexPublishFields { 3040 description_missing, 3041 licence_missing, 3042 } => { 3043 let mut text = 3044 "Licence information and package description are required to publish a 3045package to Hex.\n" 3046 .to_string(); 3047 text.push_str(if *description_missing && *licence_missing { 3048 r#"Add the licences and description fields to your gleam.toml file. 3049 3050description = "" 3051licences = ["Apache-2.0"]"# 3052 } else if *description_missing { 3053 r#"Add the description field to your gleam.toml file. 3054 3055description = """# 3056 } else { 3057 r#"Add the licences field to your gleam.toml file. 3058 3059licences = ["Apache-2.0"]"# 3060 }); 3061 Diagnostic { 3062 title: "Missing required package fields".into(), 3063 text, 3064 hint: None, 3065 location: None, 3066 level: Level::Error, 3067 } 3068 } 3069 3070 Error::PublishNonHexDependencies { package } => Diagnostic { 3071 title: "Unblished dependencies".into(), 3072 text: wrap_format!( 3073 "The package cannot be published to Hex \ 3074because dependency `{package}` is not a Hex dependency.", 3075 ), 3076 hint: None, 3077 location: None, 3078 level: Level::Error, 3079 }, 3080 3081 Error::UnsupportedBuildTool { 3082 package, 3083 build_tools, 3084 } => { 3085 let text = wrap_format!( 3086 "The package `{}` cannot be built as it does not use \ 3087a build tool supported by Gleam. It uses {:?}. 3088 3089If you would like us to support this package please let us know by opening an \ 3090issue in our tracker: https://github.com/gleam-lang/gleam/issues", 3091 package, 3092 build_tools 3093 ); 3094 Diagnostic { 3095 title: "Unsupported build tool".into(), 3096 text, 3097 hint: None, 3098 location: None, 3099 level: Level::Error, 3100 } 3101 } 3102 3103 Error::FailedToOpenDocs { path, error } => { 3104 let error = format!("\nThe error message from the library was:\n\n {error}\n"); 3105 let text = format!( 3106 "An error occurred while trying to open the docs: 3107 3108 {path} 3109{error}", 3110 ); 3111 Diagnostic { 3112 title: "Failed to open docs".into(), 3113 text, 3114 hint: None, 3115 level: Level::Error, 3116 location: None, 3117 } 3118 } 3119 3120 Error::IncompatibleCompilerVersion { 3121 package, 3122 required_version, 3123 gleam_version, 3124 } => { 3125 let text = format!( 3126 "The package `{package}` requires a Gleam version satisfying {required_version} \ 3127but you are using v{gleam_version}.", 3128 ); 3129 Diagnostic { 3130 title: "Incompatible Gleam version".into(), 3131 text, 3132 hint: None, 3133 location: None, 3134 level: Level::Error, 3135 } 3136 } 3137 3138 Error::InvalidRuntime { 3139 target, 3140 invalid_runtime, 3141 } => { 3142 let text = format!("Invalid runtime for {target} target: {invalid_runtime}"); 3143 3144 let hint = match target { 3145 Target::JavaScript => { 3146 Some("available runtimes for JavaScript are: node, deno.".into()) 3147 } 3148 Target::Erlang => Some( 3149 "You can not set a runtime for Erlang. Did you mean to target JavaScript?" 3150 .into(), 3151 ), 3152 }; 3153 3154 Diagnostic { 3155 title: format!("Invalid runtime for {target}"), 3156 text, 3157 hint, 3158 location: None, 3159 level: Level::Error, 3160 } 3161 } 3162 3163 Error::JavaScriptPreludeRequired => Diagnostic { 3164 title: "JavaScript prelude required".into(), 3165 text: "The --javascript-prelude flag must be given when compiling to JavaScript." 3166 .into(), 3167 level: Level::Error, 3168 location: None, 3169 hint: None, 3170 }, 3171 } 3172 } 3173} 3174 3175fn std_io_error_kind_text(kind: &std::io::ErrorKind) -> String { 3176 use std::io::ErrorKind; 3177 match kind { 3178 ErrorKind::NotFound => "Could not find the stdio stream".into(), 3179 ErrorKind::PermissionDenied => "Permission was denied".into(), 3180 ErrorKind::ConnectionRefused => "Connection was refused".into(), 3181 ErrorKind::ConnectionReset => "Connection was reset".into(), 3182 ErrorKind::ConnectionAborted => "Connection was aborted".into(), 3183 ErrorKind::NotConnected => "Was not connected".into(), 3184 ErrorKind::AddrInUse => "The stream was already in use".into(), 3185 ErrorKind::AddrNotAvailable => "The stream was not available".into(), 3186 ErrorKind::BrokenPipe => "The pipe was broken".into(), 3187 ErrorKind::AlreadyExists => "A handle to the stream already exists".into(), 3188 ErrorKind::WouldBlock => "This operation would block when it was requested not to".into(), 3189 ErrorKind::InvalidInput => "Some parameter was invalid".into(), 3190 ErrorKind::InvalidData => "The data was invalid. Check that the encoding is UTF-8".into(), 3191 ErrorKind::TimedOut => "The operation timed out".into(), 3192 ErrorKind::WriteZero => { 3193 "An attempt was made to write, but all bytes could not be written".into() 3194 } 3195 ErrorKind::Interrupted => "The operation was interrupted".into(), 3196 ErrorKind::UnexpectedEof => "The end of file was reached before it was expected".into(), 3197 _ => "An unknown error occurred".into(), 3198 } 3199} 3200 3201fn write_cycle(buffer: &mut String, cycle: &[EcoString]) { 3202 buffer.push_str( 3203 " 3204 ┌─────┐\n", 3205 ); 3206 for (index, name) in cycle.iter().enumerate() { 3207 if index != 0 { 3208 buffer.push_str(" │ ↓\n"); 3209 } 3210 buffer.push_str(""); 3211 buffer.push_str(name); 3212 buffer.push('\n'); 3213 } 3214 buffer.push_str(" └─────┘\n"); 3215} 3216 3217fn hint_alternative_operator(op: &BinOp, given: &Type) -> Option<String> { 3218 match op { 3219 BinOp::AddInt if given.is_float() => Some(hint_numeric_message("+.", "Float")), 3220 BinOp::DivInt if given.is_float() => Some(hint_numeric_message("/.", "Float")), 3221 BinOp::GtEqInt if given.is_float() => Some(hint_numeric_message(">=.", "Float")), 3222 BinOp::GtInt if given.is_float() => Some(hint_numeric_message(">.", "Float")), 3223 BinOp::LtEqInt if given.is_float() => Some(hint_numeric_message("<=.", "Float")), 3224 BinOp::LtInt if given.is_float() => Some(hint_numeric_message("<.", "Float")), 3225 BinOp::MultInt if given.is_float() => Some(hint_numeric_message("*.", "Float")), 3226 BinOp::SubInt if given.is_float() => Some(hint_numeric_message("-.", "Float")), 3227 3228 BinOp::AddFloat if given.is_int() => Some(hint_numeric_message("+", "Int")), 3229 BinOp::DivFloat if given.is_int() => Some(hint_numeric_message("/", "Int")), 3230 BinOp::GtEqFloat if given.is_int() => Some(hint_numeric_message(">=", "Int")), 3231 BinOp::GtFloat if given.is_int() => Some(hint_numeric_message(">", "Int")), 3232 BinOp::LtEqFloat if given.is_int() => Some(hint_numeric_message("<=", "Int")), 3233 BinOp::LtFloat if given.is_int() => Some(hint_numeric_message("<", "Int")), 3234 BinOp::MultFloat if given.is_int() => Some(hint_numeric_message("*", "Int")), 3235 BinOp::SubFloat if given.is_int() => Some(hint_numeric_message("-", "Int")), 3236 3237 BinOp::AddInt if given.is_string() => Some(hint_string_message()), 3238 BinOp::AddFloat if given.is_string() => Some(hint_string_message()), 3239 3240 _ => None, 3241 } 3242} 3243 3244fn hint_numeric_message(alt: &str, type_: &str) -> String { 3245 format!("the {alt} operator can be used with {type_}s\n") 3246} 3247 3248fn hint_string_message() -> String { 3249 wrap( 3250 "Strings can be joined using the `append` or `concat` \ 3251functions from the `gleam/string` module.", 3252 ) 3253} 3254 3255#[derive(Debug, Clone, PartialEq, Eq)] 3256pub struct Unformatted { 3257 pub source: Utf8PathBuf, 3258 pub destination: Utf8PathBuf, 3259 pub input: EcoString, 3260 pub output: String, 3261} 3262 3263pub fn wrap(text: &str) -> String { 3264 textwrap::fill(text, std::cmp::min(75, textwrap::termwidth())) 3265}