Improve spans of malformed attribute errors

This commit is contained in:
Jonathan Brouwer 2025-12-13 15:43:01 +01:00
parent 69a59e8e87
commit ae39d3d9ab
No known key found for this signature in database
GPG key ID: 13619B051B673C52
34 changed files with 116 additions and 67 deletions

View file

@ -42,7 +42,7 @@ pub fn parse_cfg<S: Stage>(
args: &ArgParser,
) -> Option<CfgEntry> {
let ArgParser::List(list) = args else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
};
let Some(single) = list.single() else {

View file

@ -25,7 +25,7 @@ impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
};
@ -478,7 +478,7 @@ fn parse_tf_attribute<S: Stage>(
) -> impl IntoIterator<Item = (Symbol, Span)> {
let mut features = Vec::new();
let ArgParser::List(list) = args else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return features;
};
if list.is_empty() {
@ -601,7 +601,7 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
};

View file

@ -13,7 +13,7 @@ impl<S: Stage> AttributeParser<S> for ConfusablesParser {
template!(List: &[r#""name1", "name2", ..."#]),
|this, cx, args| {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return;
};

View file

@ -21,7 +21,7 @@ impl<S: Stage> CombineAttributeParser<S> for DebuggerViualizerParser {
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let Some(l) = args.list() else {
cx.expected_list(args.span().unwrap_or(cx.attr_span));
cx.expected_list(cx.attr_span, args);
return None;
};
let Some(single) = l.single() else {

View file

@ -106,7 +106,7 @@ impl DocParser {
}
Some(sym::attr) => {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return;
};

View file

@ -76,7 +76,7 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
return None;
}
_ => {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
}
};
@ -379,7 +379,7 @@ impl LinkParser {
return true;
}
let Some(link_cfg) = item.args().list() else {
cx.expected_list(item.span());
cx.expected_list(item.span(), item.args());
return true;
};
let Some(link_cfg) = link_cfg.single() else {

View file

@ -99,8 +99,8 @@ impl<S: Stage> AttributeParser<S> for MacroUseParser {
}
}
}
ArgParser::NameValue(_) => {
cx.expected_list_or_no_args(span);
ArgParser::NameValue(nv) => {
cx.expected_list_or_no_args(nv.args_span());
}
}
},
@ -155,8 +155,8 @@ impl<S: Stage> SingleAttributeParser<S> for MacroExportParser {
}
}
}
ArgParser::NameValue(_) => {
cx.expected_list_or_no_args(cx.attr_span);
ArgParser::NameValue(nv) => {
cx.expected_list_or_no_args(nv.args_span());
return None;
}
};

View file

@ -41,8 +41,8 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
};
Some(value_str)
}
ArgParser::List(_) => {
cx.expected_name_value(cx.attr_span, None);
ArgParser::List(list) => {
cx.expected_nv_or_no_args(list.span);
return None;
}
},

View file

