aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/fill_match_arms.rs
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2019-08-23 06:43:32 +0100
committerGitHub <[email protected]>2019-08-23 06:43:32 +0100
commite055cfacdfe3b3451484dae5d6ed08aefba133ca (patch)
tree259613dfe4b76b63966d491f03742813bfa969e9 /crates/ra_assists/src/fill_match_arms.rs
parent0c35d82329bc0952bd179ec37933c0955fd5ed26 (diff)
parente84f93cb5b651696637d87b98653d7e8f9149086 (diff)
Merge #1724
1724: Refactor fill_match_arms assist to use AstBuilder facilities r=matklad a=viorina Co-authored-by: Ekaterina Babshukova <[email protected]>
Diffstat (limited to 'crates/ra_assists/src/fill_match_arms.rs')
-rw-r--r--crates/ra_assists/src/fill_match_arms.rs195
1 files changed, 72 insertions, 123 deletions
diff --git a/crates/ra_assists/src/fill_match_arms.rs b/crates/ra_assists/src/fill_match_arms.rs
index 85ff5c052..ce715a449 100644
--- a/crates/ra_assists/src/fill_match_arms.rs
+++ b/crates/ra_assists/src/fill_match_arms.rs
@@ -1,97 +1,91 @@
1use itertools::Itertools; 1use std::iter;
2use std::fmt::Write;
3 2
4use hir::{db::HirDatabase, AdtDef, FieldSource, HasSource}; 3use hir::{db::HirDatabase, AdtDef, HasSource};
5use ra_syntax::ast::{self, AstNode}; 4use ra_syntax::ast::{self, AstNode, NameOwner};
6 5
7use crate::{Assist, AssistCtx, AssistId}; 6use crate::{ast_editor::AstBuilder, Assist, AssistCtx, AssistId};
8
9fn is_trivial_arm(arm: &ast::MatchArm) -> bool {
10 fn single_pattern(arm: &ast::MatchArm) -> Option<ast::Pat> {
11 let (pat,) = arm.pats().collect_tuple()?;
12 Some(pat)
13 }
14 match single_pattern(arm) {
15 Some(ast::Pat::PlaceholderPat(..)) => true,
16 _ => false,
17 }
18}
19 7
20pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 8pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
21 let match_expr = ctx.node_at_offset::<ast::MatchExpr>()?; 9 let match_expr = ctx.node_at_offset::<ast::MatchExpr>()?;
10 let match_arm_list = match_expr.match_arm_list()?;
22 11
23 // We already have some match arms, so we don't provide any assists. 12 // We already have some match arms, so we don't provide any assists.
24 // Unless if there is only one trivial match arm possibly created 13 // Unless if there is only one trivial match arm possibly created
25 // by match postfix complete. Trivial match arm is the catch all arm. 14 // by match postfix complete. Trivial match arm is the catch all arm.
26 if let Some(arm_list) = match_expr.match_arm_list() { 15 let mut existing_arms = match_arm_list.arms();
27 let mut arm_iter = arm_list.arms(); 16 if let Some(arm) = existing_arms.next() {
28 let first = arm_iter.next(); 17 if !is_trivial(&arm) || existing_arms.next().is_some() {
29 18 return None;
30 match &first {
31 // If there arm list is empty or there is only one trivial arm, then proceed.
32 Some(arm) if is_trivial_arm(arm) => {
33 if arm_iter.next() != None {
34 return None;
35 }
36 }
37 None => {}
38
39 _ => {
40 return None;
41 }
42 } 19 }
43 }; 20 };
44 21
45 let expr = match_expr.expr()?; 22 let expr = match_expr.expr()?;
46 let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, expr.syntax(), None); 23 let enum_def = {
47 let match_expr_ty = analyzer.type_of(ctx.db, &expr)?; 24 let file_id = ctx.frange.file_id;
48 let enum_def = analyzer.autoderef(ctx.db, match_expr_ty).find_map(|ty| match ty.as_adt() { 25 let analyzer = hir::SourceAnalyzer::new(ctx.db, file_id, expr.syntax(), None);
49 Some((AdtDef::Enum(e), _)) => Some(e), 26 resolve_enum_def(ctx.db, &analyzer, &expr)?
50 _ => None, 27 };
51 })?; 28 let variant_list = enum_def.variant_list()?;
52 let enum_name = enum_def.name(ctx.db)?;
53 let db = ctx.db;
54 29
55 ctx.add_action(AssistId("fill_match_arms"), "fill match arms", |edit| { 30 ctx.add_action(AssistId("fill_match_arms"), "fill match arms", |edit| {
56 let mut buf = format!("match {} {{\n", expr.syntax().text().to_string()); 31 let variants = variant_list.variants();
57 let variants = enum_def.variants(db); 32 let arms = variants.into_iter().filter_map(build_pat).map(|pat| {
58 for variant in variants { 33 AstBuilder::<ast::MatchArm>::from_pieces(
59 let name = match variant.name(db) { 34 iter::once(pat),
60 Some(it) => it, 35 &AstBuilder::<ast::Expr>::unit(),
61 None => continue, 36 )
62 }; 37 });
63 write!(&mut buf, " {}::{}", enum_name, name.to_string()).unwrap(); 38 let new_arm_list = AstBuilder::<ast::MatchArmList>::from_arms(arms);
64
65 let pat = variant
66 .fields(db)
67 .into_iter()
68 .map(|field| {
69 let name = field.name(db).to_string();
70 let src = field.source(db);
71 match src.ast {
72 FieldSource::Named(_) => name,
73 FieldSource::Pos(_) => "_".to_string(),
74 }
75 })
76 .collect::<Vec<_>>();
77 39
78 match pat.first().map(|s| s.as_str()) {
79 Some("_") => write!(&mut buf, "({})", pat.join(", ")).unwrap(),
80 Some(_) => write!(&mut buf, "{{{}}}", pat.join(", ")).unwrap(),
81 None => (),
82 };
83
84 buf.push_str(" => (),\n");
85 }
86 buf.push_str("}");
87 edit.target(match_expr.syntax().text_range()); 40 edit.target(match_expr.syntax().text_range());
88 edit.set_cursor(expr.syntax().text_range().start()); 41 edit.set_cursor(expr.syntax().text_range().start());
89 edit.replace_node_and_indent(match_expr.syntax(), buf); 42 edit.replace_node_and_indent(match_arm_list.syntax(), new_arm_list.syntax().text());
90 }); 43 });
91 44
92 ctx.build() 45 ctx.build()
93} 46}
94 47
48fn is_trivial(arm: &ast::MatchArm) -> bool {
49 arm.pats().any(|pat| match pat {
50 ast::Pat::PlaceholderPat(..) => true,
51 _ => false,
52 })
53}
54
55fn resolve_enum_def(
56 db: &impl HirDatabase,
57 analyzer: &hir::SourceAnalyzer,
58 expr: &ast::Expr,
59) -> Option<ast::EnumDef> {
60 let expr_ty = analyzer.type_of(db, &expr)?;
61
62 analyzer.autoderef(db, expr_ty).find_map(|ty| match ty.as_adt() {
63 Some((AdtDef::Enum(e), _)) => Some(e.source(db).ast),
64 _ => None,
65 })
66}
67
68fn build_pat(var: ast::EnumVariant) -> Option<ast::Pat> {
69 let path = &AstBuilder::<ast::Path>::from_pieces(var.parent_enum().name()?, var.name()?);
70
71 let pat: ast::Pat = match var.kind() {
72 ast::StructKind::Tuple(field_list) => {
73 let pats = iter::repeat(AstBuilder::<ast::PlaceholderPat>::placeholder().into())
74 .take(field_list.fields().count());
75 AstBuilder::<ast::TupleStructPat>::from_pieces(path, pats).into()
76 }
77 ast::StructKind::Named(field_list) => {
78 let pats = field_list
79 .fields()
80 .map(|f| AstBuilder::<ast::BindPat>::from_name(&f.name().unwrap()).into());
81 AstBuilder::<ast::StructPat>::from_pieces(path, pats).into()
82 }
83 ast::StructKind::Unit => AstBuilder::<ast::PathPat>::from_path(path).into(),
84 };
85
86 Some(pat)
87}
88
95#[cfg(test)] 89#[cfg(test)]
96mod tests { 90mod tests {
97 use crate::helpers::{check_assist, check_assist_target}; 91 use crate::helpers::{check_assist, check_assist_target};
@@ -108,7 +102,7 @@ mod tests {
108 Bs, 102 Bs,
109 Cs(String), 103 Cs(String),
110 Ds(String, String), 104 Ds(String, String),
111 Es{x: usize, y: usize} 105 Es{ x: usize, y: usize }
112 } 106 }
113 107
114 fn main() { 108 fn main() {
@@ -122,7 +116,7 @@ mod tests {
122 Bs, 116 Bs,
123 Cs(String), 117 Cs(String),
124 Ds(String, String), 118 Ds(String, String),
125 Es{x: usize, y: usize} 119 Es{ x: usize, y: usize }
126 } 120 }
127 121
128 fn main() { 122 fn main() {
@@ -132,7 +126,7 @@ mod tests {
132 A::Bs => (), 126 A::Bs => (),
133 A::Cs(_) => (), 127 A::Cs(_) => (),
134 A::Ds(_, _) => (), 128 A::Ds(_, _) => (),
135 A::Es{x, y} => (), 129 A::Es{ x, y } => (),
136 } 130 }
137 } 131 }
138 "#, 132 "#,
@@ -170,7 +164,7 @@ mod tests {
170 fill_match_arms, 164 fill_match_arms,
171 r#" 165 r#"
172 enum A { 166 enum A {
173 Es{x: usize, y: usize} 167 Es{ x: usize, y: usize }
174 } 168 }
175 169
176 fn foo(a: &mut A) { 170 fn foo(a: &mut A) {
@@ -180,57 +174,12 @@ mod tests {
180 "#, 174 "#,
181 r#" 175 r#"
182 enum A { 176 enum A {
183 Es{x: usize, y: usize} 177 Es{ x: usize, y: usize }
184 } 178 }
185 179
186 fn foo(a: &mut A) { 180 fn foo(a: &mut A) {
187 match <|>a { 181 match <|>a {
188 A::Es{x, y} => (), 182 A::Es{ x, y } => (),
189 }
190 }
191 "#,
192 );
193
194 check_assist(
195 fill_match_arms,
196 r#"
197 enum E { X, Y}
198
199 fn main() {
200 match &E::X<|>
201 }
202 "#,
203 r#"
204 enum E { X, Y}
205
206 fn main() {
207 match <|>&E::X {
208 E::X => (),
209 E::Y => (),
210 }
211 }
212 "#,
213 );
214 }
215
216 #[test]
217 fn fill_match_arms_no_body() {
218 check_assist(
219 fill_match_arms,
220 r#"
221 enum E { X, Y}
222
223 fn main() {
224 match E::X<|>
225 }
226 "#,
227 r#"
228 enum E { X, Y}
229
230 fn main() {
231 match <|>E::X {
232 E::X => (),
233 E::Y => (),
234 } 183 }
235 } 184 }
236 "#, 185 "#,
@@ -242,7 +191,7 @@ mod tests {
242 check_assist_target( 191 check_assist_target(
243 fill_match_arms, 192 fill_match_arms,
244 r#" 193 r#"
245 enum E { X, Y} 194 enum E { X, Y }
246 195
247 fn main() { 196 fn main() {
248 match E::X<|> {} 197 match E::X<|> {}