Skip to content

Commit 14b3a23

Browse files
committed
Support nested generics and where clauses
Instead of only looking for generics and where clauses on the immediate parent, we can traverse all the `ancestors` and collect all the generics and where clauses that may be in scope.
1 parent ea7b8b8 commit 14b3a23

File tree

1 file changed

+185
-40
lines changed

1 file changed

+185
-40
lines changed

crates/ide-assists/src/handlers/extract_function.rs

+185-40
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,8 @@ struct ContainerInfo {
284284
parent_loop: Option<SyntaxNode>,
285285
/// The function's return type, const's type etc.
286286
ret_type: Option<hir::Type>,
287-
generic_param_list: Option<ast::GenericParamList>,
288-
where_clause: Option<ast::WhereClause>,
287+
generic_param_lists: Vec<ast::GenericParamList>,
288+
where_clauses: Vec<ast::WhereClause>,
289289
}
290290

291291
/// Control flow that is exported from extracted function
@@ -715,15 +715,11 @@ impl FunctionBody {
715715
}
716716
};
717717

718-
let mut generic_param_list = None;
719-
let mut where_clause = None;
720-
721718
let (is_const, expr, ty) = loop {
722719
let anc = ancestors.next()?;
723720
break match_ast! {
724721
match anc {
725722
ast::ClosureExpr(closure) => {
726-
generic_param_list = closure.generic_param_list();
727723
(false, closure.body(), infer_expr_opt(closure.body()))
728724
},
729725
ast::BlockExpr(block_expr) => {
@@ -744,8 +740,6 @@ impl FunctionBody {
744740
ret_ty = async_ret;
745741
}
746742
}
747-
generic_param_list = fn_.generic_param_list();
748-
where_clause = fn_.where_clause();
749743
(fn_.const_token().is_some(), fn_.body().map(ast::Expr::BlockExpr), Some(ret_ty))
750744
},
751745
ast::Static(statik) => {
@@ -790,13 +784,18 @@ impl FunctionBody {
790784
container_tail.zip(self.tail_expr()).map_or(false, |(container_tail, body_tail)| {
791785
container_tail.syntax().text_range().contains_range(body_tail.syntax().text_range())
792786
});
787+
788+
let parent = self.parent()?;
789+
let generic_param_list = parent_generic_param_lists(&parent);
790+
let where_clause = parent_where_clauses(&parent);
791+
793792
Some(ContainerInfo {
794793
is_in_tail,
795794
is_const,
796795
parent_loop,
797796
ret_type: ty,
798-
generic_param_list,
799-
where_clause,
797+
generic_param_lists: generic_param_list,
798+
where_clauses: where_clause,
800799
})
801800
}
802801

@@ -954,6 +953,26 @@ impl FunctionBody {
954953
}
955954
}
956955

956+
fn parent_where_clauses(parent: &SyntaxNode) -> Vec<ast::WhereClause> {
957+
let mut where_clause: Vec<ast::WhereClause> = parent
958+
.ancestors()
959+
.filter_map(ast::AnyHasGenericParams::cast)
960+
.filter_map(|it| it.where_clause())
961+
.collect();
962+
where_clause.reverse();
963+
where_clause
964+
}
965+
966+
fn parent_generic_param_lists(parent: &SyntaxNode) -> Vec<ast::GenericParamList> {
967+
let mut generic_param_list: Vec<ast::GenericParamList> = parent
968+
.ancestors()
969+
.filter_map(ast::AnyHasGenericParams::cast)
970+
.filter_map(|it| it.generic_param_list())
971+
.collect();
972+
generic_param_list.reverse();
973+
generic_param_list
974+
}
975+
957976
/// checks if relevant var is used with `&mut` access inside body
958977
fn has_exclusive_usages(ctx: &AssistContext, usages: &LocalUsages, body: &FunctionBody) -> bool {
959978
usages
@@ -1393,14 +1412,14 @@ fn format_function(
13931412

13941413
format_to!(fn_def, "{}", params);
13951414

1396-
if let Some(where_clause) = where_clause {
1397-
format_to!(fn_def, " {}", where_clause);
1398-
}
1399-
14001415
if let Some(ret_ty) = ret_ty {
14011416
format_to!(fn_def, " {}", ret_ty);
14021417
}
14031418

1419+
if let Some(where_clause) = where_clause {
1420+
format_to!(fn_def, " {}", where_clause);
1421+
}
1422+
14041423
format_to!(fn_def, " {}", body);
14051424

14061425
fn_def
@@ -1412,34 +1431,32 @@ fn make_generic_params_and_where_clause(
14121431
) -> (Option<ast::GenericParamList>, Option<ast::WhereClause>) {
14131432
let used_type_params = fun.type_params(ctx);
14141433

1415-
let generic_param_list = fun
1416-
.mods
1417-
.generic_param_list
1418-
.as_ref()
1419-
.map(|parent_params| make_generic_param_list(ctx, parent_params, &used_type_params))
1420-
.flatten();
1421-
1422-
let where_clause =
1423-
fun.mods.where_clause.as_ref().map(|parent_where_clause| {
1424-
make_where_clause(ctx, parent_where_clause, &used_type_params)
1425-
});
1434+
let generic_param_list = make_generic_param_list(ctx, fun, &used_type_params);
1435+
let where_clause = make_where_clause(ctx, fun, &used_type_params);
14261436

14271437
(generic_param_list, where_clause)
14281438
}
14291439

14301440
fn make_generic_param_list(
14311441
ctx: &AssistContext,
1432-
parent_params: &ast::GenericParamList,
1442+
fun: &Function,
14331443
used_type_params: &[TypeParam],
14341444
) -> Option<ast::GenericParamList> {
1435-
let required_generic_params: Vec<ast::GenericParam> = parent_params
1436-
.generic_params()
1437-
.filter(|param| param_is_required(ctx, param, used_type_params))
1438-
.collect();
1439-
if required_generic_params.is_empty() {
1440-
None
1445+
let mut generic_params = fun
1446+
.mods
1447+
.generic_param_lists
1448+
.iter()
1449+
.flat_map(|parent_params| {
1450+
parent_params
1451+
.generic_params()
1452+
.filter(|param| param_is_required(ctx, param, used_type_params))
1453+
})
1454+
.peekable();
1455+
1456+
if generic_params.peek().is_some() {
1457+
Some(make::generic_param_list(generic_params))
14411458
} else {
1442-
Some(make::generic_param_list(required_generic_params))
1459+
None
14431460
}
14441461
}
14451462

@@ -1451,21 +1468,33 @@ fn param_is_required(
14511468
match param {
14521469
ast::GenericParam::ConstParam(_) | ast::GenericParam::LifetimeParam(_) => false,
14531470
ast::GenericParam::TypeParam(type_param) => match &ctx.sema.to_def(type_param) {
1454-
Some(def) => used_type_params.iter().contains(def),
1471+
Some(def) => used_type_params.contains(def),
14551472
_ => false,
14561473
},
14571474
}
14581475
}
14591476

14601477
fn make_where_clause(
14611478
ctx: &AssistContext,
1462-
parent_where_clause: &ast::WhereClause,
1479+
fun: &Function,
14631480
used_type_params: &[TypeParam],
1464-
) -> ast::WhereClause {
1465-
let preds = parent_where_clause
1466-
.predicates()
1467-
.filter(|pred| pred_is_required(ctx, pred, used_type_params));
1468-
make::where_clause(preds)
1481+
) -> Option<ast::WhereClause> {
1482+
let mut predicates = fun
1483+
.mods
1484+
.where_clauses
1485+
.iter()
1486+
.flat_map(|parent_where_clause| {
1487+
parent_where_clause
1488+
.predicates()
1489+
.filter(|pred| pred_is_required(ctx, pred, used_type_params))
1490+
})
1491+
.peekable();
1492+
1493+
if predicates.peek().is_some() {
1494+
Some(make::where_clause(predicates))
1495+
} else {
1496+
None
1497+
}
14691498
}
14701499

14711500
fn pred_is_required(
@@ -5052,6 +5081,122 @@ fn func<T, U>(i: T, u: U) where T: Debug, U: Copy {
50525081
fn $0fun_name<T>(i: T) where T: Debug {
50535082
foo(i);
50545083
}
5084+
"#,
5085+
);
5086+
}
5087+
5088+
#[test]
5089+
fn nested_generics() {
5090+
check_assist(
5091+
extract_function,
5092+
r#"
5093+
struct Struct<T: Into<i32>>(T);
5094+
impl <T: Into<i32> + Copy> Struct<T> {
5095+
fn func<V: Into<i32>>(&self, v: V) -> i32 {
5096+
let t = self.0;
5097+
$0t.into() + v.into()$0
5098+
}
5099+
}
5100+
"#,
5101+
r#"
5102+
struct Struct<T: Into<i32>>(T);
5103+
impl <T: Into<i32> + Copy> Struct<T> {
5104+
fn func<V: Into<i32>>(&self, v: V) -> i32 {
5105+
let t = self.0;
5106+
fun_name(t, v)
5107+
}
5108+
}
5109+
5110+
fn $0fun_name<T: Into<i32> + Copy, V: Into<i32>>(t: T, v: V) -> i32 {
5111+
t.into() + v.into()
5112+
}
5113+
"#,
5114+
);
5115+
}
5116+
5117+
#[test]
5118+
fn filters_unused_nested_generics() {
5119+
check_assist(
5120+
extract_function,
5121+
r#"
5122+
struct Struct<T: Into<i32>, U: Debug>(T, U);
5123+
impl <T: Into<i32> + Copy, U: Debug> Struct<T, U> {
5124+
fn func<V: Into<i32>>(&self, v: V) -> i32 {
5125+
let t = self.0;
5126+
$0t.into() + v.into()$0
5127+
}
5128+
}
5129+
"#,
5130+
r#"
5131+
struct Struct<T: Into<i32>, U: Debug>(T, U);
5132+
impl <T: Into<i32> + Copy, U: Debug> Struct<T, U> {
5133+
fn func<V: Into<i32>>(&self, v: V) -> i32 {
5134+
let t = self.0;
5135+
fun_name(t, v)
5136+
}
5137+
}
5138+
5139+
fn $0fun_name<T: Into<i32> + Copy, V: Into<i32>>(t: T, v: V) -> i32 {
5140+
t.into() + v.into()
5141+
}
5142+
"#,
5143+
);
5144+
}
5145+
5146+
#[test]
5147+
fn nested_where_clauses() {
5148+
check_assist(
5149+
extract_function,
5150+
r#"
5151+
struct Struct<T>(T) where T: Into<i32>;
5152+
impl <T> Struct<T> where T: Into<i32> + Copy {
5153+
fn func<V>(&self, v: V) -> i32 where V: Into<i32> {
5154+
let t = self.0;
5155+
$0t.into() + v.into()$0
5156+
}
5157+
}
5158+
"#,
5159+
r#"
5160+
struct Struct<T>(T) where T: Into<i32>;
5161+
impl <T> Struct<T> where T: Into<i32> + Copy {
5162+
fn func<V>(&self, v: V) -> i32 where V: Into<i32> {
5163+
let t = self.0;
5164+
fun_name(t, v)
5165+
}
5166+
}
5167+
5168+
fn $0fun_name<T, V>(t: T, v: V) -> i32 where T: Into<i32> + Copy, V: Into<i32> {
5169+
t.into() + v.into()
5170+
}
5171+
"#,
5172+
);
5173+
}
5174+
5175+
#[test]
5176+
fn filters_unused_nested_where_clauses() {
5177+
check_assist(
5178+
extract_function,
5179+
r#"
5180+
struct Struct<T, U>(T, U) where T: Into<i32>, U: Debug;
5181+
impl <T, U> Struct<T, U> where T: Into<i32> + Copy, U: Debug {
5182+
fn func<V>(&self, v: V) -> i32 where V: Into<i32> {
5183+
let t = self.0;
5184+
$0t.into() + v.into()$0
5185+
}
5186+
}
5187+
"#,
5188+
r#"
5189+
struct Struct<T, U>(T, U) where T: Into<i32>, U: Debug;
5190+
impl <T, U> Struct<T, U> where T: Into<i32> + Copy, U: Debug {
5191+
fn func<V>(&self, v: V) -> i32 where V: Into<i32> {
5192+
let t = self.0;
5193+
fun_name(t, v)
5194+
}
5195+
}
5196+
5197+
fn $0fun_name<T, V>(t: T, v: V) -> i32 where T: Into<i32> + Copy, V: Into<i32> {
5198+
t.into() + v.into()
5199+
}
50555200
"#,
50565201
);
50575202
}

0 commit comments

Comments
 (0)