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

Configure Feed

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

Favour public type aliases over internal types

+295 -29
+4 -4
compiler-core/src/exhaustiveness/printer.rs
··· 54 54 .. 55 55 } => { 56 56 let (module, name) = match self.names.named_constructor(module, name) { 57 - NameContextInformation::Qualified(m, n) => (Some(m), n), 58 - NameContextInformation::Unqualified(n) => (None, n), 59 - NameContextInformation::Unimported(n) => { 60 - (Some(module.split('/').next_back().unwrap_or(module)), n) 57 + NameContextInformation::Qualified(module, name) => (Some(module), name), 58 + NameContextInformation::Unqualified(name) => (None, name), 59 + NameContextInformation::Unimported(module, name) => { 60 + (module.split('/').next_back(), name) 61 61 } 62 62 }; 63 63
+1 -1
compiler-core/src/language_server/code_action.rs
··· 5014 5014 .names 5015 5015 .named_constructor(constructor_module, constructor_name) 5016 5016 { 5017 - type_::printer::NameContextInformation::Unimported(_) => None, 5017 + type_::printer::NameContextInformation::Unimported(_, _) => None, 5018 5018 type_::printer::NameContextInformation::Unqualified(constructor_name) => { 5019 5019 Some(eco_format!("{constructor_name}")) 5020 5020 }
+98
compiler-core/src/language_server/tests/action.rs
··· 9796 9796 find_position_of("Ok(n)").to_selection() 9797 9797 ); 9798 9798 } 9799 + 9800 + #[test] 9801 + fn add_type_annotations_public_alias_to_internal_type_aliased_module() { 9802 + let src = " 9803 + import package as pkg 9804 + 9805 + pub fn main() { 9806 + pkg.make_wibble() 9807 + } 9808 + "; 9809 + 9810 + assert_code_action!( 9811 + ADD_ANNOTATION, 9812 + TestProject::for_source(src) 9813 + .add_package_module( 9814 + "package", 9815 + "package", 9816 + " 9817 + import package/internal 9818 + 9819 + pub type Wibble = internal.Wibble 9820 + 9821 + pub fn make_wibble() { 9822 + internal.Wibble 9823 + } 9824 + " 9825 + ) 9826 + .add_package_module("package", "package/internal", "pub type Wibble { Wibble }"), 9827 + find_position_of("main").to_selection(), 9828 + ); 9829 + } 9830 + 9831 + // https://github.com/gleam-lang/gleam/issues/3898 9832 + #[test] 9833 + fn add_type_annotations_public_alias_to_internal_type() { 9834 + let src = " 9835 + import package 9836 + 9837 + pub fn main() { 9838 + package.make_wibble() 9839 + } 9840 + "; 9841 + 9842 + assert_code_action!( 9843 + ADD_ANNOTATION, 9844 + TestProject::for_source(src) 9845 + .add_package_module( 9846 + "package", 9847 + "package", 9848 + " 9849 + import package/internal 9850 + 9851 + pub type Wibble = internal.Wibble 9852 + 9853 + pub fn make_wibble() { 9854 + internal.Wibble 9855 + } 9856 + " 9857 + ) 9858 + .add_package_module("package", "package/internal", "pub type Wibble { Wibble }"), 9859 + find_position_of("main").to_selection(), 9860 + ); 9861 + } 9862 + 9863 + #[test] 9864 + fn add_type_annotations_public_alias_to_internal_generic_type() { 9865 + let src = " 9866 + import package 9867 + 9868 + pub fn main() { 9869 + package.make_wibble(10) 9870 + } 9871 + "; 9872 + 9873 + assert_code_action!( 9874 + ADD_ANNOTATION, 9875 + TestProject::for_source(src) 9876 + .add_package_module( 9877 + "package", 9878 + "package", 9879 + " 9880 + import package/internal 9881 + 9882 + pub type Wibble(a, b) = internal.Wibble(a, b) 9883 + 9884 + pub fn make_wibble(x) { 9885 + internal.Wibble(x) 9886 + } 9887 + " 9888 + ) 9889 + .add_package_module( 9890 + "package", 9891 + "package/internal", 9892 + "pub type Wibble(a, b) { Wibble(a) }" 9893 + ), 9894 + find_position_of("main").to_selection(), 9895 + ); 9896 + }
+21
compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__add_type_annotations_public_alias_to_internal_generic_type.snap
··· 1 + --- 2 + source: compiler-core/src/language_server/tests/action.rs 3 + expression: "\nimport package\n\npub fn main() {\n package.make_wibble(10)\n}\n" 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + import package 8 + 9 + pub fn main() { 10 + 11 + package.make_wibble(10) 12 + } 13 + 14 + 15 + ----- AFTER ACTION 16 + 17 + import package 18 + 19 + pub fn main() -> package.Wibble(Int, a) { 20 + package.make_wibble(10) 21 + }
+21
compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__add_type_annotations_public_alias_to_internal_type.snap
··· 1 + --- 2 + source: compiler-core/src/language_server/tests/action.rs 3 + expression: "\nimport package\n\npub fn main() {\n package.make_wibble()\n}\n" 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + import package 8 + 9 + pub fn main() { 10 + 11 + package.make_wibble() 12 + } 13 + 14 + 15 + ----- AFTER ACTION 16 + 17 + import package 18 + 19 + pub fn main() -> package.Wibble { 20 + package.make_wibble() 21 + }
+21
compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__add_type_annotations_public_alias_to_internal_type_aliased_module.snap
··· 1 + --- 2 + source: compiler-core/src/language_server/tests/action.rs 3 + expression: "\nimport package as pkg\n\npub fn main() {\n pkg.make_wibble()\n}\n" 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + import package as pkg 8 + 9 + pub fn main() { 10 + 11 + pkg.make_wibble() 12 + } 13 + 14 + 15 + ----- AFTER ACTION 16 + 17 + import package as pkg 18 + 19 + pub fn main() -> pkg.Wibble { 20 + pkg.make_wibble() 21 + }
+1
compiler-core/src/parse/snapshots/gleam_core__parse__tests__const_string_concat.snap
··· 100 100 imported_modules: {}, 101 101 type_variables: {}, 102 102 local_value_constructors: {}, 103 + reexport_aliases: {}, 103 104 }, 104 105 unused_definition_positions: {}, 105 106 },
+1
compiler-core/src/parse/snapshots/gleam_core__parse__tests__deprecation_attribute_on_type_variant.snap
··· 69 69 imported_modules: {}, 70 70 type_variables: {}, 71 71 local_value_constructors: {}, 72 + reexport_aliases: {}, 72 73 }, 73 74 unused_definition_positions: {}, 74 75 },
+1
compiler-core/src/parse/snapshots/gleam_core__parse__tests__import_type.snap
··· 69 69 imported_modules: {}, 70 70 type_variables: {}, 71 71 local_value_constructors: {}, 72 + reexport_aliases: {}, 72 73 }, 73 74 unused_definition_positions: {}, 74 75 },
+1
compiler-core/src/parse/snapshots/gleam_core__parse__tests__private_opaque_type_is_parsed.snap
··· 53 53 imported_modules: {}, 54 54 type_variables: {}, 55 55 local_value_constructors: {}, 56 + reexport_aliases: {}, 56 57 }, 57 58 unused_definition_positions: {}, 58 59 },
+1
compiler-core/src/parse/snapshots/gleam_core__parse__tests__record_access_no_label.snap
··· 171 171 imported_modules: {}, 172 172 type_variables: {}, 173 173 local_value_constructors: {}, 174 + reexport_aliases: {}, 174 175 }, 175 176 unused_definition_positions: {}, 176 177 },
+45 -13
compiler-core/src/type_/environment.rs
··· 113 113 .get(PRELUDE_MODULE_NAME) 114 114 .expect("Unable to find prelude in importable modules"); 115 115 116 - let mut names = Names::new(); 117 - for name in prelude.values.keys() { 118 - names.named_constructor_in_scope( 119 - PRELUDE_MODULE_NAME.into(), 120 - name.clone(), 121 - name.clone(), 122 - ); 123 - } 124 - 125 - for name in prelude.types.keys() { 126 - names.named_type_in_scope(PRELUDE_MODULE_NAME.into(), name.clone(), name.clone()); 127 - } 116 + let names = Self::build_names(&current_package, prelude, importable_modules); 128 117 129 118 Self { 130 - current_package: current_package.clone(), 119 + current_package, 131 120 gleam_version, 132 121 previous_id: ids.next(), 133 122 ids, ··· 152 141 references: ReferenceTracker::new(), 153 142 dev_dependencies, 154 143 } 144 + } 145 + 146 + fn build_names( 147 + current_package: &str, 148 + prelude: &ModuleInterface, 149 + importable_modules: &im::HashMap<EcoString, ModuleInterface>, 150 + ) -> Names { 151 + let mut names = Names::new(); 152 + 153 + // Insert prelude types and values into scope 154 + for name in prelude.values.keys() { 155 + names.named_constructor_in_scope( 156 + PRELUDE_MODULE_NAME.into(), 157 + name.clone(), 158 + name.clone(), 159 + ); 160 + } 161 + for name in prelude.types.keys() { 162 + names.named_type_in_scope(PRELUDE_MODULE_NAME.into(), name.clone(), name.clone()); 163 + } 164 + 165 + // Find potential type aliases which reexport internal types 166 + for module in importable_modules.values() { 167 + // Internal types are accessibly within the package they are defined, 168 + // so it doesn't make sense to look for reexports within the same 169 + // package. 170 + if module.package == current_package { 171 + continue; 172 + } 173 + // Internal modules are not part of the public API so they are also 174 + // not considered. 175 + if module.is_internal { 176 + continue; 177 + } 178 + for (alias_name, alias) in module.type_aliases.iter() { 179 + // An alias can only be a public reexport if it is public. 180 + if alias.publicity.is_public() { 181 + names.maybe_register_reexport_alias(&module.package, alias_name, alias); 182 + } 183 + } 184 + } 185 + 186 + names 155 187 } 156 188 } 157 189
+79 -11
compiler-core/src/type_/printer.rs
··· 5 5 6 6 use crate::{ 7 7 ast::SrcSpan, 8 - type_::{Type, TypeVar}, 8 + type_::{Type, TypeAliasConstructor, TypeVar}, 9 9 }; 10 10 11 11 /// This class keeps track of what names are used for modules in the current ··· 117 117 /// - value: `"Woo"` 118 118 /// 119 119 local_value_constructors: BiMap<(EcoString, EcoString), EcoString>, 120 + 121 + /// A map containing information about public alias of internal types in 122 + /// other packages. This is a common pattern in Gleam, in order to reexport 123 + /// an internal type, without exposing its implementation details. Because 124 + /// of this, we want to be able to properly handle this case, and use the 125 + /// public alias rather than the internal underlying type. Since Gleam type 126 + /// aliases are not part of the type system, we have to track them manually 127 + /// here. 128 + /// 129 + /// This is a mapping of internal types to their public aliases that we want 130 + /// to favour over the internal types. 131 + /// 132 + /// For example, if we had the following code: 133 + /// 134 + /// ```gleam 135 + /// // lustre/element.gleam 136 + /// import lustre/internal 137 + /// 138 + /// pub type Element(a) = internal.Element(a) 139 + /// ``` 140 + /// 141 + /// This map would contain a key of `("lustre/internal", "Element")` with a 142 + /// value of `("lustre/element", "Element")`. This can then be used to look 143 + /// up the alias we want to print based on the type we are printing. 144 + /// 145 + reexport_aliases: HashMap<(EcoString, EcoString), (EcoString, EcoString)>, 120 146 } 121 147 122 148 /// The `PartialEq` implementation for `Type` doesn't account for `TypeVar::Link`, ··· 139 165 imported_modules: Default::default(), 140 166 type_variables: Default::default(), 141 167 local_value_constructors: Default::default(), 168 + reexport_aliases: Default::default(), 142 169 } 143 170 } 144 171 ··· 195 222 .map(|(_, location)| location) 196 223 } 197 224 225 + pub fn maybe_register_reexport_alias( 226 + &mut self, 227 + package: &EcoString, 228 + alias_name: &EcoString, 229 + alias: &TypeAliasConstructor, 230 + ) { 231 + match alias.type_.as_ref() { 232 + Type::Named { 233 + publicity, 234 + package: type_package, 235 + module, 236 + name, 237 + arguments, 238 + .. 239 + } => { 240 + // We only count this alias as a reexport if it is: 241 + // - aliasing a type in the same package 242 + // - the type is internal 243 + // - the alias exposes the same type parameters as the internal type 244 + if type_package == package 245 + && publicity.is_internal() 246 + && compare_arguments(arguments, &alias.parameters) 247 + { 248 + _ = self.reexport_aliases.insert( 249 + (module.clone(), name.clone()), 250 + (alias.module.clone(), alias_name.clone()), 251 + ); 252 + } 253 + } 254 + _ => {} 255 + } 256 + } 257 + 198 258 /// Get the name and optional module qualifier for a named type. 199 259 pub fn named_type<'a>( 200 260 &'a self, ··· 207 267 return NameContextInformation::Qualified(module, name.as_str()); 208 268 }; 209 269 210 - return NameContextInformation::Unimported(name.as_str()); 270 + return NameContextInformation::Unimported(module, name); 211 271 } 212 272 213 273 let key = (module.clone(), name.clone()); ··· 218 278 return NameContextInformation::Unqualified(name.as_str()); 219 279 } 220 280 281 + if let Some((module, alias)) = self.reexport_aliases.get(&key) { 282 + if let Some((module, _)) = self.imported_modules.get(module) { 283 + return NameContextInformation::Qualified(module, alias); 284 + } else { 285 + return NameContextInformation::Unimported(module, alias); 286 + } 287 + } 288 + 221 289 // This type is from a module that has been imported 222 290 if let Some((module, _)) = self.imported_modules.get(module) { 223 291 return NameContextInformation::Qualified(module, name.as_str()); 224 292 }; 225 293 226 - NameContextInformation::Unimported(name.as_str()) 294 + NameContextInformation::Unimported(module, name) 227 295 } 228 296 229 297 /// Record a named value in this module. ··· 257 325 return NameContextInformation::Qualified(module, name.as_str()); 258 326 }; 259 327 260 - NameContextInformation::Unimported(name.as_str()) 328 + NameContextInformation::Unimported(module, name) 261 329 } 262 330 263 331 pub fn is_imported(&self, module: &str) -> bool { ··· 272 340 #[derive(Debug)] 273 341 pub enum NameContextInformation<'a> { 274 342 /// This type is from a module that has not been imported in this module. 275 - Unimported(&'a str), 343 + Unimported(&'a str, &'a str), 276 344 /// This type has been imported in an unqualifid fashion in this module. 277 345 Unqualified(&'a str), 278 346 /// This type is from a module that has been imported. ··· 404 472 .. 405 473 } => { 406 474 let (module, name) = match self.names.named_type(module, name, print_mode) { 407 - NameContextInformation::Qualified(m, n) => (Some(m), n), 408 - NameContextInformation::Unqualified(n) => (None, n), 475 + NameContextInformation::Qualified(module, name) => (Some(module), name), 476 + NameContextInformation::Unqualified(name) => (None, name), 409 477 // TODO: indicate that the module is not import and as such 410 478 // needs to be, as well as how. 411 - NameContextInformation::Unimported(n) => { 412 - (Some(module.split('/').next_back().unwrap_or(module)), n) 479 + NameContextInformation::Unimported(module, name) => { 480 + (module.split('/').next_back(), name) 413 481 } 414 482 }; 415 483 ··· 452 520 let (module, name) = match self.names.named_constructor(module, name) { 453 521 NameContextInformation::Qualified(module, name) => (Some(module), name), 454 522 NameContextInformation::Unqualified(name) => (None, name), 455 - NameContextInformation::Unimported(name) => { 456 - (Some(module.split('/').next_back().unwrap_or(module)), name) 523 + NameContextInformation::Unimported(module, name) => { 524 + (module.split('/').next_back(), name) 457 525 } 458 526 }; 459 527