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

Configure Feed

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

do not show structure of internal type in missing patterns

+467 -23
+12 -1
CHANGELOG.md
··· 24 24 number by the literal number `0.0`. 25 25 ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 26 26 27 + - The compiler no longer shows the structure of internal types when displaying 28 + an "Inexhaustive patterns" error, making it harder to inadvertently rely on 29 + internal implementation details. 30 + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 31 + 27 32 ### Build tool 28 33 29 - - Upgraded `actions/checkout` from v4 to v6 in the GitHub Actions workflow used by `gleam new`. 34 + - Upgraded `actions/checkout` from v4 to v6 in the GitHub Actions workflow used 35 + by `gleam new`. 30 36 ([Christian Widlund](https://github.com/chrillep)) 31 37 32 38 - When adding a package that does not exist on Hex, the message is a bit ··· 99 105 100 106 - The language server now suggests completions for keywords like `echo`, 101 107 `panic`, and `todo`. 108 + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 109 + 110 + - The "Add missing patterns" code action will insert a catch all pattern for 111 + internal types, making it harder to inadvertently rely on internal 112 + implementation details. 102 113 ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 103 114 104 115 - The language server now suggests adding missing type parameters
+1 -1
compiler-core/src/exhaustiveness/missing_patterns.rs
··· 76 76 terms: vec![], 77 77 missing: IndexSet::new(), 78 78 environment, 79 - printer: Printer::new(&environment.names), 79 + printer: Printer::new(environment.current_module.clone(), &environment.names), 80 80 } 81 81 } 82 82
+48 -21
compiler-core/src/exhaustiveness/printer.rs
··· 2 2 3 3 use ecow::EcoString; 4 4 5 - use crate::type_::printer::{NameContextInformation, Names}; 5 + use crate::{ 6 + ast::Publicity, 7 + type_::printer::{NameContextInformation, Names}, 8 + }; 6 9 7 10 use super::{Variable, missing_patterns::Term}; 8 11 9 12 #[derive(Debug)] 10 13 pub struct Printer<'a> { 11 14 names: &'a Names, 15 + /// This is the module that is being currently analysed. 16 + current_module: EcoString, 12 17 } 13 18 14 19 impl<'a> Printer<'a> { 15 - pub fn new(names: &'a Names) -> Self { 16 - Printer { names } 20 + pub fn new(current_module: EcoString, names: &'a Names) -> Self { 21 + Printer { 22 + current_module, 23 + names, 24 + } 17 25 } 18 26 19 27 pub fn print_terms( ··· 51 59 name, 52 60 module, 53 61 fields, 54 - .. 62 + variable, 55 63 } => { 64 + let is_defined_in_current_module = *module == self.current_module; 65 + let is_internal = variable 66 + .type_ 67 + .publicity() 68 + .unwrap_or(Publicity::Public) 69 + .is_internal(); 70 + 71 + // We don't want to expose information about an internal type, 72 + // making it easy to rely on its internal structure. 73 + // So what we do is we just show a catch all pattern `_` for 74 + // those. 75 + // We do this only if the internal type is defined in a 76 + // different module from the one being analysed, otherwise it's 77 + // totally fair to want to match on such type. 78 + if is_internal && !is_defined_in_current_module { 79 + buffer.push('_'); 80 + return; 81 + } 82 + 56 83 let (module, name) = match self.names.named_constructor(module, name) { 57 84 NameContextInformation::Qualified(module, name) => (Some(module), name), 58 85 NameContextInformation::Unqualified(name) => (None, name), ··· 232 259 233 260 #[test] 234 261 fn test_value_in_current_module() { 262 + let current_module = EcoString::from("module"); 235 263 let mut names = Names::new(); 264 + names.named_constructor_in_scope(current_module.clone(), "Wibble".into(), "Wibble".into()); 236 265 237 - names.named_constructor_in_scope("module".into(), "Wibble".into(), "Wibble".into()); 238 - 239 - let printer = Printer::new(&names); 266 + let printer = Printer::new(current_module.clone(), &names); 240 267 241 268 let subjects = &[make_variable(0)]; 242 269 let term = Term::Variant { 243 270 variable: subjects[0].clone(), 244 271 name: "Wibble".into(), 245 - module: "module".into(), 272 + module: current_module, 246 273 fields: Vec::new(), 247 274 }; 248 275 ··· 254 281 255 282 #[test] 256 283 fn test_value_in_current_module_with_arguments() { 284 + let current_module = EcoString::from("module"); 257 285 let mut names = Names::new(); 286 + names.named_constructor_in_scope(current_module.clone(), "Wibble".into(), "Wibble".into()); 258 287 259 - names.named_constructor_in_scope("module".into(), "Wibble".into(), "Wibble".into()); 260 - 261 - let printer = Printer::new(&names); 288 + let printer = Printer::new(current_module.clone(), &names); 262 289 263 290 let var1 = make_variable(1); 264 291 ··· 268 295 let term = Term::Variant { 269 296 variable: subjects[0].clone(), 270 297 name: "Wibble".into(), 271 - module: "module".into(), 298 + module: current_module, 272 299 fields: vec![field(var1.clone(), None), field(var2.clone(), None)], 273 300 }; 274 301 ··· 287 314 288 315 #[test] 289 316 fn test_value_in_current_module_with_labelled_arguments() { 317 + let current_module = EcoString::from("module"); 290 318 let mut names = Names::new(); 319 + names.named_constructor_in_scope(current_module.clone(), "Wibble".into(), "Wibble".into()); 291 320 292 - names.named_constructor_in_scope("module".into(), "Wibble".into(), "Wibble".into()); 293 - 294 - let printer = Printer::new(&names); 321 + let printer = Printer::new(current_module.clone(), &names); 295 322 296 323 let var1 = make_variable(1); 297 324 ··· 301 328 let term = Term::Variant { 302 329 variable: subjects[0].clone(), 303 330 name: "Wibble".into(), 304 - module: "module".into(), 331 + module: current_module, 305 332 fields: vec![ 306 333 field(var1.clone(), Some("list")), 307 334 field(var2.clone(), Some("other")), ··· 331 358 .is_none() 332 359 ); 333 360 334 - let printer = Printer::new(&names); 361 + let printer = Printer::new("module".into(), &names); 335 362 336 363 let subjects = &[make_variable(0)]; 337 364 let term = Term::Variant { ··· 356 383 357 384 names.named_constructor_in_scope("regex".into(), "Regex".into(), "Regex".into()); 358 385 359 - let printer = Printer::new(&names); 386 + let printer = Printer::new("module".into(), &names); 360 387 361 388 let arg = make_variable(1); 362 389 ··· 381 408 names.named_constructor_in_scope("regex".into(), "Regex".into(), "Reg".into()); 382 409 names.named_constructor_in_scope("gleam".into(), "None".into(), "None".into()); 383 410 384 - let printer = Printer::new(&names); 411 + let printer = Printer::new("current_module".into(), &names); 385 412 386 413 let arg = make_variable(1); 387 414 ··· 413 440 414 441 names.named_constructor_in_scope("module".into(), "Type".into(), "Type".into()); 415 442 416 - let printer = Printer::new(&names); 443 + let printer = Printer::new("module".into(), &names); 417 444 418 445 let var1 = make_variable(1); 419 446 let var2 = make_variable(2); ··· 456 483 names.named_constructor_in_scope("gleam".into(), "Ok".into(), "Ok".into()); 457 484 names.named_constructor_in_scope("gleam".into(), "False".into(), "False".into()); 458 485 459 - let printer = Printer::new(&names); 486 + let printer = Printer::new("module".into(), &names); 460 487 461 488 let subjects = &[make_variable(0), make_variable(1), make_variable(2)]; 462 489
+9
compiler-core/src/type_.rs
··· 529 529 } 530 530 } 531 531 532 + /// If the type is named, return its publicity. 533 + /// 534 + pub fn publicity(&self) -> Option<Publicity> { 535 + match self { 536 + Type::Named { publicity, .. } => Some(*publicity), 537 + Type::Fn { .. } | Type::Var { .. } | Type::Tuple { .. } => None, 538 + } 539 + } 540 + 532 541 #[must_use] 533 542 /// Returns `true` is the two types are the same. This differs from the 534 543 /// standard `Eq` implementation as it also follows all links to check if
+36
compiler-core/src/type_/tests/errors.rs
··· 3441 3441 " 3442 3442 ); 3443 3443 } 3444 + 3445 + #[test] 3446 + fn incomplete_pattern_does_not_show_structure_of_internal_type_outside_of_its_module() { 3447 + assert_module_error!( 3448 + ( 3449 + "wibble", 3450 + "@internal 3451 + pub type Wibble { Wibble Wobble Woo }" 3452 + ), 3453 + "import wibble.{type Wibble} 3454 + 3455 + pub fn go(wibble: Wibble) { 3456 + case wibble {} 3457 + }" 3458 + ); 3459 + } 3460 + 3461 + #[test] 3462 + fn incomplete_pattern_does_not_show_structure_of_internal_type_outside_of_its_module_2() { 3463 + assert_module_error!( 3464 + ( 3465 + "wibble", 3466 + "@internal 3467 + pub type Wibble { Wibble Wobble Woo }" 3468 + ), 3469 + "import wibble.{type Wibble} 3470 + 3471 + pub type Type { 3472 + Type(wibble: Wibble, list: List(Int)) 3473 + } 3474 + 3475 + pub fn go(value: Type) { 3476 + case value {} 3477 + }" 3478 + ); 3479 + }
+29
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__incomplete_pattern_does_not_show_structure_of_internal_type_outside_of_its_module.snap
··· 1 + --- 2 + source: compiler-core/src/type_/tests/errors.rs 3 + expression: "import wibble.{type Wibble}\n\npub fn go(wibble: Wibble) {\n case wibble {}\n}" 4 + --- 5 + ----- SOURCE CODE 6 + -- wibble.gleam 7 + @internal 8 + pub type Wibble { Wibble Wobble Woo } 9 + 10 + -- main.gleam 11 + import wibble.{type Wibble} 12 + 13 + pub fn go(wibble: Wibble) { 14 + case wibble {} 15 + } 16 + 17 + ----- ERROR 18 + error: Inexhaustive patterns 19 + ┌─ /src/one/two.gleam:4:3 20 + 21 + 4 │ case wibble {} 22 + │ ^^^^^^^^^^^^^^ 23 + 24 + This case expression does not have a pattern for all possible values. If it 25 + is run on one of the values without a pattern then it will crash. 26 + 27 + The missing patterns are: 28 + 29 + _
+33
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__incomplete_pattern_does_not_show_structure_of_internal_type_outside_of_its_module_2.snap
··· 1 + --- 2 + source: compiler-core/src/type_/tests/errors.rs 3 + expression: "import wibble.{type Wibble}\n\npub type Type {\n Type(wibble: Wibble, list: List(Int))\n}\n\npub fn go(value: Type) {\n case value {}\n}" 4 + --- 5 + ----- SOURCE CODE 6 + -- wibble.gleam 7 + @internal 8 + pub type Wibble { Wibble Wobble Woo } 9 + 10 + -- main.gleam 11 + import wibble.{type Wibble} 12 + 13 + pub type Type { 14 + Type(wibble: Wibble, list: List(Int)) 15 + } 16 + 17 + pub fn go(value: Type) { 18 + case value {} 19 + } 20 + 21 + ----- ERROR 22 + error: Inexhaustive patterns 23 + ┌─ /src/one/two.gleam:8:3 24 + 25 + 8 │ case value {} 26 + │ ^^^^^^^^^^^^^ 27 + 28 + This case expression does not have a pattern for all possible values. If it 29 + is run on one of the values without a pattern then it will crash. 30 + 31 + The missing patterns are: 32 + 33 + Type(wibble:, list:)
+152
language-server/src/tests/action.rs
··· 9722 9722 ); 9723 9723 } 9724 9724 9725 + #[test] 9726 + fn pattern_match_on_variable_does_not_add_patterns_for_internal_type() { 9727 + let src = " 9728 + import wibble 9729 + 9730 + pub type Type { 9731 + Type(wibble: wibble.Wibble, list: List(Int)) 9732 + } 9733 + 9734 + pub fn main(thing: Type) { 9735 + case thing { 9736 + Type(wibble:, ..) -> todo 9737 + } 9738 + } 9739 + "; 9740 + 9741 + assert_no_code_actions!( 9742 + PATTERN_MATCH_ON_VARIABLE, 9743 + TestProject::for_source(src).add_module( 9744 + "wibble", 9745 + "@internal pub type Wibble { Wibble(Int) Wobble(String) }" 9746 + ), 9747 + find_position_of("wibble").nth_occurrence(3).to_selection(), 9748 + ); 9749 + } 9750 + 9751 + #[test] 9752 + fn pattern_match_on_argument_does_not_add_patterns_for_internal_type() { 9753 + let src = " 9754 + import wibble 9755 + 9756 + pub fn main(thing: wibble.Wibble) {} 9757 + "; 9758 + 9759 + assert_no_code_actions!( 9760 + PATTERN_MATCH_ON_ARGUMENT, 9761 + TestProject::for_source(src).add_module( 9762 + "wibble", 9763 + "@internal pub type Wibble { Wibble(Int) Wobble(String) }" 9764 + ), 9765 + find_position_of("thing").to_selection(), 9766 + ); 9767 + } 9768 + 9769 + #[test] 9770 + fn pattern_match_on_argument_adds_patterns_for_internal_type_inside_module_where_it_is_defined() { 9771 + let src = " 9772 + @internal 9773 + pub type Wibble { 9774 + Wibble(Int) 9775 + Wobble(String) 9776 + } 9777 + 9778 + pub fn main(thing: Wibble) {} 9779 + "; 9780 + 9781 + assert_code_action!( 9782 + PATTERN_MATCH_ON_ARGUMENT, 9783 + TestProject::for_source(src), 9784 + find_position_of("thing").to_selection(), 9785 + ); 9786 + } 9787 + 9788 + #[test] 9789 + fn add_missing_patterns_adds_a_discard_for_opaque_type() { 9790 + let src = " 9791 + import wibble 9792 + 9793 + pub fn main(w: wibble.Wibble) { 9794 + case w {} 9795 + } 9796 + "; 9797 + 9798 + assert_code_action!( 9799 + ADD_MISSING_PATTERNS, 9800 + TestProject::for_source(src).add_module( 9801 + "wibble", 9802 + "@internal pub type Wibble { Wibble(Int) Wobble(String) }" 9803 + ), 9804 + find_position_of("{}").to_selection(), 9805 + ); 9806 + } 9807 + 9808 + #[test] 9809 + fn add_missing_patterns_adds_a_discard_for_opaque_type_1() { 9810 + let src = " 9811 + import wibble 9812 + 9813 + pub type Type { 9814 + Type(wibble: wibble.Wibble, list: List(Int)) 9815 + } 9816 + 9817 + pub fn main(thing: Type) { 9818 + case thing {} 9819 + } 9820 + "; 9821 + 9822 + assert_code_action!( 9823 + ADD_MISSING_PATTERNS, 9824 + TestProject::for_source(src).add_module( 9825 + "wibble", 9826 + "@internal pub type Wibble { Wibble(Int) Wobble(String) }" 9827 + ), 9828 + find_position_of("{}").to_selection(), 9829 + ); 9830 + } 9831 + 9832 + #[test] 9833 + fn add_missing_patterns_adds_a_discard_for_opaque_type_2() { 9834 + let src = " 9835 + import wibble 9836 + 9837 + pub type Type { 9838 + Type(wibble.Wibble) 9839 + } 9840 + 9841 + pub fn main(thing: Type) { 9842 + case thing {} 9843 + } 9844 + "; 9845 + 9846 + assert_code_action!( 9847 + ADD_MISSING_PATTERNS, 9848 + TestProject::for_source(src).add_module( 9849 + "wibble", 9850 + "@internal pub type Wibble { Wibble(Int) Wobble(String) }" 9851 + ), 9852 + find_position_of("{}").to_selection(), 9853 + ); 9854 + } 9855 + 9856 + #[test] 9857 + fn add_missing_patterns_adds_patterns_for_internal_type_inside_same_module_where_it_is_defined() { 9858 + let src = " 9859 + @internal 9860 + pub type Wibble { 9861 + Wibble(Int) 9862 + Wobble(String) 9863 + } 9864 + 9865 + pub fn main(thing: Wibble) { 9866 + case thing {} 9867 + } 9868 + "; 9869 + 9870 + assert_code_action!( 9871 + ADD_MISSING_PATTERNS, 9872 + TestProject::for_source(src), 9873 + find_position_of("thing").nth_occurrence(2).to_selection(), 9874 + ); 9875 + } 9876 + 9725 9877 // https://github.com/gleam-lang/gleam/issues/4653 9726 9878 #[test] 9727 9879 fn generate_function_capture() {
+23
language-server/src/tests/snapshots/gleam_language_server__tests__action__add_missing_patterns_adds_a_discard_for_opaque_type.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\nimport wibble\n\npub fn main(w: wibble.Wibble) {\n case w {}\n}\n" 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + import wibble 8 + 9 + pub fn main(w: wibble.Wibble) { 10 + case w {} 11 + 12 + } 13 + 14 + 15 + ----- AFTER ACTION 16 + 17 + import wibble 18 + 19 + pub fn main(w: wibble.Wibble) { 20 + case w { 21 + _ -> todo 22 + } 23 + }
+31
language-server/src/tests/snapshots/gleam_language_server__tests__action__add_missing_patterns_adds_a_discard_for_opaque_type_1.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\nimport wibble\n\npub type Type {\n Type(wibble: wibble.Wibble, list: List(Int))\n}\n\npub fn main(thing: Type) {\n case thing {}\n}\n" 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + import wibble 8 + 9 + pub type Type { 10 + Type(wibble: wibble.Wibble, list: List(Int)) 11 + } 12 + 13 + pub fn main(thing: Type) { 14 + case thing {} 15 + 16 + } 17 + 18 + 19 + ----- AFTER ACTION 20 + 21 + import wibble 22 + 23 + pub type Type { 24 + Type(wibble: wibble.Wibble, list: List(Int)) 25 + } 26 + 27 + pub fn main(thing: Type) { 28 + case thing { 29 + Type(wibble:, list:) -> todo 30 + } 31 + }
+31
language-server/src/tests/snapshots/gleam_language_server__tests__action__add_missing_patterns_adds_a_discard_for_opaque_type_2.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\nimport wibble\n\npub type Type {\n Type(wibble.Wibble)\n}\n\npub fn main(thing: Type) {\n case thing {}\n}\n" 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + import wibble 8 + 9 + pub type Type { 10 + Type(wibble.Wibble) 11 + } 12 + 13 + pub fn main(thing: Type) { 14 + case thing {} 15 + 16 + } 17 + 18 + 19 + ----- AFTER ACTION 20 + 21 + import wibble 22 + 23 + pub type Type { 24 + Type(wibble.Wibble) 25 + } 26 + 27 + pub fn main(thing: Type) { 28 + case thing { 29 + Type(_) -> todo 30 + } 31 + }
+32
language-server/src/tests/snapshots/gleam_language_server__tests__action__add_missing_patterns_adds_patterns_for_internal_type_inside_same_module_where_it_is_defined.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\n@internal\npub type Wibble {\n Wibble(Int)\n Wobble(String)\n}\n\npub fn main(thing: Wibble) {\n case thing {}\n}\n" 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + @internal 8 + pub type Wibble { 9 + Wibble(Int) 10 + Wobble(String) 11 + } 12 + 13 + pub fn main(thing: Wibble) { 14 + case thing {} 15 + 16 + } 17 + 18 + 19 + ----- AFTER ACTION 20 + 21 + @internal 22 + pub type Wibble { 23 + Wibble(Int) 24 + Wobble(String) 25 + } 26 + 27 + pub fn main(thing: Wibble) { 28 + case thing { 29 + Wibble(_) -> todo 30 + Wobble(_) -> todo 31 + } 32 + }
+30
language-server/src/tests/snapshots/gleam_language_server__tests__action__pattern_match_on_argument_adds_patterns_for_internal_type_inside_module_where_it_is_defined.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\n@internal\npub type Wibble {\n Wibble(Int)\n Wobble(String)\n}\n\npub fn main(thing: Wibble) {}\n" 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + @internal 8 + pub type Wibble { 9 + Wibble(Int) 10 + Wobble(String) 11 + } 12 + 13 + pub fn main(thing: Wibble) {} 14 + 15 + 16 + 17 + ----- AFTER ACTION 18 + 19 + @internal 20 + pub type Wibble { 21 + Wibble(Int) 22 + Wobble(String) 23 + } 24 + 25 + pub fn main(thing: Wibble) { 26 + case thing { 27 + Wibble(int) -> todo 28 + Wobble(string) -> todo 29 + } 30 + }