mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-16 22:16:53 +00:00
Auto merge of #5978 - montrivo:needless-lifetime, r=ebroto
needless-lifetime - nested elision sites Closes #2944 changelog: fix needless-lifetime nested elision site FPs
This commit is contained in:
commit
d4313737d8
@ -1,21 +1,22 @@
|
||||
use crate::utils::paths;
|
||||
use crate::utils::{get_trait_def_id, in_macro, span_lint, trait_ref_of_method};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::intravisit::{
|
||||
walk_fn_decl, walk_generic_param, walk_generics, walk_param_bound, walk_ty, NestedVisitorMap, Visitor,
|
||||
walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty,
|
||||
NestedVisitorMap, Visitor,
|
||||
};
|
||||
use rustc_hir::FnRetTy::Return;
|
||||
use rustc_hir::{
|
||||
BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item,
|
||||
ItemKind, Lifetime, LifetimeName, ParamName, QPath, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty,
|
||||
TyKind, WhereClause, WherePredicate,
|
||||
BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem,
|
||||
ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, TraitFn,
|
||||
TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::{kw, Symbol};
|
||||
|
||||
use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method};
|
||||
use std::iter::FromIterator;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for lifetime annotations which can be removed by
|
||||
@ -25,8 +26,11 @@ declare_clippy_lint! {
|
||||
/// complicated, while there is nothing out of the ordinary going on. Removing
|
||||
/// them leads to more readable code.
|
||||
///
|
||||
/// **Known problems:** Potential false negatives: we bail out if the function
|
||||
/// has a `where` clause where lifetimes are mentioned.
|
||||
/// **Known problems:**
|
||||
/// - We bail out if the function has a `where` clause where lifetimes
|
||||
/// are mentioned due to potenial false positives.
|
||||
/// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the
|
||||
/// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
@ -108,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
|
||||
}
|
||||
|
||||
/// The lifetime of a &-reference.
|
||||
#[derive(PartialEq, Eq, Hash, Debug)]
|
||||
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
|
||||
enum RefLt {
|
||||
Unnamed,
|
||||
Static,
|
||||
@ -127,7 +131,6 @@ fn check_fn_inner<'tcx>(
|
||||
return;
|
||||
}
|
||||
|
||||
let mut bounds_lts = Vec::new();
|
||||
let types = generics
|
||||
.params
|
||||
.iter()
|
||||
@ -156,13 +159,12 @@ fn check_fn_inner<'tcx>(
|
||||
if bound.name != LifetimeName::Static && !bound.is_elided() {
|
||||
return;
|
||||
}
|
||||
bounds_lts.push(bound);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if could_use_elision(cx, decl, body, &generics.params, bounds_lts) {
|
||||
if could_use_elision(cx, decl, body, &generics.params) {
|
||||
span_lint(
|
||||
cx,
|
||||
NEEDLESS_LIFETIMES,
|
||||
@ -181,7 +183,6 @@ fn could_use_elision<'tcx>(
|
||||
func: &'tcx FnDecl<'_>,
|
||||
body: Option<BodyId>,
|
||||
named_generics: &'tcx [GenericParam<'_>],
|
||||
bounds_lts: Vec<&'tcx Lifetime>,
|
||||
) -> bool {
|
||||
// There are two scenarios where elision works:
|
||||
// * no output references, all input references have different LT
|
||||
@ -204,15 +205,31 @@ fn could_use_elision<'tcx>(
|
||||
if let Return(ref ty) = func.output {
|
||||
output_visitor.visit_ty(ty);
|
||||
}
|
||||
for lt in named_generics {
|
||||
input_visitor.visit_generic_param(lt)
|
||||
}
|
||||
|
||||
let input_lts = match input_visitor.into_vec() {
|
||||
Some(lts) => lts_from_bounds(lts, bounds_lts.into_iter()),
|
||||
None => return false,
|
||||
};
|
||||
let output_lts = match output_visitor.into_vec() {
|
||||
Some(val) => val,
|
||||
None => return false,
|
||||
};
|
||||
if input_visitor.abort() || output_visitor.abort() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if allowed_lts
|
||||
.intersection(&FxHashSet::from_iter(
|
||||
input_visitor
|
||||
.nested_elision_site_lts
|
||||
.iter()
|
||||
.chain(output_visitor.nested_elision_site_lts.iter())
|
||||
.cloned()
|
||||
.filter(|v| matches!(v, RefLt::Named(_))),
|
||||
))
|
||||
.next()
|
||||
.is_some()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
let input_lts = input_visitor.lts;
|
||||
let output_lts = output_visitor.lts;
|
||||
|
||||
if let Some(body_id) = body {
|
||||
let mut checker = BodyLifetimeChecker {
|
||||
@ -277,27 +294,20 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
|
||||
allowed_lts
|
||||
}
|
||||
|
||||
fn lts_from_bounds<'a, T: Iterator<Item = &'a Lifetime>>(mut vec: Vec<RefLt>, bounds_lts: T) -> Vec<RefLt> {
|
||||
for lt in bounds_lts {
|
||||
if lt.name != LifetimeName::Static {
|
||||
vec.push(RefLt::Named(lt.name.ident().name));
|
||||
}
|
||||
}
|
||||
|
||||
vec
|
||||
}
|
||||
|
||||
/// Number of unique lifetimes in the given vector.
|
||||
#[must_use]
|
||||
fn unique_lifetimes(lts: &[RefLt]) -> usize {
|
||||
lts.iter().collect::<FxHashSet<_>>().len()
|
||||
}
|
||||
|
||||
const CLOSURE_TRAIT_BOUNDS: [&[&str]; 3] = [&paths::FN, &paths::FN_MUT, &paths::FN_ONCE];
|
||||
|
||||
/// A visitor usable for `rustc_front::visit::walk_ty()`.
|
||||
struct RefVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
lts: Vec<RefLt>,
|
||||
abort: bool,
|
||||
nested_elision_site_lts: Vec<RefLt>,
|
||||
unelided_trait_object_lifetime: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
|
||||
@ -305,7 +315,8 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
|
||||
Self {
|
||||
cx,
|
||||
lts: Vec::new(),
|
||||
abort: false,
|
||||
nested_elision_site_lts: Vec::new(),
|
||||
unelided_trait_object_lifetime: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -325,40 +336,16 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn into_vec(self) -> Option<Vec<RefLt>> {
|
||||
if self.abort {
|
||||
None
|
||||
} else {
|
||||
Some(self.lts)
|
||||
}
|
||||
fn all_lts(&self) -> Vec<RefLt> {
|
||||
self.lts
|
||||
.iter()
|
||||
.chain(self.nested_elision_site_lts.iter())
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
fn collect_anonymous_lifetimes(&mut self, qpath: &QPath<'_>, ty: &Ty<'_>) {
|
||||
if let Some(ref last_path_segment) = last_path_segment(qpath).args {
|
||||
if !last_path_segment.parenthesized
|
||||
&& !last_path_segment
|
||||
.args
|
||||
.iter()
|
||||
.any(|arg| matches!(arg, GenericArg::Lifetime(_)))
|
||||
{
|
||||
let hir_id = ty.hir_id;
|
||||
match self.cx.qpath_res(qpath, hir_id) {
|
||||
Res::Def(DefKind::TyAlias | DefKind::Struct, def_id) => {
|
||||
let generics = self.cx.tcx.generics_of(def_id);
|
||||
for _ in generics.params.as_slice() {
|
||||
self.record(&None);
|
||||
}
|
||||
},
|
||||
Res::Def(DefKind::Trait, def_id) => {
|
||||
let trait_def = self.cx.tcx.trait_def(def_id);
|
||||
for _ in &self.cx.tcx.generics_of(trait_def.def_id).params {
|
||||
self.record(&None);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
fn abort(&self) -> bool {
|
||||
self.unelided_trait_object_lifetime
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,30 +357,37 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
|
||||
self.record(&Some(*lifetime));
|
||||
}
|
||||
|
||||
fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>, tbm: TraitBoundModifier) {
|
||||
let trait_ref = &poly_tref.trait_ref;
|
||||
if CLOSURE_TRAIT_BOUNDS
|
||||
.iter()
|
||||
.any(|trait_path| trait_ref.trait_def_id() == get_trait_def_id(self.cx, trait_path))
|
||||
{
|
||||
let mut sub_visitor = RefVisitor::new(self.cx);
|
||||
sub_visitor.visit_trait_ref(trait_ref);
|
||||
self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
|
||||
} else {
|
||||
walk_poly_trait_ref(self, poly_tref, tbm);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: &'tcx Ty<'_>) {
|
||||
match ty.kind {
|
||||
TyKind::Rptr(ref lt, _) if lt.is_elided() => {
|
||||
self.record(&None);
|
||||
},
|
||||
TyKind::Path(ref path) => {
|
||||
self.collect_anonymous_lifetimes(path, ty);
|
||||
},
|
||||
TyKind::OpaqueDef(item, _) => {
|
||||
let map = self.cx.tcx.hir();
|
||||
if let ItemKind::OpaqueTy(ref exist_ty) = map.expect_item(item.id).kind {
|
||||
for bound in exist_ty.bounds {
|
||||
if let GenericBound::Outlives(_) = *bound {
|
||||
self.record(&None);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
let item = map.expect_item(item.id);
|
||||
walk_item(self, item);
|
||||
walk_ty(self, ty);
|
||||
},
|
||||
TyKind::BareFn(&BareFnTy { decl, .. }) => {
|
||||
let mut sub_visitor = RefVisitor::new(self.cx);
|
||||
sub_visitor.visit_fn_decl(decl);
|
||||
self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
|
||||
return;
|
||||
},
|
||||
TyKind::TraitObject(bounds, ref lt) => {
|
||||
if !lt.is_elided() {
|
||||
self.abort = true;
|
||||
self.unelided_trait_object_lifetime = true;
|
||||
}
|
||||
for bound in bounds {
|
||||
self.visit_poly_trait_ref(bound, TraitBoundModifier::None);
|
||||
@ -430,16 +424,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl
|
||||
walk_param_bound(&mut visitor, bound);
|
||||
}
|
||||
// and check that all lifetimes are allowed
|
||||
match visitor.into_vec() {
|
||||
None => return false,
|
||||
Some(lts) => {
|
||||
for lt in lts {
|
||||
if !allowed_lts.contains(<) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
return visitor.all_lts().iter().any(|it| !allowed_lts.contains(it));
|
||||
},
|
||||
WherePredicate::EqPredicate(ref pred) => {
|
||||
let mut visitor = RefVisitor::new(cx);
|
||||
|
@ -42,6 +42,9 @@ pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
|
||||
pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
|
||||
pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
|
||||
pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
|
||||
pub const FN: [&str; 3] = ["core", "ops", "Fn"];
|
||||
pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"];
|
||||
pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"];
|
||||
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
|
||||
pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
|
||||
pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
|
||||
|
10
tests/ui/crashes/ice-2774.stderr
Normal file
10
tests/ui/crashes/ice-2774.stderr
Normal file
@ -0,0 +1,10 @@
|
||||
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
|
||||
--> $DIR/ice-2774.rs:15:1
|
||||
|
|
||||
LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::needless-lifetimes` implied by `-D warnings`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
14
tests/ui/crashes/needless_lifetimes_impl_trait.stderr
Normal file
14
tests/ui/crashes/needless_lifetimes_impl_trait.stderr
Normal file
@ -0,0 +1,14 @@
|
||||
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
|
||||
--> $DIR/needless_lifetimes_impl_trait.rs:15:5
|
||||
|
|
||||
LL | fn baz<'a>(&'a self) -> impl Foo + 'a {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/needless_lifetimes_impl_trait.rs:1:9
|
||||
|
|
||||
LL | #![deny(clippy::needless_lifetimes)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -259,4 +259,102 @@ mod issue4291 {
|
||||
}
|
||||
}
|
||||
|
||||
mod issue2944 {
|
||||
trait Foo {}
|
||||
struct Bar {}
|
||||
struct Baz<'a> {
|
||||
bar: &'a Bar,
|
||||
}
|
||||
|
||||
impl<'a> Foo for Baz<'a> {}
|
||||
impl Bar {
|
||||
fn baz<'a>(&'a self) -> impl Foo + 'a {
|
||||
Baz { bar: self }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod nested_elision_sites {
|
||||
// issue #issue2944
|
||||
|
||||
// closure trait bounds subject to nested elision
|
||||
// don't lint because they refer to outer lifetimes
|
||||
fn trait_fn<'a>(i: &'a i32) -> impl Fn() -> &'a i32 {
|
||||
move || i
|
||||
}
|
||||
fn trait_fn_mut<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 {
|
||||
move || i
|
||||
}
|
||||
fn trait_fn_once<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 {
|
||||
move || i
|
||||
}
|
||||
|
||||
// don't lint
|
||||
fn impl_trait_in_input_position<'a>(f: impl Fn() -> &'a i32) -> &'a i32 {
|
||||
f()
|
||||
}
|
||||
fn impl_trait_in_output_position<'a>(i: &'a i32) -> impl Fn() -> &'a i32 {
|
||||
move || i
|
||||
}
|
||||
// lint
|
||||
fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 {
|
||||
f(i)
|
||||
}
|
||||
fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
|
||||
f(i)
|
||||
}
|
||||
|
||||
// don't lint
|
||||
fn generics_not_elidable<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 {
|
||||
f()
|
||||
}
|
||||
// lint
|
||||
fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 {
|
||||
f(i)
|
||||
}
|
||||
|
||||
// don't lint
|
||||
fn where_clause_not_elidable<'a, T>(f: T) -> &'a i32
|
||||
where
|
||||
T: Fn() -> &'a i32,
|
||||
{
|
||||
f()
|
||||
}
|
||||
// lint
|
||||
fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32
|
||||
where
|
||||
T: Fn(&i32) -> &i32,
|
||||
{
|
||||
f(i)
|
||||
}
|
||||
|
||||
// don't lint
|
||||
fn pointer_fn_in_input_position<'a>(f: fn(&'a i32) -> &'a i32, i: &'a i32) -> &'a i32 {
|
||||
f(i)
|
||||
}
|
||||
fn pointer_fn_in_output_position<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 {
|
||||
|i| i
|
||||
}
|
||||
// lint
|
||||
fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 {
|
||||
f(i)
|
||||
}
|
||||
|
||||
// don't lint
|
||||
fn nested_fn_pointer_1<'a>(_: &'a i32) -> fn(fn(&'a i32) -> &'a i32) -> i32 {
|
||||
|f| 42
|
||||
}
|
||||
fn nested_fn_pointer_2<'a>(_: &'a i32) -> impl Fn(fn(&'a i32)) {
|
||||
|f| ()
|
||||
}
|
||||
|
||||
// lint
|
||||
fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 {
|
||||
|f| 42
|
||||
}
|
||||
fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) {
|
||||
|f| ()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -102,5 +102,53 @@ error: explicit lifetimes given in parameter types where they could be elided (o
|
||||
LL | fn needless_lt<'a>(_x: &'a u8) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 17 previous errors
|
||||
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
|
||||
--> $DIR/needless_lifetimes.rs:271:9
|
||||
|
|
||||
LL | fn baz<'a>(&'a self) -> impl Foo + 'a {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
|
||||
--> $DIR/needless_lifetimes.rs:300:5
|
||||
|
|
||||
LL | fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
|
||||
--> $DIR/needless_lifetimes.rs:303:5
|
||||
|
|
||||
LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
|
||||
--> $DIR/needless_lifetimes.rs:312:5
|
||||
|
|
||||
LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
|
||||
--> $DIR/needless_lifetimes.rs:324:5
|
||||
|
|
||||
LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
|
||||
--> $DIR/needless_lifetimes.rs:339:5
|
||||
|
|
||||
LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
|
||||
--> $DIR/needless_lifetimes.rs:352:5
|
||||
|
|
||||
LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
|
||||
--> $DIR/needless_lifetimes.rs:355:5
|
||||
|
|
||||
LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 25 previous errors
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user