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