Skip to content

Commit b2eba05

Browse files
committed
Auto merge of rust-lang#97121 - pvdrz:do-subdiagnostics-later, r=davidtwco
Avoid double binding of subdiagnostics inside `#[derive(SessionDiagnostic)]` r? `@davidtwco`
2 parents 43d9f38 + 8227e69 commit b2eba05

File tree

4 files changed

+91
-84
lines changed

4 files changed

+91
-84
lines changed

compiler/rustc_macros/src/diagnostics/diagnostic.rs

+83-72
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::str::FromStr;
1515
use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, Type};
16-
use synstructure::Structure;
16+
use synstructure::{BindingInfo, Structure};
1717

1818
/// The central struct for constructing the `into_diagnostic` method from an annotated struct.
1919
pub(crate) struct SessionDiagnosticDerive<'a> {
@@ -71,55 +71,42 @@ impl<'a> SessionDiagnosticDerive<'a> {
7171
}
7272
};
7373

74+
// Keep track of which fields are subdiagnostics or have no attributes.
75+
let mut subdiagnostics_or_empty = std::collections::HashSet::new();
76+
7477
// Generates calls to `span_label` and similar functions based on the attributes
7578
// on fields. Code for suggestions uses formatting machinery and the value of
7679
// other fields - because any given field can be referenced multiple times, it
77-
// should be accessed through a borrow. When passing fields to `set_arg` (which
78-
// happens below) for Fluent, we want to move the data, so that has to happen
79-
// in a separate pass over the fields.
80-
let attrs = structure.each(|field_binding| {
81-
let field = field_binding.ast();
82-
let result = field.attrs.iter().map(|attr| {
83-
builder
84-
.generate_field_attr_code(
85-
attr,
86-
FieldInfo {
87-
vis: &field.vis,
88-
binding: field_binding,
89-
ty: &field.ty,
90-
span: &field.span(),
91-
},
92-
)
93-
.unwrap_or_else(|v| v.to_compile_error())
94-
});
95-
96-
quote! { #(#result);* }
97-
});
80+
// should be accessed through a borrow. When passing fields to `add_subdiagnostic`
81+
// or `set_arg` (which happens below) for Fluent, we want to move the data, so that
82+
// has to happen in a separate pass over the fields.
83+
let attrs = structure
84+
.clone()
85+
.filter(|field_binding| {
86+
let attrs = &field_binding.ast().attrs;
87+
88+
(!attrs.is_empty()
89+
&& attrs.iter().all(|attr| {
90+
"subdiagnostic"
91+
!= attr.path.segments.last().unwrap().ident.to_string()
92+
}))
93+
|| {
94+
subdiagnostics_or_empty.insert(field_binding.binding.clone());
95+
false
96+
}
97+
})
98+
.each(|field_binding| builder.generate_field_attrs_code(field_binding));
9899

99-
// When generating `set_arg` calls, move data rather than borrow it to avoid
100-
// requiring clones - this must therefore be the last use of each field (for
101-
// example, any formatting machinery that might refer to a field should be
102-
// generated already).
103100
structure.bind_with(|_| synstructure::BindStyle::Move);
104-
let args = structure.each(|field_binding| {
105-
let field = field_binding.ast();
106-
// When a field has attributes like `#[label]` or `#[note]` then it doesn't
107-
// need to be passed as an argument to the diagnostic. But when a field has no
108-
// attributes then it must be passed as an argument to the diagnostic so that
109-
// it can be referred to by Fluent messages.
110-
if field.attrs.is_empty() {
111-
let diag = &builder.diag;
112-
let ident = field_binding.ast().ident.as_ref().unwrap();
113-
quote! {
114-
#diag.set_arg(
115-
stringify!(#ident),
116-
#field_binding
117-
);
118-
}
119-
} else {
120-
quote! {}
121-
}
122-
});
101+
// When a field has attributes like `#[label]` or `#[note]` then it doesn't
102+
// need to be passed as an argument to the diagnostic. But when a field has no
103+
// attributes or a `#[subdiagnostic]` attribute then it must be passed as an
104+
// argument to the diagnostic so that it can be referred to by Fluent messages.
105+
let args = structure
106+
.filter(|field_binding| {
107+
subdiagnostics_or_empty.contains(&field_binding.binding)
108+
})
109+
.each(|field_binding| builder.generate_field_attrs_code(field_binding));
123110

124111
let span = ast.span().unwrap();
125112
let (diag, sess) = (&builder.diag, &builder.sess);
@@ -347,36 +334,60 @@ impl SessionDiagnosticDeriveBuilder {
347334
Ok(tokens.drain(..).collect())
348335
}
349336

350-
fn generate_field_attr_code(
351-
&mut self,
352-
attr: &syn::Attribute,
353-
info: FieldInfo<'_>,
354-
) -> Result<TokenStream, SessionDiagnosticDeriveError> {
355-
let field_binding = &info.binding.binding;
337+
fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
338+
let field = binding_info.ast();
339+
let field_binding = &binding_info.binding;
356340

357-
let inner_ty = FieldInnerTy::from_type(&info.ty);
358-
let name = attr.path.segments.last().unwrap().ident.to_string();
359-
let (binding, needs_destructure) = match (name.as_str(), &inner_ty) {
360-
// `primary_span` can accept a `Vec<Span>` so don't destructure that.
361-
("primary_span", FieldInnerTy::Vec(_)) => (quote! { #field_binding.clone() }, false),
362-
_ => (quote! { *#field_binding }, true),
363-
};
364-
365-
let generated_code = self.generate_inner_field_code(
366-
attr,
367-
FieldInfo {
368-
vis: info.vis,
369-
binding: info.binding,
370-
ty: inner_ty.inner_type().unwrap_or(&info.ty),
371-
span: info.span,
372-
},
373-
binding,
374-
)?;
341+
let inner_ty = FieldInnerTy::from_type(&field.ty);
375342

376-
if needs_destructure {
377-
Ok(inner_ty.with(field_binding, generated_code))
343+
// When generating `set_arg` or `add_subdiagnostic` calls, move data rather than
344+
// borrow it to avoid requiring clones - this must therefore be the last use of
345+
// each field (for example, any formatting machinery that might refer to a field
346+
// should be generated already).
347+
if field.attrs.is_empty() {
348+
let diag = &self.diag;
349+
let ident = field.ident.as_ref().unwrap();
350+
quote! {
351+
#diag.set_arg(
352+
stringify!(#ident),
353+
#field_binding
354+
);
355+
}
378356
} else {
379-
Ok(generated_code)
357+
field
358+
.attrs
359+
.iter()
360+
.map(move |attr| {
361+
let name = attr.path.segments.last().unwrap().ident.to_string();
362+
let (binding, needs_destructure) = match (name.as_str(), &inner_ty) {
363+
// `primary_span` can accept a `Vec<Span>` so don't destructure that.
364+
("primary_span", FieldInnerTy::Vec(_)) => {
365+
(quote! { #field_binding.clone() }, false)
366+
}
367+
// `subdiagnostics` are not derefed because they are bound by value.
368+
("subdiagnostic", _) => (quote! { #field_binding }, true),
369+
_ => (quote! { *#field_binding }, true),
370+
};
371+
372+
let generated_code = self
373+
.generate_inner_field_code(
374+
attr,
375+
FieldInfo {
376+
binding: binding_info,
377+
ty: inner_ty.inner_type().unwrap_or(&field.ty),
378+
span: &field.span(),
379+
},
380+
binding,
381+
)
382+
.unwrap_or_else(|v| v.to_compile_error());
383+
384+
if needs_destructure {
385+
inner_ty.with(field_binding, generated_code)
386+
} else {
387+
generated_code
388+
}
389+
})
390+
.collect()
380391
}
381392
}
382393

compiler/rustc_macros/src/diagnostics/subdiagnostic.rs

-1
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,6 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
303303

304304
let inner_ty = FieldInnerTy::from_type(&ast.ty);
305305
let info = FieldInfo {
306-
vis: &ast.vis,
307306
binding: binding,
308307
ty: inner_ty.inner_type().unwrap_or(&ast.ty),
309308
span: &ast.span(),

compiler/rustc_macros/src/diagnostics/utils.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use proc_macro2::TokenStream;
44
use quote::{format_ident, quote, ToTokens};
55
use std::collections::BTreeSet;
66
use std::str::FromStr;
7-
use syn::{spanned::Spanned, Attribute, Meta, Type, TypeTuple, Visibility};
7+
use syn::{spanned::Spanned, Attribute, Meta, Type, TypeTuple};
88
use synstructure::BindingInfo;
99

1010
/// Checks whether the type name of `ty` matches `name`.
@@ -158,7 +158,6 @@ impl<'ty> FieldInnerTy<'ty> {
158158
/// Field information passed to the builder. Deliberately omits attrs to discourage the
159159
/// `generate_*` methods from walking the attributes themselves.
160160
pub(crate) struct FieldInfo<'a> {
161-
pub(crate) vis: &'a Visibility,
162161
pub(crate) binding: &'a BindingInfo<'a>,
163162
pub(crate) ty: &'a Type,
164163
pub(crate) span: &'a proc_macro2::Span,

compiler/rustc_parse/src/parser/diagnostics.rs

+7-9
Original file line numberDiff line numberDiff line change
@@ -254,23 +254,23 @@ struct AmbiguousPlus {
254254

255255
#[derive(SessionDiagnostic)]
256256
#[error(code = "E0178", slug = "parser-maybe-recover-from-bad-type-plus")]
257-
struct BadTypePlus<'a> {
257+
struct BadTypePlus {
258258
pub ty: String,
259259
#[primary_span]
260260
pub span: Span,
261261
#[subdiagnostic]
262-
pub sub: BadTypePlusSub<'a>,
262+
pub sub: BadTypePlusSub,
263263
}
264264

265-
#[derive(SessionSubdiagnostic, Clone, Copy)]
266-
pub enum BadTypePlusSub<'a> {
265+
#[derive(SessionSubdiagnostic)]
266+
pub enum BadTypePlusSub {
267267
#[suggestion(
268268
slug = "parser-add-paren",
269269
code = "{sum_with_parens}",
270270
applicability = "machine-applicable"
271271
)]
272272
AddParen {
273-
sum_with_parens: &'a str,
273+
sum_with_parens: String,
274274
#[primary_span]
275275
span: Span,
276276
},
@@ -1289,11 +1289,9 @@ impl<'a> Parser<'a> {
12891289
let bounds = self.parse_generic_bounds(None)?;
12901290
let sum_span = ty.span.to(self.prev_token.span);
12911291

1292-
let sum_with_parens: String;
1293-
12941292
let sub = match ty.kind {
12951293
TyKind::Rptr(ref lifetime, ref mut_ty) => {
1296-
sum_with_parens = pprust::to_string(|s| {
1294+
let sum_with_parens = pprust::to_string(|s| {
12971295
s.s.word("&");
12981296
s.print_opt_lifetime(lifetime);
12991297
s.print_mutability(mut_ty.mutbl, false);
@@ -1303,7 +1301,7 @@ impl<'a> Parser<'a> {
13031301
s.pclose()
13041302
});
13051303

1306-
BadTypePlusSub::AddParen { sum_with_parens: &sum_with_parens, span: sum_span }
1304+
BadTypePlusSub::AddParen { sum_with_parens, span: sum_span }
13071305
}
13081306
TyKind::Ptr(..) | TyKind::BareFn(..) => BadTypePlusSub::ForgotParen { span: sum_span },
13091307
_ => BadTypePlusSub::ExpectPath { span: sum_span },

0 commit comments

Comments
 (0)