2022-04-27 02:57:44 +01:00
|
|
|
#![deny(unused_must_use)]
|
|
|
|
|
|
2022-04-27 04:06:13 +01:00
|
|
|
use crate::diagnostics::error::{
|
2023-03-27 13:44:06 +00:00
|
|
|
invalid_attr, span_err, throw_invalid_attr, throw_span_err, DiagnosticDeriveError,
|
2022-04-27 04:06:13 +01:00
|
|
|
};
|
2022-04-27 02:57:44 +01:00
|
|
|
use crate::diagnostics::utils::{
|
2023-05-03 23:22:57 +00:00
|
|
|
build_field_mapping, build_suggestion_code, is_doc_comment, new_code_ident,
|
|
|
|
|
report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span,
|
2023-12-24 09:08:41 +11:00
|
|
|
should_generate_arg, AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap, HasFieldMap,
|
|
|
|
|
SetOnce, SpannedOption, SubdiagnosticKind,
|
2022-04-27 02:57:44 +01:00
|
|
|
};
|
|
|
|
|
use proc_macro2::TokenStream;
|
|
|
|
|
use quote::{format_ident, quote};
|
2023-03-27 13:44:06 +00:00
|
|
|
use syn::{spanned::Spanned, Attribute, Meta, MetaList, Path};
|
2022-04-27 02:57:44 +01:00
|
|
|
use synstructure::{BindingInfo, Structure, VariantInfo};
|
|
|
|
|
|
2023-05-17 10:30:14 +00:00
|
|
|
use super::utils::SubdiagnosticVariant;
|
|
|
|
|
|
2024-03-06 14:00:16 +11:00
|
|
|
/// The central struct for constructing the `add_to_diag` method from an annotated struct.
|
2024-03-08 12:13:39 +11:00
|
|
|
pub(crate) struct SubdiagnosticDerive {
|
2022-04-27 02:57:44 +01:00
|
|
|
diag: syn::Ident,
|
2022-10-03 14:09:05 +01:00
|
|
|
f: syn::Ident,
|
2022-04-27 02:57:44 +01:00
|
|
|
}
|
|
|
|
|
|
2024-03-08 12:13:39 +11:00
|
|
|
impl SubdiagnosticDerive {
|
2022-10-03 14:09:05 +01:00
|
|
|
pub(crate) fn new() -> Self {
|
2022-04-27 02:57:44 +01:00
|
|
|
let diag = format_ident!("diag");
|
2022-10-03 14:09:05 +01:00
|
|
|
let f = format_ident!("f");
|
|
|
|
|
Self { diag, f }
|
2022-04-27 02:57:44 +01:00
|
|
|
}
|
|
|
|
|
|
2022-12-20 22:10:40 +01:00
|
|
|
pub(crate) fn into_tokens(self, mut structure: Structure<'_>) -> TokenStream {
|
2022-04-27 02:57:44 +01: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 11:47:31 -04:00
|
|
|
"`#[derive(Subdiagnostic)]` can only be used on structs and enums",
|
2023-03-27 13:44:06 +00:00
|
|
|
)
|
|
|
|
|
.emit();
|
2022-04-27 02:57:44 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-14 11:09:52 +01:00
|
|
|
let is_enum = matches!(ast.data, syn::Data::Enum(..));
|
|
|
|
|
if is_enum {
|
2022-04-27 02:57:44 +01:00
|
|
|
for attr in &ast.attrs {
|
2022-10-14 11:00:46 +01:00
|
|
|
// Always allow documentation comments.
|
|
|
|
|
if is_doc_comment(attr) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-27 02:57:44 +01:00
|
|
|
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 14:09:05 +01:00
|
|
|
let mut builder = SubdiagnosticDeriveVariantBuilder {
|
|
|
|
|
parent: &self,
|
2022-04-27 02:57:44 +01: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 14:28:02 +01:00
|
|
|
formatting_init: TokenStream::new(),
|
2022-09-23 12:49:02 +01:00
|
|
|
fields: build_field_mapping(variant),
|
2022-04-27 02:57:44 +01:00
|
|
|
span_field: None,
|
|
|
|
|
applicability: None,
|
2022-09-01 19:42:49 +02:00
|
|
|
has_suggestion_parts: false,
|
2024-04-20 12:52:05 +00:00
|
|
|
has_subdiagnostic: false,
|
2022-10-14 11:09:52 +01:00
|
|
|
is_enum,
|
2022-04-27 02:57:44 +01:00
|
|
|
};
|
|
|
|
|
builder.into_tokens().unwrap_or_else(|v| v.to_compile_error())
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
quote! {
|
|
|
|
|
match self {
|
|
|
|
|
#variants_
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-10-03 14:09:05 +01:00
|
|
|
let diag = &self.diag;
|
|
|
|
|
let f = &self.f;
|
2022-04-27 02:57:44 +01:00
|
|
|
let ret = structure.gen_impl(quote! {
|
2024-03-06 14:00:16 +11:00
|
|
|
gen impl rustc_errors::Subdiagnostic for @Self {
|
|
|
|
|
fn add_to_diag_with<__G, __F>(
|
Reduce capabilities of `Diagnostic`.
Currently many diagnostic modifier methods are available on both
`Diagnostic` and `DiagnosticBuilder`. This commit removes most of them
from `Diagnostic`. To minimize the diff size, it keeps them within
`diagnostic.rs` but changes the surrounding `impl Diagnostic` block to
`impl DiagnosticBuilder`. (I intend to move things around later, to give
a more sensible code layout.)
`Diagnostic` keeps a few methods that it still needs, like `sub`,
`arg`, and `replace_args`.
The `forward!` macro, which defined two additional methods per call
(e.g. `note` and `with_note`), is replaced by the `with_fn!` macro,
which defines one additional method per call (e.g. `with_note`). It's
now also only used when necessary -- not all modifier methods currently
need a `with_*` form. (New ones can be easily added as necessary.)
All this also requires changing `trait AddToDiagnostic` so its methods
take `DiagnosticBuilder` instead of `Diagnostic`, which leads to many
mechanical changes. `SubdiagnosticMessageOp` gains a type parameter `G`.
There are three subdiagnostics -- `DelayedAtWithoutNewline`,
`DelayedAtWithNewline`, and `InvalidFlushedDelayedDiagnosticLevel` --
that are created within the diagnostics machinery and appended to
external diagnostics. These are handled at the `Diagnostic` level, which
means it's now hard to construct them via `derive(Diagnostic)`, so
instead we construct them by hand. This has no effect on what they look
like when printed.
There are lots of new `allow` markers for `untranslatable_diagnostics`
and `diagnostics_outside_of_impl`. This is because
`#[rustc_lint_diagnostics]` annotations were present on the `Diagnostic`
modifier methods, but missing from the `DiagnosticBuilder` modifier
methods. They're now present.
2024-02-06 16:44:30 +11:00
|
|
|
self,
|
2024-02-23 10:20:45 +11:00
|
|
|
#diag: &mut rustc_errors::Diag<'_, __G>,
|
2024-04-18 19:18:35 +00:00
|
|
|
#f: &__F
|
Reduce capabilities of `Diagnostic`.
Currently many diagnostic modifier methods are available on both
`Diagnostic` and `DiagnosticBuilder`. This commit removes most of them
from `Diagnostic`. To minimize the diff size, it keeps them within
`diagnostic.rs` but changes the surrounding `impl Diagnostic` block to
`impl DiagnosticBuilder`. (I intend to move things around later, to give
a more sensible code layout.)
`Diagnostic` keeps a few methods that it still needs, like `sub`,
`arg`, and `replace_args`.
The `forward!` macro, which defined two additional methods per call
(e.g. `note` and `with_note`), is replaced by the `with_fn!` macro,
which defines one additional method per call (e.g. `with_note`). It's
now also only used when necessary -- not all modifier methods currently
need a `with_*` form. (New ones can be easily added as necessary.)
All this also requires changing `trait AddToDiagnostic` so its methods
take `DiagnosticBuilder` instead of `Diagnostic`, which leads to many
mechanical changes. `SubdiagnosticMessageOp` gains a type parameter `G`.
There are three subdiagnostics -- `DelayedAtWithoutNewline`,
`DelayedAtWithNewline`, and `InvalidFlushedDelayedDiagnosticLevel` --
that are created within the diagnostics machinery and appended to
external diagnostics. These are handled at the `Diagnostic` level, which
means it's now hard to construct them via `derive(Diagnostic)`, so
instead we construct them by hand. This has no effect on what they look
like when printed.
There are lots of new `allow` markers for `untranslatable_diagnostics`
and `diagnostics_outside_of_impl`. This is because
`#[rustc_lint_diagnostics]` annotations were present on the `Diagnostic`
modifier methods, but missing from the `DiagnosticBuilder` modifier
methods. They're now present.
2024-02-06 16:44:30 +11:00
|
|
|
) where
|
|
|
|
|
__G: rustc_errors::EmissionGuarantee,
|
2024-02-29 16:18:19 +11:00
|
|
|
__F: rustc_errors::SubdiagMessageOp<__G>,
|
2022-10-03 14:09:05 +01:00
|
|
|
{
|
2022-04-27 02:57:44 +01:00
|
|
|
#implementation
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
ret
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-27 05:24:31 +01:00
|
|
|
/// Tracks persistent information required for building up the call to add to the diagnostic
|
2022-09-18 11:47:31 -04:00
|
|
|
/// for the final generated method. This is a separate struct to `SubdiagnosticDerive`
|
2022-04-27 05:24:31 +01: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 14:09:05 +01:00
|
|
|
struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
2024-02-23 10:20:45 +11:00
|
|
|
/// The identifier to use for the generated `Diag` instance.
|
2024-03-08 12:13:39 +11:00
|
|
|
parent: &'parent SubdiagnosticDerive,
|
2022-04-27 02:57:44 +01: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 14:28:02 +01:00
|
|
|
/// Initialization of format strings for code suggestions.
|
|
|
|
|
formatting_init: TokenStream,
|
|
|
|
|
|
2022-04-27 02:57:44 +01:00
|
|
|
/// Store a map of field name to its corresponding field. This is built on construction of the
|
|
|
|
|
/// derive builder.
|
2022-09-23 12:49:02 +01:00
|
|
|
fields: FieldMap,
|
2022-04-27 02:57:44 +01:00
|
|
|
|
|
|
|
|
/// Identifier for the binding to the `#[primary_span]` field.
|
2022-09-11 18:30:18 +02:00
|
|
|
span_field: SpannedOption<proc_macro2::Ident>,
|
2022-09-12 20:44:28 +02:00
|
|
|
|
|
|
|
|
/// The binding to the `#[applicability]` field, if present.
|
2022-09-11 18:30:18 +02:00
|
|
|
applicability: SpannedOption<TokenStream>,
|
2022-09-01 19:42:49 +02: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-10-14 11:09:52 +01:00
|
|
|
|
2024-04-20 12:52:05 +00:00
|
|
|
/// Set to true when a `#[subdiagnostic]` field is encountered, used to suppress the error
|
|
|
|
|
/// emitted when no subdiagnostic kinds are specified on the variant itself.
|
|
|
|
|
has_subdiagnostic: bool,
|
|
|
|
|
|
2022-10-14 11:09:52 +01:00
|
|
|
/// Set to true when this variant is an enum variant rather than just the body of a struct.
|
|
|
|
|
is_enum: bool,
|
2022-04-27 02:57:44 +01:00
|
|
|
}
|
|
|
|
|
|
2022-10-03 14:09:05 +01:00
|
|
|
impl<'parent, 'a> HasFieldMap for SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
2022-04-27 02:57:44 +01:00
|
|
|
fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
|
|
|
|
|
self.fields.get(field)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-01 20:53:59 +02: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 20:44:28 +02:00
|
|
|
all_applicabilities_static: bool,
|
2022-09-01 20:53:59 +02: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 20:44:28 +02:00
|
|
|
all_applicabilities_static: true,
|
2022-09-01 20:53:59 +02:00
|
|
|
};
|
2022-09-12 20:44:28 +02:00
|
|
|
|
2022-09-01 20:53:59 +02:00
|
|
|
for kind in kinds {
|
2022-09-12 20:44:28 +02:00
|
|
|
if let SubdiagnosticKind::MultipartSuggestion { applicability: None, .. }
|
|
|
|
|
| SubdiagnosticKind::Suggestion { applicability: None, .. } = kind
|
|
|
|
|
{
|
|
|
|
|
ret.all_applicabilities_static = false;
|
|
|
|
|
}
|
2022-09-01 20:53:59 +02: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 14:09:05 +01:00
|
|
|
impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
2023-05-17 10:30:14 +00:00
|
|
|
fn identify_kind(
|
|
|
|
|
&mut self,
|
|
|
|
|
) -> Result<Vec<(SubdiagnosticKind, Path, bool)>, DiagnosticDeriveError> {
|
2022-09-01 20:53:59 +02:00
|
|
|
let mut kind_slugs = vec![];
|
2022-09-01 19:42:49 +02:00
|
|
|
|
|
|
|
|
for attr in self.variant.ast().attrs {
|
2023-07-12 21:49:27 -04:00
|
|
|
let Some(SubdiagnosticVariant { kind, slug, no_span }) =
|
|
|
|
|
SubdiagnosticVariant::from_attr(attr, self)?
|
|
|
|
|
else {
|
2022-10-14 11:00:46 +01:00
|
|
|
// Some attributes aren't errors - like documentation comments - but also aren't
|
|
|
|
|
// subdiagnostics.
|
|
|
|
|
continue;
|
|
|
|
|
};
|
2022-08-23 07:54:06 +02:00
|
|
|
|
2022-09-12 20:44:28 +02:00
|
|
|
let Some(slug) = slug else {
|
2023-03-27 13:44:06 +00:00
|
|
|
let name = attr.path().segments.last().unwrap().ident.to_string();
|
2022-09-12 20:44:28 +02:00
|
|
|
let name = name.as_str();
|
2022-09-01 19:42:49 +02:00
|
|
|
|
2022-04-27 02:57:44 +01:00
|
|
|
throw_span_err!(
|
2022-09-12 20:44:28 +02:00
|
|
|
attr.span().unwrap(),
|
2023-06-23 02:17:39 +09:00
|
|
|
format!(
|
2022-12-19 10:31:55 +01:00
|
|
|
"diagnostic slug must be first argument of a `#[{name}(...)]` attribute"
|
2022-06-23 16:12:52 +01:00
|
|
|
)
|
2022-04-27 02:57:44 +01:00
|
|
|
);
|
2022-09-01 19:42:49 +02:00
|
|
|
};
|
|
|
|
|
|
2023-05-17 10:30:14 +00:00
|
|
|
kind_slugs.push((kind, slug, no_span));
|
2022-04-27 02:57:44 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-01 20:53:59 +02:00
|
|
|
Ok(kind_slugs)
|
2022-09-01 19:42:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Generates the code for a field with no attributes.
|
2023-12-24 09:08:41 +11:00
|
|
|
fn generate_field_arg(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
|
2022-10-03 14:09:05 +01:00
|
|
|
let diag = &self.parent.diag;
|
2023-05-03 23:53:44 +00:00
|
|
|
|
|
|
|
|
let field = binding_info.ast();
|
|
|
|
|
let mut field_binding = binding_info.binding.clone();
|
|
|
|
|
field_binding.set_span(field.ty.span());
|
|
|
|
|
|
|
|
|
|
let ident = field.ident.as_ref().unwrap();
|
|
|
|
|
let ident = format_ident!("{}", ident); // strip `r#` prefix, if present
|
2022-09-14 20:12:22 +02:00
|
|
|
|
2022-09-01 19:42:49 +02:00
|
|
|
quote! {
|
2023-12-24 09:08:41 +11:00
|
|
|
#diag.arg(
|
2022-09-01 19:42:49 +02:00
|
|
|
stringify!(#ident),
|
2023-05-03 23:53:44 +00:00
|
|
|
#field_binding
|
2022-09-01 19:42:49 +02:00
|
|
|
);
|
|
|
|
|
}
|
2022-08-23 07:54:06 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-01 19:42:49 +02:00
|
|
|
/// Generates the necessary code for all attributes on a field.
|
|
|
|
|
fn generate_field_attr_code(
|
2022-04-27 02:57:44 +01:00
|
|
|
&mut self,
|
|
|
|
|
binding: &BindingInfo<'_>,
|
2022-09-01 20:53:59 +02:00
|
|
|
kind_stats: KindsStatistics,
|
2022-09-01 19:42:49 +02:00
|
|
|
) -> TokenStream {
|
2022-04-27 02:57:44 +01:00
|
|
|
let ast = binding.ast();
|
2022-09-01 19:42:49 +02:00
|
|
|
assert!(ast.attrs.len() > 0, "field without attributes generating attr code");
|
2022-04-27 02:57:44 +01:00
|
|
|
|
2022-09-01 19:42:49 +02: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 03:43:30 +01:00
|
|
|
let inner_ty = FieldInnerTy::from_type(&ast.ty);
|
2022-09-01 19:42:49 +02:00
|
|
|
ast.attrs
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|attr| {
|
2022-10-14 11:00:46 +01:00
|
|
|
// Always allow documentation comments.
|
|
|
|
|
if is_doc_comment(attr) {
|
|
|
|
|
return quote! {};
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 16:15:39 +02:00
|
|
|
let info = FieldInfo { binding, ty: inner_ty, span: &ast.span() };
|
2022-08-23 07:54:06 +02:00
|
|
|
|
2022-09-01 19:42:49 +02: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 14:28:02 +01:00
|
|
|
.generate_field_code_inner(kind_stats, attr, info, inner_ty.will_iterate())
|
2022-09-01 19:42:49 +02:00
|
|
|
.unwrap_or_else(|v| v.to_compile_error());
|
2022-08-23 07:54:06 +02:00
|
|
|
|
2022-09-01 19:42:49 +02:00
|
|
|
inner_ty.with(binding, generated)
|
|
|
|
|
})
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn generate_field_code_inner(
|
|
|
|
|
&mut self,
|
2022-09-01 20:53:59 +02:00
|
|
|
kind_stats: KindsStatistics,
|
2022-09-01 19:42:49 +02: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 14:28:02 +01:00
|
|
|
clone_suggestion_code: bool,
|
2022-09-01 19:42:49 +02:00
|
|
|
) -> Result<TokenStream, DiagnosticDeriveError> {
|
2023-03-27 13:44:06 +00:00
|
|
|
match &attr.meta {
|
|
|
|
|
Meta::Path(path) => {
|
|
|
|
|
self.generate_field_code_inner_path(kind_stats, attr, info, path.clone())
|
|
|
|
|
}
|
|
|
|
|
Meta::List(list) => self.generate_field_code_inner_list(
|
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 14:28:02 +01:00
|
|
|
kind_stats,
|
|
|
|
|
attr,
|
|
|
|
|
info,
|
|
|
|
|
list,
|
|
|
|
|
clone_suggestion_code,
|
|
|
|
|
),
|
2023-03-27 13:44:06 +00:00
|
|
|
_ => throw_invalid_attr!(attr),
|
2022-09-01 19:42:49 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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 20:53:59 +02:00
|
|
|
kind_stats: KindsStatistics,
|
2022-09-01 19:42:49 +02: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 20:53:59 +02:00
|
|
|
if kind_stats.has_multipart_suggestion {
|
2023-03-27 13:44:06 +00:00
|
|
|
invalid_attr(attr)
|
2022-09-12 21:11:49 +02:00
|
|
|
.help(
|
2022-09-01 19:42:49 +02:00
|
|
|
"multipart suggestions use one or more `#[suggestion_part]`s rather \
|
|
|
|
|
than one `#[primary_span]`",
|
2022-08-23 07:54:06 +02:00
|
|
|
)
|
2022-09-12 21:11:49 +02:00
|
|
|
.emit();
|
|
|
|
|
} else {
|
|
|
|
|
report_error_if_not_applied_to_span(attr, &info)?;
|
2022-09-01 19:42:49 +02:00
|
|
|
|
2022-09-12 21:11:49 +02:00
|
|
|
let binding = info.binding.binding.clone();
|
2022-10-14 13:25:59 +01:00
|
|
|
// FIXME(#100717): support `Option<Span>` on `primary_span` like in the
|
|
|
|
|
// diagnostic derive
|
2022-10-16 16:15:39 +02:00
|
|
|
if !matches!(info.ty, FieldInnerTy::Plain(_)) {
|
2023-03-27 13:44:06 +00:00
|
|
|
throw_invalid_attr!(attr, |diag| {
|
2022-10-16 16:15:39 +02:00
|
|
|
let diag = diag.note("there must be exactly one primary span");
|
|
|
|
|
|
|
|
|
|
if kind_stats.has_normal_suggestion {
|
|
|
|
|
diag.help(
|
|
|
|
|
"to create a suggestion with multiple spans, \
|
|
|
|
|
use `#[multipart_suggestion]` instead",
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
diag
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-12 21:11:49 +02:00
|
|
|
self.span_field.set_once(binding, span);
|
|
|
|
|
}
|
2022-09-01 19:42:49 +02:00
|
|
|
|
|
|
|
|
Ok(quote! {})
|
|
|
|
|
}
|
|
|
|
|
"suggestion_part" => {
|
|
|
|
|
self.has_suggestion_parts = true;
|
|
|
|
|
|
2022-09-01 20:53:59 +02:00
|
|
|
if kind_stats.has_multipart_suggestion {
|
|
|
|
|
span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`")
|
2022-09-01 19:42:49 +02:00
|
|
|
.emit();
|
2022-09-01 20:53:59 +02:00
|
|
|
} else {
|
2023-03-27 13:44:06 +00:00
|
|
|
invalid_attr(attr)
|
2022-09-12 21:11:49 +02:00
|
|
|
.help(
|
|
|
|
|
"`#[suggestion_part(...)]` is only valid in multipart suggestions, \
|
|
|
|
|
use `#[primary_span]` instead",
|
|
|
|
|
)
|
|
|
|
|
.emit();
|
2022-09-01 19:42:49 +02:00
|
|
|
}
|
2022-09-12 21:11:49 +02:00
|
|
|
|
|
|
|
|
Ok(quote! {})
|
2022-08-23 11:19:38 +08:00
|
|
|
}
|
2022-09-01 19:42:49 +02:00
|
|
|
"applicability" => {
|
2022-09-01 20:53:59 +02:00
|
|
|
if kind_stats.has_multipart_suggestion || kind_stats.has_normal_suggestion {
|
2022-09-01 19:42:49 +02:00
|
|
|
report_error_if_not_applied_to_applicability(attr, &info)?;
|
|
|
|
|
|
2022-09-12 20:44:28 +02: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 19:42:49 +02:00
|
|
|
let binding = info.binding.binding.clone();
|
2022-09-11 18:30:18 +02:00
|
|
|
self.applicability.set_once(quote! { #binding }, span);
|
2022-09-01 19:42:49 +02:00
|
|
|
} else {
|
|
|
|
|
span_err(span, "`#[applicability]` is only valid on suggestions").emit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(quote! {})
|
|
|
|
|
}
|
2024-04-20 12:52:05 +00:00
|
|
|
"subdiagnostic" => {
|
|
|
|
|
let f = &self.parent.f;
|
|
|
|
|
let diag = &self.parent.diag;
|
|
|
|
|
let binding = &info.binding;
|
|
|
|
|
self.has_subdiagnostic = true;
|
|
|
|
|
Ok(quote! { #binding.add_to_diag_with(#diag, #f); })
|
|
|
|
|
}
|
2022-09-12 21:11:49 +02:00
|
|
|
_ => {
|
2022-09-01 20:53:59 +02: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 21:11:49 +02:00
|
|
|
|
2023-03-27 13:44:06 +00:00
|
|
|
invalid_attr(attr)
|
2022-09-12 21:11:49 +02:00
|
|
|
.help(format!(
|
|
|
|
|
"only `{}`, `applicability` and `skip_arg` are valid field attributes",
|
|
|
|
|
span_attrs.join(", ")
|
|
|
|
|
))
|
|
|
|
|
.emit();
|
|
|
|
|
|
|
|
|
|
Ok(quote! {})
|
|
|
|
|
}
|
2022-08-23 11:19:38 +08:00
|
|
|
}
|
2022-09-01 19:42:49 +02:00
|
|
|
}
|
2022-08-23 07:54:06 +02:00
|
|
|
|
2022-09-01 19:42:49 +02: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 20:53:59 +02:00
|
|
|
kind_stats: KindsStatistics,
|
2022-09-01 19:42:49 +02:00
|
|
|
attr: &Attribute,
|
|
|
|
|
info: FieldInfo<'_>,
|
2023-03-27 13:44:06 +00:00
|
|
|
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 14:28:02 +01:00
|
|
|
clone_suggestion_code: bool,
|
2022-09-01 19:42:49 +02:00
|
|
|
) -> Result<TokenStream, DiagnosticDeriveError> {
|
|
|
|
|
let span = attr.span().unwrap();
|
2023-05-03 23:53:44 +00:00
|
|
|
let mut ident = list.path.segments.last().unwrap().ident.clone();
|
|
|
|
|
ident.set_span(info.ty.span());
|
2022-09-01 19:42:49 +02:00
|
|
|
let name = ident.to_string();
|
|
|
|
|
let name = name.as_str();
|
|
|
|
|
|
|
|
|
|
match name {
|
|
|
|
|
"suggestion_part" => {
|
2022-09-01 20:53:59 +02:00
|
|
|
if !kind_stats.has_multipart_suggestion {
|
2023-03-27 13:44:06 +00:00
|
|
|
throw_invalid_attr!(attr, |diag| {
|
2022-09-01 19:42:49 +02:00
|
|
|
diag.help(
|
|
|
|
|
"`#[suggestion_part(...)]` is only valid in multipart suggestions",
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
}
|
2022-04-27 02:57:44 +01:00
|
|
|
|
2022-09-01 19:42:49 +02:00
|
|
|
self.has_suggestion_parts = true;
|
|
|
|
|
|
|
|
|
|
report_error_if_not_applied_to_span(attr, &info)?;
|
|
|
|
|
|
|
|
|
|
let mut code = None;
|
2023-03-27 13:44:06 +00:00
|
|
|
|
|
|
|
|
list.parse_nested_meta(|nested| {
|
|
|
|
|
if nested.path.is_ident("code") {
|
|
|
|
|
let code_field = new_code_ident();
|
2023-04-03 15:11:30 +00:00
|
|
|
let span = nested.path.span().unwrap();
|
2023-03-27 13:44:06 +00:00
|
|
|
let formatting_init = build_suggestion_code(
|
|
|
|
|
&code_field,
|
|
|
|
|
nested,
|
|
|
|
|
self,
|
|
|
|
|
AllowMultipleAlternatives::No,
|
|
|
|
|
);
|
|
|
|
|
code.set_once((code_field, formatting_init), span);
|
|
|
|
|
} else {
|
|
|
|
|
span_err(
|
|
|
|
|
nested.path.span().unwrap(),
|
|
|
|
|
"`code` is the only valid nested attribute",
|
|
|
|
|
)
|
|
|
|
|
.emit();
|
2022-09-01 19:42:49 +02:00
|
|
|
}
|
2023-03-27 13:44:06 +00:00
|
|
|
Ok(())
|
|
|
|
|
})?;
|
2022-09-01 19:42:49 +02:00
|
|
|
|
2022-10-17 19:41:49 +02:00
|
|
|
let Some((code_field, formatting_init)) = code.value() else {
|
2022-09-01 19:42:49 +02:00
|
|
|
span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`")
|
|
|
|
|
.emit();
|
|
|
|
|
return Ok(quote! {});
|
|
|
|
|
};
|
|
|
|
|
let binding = info.binding;
|
2022-08-23 07:54:06 +02:00
|
|
|
|
2022-10-17 19:41:49 +02:00
|
|
|
self.formatting_init.extend(formatting_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 14:28:02 +01:00
|
|
|
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 19:42:49 +02:00
|
|
|
}
|
2023-03-27 13:44:06 +00:00
|
|
|
_ => throw_invalid_attr!(attr, |diag| {
|
2022-09-01 20:53:59 +02: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 19:42:49 +02:00
|
|
|
diag.help(format!(
|
2022-09-01 20:53:59 +02:00
|
|
|
"only `{}`, `applicability` and `skip_arg` are valid field attributes",
|
|
|
|
|
span_attrs.join(", ")
|
2022-09-01 19:42:49 +02:00
|
|
|
))
|
|
|
|
|
}),
|
|
|
|
|
}
|
2022-04-27 02:57:44 +01:00
|
|
|
}
|
|
|
|
|
|
2023-11-08 18:27:19 +11:00
|
|
|
pub(crate) fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
|
2022-09-01 20:53:59 +02:00
|
|
|
let kind_slugs = self.identify_kind()?;
|
2022-04-27 02:57:44 +01:00
|
|
|
|
2023-05-17 10:30:14 +00:00
|
|
|
let kind_stats: KindsStatistics =
|
|
|
|
|
kind_slugs.iter().map(|(kind, _slug, _no_span)| kind).collect();
|
2022-09-01 20:53:59 +02:00
|
|
|
|
|
|
|
|
let init = if kind_stats.has_multipart_suggestion {
|
|
|
|
|
quote! { let mut suggestions = Vec::new(); }
|
|
|
|
|
} else {
|
|
|
|
|
quote! {}
|
2022-09-01 19:42:49 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let attr_args: TokenStream = self
|
|
|
|
|
.variant
|
|
|
|
|
.bindings()
|
|
|
|
|
.iter()
|
2023-12-24 09:08:41 +11:00
|
|
|
.filter(|binding| !should_generate_arg(binding.ast()))
|
2022-09-01 20:53:59 +02:00
|
|
|
.map(|binding| self.generate_field_attr_code(binding, kind_stats))
|
2022-09-01 19:42:49 +02:00
|
|
|
.collect();
|
|
|
|
|
|
2024-04-24 17:05:48 +00:00
|
|
|
if kind_slugs.is_empty() && !self.has_subdiagnostic {
|
2024-04-20 12:52:05 +00:00
|
|
|
if self.is_enum {
|
|
|
|
|
// It's okay for a variant to not be a subdiagnostic at all..
|
|
|
|
|
return Ok(quote! {});
|
2024-04-24 17:05:48 +00:00
|
|
|
} else {
|
2024-04-20 12:52:05 +00:00
|
|
|
// ..but structs should always be _something_.
|
|
|
|
|
throw_span_err!(
|
|
|
|
|
self.variant.ast().ident.span().unwrap(),
|
|
|
|
|
"subdiagnostic kind not specified"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-09-11 18:30:18 +02:00
|
|
|
let span_field = self.span_field.value_ref();
|
2022-04-27 02:57:44 +01:00
|
|
|
|
2022-10-03 14:09:05 +01:00
|
|
|
let diag = &self.parent.diag;
|
|
|
|
|
let f = &self.parent.f;
|
2022-09-01 20:53:59 +02:00
|
|
|
let mut calls = TokenStream::new();
|
2023-05-17 10:30:14 +00:00
|
|
|
for (kind, slug, no_span) in kind_slugs {
|
2022-10-03 14:09:05 +01:00
|
|
|
let message = format_ident!("__message");
|
2022-10-13 10:13:02 +01:00
|
|
|
calls.extend(
|
|
|
|
|
quote! { let #message = #f(#diag, crate::fluent_generated::#slug.into()); },
|
|
|
|
|
);
|
2022-10-03 14:09:05 +01:00
|
|
|
|
2023-05-17 10:30:14 +00:00
|
|
|
let name = format_ident!(
|
|
|
|
|
"{}{}",
|
|
|
|
|
if span_field.is_some() && !no_span { "span_" } else { "" },
|
|
|
|
|
kind
|
|
|
|
|
);
|
2022-09-01 20:53:59 +02:00
|
|
|
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 14:28:02 +01:00
|
|
|
SubdiagnosticKind::Suggestion {
|
|
|
|
|
suggestion_kind,
|
|
|
|
|
applicability,
|
|
|
|
|
code_init,
|
|
|
|
|
code_field,
|
|
|
|
|
} => {
|
|
|
|
|
self.formatting_init.extend(code_init);
|
|
|
|
|
|
2022-09-12 20:44:28 +02: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 20:53:59 +02: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 14:28:02 +01:00
|
|
|
quote! { #diag.#name(#span, #message, #code_field, #applicability, #style); }
|
2022-09-01 20:53:59 +02:00
|
|
|
} else {
|
|
|
|
|
span_err(self.span, "suggestion without `#[primary_span]` field").emit();
|
|
|
|
|
quote! { unreachable!(); }
|
|
|
|
|
}
|
2022-09-01 19:42:49 +02:00
|
|
|
}
|
2022-09-12 20:44:28 +02: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 20:53:59 +02:00
|
|
|
if !self.has_suggestion_parts {
|
|
|
|
|
span_err(
|
|
|
|
|
self.span,
|
|
|
|
|
"multipart suggestion without any `#[suggestion_part(...)]` fields",
|
|
|
|
|
)
|
|
|
|
|
.emit();
|
|
|
|
|
}
|
2022-09-01 19:42:49 +02:00
|
|
|
|
2022-09-01 20:53:59 +02:00
|
|
|
let style = suggestion_kind.to_suggestion_style();
|
2022-09-01 19:42:49 +02:00
|
|
|
|
2022-09-01 20:53:59 +02:00
|
|
|
quote! { #diag.#name(#message, suggestions, #applicability, #style); }
|
2022-08-22 18:18:54 +02:00
|
|
|
}
|
2022-09-01 20:53:59 +02: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 18:18:54 +02:00
|
|
|
}
|
2022-09-01 20:53:59 +02:00
|
|
|
_ => {
|
2023-10-13 08:58:33 +00:00
|
|
|
if let Some(span) = span_field
|
|
|
|
|
&& !no_span
|
|
|
|
|
{
|
2022-09-01 20:53:59 +02:00
|
|
|
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 14:28:02 +01:00
|
|
|
|
2022-09-01 20:53:59 +02:00
|
|
|
calls.extend(call);
|
|
|
|
|
}
|
2022-04-27 02:57:44 +01:00
|
|
|
|
2022-09-01 19:42:49 +02:00
|
|
|
let plain_args: TokenStream = self
|
|
|
|
|
.variant
|
|
|
|
|
.bindings()
|
|
|
|
|
.iter()
|
2023-12-24 09:08:41 +11:00
|
|
|
.filter(|binding| should_generate_arg(binding.ast()))
|
|
|
|
|
.map(|binding| self.generate_field_arg(binding))
|
2022-09-01 19:42:49 +02:00
|
|
|
.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 14:28:02 +01:00
|
|
|
let formatting_init = &self.formatting_init;
|
2022-09-01 19:42:49 +02: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 14:28:02 +01:00
|
|
|
#formatting_init
|
2022-09-01 19:42:49 +02: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 14:28:02 +01:00
|
|
|
#calls
|
2022-09-01 19:42:49 +02:00
|
|
|
})
|
2022-04-27 02:57:44 +01:00
|
|
|
}
|
|
|
|
|
}
|