Auto merge of #90446 - cjgillot:late-elided, r=jackh726

Lint elided lifetimes in path during lifetime resolution.

The lifetime elision lint is known to be brittle and can be redundant with later lifetime resolution errors. This PR aims to remove the redundancy by performing the lint after lifetime resolution.

This PR proposes to carry the information that an elision should be linted against by using a special `LifetimeName`. I am not certain this is the best solution, but it is certainly the easiest.

Fixes https://github.com/rust-lang/rust/issues/60199
Fixes https://github.com/rust-lang/rust/issues/55768
Fixes https://github.com/rust-lang/rust/issues/63110
Fixes https://github.com/rust-lang/rust/issues/71957
This commit is contained in:
bors 2021-12-01 23:22:43 +00:00
commit 76938d64a4
13 changed files with 401 additions and 241 deletions

View File

@ -1786,7 +1786,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
GenericArg::Lifetime(hir::Lifetime {
hir_id: self.next_id(),
span: self.lower_span(span),
name: hir::LifetimeName::Implicit,
name: hir::LifetimeName::Implicit(false),
})));
let generic_args = self.arena.alloc_from_iter(generic_args);
@ -1927,7 +1927,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
});
let param_name = match lt.name {
hir::LifetimeName::Param(param_name) => param_name,
hir::LifetimeName::Implicit
hir::LifetimeName::Implicit(_)
| hir::LifetimeName::Underscore
| hir::LifetimeName::Static => hir::ParamName::Plain(lt.name.ident()),
hir::LifetimeName::ImplicitObjectLifetimeDefault => {
@ -2290,7 +2290,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
AnonymousLifetimeMode::ReportError => self.new_error_lifetime(None, span),
AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span),
AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span, false),
}
}
@ -2322,11 +2322,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&'s mut self,
span: Span,
count: usize,
param_mode: ParamMode,
) -> impl Iterator<Item = hir::Lifetime> + Captures<'a> + Captures<'s> + Captures<'hir> {
(0..count).map(move |_| self.elided_path_lifetime(span))
(0..count).map(move |_| self.elided_path_lifetime(span, param_mode))
}
fn elided_path_lifetime(&mut self, span: Span) -> hir::Lifetime {
fn elided_path_lifetime(&mut self, span: Span, param_mode: ParamMode) -> hir::Lifetime {
match self.anonymous_lifetime_mode {
AnonymousLifetimeMode::CreateParameter => {
// We should have emitted E0726 when processing this path above
@ -2342,7 +2343,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// lifetime. Instead, we simply create an implicit lifetime, which will be checked
// later, at which point a suitable error will be emitted.
AnonymousLifetimeMode::PassThrough | AnonymousLifetimeMode::ReportError => {
self.new_implicit_lifetime(span)
self.new_implicit_lifetime(span, param_mode == ParamMode::Explicit)
}
}
}
@ -2385,11 +2386,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
r
}
fn new_implicit_lifetime(&mut self, span: Span) -> hir::Lifetime {
fn new_implicit_lifetime(&mut self, span: Span, missing: bool) -> hir::Lifetime {
hir::Lifetime {
hir_id: self.next_id(),
span: self.lower_span(span),
name: hir::LifetimeName::Implicit,
name: hir::LifetimeName::Implicit(missing),
}
}
@ -2536,7 +2537,7 @@ fn lifetimes_from_impl_trait_bounds(
fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
let name = match lifetime.name {
hir::LifetimeName::Implicit | hir::LifetimeName::Underscore => {
hir::LifetimeName::Implicit(_) | hir::LifetimeName::Underscore => {
if self.collect_elided_lifetimes {
// Use `'_` for both implicit and underscore lifetimes in
// `type Foo<'_> = impl SomeTrait<'_>;`.

View File

@ -7,8 +7,6 @@ use rustc_hir as hir;
use rustc_hir::def::{DefKind, PartialRes, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::GenericArg;
use rustc_session::lint::builtin::ELIDED_LIFETIMES_IN_PATHS;
use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_span::symbol::Ident;
use rustc_span::{BytePos, Span, DUMMY_SP};
@ -270,12 +268,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let has_lifetimes =
generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)));
if !generic_args.parenthesized && !has_lifetimes {
if !generic_args.parenthesized && !has_lifetimes && expected_lifetimes > 0 {
// Note: these spans are used for diagnostics when they can't be inferred.
// See rustc_resolve::late::lifetimes::LifetimeContext::add_missing_lifetime_specifiers_label
let elided_lifetime_span = if generic_args.span.is_empty() {
// If there are no brackets, use the identifier span.
segment.ident.span
path_span
} else if generic_args.is_empty() {
// If there are brackets, but not generic arguments, then use the opening bracket
generic_args.span.with_hi(generic_args.span.lo() + BytePos(1))
@ -284,67 +282,47 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo()
};
generic_args.args = self
.elided_path_lifetimes(elided_lifetime_span, expected_lifetimes)
.elided_path_lifetimes(elided_lifetime_span, expected_lifetimes, param_mode)
.map(GenericArg::Lifetime)
.chain(generic_args.args.into_iter())
.collect();
if expected_lifetimes > 0 && param_mode == ParamMode::Explicit {
// In create-parameter mode we error here because we don't want to support
// deprecated impl elision in new features like impl elision and `async fn`,
// both of which work using the `CreateParameter` mode:
//
// impl Foo for std::cell::Ref<u32> // note lack of '_
// async fn foo(_: std::cell::Ref<u32>) { ... }
if let (ParamMode::Explicit, AnonymousLifetimeMode::CreateParameter) =
(param_mode, self.anonymous_lifetime_mode)
{
let anon_lt_suggestion = vec!["'_"; expected_lifetimes].join(", ");
let no_non_lt_args = generic_args.args.len() == expected_lifetimes;
let no_bindings = generic_args.bindings.is_empty();
let (incl_angl_brckt, insertion_sp, suggestion) = if no_non_lt_args && no_bindings {
let (incl_angl_brckt, suggestion) = if no_non_lt_args && no_bindings {
// If there are no generic args, our suggestion can include the angle brackets.
(true, path_span.shrink_to_hi(), format!("<{}>", anon_lt_suggestion))
(true, format!("<{}>", anon_lt_suggestion))
} else {
// Otherwise we'll insert a `'_, ` right after the opening bracket.
let span = generic_args
.span
.with_lo(generic_args.span.lo() + BytePos(1))
.shrink_to_lo();
(false, span, format!("{}, ", anon_lt_suggestion))
(false, format!("{}, ", anon_lt_suggestion))
};
match self.anonymous_lifetime_mode {
// In create-parameter mode we error here because we don't want to support
// deprecated impl elision in new features like impl elision and `async fn`,
// both of which work using the `CreateParameter` mode:
//
// impl Foo for std::cell::Ref<u32> // note lack of '_
// async fn foo(_: std::cell::Ref<u32>) { ... }
AnonymousLifetimeMode::CreateParameter => {
let mut err = struct_span_err!(
self.sess,
path_span,
E0726,
"implicit elided lifetime not allowed here"
);
rustc_errors::add_elided_lifetime_in_path_suggestion(
&self.sess.source_map(),
&mut err,
expected_lifetimes,
path_span,
incl_angl_brckt,
insertion_sp,
suggestion,
);
err.note("assuming a `'static` lifetime...");
err.emit();
}
AnonymousLifetimeMode::PassThrough | AnonymousLifetimeMode::ReportError => {
self.resolver.lint_buffer().buffer_lint_with_diagnostic(
ELIDED_LIFETIMES_IN_PATHS,
CRATE_NODE_ID,
path_span,
"hidden lifetime parameters in types are deprecated",
BuiltinLintDiagnostics::ElidedLifetimesInPaths(
expected_lifetimes,
path_span,
incl_angl_brckt,
insertion_sp,
suggestion,
),
);
}
}
let insertion_sp = elided_lifetime_span.shrink_to_hi();
let mut err = struct_span_err!(
self.sess,
path_span,
E0726,
"implicit elided lifetime not allowed here"
);
rustc_errors::add_elided_lifetime_in_path_suggestion(
&self.sess.source_map(),
&mut err,
expected_lifetimes,
path_span,
incl_angl_brckt,
insertion_sp,
suggestion,
);
err.note("assuming a `'static` lifetime...");
err.emit();
}
}

View File

@ -584,7 +584,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
Some(RegionNameHighlight::MatchedAdtAndSegment(lifetime_span))
}
hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Implicit => {
hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Implicit(_) => {
// In this case, the user left off the lifetime; so
// they wrote something like:
//

View File

@ -92,7 +92,9 @@ pub enum LifetimeName {
Param(ParamName),
/// User wrote nothing (e.g., the lifetime in `&u32`).
Implicit,
///
/// The bool indicates whether the user should have written something.
Implicit(bool),
/// Implicit lifetime in a context like `dyn Foo`. This is
/// distinguished from implicit lifetimes elsewhere because the
@ -122,7 +124,7 @@ impl LifetimeName {
pub fn ident(&self) -> Ident {
match *self {
LifetimeName::ImplicitObjectLifetimeDefault
| LifetimeName::Implicit
| LifetimeName::Implicit(_)
| LifetimeName::Error => Ident::empty(),
LifetimeName::Underscore => Ident::with_dummy_span(kw::UnderscoreLifetime),
LifetimeName::Static => Ident::with_dummy_span(kw::StaticLifetime),
@ -133,7 +135,7 @@ impl LifetimeName {
pub fn is_elided(&self) -> bool {
match self {
LifetimeName::ImplicitObjectLifetimeDefault
| LifetimeName::Implicit
| LifetimeName::Implicit(_)
| LifetimeName::Underscore => true,
// It might seem surprising that `Fresh(_)` counts as
@ -3298,7 +3300,7 @@ mod size_asserts {
rustc_data_structures::static_assert_size!(super::Expr<'static>, 64);
rustc_data_structures::static_assert_size!(super::Pat<'static>, 88);
rustc_data_structures::static_assert_size!(super::QPath<'static>, 24);
rustc_data_structures::static_assert_size!(super::Ty<'static>, 72);
rustc_data_structures::static_assert_size!(super::Ty<'static>, 80);
rustc_data_structures::static_assert_size!(super::Item<'static>, 184);
rustc_data_structures::static_assert_size!(super::TraitItem<'static>, 128);

View File

@ -545,7 +545,7 @@ pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime
| LifetimeName::Param(ParamName::Error)
| LifetimeName::Static
| LifetimeName::Error
| LifetimeName::Implicit
| LifetimeName::Implicit(_)
| LifetimeName::ImplicitObjectLifetimeDefault
| LifetimeName::Underscore => {}
}

View File

@ -1871,6 +1871,117 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
err.emit();
}
/// Returns whether to add `'static` lifetime to the suggested lifetime list.
crate fn report_elision_failure(
&mut self,
db: &mut DiagnosticBuilder<'_>,
params: &[ElisionFailureInfo],
) -> bool {
let mut m = String::new();
let len = params.len();
let elided_params: Vec<_> =
params.iter().cloned().filter(|info| info.lifetime_count > 0).collect();
let elided_len = elided_params.len();
for (i, info) in elided_params.into_iter().enumerate() {
let ElisionFailureInfo { parent, index, lifetime_count: n, have_bound_regions, span } =
info;
db.span_label(span, "");
let help_name = if let Some(ident) =
parent.and_then(|body| self.tcx.hir().body(body).params[index].pat.simple_ident())
{
format!("`{}`", ident)
} else {
format!("argument {}", index + 1)
};
m.push_str(
&(if n == 1 {
help_name
} else {
format!(
"one of {}'s {} {}lifetimes",
help_name,
n,
if have_bound_regions { "free " } else { "" }
)
})[..],
);
if elided_len == 2 && i == 0 {
m.push_str(" or ");
} else if i + 2 == elided_len {
m.push_str(", or ");
} else if i != elided_len - 1 {
m.push_str(", ");
}
}
if len == 0 {
db.help(
"this function's return type contains a borrowed value, \
but there is no value for it to be borrowed from",
);
true
} else if elided_len == 0 {
db.help(
"this function's return type contains a borrowed value with \
an elided lifetime, but the lifetime cannot be derived from \
the arguments",
);
true
} else if elided_len == 1 {
db.help(&format!(
"this function's return type contains a borrowed value, \
but the signature does not say which {} it is borrowed from",
m
));
false
} else {
db.help(&format!(
"this function's return type contains a borrowed value, \
but the signature does not say whether it is borrowed from {}",
m
));
false
}
}
crate fn report_elided_lifetime_in_ty(&self, lifetime_refs: &[&hir::Lifetime]) {
let Some(missing_lifetime) = lifetime_refs.iter().find(|lt| {
lt.name == hir::LifetimeName::Implicit(true)
}) else { return };
let mut spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect();
spans.sort();
let mut spans_dedup = spans.clone();
spans_dedup.dedup();
let spans_with_counts: Vec<_> = spans_dedup
.into_iter()
.map(|sp| (sp, spans.iter().filter(|nsp| *nsp == &sp).count()))
.collect();
self.tcx.struct_span_lint_hir(
rustc_session::lint::builtin::ELIDED_LIFETIMES_IN_PATHS,
missing_lifetime.hir_id,
spans,
|lint| {
let mut db = lint.build("hidden lifetime parameters in types are deprecated");
self.add_missing_lifetime_specifiers_label(
&mut db,
spans_with_counts,
&FxHashSet::from_iter([kw::UnderscoreLifetime]),
Vec::new(),
&[],
);
db.emit()
},
);
}
// FIXME(const_generics): This patches over an ICE caused by non-'static lifetimes in const
// generics. We are disallowing this until we can decide on how we want to handle non-'static
// lifetimes in const generics. See issue #74052 for discussion.
@ -2297,7 +2408,9 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
);
let is_allowed_lifetime = matches!(
lifetime_ref.name,
hir::LifetimeName::Implicit | hir::LifetimeName::Static | hir::LifetimeName::Underscore
hir::LifetimeName::Implicit(_)
| hir::LifetimeName::Static
| hir::LifetimeName::Underscore
);
if !self.tcx.lazy_normalization() && is_anon_const && !is_allowed_lifetime {

View File

@ -357,11 +357,11 @@ enum Elide {
#[derive(Clone, Debug)]
crate struct ElisionFailureInfo {
/// Where we can find the argument pattern.
parent: Option<hir::BodyId>,
crate parent: Option<hir::BodyId>,
/// The index of the argument in the original definition.
index: usize,
lifetime_count: usize,
have_bound_regions: bool,
crate index: usize,
crate lifetime_count: usize,
crate have_bound_regions: bool,
crate span: Span,
}
@ -923,7 +923,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
}
});
match lifetime.name {
LifetimeName::Implicit => {
LifetimeName::Implicit(_) => {
// For types like `dyn Foo`, we should
// generate a special form of elided.
span_bug!(ty.span, "object-lifetime-default expected, not implicit",);
@ -3057,9 +3057,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
let error = loop {
match *scope {
// Do not assign any resolution, it will be inferred.
Scope::Body { .. } => return,
Scope::Body { .. } => break Ok(()),
Scope::Root => break None,
Scope::Root => break Err(None),
Scope::Binder { s, ref lifetimes, scope_type, .. } => {
// collect named lifetimes for suggestions
@ -3076,50 +3076,54 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
scope = s;
}
Scope::Elision { ref elide, ref s, .. } => {
let lifetime = match *elide {
Elide::FreshLateAnon(named_late_bound_vars, ref counter) => {
for lifetime_ref in lifetime_refs {
let lifetime = Region::late_anon(named_late_bound_vars, counter)
.shifted(late_depth);
Scope::Elision {
elide: Elide::FreshLateAnon(named_late_bound_vars, ref counter),
..
} => {
for lifetime_ref in lifetime_refs {
let lifetime =
Region::late_anon(named_late_bound_vars, counter).shifted(late_depth);
self.insert_lifetime(lifetime_ref, lifetime);
}
return;
}
Elide::Exact(l) => l.shifted(late_depth),
Elide::Error(ref e) => {
let mut scope = s;
loop {
match scope {
Scope::Binder { ref lifetimes, s, .. } => {
// Collect named lifetimes for suggestions.
for name in lifetimes.keys() {
if let hir::ParamName::Plain(name) = name {
lifetime_names.insert(name.name);
lifetime_spans.push(name.span);
}
}
scope = s;
}
Scope::ObjectLifetimeDefault { ref s, .. }
| Scope::Elision { ref s, .. }
| Scope::TraitRefBoundary { ref s, .. } => {
scope = s;
}
_ => break,
}
}
break Some(&e[..]);
}
Elide::Forbid => break None,
};
self.insert_lifetime(lifetime_ref, lifetime);
}
break Ok(());
}
Scope::Elision { elide: Elide::Exact(l), .. } => {
let lifetime = l.shifted(late_depth);
for lifetime_ref in lifetime_refs {
self.insert_lifetime(lifetime_ref, lifetime);
}
return;
break Ok(());
}
Scope::Elision { elide: Elide::Error(ref e), ref s, .. } => {
let mut scope = s;
loop {
match scope {
Scope::Binder { ref lifetimes, s, .. } => {
// Collect named lifetimes for suggestions.
for name in lifetimes.keys() {
if let hir::ParamName::Plain(name) = name {
lifetime_names.insert(name.name);
lifetime_spans.push(name.span);
}
}
scope = s;
}
Scope::ObjectLifetimeDefault { ref s, .. }
| Scope::Elision { ref s, .. }
| Scope::TraitRefBoundary { ref s, .. } => {
scope = s;
}
_ => break,
}
}
break Err(Some(&e[..]));
}
Scope::Elision { elide: Elide::Forbid, .. } => break Err(None),
Scope::ObjectLifetimeDefault { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s, .. } => {
@ -3128,6 +3132,14 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
}
};
let error = match error {
Ok(()) => {
self.report_elided_lifetime_in_ty(lifetime_refs);
return;
}
Err(error) => error,
};
// If we specifically need the `scope_for_path` map, then we're in the
// diagnostic pass and we don't want to emit more errors.
if self.map.scope_for_path.is_some() {
@ -3166,84 +3178,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
err.emit();
}
fn report_elision_failure(
&mut self,
db: &mut DiagnosticBuilder<'_>,
params: &[ElisionFailureInfo],
) -> bool /* add `'static` lifetime to lifetime list */ {
let mut m = String::new();
let len = params.len();
let elided_params: Vec<_> =
params.iter().cloned().filter(|info| info.lifetime_count > 0).collect();
let elided_len = elided_params.len();
for (i, info) in elided_params.into_iter().enumerate() {
let ElisionFailureInfo { parent, index, lifetime_count: n, have_bound_regions, span } =
info;
db.span_label(span, "");
let help_name = if let Some(ident) =
parent.and_then(|body| self.tcx.hir().body(body).params[index].pat.simple_ident())
{
format!("`{}`", ident)
} else {
format!("argument {}", index + 1)
};
m.push_str(
&(if n == 1 {
help_name
} else {
format!(
"one of {}'s {} {}lifetimes",
help_name,
n,
if have_bound_regions { "free " } else { "" }
)
})[..],
);
if elided_len == 2 && i == 0 {
m.push_str(" or ");
} else if i + 2 == elided_len {
m.push_str(", or ");
} else if i != elided_len - 1 {
m.push_str(", ");
}
}
if len == 0 {
db.help(
"this function's return type contains a borrowed value, \
but there is no value for it to be borrowed from",
);
true
} else if elided_len == 0 {
db.help(
"this function's return type contains a borrowed value with \
an elided lifetime, but the lifetime cannot be derived from \
the arguments",
);
true
} else if elided_len == 1 {
db.help(&format!(
"this function's return type contains a borrowed value, \
but the signature does not say which {} it is borrowed from",
m
));
false
} else {
db.help(&format!(
"this function's return type contains a borrowed value, \
but the signature does not say whether it is borrowed from {}",
m
));
false
}
}
fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
debug!("resolve_object_lifetime_default(lifetime_ref={:?})", lifetime_ref);
let mut late_depth = 0;
@ -3348,7 +3282,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
))
.emit();
}
hir::LifetimeName::Param(_) | hir::LifetimeName::Implicit => {
hir::LifetimeName::Param(_) | hir::LifetimeName::Implicit(_) => {
self.resolve_lifetime_ref(lt);
}
hir::LifetimeName::ImplicitObjectLifetimeDefault => {

View File

@ -5,23 +5,24 @@
#![deny(elided_lifetimes_in_paths)]
//~^ NOTE the lint level is defined here
use std::cell::{RefCell, Ref};
use std::cell::{Ref, RefCell};
struct Foo<'a> { x: &'a u32 }
struct Foo<'a> {
x: &'a u32,
}
fn foo(x: &Foo<'_>) {
//~^ ERROR hidden lifetime parameters in types are deprecated
//~| HELP indicate the anonymous lifetime
//~| NOTE expected named lifetime parameter
//~| HELP consider using the `'_` lifetime
}
fn bar(x: &Foo<'_>) {}
struct Wrapped<'a>(&'a str);
struct WrappedWithBow<'a> {
gift: &'a str
gift: &'a str,
}
struct MatchedSet<'a, 'b> {
@ -31,22 +32,34 @@ struct MatchedSet<'a, 'b> {
fn wrap_gift(gift: &str) -> Wrapped<'_> {
//~^ ERROR hidden lifetime parameters in types are deprecated
//~| HELP indicate the anonymous lifetime
//~| NOTE expected named lifetime parameter
//~| HELP consider using the `'_` lifetime
Wrapped(gift)
}
fn wrap_gift_with_bow(gift: &str) -> WrappedWithBow<'_> {
//~^ ERROR hidden lifetime parameters in types are deprecated
//~| HELP indicate the anonymous lifetime
//~| NOTE expected named lifetime parameter
//~| HELP consider using the `'_` lifetime
WrappedWithBow { gift }
}
fn inspect_matched_set(set: MatchedSet<'_, '_>) {
//~^ ERROR hidden lifetime parameters in types are deprecated
//~| HELP indicate the anonymous lifetime
//~| NOTE expected 2 lifetime parameters
//~| HELP consider using the `'_` lifetime
println!("{} {}", set.one, set.another);
}
// Verify that the lint does not fire, because the added `'_` wouldn't be resolved correctly.
fn match_sets() -> MatchedSet<'static, 'static> {
//~^ ERROR missing lifetime specifiers
//~| NOTE expected 2 lifetime parameters
//~| HELP this function's return type contains a borrowed value
//~| HELP consider using the `'static` lifetime
MatchedSet { one: "one", another: "another" }
}
macro_rules! autowrapper {
($type_name:ident, $fn_name:ident, $lt:lifetime) => {
struct $type_name<$lt> {
@ -55,7 +68,11 @@ macro_rules! autowrapper {
fn $fn_name(gift: &str) -> $type_name<'_> {
//~^ ERROR hidden lifetime parameters in types are deprecated
//~| HELP indicate the anonymous lifetime
//~| NOTE expected named lifetime parameter
//~| HELP consider using the `'_` lifetime
//~| ERROR hidden lifetime parameters in types are deprecated
//~| NOTE expected named lifetime parameter
//~| HELP consider using the `'_` lifetime
$type_name { gift }
}
}
@ -65,19 +82,34 @@ autowrapper!(Autowrapped, autowrap_gift, 'a);
//~^ NOTE in this expansion of autowrapper!
//~| NOTE in this expansion of autowrapper!
// Verify that rustfix does not try to apply the fix twice.
autowrapper!(AutowrappedAgain, autowrap_gift_again, 'a);
//~^ NOTE in this expansion of autowrapper!
//~| NOTE in this expansion of autowrapper!
macro_rules! anytuple_ref_ty {
($($types:ty),*) => {
Ref<'_, ($($types),*)>
//~^ ERROR hidden lifetime parameters in types are deprecated
//~| HELP indicate the anonymous lifetime
//~| NOTE expected named lifetime parameter
//~| HELP consider using the `'_` lifetime
}
}
#[allow(elided_lifetimes_in_paths)]
mod blah {
struct Thing<'a>(&'a i32);
struct Bar<T>(T);
fn foo(b: Bar<Thing>) {}
}
fn main() {
let honesty = RefCell::new((4, 'e'));
let loyalty: Ref<'_, (u32, char)> = honesty.borrow();
//~^ ERROR hidden lifetime parameters in types are deprecated
//~| HELP indicate the anonymous lifetime
//~| NOTE expected named lifetime parameter
//~| HELP consider using the `'_` lifetime
let generosity = Ref::map(loyalty, |t| &t.0);
let laughter = RefCell::new((true, "magic"));

View File

@ -5,23 +5,24 @@
#![deny(elided_lifetimes_in_paths)]
//~^ NOTE the lint level is defined here
use std::cell::{RefCell, Ref};
use std::cell::{Ref, RefCell};
struct Foo<'a> { x: &'a u32 }
struct Foo<'a> {
x: &'a u32,
}
fn foo(x: &Foo) {
//~^ ERROR hidden lifetime parameters in types are deprecated
//~| HELP indicate the anonymous lifetime
//~| NOTE expected named lifetime parameter
//~| HELP consider using the `'_` lifetime
}
fn bar(x: &Foo<'_>) {}
struct Wrapped<'a>(&'a str);
struct WrappedWithBow<'a> {
gift: &'a str
gift: &'a str,
}
struct MatchedSet<'a, 'b> {
@ -31,22 +32,34 @@ struct MatchedSet<'a, 'b> {
fn wrap_gift(gift: &str) -> Wrapped {
//~^ ERROR hidden lifetime parameters in types are deprecated
//~| HELP indicate the anonymous lifetime
//~| NOTE expected named lifetime parameter
//~| HELP consider using the `'_` lifetime
Wrapped(gift)
}
fn wrap_gift_with_bow(gift: &str) -> WrappedWithBow {
//~^ ERROR hidden lifetime parameters in types are deprecated
//~| HELP indicate the anonymous lifetime
//~| NOTE expected named lifetime parameter
//~| HELP consider using the `'_` lifetime
WrappedWithBow { gift }
}
fn inspect_matched_set(set: MatchedSet) {
//~^ ERROR hidden lifetime parameters in types are deprecated
//~| HELP indicate the anonymous lifetime
//~| NOTE expected 2 lifetime parameters
//~| HELP consider using the `'_` lifetime
println!("{} {}", set.one, set.another);
}
// Verify that the lint does not fire, because the added `'_` wouldn't be resolved correctly.
fn match_sets() -> MatchedSet {
//~^ ERROR missing lifetime specifiers
//~| NOTE expected 2 lifetime parameters
//~| HELP this function's return type contains a borrowed value
//~| HELP consider using the `'static` lifetime
MatchedSet { one: "one", another: "another" }
}
macro_rules! autowrapper {
($type_name:ident, $fn_name:ident, $lt:lifetime) => {
struct $type_name<$lt> {
@ -55,7 +68,11 @@ macro_rules! autowrapper {
fn $fn_name(gift: &str) -> $type_name {
//~^ ERROR hidden lifetime parameters in types are deprecated
//~| HELP indicate the anonymous lifetime
//~| NOTE expected named lifetime parameter
//~| HELP consider using the `'_` lifetime
//~| ERROR hidden lifetime parameters in types are deprecated
//~| NOTE expected named lifetime parameter
//~| HELP consider using the `'_` lifetime
$type_name { gift }
}
}
@ -65,19 +82,34 @@ autowrapper!(Autowrapped, autowrap_gift, 'a);
//~^ NOTE in this expansion of autowrapper!
//~| NOTE in this expansion of autowrapper!
// Verify that rustfix does not try to apply the fix twice.
autowrapper!(AutowrappedAgain, autowrap_gift_again, 'a);
//~^ NOTE in this expansion of autowrapper!
//~| NOTE in this expansion of autowrapper!
macro_rules! anytuple_ref_ty {
($($types:ty),*) => {
Ref<($($types),*)>
//~^ ERROR hidden lifetime parameters in types are deprecated
//~| HELP indicate the anonymous lifetime
//~| NOTE expected named lifetime parameter
//~| HELP consider using the `'_` lifetime
}
}
#[allow(elided_lifetimes_in_paths)]
mod blah {
struct Thing<'a>(&'a i32);
struct Bar<T>(T);
fn foo(b: Bar<Thing>) {}
}
fn main() {
let honesty = RefCell::new((4, 'e'));
let loyalty: Ref<(u32, char)> = honesty.borrow();
//~^ ERROR hidden lifetime parameters in types are deprecated
//~| HELP indicate the anonymous lifetime
//~| NOTE expected named lifetime parameter
//~| HELP consider using the `'_` lifetime
let generosity = Ref::map(loyalty, |t| &t.0);
let laughter = RefCell::new((true, "magic"));

View File

@ -1,60 +1,120 @@
error: hidden lifetime parameters in types are deprecated
--> $DIR/elided-lifetimes.rs:13:12
--> $DIR/elided-lifetimes.rs:14:12
|
LL | fn foo(x: &Foo) {
| ^^^- help: indicate the anonymous lifetime: `<'_>`
| ^^^ expected named lifetime parameter
|
note: the lint level is defined here
--> $DIR/elided-lifetimes.rs:5:9
|
LL | #![deny(elided_lifetimes_in_paths)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider using the `'_` lifetime
|
LL | fn foo(x: &Foo<'_>) {
| ~~~~~~~
error: hidden lifetime parameters in types are deprecated
--> $DIR/elided-lifetimes.rs:32:29
--> $DIR/elided-lifetimes.rs:33:29
|
LL | fn wrap_gift(gift: &str) -> Wrapped {
| ^^^^^^^- help: indicate the anonymous lifetime: `<'_>`
| ^^^^^^^ expected named lifetime parameter
|
help: consider using the `'_` lifetime
|
LL | fn wrap_gift(gift: &str) -> Wrapped<'_> {
| ~~~~~~~~~~~
error: hidden lifetime parameters in types are deprecated
--> $DIR/elided-lifetimes.rs:38:38
--> $DIR/elided-lifetimes.rs:40:38
|
LL | fn wrap_gift_with_bow(gift: &str) -> WrappedWithBow {
| ^^^^^^^^^^^^^^- help: indicate the anonymous lifetime: `<'_>`
| ^^^^^^^^^^^^^^ expected named lifetime parameter
|
help: consider using the `'_` lifetime
|
LL | fn wrap_gift_with_bow(gift: &str) -> WrappedWithBow<'_> {
| ~~~~~~~~~~~~~~~~~~
error: hidden lifetime parameters in types are deprecated
--> $DIR/elided-lifetimes.rs:44:29
--> $DIR/elided-lifetimes.rs:47:29
|
LL | fn inspect_matched_set(set: MatchedSet) {
| ^^^^^^^^^^- help: indicate the anonymous lifetimes: `<'_, '_>`
| ^^^^^^^^^^ expected 2 lifetime parameters
|
help: consider using the `'_` lifetime
|
LL | fn inspect_matched_set(set: MatchedSet<'_, '_>) {
| ~~~~~~~~~~~~~~~~~~
error[E0106]: missing lifetime specifiers
--> $DIR/elided-lifetimes.rs:55:20
|
LL | fn match_sets() -> MatchedSet {
| ^^^^^^^^^^ expected 2 lifetime parameters
|
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the `'static` lifetime
|
LL | fn match_sets() -> MatchedSet<'static, 'static> {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: hidden lifetime parameters in types are deprecated
--> $DIR/elided-lifetimes.rs:56:36
--> $DIR/elided-lifetimes.rs:69:36
|
LL | fn $fn_name(gift: &str) -> $type_name {
| ^^^^^^^^^^- help: indicate the anonymous lifetime: `<'_>`
| ^^^^^^^^^^ expected named lifetime parameter
...
LL | autowrapper!(Autowrapped, autowrap_gift, 'a);
| -------------------------------------------- in this macro invocation
|
= note: this error originates in the macro `autowrapper` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider using the `'_` lifetime
|
LL | fn $fn_name(gift: &str) -> $type_name<'_> {
| ~~~~~~~~~~~~~~
error: hidden lifetime parameters in types are deprecated
--> $DIR/elided-lifetimes.rs:78:18
--> $DIR/elided-lifetimes.rs:69:36
|
LL | fn $fn_name(gift: &str) -> $type_name {
| ^^^^^^^^^^ expected named lifetime parameter
...
LL | autowrapper!(AutowrappedAgain, autowrap_gift_again, 'a);
| ------------------------------------------------------- in this macro invocation
|
= note: this error originates in the macro `autowrapper` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider using the `'_` lifetime
|
LL | fn $fn_name(gift: &str) -> $type_name<'_> {
| ~~~~~~~~~~~~~~
error: hidden lifetime parameters in types are deprecated
--> $DIR/elided-lifetimes.rs:109:22
|
LL | let loyalty: Ref<(u32, char)> = honesty.borrow();
| ^^^^^^^^^^^^^^^^ help: indicate the anonymous lifetime: `Ref<'_, (u32, char)>`
| ^ expected named lifetime parameter
|
help: consider using the `'_` lifetime
|
LL | let loyalty: Ref<'_, (u32, char)> = honesty.borrow();
| +++
error: hidden lifetime parameters in types are deprecated
--> $DIR/elided-lifetimes.rs:70:9
--> $DIR/elided-lifetimes.rs:92:13
|
LL | Ref<($($types),*)>
| ^^^^^^^^^^^^^^^^^^ help: indicate the anonymous lifetime: `Ref<'_, ($($types),*)>`
| ^ expected named lifetime parameter
...
LL | let yellow: anytuple_ref_ty!(bool, &str) = laughter.borrow();
| ---------------------------- in this macro invocation
|
= note: this error originates in the macro `anytuple_ref_ty` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider using the `'_` lifetime
|
LL | Ref<'_, ($($types),*)>
| +++
error: aborting due to 7 previous errors
error: aborting due to 9 previous errors
For more information about this error, try `rustc --explain E0106`.

View File

@ -2,9 +2,13 @@ warning: hidden lifetime parameters in types are deprecated
--> $DIR/allowed-by-default-lint.rs:9:12
|
LL | fn foo(x: &Foo) {}
| ^^^- help: indicate the anonymous lifetime: `<'_>`
| ^^^ expected named lifetime parameter
|
= note: requested on the command line with `--force-warn elided-lifetimes-in-paths`
help: consider using the `'_` lifetime
|
LL | fn foo(x: &Foo<'_>) {}
| ~~~~~~~
warning: 1 warning emitted

View File

@ -1,7 +1,6 @@
// check-pass
#![feature(lint_reasons)]
#![warn(elided_lifetimes_in_paths,
//~^ NOTE the lint level is defined here
reason = "explicit anonymous lifetimes aid reasoning about ownership")]
@ -20,8 +19,9 @@ pub struct CheaterDetectionMechanism {}
impl fmt::Debug for CheaterDetectionMechanism {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
//~^ WARN hidden lifetime parameters in types are deprecated
//~| NOTE expected named lifetime parameter
//~| NOTE explicit anonymous lifetimes aid
//~| HELP indicate the anonymous lifetime
//~| HELP consider using the `'_` lifetime
fmt.debug_struct("CheaterDetectionMechanism").finish()
}
}

View File

@ -1,15 +1,19 @@
warning: hidden lifetime parameters in types are deprecated
--> $DIR/reasons.rs:21:29
--> $DIR/reasons.rs:20:29
|
LL | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
| ^^^^^^^^^^^^^^- help: indicate the anonymous lifetime: `<'_>`
| ^^^^^^^^^^^^^^ expected named lifetime parameter
|
= note: explicit anonymous lifetimes aid reasoning about ownership
note: the lint level is defined here
--> $DIR/reasons.rs:5:9
--> $DIR/reasons.rs:4:9
|
LL | #![warn(elided_lifetimes_in_paths,
| ^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider using the `'_` lifetime
|
LL | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
| ~~~~~~~~~~~~~~~~~~
warning: variable `Social_exchange_psychology` should have a snake case name
--> $DIR/reasons.rs:30:9
@ -20,7 +24,7 @@ LL | let Social_exchange_psychology = CheaterDetectionMechanism {};
= note: people shouldn't have to change their usual style habits
to contribute to our project
note: the lint level is defined here
--> $DIR/reasons.rs:9:5
--> $DIR/reasons.rs:8:5
|
LL | nonstandard_style,
| ^^^^^^^^^^^^^^^^^