Skip to content

Commit cb32011

Browse files
bors[bot]Veykril
andauthored
Merge #9842
9842: fix: Substitute generic types in inline_call r=Veykril a=Veykril bors r+ Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
2 parents a739a57 + 7e6eb67 commit cb32011

File tree

4 files changed

+115
-39
lines changed

4 files changed

+115
-39
lines changed

crates/ide_assists/src/handlers/inline_call.rs

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use ast::make;
22
use hir::{HasSource, PathResolution, TypeInfo};
3-
use ide_db::{defs::Definition, search::FileReference};
3+
use ide_db::{defs::Definition, path_transform::PathTransform, search::FileReference};
44
use itertools::izip;
55
use syntax::{
66
ast::{self, edit::AstNodeEdit, ArgListOwner},
@@ -34,7 +34,7 @@ use crate::{
3434
// }
3535
// ```
3636
pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
37-
let (label, function, arguments, expr) =
37+
let (label, function, arguments, generic_arg_list, expr) =
3838
if let Some(path_expr) = ctx.find_node_at_offset::<ast::PathExpr>() {
3939
let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
4040
let path = path_expr.path()?;
@@ -48,6 +48,7 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
4848
format!("Inline `{}`", path),
4949
function,
5050
call.arg_list()?.args().collect(),
51+
path.segment().and_then(|it| it.generic_arg_list()),
5152
ast::Expr::CallExpr(call),
5253
)
5354
} else {
@@ -57,10 +58,16 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
5758
let function = ctx.sema.resolve_method_call(&call)?;
5859
let mut arguments = vec![receiver];
5960
arguments.extend(call.arg_list()?.args());
60-
(format!("Inline `{}`", name_ref), function, arguments, ast::Expr::MethodCallExpr(call))
61+
(
62+
format!("Inline `{}`", name_ref),
63+
function,
64+
arguments,
65+
call.generic_arg_list(),
66+
ast::Expr::MethodCallExpr(call),
67+
)
6168
};
6269

63-
inline_(acc, ctx, label, function, arguments, expr)
70+
inline_(acc, ctx, label, function, arguments, expr, generic_arg_list)
6471
}
6572

6673
pub(crate) fn inline_(
@@ -70,6 +77,7 @@ pub(crate) fn inline_(
7077
function: hir::Function,
7178
arg_list: Vec<ast::Expr>,
7279
expr: ast::Expr,
80+
generic_arg_list: Option<ast::GenericArgList>,
7381
) -> Option<()> {
7482
let hir::InFile { value: function_source, file_id } = function.source(ctx.db())?;
7583
let param_list = function_source.param_list()?;
@@ -100,14 +108,14 @@ pub(crate) fn inline_(
100108
return None;
101109
}
102110

103-
let body = function_source.body()?;
111+
let fn_body = function_source.body()?;
104112

105113
acc.add(
106114
AssistId("inline_call", AssistKind::RefactorInline),
107115
label,
108116
expr.syntax().text_range(),
109117
|builder| {
110-
let body = body.clone_for_update();
118+
let body = fn_body.clone_for_update();
111119

112120
let file_id = file_id.original_file(ctx.sema.db);
113121
let usages_for_locals = |local| {
@@ -198,6 +206,15 @@ pub(crate) fn inline_(
198206
}
199207
}
200208
}
209+
if let Some(generic_arg_list) = generic_arg_list {
210+
PathTransform::function_call(
211+
&ctx.sema.scope(expr.syntax()),
212+
&ctx.sema.scope(fn_body.syntax()),
213+
function,
214+
generic_arg_list,
215+
)
216+
.apply(body.syntax());
217+
}
201218

202219
let original_indentation = expr.indent_level();
203220
let replacement = body.reset_indent().indent(original_indentation);
@@ -644,6 +661,36 @@ fn main() {
644661
x as u32
645662
};
646663
}
664+
"#,
665+
);
666+
}
667+
668+
// FIXME: const generics aren't being substituted, this is blocked on better support for them
669+
#[test]
670+
fn inline_substitutes_generics() {
671+
check_assist(
672+
inline_call,
673+
r#"
674+
fn foo<T, const N: usize>() {
675+
bar::<T, N>()
676+
}
677+
678+
fn bar<U, const M: usize>() {}
679+
680+
fn main() {
681+
foo::<usize, {0}>$0();
682+
}
683+
"#,
684+
r#"
685+
fn foo<T, const N: usize>() {
686+
bar::<T, N>()
687+
}
688+
689+
fn bar<U, const M: usize>() {}
690+
691+
fn main() {
692+
bar::<usize, N>();
693+
}
647694
"#,
648695
);
649696
}

