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