From 226edf64fa58e591d8abc6afef5e38c15c181698 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 12 Dec 2023 09:55:50 +1100 Subject: Improve an error involving attribute values. Attribute values must be literals. The error you get when that doesn't hold is pretty bad, e.g.: ``` unexpected expression: 1 + 1 ``` You also get the same error if the attribute value is a literal, but an invalid literal, e.g.: ``` unexpected expression: "foo"suffix ``` This commit does two things. - Changes the error message to "attribute value must be a literal", which gives a better idea of what the problem is and how to fix it. It also no longer prints the invalid expression, because the carets below highlight it anyway. - Separates the "not a literal" case from the "invalid literal" case. Which means invalid literals now get the specific error at the literal level, rather than at the attribute level. --- compiler/rustc_parse/src/validate_attr.rs | 60 +++++++++++++++++++------------ 1 file changed, 38 insertions(+), 22 deletions(-) (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index cbe75b3dab6..81055431f64 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -6,9 +6,9 @@ use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::DelimSpan; use rustc_ast::MetaItemKind; use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MetaItem}; -use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, FatalError, PResult}; use rustc_feature::{AttributeTemplate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; +use rustc_session::errors::report_lit_error; use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT; use rustc_session::parse::ParseSess; use rustc_span::{sym, Span, Symbol}; @@ -51,28 +51,44 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta MetaItemKind::List(nmis) } AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => { - if let ast::ExprKind::Lit(token_lit) = expr.kind - && let Ok(lit) = ast::MetaItemLit::from_token_lit(token_lit, expr.span) - { - if token_lit.suffix.is_some() { - let mut err = sess.span_diagnostic.struct_span_err( - expr.span, - "suffixed literals are not allowed in attributes", - ); - err.help( - "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \ - use an unsuffixed version (`1`, `1.0`, etc.)", - ); - return Err(err); - } else { - MetaItemKind::NameValue(lit) - } + if let ast::ExprKind::Lit(token_lit) = expr.kind { + let res = ast::MetaItemLit::from_token_lit(token_lit, expr.span); + let res = match res { + Ok(lit) => { + if token_lit.suffix.is_some() { + let mut err = sess.span_diagnostic.struct_span_err( + expr.span, + "suffixed literals are not allowed in attributes", + ); + err.help( + "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \ + use an unsuffixed version (`1`, `1.0`, etc.)", + ); + return Err(err); + } else { + MetaItemKind::NameValue(lit) + } + } + Err(err) => { + report_lit_error(sess, err, token_lit, expr.span); + let lit = ast::MetaItemLit { + symbol: token_lit.symbol, + suffix: token_lit.suffix, + kind: ast::LitKind::Err, + span: expr.span, + }; + MetaItemKind::NameValue(lit) + } + }; + res } else { - // The non-error case can happen with e.g. `#[foo = 1+1]`. The error case can - // happen with e.g. `#[foo = include_str!("nonexistent-file.rs")]`; in that - // case we delay the error because an earlier error will have already been - // reported. - let msg = format!("unexpected expression: `{}`", pprust::expr_to_string(expr)); + // Example cases: + // - `#[foo = 1+1]`: results in `ast::ExprKind::BinOp`. + // - `#[foo = include_str!("nonexistent-file.rs")]`: + // results in `ast::ExprKind::Err`. In that case we delay + // the error because an earlier error will have already + // been reported. + let msg = format!("attribute value must be a literal"); let mut err = sess.span_diagnostic.struct_span_err(expr.span, msg); if let ast::ExprKind::Err = expr.kind { err.downgrade_to_delayed_bug(); -- cgit 1.4.1-3-g733a5 From 1d78ce681e4bf0c0259fc97e422298d68635dc4f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 12 Dec 2023 20:08:34 +0000 Subject: Actually parse async gen blocks correctly --- compiler/rustc_parse/src/parser/expr.rs | 27 ++++++++++++------ tests/ui/coroutine/async-gen-deduce-yield.rs | 14 ++++++++++ .../feature-gate-gen_blocks.e2024.stderr | 32 +++++++++++++++++++--- .../feature-gate-gen_blocks.none.stderr | 14 +++++++++- tests/ui/feature-gates/feature-gate-gen_blocks.rs | 15 +++++++++- 5 files changed, 87 insertions(+), 15 deletions(-) create mode 100644 tests/ui/coroutine/async-gen-deduce-yield.rs (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 3c0627526be..3606c0fa098 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1440,21 +1440,23 @@ impl<'a> Parser<'a> { } else if this.eat_keyword(kw::Underscore) { Ok(this.mk_expr(this.prev_token.span, ExprKind::Underscore)) } else if this.token.uninterpolated_span().at_least_rust_2018() { - // `Span:.at_least_rust_2018()` is somewhat expensive; don't get it repeatedly. - if this.check_keyword(kw::Async) { + // `Span::at_least_rust_2018()` is somewhat expensive; don't get it repeatedly. + if this.token.uninterpolated_span().at_least_rust_2024() + // check for `gen {}` and `gen move {}` + // or `async gen {}` and `async gen move {}` + && (this.is_gen_block(kw::Gen, 0) + || (this.check_keyword(kw::Async) && this.is_gen_block(kw::Gen, 1))) + { + // FIXME: (async) gen closures aren't yet parsed. + this.parse_gen_block() + } else if this.check_keyword(kw::Async) { // FIXME(gen_blocks): Parse `gen async` and suggest swap if this.is_gen_block(kw::Async, 0) { // Check for `async {` and `async move {`, - // or `async gen {` and `async gen move {`. this.parse_gen_block() } else { this.parse_expr_closure() } - } else if this.token.uninterpolated_span().at_least_rust_2024() - && (this.is_gen_block(kw::Gen, 0) - || (this.check_keyword(kw::Async) && this.is_gen_block(kw::Gen, 1))) - { - this.parse_gen_block() } else if this.eat_keyword_noexpect(kw::Await) { this.recover_incorrect_await_syntax(lo, this.prev_token.span) } else { @@ -3219,9 +3221,16 @@ impl<'a> Parser<'a> { if self.eat_keyword(kw::Gen) { GenBlockKind::AsyncGen } else { GenBlockKind::Async } } else { assert!(self.eat_keyword(kw::Gen)); - self.sess.gated_spans.gate(sym::gen_blocks, lo.to(self.token.span)); GenBlockKind::Gen }; + match kind { + GenBlockKind::Async => { + // `async` blocks are stable + } + GenBlockKind::Gen | GenBlockKind::AsyncGen => { + self.sess.gated_spans.gate(sym::gen_blocks, lo.to(self.prev_token.span)); + } + } let capture_clause = self.parse_capture_clause()?; let (attrs, body) = self.parse_inner_attrs_and_block()?; let kind = ExprKind::Gen(capture_clause, body, kind); diff --git a/tests/ui/coroutine/async-gen-deduce-yield.rs b/tests/ui/coroutine/async-gen-deduce-yield.rs new file mode 100644 index 00000000000..9ccc8ee41f6 --- /dev/null +++ b/tests/ui/coroutine/async-gen-deduce-yield.rs @@ -0,0 +1,14 @@ +// compile-flags: --edition 2024 -Zunstable-options +// check-pass + +#![feature(async_iterator, gen_blocks)] + +use std::async_iter::AsyncIterator; + +fn deduce() -> impl AsyncIterator { + async gen { + yield Default::default(); + } +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-gen_blocks.e2024.stderr b/tests/ui/feature-gates/feature-gate-gen_blocks.e2024.stderr index 1462c41e957..c582ca7ba3d 100644 --- a/tests/ui/feature-gates/feature-gate-gen_blocks.e2024.stderr +++ b/tests/ui/feature-gates/feature-gate-gen_blocks.e2024.stderr @@ -2,16 +2,34 @@ error[E0658]: gen blocks are experimental --> $DIR/feature-gate-gen_blocks.rs:5:5 | LL | gen {}; - | ^^^^^ + | ^^^ | = note: see issue #117078 for more information = help: add `#![feature(gen_blocks)]` to the crate attributes to enable error[E0658]: gen blocks are experimental - --> $DIR/feature-gate-gen_blocks.rs:13:5 + --> $DIR/feature-gate-gen_blocks.rs:12:5 + | +LL | async gen {}; + | ^^^^^^^^^ + | + = note: see issue #117078 for more information + = help: add `#![feature(gen_blocks)]` to the crate attributes to enable + +error[E0658]: gen blocks are experimental + --> $DIR/feature-gate-gen_blocks.rs:22:5 | LL | gen {}; - | ^^^^^ + | ^^^ + | + = note: see issue #117078 for more information + = help: add `#![feature(gen_blocks)]` to the crate attributes to enable + +error[E0658]: gen blocks are experimental + --> $DIR/feature-gate-gen_blocks.rs:25:5 + | +LL | async gen {}; + | ^^^^^^^^^ | = note: see issue #117078 for more information = help: add `#![feature(gen_blocks)]` to the crate attributes to enable @@ -22,7 +40,13 @@ error[E0282]: type annotations needed LL | gen {}; | ^^ cannot infer type -error: aborting due to 3 previous errors +error[E0282]: type annotations needed + --> $DIR/feature-gate-gen_blocks.rs:12:15 + | +LL | async gen {}; + | ^^ cannot infer type + +error: aborting due to 6 previous errors Some errors have detailed explanations: E0282, E0658. For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/feature-gates/feature-gate-gen_blocks.none.stderr b/tests/ui/feature-gates/feature-gate-gen_blocks.none.stderr index 56f8309a69f..b4b37f0e638 100644 --- a/tests/ui/feature-gates/feature-gate-gen_blocks.none.stderr +++ b/tests/ui/feature-gates/feature-gate-gen_blocks.none.stderr @@ -1,9 +1,21 @@ +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `gen` + --> $DIR/feature-gate-gen_blocks.rs:12:11 + | +LL | async gen {}; + | ^^^ expected one of 8 possible tokens + +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `gen` + --> $DIR/feature-gate-gen_blocks.rs:25:11 + | +LL | async gen {}; + | ^^^ expected one of 8 possible tokens + error[E0422]: cannot find struct, variant or union type `gen` in this scope --> $DIR/feature-gate-gen_blocks.rs:5:5 | LL | gen {}; | ^^^ not found in this scope -error: aborting due to 1 previous error +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0422`. diff --git a/tests/ui/feature-gates/feature-gate-gen_blocks.rs b/tests/ui/feature-gates/feature-gate-gen_blocks.rs index e2e1574a36a..ff9a0b139c0 100644 --- a/tests/ui/feature-gates/feature-gate-gen_blocks.rs +++ b/tests/ui/feature-gates/feature-gate-gen_blocks.rs @@ -1,15 +1,28 @@ // revisions: e2024 none //[e2024] compile-flags: --edition 2024 -Zunstable-options -fn main() { +fn test_gen() { gen {}; //[none]~^ ERROR: cannot find struct, variant or union type `gen` //[e2024]~^^ ERROR: gen blocks are experimental //[e2024]~| ERROR: type annotations needed } +fn test_async_gen() { + async gen {}; + //[none]~^ ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `gen` + //[e2024]~^^ ERROR: gen blocks are experimental + //[e2024]~| ERROR: type annotations needed +} + +fn main() {} + #[cfg(FALSE)] fn foo() { gen {}; //[e2024]~^ ERROR: gen blocks are experimental + + async gen {}; + //[e2024]~^ ERROR: gen blocks are experimental + //[none]~^^ ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `gen` } -- cgit 1.4.1-3-g733a5