Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 3bbdb0d

Browse files
committed
Auto merge of rust-lang#12284 - Veykril:inlay-hints, r=Veykril
internal: Cleanup lifetime elision hints
2 parents ce9b174 + 12d5343 commit 3bbdb0d

File tree

1 file changed

+103
-122
lines changed

1 file changed

+103
-122
lines changed

crates/ide/src/inlay_hints.rs

Lines changed: 103 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ pub enum ReborrowHints {
4747
pub enum InlayKind {
4848
BindingModeHint,
4949
ChainingHint,
50+
ClosingBraceHint,
5051
ClosureReturnTypeHint,
5152
GenericParamListHint,
5253
ImplicitReborrowHint,
5354
LifetimeHint,
5455
ParameterHint,
5556
TypeHint,
56-
ClosingBraceHint,
5757
}
5858

5959
#[derive(Debug)]
@@ -127,28 +127,33 @@ fn hints(
127127
};
128128

129129
closing_brace_hints(hints, sema, config, node.clone());
130-
131-
if let Some(expr) = ast::Expr::cast(node.clone()) {
132-
chaining_hints(hints, sema, &famous_defs, config, &expr);
133-
match expr {
134-
ast::Expr::CallExpr(it) => param_name_hints(hints, sema, config, ast::Expr::from(it)),
135-
ast::Expr::MethodCallExpr(it) => {
136-
param_name_hints(hints, sema, config, ast::Expr::from(it))
137-
}
138-
ast::Expr::ClosureExpr(it) => closure_ret_hints(hints, sema, &famous_defs, config, it),
139-
// We could show reborrows for all expressions, but usually that is just noise to the user
140-
// and the main point here is to show why "moving" a mutable reference doesn't necessarily move it
141-
ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr),
142-
_ => None,
143-
};
144-
} else if let Some(it) = ast::Pat::cast(node.clone()) {
145-
binding_mode_hints(hints, sema, config, &it);
146-
if let ast::Pat::IdentPat(it) = it {
147-
bind_pat_hints(hints, sema, config, &it);
130+
match_ast! {
131+
match node {
132+
ast::Expr(expr) => {
133+
chaining_hints(hints, sema, &famous_defs, config, &expr);
134+
match expr {
135+
ast::Expr::CallExpr(it) => param_name_hints(hints, sema, config, ast::Expr::from(it)),
136+
ast::Expr::MethodCallExpr(it) => {
137+
param_name_hints(hints, sema, config, ast::Expr::from(it))
138+
}
139+
ast::Expr::ClosureExpr(it) => closure_ret_hints(hints, sema, &famous_defs, config, it),
140+
// We could show reborrows for all expressions, but usually that is just noise to the user
141+
// and the main point here is to show why "moving" a mutable reference doesn't necessarily move it
142+
ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr),
143+
_ => None,
144+
}
145+
},
146+
ast::Pat(it) => {
147+
binding_mode_hints(hints, sema, config, &it);
148+
if let ast::Pat::IdentPat(it) = it {
149+
bind_pat_hints(hints, sema, config, &it);
150+
}
151+
Some(())
152+
},
153+
ast::Fn(it) => lifetime_fn_hints(hints, config, it),
154+
_ => Some(()),
148155
}
149-
} else if let Some(it) = ast::Fn::cast(node) {
150-
lifetime_hints(hints, config, it);
151-
}
156+
};
152157
}
153158

