Skip to content

Commit a960f83

Browse files
committed
Make derived SessionDiagnostics generic on diagnostic level
Deriving SessionDiagnostic on a type no longer forces that diagnostic to be one of warning, error, or fatal. The level is instead decided when the struct is passed to the respective Handler::emit_*() method.
1 parent 91ad4e3 commit a960f83

File tree

5 files changed

+107
-184
lines changed

5 files changed

+107
-184
lines changed

compiler/rustc_macros/src/diagnostics/diagnostic.rs

+20-62
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ impl<'a> SessionDiagnosticDerive<'a> {
2121
builder: DiagnosticDeriveBuilder {
2222
diag,
2323
fields: build_field_mapping(&structure),
24-
kind: None,
24+
kind: DiagnosticDeriveKind::SessionDiagnostic,
2525
code: None,
2626
slug: None,
2727
},
@@ -34,49 +34,31 @@ impl<'a> SessionDiagnosticDerive<'a> {
3434
let SessionDiagnosticDerive { mut structure, sess, mut builder } = self;
3535

3636
let ast = structure.ast();
37-
let (implementation, param_ty) = {
37+
let implementation = {
3838
if let syn::Data::Struct(..) = ast.data {
3939
let preamble = builder.preamble(&structure);
4040
let (attrs, args) = builder.body(&mut structure);
4141

4242
let span = ast.span().unwrap();
4343
let diag = &builder.diag;
44-
let init = match (builder.kind.value(), builder.slug.value()) {
45-
(None, _) => {
46-
span_err(span, "diagnostic kind not specified")
47-
.help("use the `#[error(...)]` attribute to create an error")
48-
.emit();
49-
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
50-
}
51-
(Some(kind), None) => {
44+
let init = match builder.slug.value() {
45+
None => {
5246
span_err(span, "diagnostic slug not specified")
5347
.help(&format!(
54-
"specify the slug as the first argument to the attribute, such as \
55-
`#[{}(typeck::example_error)]`",
56-
kind.descr()
48+
"specify the slug as the first argument to the `#[diag(...)]` attribute, \
49+
such as `#[diag(typeck::example_error)]`",
5750
))
5851
.emit();
5952
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
6053
}
61-
(Some(DiagnosticDeriveKind::Lint), _) => {
62-
span_err(span, "only `#[error(..)]` and `#[warning(..)]` are supported")
63-
.help("use the `#[error(...)]` attribute to create a error")
64-
.emit();
65-
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
66-
}
67-
(Some(DiagnosticDeriveKind::Error), Some(slug)) => {
68-
quote! {
69-
let mut #diag = #sess.struct_err(rustc_errors::fluent::#slug);
70-
}
71-
}
72-
(Some(DiagnosticDeriveKind::Warn), Some(slug)) => {
54+
Some(slug) => {
7355
quote! {
74-
let mut #diag = #sess.struct_warn(rustc_errors::fluent::#slug);
56+
let mut #diag = #sess.struct_diagnostic(rustc_errors::fluent::#slug);
7557
}
7658
}
7759
};
7860

79-
let implementation = quote! {
61+
quote! {
8062
#init
8163
#preamble
8264
match self {
@@ -86,39 +68,28 @@ impl<'a> SessionDiagnosticDerive<'a> {
8668
#args
8769
}
8870
#diag
89-
};
90-
let param_ty = match builder.kind {
91-
Some((DiagnosticDeriveKind::Error, _)) => {
92-
quote! { rustc_errors::ErrorGuaranteed }
93-
}
94-
Some((DiagnosticDeriveKind::Lint | DiagnosticDeriveKind::Warn, _)) => {
95-
quote! { () }
96-
}
97-
_ => unreachable!(),
98-
};
99-
100-
(implementation, param_ty)
71+
}
10172
} else {
10273
span_err(
10374
ast.span().unwrap(),
10475
"`#[derive(SessionDiagnostic)]` can only be used on structs",
10576
)
10677
.emit();
10778

108-
let implementation = DiagnosticDeriveError::ErrorHandled.to_compile_error();
109-
let param_ty = quote! { rustc_errors::ErrorGuaranteed };
110-
(implementation, param_ty)
79+
DiagnosticDeriveError::ErrorHandled.to_compile_error()
11180
}
11281
};
11382

11483
structure.gen_impl(quote! {
115-
gen impl<'__session_diagnostic_sess> rustc_session::SessionDiagnostic<'__session_diagnostic_sess, #param_ty>
84+
gen impl<'__session_diagnostic_sess, G>
85+
rustc_session::SessionDiagnostic<'__session_diagnostic_sess, G>
11686
for @Self
87+
where G: rustc_errors::EmissionGuarantee
11788
{
11889
fn into_diagnostic(
11990
self,
12091
#sess: &'__session_diagnostic_sess rustc_session::parse::ParseSess
121-
) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess, #param_ty> {
92+
) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess, G> {
12293
use rustc_errors::IntoDiagnosticArg;
12394
#implementation
12495
}
@@ -139,7 +110,7 @@ impl<'a> LintDiagnosticDerive<'a> {
139110
builder: DiagnosticDeriveBuilder {
140111
diag,
141112
fields: build_field_mapping(&structure),
142-
kind: None,
113+
kind: DiagnosticDeriveKind::LintDiagnostic,
143114
code: None,
144115
slug: None,
145116
},
@@ -158,30 +129,17 @@ impl<'a> LintDiagnosticDerive<'a> {
158129

159130
let diag = &builder.diag;
160131
let span = ast.span().unwrap();
161-
let init = match (builder.kind.value(), builder.slug.value()) {
162-
(None, _) => {
163-
span_err(span, "diagnostic kind not specified")
164-
.help("use the `#[error(...)]` attribute to create an error")
165-
.emit();
166-
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
167-
}
168-
(Some(kind), None) => {
132+
let init = match builder.slug.value() {
133+
None => {
169134
span_err(span, "diagnostic slug not specified")
170135
.help(&format!(
171136
"specify the slug as the first argument to the attribute, such as \
172-
`#[{}(typeck::example_error)]`",
173-
kind.descr()
137+
`#[diag(typeck::example_error)]`",
174138
))
175139
.emit();
176140
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
177141
}
178-
(Some(DiagnosticDeriveKind::Error | DiagnosticDeriveKind::Warn), _) => {
179-
span_err(span, "only `#[lint(..)]` is supported")
180-
.help("use the `#[lint(...)]` attribute to create a lint")
181-
.emit();
182-
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
183-
}
184-
(Some(DiagnosticDeriveKind::Lint), Some(slug)) => {
142+
Some(slug) => {
185143
quote! {
186144
let mut #diag = #diag.build(rustc_errors::fluent::#slug);
187145
}

compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs

+38-43
Original file line numberDiff line numberDiff line change
@@ -18,30 +18,15 @@ use syn::{
1818
};
1919
use synstructure::{BindingInfo, Structure};
2020

21-
/// What kind of diagnostic is being derived - an error, a warning or a lint?
22-
#[derive(Copy, Clone)]
21+
/// What kind of diagnostic is being derived - a fatal/error/warning or a lint?
22+
#[derive(Copy, Clone, PartialEq, Eq)]
2323
pub(crate) enum DiagnosticDeriveKind {
24-
/// `#[error(..)]`
25-
Error,
26-
/// `#[warn(..)]`
27-
Warn,
28-
/// `#[lint(..)]`
29-
Lint,
30-
}
31-
32-
impl DiagnosticDeriveKind {
33-
/// Returns human-readable string corresponding to the kind.
34-
pub fn descr(&self) -> &'static str {
35-
match self {
36-
DiagnosticDeriveKind::Error => "error",
37-
DiagnosticDeriveKind::Warn => "warning",
38-
DiagnosticDeriveKind::Lint => "lint",
39-
}
40-
}
24+
SessionDiagnostic,
25+
LintDiagnostic,
4126
}
4227

4328
/// Tracks persistent information required for building up individual calls to diagnostic methods
44-
/// for generated diagnostic derives - both `SessionDiagnostic` for errors/warnings and
29+
/// for generated diagnostic derives - both `SessionDiagnostic` for fatal/errors/warnings and
4530
/// `LintDiagnostic` for lints.
4631
pub(crate) struct DiagnosticDeriveBuilder {
4732
/// The identifier to use for the generated `DiagnosticBuilder` instance.
@@ -51,8 +36,8 @@ pub(crate) struct DiagnosticDeriveBuilder {
5136
/// derive builder.
5237
pub fields: HashMap<String, TokenStream>,
5338

54-
/// Kind of diagnostic requested via the struct attribute.
55-
pub kind: Option<(DiagnosticDeriveKind, proc_macro::Span)>,
39+
/// Kind of diagnostic that should be derived.
40+
pub kind: DiagnosticDeriveKind,
5641
/// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that
5742
/// has the actual diagnostic message.
5843
pub slug: Option<(Path, proc_macro::Span)>,
@@ -143,7 +128,7 @@ impl DiagnosticDeriveBuilder {
143128
}
144129

145130
/// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct
146-
/// attributes like `#[error(..)`, such as the diagnostic kind and slug. Generates
131+
/// attributes like `#[diag(..)]`, such as the slug and error code. Generates
147132
/// diagnostic builder calls for setting error code and creating note/help messages.
148133
fn generate_structure_code_for_attr(
149134
&mut self,
@@ -156,15 +141,15 @@ impl DiagnosticDeriveBuilder {
156141
let name = name.as_str();
157142
let meta = attr.parse_meta()?;
158143

159-
let is_help_note_or_warn = matches!(name, "help" | "note" | "warn_");
144+
let is_diag = matches!(name, "error" | "warning" | "lint" | "diag");
160145

161146
let nested = match meta {
162-
// Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or
147+
// Most attributes are lists, like `#[diag(..)]` for most cases or
163148
// `#[help(..)]`/`#[note(..)]` when the user is specifying a alternative slug.
164149
Meta::List(MetaList { ref nested, .. }) => nested,
165150
// Subdiagnostics without spans can be applied to the type too, and these are just
166-
// paths: `#[help]` and `#[note]`
167-
Meta::Path(_) if is_help_note_or_warn => {
151+
// paths: `#[help]`, `#[note]` and `#[warn_]`
152+
Meta::Path(_) if !is_diag => {
168153
let fn_name = if name == "warn_" {
169154
Ident::new("warn", attr.span())
170155
} else {
@@ -178,23 +163,32 @@ impl DiagnosticDeriveBuilder {
178163
// Check the kind before doing any further processing so that there aren't misleading
179164
// "no kind specified" errors if there are failures later.
180165
match name {
181-
"error" => self.kind.set_once((DiagnosticDeriveKind::Error, span)),
182-
"warning" => self.kind.set_once((DiagnosticDeriveKind::Warn, span)),
183-
"lint" => self.kind.set_once((DiagnosticDeriveKind::Lint, span)),
184-
"help" | "note" | "warn_" => (),
166+
"error" | "warning" => {
167+
if self.kind == DiagnosticDeriveKind::LintDiagnostic {
168+
span_err(span, "only `#[lint(..)]` is supported")
169+
.help("use the `#[lint(...)]` attribute to create a lint")
170+
.emit();
171+
}
172+
}
173+
"lint" => {
174+
if self.kind == DiagnosticDeriveKind::SessionDiagnostic {
175+
span_err(span, "only `#[error(..)]` and `#[warning(..)]` are supported")
176+
.help("use the `#[error(...)]` attribute to create a error")
177+
.emit();
178+
}
179+
}
180+
"diag" | "help" | "note" | "warn_" => (),
185181
_ => throw_invalid_attr!(attr, &meta, |diag| {
186-
diag.help(
187-
"only `error`, `warning`, `help`, `note` and `warn_` are valid attributes",
188-
)
182+
diag.help("only `diag`, `help`, `note` and `warn_` are valid attributes")
189183
}),
190184
}
191185

192-
// First nested element should always be the path, e.g. `#[error(typeck::invalid)]` or
186+
// First nested element should always be the path, e.g. `#[diag(typeck::invalid)]` or
193187
// `#[help(typeck::another_help)]`.
194188
let mut nested_iter = nested.into_iter();
195189
if let Some(nested_attr) = nested_iter.next() {
196190
// Report an error if there are any other list items after the path.
197-
if is_help_note_or_warn && nested_iter.next().is_some() {
191+
if !is_diag && nested_iter.next().is_some() {
198192
throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
199193
diag.help(
200194
"`help`, `note` and `warn_` struct attributes can only have one argument",
@@ -203,16 +197,16 @@ impl DiagnosticDeriveBuilder {
203197
}
204198

205199
match nested_attr {
206-
NestedMeta::Meta(Meta::Path(path)) if is_help_note_or_warn => {
207-
let fn_name = proc_macro2::Ident::new(name, attr.span());
208-
return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); });
209-
}
210200
NestedMeta::Meta(Meta::Path(path)) => {
211-
self.slug.set_once((path.clone(), span));
201+
if is_diag {
202+
self.slug.set_once((path.clone(), span));
203+
} else {
204+
let fn_name = proc_macro2::Ident::new(name, attr.span());
205+
return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); });
206+
}
212207
}
213208
NestedMeta::Meta(meta @ Meta::NameValue(_))
214-
if !is_help_note_or_warn
215-
&& meta.path().segments.last().unwrap().ident == "code" =>
209+
if is_diag && meta.path().segments.last().unwrap().ident == "code" =>
216210
{
217211
// don't error for valid follow-up attributes
218212
}
@@ -347,6 +341,7 @@ impl DiagnosticDeriveBuilder {
347341
}
348342
"primary_span" => {
349343
report_error_if_not_applied_to_span(attr, &info)?;
344+
350345
Ok(quote! {
351346
#diag.set_span(#binding);
352347
})

compiler/rustc_macros/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ decl_derive!(
132132
warning,
133133
error,
134134
lint,
135+
diag,
135136
help,
136137
note,
137138
warn_,
@@ -151,6 +152,7 @@ decl_derive!(
151152
warning,
152153
error,
153154
lint,
155+
diag,
154156
help,
155157
note,
156158
warn_,

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

+2-4
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ struct WrongStructAttrStyle {}
5252
#[derive(SessionDiagnostic)]
5353
#[nonsense(typeck::ambiguous_lifetime_bound, code = "E0123")]
5454
//~^ ERROR `#[nonsense(...)]` is not a valid attribute
55-
//~^^ ERROR diagnostic kind not specified
55+
//~^^ ERROR diagnostic slug not specified
5656
//~^^^ ERROR cannot find attribute `nonsense` in this scope
5757
struct InvalidStructAttr {}
5858

@@ -103,15 +103,13 @@ struct WrongPlaceField {
103103
#[error(typeck::ambiguous_lifetime_bound, code = "E0456")]
104104
//~^ ERROR specified multiple times
105105
//~^^ ERROR specified multiple times
106-
//~^^^ ERROR specified multiple times
107106
struct ErrorSpecifiedTwice {}
108107

109108
#[derive(SessionDiagnostic)]
110109
#[error(typeck::ambiguous_lifetime_bound, code = "E0123")]
111110
#[warning(typeck::ambiguous_lifetime_bound, code = "E0293")]
112111
//~^ ERROR specified multiple times
113112
//~^^ ERROR specified multiple times
114-
//~^^^ ERROR specified multiple times
115113
struct WarnSpecifiedAfterError {}
116114

117115
#[derive(SessionDiagnostic)]
@@ -125,7 +123,7 @@ struct CodeSpecifiedTwice {}
125123
struct SlugSpecifiedTwice {}
126124

127125
#[derive(SessionDiagnostic)]
128-
struct KindNotProvided {} //~ ERROR diagnostic kind not specified
126+
struct KindNotProvided {} //~ ERROR diagnostic slug not specified
129127

130128
#[derive(SessionDiagnostic)]
131129
#[error(code = "E0456")]

0 commit comments

Comments
 (0)