diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 99db718bdf80f..c668dd433aa4d 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; @@ -1754,13 +1755,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 = vec!["'_"; expected_lifetimes].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(), format!("{}, ", anon_lt_suggestion)) + }; + self.sess.buffer_lint_with_diagnostic( + ELIDED_LIFETIMES_IN_PATHS, + CRATE_NODE_ID, + path_span, + "hidden lifetime parameters in types 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 0ab10bf223f07..47c5f464131af 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -254,7 +254,7 @@ declare_lint! { declare_lint! { pub ELIDED_LIFETIMES_IN_PATHS, Allow, - "hidden lifetime parameters are deprecated, try `Foo<'_>`" + "hidden lifetime parameters in types are deprecated" } declare_lint! { @@ -402,6 +402,7 @@ pub enum BuiltinLintDiagnostics { AbsPathWithModule(Span), DuplicatedMacroExports(ast::Ident, Span, Span), ProcMacroDeriveResolutionFallback(Span), + ElidedLifetimesInPaths(usize, Span, bool, Span, String), } impl BuiltinLintDiagnostics { @@ -442,6 +443,41 @@ 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 { + // When possible, prefer a suggestion that replaces the whole + // `Path` expression with `Path<'_, T>`, rather than inserting `'_, ` + // at a point (which makes for an ugly/confusing label) + if let Ok(snippet) = sess.codemap().span_to_snippet(path_span) { + // But our spans can get out of whack due to macros; if the place we think + // we want to insert `'_` isn't even within the path expression's span, we + // should bail out of making any suggestion rather than panicking on a + // subtract-with-overflow or string-slice-out-out-bounds (!) + // FIXME: can we do better? + if insertion_span.lo().0 < path_span.lo().0 { + return; + } + let insertion_index = (insertion_span.lo().0 - path_span.lo().0) as usize; + if insertion_index > snippet.len() { + return; + } + let (before, after) = snippet.split_at(insertion_index); + (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 39a57e985e89b..1309540717c2b 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. @@ -893,7 +893,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() { @@ -1728,7 +1728,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)); } @@ -2106,26 +2106,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/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index e875df0d22bec..cab51fbd98775 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -44,7 +44,8 @@ extern crate syntax_pos; use rustc::lint; use rustc::lint::{LateContext, LateLintPass, LintPass, LintArray}; -use rustc::lint::builtin::{BARE_TRAIT_OBJECTS, ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE}; +use rustc::lint::builtin::{BARE_TRAIT_OBJECTS, ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, + ELIDED_LIFETIMES_IN_PATHS}; use rustc::lint::builtin::MACRO_USE_EXTERN_CRATE; use rustc::session; use rustc::util; @@ -195,6 +196,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { UNREACHABLE_PUB, UNUSED_EXTERN_CRATES, MACRO_USE_EXTERN_CRATE, + ELIDED_LIFETIMES_IN_PATHS, ELLIPSIS_INCLUSIVE_RANGE_PATTERNS); // Guidelines for creating a future incompatibility lint: diff --git a/src/test/ui/in-band-lifetimes/elided-lifetimes.fixed b/src/test/ui/in-band-lifetimes/elided-lifetimes.fixed new file mode 100644 index 0000000000000..c9381e6350f2b --- /dev/null +++ b/src/test/ui/in-band-lifetimes/elided-lifetimes.fixed @@ -0,0 +1,97 @@ +// 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 +} + +struct MatchedSet<'a, 'b> { + one: &'a str, + another: &'b 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 } +} + +fn inspect_matched_set(set: MatchedSet<'_, '_>) { + //~^ ERROR hidden lifetime parameters in types are deprecated + //~| HELP indicate the anonymous lifetime + println!("{} {}", set.one, set.another); +} + +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/elided-lifetimes.rs b/src/test/ui/in-band-lifetimes/elided-lifetimes.rs new file mode 100644 index 0000000000000..8151dd01a98d8 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/elided-lifetimes.rs @@ -0,0 +1,97 @@ +// 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 +} + +struct MatchedSet<'a, 'b> { + one: &'a str, + another: &'b 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 } +} + +fn inspect_matched_set(set: MatchedSet) { + //~^ ERROR hidden lifetime parameters in types are deprecated + //~| HELP indicate the anonymous lifetime + println!("{} {}", set.one, set.another); +} + +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/elided-lifetimes.stderr b/src/test/ui/in-band-lifetimes/elided-lifetimes.stderr new file mode 100644 index 0000000000000..e7a1b42b8c9cc --- /dev/null +++ b/src/test/ui/in-band-lifetimes/elided-lifetimes.stderr @@ -0,0 +1,56 @@ +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetimes.rs:23:12 + | +LL | fn foo(x: &Foo) { + | ^^^- help: indicate the anonymous lifetime: `<'_>` + | +note: lint level defined here + --> $DIR/elided-lifetimes.rs:15:9 + | +LL | #![deny(elided_lifetimes_in_paths)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetimes.rs:42:29 + | +LL | fn wrap_gift(gift: &str) -> Wrapped { + | ^^^^^^^- help: indicate the anonymous lifetime: `<'_>` + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetimes.rs:48:38 + | +LL | fn wrap_gift_with_bow(gift: &str) -> WrappedWithBow { + | ^^^^^^^^^^^^^^- help: indicate the anonymous lifetime: `<'_>` + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetimes.rs:54:29 + | +LL | fn inspect_matched_set(set: MatchedSet) { + | ^^^^^^^^^^- help: indicate the anonymous lifetimes: `<'_, '_>` + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetimes.rs:66:36 + | +LL | fn $fn_name(gift: &str) -> $type_name { + | ^^^^^^^^^^- help: indicate the anonymous lifetime: `<'_>` +... +LL | autowrapper!(Autowrapped, autowrap_gift, 'a); + | --------------------------------------------- in this macro invocation + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetimes.rs:88:18 + | +LL | let loyalty: Ref<(u32, char)> = honesty.borrow(); + | ^^^^^^^^^^^^^^^^ help: indicate the anonymous lifetime: `Ref<'_, (u32, char)>` + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetimes.rs:80:9 + | +LL | Ref<($($types),*)> + | ^^^^^^^^^^^^^^^^^^ help: indicate the anonymous lifetime: `Ref<'_, ($($types),*)>` +... +LL | let yellow: anytuple_ref_ty!(bool, &str) = laughter.borrow(); + | ---------------------------- in this macro invocation + +error: aborting due to 7 previous errors + 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 -