diff options
author | Comonad <[email protected]> | 2021-04-21 12:33:45 +0100 |
---|---|---|
committer | Comonad <[email protected]> | 2021-04-21 12:33:45 +0100 |
commit | 09147c3303f0ffe607c0decb2979980f9a296a5c (patch) | |
tree | bc0b13766f35ee23cf4bdd8603d24d83ebdc7231 /crates | |
parent | 7ae0bc1bd40286200f5e5b7a4d3b086312055ed5 (diff) |
Add support for fill match arms of boolean values
- Add support for boolean inside tuple
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ide_assists/src/handlers/fill_match_arms.rs | 222 | ||||
-rw-r--r-- | crates/syntax/src/ast/make.rs | 8 |
2 files changed, 207 insertions, 23 deletions
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs index a30c4d04e..be927cc1c 100644 --- a/crates/ide_assists/src/handlers/fill_match_arms.rs +++ b/crates/ide_assists/src/handlers/fill_match_arms.rs | |||
@@ -53,7 +53,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
53 | .iter() | 53 | .iter() |
54 | .filter_map(ast::MatchArm::pat) | 54 | .filter_map(ast::MatchArm::pat) |
55 | .flat_map(|pat| match pat { | 55 | .flat_map(|pat| match pat { |
56 | // Special casee OrPat as separate top-level pats | 56 | // Special case OrPat as separate top-level pats |
57 | Pat::OrPat(or_pat) => Either::Left(or_pat.pats()), | 57 | Pat::OrPat(or_pat) => Either::Left(or_pat.pats()), |
58 | _ => Either::Right(iter::once(pat)), | 58 | _ => Either::Right(iter::once(pat)), |
59 | }) | 59 | }) |
@@ -72,7 +72,11 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
72 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) | 72 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) |
73 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | 73 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) |
74 | .collect::<Vec<_>>(); | 74 | .collect::<Vec<_>>(); |
75 | if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { | 75 | if Some(enum_def) |
76 | == FamousDefs(&ctx.sema, Some(module.krate())) | ||
77 | .core_option_Option() | ||
78 | .map(|x| lift_enum(x)) | ||
79 | { | ||
76 | // Match `Some` variant first. | 80 | // Match `Some` variant first. |
77 | cov_mark::hit!(option_order); | 81 | cov_mark::hit!(option_order); |
78 | variants.reverse() | 82 | variants.reverse() |
@@ -151,49 +155,99 @@ fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool { | |||
151 | } | 155 | } |
152 | } | 156 | } |
153 | 157 | ||
154 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { | 158 | #[derive(Eq, PartialEq, Clone)] |
159 | enum ExtendedEnum { | ||
160 | Bool, | ||
161 | Enum(hir::Enum), | ||
162 | } | ||
163 | |||
164 | #[derive(Eq, PartialEq, Clone)] | ||
165 | enum ExtendedVariant { | ||
166 | True, | ||
167 | False, | ||
168 | Variant(hir::Variant), | ||
169 | } | ||
170 | |||
171 | fn lift_enum(e: hir::Enum) -> ExtendedEnum { | ||
172 | ExtendedEnum::Enum(e) | ||
173 | } | ||
174 | |||
175 | impl ExtendedEnum { | ||
176 | fn variants(&self, db: &RootDatabase) -> Vec<ExtendedVariant> { | ||
177 | match self { | ||
178 | ExtendedEnum::Enum(e) => { | ||
179 | e.variants(db).into_iter().map(|x| ExtendedVariant::Variant(x)).collect::<Vec<_>>() | ||
180 | } | ||
181 | ExtendedEnum::Bool => { | ||
182 | Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False]) | ||
183 | } | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | |||
188 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> { | ||
155 | sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { | 189 | sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { |
156 | Some(Adt::Enum(e)) => Some(e), | 190 | Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)), |
157 | _ => None, | 191 | _ => { |
192 | if ty.is_bool() { | ||
193 | Some(ExtendedEnum::Bool) | ||
194 | } else { | ||
195 | None | ||
196 | } | ||
197 | } | ||
158 | }) | 198 | }) |
159 | } | 199 | } |
160 | 200 | ||
161 | fn resolve_tuple_of_enum_def( | 201 | fn resolve_tuple_of_enum_def( |
162 | sema: &Semantics<RootDatabase>, | 202 | sema: &Semantics<RootDatabase>, |
163 | expr: &ast::Expr, | 203 | expr: &ast::Expr, |
164 | ) -> Option<Vec<hir::Enum>> { | 204 | ) -> Option<Vec<ExtendedEnum>> { |
165 | sema.type_of_expr(&expr)? | 205 | sema.type_of_expr(&expr)? |
166 | .tuple_fields(sema.db) | 206 | .tuple_fields(sema.db) |
167 | .iter() | 207 | .iter() |
168 | .map(|ty| { | 208 | .map(|ty| { |
169 | ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() { | 209 | ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() { |
170 | Some(Adt::Enum(e)) => Some(e), | 210 | Some(Adt::Enum(e)) => Some(lift_enum(e)), |
171 | // For now we only handle expansion for a tuple of enums. Here | 211 | // For now we only handle expansion for a tuple of enums. Here |
172 | // we map non-enum items to None and rely on `collect` to | 212 | // we map non-enum items to None and rely on `collect` to |
173 | // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>. | 213 | // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>. |
174 | _ => None, | 214 | _ => { |
215 | if ty.is_bool() { | ||
216 | Some(ExtendedEnum::Bool) | ||
217 | } else { | ||
218 | None | ||
219 | } | ||
220 | } | ||
175 | }) | 221 | }) |
176 | }) | 222 | }) |
177 | .collect() | 223 | .collect() |
178 | } | 224 | } |
179 | 225 | ||
180 | fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::Variant) -> Option<ast::Pat> { | 226 | fn build_pat(db: &RootDatabase, module: hir::Module, var: ExtendedVariant) -> Option<ast::Pat> { |
181 | let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?); | 227 | match var { |
228 | ExtendedVariant::Variant(var) => { | ||
229 | let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?); | ||
230 | |||
231 | // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though | ||
232 | let pat: ast::Pat = match var.source(db)?.value.kind() { | ||
233 | ast::StructKind::Tuple(field_list) => { | ||
234 | let pats = | ||
235 | iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count()); | ||
236 | make::tuple_struct_pat(path, pats).into() | ||
237 | } | ||
238 | ast::StructKind::Record(field_list) => { | ||
239 | let pats = | ||
240 | field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into()); | ||
241 | make::record_pat(path, pats).into() | ||
242 | } | ||
243 | ast::StructKind::Unit => make::path_pat(path), | ||
244 | }; | ||
182 | 245 | ||
183 | // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though | 246 | Some(pat) |
184 | let pat: ast::Pat = match var.source(db)?.value.kind() { | ||
185 | ast::StructKind::Tuple(field_list) => { | ||
186 | let pats = iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count()); | ||
187 | make::tuple_struct_pat(path, pats).into() | ||
188 | } | ||
189 | ast::StructKind::Record(field_list) => { | ||
190 | let pats = field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into()); | ||
191 | make::record_pat(path, pats).into() | ||
192 | } | 247 | } |
193 | ast::StructKind::Unit => make::path_pat(path), | 248 | ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))), |
194 | }; | 249 | ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))), |
195 | 250 | } | |
196 | Some(pat) | ||
197 | } | 251 | } |
198 | 252 | ||
199 | #[cfg(test)] | 253 | #[cfg(test)] |
@@ -226,6 +280,21 @@ mod tests { | |||
226 | } | 280 | } |
227 | 281 | ||
228 | #[test] | 282 | #[test] |
283 | fn all_boolean_match_arms_provided() { | ||
284 | check_assist_not_applicable( | ||
285 | fill_match_arms, | ||
286 | r#" | ||
287 | fn foo(a: bool) { | ||
288 | match a$0 { | ||
289 | true => {} | ||
290 | false => {} | ||
291 | } | ||
292 | } | ||
293 | "#, | ||
294 | ) | ||
295 | } | ||
296 | |||
297 | #[test] | ||
229 | fn tuple_of_non_enum() { | 298 | fn tuple_of_non_enum() { |
230 | // for now this case is not handled, although it potentially could be | 299 | // for now this case is not handled, although it potentially could be |
231 | // in the future | 300 | // in the future |
@@ -241,6 +310,113 @@ mod tests { | |||
241 | } | 310 | } |
242 | 311 | ||
243 | #[test] | 312 | #[test] |
313 | fn fill_match_arms_boolean() { | ||
314 | check_assist( | ||
315 | fill_match_arms, | ||
316 | r#" | ||
317 | fn foo(a: bool) { | ||
318 | match a$0 { | ||
319 | } | ||
320 | } | ||
321 | "#, | ||
322 | r#" | ||
323 | fn foo(a: bool) { | ||
324 | match a { | ||
325 | $0true => {} | ||
326 | false => {} | ||
327 | } | ||
328 | } | ||
329 | "#, | ||
330 | ) | ||
331 | } | ||
332 | |||
333 | #[test] | ||
334 | fn partial_fill_boolean() { | ||
335 | check_assist( | ||
336 | fill_match_arms, | ||
337 | r#" | ||
338 | fn foo(a: bool) { | ||
339 | match a$0 { | ||
340 | true => {} | ||
341 | } | ||
342 | } | ||
343 | "#, | ||
344 | r#" | ||
345 | fn foo(a: bool) { | ||
346 | match a { | ||
347 | true => {} | ||
348 | $0false => {} | ||
349 | } | ||
350 | } | ||
351 | "#, | ||
352 | ) | ||
353 | } | ||
354 | |||
355 | #[test] | ||
356 | fn all_boolean_tuple_arms_provided() { | ||
357 | check_assist_not_applicable( | ||
358 | fill_match_arms, | ||
359 | r#" | ||
360 | fn foo(a: bool) { | ||
361 | match (a, a)$0 { | ||
362 | (true, true) => {} | ||
363 | (true, false) => {} | ||
364 | (false, true) => {} | ||
365 | (false, false) => {} | ||
366 | } | ||
367 | } | ||
368 | "#, | ||
369 | ) | ||
370 | } | ||
371 | |||
372 | #[test] | ||
373 | fn fill_boolean_tuple() { | ||
374 | check_assist( | ||
375 | fill_match_arms, | ||
376 | r#" | ||
377 | fn foo(a: bool) { | ||
378 | match (a, a)$0 { | ||
379 | } | ||
380 | } | ||
381 | "#, | ||
382 | r#" | ||
383 | fn foo(a: bool) { | ||
384 | match (a, a) { | ||
385 | $0(true, true) => {} | ||
386 | (true, false) => {} | ||
387 | (false, true) => {} | ||
388 | (false, false) => {} | ||
389 | } | ||
390 | } | ||
391 | "#, | ||
392 | ) | ||
393 | } | ||
394 | |||
395 | #[test] | ||
396 | fn partial_fill_boolean_tuple() { | ||
397 | check_assist( | ||
398 | fill_match_arms, | ||
399 | r#" | ||
400 | fn foo(a: bool) { | ||
401 | match (a, a)$0 { | ||
402 | (false, true) => {} | ||
403 | } | ||
404 | } | ||
405 | "#, | ||
406 | r#" | ||
407 | fn foo(a: bool) { | ||
408 | match (a, a) { | ||
409 | (false, true) => {} | ||
410 | $0(true, true) => {} | ||
411 | (true, false) => {} | ||
412 | (false, false) => {} | ||
413 | } | ||
414 | } | ||
415 | "#, | ||
416 | ) | ||
417 | } | ||
418 | |||
419 | #[test] | ||
244 | fn partial_fill_record_tuple() { | 420 | fn partial_fill_record_tuple() { |
245 | check_assist( | 421 | check_assist( |
246 | fill_match_arms, | 422 | fill_match_arms, |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 94d4f2cf0..4cf6f871e 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -294,6 +294,14 @@ pub fn wildcard_pat() -> ast::WildcardPat { | |||
294 | } | 294 | } |
295 | } | 295 | } |
296 | 296 | ||
297 | pub fn literal_pat(lit: &str) -> ast::LiteralPat { | ||
298 | return from_text(lit); | ||
299 | |||
300 | fn from_text(text: &str) -> ast::LiteralPat { | ||
301 | ast_from_text(&format!("fn f() {{ match x {{ {} => {{}} }} }}", text)) | ||
302 | } | ||
303 | } | ||
304 | |||
297 | /// Creates a tuple of patterns from an iterator of patterns. | 305 | /// Creates a tuple of patterns from an iterator of patterns. |
298 | /// | 306 | /// |
299 | /// Invariant: `pats` must be length > 0 | 307 | /// Invariant: `pats` must be length > 0 |