crates/ide_assists/src/utils.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -129,15 +129,11 @@ pub fn add_trait_assoc_items_to_impl(
129129
) -> (ast::Impl, ast::AssocItem) {
130130
let source_scope = sema.scope_for_def(trait_);
131131

132-
let transform = PathTransform {
133-
subst: (trait_, impl_.clone()),
134-
source_scope: &source_scope,
135-
target_scope: &target_scope,
136-
};
132+
let transform = PathTransform::trait_impl(&target_scope, &source_scope, trait_, impl_.clone());
137133

138134
let items = items.into_iter().map(|assoc_item| {
139135
let assoc_item = assoc_item.clone_for_update();
140-
transform.apply(assoc_item.clone());
136+
transform.apply(assoc_item.syntax());
141137
edit::remove_attrs_and_docs(&assoc_item).clone_subtree().clone_for_update()
142138
});
143139

crates/ide_completion/src/completions/trait_impl.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,14 @@ fn get_transformed_assoc_item(
186186
let trait_ = impl_def.trait_(ctx.db)?;
187187
let source_scope = &ctx.sema.scope_for_def(trait_);
188188
let target_scope = &ctx.sema.scope(impl_def.source(ctx.db)?.syntax().value);
189-
let transform = PathTransform {
190-
subst: (trait_, impl_def.source(ctx.db)?.value),
191-
source_scope,
189+
let transform = PathTransform::trait_impl(
192190
target_scope,
193-
};
191+
source_scope,
192+
trait_,
193+
impl_def.source(ctx.db)?.value,
194+
);
194195

195-
transform.apply(assoc_item.clone());
196+
transform.apply(assoc_item.syntax());
196197
Some(match assoc_item {
197198
ast::AssocItem::Fn(func) => ast::AssocItem::Fn(edit::remove_attrs_and_docs(&func)),
198199
_ => assoc_item,

crates/ide_db/src/path_transform.rs

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use hir::{HirDisplay, SemanticsScope};
55
use rustc_hash::FxHashMap;
66
use syntax::{
77
ast::{self, AstNode},
8-
ted,
8+
ted, SyntaxNode,
99
};
1010

1111
/// `PathTransform` substitutes path in SyntaxNodes in bulk.
@@ -32,38 +32,70 @@ use syntax::{
3232
/// }
3333
/// ```
3434
pub struct PathTransform<'a> {
35-
pub subst: (hir::Trait, ast::Impl),
36-
pub target_scope: &'a SemanticsScope<'a>,
37-
pub source_scope: &'a SemanticsScope<'a>,
35+
generic_def: hir::GenericDef,
36+
substs: Vec<ast::Type>,
37+
target_scope: &'a SemanticsScope<'a>,
38+
source_scope: &'a SemanticsScope<'a>,
3839
}
3940

4041
impl<'a> PathTransform<'a> {
41-
pub fn apply(&self, item: ast::AssocItem) {
42+
pub fn trait_impl(
43+
target_scope: &'a SemanticsScope<'a>,
44+
source_scope: &'a SemanticsScope<'a>,
45+
trait_: hir::Trait,
46+
impl_: ast::Impl,
47+
) -> PathTransform<'a> {
48+
PathTransform {
49+
source_scope,
50+
target_scope,
51+
generic_def: trait_.into(),
52+
substs: get_syntactic_substs(impl_).unwrap_or_default(),
53+
}
54+
}
55+
56+
pub fn function_call(
57+
target_scope: &'a SemanticsScope<'a>,
58+
source_scope: &'a SemanticsScope<'a>,
59+
function: hir::Function,
60+
generic_arg_list: ast::GenericArgList,
61+
) -> PathTransform<'a> {
62+
PathTransform {
63+
source_scope,
64+
target_scope,
65+
generic_def: function.into(),
66+
substs: get_type_args_from_arg_list(generic_arg_list).unwrap_or_default(),
67+
}
68+
}
69+
70+
pub fn apply(&self, syntax: &SyntaxNode) {
4271
if let Some(ctx) = self.build_ctx() {
43-
ctx.apply(item)
72+
ctx.apply(syntax)
4473
}
4574
}
75+
4676
fn build_ctx(&self) -> Option<Ctx<'a>> {
4777
let db = self.source_scope.db;
4878
let target_module = self.target_scope.module()?;
4979
let source_module = self.source_scope.module()?;
50-
51-
let substs = get_syntactic_substs(self.subst.1.clone()).unwrap_or_default();
52-
let generic_def: hir::GenericDef = self.subst.0.into();
53-
let substs_by_param: FxHashMap<_, _> = generic_def
80+
let skip = match self.generic_def {
81+
// this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky
82+
hir::GenericDef::Trait(_) => 1,
83+
_ => 0,
84+
};
85+
let substs_by_param: FxHashMap<_, _> = self
86+
.generic_def
5487
.type_params(db)
5588
.into_iter()
56-
// this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky
57-
.skip(1)
89+
.skip(skip)
5890
// The actual list of trait type parameters may be longer than the one
5991
// used in the `impl` block due to trailing default type parameters.
6092
// For that case we extend the `substs` with an empty iterator so we
6193
// can still hit those trailing values and check if they actually have
6294
// a default type. If they do, go for that type from `hir` to `ast` so
6395
// the resulting change can be applied correctly.
64-
.zip(substs.into_iter().map(Some).chain(std::iter::repeat(None)))
96+
.zip(self.substs.iter().map(Some).chain(std::iter::repeat(None)))
6597
.filter_map(|(k, v)| match v {
66-
Some(v) => Some((k, v)),
98+
Some(v) => Some((k, v.clone())),
6799
None => {
68100
let default = k.default(db)?;
69101
Some((
@@ -73,7 +105,6 @@ impl<'a> PathTransform<'a> {
73105
}
74106
})
75107
.collect();
76-
77108
let res = Ctx { substs: substs_by_param, target_module, source_scope: self.source_scope };
78109
Some(res)
79110
}
@@ -86,8 +117,8 @@ struct Ctx<'a> {
86117
}
87118

88119
impl<'a> Ctx<'a> {
89-
fn apply(&self, item: ast::AssocItem) {
90-
for event in item.syntax().preorder() {
120+
fn apply(&self, item: &SyntaxNode) {
121+
for event in item.preorder() {
91122
let node = match event {
92123
syntax::WalkEvent::Enter(_) => continue,
93124
syntax::WalkEvent::Leave(it) => it,
@@ -149,13 +180,14 @@ fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Vec<ast::Type>> {
149180
};
150181
let generic_arg_list = path_type.path()?.segment()?.generic_arg_list()?;
151182

183+
get_type_args_from_arg_list(generic_arg_list)
184+
}
185+
186+
fn get_type_args_from_arg_list(generic_arg_list: ast::GenericArgList) -> Option<Vec<ast::Type>> {
152187
let mut result = Vec::new();
153188
for generic_arg in generic_arg_list.generic_args() {
154-
match generic_arg {
155-
ast::GenericArg::TypeArg(type_arg) => result.push(type_arg.ty()?),
156-
ast::GenericArg::AssocTypeArg(_)
157-
| ast::GenericArg::LifetimeArg(_)
158-
| ast::GenericArg::ConstArg(_) => (),
189+
if let ast::GenericArg::TypeArg(type_arg) = generic_arg {
190+
result.push(type_arg.ty()?)
159191
}
160192
}
161193

0 commit comments

Comments
 (0)