Rollup merge of #118855 - nnethercote:improve-attribute-value-error, r=compiler-errors,petrochenkov

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.

r? `@compiler-errors`
This commit is contained in:
Matthias Krüger 2023-12-12 17:40:54 +01:00 committed by GitHub
commit cd7809b953
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 87 additions and 62 deletions

View File

@ -6,9 +6,9 @@ use rustc_ast::token::Delimiter;
use rustc_ast::tokenstream::DelimSpan; use rustc_ast::tokenstream::DelimSpan;
use rustc_ast::MetaItemKind; use rustc_ast::MetaItemKind;
use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MetaItem}; use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MetaItem};
use rustc_ast_pretty::pprust;
use rustc_errors::{Applicability, FatalError, PResult}; use rustc_errors::{Applicability, FatalError, PResult};
use rustc_feature::{AttributeTemplate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; 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::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
use rustc_session::parse::ParseSess; use rustc_session::parse::ParseSess;
use rustc_span::{sym, Span, Symbol}; 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) MetaItemKind::List(nmis)
} }
AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => { AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => {
if let ast::ExprKind::Lit(token_lit) = expr.kind if let ast::ExprKind::Lit(token_lit) = expr.kind {
&& let Ok(lit) = ast::MetaItemLit::from_token_lit(token_lit, expr.span) let res = ast::MetaItemLit::from_token_lit(token_lit, expr.span);
{ let res = match res {
if token_lit.suffix.is_some() { Ok(lit) => {
let mut err = sess.span_diagnostic.struct_span_err( if token_lit.suffix.is_some() {
expr.span, let mut err = sess.span_diagnostic.struct_span_err(
"suffixed literals are not allowed in attributes", expr.span,
); "suffixed literals are not allowed in attributes",
err.help( );
"instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \ err.help(
use an unsuffixed version (`1`, `1.0`, etc.)", "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \
); use an unsuffixed version (`1`, `1.0`, etc.)",
return Err(err); );
} else { return Err(err);
MetaItemKind::NameValue(lit) } 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 { } else {
// The non-error case can happen with e.g. `#[foo = 1+1]`. The error case can // Example cases:
// happen with e.g. `#[foo = include_str!("nonexistent-file.rs")]`; in that // - `#[foo = 1+1]`: results in `ast::ExprKind::BinOp`.
// case we delay the error because an earlier error will have already been // - `#[foo = include_str!("nonexistent-file.rs")]`:
// reported. // results in `ast::ExprKind::Err`. In that case we delay
let msg = format!("unexpected expression: `{}`", pprust::expr_to_string(expr)); // 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); let mut err = sess.span_diagnostic.struct_span_err(expr.span, msg);
if let ast::ExprKind::Err = expr.kind { if let ast::ExprKind::Err = expr.kind {
err.downgrade_to_delayed_bug(); err.downgrade_to_delayed_bug();

View File

@ -1,9 +1,9 @@
#![u=||{static d=||1;}] #![u=||{static d=||1;}]
//~^ unexpected expression //~^ attribute value must be a literal
//~| cannot find attribute `u` in this scope //~| cannot find attribute `u` in this scope
//~| missing type for `static` item //~| missing type for `static` item
#![a={impl std::ops::Neg for i8 {}}] #![a={impl std::ops::Neg for i8 {}}]
//~^ ERROR unexpected expression //~^ ERROR attribute value must be a literal
//~| ERROR cannot find attribute `a` in this scope //~| ERROR cannot find attribute `a` in this scope
//~| ERROR `main` function not found in crate `issue_90873` //~| ERROR `main` function not found in crate `issue_90873`

View File

@ -1,15 +1,10 @@
error: unexpected expression: `|| error: attribute value must be a literal
{
static d: _ = || 1;
}`
--> $DIR/issue-90873.rs:1:6 --> $DIR/issue-90873.rs:1:6
| |
LL | #![u=||{static d=||1;}] LL | #![u=||{static d=||1;}]
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: unexpected expression: `{ error: attribute value must be a literal
impl std::ops::Neg for i8 {}
}`
--> $DIR/issue-90873.rs:6:6 --> $DIR/issue-90873.rs:6:6
| |
LL | #![a={impl std::ops::Neg for i8 {}}] LL | #![a={impl std::ops::Neg for i8 {}}]

View File

@ -7,8 +7,8 @@ macro_rules! bar {
// FIXME?: `bar` here expands before `stringify` has a chance to expand. // FIXME?: `bar` here expands before `stringify` has a chance to expand.
// `#[rustc_dummy = ...]` is validated and dropped during expansion of `bar`, // `#[rustc_dummy = ...]` is validated and dropped during expansion of `bar`,
// the "unexpected expression" errors comes from the validation. // the "attribute value must be a literal" error comes from the validation.
#[rustc_dummy = stringify!(b)] //~ ERROR unexpected expression: `stringify!(b)` #[rustc_dummy = stringify!(b)] //~ ERROR attribute value must be a literal
bar!(); bar!();
fn main() {} fn main() {}

View File

@ -1,4 +1,4 @@
error: unexpected expression: `stringify!(b)` error: attribute value must be a literal
--> $DIR/key-value-expansion-on-mac.rs:11:17 --> $DIR/key-value-expansion-on-mac.rs:11:17
| |
LL | #[rustc_dummy = stringify!(b)] LL | #[rustc_dummy = stringify!(b)]

View File

@ -18,13 +18,13 @@ macro_rules! bug {
// Any expressions containing macro call `X` that's more complex than `X` itself. // Any expressions containing macro call `X` that's more complex than `X` itself.
// Parentheses will work. // Parentheses will work.
bug!((column!())); //~ ERROR unexpected expression: `(7u32)` bug!((column!())); //~ ERROR attribute value must be a literal
// Original test case. // Original test case.
macro_rules! bug { macro_rules! bug {
() => { () => {
bug!("bug" + stringify!(found)); //~ ERROR unexpected expression: `"bug" + "found"` bug!("bug" + stringify!(found)); //~ ERROR attribute value must be a literal
}; };
($test:expr) => { ($test:expr) => {
#[doc = $test] #[doc = $test]
@ -46,7 +46,7 @@ macro_rules! doc_comment {
macro_rules! some_macro { macro_rules! some_macro {
($t1: ty) => { ($t1: ty) => {
doc_comment! {format!("{coor}", coor = stringify!($t1)).as_str()} doc_comment! {format!("{coor}", coor = stringify!($t1)).as_str()}
//~^ ERROR unexpected expression: `{ //~^ ERROR attribute value must be a literal
}; };
} }

View File

@ -1,10 +1,10 @@
error: unexpected expression: `(7u32)` error: attribute value must be a literal
--> $DIR/key-value-expansion.rs:21:6 --> $DIR/key-value-expansion.rs:21:6
| |
LL | bug!((column!())); LL | bug!((column!()));
| ^^^^^^^^^^^ | ^^^^^^^^^^^
error: unexpected expression: `"bug" + "found"` error: attribute value must be a literal
--> $DIR/key-value-expansion.rs:27:14 --> $DIR/key-value-expansion.rs:27:14
| |
LL | bug!("bug" + stringify!(found)); LL | bug!("bug" + stringify!(found));
@ -15,7 +15,7 @@ LL | bug!();
| |
= note: this error originates in the macro `bug` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `bug` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unexpected expression: `{ let res = ::alloc::fmt::format(format_args!("{0}", "u8")); res }.as_str()` error: attribute value must be a literal
--> $DIR/key-value-expansion.rs:48:23 --> $DIR/key-value-expansion.rs:48:23
| |
LL | doc_comment! {format!("{coor}", coor = stringify!($t1)).as_str()} LL | doc_comment! {format!("{coor}", coor = stringify!($t1)).as_str()}

View File

@ -1,5 +1,5 @@
#[w = { extern crate alloc; }] #[w = { extern crate alloc; }]
//~^ ERROR unexpected expression: `{ //~^ ERROR attribute value must be a literal
//~| ERROR cannot find attribute `w` in this scope //~| ERROR cannot find attribute `w` in this scope
fn f() {} fn f() {}

View File

@ -1,6 +1,4 @@
error: unexpected expression: `{ error: attribute value must be a literal
extern crate alloc;
}`
--> $DIR/unused-item-in-attr.rs:1:7 --> $DIR/unused-item-in-attr.rs:1:7
| |
LL | #[w = { extern crate alloc; }] LL | #[w = { extern crate alloc; }]

View File

@ -1,4 +1,4 @@
#![l=|x|[b;x ]] //~ ERROR unexpected expression: `|x| [b; x]` #![l=|x|[b;x ]] //~ ERROR attribute value must be a literal
//~^ ERROR cannot find attribute `l` in this scope //~^ ERROR cannot find attribute `l` in this scope
// notice the space at the start, // notice the space at the start,

View File

@ -1,4 +1,4 @@
error: unexpected expression: `|x| [b; x]` error: attribute value must be a literal
--> $DIR/issue-90878-2.rs:1:7 --> $DIR/issue-90878-2.rs:1:7
| |
LL | #![l=|x|[b;x ]] LL | #![l=|x|[b;x ]]

View File

@ -10,7 +10,7 @@ macro_rules! check {
check!("0"); // OK check!("0"); // OK
check!(0); // OK check!(0); // OK
check!(0u8); //~ ERROR suffixed literals are not allowed in attributes check!(0u8); //~ ERROR suffixed literals are not allowed in attributes
check!(-0); //~ ERROR unexpected expression: `-0` check!(-0); //~ ERROR attribute value must be a literal
check!(0 + 0); //~ ERROR unexpected expression: `0 + 0` check!(0 + 0); //~ ERROR attribute value must be a literal
fn main() {} fn main() {}

View File

@ -6,13 +6,13 @@ LL | check!(0u8);
| |
= help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) = help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
error: unexpected expression: `-0` error: attribute value must be a literal
--> $DIR/malformed-interpolated.rs:13:8 --> $DIR/malformed-interpolated.rs:13:8
| |
LL | check!(-0); LL | check!(-0);
| ^^ | ^^
error: unexpected expression: `0 + 0` error: attribute value must be a literal
--> $DIR/malformed-interpolated.rs:14:8 --> $DIR/malformed-interpolated.rs:14:8
| |
LL | check!(0 + 0); LL | check!(0 + 0);

View File

@ -28,11 +28,12 @@ fn main() {
} }
#[rustc_dummy = "string"suffix] #[rustc_dummy = "string"suffix]
//~^ ERROR unexpected expression: `"string"suffix` //~^ ERROR suffixes on string literals are invalid
fn f() {} fn f() {}
#[must_use = "string"suffix] #[must_use = "string"suffix]
//~^ ERROR unexpected expression: `"string"suffix` //~^ ERROR suffixes on string literals are invalid
//~| ERROR malformed `must_use` attribute input
fn g() {} fn g() {}
#[link(name = "string"suffix)] #[link(name = "string"suffix)]

View File

@ -10,26 +10,39 @@ error: suffixes on string literals are invalid
LL | "C"suffix LL | "C"suffix
| ^^^^^^^^^ invalid suffix `suffix` | ^^^^^^^^^ invalid suffix `suffix`
error: unexpected expression: `"string"suffix` error: suffixes on string literals are invalid
--> $DIR/bad-lit-suffixes.rs:30:17 --> $DIR/bad-lit-suffixes.rs:30:17
| |
LL | #[rustc_dummy = "string"suffix] LL | #[rustc_dummy = "string"suffix]
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^ invalid suffix `suffix`
error: unexpected expression: `"string"suffix` error: suffixes on string literals are invalid
--> $DIR/bad-lit-suffixes.rs:34:14 --> $DIR/bad-lit-suffixes.rs:34:14
| |
LL | #[must_use = "string"suffix] LL | #[must_use = "string"suffix]
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^ invalid suffix `suffix`
error: malformed `must_use` attribute input
--> $DIR/bad-lit-suffixes.rs:34:1
|
LL | #[must_use = "string"suffix]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: the following are the possible correct uses
|
LL | #[must_use = "reason"]
|
LL | #[must_use]
|
error: suffixes on string literals are invalid error: suffixes on string literals are invalid
--> $DIR/bad-lit-suffixes.rs:38:15 --> $DIR/bad-lit-suffixes.rs:39:15
| |
LL | #[link(name = "string"suffix)] LL | #[link(name = "string"suffix)]
| ^^^^^^^^^^^^^^ invalid suffix `suffix` | ^^^^^^^^^^^^^^ invalid suffix `suffix`
error: invalid suffix `suffix` for number literal error: invalid suffix `suffix` for number literal
--> $DIR/bad-lit-suffixes.rs:42:41 --> $DIR/bad-lit-suffixes.rs:43:41
| |
LL | #[rustc_layout_scalar_valid_range_start(0suffix)] LL | #[rustc_layout_scalar_valid_range_start(0suffix)]
| ^^^^^^^ invalid suffix `suffix` | ^^^^^^^ invalid suffix `suffix`
@ -136,5 +149,5 @@ LL | 1.0e10suffix;
| |
= help: valid suffixes are `f32` and `f64` = help: valid suffixes are `f32` and `f64`
error: aborting due to 20 previous errors error: aborting due to 21 previous errors

View File

@ -1,4 +1,4 @@
#![feature(rustc_attrs)] #![feature(rustc_attrs)]
#![rustc_dummy=5z] //~ ERROR unexpected expression: `5z` #![rustc_dummy=5z] //~ ERROR invalid suffix `z` for number literal
fn main() {} fn main() {}

View File

@ -1,8 +1,10 @@
error: unexpected expression: `5z` error: invalid suffix `z` for number literal
--> $DIR/issue-104620.rs:3:16 --> $DIR/issue-104620.rs:3:16
| |
LL | #![rustc_dummy=5z] LL | #![rustc_dummy=5z]
| ^^ | ^^ invalid suffix `z`
|
= help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)
error: aborting due to 1 previous error error: aborting due to 1 previous error