Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

split definition from references/address review

+708 -313
+3 -4
compiler-core/src/analyse.rs
··· 411 411 type_references: env.references.type_references, 412 412 module_references: env.references.module_references, 413 413 label_references: env.references.label_references, 414 + label_definitions: env.references.label_definitions, 414 415 }, 415 416 inline_functions: self.inline_functions, 416 417 }, ··· 1235 1236 }; 1236 1237 1237 1238 if let Some(label) = label { 1238 - environment.references.register_label_reference( 1239 + environment.references.register_label_definition( 1239 1240 (environment.current_module.clone(), name.clone()), 1240 1241 label.clone(), 1241 1242 label_location, 1242 - ReferenceKind::Definition, 1243 - Some(constructor.name.clone()), 1244 - false, 1243 + constructor.name.clone(), 1245 1244 ); 1246 1245 } 1247 1246
+86 -45
compiler-core/src/ast.rs
··· 17 17 use crate::analyse::Inferred; 18 18 use crate::ast::typed::pairwise_all; 19 19 use crate::bit_array; 20 - use crate::build::{ExpressionPosition, LabelOwner, Located, Target, module_erlang_name}; 20 + use crate::build::{ExpressionPosition, Located, Target, module_erlang_name}; 21 21 use crate::exhaustiveness::CompiledCase; 22 22 use crate::parse::{LiteralFloatValue, SpannedString}; 23 + use crate::reference::LabelSyntax; 23 24 use crate::type_::error::VariableOrigin; 24 25 use crate::type_::expression::{Implementations, Purity}; 25 26 use crate::type_::printer::Names; ··· 1151 1152 .iter() 1152 1153 .find(|argument| argument.location.contains(byte_index)) 1153 1154 { 1154 - // The cursor is on the field's label in its declaration. 1155 1155 if let Some((label_location, label)) = &argument.label 1156 1156 && label_location.contains(byte_index) 1157 1157 { 1158 - return Some(Located::Label { 1158 + return Some(Located::RecordLabelDefinition { 1159 1159 location: *label_location, 1160 - type_: argument.type_.clone(), 1160 + field_type: argument.type_.clone(), 1161 1161 label: label.clone(), 1162 - owner: Some(LabelOwner::Definition { 1163 - type_name: self.name.clone(), 1164 - constructor: constructor.name.clone(), 1165 - }), 1162 + type_name: self.name.clone(), 1166 1163 }); 1167 1164 } 1168 1165 1169 - // The cursor is on the field's type annotation. 1170 1166 if let Some(annotation) = argument.ast.find_node(byte_index, argument.type_.clone()) 1171 1167 { 1172 1168 return Some(annotation); ··· 1733 1729 } 1734 1730 Some(located) => Some(located), 1735 1731 None => { 1736 - if self.location.contains(byte_index) && let Some(label) = &self.label { 1737 - let fn_type = called_function.type_(); 1738 - let record_type = fn_type.return_type().unwrap_or(fn_type); 1732 + if !self.location.contains(byte_index) { 1733 + return None; 1734 + } 1735 + let label = self.label.as_ref()?; 1736 + 1737 + if let Some(variant) = called_function.record_constructor_variant_name() 1738 + && let Some(label_location) = self.label_location() 1739 + && label_location.contains(byte_index) 1740 + { 1741 + let record_type = called_function 1742 + .type_() 1743 + .return_type() 1744 + .expect("record constructors with arguments are functions"); 1745 + Some(Located::RecordLabelUsage { 1746 + location: label_location, 1747 + field_type: self.value.type_(), 1748 + label: label.clone(), 1749 + record_type, 1750 + variant: variant.clone(), 1751 + }) 1752 + } else { 1739 1753 Some(Located::Label { 1740 1754 location: self.location, 1741 - type_: self.value.type_(), 1742 - label: label.clone(), 1743 - owner: called_function.name().map(|c| { 1744 - LabelOwner::Usage { 1745 - type_: record_type, 1746 - constructor: c, 1747 - } 1748 - }), 1755 + field_type: self.value.type_(), 1749 1756 }) 1750 - } else { 1751 - None 1752 1757 } 1753 1758 } 1754 1759 }, ··· 1797 1802 } 1798 1803 1799 1804 impl CallArg<TypedPattern> { 1805 + /// `record_type` is the type of the record this pattern is matching on, 1806 + /// and `variant` is the name of the variant being matched. They are used 1807 + /// to resolve the definition of the field if the cursor is on this 1808 + /// argument's label. 1800 1809 pub fn find_node( 1801 1810 &self, 1802 1811 byte_index: u32, 1803 1812 record_type: &Arc<Type>, 1804 - constructor: Option<&EcoString>, 1813 + variant: &EcoString, 1805 1814 ) -> Option<Located<'_>> { 1806 1815 match self.value.find_node(byte_index) { 1807 1816 Some(located) => Some(located), 1808 1817 _ => { 1809 - if self.location.contains(byte_index) 1810 - && let Some(label) = &self.label 1818 + if !self.location.contains(byte_index) { 1819 + return None; 1820 + } 1821 + let label = self.label.as_ref()?; 1822 + 1823 + if let Some(label_location) = self.label_location() 1824 + && label_location.contains(byte_index) 1811 1825 { 1812 - Some(Located::Label { 1813 - location: self.location, 1814 - type_: self.value.type_(), 1826 + Some(Located::RecordLabelUsage { 1827 + location: label_location, 1828 + field_type: self.value.type_(), 1815 1829 label: label.clone(), 1816 - owner: constructor.map(|c| LabelOwner::Usage { 1817 - type_: record_type.clone(), 1818 - constructor: c.clone(), 1819 - }), 1830 + record_type: record_type.clone(), 1831 + variant: variant.clone(), 1820 1832 }) 1821 1833 } else { 1822 - None 1834 + Some(Located::Label { 1835 + location: self.location, 1836 + field_type: self.value.type_(), 1837 + }) 1823 1838 } 1824 1839 } 1825 1840 } ··· 1827 1842 } 1828 1843 1829 1844 impl CallArg<TypedConstant> { 1845 + /// `record_type` is the type of the record this constant is building, and 1846 + /// `variant` is the name of the variant being built. They are used to 1847 + /// resolve the definition of the field if the cursor is on this 1848 + /// argument's label. 1830 1849 pub fn find_node( 1831 1850 &self, 1832 1851 byte_index: u32, 1833 1852 record_type: &Arc<Type>, 1834 - constructor: Option<&EcoString>, 1853 + variant: &EcoString, 1835 1854 ) -> Option<Located<'_>> { 1836 1855 match self.value.find_node(byte_index) { 1837 1856 Some(located) => Some(located), 1838 1857 _ => { 1839 - if self.location.contains(byte_index) 1840 - && let Some(label) = &self.label 1858 + if !self.location.contains(byte_index) { 1859 + return None; 1860 + } 1861 + let label = self.label.as_ref()?; 1862 + 1863 + if let Some(label_location) = self.label_location() 1864 + && label_location.contains(byte_index) 1841 1865 { 1866 + Some(Located::RecordLabelUsage { 1867 + location: label_location, 1868 + field_type: self.value.type_(), 1869 + label: label.clone(), 1870 + record_type: record_type.clone(), 1871 + variant: variant.clone(), 1872 + }) 1873 + } else { 1842 1874 Some(Located::Label { 1843 1875 location: self.location, 1844 - type_: self.value.type_(), 1845 - label: label.clone(), 1846 - owner: constructor.map(|c| LabelOwner::Usage { 1847 - type_: record_type.clone(), 1848 - constructor: c.clone(), 1849 - }), 1876 + field_type: self.value.type_(), 1850 1877 }) 1851 - } else { 1852 - None 1853 1878 } 1854 1879 } 1855 1880 } ··· 1919 1944 }) 1920 1945 } 1921 1946 } 1947 + 1948 + pub fn label_syntax(&self) -> LabelSyntax { 1949 + if self.uses_label_shorthand() { 1950 + LabelSyntax::Shorthand 1951 + } else { 1952 + LabelSyntax::Longhand 1953 + } 1954 + } 1922 1955 } 1923 1956 1924 1957 impl<T> HasLocation for CallArg<T> { ··· 1965 1998 start: self.location.start, 1966 1999 end: self.location.start + self.label.len() as u32, 1967 2000 } 2001 + } 2002 + } 2003 + 2004 + pub fn label_syntax(&self) -> LabelSyntax { 2005 + if self.uses_label_shorthand() { 2006 + LabelSyntax::Shorthand 2007 + } else { 2008 + LabelSyntax::Longhand 1968 2009 } 1969 2010 } 1970 2011 } ··· 3442 3483 } else { 3443 3484 arguments 3444 3485 .iter() 3445 - .find_map(|argument| argument.find_node(byte_index, type_, Some(name))) 3486 + .find_map(|argument| argument.find_node(byte_index, type_, name)) 3446 3487 } 3447 3488 } 3448 3489
+1 -1
compiler-core/src/ast/constant.rs
··· 182 182 } => arguments 183 183 .iter() 184 184 .flatten() 185 - .find_map(|argument| argument.find_node(byte_index, type_, Some(name))) 185 + .find_map(|argument| argument.find_node(byte_index, type_, name)) 186 186 .unwrap_or(Located::Constant(self)), 187 187 Constant::RecordUpdate { 188 188 record, arguments, ..
+22 -29
compiler-core/src/ast/tests.rs
··· 70 70 } 71 71 } 72 72 73 + fn cat_type() -> Arc<Type> { 74 + Arc::new(Type::Named { 75 + publicity: Publicity::Public, 76 + package: "mypackage".into(), 77 + module: "mymod".into(), 78 + name: "Cat".into(), 79 + arguments: vec![], 80 + inferred_variant: None, 81 + }) 82 + } 83 + 73 84 fn compile_expression(src: &str) -> TypedStatement { 74 85 let ast = crate::parse::parse_statement_sequence(src).expect("syntax error"); 75 86 ··· 96 107 .build(); 97 108 98 109 // Insert a cat record to use in the tests 99 - let cat_type = Arc::new(Type::Named { 100 - publicity: Publicity::Public, 101 - package: "mypackage".into(), 102 - module: "mymod".into(), 103 - name: "Cat".into(), 104 - arguments: vec![], 105 - inferred_variant: None, 106 - }); 110 + let cat_type = cat_type(); 107 111 let variant = ValueConstructorVariant::Record { 108 112 documentation: Some("wibble".into()), 109 113 variants_count: 1, ··· 921 925 position: ExpressionPosition::Expression 922 926 }) 923 927 ); 924 - assert_eq!( 925 - access.find_node(15), 926 - Some(Located::Expression { 927 - expression: access, 928 - position: ExpressionPosition::Expression 929 - }) 930 - ); 931 - assert_eq!( 932 - access.find_node(18), 933 - Some(Located::Expression { 934 - expression: access, 935 - position: ExpressionPosition::Expression 936 - }) 937 - ); 938 - assert_eq!( 939 - access.find_node(19), 940 - Some(Located::Expression { 941 - expression: access, 942 - position: ExpressionPosition::Expression 943 - }) 944 - ); 928 + let label = Located::RecordAccessLabel { 929 + location: SrcSpan { start: 15, end: 19 }, 930 + field_type: type_::string(), 931 + label: "name".into(), 932 + record_type: cat_type(), 933 + documentation: None, 934 + }; 935 + assert_eq!(access.find_node(15), Some(label.clone())); 936 + assert_eq!(access.find_node(18), Some(label.clone())); 937 + assert_eq!(access.find_node(19), Some(label)); 945 938 } 946 939 947 940 #[test]
+45 -9
compiler-core/src/ast/typed.rs
··· 430 430 .or_else(|| self.self_if_contains_location(byte_index)), 431 431 432 432 Self::RecordAccess { 433 - record: expression, .. 434 - } 435 - | Self::TupleIndex { 433 + record, 434 + label, 435 + location, 436 + type_, 437 + documentation, 438 + .. 439 + } => record 440 + .find_node(byte_index) 441 + .or_else(|| { 442 + // The label is the final part of the access, so its span 443 + // ends where the whole `record.label` expression does. 444 + let label_location = 445 + SrcSpan::new(location.end - label.len() as u32, location.end); 446 + if label_location.contains(byte_index) { 447 + Some(Located::RecordAccessLabel { 448 + location: label_location, 449 + field_type: type_.clone(), 450 + label: label.clone(), 451 + record_type: record.type_(), 452 + documentation: documentation.clone(), 453 + }) 454 + } else { 455 + None 456 + } 457 + }) 458 + .or_else(|| self.self_if_contains_location(byte_index)), 459 + 460 + Self::TupleIndex { 436 461 tuple: expression, .. 437 462 } => expression 438 463 .find_node(byte_index) ··· 1220 1245 } 1221 1246 } 1222 1247 1223 - /// The name of the value this expression refers to, if any. This is the 1224 - /// unqualified name for `Var` and the label for a qualified 1225 - /// `ModuleSelect`. 1248 + /// If this expression is a record constructor function with non-zero 1249 + /// arity, returns the name of the variant it constructs. 1226 1250 /// 1227 - pub fn name(&self) -> Option<EcoString> { 1251 + pub fn record_constructor_variant_name(&self) -> Option<&EcoString> { 1228 1252 match self { 1229 - TypedExpr::Var { name, .. } => Some(name.clone()), 1230 - TypedExpr::ModuleSelect { label, .. } => Some(label.clone()), 1253 + TypedExpr::Var { 1254 + constructor: 1255 + ValueConstructor { 1256 + variant: ValueConstructorVariant::Record { name, arity, .. }, 1257 + .. 1258 + }, 1259 + .. 1260 + } 1261 + | TypedExpr::ModuleSelect { 1262 + constructor: ModuleValueConstructor::Record { name, arity, .. }, 1263 + .. 1264 + } if *arity > 0 => Some(name), 1231 1265 1232 1266 TypedExpr::Int { .. } 1233 1267 | TypedExpr::Float { .. } 1234 1268 | TypedExpr::String { .. } 1235 1269 | TypedExpr::Block { .. } 1236 1270 | TypedExpr::Pipeline { .. } 1271 + | TypedExpr::Var { .. } 1237 1272 | TypedExpr::Fn { .. } 1238 1273 | TypedExpr::List { .. } 1239 1274 | TypedExpr::Call { .. } ··· 1241 1276 | TypedExpr::Case { .. } 1242 1277 | TypedExpr::RecordAccess { .. } 1243 1278 | TypedExpr::PositionalAccess { .. } 1279 + | TypedExpr::ModuleSelect { .. } 1244 1280 | TypedExpr::Tuple { .. } 1245 1281 | TypedExpr::TupleIndex { .. } 1246 1282 | TypedExpr::Todo { .. }
+66 -54
compiler-core/src/build.rs
··· 479 479 location: SrcSpan, 480 480 }, 481 481 UnqualifiedImport(UnqualifiedImport<'a>), 482 + /// The label of a labelled argument in a function call. 482 483 Label { 483 484 location: SrcSpan, 484 - /// The type of the labelled argument value (used for hover). 485 - type_: std::sync::Arc<Type>, 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>, 486 493 label: EcoString, 487 - /// The record type the label belongs to, used for go-to-definition, 488 - /// find-references and rename. `None` when it can't be determined. 489 - owner: Option<LabelOwner>, 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>, 490 521 }, 491 522 ModuleName { 492 523 location: SrcSpan, ··· 503 534 ModuleImport(&'a TypedImport), 504 535 ModuleCustomType(&'a TypedCustomType), 505 536 ModuleTypeAlias(&'a TypedTypeAlias), 506 - } 507 - 508 - /// The record type a field label belongs to. Used to resolve go-to-definition, 509 - /// find-references and rename for record fields. 510 - #[derive(Debug, Clone, PartialEq)] 511 - pub enum LabelOwner { 512 - /// A label usage (`record.field`, a labelled argument or pattern, a record 513 - /// update) where the value's type is known. The type carries the module 514 - /// and name it belongs to. 515 - Usage { 516 - type_: std::sync::Arc<Type>, 517 - constructor: EcoString, 518 - }, 519 - /// A label at its declaration in a custom type. The type is being defined 520 - /// in the current module, so only its name is needed. 521 - Definition { 522 - type_name: EcoString, 523 - constructor: EcoString, 524 - }, 525 537 } 526 538 527 539 impl<'a> Located<'a> { ··· 537 549 }) 538 550 } 539 551 540 - /// Looks up the definition location of a record field label using 541 - /// the pre-computed label reference map. 552 + /// Looks up the location at which a record field label was defined, using 553 + /// the label definitions gathered during analysis. 542 554 fn label_definition_location( 543 555 &self, 544 556 importable_modules: &'a im::HashMap<EcoString, type_::ModuleInterface>, 545 557 record_type: &Arc<Type>, 546 558 label: &EcoString, 547 - constructor: Option<&EcoString>, 559 + variant: Option<&EcoString>, 548 560 ) -> Option<DefinitionLocation> { 549 561 let (module_name, type_name) = record_type.named_type_name()?; 550 562 let module = importable_modules.get(&module_name)?; ··· 553 565 type_name, 554 566 label: label.clone(), 555 567 }; 556 - let references = module.references.label_references.get(&key)?; 557 - let definition = references.iter().find(|r| { 558 - r.kind == reference::ReferenceKind::Definition 559 - && match (&r.constructor, constructor) { 560 - (Some(a), Some(b)) => a == b, 561 - _ => true, 562 - } 563 - })?; 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 + }; 564 580 Some(DefinitionLocation { 565 581 module: Some(module_name), 566 582 span: definition.location, ··· 580 596 }), 581 597 Self::Statement(statement) => statement.definition_location(), 582 598 Self::FunctionBody(statement) => None, 583 - Self::Expression { 584 - expression: TypedExpr::RecordAccess { record, label, .. }, 585 - .. 586 - } => self.label_definition_location(importable_modules, &record.type_(), label, None), 587 599 Self::Expression { expression, .. } => expression.definition_location(), 588 600 589 601 Self::ModuleImport(import) => Some(DefinitionLocation { ··· 631 643 }), 632 644 Self::Arg(_) => None, 633 645 Self::Annotation { type_, .. } => self.type_location(importable_modules, type_.clone()), 634 - Self::Label { 635 - owner: 636 - Some(LabelOwner::Usage { 637 - type_: record_type, 638 - constructor, 639 - }), 646 + Self::Label { .. } => None, 647 + Self::RecordLabelUsage { 648 + record_type, 640 649 label, 650 + variant, 641 651 .. 642 652 } => self.label_definition_location( 643 653 importable_modules, 644 654 record_type, 645 655 label, 646 - Some(constructor), 656 + Some(variant), 647 657 ), 648 - // Already at the declaration; go-to-definition jumps to itself. 649 - Self::Label { 650 - owner: Some(LabelOwner::Definition { .. }), 651 - location, 652 - .. 653 - } => Some(DefinitionLocation { 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 { 654 663 module: None, 655 664 span: *location, 656 665 }), 657 - Self::Label { .. } => None, 658 666 Self::TypeVariable { .. } => None, 659 667 Self::ModuleName { module_name, .. } => Some(DefinitionLocation { 660 668 module: Some(module_name.clone()), ··· 672 680 Located::Statement(statement) => Some(statement.type_()), 673 681 Located::Expression { expression, .. } => Some(expression.type_()), 674 682 Located::Arg(arg) => Some(arg.type_.clone()), 675 - Located::Label { type_, .. } | Located::Annotation { type_, .. } => Some(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()), 676 688 Located::Constant(constant) => Some(constant.type_()), 677 689 Located::ClauseGuard(guard) => Some(guard.type_()), 678 690
+1
compiler-core/src/metadata/tests.rs
··· 2047 2047 )] 2048 2048 .into(), 2049 2049 label_references: HashMap::new(), 2050 + label_definitions: HashMap::new(), 2050 2051 }, 2051 2052 inline_functions: HashMap::new(), 2052 2053 };
+55 -19
compiler-core/src/reference.rs
··· 177 177 178 178 pub type ReferenceMap = HashMap<(EcoString, EcoString), Vec<Reference>>; 179 179 180 - #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] 180 + /// A use of a record field label: a labelled argument in a record constructor 181 + /// call or pattern, a record update argument, or a `record.field` access. 182 + #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] 181 183 pub struct LabelReference { 182 184 /// The location of the label. For a shorthand (`label:`) this spans the 183 185 /// whole `label:`, mirroring how variable references record label 184 186 /// shorthands. For everything else it is just the label itself. 185 187 pub location: SrcSpan, 186 - pub kind: ReferenceKind, 187 - /// The constructor this label belongs to. `None` for usages where the 188 - /// constructor is unknown (e.g. `record.field`). 189 - pub constructor: Option<EcoString>, 190 - /// Whether the label was written using the shorthand syntax (`label:`). 191 - /// Renaming the field then has to expand it so the value keeps its name: 192 - /// `wibble:` becomes `wobble: wibble`. 193 - pub shorthand: bool, 188 + pub syntax: LabelSyntax, 189 + } 190 + 191 + /// The syntax used to write a record field label where it is used. 192 + #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] 193 + pub enum LabelSyntax { 194 + /// The label is written out on its own, as in `Wibble(label: value)` or 195 + /// `record.label`, so renaming it is a simple replacement. 196 + Longhand, 197 + /// The label shorthand syntax (`label:`), which stands for both the label 198 + /// and a variable with the same name. Renaming the field then has to 199 + /// expand it so the value keeps its name: `wibble:` becomes 200 + /// `wobble: wibble`. 201 + Shorthand, 202 + } 203 + 204 + /// The location at which a record field label is defined in one of the 205 + /// variants of a custom type. 206 + #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] 207 + pub struct LabelDefinition { 208 + pub location: SrcSpan, 209 + /// The name of the variant this label is defined in. 210 + pub variant: EcoString, 194 211 } 195 212 196 213 /// Identifies a record field label within a custom type, used to look up the ··· 278 295 /// The locations of the references to each record field label in this 279 296 /// module, used for renaming and go-to reference. 280 297 pub label_references: HashMap<RecordLabel, Vec<LabelReference>>, 298 + /// The locations at which each record field label is defined, one per 299 + /// variant that defines it, used for renaming and go-to definition. 300 + pub label_definitions: HashMap<RecordLabel, Vec<LabelDefinition>>, 281 301 282 302 /// This map is used to access the nodes of modules that were not 283 303 /// aliased, given their name. ··· 639 659 .push(reference); 640 660 } 641 661 642 - /// Registers a reference to a record field label. 662 + /// Registers a use of a record field label. 643 663 /// 644 664 /// `type_` is the `(module, name)` of the type the label belongs to, as 645 665 /// returned by [`Type::named_type_name`]. ··· 648 668 type_: (EcoString, EcoString), 649 669 label: EcoString, 650 670 location: SrcSpan, 651 - kind: ReferenceKind, 652 - constructor: Option<EcoString>, 653 - shorthand: bool, 671 + syntax: LabelSyntax, 654 672 ) { 655 673 let (type_module, type_name) = type_; 656 674 self.label_references ··· 660 678 label, 661 679 }) 662 680 .or_default() 663 - .push(LabelReference { 664 - location, 665 - kind, 666 - constructor, 667 - shorthand, 668 - }); 681 + .push(LabelReference { location, syntax }); 682 + } 683 + 684 + /// Registers the definition of a record field label in one of the 685 + /// variants of a custom type. 686 + /// 687 + /// `type_` is the `(module, name)` of the type being defined, as returned 688 + /// by [`Type::named_type_name`]. 689 + pub fn register_label_definition( 690 + &mut self, 691 + type_: (EcoString, EcoString), 692 + label: EcoString, 693 + location: SrcSpan, 694 + variant: EcoString, 695 + ) { 696 + let (type_module, type_name) = type_; 697 + self.label_definitions 698 + .entry(RecordLabel { 699 + type_module, 700 + type_name, 701 + label, 702 + }) 703 + .or_default() 704 + .push(LabelDefinition { location, variant }); 669 705 } 670 706 671 707 /// Like `register_type_reference`, but doesn't modify `self.type_references`.
+2 -1
compiler-core/src/type_.rs
··· 35 35 build::{Origin, Target}, 36 36 inline::InlinableFunction, 37 37 line_numbers::LineNumbers, 38 - reference::{LabelReference, ModuleNameReference, RecordLabel, ReferenceMap}, 38 + reference::{LabelDefinition, LabelReference, ModuleNameReference, RecordLabel, ReferenceMap}, 39 39 type_::expression::Implementations, 40 40 }; 41 41 use error::*; ··· 1058 1058 pub type_references: ReferenceMap, 1059 1059 pub module_references: HashMap<EcoString, Vec<ModuleNameReference>>, 1060 1060 pub label_references: HashMap<RecordLabel, Vec<LabelReference>>, 1061 + pub label_definitions: HashMap<RecordLabel, Vec<LabelDefinition>>, 1061 1062 } 1062 1063 1063 1064 #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
+85 -52
compiler-core/src/type_/expression.rs
··· 20 20 build::Target, 21 21 exhaustiveness::{self, CompileCaseResult, CompiledCase, Reachability}, 22 22 parse::{LiteralFloatValue, PatternPosition}, 23 - reference::ReferenceKind, 23 + reference::{LabelSyntax, ReferenceKind}, 24 24 }; 25 25 use ecow::eco_format; 26 26 use hexpm::version::{LowestVersion, Version}; ··· 1227 1227 } 1228 1228 1229 1229 self.purity = self.purity.merge(fun.called_function_purity()); 1230 - 1231 - // Register field label references for record constructor calls. 1232 - if fun.is_record_constructor_function() 1233 - && let Some(type_name) = type_.named_type_name() 1234 - { 1235 - let constructor_name = fun.name(); 1236 - for arg in &arguments { 1237 - if let (Some(label), Some(label_location)) = (&arg.label, arg.label_location()) { 1238 - self.environment.references.register_label_reference( 1239 - type_name.clone(), 1240 - label.clone(), 1241 - label_location, 1242 - ReferenceKind::Unqualified, 1243 - constructor_name.clone(), 1244 - arg.uses_label_shorthand(), 1245 - ); 1246 - } 1247 - } 1248 - } 1249 1230 1250 1231 TypedExpr::Call { 1251 1232 location, ··· 3008 2989 ) -> Result<TypedExpr, Error> { 3009 2990 let record = Box::new(record); 3010 2991 let record_type = record.type_(); 2992 + let type_name = record_type.named_type_name(); 3011 2993 let RecordAccessor { 3012 2994 index, 3013 2995 label, 3014 2996 type_, 3015 2997 documentation, 3016 2998 } = self.infer_known_record_access( 3017 - record_type.clone(), 2999 + record_type, 3018 3000 record.location(), 3019 3001 usage, 3020 3002 label_location, 3021 3003 label, 3022 3004 )?; 3023 - if let Some(type_) = record_type.named_type_name() { 3005 + if let Some(type_name) = type_name { 3024 3006 self.environment.references.register_label_reference( 3025 - type_, 3007 + type_name, 3026 3008 label.clone(), 3027 3009 label_location, 3028 - ReferenceKind::Unqualified, 3029 - None, 3030 - false, 3010 + LabelSyntax::Longhand, 3031 3011 ); 3032 3012 } 3033 3013 Ok(TypedExpr::RecordAccess { ··· 3295 3275 self.problems.error(convert_unify_error(error, *location)); 3296 3276 }; 3297 3277 3298 - if let Some(type_) = return_type.named_type_name() { 3278 + if let Some(type_name) = return_type.named_type_name() { 3299 3279 self.environment.references.register_label_reference( 3300 - type_, 3280 + type_name, 3301 3281 label.clone(), 3302 3282 argument.label_location(), 3303 - ReferenceKind::Unqualified, 3304 - Some(variant.constructor_name.clone()), 3305 - argument.uses_label_shorthand(), 3283 + argument.label_syntax(), 3306 3284 ); 3307 3285 } 3308 3286 ··· 3576 3554 arguments: arguments_types, 3577 3555 return_type, 3578 3556 field_map, 3579 - constructor_name: name.clone(), 3580 3557 }); 3581 3558 } 3582 3559 ··· 3589 3566 arguments: arguments_types, 3590 3567 return_type, 3591 3568 field_map, 3592 - constructor_name: name.clone(), 3593 3569 }); 3594 3570 } 3595 3571 ··· 4129 4105 4130 4106 let mut final_arguments = base_arguments; 4131 4107 for argument in arguments { 4132 - // Captured before `argument.value` is moved below, so they 4133 - // are still available when we register the label reference. 4134 - let shorthand = argument.uses_label_shorthand(); 4108 + let syntax = argument.label_syntax(); 4135 4109 let label_location = argument.label_location(); 4136 - if shorthand { 4110 + if argument.uses_label_shorthand() { 4137 4111 self.track_feature_usage( 4138 4112 FeatureKind::LabelShorthandSyntax, 4139 4113 argument.location, ··· 4171 4145 return self.new_invalid_constant(location); 4172 4146 } 4173 4147 4174 - if let Some(type_) = expected_type.named_type_name() { 4148 + if let Some(type_name) = expected_type.named_type_name() { 4175 4149 self.environment.references.register_label_reference( 4176 - type_, 4150 + type_name, 4177 4151 label.clone(), 4178 4152 label_location, 4179 - ReferenceKind::Unqualified, 4180 - Some(constructor_tag.clone()), 4181 - shorthand, 4153 + syntax, 4182 4154 ); 4183 4155 } 4184 4156 ··· 4519 4491 // Register a reference to each labelled field so the language server can 4520 4492 // offer go-to-definition, find-references and rename on record fields. We 4521 4493 // do this before adding back the ignored arguments below, as those are 4522 - // synthetic placeholders without a real value. 4523 - if let Some(type_) = expected_return.named_type_name() { 4494 + // synthetic placeholders without a real value: their labels are 4495 + // registered using the locations captured before the values were 4496 + // discarded. 4497 + if let Some(type_name) = expected_return.named_type_name() { 4524 4498 for argument in &typed_arguments { 4525 - if let (Some(label), Some(label_location)) = 4526 - (&argument.label, argument.label_location()) 4499 + if let Some(label) = &argument.label 4500 + && let Some(label_location) = argument.label_location() 4501 + { 4502 + self.environment.references.register_label_reference( 4503 + type_name.clone(), 4504 + label.clone(), 4505 + label_location, 4506 + argument.label_syntax(), 4507 + ); 4508 + } 4509 + } 4510 + 4511 + for argument in &ignored_labelled_arguments { 4512 + if let Some(label) = &argument.label 4513 + && let Some(label_location) = argument.label_location 4514 + && argument.implicit.is_none() 4527 4515 { 4528 4516 self.environment.references.register_label_reference( 4529 - type_.clone(), 4517 + type_name.clone(), 4530 4518 label.clone(), 4531 4519 label_location, 4532 - ReferenceKind::Unqualified, 4533 - Some(name.clone()), 4534 - argument.uses_label_shorthand(), 4520 + argument.syntax, 4535 4521 ); 4536 4522 } 4537 4523 } ··· 4549 4535 label, 4550 4536 location, 4551 4537 implicit, 4538 + .. 4552 4539 } in ignored_labelled_arguments 4553 4540 { 4554 4541 typed_arguments.push(CallArg { ··· 5011 4998 }) 5012 4999 .collect(); 5013 5000 5001 + // Register a reference to each labelled field so the language server can 5002 + // offer go-to-definition, find-references and rename on record fields. We 5003 + // do this before adding back the ignored arguments below, as those are 5004 + // synthetic placeholders without a real value: their labels are 5005 + // registered using the locations captured before the values were 5006 + // discarded. 5007 + if fun.is_record_constructor_function() 5008 + && let Some(type_name) = return_type.named_type_name() 5009 + { 5010 + for argument in &typed_arguments { 5011 + if let Some(label) = &argument.label 5012 + && let Some(label_location) = argument.label_location() 5013 + { 5014 + self.environment.references.register_label_reference( 5015 + type_name.clone(), 5016 + label.clone(), 5017 + label_location, 5018 + argument.label_syntax(), 5019 + ); 5020 + } 5021 + } 5022 + 5023 + for argument in &ignored_labelled_arguments { 5024 + if let Some(label) = &argument.label 5025 + && let Some(label_location) = argument.label_location 5026 + && argument.implicit.is_none() 5027 + { 5028 + self.environment.references.register_label_reference( 5029 + type_name.clone(), 5030 + label.clone(), 5031 + label_location, 5032 + argument.syntax, 5033 + ); 5034 + } 5035 + } 5036 + } 5037 + 5014 5038 // Now if we had supplied less arguments than required and some of those 5015 5039 // were labelled, in the previous step we would have got rid of those 5016 5040 // _before_ typing. ··· 5023 5047 label, 5024 5048 location, 5025 5049 implicit, 5050 + .. 5026 5051 } in ignored_labelled_arguments 5027 5052 { 5028 5053 typed_arguments.push(CallArg { ··· 5508 5533 } 5509 5534 } 5510 5535 5511 - fn fault_tolerant_match_function_type<A>( 5536 + fn fault_tolerant_match_function_type<A: HasLocation>( 5512 5537 &mut self, 5513 5538 has_labelled_arity_error: bool, 5514 5539 call_kind: CallKind, ··· 5592 5617 label: argument.label.clone(), 5593 5618 location: argument.location(), 5594 5619 implicit: argument.implicit, 5620 + label_location: argument.label_location(), 5621 + syntax: argument.label_syntax(), 5595 5622 }) 5596 5623 .collect_vec(); 5597 5624 ··· 5623 5650 label: Option<EcoString>, 5624 5651 location: SrcSpan, 5625 5652 implicit: Option<ImplicitCallArgOrigin>, 5653 + /// The location of the label, captured before the argument's value is 5654 + /// discarded: the synthetic replacement value spans the whole argument, 5655 + /// so it can no longer tell the label and the value apart. 5656 + label_location: Option<SrcSpan>, 5657 + /// The syntax of the label, captured before the argument's value is 5658 + /// discarded for the same reason as `label_location`. 5659 + syntax: LabelSyntax, 5626 5660 } 5627 5661 5628 5662 /// Given a constants, this will change its type into the given one, turning ··· 6056 6090 arguments: Vec<Arc<Type>>, 6057 6091 return_type: Arc<Type>, 6058 6092 field_map: &'a FieldMap, 6059 - constructor_name: EcoString, 6060 6093 } 6061 6094 6062 6095 impl RecordUpdateVariant<'_> {
+7 -9
compiler-core/src/type_/pattern.rs
··· 1236 1236 constructor_field_map, 1237 1237 ); 1238 1238 1239 - if let Some(type_) = return_.named_type_name() { 1240 - for arg in &pattern_arguments { 1241 - if let (Some(label), Some(label_location)) = 1242 - (&arg.label, arg.label_location()) 1243 - && !arg.is_implicit() 1239 + if let Some(type_name) = return_.named_type_name() { 1240 + for argument in &pattern_arguments { 1241 + if let Some(label) = &argument.label 1242 + && let Some(label_location) = argument.label_location() 1243 + && !argument.is_implicit() 1244 1244 { 1245 1245 self.environment.references.register_label_reference( 1246 - type_.clone(), 1246 + type_name.clone(), 1247 1247 label.clone(), 1248 1248 label_location, 1249 - ReferenceKind::Unqualified, 1250 - Some(name.clone()), 1251 - arg.uses_label_shorthand(), 1249 + argument.label_syntax(), 1252 1250 ); 1253 1251 } 1254 1252 }
+52 -7
language-server/src/engine.rs
··· 410 410 411 411 Located::Annotation { .. } => Some(completer.completion_types()), 412 412 413 - Located::Label { .. } => None, 413 + Located::Label { .. } 414 + | Located::RecordLabelDefinition { .. } 415 + | Located::RecordLabelUsage { .. } => None, 416 + 417 + Located::RecordAccessLabel { 418 + field_type, 419 + record_type, 420 + .. 421 + } => { 422 + completer.expected_type = Some(field_type.clone()); 423 + let mut completions = vec![]; 424 + completions.append(&mut completer.completion_values()); 425 + completions 426 + .append(&mut completer.completion_field_accessors(record_type.clone())); 427 + Some(completions) 428 + } 414 429 415 430 Located::ModuleName { 416 431 layer: ast::Layer::Type, ··· 859 874 860 875 Some(Referenced::TypeVariable { location, name: _ }) => success_response(location), 861 876 877 + // For a label written using the shorthand syntax the location 878 + // spans the whole `label:`, so we trim it down to just the 879 + // label. 862 880 Some(Referenced::Label { 863 881 location, label, .. 864 - }) if location.contains(byte_index) => success_response(SrcSpan { 882 + }) => success_response(SrcSpan { 865 883 start: location.start, 866 884 end: location.start + label.len() as u32, 867 885 }), ··· 1091 1109 type_module, 1092 1110 type_name, 1093 1111 label, 1094 - location, 1095 - }) if location.contains(byte_index) => match search_scope { 1112 + .. 1113 + }) => match search_scope { 1096 1114 FindReferencesSearchScope::AllModules => Some(find_label_references( 1097 1115 type_module, 1098 1116 type_name, ··· 1338 1356 )) 1339 1357 } 1340 1358 Located::Label { 1341 - location, type_, .. 1342 - } => Some(hover_for_label(location, type_, lines, module)), 1359 + location, 1360 + field_type, 1361 + } 1362 + | Located::RecordLabelDefinition { 1363 + location, 1364 + field_type, 1365 + .. 1366 + } 1367 + | Located::RecordLabelUsage { 1368 + location, 1369 + field_type, 1370 + .. 1371 + } => Some(hover_for_label(location, field_type, None, lines, module)), 1372 + Located::RecordAccessLabel { 1373 + location, 1374 + field_type, 1375 + documentation, 1376 + .. 1377 + } => Some(hover_for_label( 1378 + location, 1379 + field_type, 1380 + documentation.as_ref(), 1381 + lines, 1382 + module, 1383 + )), 1343 1384 Located::ModuleName { 1344 1385 location, 1345 1386 module_name, ··· 1722 1763 fn hover_for_label( 1723 1764 location: SrcSpan, 1724 1765 type_: Arc<Type>, 1766 + documentation: Option<&EcoString>, 1725 1767 line_numbers: LineNumbers, 1726 1768 module: &Module, 1727 1769 ) -> Hover { 1728 1770 let type_ = Printer::new(&module.ast.names).print_type(&type_); 1729 - let contents = format!("```gleam\n{type_}\n```"); 1771 + let contents = match documentation { 1772 + Some(documentation) => format!("```gleam\n{type_}\n```\n{documentation}"), 1773 + None => format!("```gleam\n{type_}\n```"), 1774 + }; 1730 1775 Hover { 1731 1776 contents: Contents::MarkedString(MarkedString::String(contents)), 1732 1777 range: Some(src_span_to_lsp_range(location, &line_numbers)),
+40 -45
language-server/src/reference.rs
··· 12 12 self, ArgNames, AssignName, BitArraySize, ClauseGuard, CustomType, Function, 13 13 ModuleConstant, Pattern, RecordConstructor, SrcSpan, TypedExpr, TypedModule, visit::Visit, 14 14 }, 15 - build::{LabelOwner, Located}, 15 + build::Located, 16 16 reference::RecordLabel, 17 17 type_::{ 18 18 ModuleInterface, ModuleValueConstructor, Type, ValueConstructor, ValueConstructorVariant, ··· 379 379 target_kind: RenameTarget::Qualified, 380 380 }), 381 381 382 - Located::Expression { 383 - expression: 384 - TypedExpr::RecordAccess { 385 - record, 386 - label, 387 - location, 388 - .. 389 - }, 382 + Located::RecordLabelUsage { 383 + record_type, 384 + label, 385 + location, 390 386 .. 391 - } => record 392 - .type_() 393 - .named_type_name() 394 - .map(|(type_module, type_name)| Referenced::Label { 395 - type_module, 396 - type_name, 397 - label: label.clone(), 398 - // `field_start` is the start of the whole `record.field` 399 - // expression, not the field. The field label is the trailing 400 - // part of the access, so we recover its span from the end. 401 - location: SrcSpan::new(location.end - label.len() as u32, location.end), 402 - }), 403 - 404 - Located::Label { 405 - owner: Some(LabelOwner::Usage { type_, .. }), 387 + } 388 + | Located::RecordAccessLabel { 389 + record_type, 406 390 label, 407 391 location, 408 392 .. 409 - } => type_ 393 + } => record_type 410 394 .named_type_name() 411 395 .map(|(type_module, type_name)| Referenced::Label { 412 396 type_module, ··· 415 399 location, 416 400 }), 417 401 418 - // A label at its declaration in a custom type, which lives in the 402 + // A label at its definition in a custom type, which lives in the 419 403 // current module. 420 - Located::Label { 421 - owner: Some(LabelOwner::Definition { type_name, .. }), 404 + Located::RecordLabelDefinition { 405 + type_name, 422 406 label, 423 407 location, 424 408 .. ··· 502 486 ) -> Vec<Location> { 503 487 let mut reference_locations = Vec::new(); 504 488 489 + // Unlike values and types, a label can be referenced in a module that 490 + // doesn't import the type's defining module: a record value can be 491 + // obtained transitively through another module and have its fields 492 + // accessed. So every module has to be searched. 505 493 for module in modules.values() { 506 - if module.name == type_module || module.references.imported_modules.contains(&type_module) { 507 - let Some(source_information) = sources.get(&module.name) else { 508 - continue; 509 - }; 510 - reference_locations.extend(find_label_references_in_module( 511 - type_module.clone(), 512 - type_name.clone(), 513 - label.clone(), 514 - module, 515 - source_information, 516 - )); 517 - } 494 + let Some(source_information) = sources.get(&module.name) else { 495 + continue; 496 + }; 497 + reference_locations.extend(find_label_references_in_module( 498 + type_module.clone(), 499 + type_name.clone(), 500 + label.clone(), 501 + module, 502 + source_information, 503 + )); 518 504 } 519 505 520 506 reference_locations ··· 538 524 type_name, 539 525 label, 540 526 }; 541 - let Some(references) = module.references.label_references.get(&key) else { 542 - return reference_locations; 543 - }; 527 + let definitions = module.references.label_definitions.get(&key); 528 + let references = module.references.label_references.get(&key); 529 + let locations = definitions 530 + .into_iter() 531 + .flatten() 532 + .map(|definition| definition.location) 533 + .chain( 534 + references 535 + .into_iter() 536 + .flatten() 537 + .map(|reference| reference.location), 538 + ); 544 539 545 - for reference in references { 540 + for location in locations { 546 541 reference_locations.push(Location { 547 542 uri: uri.clone(), 548 - range: src_span_to_lsp_range(reference.location, &source_information.line_numbers), 543 + range: src_span_to_lsp_range(location, &source_information.line_numbers), 549 544 }); 550 545 } 551 546
+34 -24
language-server/src/rename.rs
··· 12 12 ast::{self, SrcSpan}, 13 13 build::Module, 14 14 line_numbers::LineNumbers, 15 - reference::{ModuleNameReference, RecordLabel, ReferenceKind}, 15 + reference::{LabelSyntax, ModuleNameReference, RecordLabel, ReferenceKind}, 16 16 type_::{ModuleInterface, error::Named}, 17 17 }; 18 18 ··· 247 247 /// label shorthand syntax (`wibble:`), which is expanded so the implicit value 248 248 /// variable keeps its original name. 249 249 /// 250 - /// All the reference locations come from the reference graph that is built 251 - /// during analysis, exactly like the value and type renaming above. 252 - /// 253 250 pub fn rename_label( 254 251 params: &RenameParams, 255 252 type_module: &EcoString, ··· 275 272 label: label.clone(), 276 273 }; 277 274 275 + // A label can be referenced in a module that doesn't import the type's 276 + // defining module: a record value can be obtained transitively through 277 + // another module and have its fields accessed. So every module has to be 278 + // searched. 278 279 for module in modules.values() { 279 - if &module.name == type_module || module.references.imported_modules.contains(type_module) { 280 - let Some(source_information) = sources.get(&module.name) else { 281 - continue; 282 - }; 280 + let Some(source_information) = sources.get(&module.name) else { 281 + continue; 282 + }; 283 283 284 - rename_label_references_in_module( 285 - module, 286 - source_information, 287 - &mut workspace_edit, 288 - &key, 289 - label, 290 - &params.new_name, 291 - ); 292 - } 284 + rename_label_references_in_module( 285 + module, 286 + source_information, 287 + &mut workspace_edit, 288 + &key, 289 + label, 290 + &params.new_name, 291 + ); 293 292 } 294 293 295 294 RenameOutcome::Renamed { ··· 305 304 label: &EcoString, 306 305 new_name: &str, 307 306 ) { 308 - let Some(references) = module.references.label_references.get(key) else { 307 + let definitions = module.references.label_definitions.get(key); 308 + let references = module.references.label_references.get(key); 309 + if definitions.is_none() && references.is_none() { 309 310 return; 310 - }; 311 + } 311 312 312 313 let mut edits = TextEdits::new(&source_information.line_numbers); 313 314 314 - for reference in references { 315 - if reference.shorthand { 315 + // The definitions of the field are renamed along with its references. A 316 + // field shared between multiple variants has a definition in each, and 317 + // they are all renamed together so that code accessing the shared field 318 + // keeps compiling. 319 + for definition in definitions.into_iter().flatten() { 320 + edits.replace(definition.location, new_name.to_string()); 321 + } 322 + 323 + for reference in references.into_iter().flatten() { 324 + match reference.syntax { 316 325 // A label written using shorthand syntax (`wibble:`) relies on the 317 326 // field and the variable sharing a name. Renaming the field alone 318 327 // would break that, so we expand the shorthand and keep the original 319 328 // name as the value: `wibble:` becomes `new_name: wibble`. 320 - edits.replace(reference.location, format!("{new_name}: {label}")); 321 - } else { 322 - edits.replace(reference.location, new_name.to_string()); 329 + LabelSyntax::Shorthand => { 330 + edits.replace(reference.location, format!("{new_name}: {label}")) 331 + } 332 + LabelSyntax::Longhand => edits.replace(reference.location, new_name.to_string()), 323 333 } 324 334 } 325 335
+21 -2
language-server/src/tests/reference.rs
··· 1235 1235 ); 1236 1236 } 1237 1237 1238 - // A field elided by a `..` pattern spread is not a reference to that field, so 1239 - // it must not show up in the results. 1238 + #[test] 1239 + fn references_for_record_field_in_module_not_importing_the_type_module() { 1240 + assert_references!( 1241 + TestProject::for_source( 1242 + " 1243 + import wobble 1244 + 1245 + pub fn main() { 1246 + wobble.make().wibble 1247 + } 1248 + " 1249 + ) 1250 + .add_module("wibble", "pub type Wibble {\n Wibble(wibble: Int)\n}") 1251 + .add_module( 1252 + "wobble", 1253 + "import wibble\n\npub fn make() -> wibble.Wibble {\n wibble.Wibble(wibble: 1)\n}" 1254 + ), 1255 + find_position_of("().wibble").under_char('b') 1256 + ); 1257 + } 1258 + 1240 1259 #[test] 1241 1260 fn references_for_record_field_ignored_by_pattern_spread() { 1242 1261 assert_references!(
+39 -8
language-server/src/tests/rename.rs
··· 2934 2934 } 2935 2935 2936 2936 #[test] 2937 + fn rename_record_field_in_module_not_importing_the_type_module() { 2938 + assert_rename!( 2939 + TestProject::for_source( 2940 + " 2941 + import wobble 2942 + 2943 + pub fn main() { 2944 + wobble.make().wibble 2945 + } 2946 + " 2947 + ) 2948 + .add_module("wibble", "pub type Wibble {\n Wibble(wibble: Int)\n}") 2949 + .add_module( 2950 + "wobble", 2951 + "import wibble\n\npub fn make() -> wibble.Wibble {\n wibble.Wibble(wibble: 1)\n}" 2952 + ), 2953 + "wabble", 2954 + find_position_of("().wibble").under_char('b') 2955 + ); 2956 + } 2957 + 2958 + #[test] 2937 2959 fn rename_record_field_with_invalid_name() { 2938 2960 assert_rename_error!( 2939 2961 " ··· 2969 2991 ); 2970 2992 } 2971 2993 2972 - // A field elided by a `..` pattern spread must not be touched by renaming 2973 - // that field: the spread is not a real label reference, just a synthetic 2974 - // placeholder for the ignored fields. 2994 + #[test] 2995 + fn rename_record_field_renames_labelled_arguments_of_call_with_incorrect_arity() { 2996 + assert_rename!( 2997 + " 2998 + type Wibble { 2999 + Wibble(wibble: Int, wobble: Int) 3000 + } 3001 + 3002 + pub fn main() { 3003 + Wibble(wibble: 1) 3004 + } 3005 + ", 3006 + "wabble", 3007 + find_position_of("wibble: Int").under_char('w') 3008 + ); 3009 + } 3010 + 2975 3011 #[test] 2976 3012 fn rename_record_field_ignored_by_pattern_spread() { 2977 3013 assert_rename!( ··· 2991 3027 ); 2992 3028 } 2993 3029 2994 - // A bare `..` eliding every field must likewise be left untouched. 2995 3030 #[test] 2996 3031 fn rename_record_field_ignored_by_bare_pattern_spread() { 2997 3032 assert_rename!( ··· 3011 3046 ); 3012 3047 } 3013 3048 3014 - // When multiple variants share a field with the same name, renaming it on one 3015 - // variant renames it on every variant: an accessor like `shape.colour` only 3016 - // works while all the variants share the field, so renaming just one of them 3017 - // would break the others. 3018 3049 #[test] 3019 3050 fn rename_record_field_shared_between_variants() { 3020 3051 assert_rename!(
+1 -1
language-server/src/tests/snapshots/gleam_language_server__tests__hover__documentation_for_shared_record_field_when_variant_is_known.snap
··· 17 17 pub fn wibble(w: Wibble) { 18 18 let assert Wibble(..) = w 19 19 w.wibble 20 - ▔▔▔▔▔▔↑▔ 20 + ▔▔▔▔↑▔ 21 21 } 22 22 23 23
+1 -1
language-server/src/tests/snapshots/gleam_language_server__tests__hover__hover_for_label_in_pattern.snap
··· 9 9 10 10 pub fn main() { 11 11 let Wibble(wibble: _, wobble: _) = todo 12 - ▔▔▔▔↑▔▔▔▔ 12 + ▔▔▔▔↑▔ 13 13 todo 14 14 } 15 15
+1 -1
language-server/src/tests/snapshots/gleam_language_server__tests__hover__no_documentation_for_shared_record_field.snap
··· 16 16 17 17 pub fn wibble(w: Wibble) { 18 18 w.wibble 19 - ▔▔▔▔▔▔↑▔ 19 + ▔▔▔▔↑▔ 20 20 } 21 21 22 22
+1 -1
language-server/src/tests/snapshots/gleam_language_server__tests__hover__record_field_documentation.snap
··· 12 12 13 13 pub fn wibble(w: Wibble) { 14 14 w.wibble 15 - ▔▔▔▔▔▔↑▔ 15 + ▔▔▔▔↑▔ 16 16 } 17 17 18 18
+21
language-server/src/tests/snapshots/gleam_language_server__tests__hover__scratch_hover_on_colon_in_pattern.snap.new
··· 1 + --- 2 + source: language-server/src/tests/hover.rs 3 + assertion_line: 2320 4 + expression: "\ntype Wibble {\n Wibble(wibble: Int, wobble: Int)\n}\n\npub fn main() {\n let Wibble(wibble: _, wobble: _) = todo\n todo\n}\n" 5 + --- 6 + 7 + type Wibble { 8 + Wibble(wibble: Int, wobble: Int) 9 + } 10 + 11 + pub fn main() { 12 + let Wibble(wibble: _, wobble: _) = todo 13 + ▔▔▔▔▔▔↑ 14 + todo 15 + } 16 + 17 + 18 + ----- Hover content (markdown) ----- 19 + ```gleam 20 + Int 21 + ```
+21
language-server/src/tests/snapshots/gleam_language_server__tests__hover__scratch_hover_on_discard_in_pattern.snap.new
··· 1 + --- 2 + source: language-server/src/tests/hover.rs 3 + assertion_line: 2339 4 + expression: "\ntype Wibble {\n Wibble(wibble: Int, wobble: Int)\n}\n\npub fn main() {\n let Wibble(wibble: _, wobble: _) = todo\n todo\n}\n" 5 + --- 6 + 7 + type Wibble { 8 + Wibble(wibble: Int, wobble: Int) 9 + } 10 + 11 + pub fn main() { 12 + let Wibble(wibble: _, wobble: _) = todo 13 + 14 + todo 15 + } 16 + 17 + 18 + ----- Hover content (markdown) ----- 19 + ```gleam 20 + Int 21 + ```
+28
language-server/src/tests/snapshots/gleam_language_server__tests__reference__references_for_record_field_in_module_not_importing_the_type_module.snap
··· 1 + --- 2 + source: language-server/src/tests/reference.rs 3 + expression: "\nimport wobble\n\npub fn main() {\n wobble.make().wibble\n}\n" 4 + --- 5 + -- wibble.gleam 6 + pub type Wibble { 7 + Wibble(wibble: Int) 8 + ▔▔▔▔▔▔ 9 + } 10 + 11 + 12 + -- wobble.gleam 13 + import wibble 14 + 15 + pub fn make() -> wibble.Wibble { 16 + wibble.Wibble(wibble: 1) 17 + ▔▔▔▔▔▔ 18 + } 19 + 20 + 21 + -- app.gleam 22 + 23 + import wobble 24 + 25 + pub fn main() { 26 + wobble.make().wibble 27 + ▔▔↑▔▔▔ 28 + }
+48
language-server/src/tests/snapshots/gleam_language_server__tests__rename__rename_record_field_in_module_not_importing_the_type_module.snap
··· 1 + --- 2 + source: language-server/src/tests/rename.rs 3 + assertion_line: 2718 4 + expression: "\nimport wobble\n\npub fn main() {\n wobble.make().wibble\n}\n" 5 + --- 6 + ----- BEFORE RENAME 7 + -- wibble.gleam 8 + pub type Wibble { 9 + Wibble(wibble: Int) 10 + } 11 + 12 + -- wobble.gleam 13 + import wibble 14 + 15 + pub fn make() -> wibble.Wibble { 16 + wibble.Wibble(wibble: 1) 17 + } 18 + 19 + -- app.gleam 20 + 21 + import wobble 22 + 23 + pub fn main() { 24 + wobble.make().wibble 25 + ↑▔▔▔▔▔ 26 + } 27 + 28 + 29 + ----- AFTER RENAME 30 + -- wibble.gleam 31 + pub type Wibble { 32 + Wibble(wabble: Int) 33 + } 34 + 35 + -- wobble.gleam 36 + import wibble 37 + 38 + pub fn make() -> wibble.Wibble { 39 + wibble.Wibble(wabble: 1) 40 + } 41 + 42 + -- app.gleam 43 + 44 + import wobble 45 + 46 + pub fn main() { 47 + wobble.make().wabble 48 + }
+27
language-server/src/tests/snapshots/gleam_language_server__tests__rename__rename_record_field_renames_labelled_arguments_of_call_with_incorrect_arity.snap
··· 1 + --- 2 + source: language-server/src/tests/rename.rs 3 + expression: "\ntype Wibble {\n Wibble(wibble: Int, wobble: Int)\n}\n\npub fn main() {\n Wibble(wibble: 1)\n}\n" 4 + --- 5 + ----- BEFORE RENAME 6 + -- app.gleam 7 + 8 + type Wibble { 9 + Wibble(wibble: Int, wobble: Int) 10 + ↑▔▔▔▔▔ 11 + } 12 + 13 + pub fn main() { 14 + Wibble(wibble: 1) 15 + } 16 + 17 + 18 + ----- AFTER RENAME 19 + -- app.gleam 20 + 21 + type Wibble { 22 + Wibble(wabble: Int, wobble: Int) 23 + } 24 + 25 + pub fn main() { 26 + Wibble(wabble: 1) 27 + }