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

Configure Feed

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

fix remove unreachable clauses

author
Giacomo Cavalieri
committer
Louis Pilfold
date (Jun 15, 2026, 10:56 AM +0100) commit ad54e3cc parent 47be6013 change-id syktqptr
+350 -30
+8
CHANGELOG.md
··· 68 68 69 69 ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 70 70 71 + - The "remove unreachable patterns" code action can now be triggered on 72 + unreachable alternative patterns of a case expression. 73 + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 74 + 71 75 - The language server now permits renaming type variables in functions, types, 72 76 and constants. For example: 73 77 ··· 165 169 use statement, only the selected statement(s) are extracted. 166 170 167 171 For example, 172 + 168 173 ```gleam 169 174 pub fn wibble() { 170 175 use wobble <- result.map(todo) ··· 172 177 echo wobble as "2" 173 178 } 174 179 ``` 180 + 175 181 is turned into 182 + 176 183 ```gleam 177 184 pub fn wibble() { 178 185 use wobble <- result.map(todo) ··· 184 191 Nil 185 192 } 186 193 ``` 194 + 187 195 ([Gavin Morrow](https://github.com/gavinmorrow))
+5
compiler-core/src/ast.rs
··· 1915 1915 } 1916 1916 } 1917 1917 1918 + /// Returns an iterator over all the alternative patterns of a case clause. 1919 + pub fn alternatives(&self) -> impl Iterator<Item = &Vec<TypedPattern>> { 1920 + std::iter::once(&self.pattern).chain(self.alternative_patterns.iter()) 1921 + } 1922 + 1918 1923 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> { 1919 1924 self.pattern 1920 1925 .iter()
+97 -12
language-server/src/code_action.rs
··· 10127 10127 let pattern_range = self.edits.src_span_to_lsp_range(clause.pattern_location()); 10128 10128 within(self.params.range, pattern_range) 10129 10129 }); 10130 - if is_hovering_clause { 10131 - self.clauses_to_delete = clauses 10132 - .iter() 10133 - .filter(|clause| { 10134 - self.unreachable_clauses 10135 - .contains(&clause.pattern_location()) 10136 - }) 10137 - .map(|clause| clause.location()) 10138 - .collect_vec(); 10139 - return; 10140 - } 10141 10130 10142 10131 // If we're not hovering any of the clauses then we want to 10143 10132 // keep visiting the case expression as the unreachable branch might be 10144 10133 // in one of the nested cases. 10145 - ast::visit::visit_typed_expr_case(self, location, type_, subjects, clauses, compiled_case); 10134 + if !is_hovering_clause { 10135 + ast::visit::visit_typed_expr_case( 10136 + self, 10137 + location, 10138 + type_, 10139 + subjects, 10140 + clauses, 10141 + compiled_case, 10142 + ); 10143 + return; 10144 + } 10145 + 10146 + for clause in clauses { 10147 + let mut all_alternatives_are_unreachable = true; 10148 + let mut unreachable_alternatives = vec![]; 10149 + let mut previous_alternative_end = None; 10150 + let mut all_previous_alternatives_were_deleted = true; 10151 + 10152 + for alternative in clause.alternatives() { 10153 + let alternative_location = alternative_location(alternative); 10154 + if self.unreachable_clauses.contains(&alternative_location) { 10155 + // If an alternative is unreachable we want to delete 10156 + // everything from the end of the previous alternative to 10157 + // the start of this one. 10158 + // 10159 + // ```gleam 10160 + // Error(_) | Error(_) | Ok(_) 10161 + // // ^^^^^^^^^^^ We want to delete all of this 10162 + // ``` 10163 + unreachable_alternatives.push( 10164 + previous_alternative_end.map_or(alternative_location, |previous_end| { 10165 + SrcSpan::new(previous_end, alternative_location.end) 10166 + }), 10167 + ); 10168 + } else { 10169 + // If all the previous alternatives have been deleted and 10170 + // this one is reachable there's some final cleanup we need 10171 + // to take care of: 10172 + // 10173 + // ```gleam 10174 + // Ok(_) | Ok(_) | Error(_) -> todo 10175 + // //^^^^^^^^^^^ All of this has been deleted, but there's 10176 + // // that last vertical bar that needs to be 10177 + // // taken care of! 10178 + // ``` 10179 + // 10180 + 10181 + if all_previous_alternatives_were_deleted 10182 + && let Some(end) = previous_alternative_end 10183 + { 10184 + self.clauses_to_delete 10185 + .push(SrcSpan::new(end, alternative_location.start)); 10186 + } 10187 + 10188 + all_previous_alternatives_were_deleted = false; 10189 + all_alternatives_are_unreachable = false; 10190 + } 10191 + 10192 + previous_alternative_end = alternative.last().map(|pattern| pattern.location().end); 10193 + } 10194 + 10195 + if all_alternatives_are_unreachable { 10196 + // If all the patterns of the clause are unreachable then we 10197 + // want to delete the entire branch: 10198 + // 10199 + // ```gleam 10200 + // case a, b { 10201 + // _, _ -> todo 10202 + // Ok(_), Ok(_) | Error(_), Error(_) -> todo 10203 + // // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 10204 + // // we want the entire branch to be deleted! 10205 + // } 10206 + // ``` 10207 + self.clauses_to_delete.push(clause.location()) 10208 + } else { 10209 + // If only some of the variants are unreachable but not all 10210 + // we want to delete just those. 10211 + // case a, b { 10212 + // 1, 2 | 1, 2 -> todo 10213 + // // ^^^^ just this one should be deleted 10214 + // } 10215 + self.clauses_to_delete.extend(&unreachable_alternatives); 10216 + } 10217 + } 10146 10218 } 10219 + } 10220 + 10221 + fn alternative_location(alternative: &[Pattern<Arc<Type>>]) -> SrcSpan { 10222 + let start = alternative 10223 + .first() 10224 + .map(|pattern| pattern.location().start) 10225 + .unwrap_or_default(); 10226 + let end = alternative 10227 + .last() 10228 + .map(|pattern| pattern.location().end) 10229 + .unwrap_or_default(); 10230 + 10231 + SrcSpan::new(start, end) 10147 10232 } 10148 10233 10149 10234 /// Code action to remove a record update when all of its fields have been
+102 -6
language-server/src/tests/action.rs
··· 442 442 } 443 443 444 444 #[test] 445 - fn generate_unqualified_variant_in_other_module_adds_an_unqualified_import_if_other_variants_are_unqualified() 446 - { 445 + fn generate_unqualified_variant_in_other_module_adds_an_unqualified_import_if_other_variants_are_unqualified( 446 + ) { 447 447 let src = r#" 448 448 import other.{ Wibble } 449 449 ··· 467 467 } 468 468 469 469 #[test] 470 - fn generate_unqualified_variant_in_other_module_adds_qualification_if_other_variants_are_not_imported() 471 - { 470 + fn generate_unqualified_variant_in_other_module_adds_qualification_if_other_variants_are_not_imported( 471 + ) { 472 472 let src = r#" 473 473 import other 474 474 ··· 8109 8109 } 8110 8110 8111 8111 #[test] 8112 - fn pattern_match_on_argument_with_multiple_constructors_is_nicely_formatted_in_function_with_empty_body() 8113 - { 8112 + fn pattern_match_on_argument_with_multiple_constructors_is_nicely_formatted_in_function_with_empty_body( 8113 + ) { 8114 8114 assert_code_action!( 8115 8115 PATTERN_MATCH_ON_ARGUMENT, 8116 8116 " ··· 11774 11774 } 11775 11775 ", 11776 11776 find_position_of("Ok(1)").to_selection() 11777 + ); 11778 + } 11779 + 11780 + #[test] 11781 + fn remove_unreachable_clauses_removes_alternative_patterns() { 11782 + assert_code_action!( 11783 + REMOVE_UNREACHABLE_CLAUSES, 11784 + "pub fn main(x) { 11785 + case x { 11786 + Ok(n) -> 1 11787 + Ok(1) -> 3 11788 + Error(_) | Ok(_) -> todo 11789 + } 11790 + } 11791 + ", 11792 + find_position_of("Ok(1)").to_selection() 11793 + ); 11794 + } 11795 + 11796 + #[test] 11797 + fn remove_unreachable_clauses_removes_multiple_alternative_patterns_at_the_start_of_branch() { 11798 + assert_code_action!( 11799 + REMOVE_UNREACHABLE_CLAUSES, 11800 + "pub fn main(x) { 11801 + case x { 11802 + Ok(n) -> 1 11803 + Ok(1) -> 3 11804 + Ok(_) | Ok(_) | Ok(_) | Error(_) -> todo 11805 + } 11806 + } 11807 + ", 11808 + find_position_of("Ok(1)").to_selection() 11809 + ); 11810 + } 11811 + 11812 + #[test] 11813 + fn remove_unreachable_clauses_removes_multiple_alternative_patterns_at_the_end_of_branch() { 11814 + assert_code_action!( 11815 + REMOVE_UNREACHABLE_CLAUSES, 11816 + "pub fn main(x) { 11817 + case x { 11818 + Ok(n) -> 1 11819 + Ok(1) -> 3 11820 + Error(_) | Ok(_) | Ok(_) | Ok(_) -> todo 11821 + } 11822 + } 11823 + ", 11824 + find_position_of("Ok(1)").to_selection() 11825 + ); 11826 + } 11827 + 11828 + #[test] 11829 + fn remove_unreachable_clauses_removes_multiple_alternative_patterns_in_the_middle_of_branch() { 11830 + assert_code_action!( 11831 + REMOVE_UNREACHABLE_CLAUSES, 11832 + "pub fn main(x) { 11833 + case x { 11834 + 1 -> todo 11835 + 2 -> todo 11836 + 3 | 3 | 3 | 3 | 4 -> todo 11837 + } 11838 + } 11839 + ", 11840 + find_position_of("3").nth_occurrence(2).to_selection() 11841 + ); 11842 + } 11843 + 11844 + #[test] 11845 + fn remove_unreachable_clauses_with_mix_of_reachable_and_unreachables() { 11846 + assert_code_action!( 11847 + REMOVE_UNREACHABLE_CLAUSES, 11848 + "pub fn main(x) { 11849 + case x { 11850 + 1 -> todo 11851 + 2 -> todo 11852 + 3 | 3 | 3 | 4 | 3 | 5 -> todo 11853 + } 11854 + } 11855 + ", 11856 + find_position_of("3").nth_occurrence(2).to_selection() 11857 + ); 11858 + } 11859 + 11860 + #[test] 11861 + fn remove_unreachable_clauses_can_be_triggered_from_alternative_pattern() { 11862 + assert_code_action!( 11863 + REMOVE_UNREACHABLE_CLAUSES, 11864 + "pub fn main(x) { 11865 + case x { 11866 + Ok(n) -> 1 11867 + Error(_) | Ok(_) -> 3 11868 + Ok(1) -> todo 11869 + } 11870 + } 11871 + ", 11872 + find_position_of("Ok(_)").to_selection() 11777 11873 ); 11778 11874 } 11779 11875
+23
language-server/src/tests/snapshots/gleam_language_server__tests__action__remove_unreachable_clauses_can_be_triggered_from_alternative_pattern.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "pub fn main(x) {\n case x {\n Ok(n) -> 1\n Error(_) | Ok(_) -> 3\n Ok(1) -> todo\n }\n}\n" 4 + --- 5 + ----- BEFORE ACTION 6 + pub fn main(x) { 7 + case x { 8 + Ok(n) -> 1 9 + Error(_) | Ok(_) -> 3 10 + 11 + Ok(1) -> todo 12 + } 13 + } 14 + 15 + 16 + ----- AFTER ACTION 17 + pub fn main(x) { 18 + case x { 19 + Ok(n) -> 1 20 + Error(_) -> 3 21 + 22 + } 23 + }
+23
language-server/src/tests/snapshots/gleam_language_server__tests__action__remove_unreachable_clauses_removes_alternative_patterns.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "pub fn main(x) {\n case x {\n Ok(n) -> 1\n Ok(1) -> 3\n Error(_) | Ok(_) -> todo\n }\n}\n" 4 + --- 5 + ----- BEFORE ACTION 6 + pub fn main(x) { 7 + case x { 8 + Ok(n) -> 1 9 + Ok(1) -> 3 10 + 11 + Error(_) | Ok(_) -> todo 12 + } 13 + } 14 + 15 + 16 + ----- AFTER ACTION 17 + pub fn main(x) { 18 + case x { 19 + Ok(n) -> 1 20 + 21 + Error(_) -> todo 22 + } 23 + }
+23
language-server/src/tests/snapshots/gleam_language_server__tests__action__remove_unreachable_clauses_removes_multiple_alternative_patterns_at_the_end_of_branch.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "pub fn main(x) {\n case x {\n Ok(n) -> 1\n Ok(1) -> 3\n Error(_) | Ok(_) | Ok(_) | Ok(_) -> todo\n }\n}\n" 4 + --- 5 + ----- BEFORE ACTION 6 + pub fn main(x) { 7 + case x { 8 + Ok(n) -> 1 9 + Ok(1) -> 3 10 + 11 + Error(_) | Ok(_) | Ok(_) | Ok(_) -> todo 12 + } 13 + } 14 + 15 + 16 + ----- AFTER ACTION 17 + pub fn main(x) { 18 + case x { 19 + Ok(n) -> 1 20 + 21 + Error(_) -> todo 22 + } 23 + }
+23
language-server/src/tests/snapshots/gleam_language_server__tests__action__remove_unreachable_clauses_removes_multiple_alternative_patterns_at_the_start_of_branch.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "pub fn main(x) {\n case x {\n Ok(n) -> 1\n Ok(1) -> 3\n Ok(_) | Ok(_) | Ok(_) | Error(_) -> todo\n }\n}\n" 4 + --- 5 + ----- BEFORE ACTION 6 + pub fn main(x) { 7 + case x { 8 + Ok(n) -> 1 9 + Ok(1) -> 3 10 + 11 + Ok(_) | Ok(_) | Ok(_) | Error(_) -> todo 12 + } 13 + } 14 + 15 + 16 + ----- AFTER ACTION 17 + pub fn main(x) { 18 + case x { 19 + Ok(n) -> 1 20 + 21 + Error(_) -> todo 22 + } 23 + }
+23
language-server/src/tests/snapshots/gleam_language_server__tests__action__remove_unreachable_clauses_removes_multiple_alternative_patterns_in_the_middle_of_branch.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "pub fn main(x) {\n case x {\n 1 -> todo\n 2 -> todo\n 3 | 3 | 3 | 3 | 4 -> todo\n }\n}\n" 4 + --- 5 + ----- BEFORE ACTION 6 + pub fn main(x) { 7 + case x { 8 + 1 -> todo 9 + 2 -> todo 10 + 3 | 3 | 3 | 3 | 4 -> todo 11 + 12 + } 13 + } 14 + 15 + 16 + ----- AFTER ACTION 17 + pub fn main(x) { 18 + case x { 19 + 1 -> todo 20 + 2 -> todo 21 + 3 | 4 -> todo 22 + } 23 + }
+23
language-server/src/tests/snapshots/gleam_language_server__tests__action__remove_unreachable_clauses_with_mix_of_reachable_and_unreachables.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "pub fn main(x) {\n case x {\n 1 -> todo\n 2 -> todo\n 3 | 3 | 3 | 4 | 3 | 5 -> todo\n }\n}\n" 4 + --- 5 + ----- BEFORE ACTION 6 + pub fn main(x) { 7 + case x { 8 + 1 -> todo 9 + 2 -> todo 10 + 3 | 3 | 3 | 4 | 3 | 5 -> todo 11 + 12 + } 13 + } 14 + 15 + 16 + ----- AFTER ACTION 17 + pub fn main(x) { 18 + case x { 19 + 1 -> todo 20 + 2 -> todo 21 + 3 | 4 | 5 -> todo 22 + } 23 + }
-3
test-commands/cases/escript_ok/manifest.toml
··· 1 - # SPDX-License-Identifier: Apache-2.0 2 - # SPDX-FileCopyrightText: 2026 The Gleam contributors 3 - 4 1 # Do not manually edit this file, it is managed by Gleam. 5 2 # 6 3 # This file locks the dependency versions used, to make your build
-3
test-commands/cases/escript_with_dependency/manifest.toml
··· 1 - # SPDX-License-Identifier: Apache-2.0 2 - # SPDX-FileCopyrightText: 2026 The Gleam contributors 3 - 4 1 # Do not manually edit this file, it is managed by Gleam. 5 2 # 6 3 # This file locks the dependency versions used, to make your build
-3
test-commands/cases/escript_with_wrong_arity_main_function/manifest.toml
··· 1 - # SPDX-License-Identifier: Apache-2.0 2 - # SPDX-FileCopyrightText: 2026 The Gleam contributors 3 - 4 1 # Do not manually edit this file, it is managed by Gleam. 5 2 # 6 3 # This file locks the dependency versions used, to make your build
-3
test-commands/cases/escript_without_main_function/manifest.toml
··· 1 - # SPDX-License-Identifier: Apache-2.0 2 - # SPDX-FileCopyrightText: 2026 The Gleam contributors 3 - 4 1 # Do not manually edit this file, it is managed by Gleam. 5 2 # 6 3 # This file locks the dependency versions used, to make your build