@ -65,7 +65,7 @@ fn parse_derive_like<S: Stage>(
if args.no_args().is_ok() && !trait_name_mandatory {
return Some((None, ThinVec::new()));
}
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
};
let mut items = list.mixed();
@ -96,7 +96,7 @@ fn parse_derive_like<S: Stage>(
let mut attributes = ThinVec::new();
if let Some(attrs) = items.next() {
let Some(attr_list) = attrs.meta_item() else {
cx.expected_list(attrs.span());
cx.unexpected_literal(attrs.span());
return None;
};
if !attr_list.path().word_is(sym::attributes) {
@ -104,7 +104,7 @@ fn parse_derive_like<S: Stage>(
return None;
}
let Some(attr_list) = attr_list.args().list() else {
cx.expected_list(attrs.span());
cx.expected_list(attrs.span(), attr_list.args());
return None;
};

View file

@ -27,7 +27,7 @@ impl<S: Stage> SingleAttributeParser<S> for CustomMirParser {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
};

View file

@ -33,7 +33,7 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
let mut reprs = Vec::new();
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return reprs;
};
@ -278,7 +278,7 @@ impl AlignParser {
fn parse<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {
match args {
ArgParser::NoArgs | ArgParser::NameValue(_) => {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
}
ArgParser::List(list) => {
let Some(align) = list.single() else {

View file

@ -295,7 +295,7 @@ pub(crate) fn parse_stability<S: Stage>(
let mut since = None;
let ArgParser::List(list) = args else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
};
@ -367,7 +367,7 @@ pub(crate) fn parse_unstability<S: Stage>(
let mut old_name = None;
let ArgParser::List(list) = args else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
};

View file

@ -22,7 +22,7 @@ impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
let mut array = false;
let mut boxed_slice = false;
let Some(args) = args.list() else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
};
if args.is_empty() {

View file

@ -43,7 +43,7 @@ pub(crate) fn parse_single_integer<S: Stage>(
args: &ArgParser,
) -> Option<u128> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
cx.expected_list(cx.attr_span, args);
return None;
};
let Some(single) = list.single() else {

View file

@ -464,7 +464,12 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIntegerLiteral)
}
pub(crate) fn expected_list(&self, span: Span) -> ErrorGuaranteed {
pub(crate) fn expected_list(&self, span: Span, args: &ArgParser) -> ErrorGuaranteed {
let span = match args {
ArgParser::NoArgs => span,
ArgParser::List(list) => list.span,
ArgParser::NameValue(nv) => nv.args_span(),
};
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedList)
}
@ -472,6 +477,10 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedListOrNoArgs)
}
pub(crate) fn expected_nv_or_no_args(&self, span: Span) -> ErrorGuaranteed {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValueOrNoArgs)
}
pub(crate) fn expected_no_args(&self, span: Span) -> ErrorGuaranteed {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNoArgs)
}

View file

@ -177,7 +177,7 @@ impl ArgParser {
match self {
Self::NoArgs => Ok(()),
Self::List(args) => Err(args.span),
Self::NameValue(args) => Err(args.eq_span.to(args.value_span)),
Self::NameValue(args) => Err(args.args_span()),
}
}
}
@ -314,6 +314,10 @@ impl NameValueParser {
pub fn value_as_str(&self) -> Option<Symbol> {
self.value_as_lit().kind.str()
}
pub fn args_span(&self) -> Span {
self.eq_span.to(self.value_span)
}
}
fn expr_to_lit(

View file

@ -511,6 +511,7 @@ pub(crate) enum AttributeParseErrorReason<'a> {
ExpectedSingleArgument,
ExpectedList,
ExpectedListOrNoArgs,
ExpectedNameValueOrNoArgs,
UnexpectedLiteral,
ExpectedNameValue(Option<Symbol>),
DuplicateKey(Symbol),
@ -586,6 +587,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
AttributeParseErrorReason::ExpectedListOrNoArgs => {
diag.span_label(self.span, "expected a list or no arguments here");
}
AttributeParseErrorReason::ExpectedNameValueOrNoArgs => {
diag.span_label(self.span, "didn't expect a list here");
}
AttributeParseErrorReason::DuplicateKey(key) => {
diag.span_label(self.span, format!("found `{key}` used as a key more than once"));
diag.code(E0538);

View file

@ -2,7 +2,9 @@ error[E0539]: malformed `doc` attribute input
--> $DIR/invalid-cfg.rs:2:1
|
LL | #[doc(cfg = "x")]
| ^^^^^^^^^^^^^^^^^ expected this to be a list
| ^^^^^^^^^^-----^^
| |
| expected this to be a list
error[E0805]: malformed `doc` attribute input
--> $DIR/invalid-cfg.rs:3:1
@ -16,7 +18,9 @@ error[E0539]: malformed `doc` attribute input
--> $DIR/invalid-cfg.rs:7:1
|
LL | #[doc(cfg = "x")]
| ^^^^^^^^^^^^^^^^^ expected this to be a list
| ^^^^^^^^^^-----^^
| |
| expected this to be a list
error[E0805]: malformed `doc` attribute input
--> $DIR/invalid-cfg.rs:8:1
@ -30,7 +34,9 @@ error[E0539]: malformed `doc` attribute input
--> $DIR/invalid-cfg.rs:12:1
|
LL | #[doc(cfg = "x")]
| ^^^^^^^^^^^^^^^^^ expected this to be a list
| ^^^^^^^^^^-----^^
| |
| expected this to be a list
error[E0805]: malformed `doc` attribute input
--> $DIR/invalid-cfg.rs:13:1
@ -44,7 +50,9 @@ error[E0539]: malformed `doc` attribute input
--> $DIR/invalid-cfg.rs:18:1
|
LL | #[doc(cfg = "x")]
| ^^^^^^^^^^^^^^^^^ expected this to be a list
| ^^^^^^^^^^-----^^
| |
| expected this to be a list
error[E0805]: malformed `doc` attribute input
--> $DIR/invalid-cfg.rs:19:1

View file

@ -14,7 +14,9 @@ error[E0539]: malformed `macro_use` attribute input
--> $DIR/invalid-macro-use.rs:4:1
|
LL | #[macro_use = 5]
| ^^^^^^^^^^^^^^^^ expected a list or no arguments here
| ^^^^^^^^^^^^---^
| |
| expected a list or no arguments here
|
= note: for more information, visit <https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute>
help: try changing it to one of the following valid forms of the attribute

View file

@ -318,7 +318,9 @@ error[E0539]: malformed `must_use` attribute input
--> $DIR/malformed-attrs.rs:61:1
|
LL | #[must_use()]
| ^^^^^^^^^^^^^
| ^^^^^^^^^^--^
| |
| didn't expect a list here
|
= note: for more information, visit <https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute>
help: try changing it to one of the following valid forms of the attribute
@ -628,7 +630,9 @@ error[E0539]: malformed `macro_use` attribute input
--> $DIR/malformed-attrs.rs:216:1
|
LL | #[macro_use = 1]
| ^^^^^^^^^^^^^^^^ expected a list or no arguments here
| ^^^^^^^^^^^^---^
| |
| expected a list or no arguments here
|
= note: for more information, visit <https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute>
help: try changing it to one of the following valid forms of the attribute
@ -644,7 +648,9 @@ error[E0539]: malformed `macro_export` attribute input
--> $DIR/malformed-attrs.rs:221:1
|
LL | #[macro_export = 18]
| ^^^^^^^^^^^^^^^^^^^^ expected a list or no arguments here
| ^^^^^^^^^^^^^^^----^
| |
| expected a list or no arguments here
|
help: try changing it to one of the following valid forms of the attribute
|

View file

@ -20,9 +20,9 @@ error[E0539]: malformed `rustc_align` attribute input
--> $DIR/malformed-fn-align.rs:17:1
|
LL | #[rustc_align = 16]
| ^^^^^^^^^^^^^^^^^^^
| |
| expected this to be a list
| ^^^^^^^^^^^^^^----^
| | |
| | expected this to be a list
| help: must be of the form: `#[rustc_align(<alignment in bytes>)]`
error[E0589]: invalid alignment value: not an unsuffixed integer

View file

@ -2,7 +2,9 @@ error[E0539]: malformed `must_use` attribute input
--> $DIR/malformed-must_use.rs:1:1
|
LL | #[must_use()]
| ^^^^^^^^^^^^^
| ^^^^^^^^^^--^
| |
| didn't expect a list here
|
= note: for more information, visit <https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute>
help: try changing it to one of the following valid forms of the attribute

View file

@ -2,9 +2,9 @@ error[E0539]: malformed `rustc_align_static` attribute input
--> $DIR/malformed-static-align.rs:4:1
|
LL | #[rustc_align_static = 16]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected this to be a list
| ^^^^^^^^^^^^^^^^^^^^^----^
| | |
| | expected this to be a list
| help: must be of the form: `#[rustc_align_static(<alignment in bytes>)]`
error[E0589]: invalid alignment value: not an unsuffixed integer

View file

@ -11,9 +11,9 @@ error[E0539]: malformed `rustc_skip_during_method_dispatch` attribute input
--> $DIR/rustc_skip_during_method_dispatch.rs:7:1
|
LL | #[rustc_skip_during_method_dispatch = "array"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected this to be a list
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------^
| | |
| | expected this to be a list
| help: must be of the form: `#[rustc_skip_during_method_dispatch(array, boxed_slice)]`
error[E0539]: malformed `rustc_skip_during_method_dispatch` attribute input

View file

@ -13,9 +13,9 @@ error[E0539]: malformed `cfg` attribute input
--> $DIR/cfg-attr-syntax-validation.rs:7:1
|
LL | #[cfg = 10]
| ^^^^^^^^^^^
| |
| expected this to be a list
| ^^^^^^----^
| | |
| | expected this to be a list
| help: must be of the form: `#[cfg(predicate)]`
|
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute>

View file

@ -20,7 +20,9 @@ error[E0539]: malformed `macro_use` attribute input
--> $DIR/issue-43106-gating-of-macro_use.rs:15:5
|
LL | #[macro_use = "2700"] struct S;
| ^^^^^^^^^^^^^^^^^^^^^ expected a list or no arguments here
| ^^^^^^^^^^^^--------^
| |
| expected a list or no arguments here
|
= note: for more information, visit <https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute>
help: try changing it to one of the following valid forms of the attribute

View file

@ -10,7 +10,9 @@ error[E0539]: malformed `link` attribute input
--> $DIR/link-attr-validation-early.rs:3:1
|
LL | #[link = "foo"]
| ^^^^^^^^^^^^^^^ expected this to be a list
| ^^^^^^^-------^
| |
| expected this to be a list
|
= note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute>

View file

@ -142,9 +142,9 @@ error[E0539]: malformed `link` attribute input
--> $DIR/link-attr-validation-late.rs:24:1
|
LL | #[link(name = "...", cfg = "literal")]
| ^^^^^^^^^^^^^^^^^^^^^---------------^^
| |
| expected this to be a list
| ^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^
| |
| expected this to be a list
|
= note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute>

View file

@ -10,7 +10,9 @@ error[E0539]: malformed `link` attribute input
--> $DIR/malformed-regressions.rs:10:1
|
LL | #[link = ""]
| ^^^^^^^^^^^^ expected this to be a list
| ^^^^^^^----^
| |
| expected this to be a list
|
= note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute>

View file

@ -16,7 +16,9 @@ error[E0539]: malformed `proc_macro_derive` attribute input
--> $DIR/attribute.rs:15:1
|
LL | #[proc_macro_derive = ""]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected this to be a list
| ^^^^^^^^^^^^^^^^^^^^----^
| |
| expected this to be a list
|
= note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
help: try changing it to one of the following valid forms of the attribute

View file

@ -10,7 +10,9 @@ error[E0539]: malformed `repr` attribute input
--> $DIR/repr.rs:4:1
|
LL | #[repr = "B"]
| ^^^^^^^^^^^^^ expected this to be a list
| ^^^^^^^-----^
| |
| expected this to be a list
|
= note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html#representations>
@ -18,7 +20,9 @@ error[E0539]: malformed `repr` attribute input
--> $DIR/repr.rs:7:1
|
LL | #[repr = "C"]
| ^^^^^^^^^^^^^ expected this to be a list
| ^^^^^^^-----^
| |
| expected this to be a list
|
= note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html#representations>

View file

@ -42,7 +42,9 @@ error[E0539]: malformed `sanitize` attribute input
--> $DIR/invalid-sanitize.rs:18:1
|
LL | #[sanitize = "off"]
| ^^^^^^^^^^^^^^^^^^^ expected this to be a list
| ^^^^^^^^^^^-------^
| |
| expected this to be a list
error[E0539]: malformed `sanitize` attribute input
--> $DIR/invalid-sanitize.rs:21:1

View file

@ -11,9 +11,9 @@ error[E0539]: malformed `unstable` attribute input
--> $DIR/stability-attribute-sanity-4.rs:11:5
|
LL | #[unstable = "b"]
| ^^^^^^^^^^^^^^^^^
| |
| expected this to be a list
| ^^^^^^^^^^^-----^
| | |
| | expected this to be a list
| help: must be of the form: `#[unstable(feature = "name", reason = "...", issue = "N")]`
error[E0539]: malformed `stable` attribute input
@ -29,9 +29,9 @@ error[E0539]: malformed `stable` attribute input
--> $DIR/stability-attribute-sanity-4.rs:17:5
|
LL | #[stable = "a"]
| ^^^^^^^^^^^^^^^
| |
| expected this to be a list
| ^^^^^^^^^-----^
| | |
| | expected this to be a list
| help: must be of the form: `#[stable(feature = "name", since = "version")]`
error[E0542]: missing 'since'

View file

@ -26,9 +26,9 @@ error[E0539]: malformed `target_feature` attribute input
--> $DIR/invalid-attribute.rs:17:1
|
LL | #[target_feature = "+sse2"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected this to be a list
| ^^^^^^^^^^^^^^^^^---------^
| | |
| | expected this to be a list
| help: must be of the form: `#[target_feature(enable = "feat1, feat2")]`
error[E0539]: malformed `target_feature` attribute input