Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 19e1f73

Browse files
committed
generate set_arg code inside generate_field_attrs_code
1 parent 462c1c8 commit 19e1f73

File tree

1 file changed

+101
-99
lines changed

1 file changed

+101
-99
lines changed

compiler/rustc_macros/src/diagnostics/diagnostic.rs

Lines changed: 101 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -71,91 +71,72 @@ impl<'a> SessionDiagnosticDerive<'a> {
7171
}
7272
};
7373

74-
// Keep track of which fields are subdiagnostics
75-
let mut subdiagnostics = std::collections::HashSet::new();
74+
// Keep track of which fields are subdiagnostics or have no attributes.
75+
let mut subdiagnostics_or_empty = std::collections::HashSet::new();
7676

7777
// Generates calls to `span_label` and similar functions based on the attributes
7878
// on fields. Code for suggestions uses formatting machinery and the value of
7979
// other fields - because any given field can be referenced multiple times, it
80-
// should be accessed through a borrow. When passing fields to `set_arg` (which
81-
// happens below) for Fluent, we want to move the data, so that has to happen
82-
// in a separate pass over the fields.
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.
8383
let attrs = structure
8484
.clone()
85-
// Remove the fields that have a `subdiagnostic` attribute.
8685
.filter(|field_binding| {
87-
field_binding.ast().attrs.iter().all(|attr| {
88-
"subdiagnostic" != attr.path.segments.last().unwrap().ident.to_string()
89-
|| {
90-
subdiagnostics.insert(field_binding.binding.clone());
91-
false
92-
}
93-
})
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+
}
9497
})
9598
.each(|field_binding| {
9699
let field = field_binding.ast();
97-
let result = field.attrs.iter().map(|attr| {
98-
builder
99-
.generate_field_attr_code(
100-
attr,
101-
FieldInfo {
102-
vis: &field.vis,
103-
binding: field_binding,
104-
ty: &field.ty,
105-
span: &field.span(),
106-
},
107-
)
108-
.unwrap_or_else(|v| v.to_compile_error())
109-
});
110-
111-
quote! { #(#result);* }
100+
let field_span = field.span();
101+
102+
builder.generate_field_attrs_code(
103+
&field.attrs,
104+
FieldInfo {
105+
vis: &field.vis,
106+
binding: field_binding,
107+
ty: &field.ty,
108+
span: &field_span,
109+
},
110+
)
112111
});
113112

114-
// When generating `set_arg` calls, move data rather than borrow it to avoid
115-
// requiring clones - this must therefore be the last use of each field (for
116-
// example, any formatting machinery that might refer to a field should be
117-
// generated already).
113+
// When generating `set_arg` or `add_subdiagnostic` calls, move data rather than
114+
// borrow it to avoid requiring clones - this must therefore be the last use of
115+
// each field (for example, any formatting machinery that might refer to a field
116+
// should be generated already).
118117
structure.bind_with(|_| synstructure::BindStyle::Move);
119-
let args = structure.each(|field_binding| {
120-
let field = field_binding.ast();
121-
// When a field has attributes like `#[label]` or `#[note]` then it doesn't
122-
// need to be passed as an argument to the diagnostic. But when a field has no
123-
// attributes then it must be passed as an argument to the diagnostic so that
124-
// it can be referred to by Fluent messages.
125-
let tokens = if field.attrs.is_empty() {
126-
let diag = &builder.diag;
127-
let ident = field_binding.ast().ident.as_ref().unwrap();
128-
quote! {
129-
#diag.set_arg(
130-
stringify!(#ident),
131-
#field_binding
132-
);
133-
}
134-
} else {
135-
quote! {}
136-
};
137-
// If this field had a subdiagnostic attribute, we generate the code here to
138-
// avoid binding it twice.
139-
if subdiagnostics.contains(&field_binding.binding) {
140-
let result = field.attrs.iter().map(|attr| {
141-
builder
142-
.generate_field_attr_code(
143-
attr,
144-
FieldInfo {
145-
vis: &field.vis,
146-
binding: field_binding,
147-
ty: &field.ty,
148-
span: &field.span(),
149-
},
150-
)
151-
.unwrap_or_else(|v| v.to_compile_error())
152-
});
153-
154-
quote! { #(#result);* #tokens }
155-
} else {
156-
tokens
157-
}
158-
});
118+
// When a field has attributes like `#[label]` or `#[note]` then it doesn't
119+
// need to be passed as an argument to the diagnostic. But when a field has no
120+
// attributes or a `#[subdiagnostic]` attribute then it must be passed as an
121+
// argument to the diagnostic so that it can be referred to by Fluent messages.
122+
let args = structure
123+
.filter(|field_binding| {
124+
subdiagnostics_or_empty.contains(&field_binding.binding)
125+
})
126+
.each(|field_binding| {
127+
let field = field_binding.ast();
128+
let field_span = field.span();
129+
130+
builder.generate_field_attrs_code(
131+
&field.attrs,
132+
FieldInfo {
133+
vis: &field.vis,
134+
binding: field_binding,
135+
ty: &field.ty,
136+
span: &field_span,
137+
},
138+
)
139+
});
159140

160141
let span = ast.span().unwrap();
161142
let (diag, sess) = (&builder.diag, &builder.sess);
@@ -383,38 +364,59 @@ impl SessionDiagnosticDeriveBuilder {
383364
Ok(tokens.drain(..).collect())
384365
}
385366

386-
fn generate_field_attr_code(
387-
&mut self,
388-
attr: &syn::Attribute,
389-
info: FieldInfo<'_>,
390-
) -> Result<TokenStream, SessionDiagnosticDeriveError> {
367+
fn generate_field_attrs_code<'a>(
368+
&'a mut self,
369+
attrs: &'a [syn::Attribute],
370+
info: FieldInfo<'a>,
371+
) -> TokenStream {
391372
let field_binding = &info.binding.binding;
392373

393374
let inner_ty = FieldInnerTy::from_type(&info.ty);
394-
let name = attr.path.segments.last().unwrap().ident.to_string();
395-
let (binding, needs_destructure) = match (name.as_str(), &inner_ty) {
396-
// `primary_span` can accept a `Vec<Span>` so don't destructure that.
397-
("primary_span", FieldInnerTy::Vec(_)) => (quote! { #field_binding.clone() }, false),
398-
// `subdiagnostics` are not derefed because they are bound by value.
399-
("subdiagnostic", _) => (quote! { #field_binding }, true),
400-
_ => (quote! { *#field_binding }, true),
401-
};
402-
403-
let generated_code = self.generate_inner_field_code(
404-
attr,
405-
FieldInfo {
406-
vis: info.vis,
407-
binding: info.binding,
408-
ty: inner_ty.inner_type().unwrap_or(&info.ty),
409-
span: info.span,
410-
},
411-
binding,
412-
)?;
413375

414-
if needs_destructure {
415-
Ok(inner_ty.with(field_binding, generated_code))
376+
if attrs.is_empty() {
377+
let diag = &self.diag;
378+
let ident = info.binding.ast().ident.as_ref().unwrap();
379+
quote! {
380+
#diag.set_arg(
381+
stringify!(#ident),
382+
#field_binding
383+
);
384+
}
416385
} else {
417-
Ok(generated_code)
386+
attrs
387+
.iter()
388+
.map(move |attr| {
389+
let name = attr.path.segments.last().unwrap().ident.to_string();
390+
let (binding, needs_destructure) = match (name.as_str(), &inner_ty) {
391+
// `primary_span` can accept a `Vec<Span>` so don't destructure that.
392+
("primary_span", FieldInnerTy::Vec(_)) => {
393+
(quote! { #field_binding.clone() }, false)
394+
}
395+
// `subdiagnostics` are not derefed because they are bound by value.
396+
("subdiagnostic", _) => (quote! { #field_binding }, true),
397+
_ => (quote! { *#field_binding }, true),
398+
};
399+
400+
let generated_code = self
401+
.generate_inner_field_code(
402+
attr,
403+
FieldInfo {
404+
vis: info.vis,
405+
binding: info.binding,
406+
ty: inner_ty.inner_type().unwrap_or(&info.ty),
407+
span: info.span,
408+
},
409+
binding,
410+
)
411+
.unwrap_or_else(|v| v.to_compile_error());
412+
413+
if needs_destructure {
414+
inner_ty.with(field_binding, generated_code)
415+
} else {
416+
generated_code
417+
}
418+
})
419+
.collect()
418420
}
419421
}
420422

0 commit comments

Comments
 (0)