Skip lifetime elision on fn pointers and fn trait types

This commit is contained in:
Lukas Wirth 2023-01-03 11:58:31 +01:00
parent 506895fa2f
commit b996a54cd8
5 changed files with 105 additions and 70 deletions

View file

@ -334,6 +334,7 @@ impl<'a> InferenceContext<'a> {
let (param_tys, ret_ty) = match res { let (param_tys, ret_ty) = match res {
Some(res) => { Some(res) => {
let adjustments = auto_deref_adjust_steps(&derefs); let adjustments = auto_deref_adjust_steps(&derefs);
// FIXME: Handle call adjustments for Fn/FnMut
self.write_expr_adj(*callee, adjustments); self.write_expr_adj(*callee, adjustments);
res res
} }

View file

@ -47,7 +47,10 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
// Don't enable the assist if there is a type ascription without any placeholders // Don't enable the assist if there is a type ascription without any placeholders
if let Some(ty) = &ascribed_ty { if let Some(ty) = &ascribed_ty {
let mut contains_infer_ty = false; let mut contains_infer_ty = false;
walk_ty(ty, &mut |ty| contains_infer_ty |= matches!(ty, ast::Type::InferType(_))); walk_ty(ty, &mut |ty| {
contains_infer_ty |= matches!(ty, ast::Type::InferType(_));
false
});
if !contains_infer_ty { if !contains_infer_ty {
cov_mark::hit!(add_explicit_type_not_applicable_if_ty_already_specified); cov_mark::hit!(add_explicit_type_not_applicable_if_ty_already_specified);
return None; return None;

View file

@ -108,76 +108,80 @@ fn collect_used_generics<'gp>(
} }
let mut generics = Vec::new(); let mut generics = Vec::new();
walk_ty(ty, &mut |ty| match ty { walk_ty(ty, &mut |ty| {
ast::Type::PathType(ty) => { match ty {
if let Some(path) = ty.path() { ast::Type::PathType(ty) => {
if let Some(name_ref) = path.as_single_name_ref() { if let Some(path) = ty.path() {
if let Some(param) = known_generics.iter().find(|gp| { if let Some(name_ref) = path.as_single_name_ref() {
match gp { if let Some(param) = known_generics.iter().find(|gp| {
ast::GenericParam::ConstParam(cp) => cp.name(), match gp {
ast::GenericParam::TypeParam(tp) => tp.name(), ast::GenericParam::ConstParam(cp) => cp.name(),
_ => None, ast::GenericParam::TypeParam(tp) => tp.name(),
} _ => None,
.map_or(false, |n| n.text() == name_ref.text())
}) {
generics.push(param);
}
}
generics.extend(
path.segments()
.filter_map(|seg| seg.generic_arg_list())
.flat_map(|it| it.generic_args())
.filter_map(|it| match it {
ast::GenericArg::LifetimeArg(lt) => {
let lt = lt.lifetime()?;
known_generics.iter().find(find_lifetime(&lt.text()))
} }
_ => None, .map_or(false, |n| n.text() == name_ref.text())
}), }) {
); generics.push(param);
} }
} }
ast::Type::ImplTraitType(impl_ty) => { generics.extend(
if let Some(it) = impl_ty.type_bound_list() { path.segments()
generics.extend( .filter_map(|seg| seg.generic_arg_list())
it.bounds() .flat_map(|it| it.generic_args())
.filter_map(|it| it.lifetime()) .filter_map(|it| match it {
.filter_map(|lt| known_generics.iter().find(find_lifetime(&lt.text()))), ast::GenericArg::LifetimeArg(lt) => {
); let lt = lt.lifetime()?;
} known_generics.iter().find(find_lifetime(&lt.text()))
} }
ast::Type::DynTraitType(dyn_ty) => { _ => None,
if let Some(it) = dyn_ty.type_bound_list() { }),
generics.extend( );
it.bounds() }
.filter_map(|it| it.lifetime()) }
.filter_map(|lt| known_generics.iter().find(find_lifetime(&lt.text()))), ast::Type::ImplTraitType(impl_ty) => {
); if let Some(it) = impl_ty.type_bound_list() {
} generics.extend(
} it.bounds()
ast::Type::RefType(ref_) => generics.extend( .filter_map(|it| it.lifetime())
ref_.lifetime().and_then(|lt| known_generics.iter().find(find_lifetime(&lt.text()))), .filter_map(|lt| known_generics.iter().find(find_lifetime(&lt.text()))),
), );
ast::Type::ArrayType(ar) => { }
if let Some(expr) = ar.expr() { }
if let ast::Expr::PathExpr(p) = expr { ast::Type::DynTraitType(dyn_ty) => {
if let Some(path) = p.path() { if let Some(it) = dyn_ty.type_bound_list() {
if let Some(name_ref) = path.as_single_name_ref() { generics.extend(
if let Some(param) = known_generics.iter().find(|gp| { it.bounds()
if let ast::GenericParam::ConstParam(cp) = gp { .filter_map(|it| it.lifetime())
cp.name().map_or(false, |n| n.text() == name_ref.text()) .filter_map(|lt| known_generics.iter().find(find_lifetime(&lt.text()))),
} else { );
false }
}
ast::Type::RefType(ref_) => generics.extend(
ref_.lifetime()
.and_then(|lt| known_generics.iter().find(find_lifetime(&lt.text()))),
),
ast::Type::ArrayType(ar) => {
if let Some(expr) = ar.expr() {
if let ast::Expr::PathExpr(p) = expr {
if let Some(path) = p.path() {
if let Some(name_ref) = path.as_single_name_ref() {
if let Some(param) = known_generics.iter().find(|gp| {
if let ast::GenericParam::ConstParam(cp) = gp {
cp.name().map_or(false, |n| n.text() == name_ref.text())
} else {
false
}
}) {
generics.push(param);
} }
}) {
generics.push(param);
} }
} }
} }
} }
} }
} _ => (),
_ => (), };
false
}); });
// stable resort to lifetime, type, const // stable resort to lifetime, type, const
generics.sort_by_key(|gp| match gp { generics.sort_by_key(|gp| match gp {

View file

@ -173,7 +173,8 @@ pub fn walk_pat(pat: &ast::Pat, cb: &mut dyn FnMut(ast::Pat)) {
} }
/// Preorder walk all the type's sub types. /// Preorder walk all the type's sub types.
pub fn walk_ty(ty: &ast::Type, cb: &mut dyn FnMut(ast::Type)) { // FIXME: Make the control flow more proper
pub fn walk_ty(ty: &ast::Type, cb: &mut dyn FnMut(ast::Type) -> bool) {
let mut preorder = ty.syntax().preorder(); let mut preorder = ty.syntax().preorder();
while let Some(event) = preorder.next() { while let Some(event) = preorder.next() {
let node = match event { let node = match event {
@ -184,10 +185,12 @@ pub fn walk_ty(ty: &ast::Type, cb: &mut dyn FnMut(ast::Type)) {
match ast::Type::cast(node) { match ast::Type::cast(node) {
Some(ty @ ast::Type::MacroType(_)) => { Some(ty @ ast::Type::MacroType(_)) => {
preorder.skip_subtree(); preorder.skip_subtree();
cb(ty) cb(ty);
} }
Some(ty) => { Some(ty) => {
cb(ty); if cb(ty) {
preorder.skip_subtree();
}
} }
// skip const args // skip const args
None if ast::ConstArg::can_cast(kind) => { None if ast::ConstArg::can_cast(kind) => {

View file

@ -59,9 +59,14 @@ pub(super) fn hints(
r.amp_token(), r.amp_token(),
lifetime, lifetime,
is_elided, is_elided,
)) ));
false
} }
_ => (), ast::Type::FnPtrType(_) => true,
ast::Type::PathType(t) => {
t.path().and_then(|it| it.segment()).and_then(|it| it.param_list()).is_some()
}
_ => false,
}) })
}); });
acc acc
@ -146,8 +151,13 @@ pub(super) fn hints(
is_trivial = false; is_trivial = false;
acc.push(mk_lt_hint(amp, output_lt.to_string())); acc.push(mk_lt_hint(amp, output_lt.to_string()));
} }
false
} }
_ => (), ast::Type::FnPtrType(_) => true,
ast::Type::PathType(t) => {
t.path().and_then(|it| it.segment()).and_then(|it| it.param_list()).is_some()
}
_ => false,
}) })
} }
} }
@ -295,6 +305,20 @@ impl () {
// ^^^<'0, '1> // ^^^<'0, '1>
// ^'0 ^'1 ^'0 // ^'0 ^'1 ^'0
} }
"#,
);
}
#[test]
fn hints_lifetimes_skip_fn_likes() {
check_with_config(
InlayHintsConfig {
lifetime_elision_hints: LifetimeElisionHints::Always,
..TEST_CONFIG
},
r#"
fn fn_ptr(a: fn(&()) -> &()) {}
fn fn_trait<>(a: impl Fn(&()) -> &()) {}
"#, "#,
); );
} }