Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disallow non-explicit elided lifetimes in async fn #60388

Merged
merged 1 commit into from
May 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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`.