diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 722934ac39a53..be4a63b18d9be 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -47,7 +47,8 @@ use hir::map::{DefKey, DefPathData, Definitions}; use hir::def_id::{DefId, DefIndex, DefIndexAddressSpace, CRATE_DEF_INDEX}; use hir::def::{Def, PathResolution, PerNS}; use hir::GenericArg; -use lint::builtin::{self, PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES}; +use lint::builtin::{self, PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES, + ELIDED_LIFETIMES_IN_PATHS}; use middle::cstore::CrateStore; use rustc_data_structures::indexed_vec::IndexVec; use session::Session; @@ -1753,13 +1754,40 @@ impl<'a> LoweringContext<'a> { GenericArg::Lifetime(_) => true, _ => false, }); + let first_generic_span = generic_args.args.iter().map(|a| a.span()) + .chain(generic_args.bindings.iter().map(|b| b.span)).next(); if !generic_args.parenthesized && !has_lifetimes { generic_args.args = self.elided_path_lifetimes(path_span, expected_lifetimes) .into_iter() .map(|lt| GenericArg::Lifetime(lt)) .chain(generic_args.args.into_iter()) - .collect(); + .collect(); + if expected_lifetimes > 0 && param_mode == ParamMode::Explicit { + let anon_lt_suggestion = iter::repeat("'_").collect::>().join(", "); + let no_ty_args = generic_args.args.len() == expected_lifetimes; + let no_bindings = generic_args.bindings.is_empty(); + let (incl_angl_brckt, insertion_span, suggestion) = if no_ty_args && no_bindings { + // If there are no (non-implicit) generic args or associated-type + // bindings, our suggestion includes the angle brackets + (true, path_span.shrink_to_hi(), format!("<{}>", anon_lt_suggestion)) + } else { + // Otherwise—sorry, this is kind of gross—we need to infer the + // place to splice in the `'_, ` from the generics that do exist + let first_generic_span = first_generic_span + .expect("already checked that type args or bindings exist"); + (false, first_generic_span.shrink_to_lo(), anon_lt_suggestion) + }; + self.sess.buffer_lint_with_diagnostic( + ELIDED_LIFETIMES_IN_PATHS, + CRATE_NODE_ID, + path_span, + "hidden lifetime parameters are deprecated", + builtin::BuiltinLintDiagnostics::ElidedLifetimesInPaths( + expected_lifetimes, path_span, incl_angl_brckt, insertion_span, suggestion + ) + ); + } } hir::PathSegment::new( diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index a46b312062247..72365a3a244ef 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -400,6 +400,7 @@ pub enum BuiltinLintDiagnostics { AbsPathWithModule(Span), DuplicatedMacroExports(ast::Ident, Span, Span), ProcMacroDeriveResolutionFallback(Span), + ElidedLifetimesInPaths(usize, Span, bool, Span, String), } impl BuiltinLintDiagnostics { @@ -440,6 +441,30 @@ impl BuiltinLintDiagnostics { db.span_label(span, "names from parent modules are not \ accessible without an explicit import"); } + BuiltinLintDiagnostics::ElidedLifetimesInPaths( + n, path_span, incl_angl_brckt, insertion_span, anon_lts + ) => { + let (replace_span, suggestion) = if incl_angl_brckt { + (insertion_span, anon_lts) + } else { + // If our codemap can be trusted, construct the entire `Path<'_, T>` source + // suggestion + if let Ok(snippet) = sess.codemap().span_to_snippet(path_span) { + let (before, after) = snippet.split_at( + (insertion_span.lo().0 - path_span.lo().0) as usize + ); + (path_span, format!("{}{}{}", before, anon_lts, after)) + } else { + (insertion_span, anon_lts) + } + }; + db.span_suggestion_with_applicability( + replace_span, + &format!("indicate the anonymous lifetime{}", if n >= 2 { "s" } else { "" }), + suggestion, + Applicability::MachineApplicable + ); + } } } } diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 05a6cd9c243d2..2f85b07c43ceb 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -609,7 +609,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // resolved the same as the `'_` in `&'_ Foo`. // // cc #48468 - self.resolve_elided_lifetimes(vec![lifetime], false) + self.resolve_elided_lifetimes(vec![lifetime]) } LifetimeName::Param(_) | LifetimeName::Static => { // If the user wrote an explicit name, use that. @@ -857,7 +857,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { if lifetime_ref.is_elided() { - self.resolve_elided_lifetimes(vec![lifetime_ref], false); + self.resolve_elided_lifetimes(vec![lifetime_ref]); return; } if lifetime_ref.is_static() { @@ -1691,7 +1691,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { _ => None, }).collect(); if elide_lifetimes { - self.resolve_elided_lifetimes(lifetimes, true); + self.resolve_elided_lifetimes(lifetimes); } else { lifetimes.iter().for_each(|lt| self.visit_lifetime(lt)); } @@ -2069,26 +2069,14 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } fn resolve_elided_lifetimes(&mut self, - lifetime_refs: Vec<&'tcx hir::Lifetime>, - deprecated: bool) { + lifetime_refs: Vec<&'tcx hir::Lifetime>) { if lifetime_refs.is_empty() { return; } let span = lifetime_refs[0].span; - let id = lifetime_refs[0].id; let mut late_depth = 0; let mut scope = self.scope; - if deprecated { - self.tcx - .struct_span_lint_node( - lint::builtin::ELIDED_LIFETIMES_IN_PATHS, - id, - span, - &format!("hidden lifetime parameters are deprecated, try `Foo<'_>`"), - ) - .emit(); - } let error = loop { match *scope { // Do not assign any resolution, it will be inferred. diff --git a/src/test/ui/in-band-lifetimes/elided-lifetimes.rs b/src/test/ui/in-band-lifetimes/elided-lifetimes.rs new file mode 100644 index 0000000000000..092ce1cd016af --- /dev/null +++ b/src/test/ui/in-band-lifetimes/elided-lifetimes.rs @@ -0,0 +1,86 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// run-rustfix +// compile-flags: --edition 2018 + +#![allow(unused)] +#![deny(elided_lifetimes_in_paths)] +//~^ NOTE lint level defined here + +use std::cell::{RefCell, Ref}; + + +struct Foo<'a> { x: &'a u32 } + +fn foo(x: &Foo) { + //~^ ERROR hidden lifetime parameters in types are deprecated + //~| HELP indicate the anonymous lifetime +} + +fn bar(x: &Foo<'_>) {} + + +struct Wrapped<'a>(&'a str); + +struct WrappedWithBow<'a> { + gift: &'a str +} + +fn wrap_gift(gift: &str) -> Wrapped { + //~^ ERROR hidden lifetime parameters in types are deprecated + //~| HELP indicate the anonymous lifetime + Wrapped(gift) +} + +fn wrap_gift_with_bow(gift: &str) -> WrappedWithBow { + //~^ ERROR hidden lifetime parameters in types are deprecated + //~| HELP indicate the anonymous lifetime + WrappedWithBow { gift } +} + +macro_rules! autowrapper { + ($type_name:ident, $fn_name:ident, $lt:lifetime) => { + struct $type_name<$lt> { + gift: &$lt str + } + + fn $fn_name(gift: &str) -> $type_name { + //~^ ERROR hidden lifetime parameters in types are deprecated + //~| HELP indicate the anonymous lifetime + $type_name { gift } + } + } +} + +autowrapper!(Autowrapped, autowrap_gift, '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 + } +} + +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 + let generosity = Ref::map(loyalty, |t| &t.0); + + let laughter = RefCell::new((true, "magic")); + let yellow: anytuple_ref_ty!(bool, &str) = laughter.borrow(); + //~^ NOTE in this expansion of anytuple_ref_ty! + //~| NOTE in this expansion of anytuple_ref_ty! +} diff --git a/src/test/ui/in-band-lifetimes/ellided-lifetimes.rs b/src/test/ui/in-band-lifetimes/ellided-lifetimes.rs deleted file mode 100644 index 3739ffe6a2648..0000000000000 --- a/src/test/ui/in-band-lifetimes/ellided-lifetimes.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2018 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -#![allow(warnings)] -#![allow(unused_variables, dead_code, unused, bad_style)] -#![deny(elided_lifetimes_in_paths)] - -struct Foo<'a> { x: &'a u32 } -fn foo(x: &Foo) { - //~^ ERROR: hidden lifetime parameters are deprecated, try `Foo<'_>` -} - -fn main() {} diff --git a/src/test/ui/in-band-lifetimes/ellided-lifetimes.stderr b/src/test/ui/in-band-lifetimes/ellided-lifetimes.stderr deleted file mode 100644 index c2bd2c261ac46..0000000000000 --- a/src/test/ui/in-band-lifetimes/ellided-lifetimes.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: hidden lifetime parameters are deprecated, try `Foo<'_>` - --> $DIR/ellided-lifetimes.rs:15:12 - | -LL | fn foo(x: &Foo) { - | ^^^ - | -note: lint level defined here - --> $DIR/ellided-lifetimes.rs:12:9 - | -LL | #![deny(elided_lifetimes_in_paths)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error -