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 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
let (param_tys, ret_ty) = match res {
Some(res) => {
let adjustments = auto_deref_adjust_steps(&derefs);
// FIXME: Handle call adjustments for Fn/FnMut
self.write_expr_adj(*callee, adjustments);
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
if let Some(ty) = &ascribed_ty {
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 {
cov_mark::hit!(add_explicit_type_not_applicable_if_ty_already_specified);
return None;

View File

@ -108,76 +108,80 @@ fn find_lifetime(text: &str) -> impl Fn(&&ast::GenericParam) -> bool + '_ {
}
let mut generics = Vec::new();
walk_ty(ty, &mut |ty| match ty {
ast::Type::PathType(ty) => {
if let Some(path) = ty.path() {
if let Some(name_ref) = path.as_single_name_ref() {
if let Some(param) = known_generics.iter().find(|gp| {
match gp {
ast::GenericParam::ConstParam(cp) => cp.name(),
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()))
walk_ty(ty, &mut |ty| {
match ty {
ast::Type::PathType(ty) => {
if let Some(path) = ty.path() {
if let Some(name_ref) = path.as_single_name_ref() {
if let Some(param) = known_generics.iter().find(|gp| {
match gp {
ast::GenericParam::ConstParam(cp) => cp.name(),
ast::GenericParam::TypeParam(tp) => tp.name(),
_ => None,
}
_ => None,
}),
);
}
}
ast::Type::ImplTraitType(impl_ty) => {
if let Some(it) = impl_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::DynTraitType(dyn_ty) => {
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::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
.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,
}),
);
}
}
ast::Type::ImplTraitType(impl_ty) => {
if let Some(it) = impl_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::DynTraitType(dyn_ty) => {
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::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
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.
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();
while let Some(event) = preorder.next() {
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) {
Some(ty @ ast::Type::MacroType(_)) => {
preorder.skip_subtree();
cb(ty)
cb(ty);
}
Some(ty) => {
cb(ty);
if cb(ty) {
preorder.skip_subtree();
}
}
// skip const args
None if ast::ConstArg::can_cast(kind) => {

View File

@ -59,9 +59,14 @@ pub(super) fn hints(
r.amp_token(),
lifetime,
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
@ -146,8 +151,13 @@ pub(super) fn hints(
is_trivial = false;
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 @@ fn foo(&self, a: &()) -> &() {}
// ^^^<'0, '1>
// ^'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(&()) -> &()) {}
"#,
);
}