Skip to content

Commit abd3467

Browse files
committed
macros: use typed identifiers in subdiag derive
As in the diagnostic derive, using typed identifiers in the subdiagnostic derive improves the diagnostics of using the subdiagnostic derive as Fluent messages will be confirmed to exist at compile-time. Signed-off-by: David Wood <[email protected]>
1 parent 99bc979 commit abd3467

File tree

9 files changed

+298
-197
lines changed

9 files changed

+298
-197
lines changed

compiler/rustc_error_messages/src/lib.rs

+21
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,27 @@ impl<S: Into<String>> From<S> for DiagnosticMessage {
347347
}
348348
}
349349

350+
/// Translating *into* a subdiagnostic message from a diagnostic message is a little strange - but
351+
/// the subdiagnostic functions (e.g. `span_label`) take a `SubdiagnosticMessage` and the
352+
/// subdiagnostic derive refers to typed identifiers that are `DiagnosticMessage`s, so need to be
353+
/// able to convert between these, as much as they'll be converted back into `DiagnosticMessage`
354+
/// using `with_subdiagnostic_message` eventually. Don't use this other than for the derive.
355+
impl Into<SubdiagnosticMessage> for DiagnosticMessage {
356+
fn into(self) -> SubdiagnosticMessage {
357+
match self {
358+
DiagnosticMessage::Str(s) => SubdiagnosticMessage::Str(s),
359+
DiagnosticMessage::FluentIdentifier(id, None) => {
360+
SubdiagnosticMessage::FluentIdentifier(id)
361+
}
362+
// There isn't really a sensible behaviour for this because it loses information but
363+
// this is the most sensible of the behaviours.
364+
DiagnosticMessage::FluentIdentifier(_, Some(attr)) => {
365+
SubdiagnosticMessage::FluentAttr(attr)
366+
}
367+
}
368+
}
369+
}
370+
350371
/// A span together with some additional data.
351372
#[derive(Clone, Debug)]
352373
pub struct SpanLabel {

compiler/rustc_macros/src/diagnostics/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,12 @@ pub fn session_diagnostic_derive(s: Structure<'_>) -> TokenStream {
7272
/// ```ignore (rust)
7373
/// #[derive(SessionSubdiagnostic)]
7474
/// pub enum ExpectedIdentifierLabel<'tcx> {
75-
/// #[label(slug = "parser-expected-identifier")]
75+
/// #[label(parser::expected_identifier)]
7676
/// WithoutFound {
7777
/// #[primary_span]
7878
/// span: Span,
7979
/// }
80-
/// #[label(slug = "parser-expected-identifier-found")]
80+
/// #[label(parser::expected_identifier_found)]
8181
/// WithFound {
8282
/// #[primary_span]
8383
/// span: Span,
@@ -86,7 +86,7 @@ pub fn session_diagnostic_derive(s: Structure<'_>) -> TokenStream {
8686
/// }
8787
///
8888
/// #[derive(SessionSubdiagnostic)]
89-
/// #[suggestion_verbose(slug = "parser-raw-identifier")]
89+
/// #[suggestion_verbose(parser::raw_identifier)]
9090
/// pub struct RawIdentifierSuggestion<'tcx> {
9191
/// #[primary_span]
9292
/// span: Span,

compiler/rustc_macros/src/diagnostics/subdiagnostic.rs

+72-12
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use quote::{format_ident, quote};
1313
use std::collections::HashMap;
1414
use std::fmt;
1515
use std::str::FromStr;
16-
use syn::{spanned::Spanned, Meta, MetaList, MetaNameValue};
16+
use syn::{parse_quote, spanned::Spanned, Meta, MetaList, MetaNameValue, NestedMeta, Path};
1717
use synstructure::{BindingInfo, Structure, VariantInfo};
1818

1919
/// Which kind of suggestion is being created?
@@ -194,8 +194,8 @@ struct SessionSubdiagnosticDeriveBuilder<'a> {
194194
kind: Option<(SubdiagnosticKind, proc_macro::Span)>,
195195

196196
/// Slug of the subdiagnostic - corresponds to the Fluent identifier for the message - from the
197-
/// `#[kind(slug = "...")]` attribute on the type or variant.
198-
slug: Option<(String, proc_macro::Span)>,
197+
/// `#[kind(slug)]` attribute on the type or variant.
198+
slug: Option<(Path, proc_macro::Span)>,
199199
/// If a suggestion, the code to suggest as a replacement - from the `#[kind(code = "...")]`
200200
/// attribute on the type or variant.
201201
code: Option<(TokenStream, proc_macro::Span)>,
@@ -224,9 +224,34 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
224224
let meta = attr.parse_meta()?;
225225
let kind = match meta {
226226
Meta::List(MetaList { ref nested, .. }) => {
227-
for nested_attr in nested {
227+
let mut nested_iter = nested.into_iter();
228+
if let Some(nested_attr) = nested_iter.next() {
229+
match nested_attr {
230+
NestedMeta::Meta(Meta::Path(path)) => {
231+
self.slug.set_once((path.clone(), span));
232+
}
233+
NestedMeta::Meta(meta @ Meta::NameValue(_))
234+
if matches!(
235+
meta.path().segments.last().unwrap().ident.to_string().as_str(),
236+
"code" | "applicability"
237+
) =>
238+
{
239+
// don't error for valid follow-up attributes
240+
}
241+
nested_attr => {
242+
throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
243+
diag.help(
244+
"first argument of the attribute should be the diagnostic \
245+
slug",
246+
)
247+
})
248+
}
249+
};
250+
}
251+
252+
for nested_attr in nested_iter {
228253
let meta = match nested_attr {
229-
syn::NestedMeta::Meta(ref meta) => meta,
254+
NestedMeta::Meta(ref meta) => meta,
230255
_ => throw_invalid_nested_attr!(attr, &nested_attr),
231256
};
232257

@@ -241,7 +266,6 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
241266
let formatted_str = self.build_format(&s.value(), s.span());
242267
self.code.set_once((formatted_str, span));
243268
}
244-
"slug" => self.slug.set_once((s.value(), span)),
245269
"applicability" => {
246270
let value = match Applicability::from_str(&s.value()) {
247271
Ok(v) => v,
@@ -253,11 +277,23 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
253277
self.applicability.set_once((quote! { #value }, span));
254278
}
255279
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
256-
diag.help("only `code`, `slug` and `applicability` are valid nested attributes")
280+
diag.help(
281+
"only `code` and `applicability` are valid nested \
282+
attributes",
283+
)
257284
}),
258285
}
259286
}
260-
_ => throw_invalid_nested_attr!(attr, &nested_attr),
287+
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
288+
if matches!(meta, Meta::Path(_)) {
289+
diag.help(
290+
"a diagnostic slug must be the first argument to the \
291+
attribute",
292+
)
293+
} else {
294+
diag
295+
}
296+
}),
261297
}
262298
}
263299

