Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid double binding of subdiagnostics inside #[derive(SessionDiagnostic)] #97121

Merged
merged 6 commits into from
May 24, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 56 additions & 18 deletions compiler/rustc_macros/src/diagnostics/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,31 +71,46 @@ impl<'a> SessionDiagnosticDerive<'a> {
}
};

// Keep track of which fields are subdiagnostics
let mut subdiagnostics = std::collections::HashSet::new();

// Generates calls to `span_label` and similar functions based on the attributes
// on fields. Code for suggestions uses formatting machinery and the value of
// other fields - because any given field can be referenced multiple times, it
// should be accessed through a borrow. When passing fields to `set_arg` (which
// happens below) for Fluent, we want to move the data, so that has to happen
// in a separate pass over the fields.
let attrs = structure.each(|field_binding| {
let field = field_binding.ast();
let result = field.attrs.iter().map(|attr| {
builder
.generate_field_attr_code(
attr,
FieldInfo {
vis: &field.vis,
binding: field_binding,
ty: &field.ty,
span: &field.span(),
},
)
.unwrap_or_else(|v| v.to_compile_error())
let attrs = structure
.clone()
// Remove the fields that have a `subdiagnostic` attribute.
.filter(|field_binding| {
pvdrz marked this conversation as resolved.
Show resolved Hide resolved
field_binding.ast().attrs.iter().all(|attr| {
"subdiagnostic" != attr.path.segments.last().unwrap().ident.to_string()
|| {
subdiagnostics.insert(field_binding.binding.clone());
false
}
})
})
.each(|field_binding| {
let field = field_binding.ast();
let result = field.attrs.iter().map(|attr| {
builder
.generate_field_attr_code(
attr,
FieldInfo {
vis: &field.vis,
binding: field_binding,
ty: &field.ty,
span: &field.span(),
},
)
.unwrap_or_else(|v| v.to_compile_error())
});

quote! { #(#result);* }
});

quote! { #(#result);* }
});

// When generating `set_arg` calls, move data rather than borrow it to avoid
// requiring clones - this must therefore be the last use of each field (for
// example, any formatting machinery that might refer to a field should be
Expand All @@ -107,7 +122,7 @@ impl<'a> SessionDiagnosticDerive<'a> {
// need to be passed as an argument to the diagnostic. But when a field has no
// attributes then it must be passed as an argument to the diagnostic so that
// it can be referred to by Fluent messages.
if field.attrs.is_empty() {
let tokens = if field.attrs.is_empty() {
let diag = &builder.diag;
let ident = field_binding.ast().ident.as_ref().unwrap();
quote! {
Expand All @@ -118,6 +133,27 @@ impl<'a> SessionDiagnosticDerive<'a> {
}
} else {
quote! {}
};
// If this field had a subdiagnostic attribute, we generate the code here to
// avoid binding it twice.
if subdiagnostics.contains(&field_binding.binding) {
let result = field.attrs.iter().map(|attr| {
builder
.generate_field_attr_code(
attr,
FieldInfo {
vis: &field.vis,
binding: field_binding,
ty: &field.ty,
span: &field.span(),
},
)
.unwrap_or_else(|v| v.to_compile_error())
});

quote! { #(#result);* #tokens }
} else {
tokens
}
});

Expand Down Expand Up @@ -359,6 +395,8 @@ impl SessionDiagnosticDeriveBuilder {
let (binding, needs_destructure) = match (name.as_str(), &inner_ty) {
// `primary_span` can accept a `Vec<Span>` so don't destructure that.
("primary_span", FieldInnerTy::Vec(_)) => (quote! { #field_binding.clone() }, false),
// `subdiagnostics` are not derefed because they are bound by value.
("subdiagnostic", _) => (quote! { #field_binding }, true),
_ => (quote! { *#field_binding }, true),
};

Expand Down
16 changes: 7 additions & 9 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,23 +254,23 @@ struct AmbiguousPlus {

#[derive(SessionDiagnostic)]
#[error(code = "E0178", slug = "parser-maybe-recover-from-bad-type-plus")]
struct BadTypePlus<'a> {
struct BadTypePlus {
pub ty: String,
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub sub: BadTypePlusSub<'a>,
pub sub: BadTypePlusSub,
}

#[derive(SessionSubdiagnostic, Clone, Copy)]
pub enum BadTypePlusSub<'a> {
#[derive(SessionSubdiagnostic)]
pub enum BadTypePlusSub {
#[suggestion(
slug = "parser-add-paren",
code = "{sum_with_parens}",
applicability = "machine-applicable"
)]
AddParen {
sum_with_parens: &'a str,
sum_with_parens: String,
#[primary_span]
span: Span,
},
Expand Down Expand Up @@ -1289,11 +1289,9 @@ impl<'a> Parser<'a> {
let bounds = self.parse_generic_bounds(None)?;
let sum_span = ty.span.to(self.prev_token.span);

let sum_with_parens: String;

let sub = match ty.kind {
TyKind::Rptr(ref lifetime, ref mut_ty) => {
sum_with_parens = pprust::to_string(|s| {
let sum_with_parens = pprust::to_string(|s| {
s.s.word("&");
s.print_opt_lifetime(lifetime);
s.print_mutability(mut_ty.mutbl, false);
Expand All @@ -1303,7 +1301,7 @@ impl<'a> Parser<'a> {
s.pclose()
});

BadTypePlusSub::AddParen { sum_with_parens: &sum_with_parens, span: sum_span }
BadTypePlusSub::AddParen { sum_with_parens, span: sum_span }
}
TyKind::Ptr(..) | TyKind::BareFn(..) => BadTypePlusSub::ForgotParen { span: sum_span },
_ => BadTypePlusSub::ExpectPath { span: sum_span },
Expand Down