Skip to content

Commit 81cf229

Browse files
committed
macros: support adding warnings to diags
Both diagnostic and subdiagnostic derives were missing the ability to add warnings to diagnostics - this is made more difficult by the `warn` attribute already existing, so this name being unavailable for the derives to use. `#[warn_]` is used instead, which requires special-casing so that `{span_,}warn` is called instead of `{span_,}warn_`. Signed-off-by: David Wood <[email protected]>
1 parent 88c11c5 commit 81cf229

File tree

8 files changed

+70
-25
lines changed

8 files changed

+70
-25
lines changed

compiler/rustc_macros/src/diagnostics/diagnostic.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ impl<'a> SessionDiagnosticDerive<'a> {
5959
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
6060
}
6161
(Some(DiagnosticDeriveKind::Lint), _) => {
62-
span_err(span, "only `#[error(..)]` and `#[warn(..)]` are supported")
62+
span_err(span, "only `#[error(..)]` and `#[warning(..)]` are supported")
6363
.help("use the `#[error(...)]` attribute to create a error")
6464
.emit();
6565
return DiagnosticDeriveError::ErrorHandled.to_compile_error();

compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs

+34-17
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::diagnostics::utils::{
88
report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path,
99
Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce,
1010
};
11-
use proc_macro2::{Ident, TokenStream};
11+
use proc_macro2::{Ident, Span, TokenStream};
1212
use quote::{format_ident, quote};
1313
use std::collections::HashMap;
1414
use std::str::FromStr;
@@ -156,16 +156,20 @@ impl DiagnosticDeriveBuilder {
156156
let name = name.as_str();
157157
let meta = attr.parse_meta()?;
158158

159-
let is_help_or_note = matches!(name, "help" | "note");
159+
let is_help_note_or_warn = matches!(name, "help" | "note" | "warn_");
160160

161161
let nested = match meta {
162162
// Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or
163163
// `#[help(..)]`/`#[note(..)]` when the user is specifying a alternative slug.
164164
Meta::List(MetaList { ref nested, .. }) => nested,
165165
// Subdiagnostics without spans can be applied to the type too, and these are just
166166
// paths: `#[help]` and `#[note]`
167-
Meta::Path(_) if is_help_or_note => {
168-
let fn_name = proc_macro2::Ident::new(name, attr.span());
167+
Meta::Path(_) if is_help_note_or_warn => {
168+
let fn_name = if name == "warn_" {
169+
Ident::new("warn", attr.span())
170+
} else {
171+
Ident::new(name, attr.span())
172+
};
169173
return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::_subdiag::#fn_name); });
170174
}
171175
_ => throw_invalid_attr!(attr, &meta),
@@ -177,9 +181,11 @@ impl DiagnosticDeriveBuilder {
177181
"error" => self.kind.set_once((DiagnosticDeriveKind::Error, span)),
178182
"warning" => self.kind.set_once((DiagnosticDeriveKind::Warn, span)),
179183
"lint" => self.kind.set_once((DiagnosticDeriveKind::Lint, span)),
180-
"help" | "note" => (),
184+
"help" | "note" | "warn_" => (),
181185
_ => throw_invalid_attr!(attr, &meta, |diag| {
182-
diag.help("only `error`, `warning`, `help` and `note` are valid attributes")
186+
diag.help(
187+
"only `error`, `warning`, `help`, `note` and `warn_` are valid attributes",
188+
)
183189
}),
184190
}
185191

@@ -188,22 +194,24 @@ impl DiagnosticDeriveBuilder {
188194
let mut nested_iter = nested.into_iter();
189195
if let Some(nested_attr) = nested_iter.next() {
190196
// Report an error if there are any other list items after the path.
191-
if is_help_or_note && nested_iter.next().is_some() {
197+
if is_help_note_or_warn && nested_iter.next().is_some() {
192198
throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
193-
diag.help("`help` and `note` struct attributes can only have one argument")
199+
diag.help(
200+
"`help`, `note` and `warn_` struct attributes can only have one argument",
201+
)
194202
});
195203
}
196204

197205
match nested_attr {
198-
NestedMeta::Meta(Meta::Path(path)) if is_help_or_note => {
206+
NestedMeta::Meta(Meta::Path(path)) if is_help_note_or_warn => {
199207
let fn_name = proc_macro2::Ident::new(name, attr.span());
200208
return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); });
201209
}
202210
NestedMeta::Meta(Meta::Path(path)) => {
203211
self.slug.set_once((path.clone(), span));
204212
}
205213
NestedMeta::Meta(meta @ Meta::NameValue(_))
206-
if !is_help_or_note
214+
if !is_help_note_or_warn
207215
&& meta.path().segments.last().unwrap().ident.to_string() == "code" =>
208216
{
209217
// don't error for valid follow-up attributes
@@ -347,10 +355,12 @@ impl DiagnosticDeriveBuilder {
347355
report_error_if_not_applied_to_span(attr, &info)?;
348356
Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label }))
349357
}
350-
"note" | "help" => {
351-
let path = match name {
352-
"note" => parse_quote! { _subdiag::note },
353-
"help" => parse_quote! { _subdiag::help },
358+
"note" | "help" | "warn_" => {
359+
let warn_ident = Ident::new("warn", Span::call_site());
360+
let (ident, path) = match name {
361+
"note" => (ident, parse_quote! { _subdiag::note }),
362+
"help" => (ident, parse_quote! { _subdiag::help }),
363+
"warn_" => (&warn_ident, parse_quote! { _subdiag::warn }),
354364
_ => unreachable!(),
355365
};
356366
if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
@@ -387,10 +397,10 @@ impl DiagnosticDeriveBuilder {
387397
"suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => {
388398
return self.generate_inner_field_code_suggestion(attr, info);
389399
}
390-
"label" | "help" | "note" => (),
400+
"label" | "help" | "note" | "warn_" => (),
391401
_ => throw_invalid_attr!(attr, &meta, |diag| {
392402
diag.help(
393-
"only `label`, `note`, `help` or `suggestion{,_short,_hidden,_verbose}` are \
403+
"only `label`, `help`, `note`, `warn` or `suggestion{,_short,_hidden,_verbose}` are \
394404
valid field attributes",
395405
)
396406
}),
@@ -419,7 +429,14 @@ impl DiagnosticDeriveBuilder {
419429
Ok(self.add_spanned_subdiagnostic(binding, ident, msg))
420430
}
421431
"note" | "help" if type_is_unit(&info.ty) => Ok(self.add_subdiagnostic(ident, msg)),
422-
"note" | "help" => report_type_error(attr, "`Span` or `()`")?,
432+
// `warn_` must be special-cased because the attribute `warn` already has meaning and
433+
// so isn't used, despite the diagnostic API being named `warn`.
434+
"warn_" if type_matches_path(&info.ty, &["rustc_span", "Span"]) => Ok(self
435+
.add_spanned_subdiagnostic(binding, &Ident::new("warn", Span::call_site()), msg)),
436+
"warn_" if type_is_unit(&info.ty) => {
437+
Ok(self.add_subdiagnostic(&Ident::new("warn", Span::call_site()), msg))
438+
}
439+
"note" | "help" | "warn_" => report_type_error(attr, "`Span` or `()`")?,
423440
_ => unreachable!(),
424441
}
425442
}

compiler/rustc_macros/src/diagnostics/fluent.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -260,10 +260,12 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok
260260
#generated
261261

262262
pub mod _subdiag {
263-
pub const note: crate::SubdiagnosticMessage =
264-
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note"));
265263
pub const help: crate::SubdiagnosticMessage =
266264
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("help"));
265+
pub const note: crate::SubdiagnosticMessage =
266+
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note"));
267+
pub const warn: crate::SubdiagnosticMessage =
268+
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("warn"));
267269
pub const label: crate::SubdiagnosticMessage =
268270
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("label"));
269271
pub const suggestion: crate::SubdiagnosticMessage =

compiler/rustc_macros/src/diagnostics/subdiagnostic.rs

+4
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ enum SubdiagnosticKind {
3737
Note,
3838
/// `#[help(...)]`
3939
Help,
40+
/// `#[warn_(...)]`
41+
Warn,
4042
/// `#[suggestion{,_short,_hidden,_verbose}]`
4143
Suggestion(SubdiagnosticSuggestionKind),
4244
}
@@ -49,6 +51,7 @@ impl FromStr for SubdiagnosticKind {
4951
"label" => Ok(SubdiagnosticKind::Label),
5052
"note" => Ok(SubdiagnosticKind::Note),
5153
"help" => Ok(SubdiagnosticKind::Help),
54+
"warn_" => Ok(SubdiagnosticKind::Warn),
5255
"suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)),
5356
"suggestion_short" => {
5457
Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short))
@@ -70,6 +73,7 @@ impl quote::IdentFragment for SubdiagnosticKind {
7073
SubdiagnosticKind::Label => write!(f, "label"),
7174
SubdiagnosticKind::Note => write!(f, "note"),
7275
SubdiagnosticKind::Help => write!(f, "help"),
76+
SubdiagnosticKind::Warn => write!(f, "warn"),
7377
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => {
7478
write!(f, "suggestion")
7579
}

compiler/rustc_macros/src/lib.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,9 @@ decl_derive!(
130130
warning,
131131
error,
132132
lint,
133-
note,
134133
help,
134+
note,
135+
warn_,
135136
// field attributes
136137
skip_arg,
137138
primary_span,
@@ -148,8 +149,9 @@ decl_derive!(
148149
warning,
149150
error,
150151
lint,
151-
note,
152152
help,
153+
note,
154+
warn_,
153155
// field attributes
154156
skip_arg,
155157
primary_span,
@@ -166,6 +168,7 @@ decl_derive!(
166168
label,
167169
help,
168170
note,
171+
warn_,
169172
suggestion,
170173
suggestion_short,
171174
suggestion_hidden,

src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ struct LabelWithTrailingList {
538538

539539
#[derive(SessionDiagnostic)]
540540
#[lint(typeck::ambiguous_lifetime_bound)]
541-
//~^ ERROR only `#[error(..)]` and `#[warn(..)]` are supported
541+
//~^ ERROR only `#[error(..)]` and `#[warning(..)]` are supported
542542
struct LintsBad {
543543
}
544544

@@ -559,3 +559,10 @@ struct ErrorWithMultiSpan {
559559
#[primary_span]
560560
span: MultiSpan,
561561
}
562+
563+
#[derive(SessionDiagnostic)]
564+
#[error(typeck::ambiguous_lifetime_bound, code = "E0123")]
565+
#[warn_]
566+
struct ErrorWithWarn {
567+
val: String,
568+
}

src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ error: `#[nonsense(...)]` is not a valid attribute
2121
LL | #[nonsense(typeck::ambiguous_lifetime_bound, code = "E0123")]
2222
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2323
|
24-
= help: only `error`, `warning`, `help` and `note` are valid attributes
24+
= help: only `error`, `warning`, `help`, `note` and `warn_` are valid attributes
2525

2626
error: diagnostic kind not specified
2727
--> $DIR/diagnostic-derive.rs:53:1
@@ -363,7 +363,7 @@ error: `#[label(...)]` is not a valid attribute
363363
LL | #[label(typeck::label, foo("..."))]
364364
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
365365

366-
error: only `#[error(..)]` and `#[warn(..)]` are supported
366+
error: only `#[error(..)]` and `#[warning(..)]` are supported
367367
--> $DIR/diagnostic-derive.rs:540:1
368368
|
369369
LL | / #[lint(typeck::ambiguous_lifetime_bound)]

src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs

+12
Original file line numberDiff line numberDiff line change
@@ -508,3 +508,15 @@ enum AX {
508508
span: Span,
509509
}
510510
}
511+
512+
#[derive(SessionSubdiagnostic)]
513+
#[warn_(parser::add_paren)]
514+
struct AY {
515+
}
516+
517+
#[derive(SessionSubdiagnostic)]
518+
#[warn_(parser::add_paren)]
519+
struct AZ {
520+
#[primary_span]
521+
span: Span,
522+
}

0 commit comments

Comments
 (0)