From f0906f5291ad2faad12b82621a6b326bf765014f Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 11 Apr 2024 13:15:34 +0000 Subject: [PATCH] Error on using `yield` without also using `gen` on the closure And suggest adding the `gen` to the closure or function --- compiler/rustc_ast_lowering/messages.ftl | 3 ++ compiler/rustc_ast_lowering/src/errors.rs | 9 ++++ compiler/rustc_ast_lowering/src/expr.rs | 6 +++ compiler/rustc_ast_lowering/src/item.rs | 2 +- ...async-outside-of-await-issue-121096.stderr | 14 +++-- .../incorrect-syntax-suggestions.stderr | 52 ++++++++++++------- .../ui/async-await/issues/issue-51751.stderr | 12 +++-- .../async-await/issues/issue-62009-1.stderr | 44 +++++++++++----- .../async-await/issues/issue-62009-2.stderr | 10 ++-- .../issues/non-async-enclosing-span.stderr | 12 +++-- tests/ui/coroutine/gen_block.e2024.stderr | 13 ++++- tests/ui/coroutine/gen_block.rs | 1 + tests/ui/coroutine/missing_gen_in_2024.fixed | 16 ++++++ tests/ui/coroutine/missing_gen_in_2024.rs | 16 ++++++ tests/ui/coroutine/missing_gen_in_2024.stderr | 31 +++++++++++ .../feature-gate-coroutines.e2024.stderr | 26 +++++++++- .../feature-gate-coroutines.none.stderr | 8 +-- .../feature-gates/feature-gate-coroutines.rs | 2 + tests/ui/sanitizer/cfi-coroutine.rs | 2 +- 19 files changed, 217 insertions(+), 62 deletions(-) create mode 100644 tests/ui/coroutine/missing_gen_in_2024.fixed create mode 100644 tests/ui/coroutine/missing_gen_in_2024.rs create mode 100644 tests/ui/coroutine/missing_gen_in_2024.stderr diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index d91d65497e1c1..7f31c69d28524 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -161,3 +161,6 @@ ast_lowering_underscore_expr_lhs_assign = .label = `_` not allowed here ast_lowering_use_angle_brackets = use angle brackets instead +ast_lowering_yield_in_closure = + `yield` can only be used in `gen` closures or blocks + .suggestion = use `gen` to make the closure a coroutine diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 6fd980ed3ca96..be7b058ba4a76 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -414,3 +414,12 @@ pub(crate) struct AsyncBoundOnlyForFnTraits { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(ast_lowering_yield_in_closure)] +pub(crate) struct YieldInClosure { + #[primary_span] + pub span: Span, + #[suggestion(code = "gen ", applicability = "maybe-incorrect", style = "verbose")] + pub suggestion: Option, +} diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 3bbd4514a343c..60d32db192a2f 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -8,6 +8,7 @@ use super::errors::{ }; use super::ResolverAstLoweringExt; use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs}; +use crate::errors::YieldInClosure; use crate::{FnDeclKind, ImplTraitPosition}; use rustc_ast::ptr::P as AstP; use rustc_ast::*; @@ -1578,7 +1579,12 @@ impl<'hir> LoweringContext<'_, 'hir> { ) .emit(); } + if self.tcx.sess.edition().at_least_rust_2024() { + let suggestion = self.current_item.map(|s| s.shrink_to_lo()); + self.dcx().emit_err(YieldInClosure { span, suggestion }); + } self.coroutine_kind = Some(hir::CoroutineKind::Coroutine(Movability::Movable)); + false } }; diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index abfea6078f21c..59c6a80340989 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -203,7 +203,7 @@ impl<'hir> LoweringContext<'_, 'hir> { body, .. }) => { - self.with_new_scopes(ident.span, |this| { + self.with_new_scopes(span, |this| { // Note: we don't need to change the return type from `T` to // `impl Future` here because lower_body // only cares about the input argument patterns in the function diff --git a/tests/ui/async-await/async-outside-of-await-issue-121096.stderr b/tests/ui/async-await/async-outside-of-await-issue-121096.stderr index b0677a83864e0..af902a150f38c 100644 --- a/tests/ui/async-await/async-outside-of-await-issue-121096.stderr +++ b/tests/ui/async-await/async-outside-of-await-issue-121096.stderr @@ -1,11 +1,15 @@ error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/async-outside-of-await-issue-121096.rs:7:7 | -LL | fn main() { - | ---- this is not `async` -... -LL | }.await - | ^^^^^ only allowed inside `async` functions and blocks +LL | / fn main() { +LL | | async { +LL | | use std::ops::Add; +LL | | let _ = 1.add(3); +LL | | }.await + | | ^^^^^ only allowed inside `async` functions and blocks +LL | | +LL | | } + | |_- this is not `async` error: aborting due to 1 previous error diff --git a/tests/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr b/tests/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr index 928eb0b821db1..464b429a8462b 100644 --- a/tests/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr +++ b/tests/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr @@ -140,42 +140,54 @@ LL | } error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/incorrect-syntax-suggestions.rs:68:19 | -LL | fn foo13() -> Result<(), ()> { - | ----- this is not `async` -LL | let _ = bar().await(); - | ^^^^^ only allowed inside `async` functions and blocks +LL | / fn foo13() -> Result<(), ()> { +LL | | let _ = bar().await(); + | | ^^^^^ only allowed inside `async` functions and blocks +LL | | +LL | | Ok(()) +LL | | } + | |_- this is not `async` error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/incorrect-syntax-suggestions.rs:73:19 | -LL | fn foo14() -> Result<(), ()> { - | ----- this is not `async` -LL | let _ = bar().await()?; - | ^^^^^ only allowed inside `async` functions and blocks +LL | / fn foo14() -> Result<(), ()> { +LL | | let _ = bar().await()?; + | | ^^^^^ only allowed inside `async` functions and blocks +LL | | +LL | | Ok(()) +LL | | } + | |_- this is not `async` error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/incorrect-syntax-suggestions.rs:78:19 | -LL | fn foo15() -> Result<(), ()> { - | ----- this is not `async` -LL | let _ = bar().await; - | ^^^^^ only allowed inside `async` functions and blocks +LL | / fn foo15() -> Result<(), ()> { +LL | | let _ = bar().await; + | | ^^^^^ only allowed inside `async` functions and blocks +LL | | Ok(()) +LL | | } + | |_- this is not `async` error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/incorrect-syntax-suggestions.rs:82:19 | -LL | fn foo16() -> Result<(), ()> { - | ----- this is not `async` -LL | let _ = bar().await?; - | ^^^^^ only allowed inside `async` functions and blocks +LL | / fn foo16() -> Result<(), ()> { +LL | | let _ = bar().await?; + | | ^^^^^ only allowed inside `async` functions and blocks +LL | | Ok(()) +LL | | } + | |_- this is not `async` error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/incorrect-syntax-suggestions.rs:87:23 | -LL | fn foo() -> Result<(), ()> { - | --- this is not `async` -LL | let _ = bar().await?; - | ^^^^^ only allowed inside `async` functions and blocks +LL | / fn foo() -> Result<(), ()> { +LL | | let _ = bar().await?; + | | ^^^^^ only allowed inside `async` functions and blocks +LL | | Ok(()) +LL | | } + | |_____- this is not `async` error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/incorrect-syntax-suggestions.rs:94:23 diff --git a/tests/ui/async-await/issues/issue-51751.stderr b/tests/ui/async-await/issues/issue-51751.stderr index ba256b19948c0..2801ae5c3ddf6 100644 --- a/tests/ui/async-await/issues/issue-51751.stderr +++ b/tests/ui/async-await/issues/issue-51751.stderr @@ -1,11 +1,13 @@ error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/issue-51751.rs:9:27 | -LL | fn main() { - | ---- this is not `async` -LL | let result = inc(10000); -LL | let finished = result.await; - | ^^^^^ only allowed inside `async` functions and blocks +LL | / fn main() { +LL | | let result = inc(10000); +LL | | let finished = result.await; + | | ^^^^^ only allowed inside `async` functions and blocks +LL | | +LL | | } + | |_- this is not `async` error: aborting due to 1 previous error diff --git a/tests/ui/async-await/issues/issue-62009-1.stderr b/tests/ui/async-await/issues/issue-62009-1.stderr index 02933f4f2f233..92d06fca91d56 100644 --- a/tests/ui/async-await/issues/issue-62009-1.stderr +++ b/tests/ui/async-await/issues/issue-62009-1.stderr @@ -1,28 +1,44 @@ error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/issue-62009-1.rs:6:23 | -LL | fn main() { - | ---- this is not `async` -LL | async { let (); }.await; - | ^^^^^ only allowed inside `async` functions and blocks +LL | / fn main() { +LL | | async { let (); }.await; + | | ^^^^^ only allowed inside `async` functions and blocks +LL | | +LL | | async { +... | +LL | | +LL | | } + | |_- this is not `async` error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/issue-62009-1.rs:10:7 | -LL | fn main() { - | ---- this is not `async` -... -LL | }.await; - | ^^^^^ only allowed inside `async` functions and blocks +LL | / fn main() { +LL | | async { let (); }.await; +LL | | +LL | | async { +LL | | let task1 = print_dur().await; +LL | | }.await; + | | ^^^^^ only allowed inside `async` functions and blocks +... | +LL | | +LL | | } + | |_- this is not `async` error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/issue-62009-1.rs:12:16 | -LL | fn main() { - | ---- this is not `async` -... -LL | (|_| 2333).await; - | ^^^^^ only allowed inside `async` functions and blocks +LL | / fn main() { +LL | | async { let (); }.await; +LL | | +LL | | async { +... | +LL | | (|_| 2333).await; + | | ^^^^^ only allowed inside `async` functions and blocks +LL | | +LL | | } + | |_- this is not `async` error: aborting due to 3 previous errors diff --git a/tests/ui/async-await/issues/issue-62009-2.stderr b/tests/ui/async-await/issues/issue-62009-2.stderr index 80a831cc5475f..d1eaeb11e84be 100644 --- a/tests/ui/async-await/issues/issue-62009-2.stderr +++ b/tests/ui/async-await/issues/issue-62009-2.stderr @@ -1,10 +1,12 @@ error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/issue-62009-2.rs:8:23 | -LL | fn main() { - | ---- this is not `async` -LL | (async || 2333)().await; - | ^^^^^ only allowed inside `async` functions and blocks +LL | / fn main() { +LL | | (async || 2333)().await; + | | ^^^^^ only allowed inside `async` functions and blocks +LL | | +LL | | } + | |_- this is not `async` error: aborting due to 1 previous error diff --git a/tests/ui/async-await/issues/non-async-enclosing-span.stderr b/tests/ui/async-await/issues/non-async-enclosing-span.stderr index 91a9e5aa6cabf..63de483e2d74a 100644 --- a/tests/ui/async-await/issues/non-async-enclosing-span.stderr +++ b/tests/ui/async-await/issues/non-async-enclosing-span.stderr @@ -1,11 +1,13 @@ error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/non-async-enclosing-span.rs:9:28 | -LL | fn main() { - | ---- this is not `async` -LL | let x = move || {}; -LL | let y = do_the_thing().await; - | ^^^^^ only allowed inside `async` functions and blocks +LL | / fn main() { +LL | | let x = move || {}; +LL | | let y = do_the_thing().await; + | | ^^^^^ only allowed inside `async` functions and blocks +LL | | +LL | | } + | |_- this is not `async` error: aborting due to 1 previous error diff --git a/tests/ui/coroutine/gen_block.e2024.stderr b/tests/ui/coroutine/gen_block.e2024.stderr index 2b9eb4a820b65..55cf43462eaec 100644 --- a/tests/ui/coroutine/gen_block.e2024.stderr +++ b/tests/ui/coroutine/gen_block.e2024.stderr @@ -8,13 +8,24 @@ LL | let _ = || yield true; = help: add `#![feature(coroutines)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error: `yield` can only be used in `gen` closures or blocks + --> $DIR/gen_block.rs:15:16 + | +LL | let _ = || yield true; + | ^^^^^^^^^^ + | +help: use `gen` to make the closure a coroutine + | +LL | let _ = gen || yield true; + | +++ + error[E0282]: type annotations needed --> $DIR/gen_block.rs:6:13 | LL | let x = gen {}; | ^^^^^^ cannot infer type -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0282, E0658. For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/coroutine/gen_block.rs b/tests/ui/coroutine/gen_block.rs index f6a775aa66199..0cd518744737d 100644 --- a/tests/ui/coroutine/gen_block.rs +++ b/tests/ui/coroutine/gen_block.rs @@ -14,4 +14,5 @@ fn main() { let _ = || yield true; //[none]~ ERROR yield syntax is experimental //~^ ERROR yield syntax is experimental + //[e2024]~^^ ERROR `yield` can only be used in `gen` closures or blocks } diff --git a/tests/ui/coroutine/missing_gen_in_2024.fixed b/tests/ui/coroutine/missing_gen_in_2024.fixed new file mode 100644 index 0000000000000..cd5c1912312a7 --- /dev/null +++ b/tests/ui/coroutine/missing_gen_in_2024.fixed @@ -0,0 +1,16 @@ +//@ edition: 2024 +//@ compile-flags: -Zunstable-options +//@ run-rustfix + +#![feature(coroutines, gen_blocks)] + +fn main() { + let _ = gen || yield; + //~^ ERROR `yield` can only be used in `gen` closures or blocks +} + +gen fn _foo() { + yield; + //~^ ERROR `yield` can only be used in `gen` closures or blocks + //~| ERROR yield expression outside of coroutine literal +} diff --git a/tests/ui/coroutine/missing_gen_in_2024.rs b/tests/ui/coroutine/missing_gen_in_2024.rs new file mode 100644 index 0000000000000..f718b2ff045f3 --- /dev/null +++ b/tests/ui/coroutine/missing_gen_in_2024.rs @@ -0,0 +1,16 @@ +//@ edition: 2024 +//@ compile-flags: -Zunstable-options +//@ run-rustfix + +#![feature(coroutines, gen_blocks)] + +fn main() { + let _ = || yield; + //~^ ERROR `yield` can only be used in `gen` closures or blocks +} + +fn _foo() { + yield; + //~^ ERROR `yield` can only be used in `gen` closures or blocks + //~| ERROR yield expression outside of coroutine literal +} diff --git a/tests/ui/coroutine/missing_gen_in_2024.stderr b/tests/ui/coroutine/missing_gen_in_2024.stderr new file mode 100644 index 0000000000000..dcb9e1d4f8704 --- /dev/null +++ b/tests/ui/coroutine/missing_gen_in_2024.stderr @@ -0,0 +1,31 @@ +error: `yield` can only be used in `gen` closures or blocks + --> $DIR/missing_gen_in_2024.rs:8:16 + | +LL | let _ = || yield; + | ^^^^^ + | +help: use `gen` to make the closure a coroutine + | +LL | let _ = gen || yield; + | +++ + +error: `yield` can only be used in `gen` closures or blocks + --> $DIR/missing_gen_in_2024.rs:13:5 + | +LL | yield; + | ^^^^^ + | +help: use `gen` to make the closure a coroutine + | +LL | gen fn _foo() { + | +++ + +error[E0627]: yield expression outside of coroutine literal + --> $DIR/missing_gen_in_2024.rs:13:5 + | +LL | yield; + | ^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0627`. diff --git a/tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr b/tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr index 1cef163cef5c2..2badd5560496f 100644 --- a/tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr +++ b/tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr @@ -8,8 +8,19 @@ LL | yield true; = help: add `#![feature(coroutines)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error: `yield` can only be used in `gen` closures or blocks + --> $DIR/feature-gate-coroutines.rs:5:5 + | +LL | yield true; + | ^^^^^^^^^^ + | +help: use `gen` to make the closure a coroutine + | +LL | gen fn main() { + | +++ + error[E0658]: yield syntax is experimental - --> $DIR/feature-gate-coroutines.rs:9:16 + --> $DIR/feature-gate-coroutines.rs:10:16 | LL | let _ = || yield true; | ^^^^^^^^^^ @@ -18,13 +29,24 @@ LL | let _ = || yield true; = help: add `#![feature(coroutines)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error: `yield` can only be used in `gen` closures or blocks + --> $DIR/feature-gate-coroutines.rs:10:16 + | +LL | let _ = || yield true; + | ^^^^^^^^^^ + | +help: use `gen` to make the closure a coroutine + | +LL | let _ = gen || yield true; + | +++ + error[E0627]: yield expression outside of coroutine literal --> $DIR/feature-gate-coroutines.rs:5:5 | LL | yield true; | ^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0627, E0658. For more information about an error, try `rustc --explain E0627`. diff --git a/tests/ui/feature-gates/feature-gate-coroutines.none.stderr b/tests/ui/feature-gates/feature-gate-coroutines.none.stderr index 403f0549aef27..662cdfd81d1f4 100644 --- a/tests/ui/feature-gates/feature-gate-coroutines.none.stderr +++ b/tests/ui/feature-gates/feature-gate-coroutines.none.stderr @@ -9,7 +9,7 @@ LL | yield true; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental - --> $DIR/feature-gate-coroutines.rs:9:16 + --> $DIR/feature-gate-coroutines.rs:10:16 | LL | let _ = || yield true; | ^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | let _ = || yield true; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental - --> $DIR/feature-gate-coroutines.rs:16:5 + --> $DIR/feature-gate-coroutines.rs:18:5 | LL | yield; | ^^^^^ @@ -29,7 +29,7 @@ LL | yield; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: yield syntax is experimental - --> $DIR/feature-gate-coroutines.rs:17:5 + --> $DIR/feature-gate-coroutines.rs:19:5 | LL | yield 0; | ^^^^^^^ @@ -50,7 +50,7 @@ LL | yield true; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0658]: yield syntax is experimental - --> $DIR/feature-gate-coroutines.rs:9:16 + --> $DIR/feature-gate-coroutines.rs:10:16 | LL | let _ = || yield true; | ^^^^^^^^^^ diff --git a/tests/ui/feature-gates/feature-gate-coroutines.rs b/tests/ui/feature-gates/feature-gate-coroutines.rs index b3df2351b680b..b1098f44dc18e 100644 --- a/tests/ui/feature-gates/feature-gate-coroutines.rs +++ b/tests/ui/feature-gates/feature-gate-coroutines.rs @@ -5,9 +5,11 @@ fn main() { yield true; //~ ERROR yield syntax is experimental //~^ ERROR yield expression outside of coroutine literal //[none]~^^ ERROR yield syntax is experimental + //[e2024]~^^^ ERROR `yield` can only be used in `gen` closures or blocks let _ = || yield true; //~ ERROR yield syntax is experimental //[none]~^ ERROR yield syntax is experimental + //[e2024]~^^ ERROR `yield` can only be used in `gen` closures or blocks } #[cfg(FALSE)] diff --git a/tests/ui/sanitizer/cfi-coroutine.rs b/tests/ui/sanitizer/cfi-coroutine.rs index 5c6a489a7e89e..611826de92341 100644 --- a/tests/ui/sanitizer/cfi-coroutine.rs +++ b/tests/ui/sanitizer/cfi-coroutine.rs @@ -27,7 +27,7 @@ use std::async_iter::AsyncIterator; #[test] fn general_coroutine() { - let mut coro = |x: i32| { + let mut coro = gen |x: i32| { yield x; "done" };