Skip to content

Commit 399254a

Browse files
committed
better diagnostics for arguments
1 parent 548489e commit 399254a

18 files changed

+160
-44
lines changed

compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ impl<S: Stage> AttributeParser<S> for NakedParser {
226226
const ATTRIBUTES: AcceptMapping<Self, S> =
227227
&[(&[sym::naked], template!(Word), |this, cx, args| {
228228
if let Err(span) = args.no_args() {
229-
cx.expected_no_args(span);
229+
cx.expected_no_args(&cx.attr_path, span);
230230
return;
231231
}
232232

compiler/rustc_attr_parsing/src/attributes/crate_level.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ impl<S: Stage> CombineAttributeParser<S> for FeatureParser {
214214
continue;
215215
};
216216
if let Err(arg_span) = elem.args().no_args() {
217-
cx.expected_no_args(arg_span);
217+
cx.expected_no_args(&elem.path().get_attribute_path(), arg_span);
218218
continue;
219219
}
220220

compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,10 @@ impl<S: Stage> AttributeParser<S> for MacroUseParser {
8888
continue;
8989
};
9090
if let Err(err_span) = item.args().no_args() {
91-
cx.expected_no_args(err_span);
91+
cx.expected_no_args(
92+
&item.path().get_attribute_path(),
93+
err_span,
94+
);
9295
continue;
9396
}
9497
let Some(item) = item.path().word() else {

compiler/rustc_attr_parsing/src/attributes/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ impl<T: NoArgsAttributeParser<S>, S: Stage> SingleAttributeParser<S> for Without
280280

281281
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
282282
if let Err(span) = args.no_args() {
283-
cx.expected_no_args(span);
283+
cx.expected_no_args(&cx.attr_path, span);
284284
}
285285
Some(T::CREATE(cx.attr_span))
286286
}

compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ fn parse_derive_like<S: Stage>(
8888
return None;
8989
}
9090
if let Err(e) = trait_attr.args().no_args() {
91-
cx.expected_no_args(e);
91+
cx.expected_no_args(&trait_attr.path().get_attribute_path(), e);
9292
return None;
9393
};
9494

@@ -121,7 +121,7 @@ fn parse_derive_like<S: Stage>(
121121
return None;
122122
};
123123
if let Err(e) = attr.args().no_args() {
124-
cx.expected_no_args(e);
124+
cx.expected_no_args(&attr.path().get_attribute_path(), e);
125125
return None;
126126
};
127127
let Some(ident) = attr.path().word() else {

compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcObjectLifetimeDefaultParser {
4242

4343
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
4444
if let Err(span) = args.no_args() {
45-
cx.expected_no_args(span);
45+
cx.expected_no_args(&cx.attr_path, span);
4646
return None;
4747
}
4848

compiler/rustc_attr_parsing/src/attributes/traits.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
3535
continue;
3636
};
3737
if let Err(span) = arg.args().no_args() {
38-
cx.expected_no_args(span);
38+
cx.expected_no_args(&arg.path().get_attribute_path(), span);
3939
}
4040
let path = arg.path();
4141
let (key, skip): (Symbol, &mut bool) = match path.word_sym() {

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -450,13 +450,13 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
450450
})
451451
}
452452

453-
pub(crate) fn expected_no_args(&self, args_span: Span) -> ErrorGuaranteed {
453+
pub(crate) fn expected_no_args(&self, path: &AttrPath, args_span: Span) -> ErrorGuaranteed {
454454
self.emit_err(AttributeParseError {
455455
span: args_span,
456456
attr_span: self.attr_span,
457457
template: self.template.clone(),
458458
attribute: self.attr_path.clone(),
459-
reason: AttributeParseErrorReason::ExpectedNoArgs,
459+
reason: AttributeParseErrorReason::ExpectedNoArgs { path },
460460
attr_style: self.attr_style,
461461
})
462462
}

compiler/rustc_attr_parsing/src/session_diagnostics.rs

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,9 @@ pub(crate) struct LinkOrdinalOutOfRange {
587587
}
588588

589589
pub(crate) enum AttributeParseErrorReason<'a> {
590-
ExpectedNoArgs,
590+
ExpectedNoArgs {
591+
path: &'a AttrPath,
592+
},
591593
ExpectedStringLiteral {
592594
byte_string: Option<Span>,
593595
},
@@ -619,13 +621,28 @@ pub(crate) struct AttributeParseError<'a> {
619621
pub(crate) reason: AttributeParseErrorReason<'a>,
620622
}
621623

624+
/// based on the attribute's template we add relevant suggestions to the error automatically.
625+
enum DefaultSuggestionStyle {
626+
/// give a hint about the valid forms of the attribute.
627+
/// Useful if there's already a better suggestion given than the automatic ones can provide
628+
/// but we'd still like to show which syntax forms are valid.
629+
Hint,
630+
/// Use the template to suggest changes to the attribute
631+
Suggestion,
632+
/// Don't show any default suggestions
633+
None,
634+
}
635+
622636
impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
623637
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
624638
let name = self.attribute.to_string();
625639

626640
let mut diag = Diag::new(dcx, level, format!("malformed `{name}` attribute input"));
627641
diag.span(self.attr_span);
628642
diag.code(E0539);
643+
644+
let mut show_default_suggestions = DefaultSuggestionStyle::Suggestion;
645+
629646
match self.reason {
630647
AttributeParseErrorReason::ExpectedStringLiteral { byte_string } => {
631648
if let Some(start_point_span) = byte_string {
@@ -637,7 +654,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
637654
);
638655
diag.note("expected a normal string literal, not a byte string literal");
639656

640-
return diag;
657+
show_default_suggestions = DefaultSuggestionStyle::None;
641658
} else {
642659
diag.span_label(self.span, "expected a string literal here");
643660
}
@@ -663,9 +680,19 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
663680
diag.span_label(self.span, "didn't expect a literal here");
664681
diag.code(E0565);
665682
}
666-
AttributeParseErrorReason::ExpectedNoArgs => {
683+
AttributeParseErrorReason::ExpectedNoArgs { path } => {
667684
diag.span_label(self.span, "didn't expect any arguments here");
668685
diag.code(E0565);
686+
687+
if path.span != self.attribute.span {
688+
diag.span_suggestion(
689+
path.span.to(self.span),
690+
"remove this argument",
691+
path,
692+
Applicability::MachineApplicable,
693+
);
694+
show_default_suggestions = DefaultSuggestionStyle::Hint;
695+
}
669696
}
670697
AttributeParseErrorReason::ExpectedNameValue(None) => {
671698
// If the span is the entire attribute, the suggestion we add below this match already contains enough information
@@ -756,18 +783,27 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
756783
if let Some(link) = self.template.docs {
757784
diag.note(format!("for more information, visit <{link}>"));
758785
}
786+
759787
let suggestions = self.template.suggestions(self.attr_style, &name);
788+
let text = match show_default_suggestions {
789+
DefaultSuggestionStyle::Hint => {
790+
if suggestions.len() == 1 {
791+
"the only valid form of the attribute is"
792+
} else {
793+
"these are the valid forms of the attribute"
794+
}
795+
}
796+
DefaultSuggestionStyle::Suggestion => {
797+
if suggestions.len() == 1 {
798+
"must be of the form"
799+
} else {
800+
"try changing it to one of the following valid forms of the attribute"
801+
}
802+
}
803+
DefaultSuggestionStyle::None => return diag,
804+
};
760805

761-
diag.span_suggestions(
762-
self.attr_span,
763-
if suggestions.len() == 1 {
764-
"must be of the form"
765-
} else {
766-
"try changing it to one of the following valid forms of the attribute"
767-
},
768-
suggestions,
769-
Applicability::HasPlaceholders,
770-
);
806+
diag.span_suggestions(self.attr_span, text, suggestions, Applicability::HasPlaceholders);
771807

772808
diag
773809
}

tests/ui/attributes/invalid-macro-use.stderr

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,12 @@ LL | #[macro_use(a = "b")]
4343
| didn't expect any arguments here
4444
|
4545
= note: for more information, visit <https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute>
46-
help: try changing it to one of the following valid forms of the attribute
46+
help: remove this argument
47+
|
48+
LL - #[macro_use(a = "b")]
49+
LL + #[macro_use(a)]
50+
|
51+
help: these are the valid forms of the attribute
4752
|
4853
LL - #[macro_use(a = "b")]
4954
LL + #[macro_use(name1, name2, ...)]
@@ -61,7 +66,12 @@ LL | #[macro_use(a(b))]
6166
| didn't expect any arguments here
6267
|
6368
= note: for more information, visit <https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute>
64-
help: try changing it to one of the following valid forms of the attribute
69+
help: remove this argument
70+
|
71+
LL - #[macro_use(a(b))]
72+
LL + #[macro_use(a)]
73+
|
74+
help: these are the valid forms of the attribute
6575
|
6676
LL - #[macro_use(a(b))]
6777
LL + #[macro_use(name1, name2, ...)]

0 commit comments

Comments
 (0)