@@ -281,10 +317,27 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
281317
);
282318
}
283319

320+
if matches!(
321+
kind,
322+
SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note
323+
) && self.applicability.is_some()
324+
{
325+
throw_span_err!(
326+
span,
327+
&format!(
328+
"`applicability` is not a valid nested attribute of a `{}` attribute",
329+
name
330+
)
331+
);
332+
}
333+
284334
if self.slug.is_none() {
285335
throw_span_err!(
286336
span,
287-
&format!("`slug` must be set in a `#[{}(...)]` attribute", name)
337+
&format!(
338+
"diagnostic slug must be first argument of a `#[{}(...)]` attribute",
339+
name
340+
)
288341
);
289342
}
290343

@@ -335,7 +388,10 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
335388
return Ok(quote! {});
336389
}
337390
_ => throw_invalid_attr!(attr, &meta, |diag| {
338-
diag.help("only `primary_span`, `applicability` and `skip_arg` are valid field attributes")
391+
diag.help(
392+
"only `primary_span`, `applicability` and `skip_arg` are valid field \
393+
attributes",
394+
)
339395
}),
340396
},
341397
_ => throw_invalid_attr!(attr, &meta),
@@ -375,7 +431,11 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
375431
}
376432

377433
// Missing slug errors will already have been reported.
378-
let slug = self.slug.as_ref().map(|(slug, _)| &**slug).unwrap_or("missing-slug");
434+
let slug = self
435+
.slug
436+
.as_ref()
437+
.map(|(slug, _)| slug.clone())
438+
.unwrap_or_else(|| parse_quote! { you::need::to::specify::a::slug });
379439
let code = match self.code.as_ref() {
380440
Some((code, _)) => Some(quote! { #code }),
381441
None if is_suggestion => {
@@ -397,7 +457,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
397457

398458
let diag = &self.diag;
399459
let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
400-
let message = quote! { rustc_errors::SubdiagnosticMessage::message(#slug) };
460+
let message = quote! { rustc_errors::fluent::#slug };
401461
let call = if matches!(kind, SubdiagnosticKind::Suggestion(..)) {
402462
if let Some(span) = span_field {
403463
quote! { #diag.#name(#span, #message, #code, #applicability); }

compiler/rustc_parse/src/parser/diagnostics.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ struct BadTypePlus {
265265
#[derive(SessionSubdiagnostic)]
266266
pub enum BadTypePlusSub {
267267
#[suggestion(
268-
slug = "parser-add-paren",
268+
parser::add_paren,
269269
code = "{sum_with_parens}",
270270
applicability = "machine-applicable"
271271
)]
@@ -274,12 +274,12 @@ pub enum BadTypePlusSub {
274274
#[primary_span]
275275
span: Span,
276276
},
277-
#[label(slug = "parser-forgot-paren")]
277+
#[label(parser::forgot_paren)]
278278
ForgotParen {
279279
#[primary_span]
280280
span: Span,
281281
},
282-
#[label(slug = "parser-expect-path")]
282+
#[label(parser::expect_path)]
283283
ExpectPath {
284284
#[primary_span]
285285
span: Span,

compiler/rustc_typeck/src/errors.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ pub struct AddressOfTemporaryTaken {
197197
#[derive(SessionSubdiagnostic)]
198198
pub enum AddReturnTypeSuggestion<'tcx> {
199199
#[suggestion(
200-
slug = "typeck-add-return-type-add",
200+
typeck::add_return_type_add,
201201
code = "-> {found} ",
202202
applicability = "machine-applicable"
203203
)]
@@ -207,7 +207,7 @@ pub enum AddReturnTypeSuggestion<'tcx> {
207207
found: Ty<'tcx>,
208208
},
209209
#[suggestion(
210-
slug = "typeck-add-return-type-missing-here",
210+
typeck::add_return_type_missing_here,
211211
code = "-> _ ",
212212
applicability = "has-placeholders"
213213
)]
@@ -219,12 +219,12 @@ pub enum AddReturnTypeSuggestion<'tcx> {
219219

220220
#[derive(SessionSubdiagnostic)]
221221
pub enum ExpectedReturnTypeLabel<'tcx> {
222-
#[label(slug = "typeck-expected-default-return-type")]
222+
#[label(typeck::expected_default_return_type)]
223223
Unit {
224224
#[primary_span]
225225
span: Span,
226226
},
227-
#[label(slug = "typeck-expected-return-type")]
227+
#[label(typeck::expected_return_type)]
228228
Other {
229229
#[primary_span]
230230
span: Span,

src/test/ui-fulldeps/internal-lints/diagnostics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ struct DeriveSessionDiagnostic {
2323
}
2424

2525
#[derive(SessionSubdiagnostic)]
26-
#[note(slug = "note")]
26+
#[note(parser::add_paren)]
2727
struct Note {
2828
#[primary_span]
2929
span: Span,

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ struct NoApplicability {
470470
}
471471

472472
#[derive(SessionSubdiagnostic)]
473-
#[note(slug = "note")]
473+
#[note(parser::add_paren)]
474474
struct Note;
475475

476476
#[derive(SessionDiagnostic)]

0 commit comments

Comments
 (0)