Skip to content

Commit 51ac6de

Browse files
committed
Auto merge of rust-lang#16277 - roife:fix-issue16276, r=Veykril
Resolve panic in `generate_delegate_methods` Fixes rust-lang#16276 This PR addresses two issues: 1. When using `PathTransform`, it searches for the node corresponding to the `path` in the `source_scope` during `make::fn_`. Therefore, we need to perform the transform before `make::fn_` (similar to the problem in issue rust-lang#15804). Otherwise, even though the tokens are the same, their offsets (i.e., `span`) differ, resulting in the error "Can't find CONST_ARG@xxx." 2. As mentioned in the first point, `PathTransform` searches for the node corresponding to the `path` in the `source_scope`. Thus, when transforming paths, we should update nodes from right to left (i.e., use **reverse of preorder** (right -> left -> root) instead of **postorder** (left -> right -> root)). Reasons are as follows: In the red-green tree (rowan), we do not store absolute ranges but instead store the length of each node and dynamically calculate offsets (spans). Therefore, when modifying the left-side node (such as nodes are inserted or deleted), it causes all right-side nodes' spans to change. This, in turn, leads to PathTransform being unable to find nodes with the same paths (due to different spans), resulting in errors.
2 parents da6f7e2 + ba952e6 commit 51ac6de

File tree

2 files changed

+41
-25
lines changed

2 files changed

+41
-25
lines changed

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

+28-17
Original file line numberDiff line numberDiff line change
@@ -107,31 +107,48 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
107107
|edit| {
108108
// Create the function
109109
let method_source = match ctx.sema.source(method) {
110-
Some(source) => source.value,
110+
Some(source) => {
111+
let v = source.value.clone_for_update();
112+
let source_scope = ctx.sema.scope(v.syntax());
113+
let target_scope = ctx.sema.scope(strukt.syntax());
114+
if let (Some(s), Some(t)) = (source_scope, target_scope) {
115+
PathTransform::generic_transformation(&t, &s).apply(v.syntax());
116+
}
117+
v
118+
}
111119
None => return,
112120
};
121+
113122
let vis = method_source.visibility();
123+
let is_async = method_source.async_token().is_some();
124+
let is_const = method_source.const_token().is_some();
125+
let is_unsafe = method_source.unsafe_token().is_some();
126+
114127
let fn_name = make::name(&name);
128+
129+
let type_params = method_source.generic_param_list();
130+
let where_clause = method_source.where_clause();
115131
let params =
116132
method_source.param_list().unwrap_or_else(|| make::param_list(None, []));
117-
let type_params = method_source.generic_param_list();
118-
let arg_list = match method_source.param_list() {
119-
Some(list) => convert_param_list_to_arg_list(list),
120-
None => make::arg_list([]),
121-
};
133+
134+
// compute the `body`
135+
let arg_list = method_source
136+
.param_list()
137+
.map(|list| convert_param_list_to_arg_list(list))
138+
.unwrap_or_else(|| make::arg_list([]));
139+
122140
let tail_expr = make::expr_method_call(field, make::name_ref(&name), arg_list);
123-
let ret_type = method_source.ret_type();
124-
let is_async = method_source.async_token().is_some();
125-
let is_const = method_source.const_token().is_some();
126-
let is_unsafe = method_source.unsafe_token().is_some();
127141
let tail_expr_finished =
128142
if is_async { make::expr_await(tail_expr) } else { tail_expr };
129143
let body = make::block_expr([], Some(tail_expr_finished));
144+
145+
let ret_type = method_source.ret_type();
146+
130147
let f = make::fn_(
131148
vis,
132149
fn_name,
133150
type_params,
134-
method_source.where_clause(),
151+
where_clause,
135152
params,
136153
body,
137154
ret_type,
@@ -184,12 +201,6 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
184201
let assoc_items = impl_def.get_or_create_assoc_item_list();
185202
assoc_items.add_item(f.clone().into());
186203

187-
if let Some((target, source)) =
188-
ctx.sema.scope(strukt.syntax()).zip(ctx.sema.scope(method_source.syntax()))
189-
{
190-
PathTransform::generic_transformation(&target, &source).apply(f.syntax());
191-
}
192-
193204
if let Some(cap) = ctx.config.snippet_cap {
194205
edit.add_tabstop_before(cap, f)
195206
}

crates/ide-db/src/path_transform.rs

+13-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use crate::helpers::mod_path_to_ast;
44
use either::Either;
55
use hir::{AsAssocItem, HirDisplay, ModuleDef, SemanticsScope};
6+
use itertools::Itertools;
67
use rustc_hash::FxHashMap;
78
use syntax::{
89
ast::{self, make, AstNode},
@@ -227,24 +228,28 @@ struct Ctx<'a> {
227228
same_self_type: bool,
228229
}
229230

230-
fn postorder(item: &SyntaxNode) -> impl Iterator<Item = SyntaxNode> {
231-
item.preorder().filter_map(|event| match event {
232-
syntax::WalkEvent::Enter(_) => None,
233-
syntax::WalkEvent::Leave(node) => Some(node),
234-
})
231+
fn preorder_rev(item: &SyntaxNode) -> impl Iterator<Item = SyntaxNode> {
232+
let x = item
233+
.preorder()
234+
.filter_map(|event| match event {
235+
syntax::WalkEvent::Enter(node) => Some(node),
236+
syntax::WalkEvent::Leave(_) => None,
237+
})
238+
.collect_vec();
239+
x.into_iter().rev()
235240
}
236241

237242
impl Ctx<'_> {
238243
fn apply(&self, item: &SyntaxNode) {
239244
// `transform_path` may update a node's parent and that would break the
240245
// tree traversal. Thus all paths in the tree are collected into a vec
241246
// so that such operation is safe.
242-
let paths = postorder(item).filter_map(ast::Path::cast).collect::<Vec<_>>();
247+
let paths = preorder_rev(item).filter_map(ast::Path::cast).collect::<Vec<_>>();
243248
for path in paths {
244249
self.transform_path(path);
245250
}
246251

247-
postorder(item).filter_map(ast::Lifetime::cast).for_each(|lifetime| {
252+
preorder_rev(item).filter_map(ast::Lifetime::cast).for_each(|lifetime| {
248253
if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string()) {
249254
ted::replace(lifetime.syntax(), subst.clone_subtree().clone_for_update().syntax());
250255
}
@@ -263,7 +268,7 @@ impl Ctx<'_> {
263268
// `transform_path` may update a node's parent and that would break the
264269
// tree traversal. Thus all paths in the tree are collected into a vec
265270
// so that such operation is safe.
266-
let paths = postorder(value).filter_map(ast::Path::cast).collect::<Vec<_>>();
271+
let paths = preorder_rev(value).filter_map(ast::Path::cast).collect::<Vec<_>>();
267272
for path in paths {
268273
self.transform_path(path);
269274
}

0 commit comments

Comments
 (0)