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