2022-04-27 01:57:44 +00:00
|
|
|
#![deny(unused_must_use)]
|
|
|
|
|
2022-04-27 03:06:13 +00:00
|
|
|
use crate::diagnostics::error::{
|
2022-09-23 11:49:02 +00:00
|
|
|
invalid_attr, span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err,
|
|
|
|
DiagnosticDeriveError,
|
2022-04-27 03:06:13 +00:00
|
|
|
};
|
2022-04-27 01:57:44 +00:00
|
|
|
use crate::diagnostics::utils::{
|
macros: separate suggestion fmt'ing and emission
Diagnostic derives have previously had to take special care when
ordering the generated code so that fields were not used after a move.
This is unlikely for most fields because a field is either annotated
with a subdiagnostic attribute and is thus likely a `Span` and copiable,
or is a argument, in which case it is only used once by `set_arg`
anyway.
However, format strings for code in suggestions can result in fields
being used after being moved if not ordered carefully. As a result, the
derive currently puts `set_arg` calls last (just before emission), such
as:
```rust
let diag = { /* create diagnostic */ };
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
format!("{}", __binding_0),
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.emit();
```
For eager translation, this doesn't work, as the message being
translated eagerly can assume that all arguments are available - so
arguments _must_ be set first.
Format strings for suggestion code are now separated into two parts - an
initialization line that performs the formatting into a variable, and a
usage in the subdiagnostic addition.
By separating these parts, the initialization can happen before
arguments are set, preserving the desired order so that code compiles,
while still enabling arguments to be set before subdiagnostics are
added.
```rust
let diag = { /* create diagnostic */ };
let __code_0 = format!("{}", __binding_0);
/* + other formatting */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
__code_0,
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.emit();
```
Signed-off-by: David Wood <david.wood@huawei.com>
2022-10-03 13:28:02 +00:00
|
|
|
build_field_mapping, new_code_ident, report_error_if_not_applied_to_applicability,
|
2022-09-23 11:49:02 +00:00
|
|
|
report_error_if_not_applied_to_span, FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce,
|
|
|
|
SpannedOption, SubdiagnosticKind,
|
2022-04-27 01:57:44 +00:00
|
|
|
};
|
|
|
|
use proc_macro2::TokenStream;
|
|
|
|
use quote::{format_ident, quote};
|
2022-09-01 17:42:49 +00:00
|
|
|
use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path};
|
2022-04-27 01:57:44 +00:00
|
|
|
use synstructure::{BindingInfo, Structure, VariantInfo};
|
|
|
|
|
2022-04-27 04:24:31 +00:00
|
|
|
/// The central struct for constructing the `add_to_diagnostic` method from an annotated struct.
|
2022-10-03 13:09:05 +00:00
|
|
|
pub(crate) struct SubdiagnosticDeriveBuilder {
|
2022-04-27 01:57:44 +00:00
|
|
|
diag: syn::Ident,
|
2022-10-03 13:09:05 +00:00
|
|
|
f: syn::Ident,
|
2022-04-27 01:57:44 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 13:09:05 +00:00
|
|
|
impl SubdiagnosticDeriveBuilder {
|
|
|
|
pub(crate) fn new() -> Self {
|
2022-04-27 01:57:44 +00:00
|
|
|
let diag = format_ident!("diag");
|
2022-10-03 13:09:05 +00:00
|
|
|
let f = format_ident!("f");
|
|
|
|
Self { diag, f }
|
2022-04-27 01:57:44 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 13:09:05 +00:00
|
|
|
pub(crate) fn into_tokens<'a>(self, mut structure: Structure<'a>) -> TokenStream {
|
2022-04-27 01:57:44 +00:00
|
|
|
let implementation = {
|
|
|
|
let ast = structure.ast();
|
|
|
|
let span = ast.span().unwrap();
|
|
|
|
match ast.data {
|
|
|
|
syn::Data::Struct(..) | syn::Data::Enum(..) => (),
|
|
|
|
syn::Data::Union(..) => {
|
|
|
|
span_err(
|
|
|
|
span,
|
2022-09-18 15:47:31 +00:00
|
|
|
"`#[derive(Subdiagnostic)]` can only be used on structs and enums",
|
2022-04-27 01:57:44 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if matches!(ast.data, syn::Data::Enum(..)) {
|
|
|
|
for attr in &ast.attrs {
|
|
|
|
span_err(
|
|
|
|
attr.span().unwrap(),
|
|
|
|
"unsupported type attribute for subdiagnostic enum",
|
|
|
|
)
|
|
|
|
.emit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
structure.bind_with(|_| synstructure::BindStyle::Move);
|
|
|
|
let variants_ = structure.each_variant(|variant| {
|
2022-10-03 13:09:05 +00:00
|
|
|
let mut builder = SubdiagnosticDeriveVariantBuilder {
|
|
|
|
parent: &self,
|
2022-04-27 01:57:44 +00:00
|
|
|
variant,
|
|
|
|
span,
|
macros: separate suggestion fmt'ing and emission
Diagnostic derives have previously had to take special care when
ordering the generated code so that fields were not used after a move.
This is unlikely for most fields because a field is either annotated
with a subdiagnostic attribute and is thus likely a `Span` and copiable,
or is a argument, in which case it is only used once by `set_arg`
anyway.
However, format strings for code in suggestions can result in fields
being used after being moved if not ordered carefully. As a result, the
derive currently puts `set_arg` calls last (just before emission), such
as:
```rust
let diag = { /* create diagnostic */ };
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
format!("{}", __binding_0),
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.emit();
```
For eager translation, this doesn't work, as the message being
translated eagerly can assume that all arguments are available - so
arguments _must_ be set first.
Format strings for suggestion code are now separated into two parts - an
initialization line that performs the formatting into a variable, and a
usage in the subdiagnostic addition.
By separating these parts, the initialization can happen before
arguments are set, preserving the desired order so that code compiles,
while still enabling arguments to be set before subdiagnostics are
added.
```rust
let diag = { /* create diagnostic */ };
let __code_0 = format!("{}", __binding_0);
/* + other formatting */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
__code_0,
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.emit();
```
Signed-off-by: David Wood <david.wood@huawei.com>
2022-10-03 13:28:02 +00:00
|
|
|
formatting_init: TokenStream::new(),
|
2022-09-23 11:49:02 +00:00
|
|
|
fields: build_field_mapping(variant),
|
2022-04-27 01:57:44 +00:00
|
|
|
span_field: None,
|
|
|
|
applicability: None,
|
2022-09-01 17:42:49 +00:00
|
|
|
has_suggestion_parts: false,
|
2022-04-27 01:57:44 +00:00
|
|
|
};
|
|
|
|
builder.into_tokens().unwrap_or_else(|v| v.to_compile_error())
|
|
|
|
});
|
|
|
|
|
|
|
|
quote! {
|
|
|
|
match self {
|
|
|
|
#variants_
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-10-03 13:09:05 +00:00
|
|
|
let diag = &self.diag;
|
|
|
|
let f = &self.f;
|
2022-04-27 01:57:44 +00:00
|
|
|
let ret = structure.gen_impl(quote! {
|
2022-09-18 15:46:16 +00:00
|
|
|
gen impl rustc_errors::AddToDiagnostic for @Self {
|
2022-10-03 13:09:05 +00:00
|
|
|
fn add_to_diagnostic_with<__F>(self, #diag: &mut rustc_errors::Diagnostic, #f: __F)
|
|
|
|
where
|
|
|
|
__F: Fn(
|
|
|
|
&mut rustc_errors::Diagnostic,
|
|
|
|
rustc_errors::SubdiagnosticMessage
|
|
|
|
) -> rustc_errors::SubdiagnosticMessage,
|
|
|
|
{
|
2022-04-27 01:57:44 +00:00
|
|
|
use rustc_errors::{Applicability, IntoDiagnosticArg};
|
|
|
|
#implementation
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
ret
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-27 04:24:31 +00:00
|
|
|
/// Tracks persistent information required for building up the call to add to the diagnostic
|
2022-09-18 15:47:31 +00:00
|
|
|
/// for the final generated method. This is a separate struct to `SubdiagnosticDerive`
|
2022-04-27 04:24:31 +00:00
|
|
|
/// only to be able to destructure and split `self.builder` and the `self.structure` up to avoid a
|
|
|
|
/// double mut borrow later on.
|
2022-10-03 13:09:05 +00:00
|
|
|
struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
2022-04-27 01:57:44 +00:00
|
|
|
/// The identifier to use for the generated `DiagnosticBuilder` instance.
|
2022-10-03 13:09:05 +00:00
|
|
|
parent: &'parent SubdiagnosticDeriveBuilder,
|
2022-04-27 01:57:44 +00:00
|
|
|
|
|
|
|
/// Info for the current variant (or the type if not an enum).
|
|
|
|
variant: &'a VariantInfo<'a>,
|
|
|
|
/// Span for the entire type.
|
|
|
|
span: proc_macro::Span,
|
|
|
|
|
macros: separate suggestion fmt'ing and emission
Diagnostic derives have previously had to take special care when
ordering the generated code so that fields were not used after a move.
This is unlikely for most fields because a field is either annotated
with a subdiagnostic attribute and is thus likely a `Span` and copiable,
or is a argument, in which case it is only used once by `set_arg`
anyway.
However, format strings for code in suggestions can result in fields
being used after being moved if not ordered carefully. As a result, the
derive currently puts `set_arg` calls last (just before emission), such
as:
```rust
let diag = { /* create diagnostic */ };
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
format!("{}", __binding_0),
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.emit();
```
For eager translation, this doesn't work, as the message being
translated eagerly can assume that all arguments are available - so
arguments _must_ be set first.
Format strings for suggestion code are now separated into two parts - an
initialization line that performs the formatting into a variable, and a
usage in the subdiagnostic addition.
By separating these parts, the initialization can happen before
arguments are set, preserving the desired order so that code compiles,
while still enabling arguments to be set before subdiagnostics are
added.
```rust
let diag = { /* create diagnostic */ };
let __code_0 = format!("{}", __binding_0);
/* + other formatting */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
__code_0,
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.emit();
```
Signed-off-by: David Wood <david.wood@huawei.com>
2022-10-03 13:28:02 +00:00
|
|
|
/// Initialization of format strings for code suggestions.
|
|
|
|
formatting_init: TokenStream,
|
|
|
|
|
2022-04-27 01:57:44 +00:00
|
|
|
/// Store a map of field name to its corresponding field. This is built on construction of the
|
|
|
|
/// derive builder.
|
2022-09-23 11:49:02 +00:00
|
|
|
fields: FieldMap,
|
2022-04-27 01:57:44 +00:00
|
|
|
|
|
|
|
/// Identifier for the binding to the `#[primary_span]` field.
|
2022-09-11 16:30:18 +00:00
|
|
|
span_field: SpannedOption<proc_macro2::Ident>,
|
2022-09-12 18:44:28 +00:00
|
|
|
|
|
|
|
/// The binding to the `#[applicability]` field, if present.
|
2022-09-11 16:30:18 +00:00
|
|
|
applicability: SpannedOption<TokenStream>,
|
2022-09-01 17:42:49 +00:00
|
|
|
|
|
|
|
/// Set to true when a `#[suggestion_part]` field is encountered, used to generate an error
|
|
|
|
/// during finalization if still `false`.
|
|
|
|
has_suggestion_parts: bool,
|
2022-04-27 01:57:44 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 13:09:05 +00:00
|
|
|
impl<'parent, 'a> HasFieldMap for SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
2022-04-27 01:57:44 +00:00
|
|
|
fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
|
|
|
|
self.fields.get(field)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-01 18:53:59 +00:00
|
|
|
/// Provides frequently-needed information about the diagnostic kinds being derived for this type.
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
struct KindsStatistics {
|
|
|
|
has_multipart_suggestion: bool,
|
|
|
|
all_multipart_suggestions: bool,
|
|
|
|
has_normal_suggestion: bool,
|
2022-09-12 18:44:28 +00:00
|
|
|
all_applicabilities_static: bool,
|
2022-09-01 18:53:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromIterator<&'a SubdiagnosticKind> for KindsStatistics {
|
|
|
|
fn from_iter<T: IntoIterator<Item = &'a SubdiagnosticKind>>(kinds: T) -> Self {
|
|
|
|
let mut ret = Self {
|
|
|
|
has_multipart_suggestion: false,
|
|
|
|
all_multipart_suggestions: true,
|
|
|
|
has_normal_suggestion: false,
|
2022-09-12 18:44:28 +00:00
|
|
|
all_applicabilities_static: true,
|
2022-09-01 18:53:59 +00:00
|
|
|
};
|
2022-09-12 18:44:28 +00:00
|
|
|
|
2022-09-01 18:53:59 +00:00
|
|
|
for kind in kinds {
|
2022-09-12 18:44:28 +00:00
|
|
|
if let SubdiagnosticKind::MultipartSuggestion { applicability: None, .. }
|
|
|
|
| SubdiagnosticKind::Suggestion { applicability: None, .. } = kind
|
|
|
|
{
|
|
|
|
ret.all_applicabilities_static = false;
|
|
|
|
}
|
2022-09-01 18:53:59 +00:00
|
|
|
if let SubdiagnosticKind::MultipartSuggestion { .. } = kind {
|
|
|
|
ret.has_multipart_suggestion = true;
|
|
|
|
} else {
|
|
|
|
ret.all_multipart_suggestions = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let SubdiagnosticKind::Suggestion { .. } = kind {
|
|
|
|
ret.has_normal_suggestion = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ret
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-03 13:09:05 +00:00
|
|
|
impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
2022-09-01 18:53:59 +00:00
|
|
|
fn identify_kind(&mut self) -> Result<Vec<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> {
|
|
|
|
let mut kind_slugs = vec![];
|
2022-09-01 17:42:49 +00:00
|
|
|
|
|
|
|
for attr in self.variant.ast().attrs {
|
2022-09-12 18:44:28 +00:00
|
|
|
let (kind, slug) = SubdiagnosticKind::from_attr(attr, self)?;
|
2022-08-23 05:54:06 +00:00
|
|
|
|
2022-09-12 18:44:28 +00:00
|
|
|
let Some(slug) = slug else {
|
|
|
|
let name = attr.path.segments.last().unwrap().ident.to_string();
|
|
|
|
let name = name.as_str();
|
2022-09-01 17:42:49 +00:00
|
|
|
|
2022-04-27 01:57:44 +00:00
|
|
|
throw_span_err!(
|
2022-09-12 18:44:28 +00:00
|
|
|
attr.span().unwrap(),
|
2022-06-23 15:12:52 +00:00
|
|
|
&format!(
|
|
|
|
"diagnostic slug must be first argument of a `#[{}(...)]` attribute",
|
|
|
|
name
|
|
|
|
)
|
2022-04-27 01:57:44 +00:00
|
|
|
);
|
2022-09-01 17:42:49 +00:00
|
|
|
};
|
|
|
|
|
2022-09-12 18:44:28 +00:00
|
|
|
kind_slugs.push((kind, slug));
|
2022-04-27 01:57:44 +00:00
|
|
|
}
|
|
|
|
|
2022-09-01 18:53:59 +00:00
|
|
|
Ok(kind_slugs)
|
2022-09-01 17:42:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Generates the code for a field with no attributes.
|
|
|
|
fn generate_field_set_arg(&mut self, binding: &BindingInfo<'_>) -> TokenStream {
|
|
|
|
let ast = binding.ast();
|
|
|
|
assert_eq!(ast.attrs.len(), 0, "field with attribute used as diagnostic arg");
|
|
|
|
|
2022-10-03 13:09:05 +00:00
|
|
|
let diag = &self.parent.diag;
|
2022-09-01 17:42:49 +00:00
|
|
|
let ident = ast.ident.as_ref().unwrap();
|
2022-09-14 18:12:22 +00:00
|
|
|
// strip `r#` prefix, if present
|
|
|
|
let ident = format_ident!("{}", ident);
|
|
|
|
|
2022-09-01 17:42:49 +00:00
|
|
|
quote! {
|
|
|
|
#diag.set_arg(
|
|
|
|
stringify!(#ident),
|
|
|
|
#binding
|
|
|
|
);
|
|
|
|
}
|
2022-08-23 05:54:06 +00:00
|
|
|
}
|
|
|
|
|
2022-09-01 17:42:49 +00:00
|
|
|
/// Generates the necessary code for all attributes on a field.
|
|
|
|
fn generate_field_attr_code(
|
2022-04-27 01:57:44 +00:00
|
|
|
&mut self,
|
|
|
|
binding: &BindingInfo<'_>,
|
2022-09-01 18:53:59 +00:00
|
|
|
kind_stats: KindsStatistics,
|
2022-09-01 17:42:49 +00:00
|
|
|
) -> TokenStream {
|
2022-04-27 01:57:44 +00:00
|
|
|
let ast = binding.ast();
|
2022-09-01 17:42:49 +00:00
|
|
|
assert!(ast.attrs.len() > 0, "field without attributes generating attr code");
|
2022-04-27 01:57:44 +00:00
|
|
|
|
2022-09-01 17:42:49 +00:00
|
|
|
// Abstract over `Vec<T>` and `Option<T>` fields using `FieldInnerTy`, which will
|
|
|
|
// apply the generated code on each element in the `Vec` or `Option`.
|
2022-05-06 02:43:30 +00:00
|
|
|
let inner_ty = FieldInnerTy::from_type(&ast.ty);
|
2022-09-01 17:42:49 +00:00
|
|
|
ast.attrs
|
|
|
|
.iter()
|
|
|
|
.map(|attr| {
|
|
|
|
let info = FieldInfo {
|
|
|
|
binding,
|
|
|
|
ty: inner_ty.inner_type().unwrap_or(&ast.ty),
|
|
|
|
span: &ast.span(),
|
|
|
|
};
|
2022-08-23 05:54:06 +00:00
|
|
|
|
2022-09-01 17:42:49 +00:00
|
|
|
let generated = self
|
macros: separate suggestion fmt'ing and emission
Diagnostic derives have previously had to take special care when
ordering the generated code so that fields were not used after a move.
This is unlikely for most fields because a field is either annotated
with a subdiagnostic attribute and is thus likely a `Span` and copiable,
or is a argument, in which case it is only used once by `set_arg`
anyway.
However, format strings for code in suggestions can result in fields
being used after being moved if not ordered carefully. As a result, the
derive currently puts `set_arg` calls last (just before emission), such
as:
```rust
let diag = { /* create diagnostic */ };
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
format!("{}", __binding_0),
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.emit();
```
For eager translation, this doesn't work, as the message being
translated eagerly can assume that all arguments are available - so
arguments _must_ be set first.
Format strings for suggestion code are now separated into two parts - an
initialization line that performs the formatting into a variable, and a
usage in the subdiagnostic addition.
By separating these parts, the initialization can happen before
arguments are set, preserving the desired order so that code compiles,
while still enabling arguments to be set before subdiagnostics are
added.
```rust
let diag = { /* create diagnostic */ };
let __code_0 = format!("{}", __binding_0);
/* + other formatting */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
__code_0,
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.emit();
```
Signed-off-by: David Wood <david.wood@huawei.com>
2022-10-03 13:28:02 +00:00
|
|
|
.generate_field_code_inner(kind_stats, attr, info, inner_ty.will_iterate())
|
2022-09-01 17:42:49 +00:00
|
|
|
.unwrap_or_else(|v| v.to_compile_error());
|
2022-08-23 05:54:06 +00:00
|
|
|
|
2022-09-01 17:42:49 +00:00
|
|
|
inner_ty.with(binding, generated)
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn generate_field_code_inner(
|
|
|
|
&mut self,
|
2022-09-01 18:53:59 +00:00
|
|
|
kind_stats: KindsStatistics,
|
2022-09-01 17:42:49 +00:00
|
|
|
attr: &Attribute,
|
|
|
|
info: FieldInfo<'_>,
|
macros: separate suggestion fmt'ing and emission
Diagnostic derives have previously had to take special care when
ordering the generated code so that fields were not used after a move.
This is unlikely for most fields because a field is either annotated
with a subdiagnostic attribute and is thus likely a `Span` and copiable,
or is a argument, in which case it is only used once by `set_arg`
anyway.
However, format strings for code in suggestions can result in fields
being used after being moved if not ordered carefully. As a result, the
derive currently puts `set_arg` calls last (just before emission), such
as:
```rust
let diag = { /* create diagnostic */ };
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
format!("{}", __binding_0),
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.emit();
```
For eager translation, this doesn't work, as the message being
translated eagerly can assume that all arguments are available - so
arguments _must_ be set first.
Format strings for suggestion code are now separated into two parts - an
initialization line that performs the formatting into a variable, and a
usage in the subdiagnostic addition.
By separating these parts, the initialization can happen before
arguments are set, preserving the desired order so that code compiles,
while still enabling arguments to be set before subdiagnostics are
added.
```rust
let diag = { /* create diagnostic */ };
let __code_0 = format!("{}", __binding_0);
/* + other formatting */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
__code_0,
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.emit();
```
Signed-off-by: David Wood <david.wood@huawei.com>
2022-10-03 13:28:02 +00:00
|
|
|
clone_suggestion_code: bool,
|
2022-09-01 17:42:49 +00:00
|
|
|
) -> Result<TokenStream, DiagnosticDeriveError> {
|
|
|
|
let meta = attr.parse_meta()?;
|
|
|
|
match meta {
|
2022-09-01 18:53:59 +00:00
|
|
|
Meta::Path(path) => self.generate_field_code_inner_path(kind_stats, attr, info, path),
|
macros: separate suggestion fmt'ing and emission
Diagnostic derives have previously had to take special care when
ordering the generated code so that fields were not used after a move.
This is unlikely for most fields because a field is either annotated
with a subdiagnostic attribute and is thus likely a `Span` and copiable,
or is a argument, in which case it is only used once by `set_arg`
anyway.
However, format strings for code in suggestions can result in fields
being used after being moved if not ordered carefully. As a result, the
derive currently puts `set_arg` calls last (just before emission), such
as:
```rust
let diag = { /* create diagnostic */ };
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
format!("{}", __binding_0),
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.emit();
```
For eager translation, this doesn't work, as the message being
translated eagerly can assume that all arguments are available - so
arguments _must_ be set first.
Format strings for suggestion code are now separated into two parts - an
initialization line that performs the formatting into a variable, and a
usage in the subdiagnostic addition.
By separating these parts, the initialization can happen before
arguments are set, preserving the desired order so that code compiles,
while still enabling arguments to be set before subdiagnostics are
added.
```rust
let diag = { /* create diagnostic */ };
let __code_0 = format!("{}", __binding_0);
/* + other formatting */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
__code_0,
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.emit();
```
Signed-off-by: David Wood <david.wood@huawei.com>
2022-10-03 13:28:02 +00:00
|
|
|
Meta::List(list @ MetaList { .. }) => self.generate_field_code_inner_list(
|
|
|
|
kind_stats,
|
|
|
|
attr,
|
|
|
|
info,
|
|
|
|
list,
|
|
|
|
clone_suggestion_code,
|
|
|
|
),
|
2022-09-01 17:42:49 +00:00
|
|
|
_ => throw_invalid_attr!(attr, &meta),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Generates the code for a `[Meta::Path]`-like attribute on a field (e.g. `#[primary_span]`).
|
|
|
|
fn generate_field_code_inner_path(
|
|
|
|
&mut self,
|
2022-09-01 18:53:59 +00:00
|
|
|
kind_stats: KindsStatistics,
|
2022-09-01 17:42:49 +00:00
|
|
|
attr: &Attribute,
|
|
|
|
info: FieldInfo<'_>,
|
|
|
|
path: Path,
|
|
|
|
) -> Result<TokenStream, DiagnosticDeriveError> {
|
|
|
|
let span = attr.span().unwrap();
|
|
|
|
let ident = &path.segments.last().unwrap().ident;
|
|
|
|
let name = ident.to_string();
|
|
|
|
let name = name.as_str();
|
|
|
|
|
|
|
|
match name {
|
|
|
|
"skip_arg" => Ok(quote! {}),
|
|
|
|
"primary_span" => {
|
2022-09-01 18:53:59 +00:00
|
|
|
if kind_stats.has_multipart_suggestion {
|
2022-09-12 19:11:49 +00:00
|
|
|
invalid_attr(attr, &Meta::Path(path))
|
|
|
|
.help(
|
2022-09-01 17:42:49 +00:00
|
|
|
"multipart suggestions use one or more `#[suggestion_part]`s rather \
|
|
|
|
than one `#[primary_span]`",
|
2022-08-23 05:54:06 +00:00
|
|
|
)
|
2022-09-12 19:11:49 +00:00
|
|
|
.emit();
|
|
|
|
} else {
|
|
|
|
report_error_if_not_applied_to_span(attr, &info)?;
|
2022-09-01 17:42:49 +00:00
|
|
|
|
2022-09-12 19:11:49 +00:00
|
|
|
let binding = info.binding.binding.clone();
|
|
|
|
self.span_field.set_once(binding, span);
|
|
|
|
}
|
2022-09-01 17:42:49 +00:00
|
|
|
|
|
|
|
Ok(quote! {})
|
|
|
|
}
|
|
|
|
"suggestion_part" => {
|
|
|
|
self.has_suggestion_parts = true;
|
|
|
|
|
2022-09-01 18:53:59 +00:00
|
|
|
if kind_stats.has_multipart_suggestion {
|
|
|
|
span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`")
|
2022-09-01 17:42:49 +00:00
|
|
|
.emit();
|
2022-09-01 18:53:59 +00:00
|
|
|
} else {
|
2022-09-12 19:11:49 +00:00
|
|
|
invalid_attr(attr, &Meta::Path(path))
|
|
|
|
.help(
|
|
|
|
"`#[suggestion_part(...)]` is only valid in multipart suggestions, \
|
|
|
|
use `#[primary_span]` instead",
|
|
|
|
)
|
|
|
|
.emit();
|
2022-09-01 17:42:49 +00:00
|
|
|
}
|
2022-09-12 19:11:49 +00:00
|
|
|
|
|
|
|
Ok(quote! {})
|
2022-08-23 03:19:38 +00:00
|
|
|
}
|
2022-09-01 17:42:49 +00:00
|
|
|
"applicability" => {
|
2022-09-01 18:53:59 +00:00
|
|
|
if kind_stats.has_multipart_suggestion || kind_stats.has_normal_suggestion {
|
2022-09-01 17:42:49 +00:00
|
|
|
report_error_if_not_applied_to_applicability(attr, &info)?;
|
|
|
|
|
2022-09-12 18:44:28 +00:00
|
|
|
if kind_stats.all_applicabilities_static {
|
|
|
|
span_err(
|
|
|
|
span,
|
|
|
|
"`#[applicability]` has no effect if all `#[suggestion]`/\
|
|
|
|
`#[multipart_suggestion]` attributes have a static \
|
|
|
|
`applicability = \"...\"`",
|
|
|
|
)
|
|
|
|
.emit();
|
|
|
|
}
|
2022-09-01 17:42:49 +00:00
|
|
|
let binding = info.binding.binding.clone();
|
2022-09-11 16:30:18 +00:00
|
|
|
self.applicability.set_once(quote! { #binding }, span);
|
2022-09-01 17:42:49 +00:00
|
|
|
} else {
|
|
|
|
span_err(span, "`#[applicability]` is only valid on suggestions").emit();
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(quote! {})
|
|
|
|
}
|
2022-09-12 19:11:49 +00:00
|
|
|
_ => {
|
2022-09-01 18:53:59 +00:00
|
|
|
let mut span_attrs = vec![];
|
|
|
|
if kind_stats.has_multipart_suggestion {
|
|
|
|
span_attrs.push("suggestion_part");
|
|
|
|
}
|
|
|
|
if !kind_stats.all_multipart_suggestions {
|
|
|
|
span_attrs.push("primary_span")
|
|
|
|
}
|
2022-09-12 19:11:49 +00:00
|
|
|
|
|
|
|
invalid_attr(attr, &Meta::Path(path))
|
|
|
|
.help(format!(
|
|
|
|
"only `{}`, `applicability` and `skip_arg` are valid field attributes",
|
|
|
|
span_attrs.join(", ")
|
|
|
|
))
|
|
|
|
.emit();
|
|
|
|
|
|
|
|
Ok(quote! {})
|
|
|
|
}
|
2022-08-23 03:19:38 +00:00
|
|
|
}
|
2022-09-01 17:42:49 +00:00
|
|
|
}
|
2022-08-23 05:54:06 +00:00
|
|
|
|
2022-09-01 17:42:49 +00:00
|
|
|
/// Generates the code for a `[Meta::List]`-like attribute on a field (e.g.
|
|
|
|
/// `#[suggestion_part(code = "...")]`).
|
|
|
|
fn generate_field_code_inner_list(
|
|
|
|
&mut self,
|
2022-09-01 18:53:59 +00:00
|
|
|
kind_stats: KindsStatistics,
|
2022-09-01 17:42:49 +00:00
|
|
|
attr: &Attribute,
|
|
|
|
info: FieldInfo<'_>,
|
|
|
|
list: MetaList,
|
macros: separate suggestion fmt'ing and emission
Diagnostic derives have previously had to take special care when
ordering the generated code so that fields were not used after a move.
This is unlikely for most fields because a field is either annotated
with a subdiagnostic attribute and is thus likely a `Span` and copiable,
or is a argument, in which case it is only used once by `set_arg`
anyway.
However, format strings for code in suggestions can result in fields
being used after being moved if not ordered carefully. As a result, the
derive currently puts `set_arg` calls last (just before emission), such
as:
```rust
let diag = { /* create diagnostic */ };
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
format!("{}", __binding_0),
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.emit();
```
For eager translation, this doesn't work, as the message being
translated eagerly can assume that all arguments are available - so
arguments _must_ be set first.
Format strings for suggestion code are now separated into two parts - an
initialization line that performs the formatting into a variable, and a
usage in the subdiagnostic addition.
By separating these parts, the initialization can happen before
arguments are set, preserving the desired order so that code compiles,
while still enabling arguments to be set before subdiagnostics are
added.
```rust
let diag = { /* create diagnostic */ };
let __code_0 = format!("{}", __binding_0);
/* + other formatting */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
__code_0,
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.emit();
```
Signed-off-by: David Wood <david.wood@huawei.com>
2022-10-03 13:28:02 +00:00
|
|
|
clone_suggestion_code: bool,
|
2022-09-01 17:42:49 +00:00
|
|
|
) -> Result<TokenStream, DiagnosticDeriveError> {
|
|
|
|
let span = attr.span().unwrap();
|
|
|
|
let ident = &list.path.segments.last().unwrap().ident;
|
|
|
|
let name = ident.to_string();
|
|
|
|
let name = name.as_str();
|
|
|
|
|
|
|
|
match name {
|
|
|
|
"suggestion_part" => {
|
2022-09-01 18:53:59 +00:00
|
|
|
if !kind_stats.has_multipart_suggestion {
|
2022-09-01 17:42:49 +00:00
|
|
|
throw_invalid_attr!(attr, &Meta::List(list), |diag| {
|
|
|
|
diag.help(
|
|
|
|
"`#[suggestion_part(...)]` is only valid in multipart suggestions",
|
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
2022-04-27 01:57:44 +00:00
|
|
|
|
2022-09-01 17:42:49 +00:00
|
|
|
self.has_suggestion_parts = true;
|
|
|
|
|
|
|
|
report_error_if_not_applied_to_span(attr, &info)?;
|
|
|
|
|
|
|
|
let mut code = None;
|
|
|
|
for nested_attr in list.nested.iter() {
|
|
|
|
let NestedMeta::Meta(ref meta) = nested_attr else {
|
|
|
|
throw_invalid_nested_attr!(attr, &nested_attr);
|
|
|
|
};
|
|
|
|
|
|
|
|
let span = meta.span().unwrap();
|
|
|
|
let nested_name = meta.path().segments.last().unwrap().ident.to_string();
|
|
|
|
let nested_name = nested_name.as_str();
|
|
|
|
|
|
|
|
let Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) = meta else {
|
|
|
|
throw_invalid_nested_attr!(attr, &nested_attr);
|
|
|
|
};
|
|
|
|
|
|
|
|
match nested_name {
|
|
|
|
"code" => {
|
|
|
|
let formatted_str = self.build_format(&value.value(), value.span());
|
macros: separate suggestion fmt'ing and emission
Diagnostic derives have previously had to take special care when
ordering the generated code so that fields were not used after a move.
This is unlikely for most fields because a field is either annotated
with a subdiagnostic attribute and is thus likely a `Span` and copiable,
or is a argument, in which case it is only used once by `set_arg`
anyway.
However, format strings for code in suggestions can result in fields
being used after being moved if not ordered carefully. As a result, the
derive currently puts `set_arg` calls last (just before emission), such
as:
```rust
let diag = { /* create diagnostic */ };
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
format!("{}", __binding_0),
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.emit();
```
For eager translation, this doesn't work, as the message being
translated eagerly can assume that all arguments are available - so
arguments _must_ be set first.
Format strings for suggestion code are now separated into two parts - an
initialization line that performs the formatting into a variable, and a
usage in the subdiagnostic addition.
By separating these parts, the initialization can happen before
arguments are set, preserving the desired order so that code compiles,
while still enabling arguments to be set before subdiagnostics are
added.
```rust
let diag = { /* create diagnostic */ };
let __code_0 = format!("{}", __binding_0);
/* + other formatting */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
__code_0,
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.emit();
```
Signed-off-by: David Wood <david.wood@huawei.com>
2022-10-03 13:28:02 +00:00
|
|
|
let code_field = new_code_ident();
|
|
|
|
code.set_once((code_field, formatted_str), span);
|
2022-09-01 17:42:49 +00:00
|
|
|
}
|
|
|
|
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
|
|
|
diag.help("`code` is the only valid nested attribute")
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
macros: separate suggestion fmt'ing and emission
Diagnostic derives have previously had to take special care when
ordering the generated code so that fields were not used after a move.
This is unlikely for most fields because a field is either annotated
with a subdiagnostic attribute and is thus likely a `Span` and copiable,
or is a argument, in which case it is only used once by `set_arg`
anyway.
However, format strings for code in suggestions can result in fields
being used after being moved if not ordered carefully. As a result, the
derive currently puts `set_arg` calls last (just before emission), such
as:
```rust
let diag = { /* create diagnostic */ };
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
format!("{}", __binding_0),
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.emit();
```
For eager translation, this doesn't work, as the message being
translated eagerly can assume that all arguments are available - so
arguments _must_ be set first.
Format strings for suggestion code are now separated into two parts - an
initialization line that performs the formatting into a variable, and a
usage in the subdiagnostic addition.
By separating these parts, the initialization can happen before
arguments are set, preserving the desired order so that code compiles,
while still enabling arguments to be set before subdiagnostics are
added.
```rust
let diag = { /* create diagnostic */ };
let __code_0 = format!("{}", __binding_0);
/* + other formatting */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
__code_0,
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.emit();
```
Signed-off-by: David Wood <david.wood@huawei.com>
2022-10-03 13:28:02 +00:00
|
|
|
let Some((code_field, formatted_str)) = code.value() else {
|
2022-09-01 17:42:49 +00:00
|
|
|
span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`")
|
|
|
|
.emit();
|
|
|
|
return Ok(quote! {});
|
|
|
|
};
|
|
|
|
let binding = info.binding;
|
2022-08-23 05:54:06 +00:00
|
|
|
|
macros: separate suggestion fmt'ing and emission
Diagnostic derives have previously had to take special care when
ordering the generated code so that fields were not used after a move.
This is unlikely for most fields because a field is either annotated
with a subdiagnostic attribute and is thus likely a `Span` and copiable,
or is a argument, in which case it is only used once by `set_arg`
anyway.
However, format strings for code in suggestions can result in fields
being used after being moved if not ordered carefully. As a result, the
derive currently puts `set_arg` calls last (just before emission), such
as:
```rust
let diag = { /* create diagnostic */ };
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
format!("{}", __binding_0),
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.emit();
```
For eager translation, this doesn't work, as the message being
translated eagerly can assume that all arguments are available - so
arguments _must_ be set first.
Format strings for suggestion code are now separated into two parts - an
initialization line that performs the formatting into a variable, and a
usage in the subdiagnostic addition.
By separating these parts, the initialization can happen before
arguments are set, preserving the desired order so that code compiles,
while still enabling arguments to be set before subdiagnostics are
added.
```rust
let diag = { /* create diagnostic */ };
let __code_0 = format!("{}", __binding_0);
/* + other formatting */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
__code_0,
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.emit();
```
Signed-off-by: David Wood <david.wood@huawei.com>
2022-10-03 13:28:02 +00:00
|
|
|
self.formatting_init.extend(quote! { let #code_field = #formatted_str; });
|
|
|
|
let code_field = if clone_suggestion_code {
|
|
|
|
quote! { #code_field.clone() }
|
|
|
|
} else {
|
|
|
|
quote! { #code_field }
|
|
|
|
};
|
|
|
|
Ok(quote! { suggestions.push((#binding, #code_field)); })
|
2022-09-01 17:42:49 +00:00
|
|
|
}
|
|
|
|
_ => throw_invalid_attr!(attr, &Meta::List(list), |diag| {
|
2022-09-01 18:53:59 +00:00
|
|
|
let mut span_attrs = vec![];
|
|
|
|
if kind_stats.has_multipart_suggestion {
|
|
|
|
span_attrs.push("suggestion_part");
|
|
|
|
}
|
|
|
|
if !kind_stats.all_multipart_suggestions {
|
|
|
|
span_attrs.push("primary_span")
|
|
|
|
}
|
2022-09-01 17:42:49 +00:00
|
|
|
diag.help(format!(
|
2022-09-01 18:53:59 +00:00
|
|
|
"only `{}`, `applicability` and `skip_arg` are valid field attributes",
|
|
|
|
span_attrs.join(", ")
|
2022-09-01 17:42:49 +00:00
|
|
|
))
|
|
|
|
}),
|
|
|
|
}
|
2022-04-27 01:57:44 +00:00
|
|
|
}
|
|
|
|
|
2022-09-01 17:42:49 +00:00
|
|
|
pub fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
|
2022-09-01 18:53:59 +00:00
|
|
|
let kind_slugs = self.identify_kind()?;
|
|
|
|
if kind_slugs.is_empty() {
|
2022-04-27 01:57:44 +00:00
|
|
|
throw_span_err!(
|
|
|
|
self.variant.ast().ident.span().unwrap(),
|
|
|
|
"subdiagnostic kind not specified"
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2022-09-01 18:53:59 +00:00
|
|
|
let kind_stats: KindsStatistics = kind_slugs.iter().map(|(kind, _slug)| kind).collect();
|
|
|
|
|
|
|
|
let init = if kind_stats.has_multipart_suggestion {
|
|
|
|
quote! { let mut suggestions = Vec::new(); }
|
|
|
|
} else {
|
|
|
|
quote! {}
|
2022-09-01 17:42:49 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let attr_args: TokenStream = self
|
|
|
|
.variant
|
|
|
|
.bindings()
|
|
|
|
.iter()
|
|
|
|
.filter(|binding| !binding.ast().attrs.is_empty())
|
2022-09-01 18:53:59 +00:00
|
|
|
.map(|binding| self.generate_field_attr_code(binding, kind_stats))
|
2022-09-01 17:42:49 +00:00
|
|
|
.collect();
|
|
|
|
|
2022-09-11 16:30:18 +00:00
|
|
|
let span_field = self.span_field.value_ref();
|
2022-04-27 01:57:44 +00:00
|
|
|
|
2022-10-03 13:09:05 +00:00
|
|
|
let diag = &self.parent.diag;
|
|
|
|
let f = &self.parent.f;
|
2022-09-01 18:53:59 +00:00
|
|
|
let mut calls = TokenStream::new();
|
|
|
|
for (kind, slug) in kind_slugs {
|
2022-10-03 13:09:05 +00:00
|
|
|
let message = format_ident!("__message");
|
|
|
|
calls.extend(quote! { let #message = #f(#diag, rustc_errors::fluent::#slug.into()); });
|
|
|
|
|
2022-09-01 18:53:59 +00:00
|
|
|
let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
|
|
|
|
let call = match kind {
|
macros: separate suggestion fmt'ing and emission
Diagnostic derives have previously had to take special care when
ordering the generated code so that fields were not used after a move.
This is unlikely for most fields because a field is either annotated
with a subdiagnostic attribute and is thus likely a `Span` and copiable,
or is a argument, in which case it is only used once by `set_arg`
anyway.
However, format strings for code in suggestions can result in fields
being used after being moved if not ordered carefully. As a result, the
derive currently puts `set_arg` calls last (just before emission), such
as:
```rust
let diag = { /* create diagnostic */ };
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
format!("{}", __binding_0),
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.emit();
```
For eager translation, this doesn't work, as the message being
translated eagerly can assume that all arguments are available - so
arguments _must_ be set first.
Format strings for suggestion code are now separated into two parts - an
initialization line that performs the formatting into a variable, and a
usage in the subdiagnostic addition.
By separating these parts, the initialization can happen before
arguments are set, preserving the desired order so that code compiles,
while still enabling arguments to be set before subdiagnostics are
added.
```rust
let diag = { /* create diagnostic */ };
let __code_0 = format!("{}", __binding_0);
/* + other formatting */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
__code_0,
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.emit();
```
Signed-off-by: David Wood <david.wood@huawei.com>
2022-10-03 13:28:02 +00:00
|
|
|
SubdiagnosticKind::Suggestion {
|
|
|
|
suggestion_kind,
|
|
|
|
applicability,
|
|
|
|
code_init,
|
|
|
|
code_field,
|
|
|
|
} => {
|
|
|
|
self.formatting_init.extend(code_init);
|
|
|
|
|
2022-09-12 18:44:28 +00:00
|
|
|
let applicability = applicability
|
|
|
|
.value()
|
|
|
|
.map(|a| quote! { #a })
|
|
|
|
.or_else(|| self.applicability.take().value())
|
|
|
|
.unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified });
|
|
|
|
|
2022-09-01 18:53:59 +00:00
|
|
|
if let Some(span) = span_field {
|
|
|
|
let style = suggestion_kind.to_suggestion_style();
|
macros: separate suggestion fmt'ing and emission
Diagnostic derives have previously had to take special care when
ordering the generated code so that fields were not used after a move.
This is unlikely for most fields because a field is either annotated
with a subdiagnostic attribute and is thus likely a `Span` and copiable,
or is a argument, in which case it is only used once by `set_arg`
anyway.
However, format strings for code in suggestions can result in fields
being used after being moved if not ordered carefully. As a result, the
derive currently puts `set_arg` calls last (just before emission), such
as:
```rust
let diag = { /* create diagnostic */ };
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
format!("{}", __binding_0),
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.emit();
```
For eager translation, this doesn't work, as the message being
translated eagerly can assume that all arguments are available - so
arguments _must_ be set first.
Format strings for suggestion code are now separated into two parts - an
initialization line that performs the formatting into a variable, and a
usage in the subdiagnostic addition.
By separating these parts, the initialization can happen before
arguments are set, preserving the desired order so that code compiles,
while still enabling arguments to be set before subdiagnostics are
added.
```rust
let diag = { /* create diagnostic */ };
let __code_0 = format!("{}", __binding_0);
/* + other formatting */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
__code_0,
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.emit();
```
Signed-off-by: David Wood <david.wood@huawei.com>
2022-10-03 13:28:02 +00:00
|
|
|
quote! { #diag.#name(#span, #message, #code_field, #applicability, #style); }
|
2022-09-01 18:53:59 +00:00
|
|
|
} else {
|
|
|
|
span_err(self.span, "suggestion without `#[primary_span]` field").emit();
|
|
|
|
quote! { unreachable!(); }
|
|
|
|
}
|
2022-09-01 17:42:49 +00:00
|
|
|
}
|
2022-09-12 18:44:28 +00:00
|
|
|
SubdiagnosticKind::MultipartSuggestion { suggestion_kind, applicability } => {
|
|
|
|
let applicability = applicability
|
|
|
|
.value()
|
|
|
|
.map(|a| quote! { #a })
|
|
|
|
.or_else(|| self.applicability.take().value())
|
|
|
|
.unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified });
|
|
|
|
|
2022-09-01 18:53:59 +00:00
|
|
|
if !self.has_suggestion_parts {
|
|
|
|
span_err(
|
|
|
|
self.span,
|
|
|
|
"multipart suggestion without any `#[suggestion_part(...)]` fields",
|
|
|
|
)
|
|
|
|
.emit();
|
|
|
|
}
|
2022-09-01 17:42:49 +00:00
|
|
|
|
2022-09-01 18:53:59 +00:00
|
|
|
let style = suggestion_kind.to_suggestion_style();
|
2022-09-01 17:42:49 +00:00
|
|
|
|
2022-09-01 18:53:59 +00:00
|
|
|
quote! { #diag.#name(#message, suggestions, #applicability, #style); }
|
2022-08-22 16:18:54 +00:00
|
|
|
}
|
2022-09-01 18:53:59 +00:00
|
|
|
SubdiagnosticKind::Label => {
|
|
|
|
if let Some(span) = span_field {
|
|
|
|
quote! { #diag.#name(#span, #message); }
|
|
|
|
} else {
|
|
|
|
span_err(self.span, "label without `#[primary_span]` field").emit();
|
|
|
|
quote! { unreachable!(); }
|
|
|
|
}
|
2022-08-22 16:18:54 +00:00
|
|
|
}
|
2022-09-01 18:53:59 +00:00
|
|
|
_ => {
|
|
|
|
if let Some(span) = span_field {
|
|
|
|
quote! { #diag.#name(#span, #message); }
|
|
|
|
} else {
|
|
|
|
quote! { #diag.#name(#message); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
macros: separate suggestion fmt'ing and emission
Diagnostic derives have previously had to take special care when
ordering the generated code so that fields were not used after a move.
This is unlikely for most fields because a field is either annotated
with a subdiagnostic attribute and is thus likely a `Span` and copiable,
or is a argument, in which case it is only used once by `set_arg`
anyway.
However, format strings for code in suggestions can result in fields
being used after being moved if not ordered carefully. As a result, the
derive currently puts `set_arg` calls last (just before emission), such
as:
```rust
let diag = { /* create diagnostic */ };
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
format!("{}", __binding_0),
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.emit();
```
For eager translation, this doesn't work, as the message being
translated eagerly can assume that all arguments are available - so
arguments _must_ be set first.
Format strings for suggestion code are now separated into two parts - an
initialization line that performs the formatting into a variable, and a
usage in the subdiagnostic addition.
By separating these parts, the initialization can happen before
arguments are set, preserving the desired order so that code compiles,
while still enabling arguments to be set before subdiagnostics are
added.
```rust
let diag = { /* create diagnostic */ };
let __code_0 = format!("{}", __binding_0);
/* + other formatting */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
__code_0,
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.emit();
```
Signed-off-by: David Wood <david.wood@huawei.com>
2022-10-03 13:28:02 +00:00
|
|
|
|
2022-09-01 18:53:59 +00:00
|
|
|
calls.extend(call);
|
|
|
|
}
|
2022-04-27 01:57:44 +00:00
|
|
|
|
2022-09-01 17:42:49 +00:00
|
|
|
let plain_args: TokenStream = self
|
|
|
|
.variant
|
|
|
|
.bindings()
|
|
|
|
.iter()
|
|
|
|
.filter(|binding| binding.ast().attrs.is_empty())
|
|
|
|
.map(|binding| self.generate_field_set_arg(binding))
|
|
|
|
.collect();
|
|
|
|
|
macros: separate suggestion fmt'ing and emission
Diagnostic derives have previously had to take special care when
ordering the generated code so that fields were not used after a move.
This is unlikely for most fields because a field is either annotated
with a subdiagnostic attribute and is thus likely a `Span` and copiable,
or is a argument, in which case it is only used once by `set_arg`
anyway.
However, format strings for code in suggestions can result in fields
being used after being moved if not ordered carefully. As a result, the
derive currently puts `set_arg` calls last (just before emission), such
as:
```rust
let diag = { /* create diagnostic */ };
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
format!("{}", __binding_0),
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.emit();
```
For eager translation, this doesn't work, as the message being
translated eagerly can assume that all arguments are available - so
arguments _must_ be set first.
Format strings for suggestion code are now separated into two parts - an
initialization line that performs the formatting into a variable, and a
usage in the subdiagnostic addition.
By separating these parts, the initialization can happen before
arguments are set, preserving the desired order so that code compiles,
while still enabling arguments to be set before subdiagnostics are
added.
```rust
let diag = { /* create diagnostic */ };
let __code_0 = format!("{}", __binding_0);
/* + other formatting */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
__code_0,
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.emit();
```
Signed-off-by: David Wood <david.wood@huawei.com>
2022-10-03 13:28:02 +00:00
|
|
|
let formatting_init = &self.formatting_init;
|
2022-09-01 17:42:49 +00:00
|
|
|
Ok(quote! {
|
|
|
|
#init
|
macros: separate suggestion fmt'ing and emission
Diagnostic derives have previously had to take special care when
ordering the generated code so that fields were not used after a move.
This is unlikely for most fields because a field is either annotated
with a subdiagnostic attribute and is thus likely a `Span` and copiable,
or is a argument, in which case it is only used once by `set_arg`
anyway.
However, format strings for code in suggestions can result in fields
being used after being moved if not ordered carefully. As a result, the
derive currently puts `set_arg` calls last (just before emission), such
as:
```rust
let diag = { /* create diagnostic */ };
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
format!("{}", __binding_0),
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.emit();
```
For eager translation, this doesn't work, as the message being
translated eagerly can assume that all arguments are available - so
arguments _must_ be set first.
Format strings for suggestion code are now separated into two parts - an
initialization line that performs the formatting into a variable, and a
usage in the subdiagnostic addition.
By separating these parts, the initialization can happen before
arguments are set, preserving the desired order so that code compiles,
while still enabling arguments to be set before subdiagnostics are
added.
```rust
let diag = { /* create diagnostic */ };
let __code_0 = format!("{}", __binding_0);
/* + other formatting */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
__code_0,
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.emit();
```
Signed-off-by: David Wood <david.wood@huawei.com>
2022-10-03 13:28:02 +00:00
|
|
|
#formatting_init
|
2022-09-01 17:42:49 +00:00
|
|
|
#attr_args
|
|
|
|
#plain_args
|
macros: separate suggestion fmt'ing and emission
Diagnostic derives have previously had to take special care when
ordering the generated code so that fields were not used after a move.
This is unlikely for most fields because a field is either annotated
with a subdiagnostic attribute and is thus likely a `Span` and copiable,
or is a argument, in which case it is only used once by `set_arg`
anyway.
However, format strings for code in suggestions can result in fields
being used after being moved if not ordered carefully. As a result, the
derive currently puts `set_arg` calls last (just before emission), such
as:
```rust
let diag = { /* create diagnostic */ };
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
format!("{}", __binding_0),
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.emit();
```
For eager translation, this doesn't work, as the message being
translated eagerly can assume that all arguments are available - so
arguments _must_ be set first.
Format strings for suggestion code are now separated into two parts - an
initialization line that performs the formatting into a variable, and a
usage in the subdiagnostic addition.
By separating these parts, the initialization can happen before
arguments are set, preserving the desired order so that code compiles,
while still enabling arguments to be set before subdiagnostics are
added.
```rust
let diag = { /* create diagnostic */ };
let __code_0 = format!("{}", __binding_0);
/* + other formatting */
diag.set_arg("foo", __binding_0);
/* + other `set_arg` calls */
diag.span_suggestion_with_style(
span,
fluent::crate::slug,
__code_0,
Applicability::Unknown,
SuggestionStyle::ShowAlways
);
/* + other subdiagnostic additions */
diag.emit();
```
Signed-off-by: David Wood <david.wood@huawei.com>
2022-10-03 13:28:02 +00:00
|
|
|
#calls
|
2022-09-01 17:42:49 +00:00
|
|
|
})
|
2022-04-27 01:57:44 +00:00
|
|
|
}
|
|
|
|
}
|