diff options
Diffstat (limited to 'crates/ra_assists/src/handlers')
-rw-r--r-- | crates/ra_assists/src/handlers/fill_match_arms.rs | 172 |
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 | ||
3 | use std::{collections::LinkedList, iter}; | 3 | use std::iter; |
4 | 4 | ||
5 | use hir::{Adt, HasSource, Semantics}; | 5 | use hir::{Adt, HasSource, Semantics}; |
6 | use ra_ide_db::RootDatabase; | 6 | use ra_ide_db::RootDatabase; |
7 | 7 | ||
8 | use crate::{Assist, AssistCtx, AssistId}; | 8 | use crate::{Assist, AssistCtx, AssistId}; |
9 | use ra_syntax::{ | 9 | use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner}; |
10 | ast::{self, edit::IndentLevel, make, AstNode, NameOwner}, | ||
11 | SyntaxKind, SyntaxNode, | ||
12 | }; | ||
13 | 10 | ||
14 | use ast::{MatchArm, MatchGuard, Pat}; | 11 | use 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 | ||
112 | enum ArmMatch { | 81 | fn 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], | |
118 | fn 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 | ||
157 | fn flatten_pats(pat: Pat) -> Vec<SyntaxNode> { | 93 | fn 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 | ||
167 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { | 100 | fn 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)] |
195 | mod tests { | 128 | mod 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 | "#, |