Skip to content

Commit

Permalink
Disallow non-explicit elided lifetimes in async fn
Browse files Browse the repository at this point in the history
  • Loading branch information
cramertj committed May 2, 2019
1 parent 00859e3 commit c6e13bc
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 65 deletions.
5 changes: 1 addition & 4 deletions src/librustc/error_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -2208,4 +2204,5 @@ register_diagnostics! {
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
}
68 changes: 52 additions & 16 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2110,15 +2110,49 @@ impl<'a> LoweringContext<'a> {
.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<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"
);
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,
)
);
}
}
}
}

Expand Down Expand Up @@ -5298,13 +5332,15 @@ impl<'a> LoweringContext<'a> {

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<u32> // 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),

Expand Down
80 changes: 50 additions & 30 deletions src/librustc/lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>` 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 {
Expand Down Expand Up @@ -521,36 +563,14 @@ impl BuiltinLintDiagnostics {
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<T>` 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) => {
Expand Down
16 changes: 16 additions & 0 deletions src/test/ui/async-fn-path-elision.rs
Original file line number Diff line number Diff line change
@@ -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() {}
8 changes: 8 additions & 0 deletions src/test/ui/async-fn-path-elision.stderr
Original file line number Diff line number Diff line change
@@ -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

2 changes: 1 addition & 1 deletion src/test/ui/impl-header-lifetime-elision/path-elided.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
5 changes: 2 additions & 3 deletions src/test/ui/impl-header-lifetime-elision/path-elided.stderr
Original file line number Diff line number Diff line change
@@ -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`.
2 changes: 1 addition & 1 deletion src/test/ui/impl-header-lifetime-elision/trait-elided.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
trait MyTrait<'a> { }

impl MyTrait for u32 {
//~^ ERROR missing lifetime specifier
//~^ ERROR implicit elided lifetime not allowed here
}

fn main() {}
5 changes: 2 additions & 3 deletions src/test/ui/impl-header-lifetime-elision/trait-elided.stderr
Original file line number Diff line number Diff line change
@@ -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`.
3 changes: 2 additions & 1 deletion src/test/ui/issues/issue-10412.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ trait Serializable<'self, T> { //~ ERROR lifetimes cannot use keyword names

impl<'self> Serializable<str> 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<u8> { //~ ERROR lifetimes cannot use keyword names
vec![1]
}
Expand Down
21 changes: 15 additions & 6 deletions src/test/ui/issues/issue-10412.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,32 @@ LL | impl<'self> Serializable<str> 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<u8> {
| ^^^^^

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<str> 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<str> 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 <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>

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`.

0 comments on commit c6e13bc

Please sign in to comment.