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 / build.rs
32 kB 1002 lines
1// SPDX-License-Identifier: Apache-2.0 2// SPDX-FileCopyrightText: 2021 The Gleam contributors 3 4#![allow(warnings)] 5 6mod elixir_libraries; 7mod module_loader; 8mod native_file_copier; 9pub mod package_compiler; 10mod package_loader; 11mod project_compiler; 12mod telemetry; 13 14#[cfg(test)] 15mod tests; 16 17pub use self::package_compiler::PackageCompiler; 18pub use self::package_loader::StaleTracker; 19pub use self::project_compiler::{Built, Options, ProjectCompiler}; 20pub use self::telemetry::{NullTelemetry, Telemetry}; 21 22use crate::ast::{ 23 self, CallArg, CustomType, DefinitionLocation, TypeAst, TypedArg, TypedClauseGuard, 24 TypedConstant, TypedCustomType, TypedDefinitions, TypedExpr, TypedFunction, TypedImport, 25 TypedModuleConstant, TypedPattern, TypedRecordConstructor, TypedStatement, TypedTypeAlias, 26}; 27use crate::reference; 28use crate::type_::error::Named; 29use crate::type_::{Type, TypedCallArg}; 30use crate::{ 31 ast::{Definition, SrcSpan, TypedModule}, 32 config::{self, PackageConfig}, 33 erlang, 34 error::{Error, FileIoAction, FileKind}, 35 io::OutputFile, 36 parse::extra::{Comment, ModuleExtra}, 37 type_, 38}; 39use camino::Utf8PathBuf; 40use clap::ValueEnum; 41use ecow::EcoString; 42use itertools::Itertools; 43use serde::{Deserialize, Serialize}; 44use std::fmt::{Debug, Display}; 45use std::sync::Arc; 46use std::time::SystemTime; 47use std::{collections::HashMap, ffi::OsString, fs::DirEntry, iter::Peekable, process}; 48use strum::{Display, EnumIter, EnumString, EnumVariantNames, VariantNames}; 49use vec1::Vec1; 50 51#[derive( 52 Debug, 53 Serialize, 54 Deserialize, 55 ValueEnum, 56 EnumString, 57 EnumVariantNames, 58 EnumIter, 59 Clone, 60 Copy, 61 PartialEq, 62 Eq, 63)] 64#[strum(serialize_all = "lowercase")] 65#[clap(rename_all = "lower")] 66#[serde(rename_all = "lowercase")] 67pub enum Target { 68 #[strum(serialize = "erl")] 69 #[serde(alias = "erl")] 70 #[clap(alias = "erl")] 71 Erlang, 72 #[strum(serialize = "js")] 73 #[serde(alias = "js")] 74 #[clap(alias = "js")] 75 JavaScript, 76 #[strum(serialize = "wasm")] 77 #[serde(rename = "wasm")] 78 Wasm, 79} 80 81impl Target { 82 pub fn as_presentable_str(&self) -> &str { 83 match self { 84 Target::Erlang => "Erlang", 85 Target::JavaScript => "JavaScript", 86 Target::Wasm => "Wasm", 87 } 88 } 89 90 pub fn variant_strings() -> Vec<EcoString> { 91 Self::VARIANTS.iter().map(|s| (*s).into()).collect() 92 } 93 94 /// Returns `true` if the target is [`JavaScript`]. 95 /// 96 /// [`JavaScript`]: Target::JavaScript 97 #[must_use] 98 pub fn is_javascript(&self) -> bool { 99 matches!(self, Self::JavaScript) 100 } 101 102 /// Returns `true` if the target is [`Erlang`]. 103 /// 104 /// [`Erlang`]: Target::Erlang 105 #[must_use] 106 pub fn is_erlang(&self) -> bool { 107 matches!(self, Self::Erlang) 108 } 109} 110 111#[derive(Debug, PartialEq, Eq, Clone, Copy)] 112/// This is used to skip compiling the root package when running the main 113/// function coming from a dependency. This way a dependency can be run even 114/// there's compilation errors in the root package. 115/// 116pub enum Compile { 117 /// The default compiler behaviour, compile all packages. 118 /// 119 All, 120 /// Only compile the dependency packages, skipping the root package. 121 /// 122 DepsOnly, 123} 124 125#[derive(Debug, PartialEq, Eq, Clone, Copy)] 126pub enum Codegen { 127 All, 128 DepsOnly, 129 None, 130} 131 132impl Codegen { 133 fn should_codegen(&self, is_root_package: bool) -> bool { 134 match self { 135 Codegen::All => true, 136 Codegen::DepsOnly => !is_root_package, 137 Codegen::None => false, 138 } 139 } 140} 141 142#[derive( 143 Debug, 144 Serialize, 145 Deserialize, 146 EnumString, 147 EnumVariantNames, 148 ValueEnum, 149 Clone, 150 Copy, 151 PartialEq, 152 Eq, 153)] 154#[clap(rename_all = "lower")] 155#[serde(rename_all = "lowercase")] 156#[strum(serialize_all = "lowercase")] 157pub enum Runtime { 158 #[strum(serialize = "node")] 159 #[serde(alias = "node")] 160 #[clap(alias = "node")] 161 NodeJs, 162 Deno, 163 Bun, 164} 165 166impl Runtime { 167 pub fn as_presentable_str(&self) -> &str { 168 match self { 169 Runtime::NodeJs => "NodeJS", 170 Runtime::Deno => "Deno", 171 Runtime::Bun => "Bun", 172 } 173 } 174} 175 176impl Default for Runtime { 177 fn default() -> Self { 178 Self::NodeJs 179 } 180} 181 182#[derive(Debug)] 183pub enum TargetCodegenConfiguration { 184 JavaScript { 185 emit_typescript_definitions: bool, 186 emit_source_maps: bool, 187 prelude_location: Utf8PathBuf, 188 }, 189 Erlang { 190 app_file: Option<ErlangAppCodegenConfiguration>, 191 }, 192 Wasm {}, 193} 194 195impl TargetCodegenConfiguration { 196 pub fn target(&self) -> Target { 197 match self { 198 Self::JavaScript { .. } => Target::JavaScript, 199 Self::Erlang { .. } => Target::Erlang, 200 Self::Wasm { .. } => Target::Wasm, 201 } 202 } 203} 204 205#[derive(Debug)] 206pub struct ErlangAppCodegenConfiguration { 207 pub include_dev_deps: bool, 208 /// Some packages have a different OTP application name than their package 209 /// name, as rebar3 (and Mix?) support this. The .app file must use the OTP 210 /// name, not the package name. 211 pub package_name_overrides: HashMap<EcoString, EcoString>, 212} 213 214#[derive( 215 Debug, 216 Serialize, 217 Deserialize, 218 Display, 219 EnumString, 220 EnumVariantNames, 221 EnumIter, 222 Clone, 223 Copy, 224 PartialEq, 225)] 226#[strum(serialize_all = "lowercase")] 227pub enum Mode { 228 Dev, 229 Prod, 230 Lsp, 231} 232 233impl Mode { 234 /// Returns `true` if the mode includes development code. 235 /// 236 pub fn includes_dev_code(&self) -> bool { 237 match self { 238 Self::Dev | Self::Lsp => true, 239 Self::Prod => false, 240 } 241 } 242 243 pub fn includes_dev_dependencies(&self) -> bool { 244 match self { 245 Mode::Dev | Mode::Lsp => true, 246 Mode::Prod => false, 247 } 248 } 249} 250 251#[test] 252fn mode_includes_dev_code() { 253 assert!(Mode::Dev.includes_dev_code()); 254 assert!(Mode::Lsp.includes_dev_code()); 255 assert!(!Mode::Prod.includes_dev_code()); 256} 257 258#[derive(Debug)] 259pub struct Package { 260 pub config: PackageConfig, 261 pub modules: Vec<Module>, 262 pub cached_module_names: Vec<EcoString>, 263} 264 265impl Package { 266 pub fn attach_doc_and_module_comments(&mut self) { 267 for mut module in &mut self.modules { 268 module.attach_doc_and_module_comments(); 269 } 270 } 271 272 pub fn into_modules_hashmap(self) -> HashMap<String, Module> { 273 self.modules 274 .into_iter() 275 .map(|m| (m.name.to_string(), m)) 276 .collect() 277 } 278} 279 280#[derive(Debug)] 281pub struct Module { 282 pub name: EcoString, 283 pub code: EcoString, 284 pub mtime: SystemTime, 285 pub input_path: Utf8PathBuf, 286 pub origin: Origin, 287 pub ast: TypedModule, 288 pub extra: ModuleExtra, 289 pub dependencies: Vec<(EcoString, SrcSpan)>, 290} 291 292#[derive(Debug)] 293/// A data structure used to store all definitions in a single homogeneous 294/// vector and sort them by their position in order to attach to each the right 295/// documentation. 296/// 297enum DocumentableDefinition<'a> { 298 Constant(&'a mut TypedModuleConstant), 299 TypeAlias(&'a mut TypedTypeAlias), 300 CustomType(&'a mut TypedCustomType), 301 Function(&'a mut TypedFunction), 302 Import(&'a mut TypedImport), 303} 304 305impl<'a> DocumentableDefinition<'a> { 306 pub fn location(&self) -> SrcSpan { 307 match self { 308 Self::Constant(module_constant) => module_constant.location, 309 Self::TypeAlias(type_alias) => type_alias.location, 310 Self::CustomType(custom_type) => custom_type.location, 311 Self::Function(function) => function.location, 312 Self::Import(import) => import.location, 313 } 314 } 315 316 pub fn put_doc(&mut self, new_documentation: (u32, EcoString)) { 317 match self { 318 Self::Import(_import) => (), 319 Self::Function(function) => { 320 let _ = function.documentation.replace(new_documentation); 321 } 322 Self::TypeAlias(type_alias) => { 323 let _ = type_alias.documentation.replace(new_documentation); 324 } 325 Self::CustomType(custom_type) => { 326 let _ = custom_type.documentation.replace(new_documentation); 327 } 328 Self::Constant(constant) => { 329 let _ = constant.documentation.replace(new_documentation); 330 } 331 } 332 } 333} 334 335impl Module { 336 pub fn erlang_name(&self) -> EcoString { 337 module_erlang_name(&self.name) 338 } 339 340 pub fn compiled_erlang_path(&self) -> Utf8PathBuf { 341 let mut path = Utf8PathBuf::from(&module_erlang_name(&self.name)); 342 assert!(path.set_extension("erl"), "Couldn't set file extension"); 343 path 344 } 345 346 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> { 347 self.ast.find_node(byte_index) 348 } 349 350 pub fn attach_doc_and_module_comments(&mut self) { 351 // Module Comments 352 self.ast.documentation = self 353 .extra 354 .module_comments 355 .iter() 356 .map(|span| Comment::from((span, self.code.as_str())).content.into()) 357 .collect(); 358 359 self.ast.type_info.documentation = self.ast.documentation.clone(); 360 361 // Order definitions to avoid misassociating doc comments after the 362 // order has changed during compilation. 363 let TypedDefinitions { 364 imports, 365 constants, 366 custom_types, 367 type_aliases, 368 functions, 369 } = &mut self.ast.definitions; 370 371 let mut definitions = ((imports.iter_mut()).map(DocumentableDefinition::Import)) 372 .chain((constants.iter_mut()).map(DocumentableDefinition::Constant)) 373 .chain((custom_types.iter_mut()).map(DocumentableDefinition::CustomType)) 374 .chain((type_aliases.iter_mut()).map(DocumentableDefinition::TypeAlias)) 375 .chain((functions.iter_mut()).map(DocumentableDefinition::Function)) 376 .sorted_by_key(|definition| definition.location()) 377 .collect_vec(); 378 379 // Doc Comments 380 let mut doc_comments = self.extra.doc_comments.iter().peekable(); 381 for definition in &mut definitions { 382 let (docs_start, docs): (u32, Vec<&str>) = doc_comments_before( 383 &mut doc_comments, 384 &self.extra, 385 definition.location().start, 386 &self.code, 387 ); 388 if !docs.is_empty() { 389 let doc = docs.join("\n").into(); 390 definition.put_doc((docs_start, doc)); 391 } 392 393 if let DocumentableDefinition::CustomType(CustomType { constructors, .. }) = definition 394 { 395 for constructor in constructors { 396 let (docs_start, docs): (u32, Vec<&str>) = doc_comments_before( 397 &mut doc_comments, 398 &self.extra, 399 constructor.location.start, 400 &self.code, 401 ); 402 if !docs.is_empty() { 403 let doc = docs.join("\n").into(); 404 constructor.put_doc((docs_start, doc)); 405 } 406 407 for argument in constructor.arguments.iter_mut() { 408 let (docs_start, docs): (u32, Vec<&str>) = doc_comments_before( 409 &mut doc_comments, 410 &self.extra, 411 argument.location.start, 412 &self.code, 413 ); 414 if !docs.is_empty() { 415 let doc = docs.join("\n").into(); 416 argument.put_doc((docs_start, doc)); 417 } 418 } 419 } 420 } 421 } 422 } 423} 424 425pub fn module_erlang_name(gleam_name: &EcoString) -> EcoString { 426 gleam_name.replace("/", "@") 427} 428 429#[derive(Debug, Clone, PartialEq)] 430pub struct UnqualifiedImport<'a> { 431 pub name: &'a EcoString, 432 pub module: &'a EcoString, 433 pub location: &'a SrcSpan, 434 /// The position of the original name. For example, in `type Wibble as 435 /// Wobble`, it points to the start of `Wibble`. 436 pub name_position: u32, 437 pub as_name: Option<&'a EcoString>, 438} 439 440impl<'a> UnqualifiedImport<'a> { 441 /// The location of the original name, excluding the potential `as ...` 442 /// clause or the `type` keyword. For example, in `type Wibble as Wobble`, 443 /// it covers `Wibble`. 444 pub fn name_location(&self) -> SrcSpan { 445 SrcSpan::new( 446 self.name_position, 447 self.name_position + self.name.len() as u32, 448 ) 449 } 450 451 /// If the import is aliased, it is the start of the alias. Otherwise, it is 452 /// the start of the imported name. 453 /// 454 /// For example, in `type Wibble as Wobble`, the used name will start at 455 /// `Wobble`. In `type Wibble`, it will start at `Wibble`. 456 pub fn used_name_position(&self) -> u32 { 457 match self.as_name { 458 // For aliases, the location span will cover the whole of `type 459 // Wibble as Wobble`. 460 // So, the used name will start 6 chars (length of `Wobble`) before 461 // the span ends. 462 Some(as_name) => self.location.end - as_name.len() as u32, 463 // For non-aliased imports, use the start of the imported name 464 // location. It covers `Wibble` in `type Wibble as Wobble`. 465 None => self.name_position, 466 } 467 } 468 469 pub fn name_kind(&self) -> Named { 470 let is_upname = match self.name.chars().next() { 471 Some(c) => c.is_uppercase(), 472 None => false, 473 }; 474 475 if is_upname { 476 Named::CustomTypeVariant 477 } else { 478 Named::Function 479 } 480 } 481 482 pub fn is_type(&self) -> bool { 483 // If the position is not at the start location then that means 484 // that there is a `type` keyword before it, along with an amount of 485 // whitespace and optional comments 486 self.name_position != self.location.start 487 } 488} 489 490/// The position of a located expression. Used to determine extra context, 491/// such as whether to provide label completions if the expression is in 492/// argument position. 493#[derive(Debug, Clone, PartialEq)] 494pub enum ExpressionPosition<'a> { 495 Expression, 496 ArgumentOrLabel { 497 called_function: &'a TypedExpr, 498 function_arguments: &'a [TypedCallArg], 499 }, 500 /// This is for an expression that is used in a record update spread: 501 /// ```gleam 502 /// Wibble(..wibble, a: 1) 503 /// // ^^^^^^^^ This! 504 /// ``` 505 UpdatedRecord { 506 /// These are all the record fields that are not being updated in the 507 /// update. 508 unchanged_record_fields: Vec<(EcoString, Arc<Type>)>, 509 }, 510} 511 512#[derive(Debug, Clone, PartialEq)] 513pub enum Located<'a> { 514 Pattern(&'a TypedPattern), 515 PatternSpread { 516 spread_location: SrcSpan, 517 pattern: &'a TypedPattern, 518 }, 519 // A prefix alias or a suffix variable defined in a string prefix pattern: 520 // "prefix" as alias <> suffix 521 StringPrefixPatternVariable { 522 location: SrcSpan, 523 name: &'a EcoString, 524 }, 525 Statement(&'a TypedStatement), 526 Expression { 527 expression: &'a TypedExpr, 528 position: ExpressionPosition<'a>, 529 }, 530 VariantConstructorDefinition(&'a TypedRecordConstructor), 531 FunctionBody(&'a TypedFunction), 532 Arg(&'a TypedArg), 533 Annotation { 534 ast: &'a TypeAst, 535 type_: std::sync::Arc<Type>, 536 }, 537 TypeVariable { 538 name: EcoString, 539 location: SrcSpan, 540 }, 541 UnqualifiedImport(UnqualifiedImport<'a>), 542 /// The label of a labelled argument in a function call. 543 Label { 544 location: SrcSpan, 545 /// The type of the labelled argument's value (used for hover). 546 field_type: std::sync::Arc<Type>, 547 }, 548 /// A record field label at its definition in a custom type variant. 549 RecordLabelDefinition { 550 location: SrcSpan, 551 /// The type of the field (used for hover). 552 field_type: std::sync::Arc<Type>, 553 label: EcoString, 554 /// The name of the custom type this field belongs to. The type is 555 /// being defined in the module being analysed, so only its name is 556 /// needed. 557 type_name: EcoString, 558 }, 559 /// A record field label used in a record constructor call or pattern, or 560 /// in a record update. 561 RecordLabelUsage { 562 location: SrcSpan, 563 /// The type of the field's value (used for hover). 564 field_type: std::sync::Arc<Type>, 565 label: EcoString, 566 /// The record type the field belongs to. 567 record_type: std::sync::Arc<Type>, 568 /// The name of the variant the field was used with. 569 variant: EcoString, 570 }, 571 /// The label of a record field access: `record.label`. 572 RecordAccessLabel { 573 location: SrcSpan, 574 /// The type of the field (used for hover). 575 field_type: std::sync::Arc<Type>, 576 label: EcoString, 577 /// The record type the field belongs to. 578 record_type: std::sync::Arc<Type>, 579 /// The documentation of the field (used for hover). 580 documentation: Option<EcoString>, 581 }, 582 ModuleName { 583 location: SrcSpan, 584 module_name: EcoString, 585 module_alias: EcoString, 586 layer: ast::Layer, 587 }, 588 Constant(&'a TypedConstant), 589 ClauseGuard(&'a TypedClauseGuard), 590 591 // A module's top level definitions 592 ModuleFunction(&'a TypedFunction), 593 ModuleConstant(&'a TypedModuleConstant), 594 ModuleImport(&'a TypedImport), 595 ModuleCustomType(&'a TypedCustomType), 596 ModuleTypeAlias(&'a TypedTypeAlias), 597} 598 599impl<'a> Located<'a> { 600 // Looks up the type constructor for the given type and then create the location. 601 fn type_location( 602 &self, 603 importable_modules: &'a im::HashMap<EcoString, type_::ModuleInterface>, 604 type_: std::sync::Arc<Type>, 605 ) -> Option<DefinitionLocation> { 606 type_constructor_from_modules(importable_modules, type_).map(|t| DefinitionLocation { 607 module: Some(t.module.clone()), 608 span: t.origin, 609 }) 610 } 611 612 /// Looks up the location at which a record field label was defined, using 613 /// the label definitions gathered during analysis. 614 fn label_definition_location( 615 &self, 616 importable_modules: &'a im::HashMap<EcoString, type_::ModuleInterface>, 617 record_type: &Arc<Type>, 618 label: &EcoString, 619 variant: Option<&EcoString>, 620 ) -> Option<DefinitionLocation> { 621 let (module_name, type_name) = record_type.named_type_name()?; 622 let module = importable_modules.get(&module_name)?; 623 let key = reference::RecordLabel { 624 type_module: module_name.clone(), 625 type_name, 626 label: label.clone(), 627 }; 628 let definitions = module.references.label_definitions.get(&key)?; 629 // A label can be defined in multiple variants of the same type. If we 630 // know which variant the label was used with we jump to its 631 // definition in that variant. Otherwise the label comes from a 632 // `record.field` access, which works across all the variants defining 633 // it, so we jump to the first definition. 634 let definition = match variant { 635 Some(variant) => definitions 636 .iter() 637 .find(|definition| &definition.variant == variant)?, 638 None => definitions.first()?, 639 }; 640 Some(DefinitionLocation { 641 module: Some(module_name), 642 span: definition.location, 643 }) 644 } 645 646 pub fn definition_location( 647 &self, 648 importable_modules: &'a im::HashMap<EcoString, type_::ModuleInterface>, 649 ) -> Option<DefinitionLocation> { 650 match self { 651 Self::PatternSpread { .. } => None, 652 Self::Pattern(pattern) => pattern.definition_location(), 653 Self::StringPrefixPatternVariable { location, .. } => Some(DefinitionLocation { 654 module: None, 655 span: *location, 656 }), 657 Self::Statement(statement) => statement.definition_location(), 658 Self::FunctionBody(statement) => None, 659 Self::Expression { expression, .. } => expression.definition_location(), 660 661 Self::ModuleImport(import) => Some(DefinitionLocation { 662 module: Some(import.module.clone()), 663 span: SrcSpan { start: 0, end: 0 }, 664 }), 665 Self::ModuleConstant(constant) => Some(DefinitionLocation { 666 module: None, 667 span: constant.location, 668 }), 669 Self::ModuleCustomType(custom_type) => Some(DefinitionLocation { 670 module: None, 671 span: custom_type.location, 672 }), 673 Self::ModuleFunction(function) => Some(DefinitionLocation { 674 module: None, 675 span: function.location, 676 }), 677 Self::ModuleTypeAlias(type_alias) => Some(DefinitionLocation { 678 module: None, 679 span: type_alias.location, 680 }), 681 682 Self::VariantConstructorDefinition(record) => Some(DefinitionLocation { 683 module: None, 684 span: record.location, 685 }), 686 Self::UnqualifiedImport(import @ UnqualifiedImport { module, name, .. }) => { 687 importable_modules.get(*module).and_then(|m| { 688 if import.is_type() { 689 m.types.get(*name).map(|t| DefinitionLocation { 690 module: Some((*module).clone()), 691 span: t.origin, 692 }) 693 } else { 694 m.values.get(*name).map(|v| DefinitionLocation { 695 module: Some((*module).clone()), 696 span: v.definition_location().span, 697 }) 698 } 699 }) 700 } 701 Self::Arg(_) => None, 702 Self::Annotation { type_, .. } => self.type_location(importable_modules, type_.clone()), 703 Self::Label { .. } => None, 704 Self::RecordLabelUsage { 705 record_type, 706 label, 707 variant, 708 .. 709 } => self.label_definition_location( 710 importable_modules, 711 record_type, 712 label, 713 Some(variant), 714 ), 715 Self::RecordAccessLabel { 716 record_type, label, .. 717 } => self.label_definition_location(importable_modules, record_type, label, None), 718 // Already at the definition; go-to-definition jumps to itself. 719 Self::RecordLabelDefinition { location, .. } => Some(DefinitionLocation { 720 module: None, 721 span: *location, 722 }), 723 Self::TypeVariable { .. } => None, 724 Self::ModuleName { module_name, .. } => Some(DefinitionLocation { 725 module: Some(module_name.clone()), 726 span: SrcSpan::new(0, 0), 727 }), 728 Self::Constant(constant) => constant.definition_location(), 729 Self::ClauseGuard(guard) => guard.definition_location(), 730 } 731 } 732 733 pub(crate) fn type_(&self) -> Option<Arc<Type>> { 734 match self { 735 Located::Pattern(pattern) => Some(pattern.type_()), 736 Located::StringPrefixPatternVariable { .. } => Some(type_::string()), 737 Located::Statement(statement) => Some(statement.type_()), 738 Located::Expression { expression, .. } => Some(expression.type_()), 739 Located::Arg(arg) => Some(arg.type_.clone()), 740 Located::Label { field_type, .. } 741 | Located::RecordLabelDefinition { field_type, .. } 742 | Located::RecordLabelUsage { field_type, .. } 743 | Located::RecordAccessLabel { field_type, .. } => Some(field_type.clone()), 744 Located::Annotation { type_, .. } => Some(type_.clone()), 745 Located::Constant(constant) => Some(constant.type_()), 746 Located::ClauseGuard(guard) => Some(guard.type_()), 747 748 Located::PatternSpread { .. } 749 | Located::ModuleConstant(_) 750 | Located::ModuleCustomType(_) 751 | Located::ModuleFunction(_) 752 | Located::ModuleImport(_) 753 | Located::ModuleTypeAlias(_) 754 | Located::VariantConstructorDefinition(_) 755 | Located::FunctionBody(_) 756 | Located::UnqualifiedImport(_) 757 | Located::ModuleName { .. } 758 | Located::TypeVariable { .. } => None, 759 } 760 } 761 762 pub fn type_definition_locations( 763 &self, 764 importable_modules: &im::HashMap<EcoString, type_::ModuleInterface>, 765 ) -> Option<Vec<DefinitionLocation>> { 766 let type_ = self.type_()?; 767 Some(type_to_definition_locations(type_, importable_modules)) 768 } 769} 770 771/// Returns the locations of all the types that one could reach starting from 772/// the given type (included). This includes all types that are part of a 773/// tuple/function type or that are used as args in a named type. 774/// 775/// For example, given this type `Dict(Int, #(Wibble, Wobble))` all the 776/// "reachable" include: `Dict`, `Int`, `Wibble` and `Wobble`. 777/// 778/// This is what powers the "go to type definition" capability of the language 779/// server. 780/// 781fn type_to_definition_locations<'a>( 782 type_: Arc<Type>, 783 importable_modules: &'a im::HashMap<EcoString, type_::ModuleInterface>, 784) -> Vec<DefinitionLocation> { 785 match type_.as_ref() { 786 // For named types we start with the location of the named type itself 787 // followed by the locations of all types they reference in their args. 788 // 789 // For example with a `Dict(Wibble, Wobble)` we'd start with the 790 // definition of `Dict`, followed by the definition of `Wibble` and 791 // `Wobble`. 792 // 793 Type::Named { 794 module, 795 name, 796 arguments, 797 .. 798 } => { 799 let Some(module) = importable_modules.get(module) else { 800 return vec![]; 801 }; 802 803 let Some(type_) = module.get_importable_type(&name) else { 804 return vec![]; 805 }; 806 807 let mut locations = vec![DefinitionLocation { 808 module: Some(module.name.clone()), 809 span: type_.origin, 810 }]; 811 for argument in arguments { 812 locations.extend(type_to_definition_locations( 813 argument.clone(), 814 importable_modules, 815 )); 816 } 817 locations 818 } 819 820 // For fn types we just get the locations of their arguments and return 821 // type. 822 // 823 Type::Fn { arguments, return_ } => arguments 824 .iter() 825 .flat_map(|argument| type_to_definition_locations(argument.clone(), importable_modules)) 826 .chain(type_to_definition_locations( 827 return_.clone(), 828 importable_modules, 829 )) 830 .collect_vec(), 831 832 // In case of a var we just follow it and get the locations of the type 833 // it points to. 834 // 835 Type::Var { type_ } => match type_.borrow().clone() { 836 type_::TypeVar::Unbound { .. } | type_::TypeVar::Generic { .. } => vec![], 837 type_::TypeVar::Link { type_ } => { 838 type_to_definition_locations(type_, importable_modules) 839 } 840 }, 841 842 // In case of tuples we get the locations of the wrapped types. 843 // 844 Type::Tuple { elements } => elements 845 .iter() 846 .flat_map(|element| type_to_definition_locations(element.clone(), importable_modules)) 847 .collect_vec(), 848 } 849} 850 851// Looks up the type constructor for the given type 852pub fn type_constructor_from_modules( 853 importable_modules: &im::HashMap<EcoString, type_::ModuleInterface>, 854 type_: std::sync::Arc<Type>, 855) -> Option<&type_::TypeConstructor> { 856 let type_ = type_::collapse_links(type_); 857 match type_.as_ref() { 858 Type::Named { name, module, .. } => importable_modules 859 .get(module) 860 .and_then(|i| i.types.get(name)), 861 _ => None, 862 } 863} 864 865#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] 866pub enum Origin { 867 Src, 868 Test, 869 Dev, 870} 871 872impl Origin { 873 /// Returns `true` if the origin is [`Src`]. 874 /// 875 /// [`Src`]: Origin::Src 876 #[must_use] 877 pub fn is_src(&self) -> bool { 878 matches!(self, Self::Src) 879 } 880 881 /// Returns `true` if the origin is [`Test`]. 882 /// 883 /// [`Test`]: Origin::Test 884 #[must_use] 885 pub fn is_test(&self) -> bool { 886 matches!(self, Self::Test) 887 } 888 889 /// Returns `true` if the origin is [`Dev`]. 890 /// 891 /// [`Dev`]: Origin::Dev 892 #[must_use] 893 pub fn is_dev(&self) -> bool { 894 matches!(self, Self::Dev) 895 } 896 897 /// Name of the folder containing the origin. 898 #[must_use] 899 pub fn folder_name(&self) -> &str { 900 match self { 901 Origin::Src => "src", 902 Origin::Test => "test", 903 Origin::Dev => "dev", 904 } 905 } 906} 907 908fn doc_comments_before<'a>( 909 doc_comments_spans: &mut Peekable<impl Iterator<Item = &'a SrcSpan>>, 910 extra: &ModuleExtra, 911 byte: u32, 912 src: &'a str, 913) -> (u32, Vec<&'a str>) { 914 let mut comments = vec![]; 915 let mut comment_start = u32::MAX; 916 while let Some(SrcSpan { start, end }) = doc_comments_spans.peek() { 917 if start > &byte { 918 break; 919 } 920 if extra.has_comment_between(*end, byte) { 921 // We ignore doc comments that come before a regular comment. 922 _ = doc_comments_spans.next(); 923 continue; 924 } 925 let comment = doc_comments_spans 926 .next() 927 .expect("Comment before accessing next span"); 928 929 if comment.start < comment_start { 930 comment_start = comment.start; 931 } 932 933 comments.push(Comment::from((comment, src)).content) 934 } 935 (comment_start, comments) 936} 937 938#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] 939pub struct SourceFingerprint(u64); 940 941impl SourceFingerprint { 942 pub fn new(source: &str) -> Self { 943 SourceFingerprint(xxhash_rust::xxh3::xxh3_64(source.as_bytes())) 944 } 945 946 pub fn to_numerical_string(&self) -> String { 947 self.0.to_string() 948 } 949} 950 951/// Like a `Result`, but the operation can partially succeed or fail. 952/// 953#[derive(Debug)] 954pub enum Outcome<T, E> { 955 /// The operation was totally succesful. 956 Ok(T), 957 958 /// The operation was partially successful but there were problems. 959 PartialFailure(T, E), 960 961 /// The operation was entirely unsuccessful. 962 TotalFailure(E), 963} 964 965impl<T, E> Outcome<T, E> 966where 967 E: Debug, 968{ 969 #[cfg(test)] 970 /// Panic if there's any errors 971 pub fn unwrap(self) -> T { 972 match self { 973 Outcome::Ok(t) => t, 974 Outcome::PartialFailure(_, errors) => panic!("Error: {:?}", errors), 975 Outcome::TotalFailure(error) => panic!("Error: {:?}", error), 976 } 977 } 978 979 /// Panic if there's any errors 980 pub fn expect(self, e: &'static str) -> T { 981 match self { 982 Outcome::Ok(t) => t, 983 Outcome::PartialFailure(_, errors) => panic!("{e}: {:?}", errors), 984 Outcome::TotalFailure(error) => panic!("{e}: {:?}", error), 985 } 986 } 987 988 pub fn into_result(self) -> Result<T, E> { 989 match self { 990 Outcome::Ok(t) => Ok(t), 991 Outcome::PartialFailure(_, e) | Outcome::TotalFailure(e) => Err(e), 992 } 993 } 994 995 pub fn map<T2>(self, f: impl FnOnce(T) -> T2) -> Outcome<T2, E> { 996 match self { 997 Outcome::Ok(t) => Outcome::Ok(f(t)), 998 Outcome::PartialFailure(t, e) => Outcome::PartialFailure(f(t), e), 999 Outcome::TotalFailure(e) => Outcome::TotalFailure(e), 1000 } 1001 } 1002}