aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src')
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs172
1 files changed, 32 insertions, 140 deletions
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index f8859ff6d..fbd6a3ec3 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -1,17 +1,14 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::{collections::LinkedList, iter}; 3use std::iter;
4 4
5use hir::{Adt, HasSource, Semantics}; 5use hir::{Adt, HasSource, Semantics};
6use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
7 7
8use crate::{Assist, AssistCtx, AssistId}; 8use crate::{Assist, AssistCtx, AssistId};
9use ra_syntax::{ 9use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner};
10 ast::{self, edit::IndentLevel, make, AstNode, NameOwner},
11 SyntaxKind, SyntaxNode,
12};
13 10
14use ast::{MatchArm, MatchGuard, Pat}; 11use ast::{MatchArm, Pat};
15 12
16// Assist: fill_match_arms 13// Assist: fill_match_arms
17// 14//
@@ -57,48 +54,20 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
57 } 54 }
58 } 55 }
59 56
60 let mut has_partial_match = false;
61 let db = ctx.db; 57 let db = ctx.db;
62 let missing_arms: Vec<MatchArm> = variants 58 let missing_arms: Vec<MatchArm> = variants
63 .into_iter() 59 .into_iter()
64 .filter_map(|variant| build_pat(db, module, variant)) 60 .filter_map(|variant| build_pat(db, module, variant))
65 .filter(|variant_pat| { 61 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
66 !arms.iter().filter_map(|arm| arm.pat().map(|_| arm)).any(|arm| {
67 let pat = arm.pat().unwrap();
68
69 // Special casee OrPat as separate top-level pats
70 let pats: Vec<Pat> = match Pat::from(pat.clone()) {
71 Pat::OrPat(pats) => pats.pats().collect::<Vec<_>>(),
72 _ => vec![pat],
73 };
74
75 pats.iter().any(|pat| {
76 match does_arm_pat_match_variant(pat, arm.guard(), variant_pat) {
77 ArmMatch::Yes => true,
78 ArmMatch::No => false,
79 ArmMatch::Partial => {
80 has_partial_match = true;
81 true
82 }
83 }
84 })
85 })
86 })
87 .map(|pat| make::match_arm(iter::once(pat), make::expr_unit())) 62 .map(|pat| make::match_arm(iter::once(pat), make::expr_unit()))
88 .collect(); 63 .collect();
89 64
90 if missing_arms.is_empty() && !has_partial_match { 65 if missing_arms.is_empty() {
91 return None; 66 return None;
92 } 67 }
93 68
94 ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", |edit| { 69 ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", |edit| {
95 arms.extend(missing_arms); 70 arms.extend(missing_arms);
96 if has_partial_match {
97 arms.push(make::match_arm(
98 iter::once(make::placeholder_pat().into()),
99 make::expr_unit(),
100 ));
101 }
102 71
103 let indent_level = IndentLevel::from_node(match_arm_list.syntax()); 72 let indent_level = IndentLevel::from_node(match_arm_list.syntax());
104 let new_arm_list = indent_level.increase_indent(make::match_arm_list(arms)); 73 let new_arm_list = indent_level.increase_indent(make::match_arm_list(arms));
@@ -109,59 +78,23 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
109 }) 78 })
110} 79}
111 80
112enum ArmMatch { 81fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool {
113 Yes, 82 existing_arms.iter().filter_map(|arm| arm.pat()).all(|pat| {
114 No, 83 // Special casee OrPat as separate top-level pats
115 Partial, 84 let top_level_pats: Vec<Pat> = match pat {
116} 85 Pat::OrPat(pats) => pats.pats().collect::<Vec<_>>(),
117 86 _ => vec![pat],
118fn does_arm_pat_match_variant(arm: &Pat, arm_guard: Option<MatchGuard>, var: &Pat) -> ArmMatch { 87 };
119 let arm = flatten_pats(arm.clone());
120 let var = flatten_pats(var.clone());
121 let mut arm = arm.iter();
122 let mut var = var.iter();
123
124 // If the first part of the Pat don't match, there's no match
125 match (arm.next(), var.next()) {
126 (Some(arm), Some(var)) if arm.text() == var.text() => {}
127 _ => return ArmMatch::No,
128 }
129
130 // If we have a guard we automatically know we have a partial match
131 if arm_guard.is_some() {
132 return ArmMatch::Partial;
133 }
134 88
135 if arm.clone().count() != var.clone().count() { 89 !top_level_pats.iter().any(|pat| does_pat_match_variant(pat, var))
136 return ArmMatch::Partial; 90 })
137 }
138
139 let direct_match = arm.zip(var).all(|(arm, var)| {
140 if arm.text() == var.text() {
141 return true;
142 }
143 match (arm.kind(), var.kind()) {
144 (SyntaxKind::PLACEHOLDER_PAT, SyntaxKind::PLACEHOLDER_PAT) => true,
145 (SyntaxKind::DOT_DOT_PAT, SyntaxKind::PLACEHOLDER_PAT) => true,
146 (SyntaxKind::BIND_PAT, SyntaxKind::PLACEHOLDER_PAT) => true,
147 _ => false,
148 }
149 });
150
151 match direct_match {
152 true => ArmMatch::Yes,
153 false => ArmMatch::Partial,
154 }
155} 91}
156 92
157fn flatten_pats(pat: Pat) -> Vec<SyntaxNode> { 93fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
158 let mut pats: LinkedList<SyntaxNode> = pat.syntax().children().collect(); 94 let pat_head = pat.syntax().first_child().map(|node| node.text());
159 let mut out: Vec<SyntaxNode> = vec![]; 95 let var_head = var.syntax().first_child().map(|node| node.text());
160 while let Some(p) = pats.pop_front() { 96
161 pats.extend(p.children()); 97 pat_head == var_head
162 out.push(p);
163 }
164 out
165} 98}
166 99
167fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { 100fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> {
@@ -193,35 +126,25 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> O
193 126
194#[cfg(test)] 127#[cfg(test)]
195mod tests { 128mod tests {
196 use crate::helpers::{check_assist, check_assist_target}; 129 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target};
197 130
198 use super::fill_match_arms; 131 use super::fill_match_arms;
199 132
200 #[test] 133 #[test]
201 fn partial_fill_multi() { 134 fn all_match_arms_provided() {
202 check_assist( 135 check_assist_not_applicable(
203 fill_match_arms, 136 fill_match_arms,
204 r#" 137 r#"
205 enum A { 138 enum A {
206 As, 139 As,
207 Bs(i32, Option<i32>) 140 Bs{x:i32, y:Option<i32>},
141 Cs(i32, Option<i32>),
208 } 142 }
209 fn main() { 143 fn main() {
210 match A::As<|> { 144 match A::As<|> {
211 A::Bs(_, Some(_)) => (), 145 A::As,
212 } 146 A::Bs{x,y:Some(_)} => (),
213 } 147 A::Cs(_, Some(_)) => (),
214 "#,
215 r#"
216 enum A {
217 As,
218 Bs(i32, Option<i32>)
219 }
220 fn main() {
221 match <|>A::As {
222 A::Bs(_, Some(_)) => (),
223 A::As => (),
224 _ => (),
225 } 148 }
226 } 149 }
227 "#, 150 "#,
@@ -229,17 +152,19 @@ mod tests {
229 } 152 }
230 153
231 #[test] 154 #[test]
232 fn partial_fill_record() { 155 fn partial_fill_record_tuple() {
233 check_assist( 156 check_assist(
234 fill_match_arms, 157 fill_match_arms,
235 r#" 158 r#"
236 enum A { 159 enum A {
237 As, 160 As,
238 Bs{x:i32, y:Option<i32>}, 161 Bs{x:i32, y:Option<i32>},
162 Cs(i32, Option<i32>),
239 } 163 }
240 fn main() { 164 fn main() {
241 match A::As<|> { 165 match A::As<|> {
242 A::Bs{x,y:Some(_)} => (), 166 A::Bs{x,y:Some(_)} => (),
167 A::Cs(_, Some(_)) => (),
243 } 168 }
244 } 169 }
245 "#, 170 "#,
@@ -247,12 +172,13 @@ mod tests {
247 enum A { 172 enum A {
248 As, 173 As,
249 Bs{x:i32, y:Option<i32>}, 174 Bs{x:i32, y:Option<i32>},
175 Cs(i32, Option<i32>),
250 } 176 }
251 fn main() { 177 fn main() {
252 match <|>A::As { 178 match <|>A::As {
253 A::Bs{x,y:Some(_)} => (), 179 A::Bs{x,y:Some(_)} => (),
180 A::Cs(_, Some(_)) => (),
254 A::As => (), 181 A::As => (),
255 _ => (),
256 } 182 }
257 } 183 }
258 "#, 184 "#,
@@ -292,39 +218,6 @@ mod tests {
292 } 218 }
293 219
294 #[test] 220 #[test]
295 fn partial_fill_or_pat2() {
296 check_assist(
297 fill_match_arms,
298 r#"
299 enum A {
300 As,
301 Bs,
302 Cs(Option<i32>),
303 }
304 fn main() {
305 match A::As<|> {
306 A::Cs(Some(_)) | A::Bs => (),
307 }
308 }
309 "#,
310 r#"
311 enum A {
312 As,
313 Bs,
314 Cs(Option<i32>),
315 }
316 fn main() {
317 match <|>A::As {
318 A::Cs(Some(_)) | A::Bs => (),
319 A::As => (),
320 _ => (),
321 }
322 }
323 "#,
324 );
325 }
326
327 #[test]
328 fn partial_fill() { 221 fn partial_fill() {
329 check_assist( 222 check_assist(
330 fill_match_arms, 223 fill_match_arms,
@@ -367,7 +260,6 @@ mod tests {
367 A::Es(B::Xs) => (), 260 A::Es(B::Xs) => (),
368 A::As => (), 261 A::As => (),
369 A::Cs => (), 262 A::Cs => (),
370 _ => (),
371 } 263 }
372 } 264 }
373 "#, 265 "#,