154159
fn closing_brace_hints(
@@ -249,7 +254,7 @@ fn closing_brace_hints(
249254
None
250255
}
251256

252-
fn lifetime_hints(
257+
fn lifetime_fn_hints(
253258
acc: &mut Vec<InlayHint>,
254259
config: &InlayHintsConfig,
255260
func: ast::Fn,
@@ -262,80 +267,75 @@ fn lifetime_hints(
262267
let ret_type = func.ret_type();
263268
let self_param = param_list.self_param().filter(|it| it.amp_token().is_some());
264269

265-
let mut used_names: FxHashMap<SmolStr, usize> = generic_param_list
266-
.iter()
267-
.filter(|_| config.param_names_for_lifetime_elision_hints)
268-
.flat_map(|gpl| gpl.lifetime_params())
269-
.filter_map(|param| param.lifetime())
270-
.filter_map(|lt| Some((SmolStr::from(lt.text().as_str().get(1..)?), 0)))
271-
.collect();
272-
273-
let mut allocated_lifetimes = vec![];
274-
let mut gen_idx_name = {
275-
let mut gen = (0u8..).map(|idx| match idx {
276-
idx if idx < 10 => SmolStr::from_iter(['\'', (idx + 48) as char]),
277-
idx => format!("'{idx}").into(),
278-
});
279-
move || gen.next().unwrap_or_default()
270+
let is_elided = |lt: &Option<ast::Lifetime>| match lt {
271+
Some(lt) => matches!(lt.text().as_str(), "'_"),
272+
None => true,
280273
};
281274

282-
let mut potential_lt_refs: Vec<_> = vec![];
283-
param_list
284-
.params()
285-
.filter_map(|it| {
286-
Some((
287-
config.param_names_for_lifetime_elision_hints.then(|| it.pat()).flatten(),
288-
it.ty()?,
289-
))
290-
})
291-
.for_each(|(pat, ty)| {
275+
let potential_lt_refs = {
276+
let mut acc: Vec<_> = vec![];
277+
if let Some(self_param) = &self_param {
278+
let lifetime = self_param.lifetime();
279+
let is_elided = is_elided(&lifetime);
280+
acc.push((None, self_param.amp_token(), lifetime, is_elided));
281+
}
282+
param_list.params().filter_map(|it| Some((it.pat(), it.ty()?))).for_each(|(pat, ty)| {
292283
// FIXME: check path types
293284
walk_ty(&ty, &mut |ty| match ty {
294-
ast::Type::RefType(r) => potential_lt_refs.push((
295-
pat.as_ref().and_then(|it| match it {
296-
ast::Pat::IdentPat(p) => p.name(),
297-
_ => None,
298-
}),
299-
r,
300-
)),
285+
ast::Type::RefType(r) => {
286+
let lifetime = r.lifetime();
287+
let is_elided = is_elided(&lifetime);
288+
acc.push((
289+
pat.as_ref().and_then(|it| match it {
290+
ast::Pat::IdentPat(p) => p.name(),
291+
_ => None,
292+
}),
293+
r.amp_token(),
294+
lifetime,
295+
is_elided,
296+
))
297+
}
301298
_ => (),
302299
})
303300
});
304-
305-
enum LifetimeKind {
306-
Elided,
307-
Named(SmolStr),
308-
Static,
309-
}
310-
311-
let fetch_lt_text = |lt: Option<ast::Lifetime>| match lt {
312-
Some(lt) => match lt.text().as_str() {
313-
"'_" => LifetimeKind::Elided,
314-
"'static" => LifetimeKind::Static,
315-
name => LifetimeKind::Named(name.into()),
316-
},
317-
None => LifetimeKind::Elided,
318-
};
319-
let is_elided = |lt: Option<ast::Lifetime>| match lt {
320-
Some(lt) => matches!(lt.text().as_str(), "'_"),
321-
None => true,
301+
acc
322302
};
323303

324304
// allocate names
325-
if let Some(self_param) = &self_param {
326-
if is_elided(self_param.lifetime()) {
327-
allocated_lifetimes.push(if config.param_names_for_lifetime_elision_hints {
328-
// self can't be used as a lifetime, so no need to check for collisions
329-
"'self".into()
330-
} else {
331-
gen_idx_name()
332-
});
305+
let mut gen_idx_name = {
306+
let mut gen = (0u8..).map(|idx| match idx {
307+
idx if idx < 10 => SmolStr::from_iter(['\'', (idx + 48) as char]),
308+
idx => format!("'{idx}").into(),
309+
});
310+
move || gen.next().unwrap_or_default()
311+
};
312+
let mut allocated_lifetimes = vec![];
313+
314+
let mut used_names: FxHashMap<SmolStr, usize> =
315+
match config.param_names_for_lifetime_elision_hints {
316+
true => generic_param_list
317+
.iter()
318+
.flat_map(|gpl| gpl.lifetime_params())
319+
.filter_map(|param| param.lifetime())
320+
.filter_map(|lt| Some((SmolStr::from(lt.text().as_str().get(1..)?), 0)))
321+
.collect(),
322+
false => Default::default(),
323+
};
324+
{
325+
let mut potential_lt_refs = potential_lt_refs.iter().filter(|&&(.., is_elided)| is_elided);
326+
if let Some(_) = &self_param {
327+
if let Some(_) = potential_lt_refs.next() {
328+
allocated_lifetimes.push(if config.param_names_for_lifetime_elision_hints {
329+
// self can't be used as a lifetime, so no need to check for collisions
330+
"'self".into()
331+
} else {
332+
gen_idx_name()
333+
});
334+
}
333335
}
334-
}
335-
potential_lt_refs.iter().for_each(|(name, it)| {
336-
if is_elided(it.lifetime()) {
336+
potential_lt_refs.for_each(|(name, ..)| {
337337
let name = match name {
338-
Some(it) => {
338+
Some(it) if config.param_names_for_lifetime_elision_hints => {
339339
if let Some(c) = used_names.get_mut(it.text().as_str()) {
340340
*c += 1;
341341
SmolStr::from(format!("'{text}{c}", text = it.text().as_str()))
@@ -347,26 +347,22 @@ fn lifetime_hints(
347347
_ => gen_idx_name(),
348348
};
349349
allocated_lifetimes.push(name);
350-
}
351-
});
350+
});
351+
}
352352

353353
// fetch output lifetime if elision rule applies
354-
355-
let output = if let Some(self_param) = &self_param {
356-
match fetch_lt_text(self_param.lifetime()) {
357-
LifetimeKind::Elided => allocated_lifetimes.get(0).cloned(),
358-
LifetimeKind::Named(name) => Some(name),
359-
LifetimeKind::Static => None,
360-
}
361-
} else {
362-
match potential_lt_refs.as_slice() {
363-
[(_, r)] => match fetch_lt_text(r.lifetime()) {
364-
LifetimeKind::Elided => allocated_lifetimes.get(0).cloned(),
365-
LifetimeKind::Named(name) => Some(name),
366-
LifetimeKind::Static => None,
367-
},
368-
[..] => None,
354+
let output = match potential_lt_refs.as_slice() {
355+
[(_, _, lifetime, _), ..] if self_param.is_some() || potential_lt_refs.len() == 1 => {
356+
match lifetime {
357+
Some(lt) => match lt.text().as_str() {
358+
"'_" => allocated_lifetimes.get(0).cloned(),
359+
"'static" => None,
360+
name => Some(name.into()),
361+
},
362+
None => allocated_lifetimes.get(0).cloned(),
363+
}
369364
}
365+
[..] => None,
370366
};
371367

372368
if allocated_lifetimes.is_empty() && output.is_none() {
@@ -398,27 +394,12 @@ fn lifetime_hints(
398394
return None;
399395
}
400396

401-
let mut idx = match &self_param {
402-
Some(self_param) if is_elided(self_param.lifetime()) => {
403-
if let Some(amp) = self_param.amp_token() {
404-
let lt = allocated_lifetimes[0].clone();
405-
acc.push(InlayHint {
406-
range: amp.text_range(),
407-
kind: InlayKind::LifetimeHint,
408-
label: lt,
409-
});
410-
}
411-
1
412-
}
413-
_ => 0,
414-
};
415-
416-
for (_, p) in potential_lt_refs.iter() {
417-
if is_elided(p.lifetime()) {
418-
let t = p.amp_token()?;
419-
let lt = allocated_lifetimes[idx].clone();
397+
let mut a = allocated_lifetimes.iter();
398+
for (_, amp_token, _, is_elided) in potential_lt_refs {
399+
if is_elided {
400+
let t = amp_token?;
401+
let lt = a.next()?.clone();
420402
acc.push(InlayHint { range: t.text_range(), kind: InlayKind::LifetimeHint, label: lt });
421-
idx += 1;
422403
}
423404
}
424405

0 commit comments

Comments
 (0)