From c6e13bc20b5664ba9a8bd4152412bcc497e4f041 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Mon, 29 Apr 2019 17:45:22 -0700 Subject: [PATCH] Disallow non-explicit elided lifetimes in async fn --- src/librustc/error_codes.rs | 5 +- src/librustc/hir/lowering.rs | 68 ++++++++++++---- src/librustc/lint/builtin.rs | 80 ++++++++++++------- src/test/ui/async-fn-path-elision.rs | 16 ++++ src/test/ui/async-fn-path-elision.stderr | 8 ++ .../path-elided.rs | 2 +- .../path-elided.stderr | 5 +- .../trait-elided.rs | 2 +- .../trait-elided.stderr | 5 +- src/test/ui/issues/issue-10412.rs | 3 +- src/test/ui/issues/issue-10412.stderr | 21 +++-- 11 files changed, 150 insertions(+), 65 deletions(-) create mode 100644 src/test/ui/async-fn-path-elision.rs create mode 100644 src/test/ui/async-fn-path-elision.stderr diff --git a/src/librustc/error_codes.rs b/src/librustc/error_codes.rs index 7f68b35d014..fd089fc688e 100644 --- a/src/librustc/error_codes.rs +++ b/src/librustc/error_codes.rs @@ -362,10 +362,6 @@ struct Foo1 { x: &bool } // ^ expected lifetime parameter struct Foo2<'a> { x: &'a bool } // correct -impl Foo2 {} - // ^^^^ expected lifetime parameter -impl<'a> Foo2<'a> {} // correct - struct Bar1 { x: Foo2 } // ^^^^ expected lifetime parameter struct Bar2<'a> { x: Foo2<'a> } // correct @@ -2208,4 +2204,5 @@ trait Foo { } E0710, // an unknown tool name found in scoped lint E0711, // a feature has been declared with conflicting stability attributes // E0702, // replaced with a generic attribute input check + E0726, // non-explicit (not `'_`) elided lifetime in unsupported position } diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index df455a725c5..0587b978105 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -2110,15 +2110,49 @@ fn lower_path_segment( .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 - ) - ); + 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 // note lack of '_ + // async fn foo(_: std::cell::Ref) { ... } + AnonymousLifetimeMode::CreateParameter => { + let mut err = struct_span_err!( + self.sess, + path_span, + E0726, + "implicit elided lifetime not allowed here" + ); + crate::lint::builtin::add_elided_lifetime_in_path_suggestion( + &self.sess, + &mut err, + expected_lifetimes, + path_span, + incl_angl_brckt, + insertion_span, + suggestion, + ); + err.emit(); + } + AnonymousLifetimeMode::PassThrough | + AnonymousLifetimeMode::ReportError | + AnonymousLifetimeMode::Replace(_) => { + 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, + ) + ); + } + } } } @@ -5298,13 +5332,15 @@ fn elided_path_lifetimes(&mut self, span: Span, count: usize) -> P<[hir::Lifetim fn elided_path_lifetime(&mut self, span: Span) -> hir::Lifetime { match self.anonymous_lifetime_mode { - // N.B., We intentionally ignore the create-parameter mode here - // and instead "pass through" to resolve-lifetimes, which will then - // report an error. This is because we don't want to support - // impl elision for deprecated forms like - // - // impl Foo for std::cell::Ref // note lack of '_ - AnonymousLifetimeMode::CreateParameter | + AnonymousLifetimeMode::CreateParameter => { + // We should have emitted E0726 when processing this path above + self.sess.delay_span_bug( + span, + "expected 'implicit elided lifetime not allowed' error", + ); + let id = self.sess.next_node_id(); + self.new_named_lifetime(id, span, hir::LifetimeName::Error) + } // This is the normal case. AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span), diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 28a2a1eaf6b..a9481cb32a1 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -477,6 +477,48 @@ pub enum BuiltinLintDiagnostics { RedundantImport(Vec<(Span, bool)>, ast::Ident), } +pub(crate) fn add_elided_lifetime_in_path_suggestion( + sess: &Session, + db: &mut DiagnosticBuilder<'_>, + n: usize, + path_span: Span, + incl_angl_brckt: bool, + insertion_span: Span, + anon_lts: String, +) { + 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.source_map().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( + replace_span, + &format!("indicate the anonymous lifetime{}", if n >= 2 { "s" } else { "" }), + suggestion, + Applicability::MachineApplicable + ); +} + impl BuiltinLintDiagnostics { pub fn run(self, sess: &Session, db: &mut DiagnosticBuilder<'_>) { match self { @@ -521,36 +563,14 @@ pub fn run(self, sess: &Session, db: &mut DiagnosticBuilder<'_>) { 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.source_map().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( - replace_span, - &format!("indicate the anonymous lifetime{}", if n >= 2 { "s" } else { "" }), - suggestion, - Applicability::MachineApplicable + add_elided_lifetime_in_path_suggestion( + sess, + db, + n, + path_span, + incl_angl_brckt, + insertion_span, + anon_lts, ); } BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => { diff --git a/src/test/ui/async-fn-path-elision.rs b/src/test/ui/async-fn-path-elision.rs new file mode 100644 index 00000000000..8db7631ef41 --- /dev/null +++ b/src/test/ui/async-fn-path-elision.rs @@ -0,0 +1,16 @@ +// edition:2018 + +#![feature(async_await, await_macro)] +#![allow(dead_code)] + +struct HasLifetime<'a>(&'a bool); + +async fn error(lt: HasLifetime) { //~ ERROR implicit elided lifetime not allowed here + if *lt.0 {} +} + +fn no_error(lt: HasLifetime) { + if *lt.0 {} +} + +fn main() {} diff --git a/src/test/ui/async-fn-path-elision.stderr b/src/test/ui/async-fn-path-elision.stderr new file mode 100644 index 00000000000..3b311baba01 --- /dev/null +++ b/src/test/ui/async-fn-path-elision.stderr @@ -0,0 +1,8 @@ +error[E0726]: implicit elided lifetime not allowed here + --> $DIR/async-fn-path-elision.rs:8:20 + | +LL | async fn error(lt: HasLifetime) { + | ^^^^^^^^^^^- help: indicate the anonymous lifetime: `<'_>` + +error: aborting due to previous error + diff --git a/src/test/ui/impl-header-lifetime-elision/path-elided.rs b/src/test/ui/impl-header-lifetime-elision/path-elided.rs index 6532b0aebe7..40a52efc7f9 100644 --- a/src/test/ui/impl-header-lifetime-elision/path-elided.rs +++ b/src/test/ui/impl-header-lifetime-elision/path-elided.rs @@ -5,7 +5,7 @@ trait MyTrait { } struct Foo<'a> { x: &'a u32 } impl MyTrait for Foo { - //~^ ERROR missing lifetime specifier + //~^ ERROR implicit elided lifetime not allowed here } fn main() {} diff --git a/src/test/ui/impl-header-lifetime-elision/path-elided.stderr b/src/test/ui/impl-header-lifetime-elision/path-elided.stderr index c9287ffba96..6500a2a55f6 100644 --- a/src/test/ui/impl-header-lifetime-elision/path-elided.stderr +++ b/src/test/ui/impl-header-lifetime-elision/path-elided.stderr @@ -1,9 +1,8 @@ -error[E0106]: missing lifetime specifier +error[E0726]: implicit elided lifetime not allowed here --> $DIR/path-elided.rs:7:18 | LL | impl MyTrait for Foo { - | ^^^ expected lifetime parameter + | ^^^- help: indicate the anonymous lifetime: `<'_>` error: aborting due to previous error -For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/impl-header-lifetime-elision/trait-elided.rs b/src/test/ui/impl-header-lifetime-elision/trait-elided.rs index 3979eda740a..102d259b0c8 100644 --- a/src/test/ui/impl-header-lifetime-elision/trait-elided.rs +++ b/src/test/ui/impl-header-lifetime-elision/trait-elided.rs @@ -3,7 +3,7 @@ trait MyTrait<'a> { } impl MyTrait for u32 { - //~^ ERROR missing lifetime specifier + //~^ ERROR implicit elided lifetime not allowed here } fn main() {} diff --git a/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr b/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr index b742db1c75c..ad97cb0abd6 100644 --- a/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr +++ b/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr @@ -1,9 +1,8 @@ -error[E0106]: missing lifetime specifier +error[E0726]: implicit elided lifetime not allowed here --> $DIR/trait-elided.rs:5:6 | LL | impl MyTrait for u32 { - | ^^^^^^^ expected lifetime parameter + | ^^^^^^^- help: indicate the anonymous lifetime: `<'_>` error: aborting due to previous error -For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/issues/issue-10412.rs b/src/test/ui/issues/issue-10412.rs index a0bc2fc2f3c..02058513685 100644 --- a/src/test/ui/issues/issue-10412.rs +++ b/src/test/ui/issues/issue-10412.rs @@ -5,7 +5,8 @@ trait Serializable<'self, T> { //~ ERROR lifetimes cannot use keyword names impl<'self> Serializable for &'self str { //~ ERROR lifetimes cannot use keyword names //~^ ERROR lifetimes cannot use keyword names - //~| ERROR missing lifetime specifier + //~| ERROR implicit elided lifetime not allowed here + //~| ERROR the size for values of type `str` cannot be known at compilation time fn serialize(val : &'self str) -> Vec { //~ ERROR lifetimes cannot use keyword names vec![1] } diff --git a/src/test/ui/issues/issue-10412.stderr b/src/test/ui/issues/issue-10412.stderr index 48920813ae1..0793dd99b4d 100644 --- a/src/test/ui/issues/issue-10412.stderr +++ b/src/test/ui/issues/issue-10412.stderr @@ -29,23 +29,32 @@ LL | impl<'self> Serializable for &'self str { | ^^^^^ error: lifetimes cannot use keyword names - --> $DIR/issue-10412.rs:9:25 + --> $DIR/issue-10412.rs:10:25 | LL | fn serialize(val : &'self str) -> Vec { | ^^^^^ error: lifetimes cannot use keyword names - --> $DIR/issue-10412.rs:12:37 + --> $DIR/issue-10412.rs:13:37 | LL | fn deserialize(repr: &[u8]) -> &'self str { | ^^^^^ -error[E0106]: missing lifetime specifier +error[E0726]: implicit elided lifetime not allowed here --> $DIR/issue-10412.rs:6:13 | LL | impl<'self> Serializable for &'self str { - | ^^^^^^^^^^^^^^^^^ expected lifetime parameter + | ^^^^^^^^^^^^^^^^^ help: indicate the anonymous lifetime: `Serializable<'_, str>` -error: aborting due to 8 previous errors +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/issue-10412.rs:6:13 + | +LL | impl<'self> Serializable for &'self str { + | ^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `str` + = note: to learn more, visit + +error: aborting due to 9 previous errors -For more information about this error, try `rustc --explain E0106`. +For more information about this error, try `rustc --explain E0277`. -- 2.44.0