Rollup merge of #102623 - davidtwco:translation-eager, r=compiler-errors
translation: eager translation Part of #100717. See [Zulip thread](https://rust-lang.zulipchat.com/#narrow/stream/336883-i18n/topic/.23100717.20lists!/near/295010720) for additional context. - **Store diagnostic arguments in a `HashMap`**: Eager translation will enable subdiagnostics to be translated multiple times with different arguments - this requires the ability to replace the value of one argument with a new value, which is better suited to a `HashMap` than the previous storage, a `Vec`. - **Add `AddToDiagnostic::add_to_diagnostic_with`**: `AddToDiagnostic::add_to_diagnostic_with` is similar to the previous `AddToDiagnostic::add_to_diagnostic` but takes a function that can be used by the caller to modify diagnostic messages originating from the subdiagnostic (such as performing translation eagerly). `add_to_diagnostic` now just calls `add_to_diagnostic_with` with an empty closure. - **Add `DiagnosticMessage::Eager`**: Add variant of `DiagnosticMessage` for eagerly translated messages (messages in the target language which don't need translated by the emitter during emission). Also adds `eager_subdiagnostic` function which is intended to be invoked by the diagnostic derive for subdiagnostic fields which are marked as needing eager translation. - **Support `#[subdiagnostic(eager)]`**: Add support for `eager` argument to the `subdiagnostic` attribute which generates a call to `eager_subdiagnostic`. - **Finish migrating `rustc_query_system`**: Using eager translation, migrate the remaining repeated cycle stack diagnostic. - **Split formatting initialization and use in diagnostic derives**: 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: 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. 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(); - **Remove field ordering logic in diagnostic derive:** Following the approach taken in earlier commits to separate formatting initialization from use in the subdiagnostic derive, simplify the diagnostic derive by removing the field-ordering logic that previously solved this problem. r? ```@compiler-errors```
This commit is contained in:
commit
dc9f6f3243
26 changed files with 540 additions and 236 deletions
|
@ -1,4 +1,7 @@
|
||||||
use rustc_errors::{fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgFromDisplay};
|
use rustc_errors::{
|
||||||
|
fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgFromDisplay,
|
||||||
|
SubdiagnosticMessage,
|
||||||
|
};
|
||||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||||
use rustc_span::{symbol::Ident, Span, Symbol};
|
use rustc_span::{symbol::Ident, Span, Symbol};
|
||||||
|
|
||||||
|
@ -19,7 +22,10 @@ pub struct UseAngleBrackets {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddToDiagnostic for UseAngleBrackets {
|
impl AddToDiagnostic for UseAngleBrackets {
|
||||||
fn add_to_diagnostic(self, diag: &mut Diagnostic) {
|
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
|
||||||
|
where
|
||||||
|
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
|
||||||
|
{
|
||||||
diag.multipart_suggestion(
|
diag.multipart_suggestion(
|
||||||
fluent::ast_lowering::use_angle_brackets,
|
fluent::ast_lowering::use_angle_brackets,
|
||||||
vec![(self.open_param, String::from("<")), (self.close_param, String::from(">"))],
|
vec![(self.open_param, String::from("<")), (self.close_param, String::from(">"))],
|
||||||
|
@ -69,7 +75,10 @@ pub enum AssocTyParenthesesSub {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddToDiagnostic for AssocTyParenthesesSub {
|
impl AddToDiagnostic for AssocTyParenthesesSub {
|
||||||
fn add_to_diagnostic(self, diag: &mut Diagnostic) {
|
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
|
||||||
|
where
|
||||||
|
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
|
||||||
|
{
|
||||||
match self {
|
match self {
|
||||||
Self::Empty { parentheses_span } => diag.multipart_suggestion(
|
Self::Empty { parentheses_span } => diag.multipart_suggestion(
|
||||||
fluent::ast_lowering::remove_parentheses,
|
fluent::ast_lowering::remove_parentheses,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Errors emitted by ast_passes.
|
//! Errors emitted by ast_passes.
|
||||||
|
|
||||||
use rustc_errors::{fluent, AddToDiagnostic, Applicability, Diagnostic};
|
use rustc_errors::{fluent, AddToDiagnostic, Applicability, Diagnostic, SubdiagnosticMessage};
|
||||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||||
use rustc_span::{Span, Symbol};
|
use rustc_span::{Span, Symbol};
|
||||||
|
|
||||||
|
@ -17,7 +17,10 @@ pub struct ForbiddenLet {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddToDiagnostic for ForbiddenLetReason {
|
impl AddToDiagnostic for ForbiddenLetReason {
|
||||||
fn add_to_diagnostic(self, diag: &mut Diagnostic) {
|
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
|
||||||
|
where
|
||||||
|
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
|
||||||
|
{
|
||||||
match self {
|
match self {
|
||||||
Self::GenericForbidden => {}
|
Self::GenericForbidden => {}
|
||||||
Self::NotSupportedOr(span) => {
|
Self::NotSupportedOr(span) => {
|
||||||
|
@ -228,7 +231,10 @@ pub struct ExternBlockSuggestion {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddToDiagnostic for ExternBlockSuggestion {
|
impl AddToDiagnostic for ExternBlockSuggestion {
|
||||||
fn add_to_diagnostic(self, diag: &mut Diagnostic) {
|
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
|
||||||
|
where
|
||||||
|
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
|
||||||
|
{
|
||||||
let start_suggestion = if let Some(abi) = self.abi {
|
let start_suggestion = if let Some(abi) = self.abi {
|
||||||
format!("extern \"{}\" {{", abi)
|
format!("extern \"{}\" {{", abi)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -15,7 +15,10 @@ use rustc_data_structures::profiling::TimingGuard;
|
||||||
use rustc_data_structures::profiling::VerboseTimingGuard;
|
use rustc_data_structures::profiling::VerboseTimingGuard;
|
||||||
use rustc_data_structures::sync::Lrc;
|
use rustc_data_structures::sync::Lrc;
|
||||||
use rustc_errors::emitter::Emitter;
|
use rustc_errors::emitter::Emitter;
|
||||||
use rustc_errors::{translation::Translate, DiagnosticId, FatalError, Handler, Level};
|
use rustc_errors::{
|
||||||
|
translation::{to_fluent_args, Translate},
|
||||||
|
DiagnosticId, FatalError, Handler, Level,
|
||||||
|
};
|
||||||
use rustc_fs_util::link_or_copy;
|
use rustc_fs_util::link_or_copy;
|
||||||
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
|
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
|
||||||
use rustc_incremental::{
|
use rustc_incremental::{
|
||||||
|
@ -1740,7 +1743,7 @@ impl Translate for SharedEmitter {
|
||||||
|
|
||||||
impl Emitter for SharedEmitter {
|
impl Emitter for SharedEmitter {
|
||||||
fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) {
|
fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) {
|
||||||
let fluent_args = self.to_fluent_args(diag.args());
|
let fluent_args = to_fluent_args(diag.args());
|
||||||
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
|
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
|
||||||
msg: self.translate_messages(&diag.message, &fluent_args).to_string(),
|
msg: self.translate_messages(&diag.message, &fluent_args).to_string(),
|
||||||
code: diag.code.clone(),
|
code: diag.code.clone(),
|
||||||
|
|
|
@ -12,6 +12,8 @@ query_system_cycle_usage = cycle used when {$usage}
|
||||||
|
|
||||||
query_system_cycle_stack_single = ...which immediately requires {$stack_bottom} again
|
query_system_cycle_stack_single = ...which immediately requires {$stack_bottom} again
|
||||||
|
|
||||||
|
query_system_cycle_stack_middle = ...which requires {$desc}...
|
||||||
|
|
||||||
query_system_cycle_stack_multiple = ...which again requires {$stack_bottom}, completing the cycle
|
query_system_cycle_stack_multiple = ...which again requires {$stack_bottom}, completing the cycle
|
||||||
|
|
||||||
query_system_cycle_recursive_ty_alias = type aliases cannot be recursive
|
query_system_cycle_recursive_ty_alias = type aliases cannot be recursive
|
||||||
|
|
|
@ -277,6 +277,18 @@ pub enum SubdiagnosticMessage {
|
||||||
/// Non-translatable diagnostic message.
|
/// Non-translatable diagnostic message.
|
||||||
// FIXME(davidtwco): can a `Cow<'static, str>` be used here?
|
// FIXME(davidtwco): can a `Cow<'static, str>` be used here?
|
||||||
Str(String),
|
Str(String),
|
||||||
|
/// Translatable message which has already been translated eagerly.
|
||||||
|
///
|
||||||
|
/// Some diagnostics have repeated subdiagnostics where the same interpolated variables would
|
||||||
|
/// be instantiated multiple times with different values. As translation normally happens
|
||||||
|
/// immediately prior to emission, after the diagnostic and subdiagnostic derive logic has run,
|
||||||
|
/// the setting of diagnostic arguments in the derived code will overwrite previous variable
|
||||||
|
/// values and only the final value will be set when translation occurs - resulting in
|
||||||
|
/// incorrect diagnostics. Eager translation results in translation for a subdiagnostic
|
||||||
|
/// happening immediately after the subdiagnostic derive's logic has been run. This variant
|
||||||
|
/// stores messages which have been translated eagerly.
|
||||||
|
// FIXME(#100717): can a `Cow<'static, str>` be used here?
|
||||||
|
Eager(String),
|
||||||
/// Identifier of a Fluent message. Instances of this variant are generated by the
|
/// Identifier of a Fluent message. Instances of this variant are generated by the
|
||||||
/// `Subdiagnostic` derive.
|
/// `Subdiagnostic` derive.
|
||||||
FluentIdentifier(FluentId),
|
FluentIdentifier(FluentId),
|
||||||
|
@ -304,8 +316,20 @@ impl<S: Into<String>> From<S> for SubdiagnosticMessage {
|
||||||
#[rustc_diagnostic_item = "DiagnosticMessage"]
|
#[rustc_diagnostic_item = "DiagnosticMessage"]
|
||||||
pub enum DiagnosticMessage {
|
pub enum DiagnosticMessage {
|
||||||
/// Non-translatable diagnostic message.
|
/// Non-translatable diagnostic message.
|
||||||
// FIXME(davidtwco): can a `Cow<'static, str>` be used here?
|
// FIXME(#100717): can a `Cow<'static, str>` be used here?
|
||||||
Str(String),
|
Str(String),
|
||||||
|
/// Translatable message which has already been translated eagerly.
|
||||||
|
///
|
||||||
|
/// Some diagnostics have repeated subdiagnostics where the same interpolated variables would
|
||||||
|
/// be instantiated multiple times with different values. As translation normally happens
|
||||||
|
/// immediately prior to emission, after the diagnostic and subdiagnostic derive logic has run,
|
||||||
|
/// the setting of diagnostic arguments in the derived code will overwrite previous variable
|
||||||
|
/// values and only the final value will be set when translation occurs - resulting in
|
||||||
|
/// incorrect diagnostics. Eager translation results in translation for a subdiagnostic
|
||||||
|
/// happening immediately after the subdiagnostic derive's logic has been run. This variant
|
||||||
|
/// stores messages which have been translated eagerly.
|
||||||
|
// FIXME(#100717): can a `Cow<'static, str>` be used here?
|
||||||
|
Eager(String),
|
||||||
/// Identifier for a Fluent message (with optional attribute) corresponding to the diagnostic
|
/// Identifier for a Fluent message (with optional attribute) corresponding to the diagnostic
|
||||||
/// message.
|
/// message.
|
||||||
///
|
///
|
||||||
|
@ -324,6 +348,7 @@ impl DiagnosticMessage {
|
||||||
pub fn with_subdiagnostic_message(&self, sub: SubdiagnosticMessage) -> Self {
|
pub fn with_subdiagnostic_message(&self, sub: SubdiagnosticMessage) -> Self {
|
||||||
let attr = match sub {
|
let attr = match sub {
|
||||||
SubdiagnosticMessage::Str(s) => return DiagnosticMessage::Str(s),
|
SubdiagnosticMessage::Str(s) => return DiagnosticMessage::Str(s),
|
||||||
|
SubdiagnosticMessage::Eager(s) => return DiagnosticMessage::Eager(s),
|
||||||
SubdiagnosticMessage::FluentIdentifier(id) => {
|
SubdiagnosticMessage::FluentIdentifier(id) => {
|
||||||
return DiagnosticMessage::FluentIdentifier(id, None);
|
return DiagnosticMessage::FluentIdentifier(id, None);
|
||||||
}
|
}
|
||||||
|
@ -332,6 +357,7 @@ impl DiagnosticMessage {
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
DiagnosticMessage::Str(s) => DiagnosticMessage::Str(s.clone()),
|
DiagnosticMessage::Str(s) => DiagnosticMessage::Str(s.clone()),
|
||||||
|
DiagnosticMessage::Eager(s) => DiagnosticMessage::Eager(s.clone()),
|
||||||
DiagnosticMessage::FluentIdentifier(id, _) => {
|
DiagnosticMessage::FluentIdentifier(id, _) => {
|
||||||
DiagnosticMessage::FluentIdentifier(id.clone(), Some(attr))
|
DiagnosticMessage::FluentIdentifier(id.clone(), Some(attr))
|
||||||
}
|
}
|
||||||
|
@ -367,6 +393,7 @@ impl Into<SubdiagnosticMessage> for DiagnosticMessage {
|
||||||
fn into(self) -> SubdiagnosticMessage {
|
fn into(self) -> SubdiagnosticMessage {
|
||||||
match self {
|
match self {
|
||||||
DiagnosticMessage::Str(s) => SubdiagnosticMessage::Str(s),
|
DiagnosticMessage::Str(s) => SubdiagnosticMessage::Str(s),
|
||||||
|
DiagnosticMessage::Eager(s) => SubdiagnosticMessage::Eager(s),
|
||||||
DiagnosticMessage::FluentIdentifier(id, None) => {
|
DiagnosticMessage::FluentIdentifier(id, None) => {
|
||||||
SubdiagnosticMessage::FluentIdentifier(id)
|
SubdiagnosticMessage::FluentIdentifier(id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
use crate::emitter::FileWithAnnotatedLines;
|
use crate::emitter::FileWithAnnotatedLines;
|
||||||
use crate::snippet::Line;
|
use crate::snippet::Line;
|
||||||
use crate::translation::Translate;
|
use crate::translation::{to_fluent_args, Translate};
|
||||||
use crate::{
|
use crate::{
|
||||||
CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Emitter, FluentBundle,
|
CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Emitter, FluentBundle,
|
||||||
LazyFallbackBundle, Level, MultiSpan, Style, SubDiagnostic,
|
LazyFallbackBundle, Level, MultiSpan, Style, SubDiagnostic,
|
||||||
|
@ -46,7 +46,7 @@ impl Translate for AnnotateSnippetEmitterWriter {
|
||||||
impl Emitter for AnnotateSnippetEmitterWriter {
|
impl Emitter for AnnotateSnippetEmitterWriter {
|
||||||
/// The entry point for the diagnostics generation
|
/// The entry point for the diagnostics generation
|
||||||
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
|
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
|
||||||
let fluent_args = self.to_fluent_args(diag.args());
|
let fluent_args = to_fluent_args(diag.args());
|
||||||
|
|
||||||
let mut children = diag.children.clone();
|
let mut children = diag.children.clone();
|
||||||
let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args);
|
let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args);
|
||||||
|
|
|
@ -27,7 +27,11 @@ pub struct SuggestionsDisabled;
|
||||||
/// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of
|
/// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of
|
||||||
/// `DiagnosticArg` are converted to `FluentArgs` (consuming the collection) at the start of
|
/// `DiagnosticArg` are converted to `FluentArgs` (consuming the collection) at the start of
|
||||||
/// diagnostic emission.
|
/// diagnostic emission.
|
||||||
pub type DiagnosticArg<'source> = (Cow<'source, str>, DiagnosticArgValue<'source>);
|
pub type DiagnosticArg<'iter, 'source> =
|
||||||
|
(&'iter DiagnosticArgName<'source>, &'iter DiagnosticArgValue<'source>);
|
||||||
|
|
||||||
|
/// Name of a diagnostic argument.
|
||||||
|
pub type DiagnosticArgName<'source> = Cow<'source, str>;
|
||||||
|
|
||||||
/// Simplified version of `FluentValue` that can implement `Encodable` and `Decodable`. Converted
|
/// Simplified version of `FluentValue` that can implement `Encodable` and `Decodable`. Converted
|
||||||
/// to a `FluentValue` by the emitter to be used in diagnostic translation.
|
/// to a `FluentValue` by the emitter to be used in diagnostic translation.
|
||||||
|
@ -199,9 +203,20 @@ impl IntoDiagnosticArg for ast::token::TokenKind {
|
||||||
/// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic].
|
/// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic].
|
||||||
#[cfg_attr(bootstrap, rustc_diagnostic_item = "AddSubdiagnostic")]
|
#[cfg_attr(bootstrap, rustc_diagnostic_item = "AddSubdiagnostic")]
|
||||||
#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "AddToDiagnostic")]
|
#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "AddToDiagnostic")]
|
||||||
pub trait AddToDiagnostic {
|
pub trait AddToDiagnostic
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
/// Add a subdiagnostic to an existing diagnostic.
|
/// Add a subdiagnostic to an existing diagnostic.
|
||||||
fn add_to_diagnostic(self, diag: &mut Diagnostic);
|
fn add_to_diagnostic(self, diag: &mut Diagnostic) {
|
||||||
|
self.add_to_diagnostic_with(diag, |_, m| m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a subdiagnostic to an existing diagnostic where `f` is invoked on every message used
|
||||||
|
/// (to optionally perform eager translation).
|
||||||
|
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, f: F)
|
||||||
|
where
|
||||||
|
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait implemented by lint types. This should not be implemented manually. Instead, use
|
/// Trait implemented by lint types. This should not be implemented manually. Instead, use
|
||||||
|
@ -229,7 +244,7 @@ pub struct Diagnostic {
|
||||||
pub span: MultiSpan,
|
pub span: MultiSpan,
|
||||||
pub children: Vec<SubDiagnostic>,
|
pub children: Vec<SubDiagnostic>,
|
||||||
pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
|
pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
|
||||||
args: Vec<DiagnosticArg<'static>>,
|
args: FxHashMap<DiagnosticArgName<'static>, DiagnosticArgValue<'static>>,
|
||||||
|
|
||||||
/// This is not used for highlighting or rendering any error message. Rather, it can be used
|
/// This is not used for highlighting or rendering any error message. Rather, it can be used
|
||||||
/// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
|
/// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
|
||||||
|
@ -321,7 +336,7 @@ impl Diagnostic {
|
||||||
span: MultiSpan::new(),
|
span: MultiSpan::new(),
|
||||||
children: vec![],
|
children: vec![],
|
||||||
suggestions: Ok(vec![]),
|
suggestions: Ok(vec![]),
|
||||||
args: vec![],
|
args: Default::default(),
|
||||||
sort_span: DUMMY_SP,
|
sort_span: DUMMY_SP,
|
||||||
is_lint: false,
|
is_lint: false,
|
||||||
}
|
}
|
||||||
|
@ -917,13 +932,30 @@ impl Diagnostic {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a subdiagnostic from a type that implements `Subdiagnostic` - see
|
/// Add a subdiagnostic from a type that implements `Subdiagnostic` (see
|
||||||
/// [rustc_macros::Subdiagnostic].
|
/// [rustc_macros::Subdiagnostic]).
|
||||||
pub fn subdiagnostic(&mut self, subdiagnostic: impl AddToDiagnostic) -> &mut Self {
|
pub fn subdiagnostic(&mut self, subdiagnostic: impl AddToDiagnostic) -> &mut Self {
|
||||||
subdiagnostic.add_to_diagnostic(self);
|
subdiagnostic.add_to_diagnostic(self);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a subdiagnostic from a type that implements `Subdiagnostic` (see
|
||||||
|
/// [rustc_macros::Subdiagnostic]). Performs eager translation of any translatable messages
|
||||||
|
/// used in the subdiagnostic, so suitable for use with repeated messages (i.e. re-use of
|
||||||
|
/// interpolated variables).
|
||||||
|
pub fn eager_subdiagnostic(
|
||||||
|
&mut self,
|
||||||
|
handler: &crate::Handler,
|
||||||
|
subdiagnostic: impl AddToDiagnostic,
|
||||||
|
) -> &mut Self {
|
||||||
|
subdiagnostic.add_to_diagnostic_with(self, |diag, msg| {
|
||||||
|
let args = diag.args();
|
||||||
|
let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
|
||||||
|
handler.eagerly_translate(msg, args)
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
|
pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
|
||||||
self.span = sp.into();
|
self.span = sp.into();
|
||||||
if let Some(span) = self.span.primary_span() {
|
if let Some(span) = self.span.primary_span() {
|
||||||
|
@ -956,8 +988,11 @@ impl Diagnostic {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn args(&self) -> &[DiagnosticArg<'static>] {
|
// Exact iteration order of diagnostic arguments shouldn't make a difference to output because
|
||||||
&self.args
|
// they're only used in interpolation.
|
||||||
|
#[allow(rustc::potential_query_instability)]
|
||||||
|
pub fn args<'a>(&'a self) -> impl Iterator<Item = DiagnosticArg<'a, 'static>> {
|
||||||
|
self.args.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_arg(
|
pub fn set_arg(
|
||||||
|
@ -965,7 +1000,7 @@ impl Diagnostic {
|
||||||
name: impl Into<Cow<'static, str>>,
|
name: impl Into<Cow<'static, str>>,
|
||||||
arg: impl IntoDiagnosticArg,
|
arg: impl IntoDiagnosticArg,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.args.push((name.into(), arg.into_diagnostic_arg()));
|
self.args.insert(name.into(), arg.into_diagnostic_arg());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -976,7 +1011,7 @@ impl Diagnostic {
|
||||||
/// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by
|
/// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by
|
||||||
/// combining it with the primary message of the diagnostic (if translatable, otherwise it just
|
/// combining it with the primary message of the diagnostic (if translatable, otherwise it just
|
||||||
/// passes the user's string along).
|
/// passes the user's string along).
|
||||||
fn subdiagnostic_message_to_diagnostic_message(
|
pub(crate) fn subdiagnostic_message_to_diagnostic_message(
|
||||||
&self,
|
&self,
|
||||||
attr: impl Into<SubdiagnosticMessage>,
|
attr: impl Into<SubdiagnosticMessage>,
|
||||||
) -> DiagnosticMessage {
|
) -> DiagnosticMessage {
|
||||||
|
|
|
@ -14,7 +14,7 @@ use rustc_span::{FileLines, SourceFile, Span};
|
||||||
|
|
||||||
use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString};
|
use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString};
|
||||||
use crate::styled_buffer::StyledBuffer;
|
use crate::styled_buffer::StyledBuffer;
|
||||||
use crate::translation::Translate;
|
use crate::translation::{to_fluent_args, Translate};
|
||||||
use crate::{
|
use crate::{
|
||||||
CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, FluentBundle, Handler,
|
CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, FluentBundle, Handler,
|
||||||
LazyFallbackBundle, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight, SuggestionStyle,
|
LazyFallbackBundle, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight, SuggestionStyle,
|
||||||
|
@ -535,7 +535,7 @@ impl Emitter for EmitterWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
|
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
|
||||||
let fluent_args = self.to_fluent_args(diag.args());
|
let fluent_args = to_fluent_args(diag.args());
|
||||||
|
|
||||||
let mut children = diag.children.clone();
|
let mut children = diag.children.clone();
|
||||||
let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args);
|
let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args);
|
||||||
|
|
|
@ -13,7 +13,7 @@ use rustc_span::source_map::{FilePathMapping, SourceMap};
|
||||||
|
|
||||||
use crate::emitter::{Emitter, HumanReadableErrorType};
|
use crate::emitter::{Emitter, HumanReadableErrorType};
|
||||||
use crate::registry::Registry;
|
use crate::registry::Registry;
|
||||||
use crate::translation::Translate;
|
use crate::translation::{to_fluent_args, Translate};
|
||||||
use crate::DiagnosticId;
|
use crate::DiagnosticId;
|
||||||
use crate::{
|
use crate::{
|
||||||
CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, SubDiagnostic,
|
CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, SubDiagnostic,
|
||||||
|
@ -312,7 +312,7 @@ struct UnusedExterns<'a, 'b, 'c> {
|
||||||
|
|
||||||
impl Diagnostic {
|
impl Diagnostic {
|
||||||
fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
|
fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
|
||||||
let args = je.to_fluent_args(diag.args());
|
let args = to_fluent_args(diag.args());
|
||||||
let sugg = diag.suggestions.iter().flatten().map(|sugg| {
|
let sugg = diag.suggestions.iter().flatten().map(|sugg| {
|
||||||
let translated_message = je.translate_message(&sugg.msg, &args);
|
let translated_message = je.translate_message(&sugg.msg, &args);
|
||||||
Diagnostic {
|
Diagnostic {
|
||||||
|
|
|
@ -598,6 +598,17 @@ impl Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Translate `message` eagerly with `args`.
|
||||||
|
pub fn eagerly_translate<'a>(
|
||||||
|
&self,
|
||||||
|
message: DiagnosticMessage,
|
||||||
|
args: impl Iterator<Item = DiagnosticArg<'a, 'static>>,
|
||||||
|
) -> SubdiagnosticMessage {
|
||||||
|
let inner = self.inner.borrow();
|
||||||
|
let args = crate::translation::to_fluent_args(args);
|
||||||
|
SubdiagnosticMessage::Eager(inner.emitter.translate_message(&message, &args).to_string())
|
||||||
|
}
|
||||||
|
|
||||||
// This is here to not allow mutation of flags;
|
// This is here to not allow mutation of flags;
|
||||||
// as of this writing it's only used in tests in librustc_middle.
|
// as of this writing it's only used in tests in librustc_middle.
|
||||||
pub fn can_emit_warnings(&self) -> bool {
|
pub fn can_emit_warnings(&self) -> bool {
|
||||||
|
|
|
@ -4,6 +4,27 @@ use rustc_data_structures::sync::Lrc;
|
||||||
use rustc_error_messages::FluentArgs;
|
use rustc_error_messages::FluentArgs;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
/// Convert diagnostic arguments (a rustc internal type that exists to implement
|
||||||
|
/// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation.
|
||||||
|
///
|
||||||
|
/// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then
|
||||||
|
/// passed around as a reference thereafter.
|
||||||
|
pub fn to_fluent_args<'iter, 'arg: 'iter>(
|
||||||
|
iter: impl Iterator<Item = DiagnosticArg<'iter, 'arg>>,
|
||||||
|
) -> FluentArgs<'arg> {
|
||||||
|
let mut args = if let Some(size) = iter.size_hint().1 {
|
||||||
|
FluentArgs::with_capacity(size)
|
||||||
|
} else {
|
||||||
|
FluentArgs::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
for (k, v) in iter {
|
||||||
|
args.set(k.clone(), v.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
args
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Translate {
|
pub trait Translate {
|
||||||
/// Return `FluentBundle` with localized diagnostics for the locale requested by the user. If no
|
/// Return `FluentBundle` with localized diagnostics for the locale requested by the user. If no
|
||||||
/// language was requested by the user then this will be `None` and `fallback_fluent_bundle`
|
/// language was requested by the user then this will be `None` and `fallback_fluent_bundle`
|
||||||
|
@ -15,15 +36,6 @@ pub trait Translate {
|
||||||
/// unavailable for the requested locale.
|
/// unavailable for the requested locale.
|
||||||
fn fallback_fluent_bundle(&self) -> &FluentBundle;
|
fn fallback_fluent_bundle(&self) -> &FluentBundle;
|
||||||
|
|
||||||
/// Convert diagnostic arguments (a rustc internal type that exists to implement
|
|
||||||
/// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation.
|
|
||||||
///
|
|
||||||
/// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then
|
|
||||||
/// passed around as a reference thereafter.
|
|
||||||
fn to_fluent_args<'arg>(&self, args: &[DiagnosticArg<'arg>]) -> FluentArgs<'arg> {
|
|
||||||
FromIterator::from_iter(args.iter().cloned())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert `DiagnosticMessage`s to a string, performing translation if necessary.
|
/// Convert `DiagnosticMessage`s to a string, performing translation if necessary.
|
||||||
fn translate_messages(
|
fn translate_messages(
|
||||||
&self,
|
&self,
|
||||||
|
@ -43,7 +55,9 @@ pub trait Translate {
|
||||||
) -> Cow<'_, str> {
|
) -> Cow<'_, str> {
|
||||||
trace!(?message, ?args);
|
trace!(?message, ?args);
|
||||||
let (identifier, attr) = match message {
|
let (identifier, attr) = match message {
|
||||||
DiagnosticMessage::Str(msg) => return Cow::Borrowed(&msg),
|
DiagnosticMessage::Str(msg) | DiagnosticMessage::Eager(msg) => {
|
||||||
|
return Cow::Borrowed(&msg);
|
||||||
|
}
|
||||||
DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
|
DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use hir::GenericParamKind;
|
use hir::GenericParamKind;
|
||||||
use rustc_errors::{
|
use rustc_errors::{
|
||||||
fluent, AddToDiagnostic, Applicability, DiagnosticMessage, DiagnosticStyledString, MultiSpan,
|
fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticMessage, DiagnosticStyledString,
|
||||||
|
MultiSpan, SubdiagnosticMessage,
|
||||||
};
|
};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::{FnRetTy, Ty};
|
use rustc_hir::{FnRetTy, Ty};
|
||||||
|
@ -229,7 +230,10 @@ pub enum RegionOriginNote<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddToDiagnostic for RegionOriginNote<'_> {
|
impl AddToDiagnostic for RegionOriginNote<'_> {
|
||||||
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
|
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
|
||||||
|
where
|
||||||
|
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
|
||||||
|
{
|
||||||
let mut label_or_note = |span, msg: DiagnosticMessage| {
|
let mut label_or_note = |span, msg: DiagnosticMessage| {
|
||||||
let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count();
|
let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count();
|
||||||
let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count();
|
let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count();
|
||||||
|
@ -290,7 +294,10 @@ pub enum LifetimeMismatchLabels {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddToDiagnostic for LifetimeMismatchLabels {
|
impl AddToDiagnostic for LifetimeMismatchLabels {
|
||||||
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
|
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
|
||||||
|
where
|
||||||
|
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
|
||||||
|
{
|
||||||
match self {
|
match self {
|
||||||
LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => {
|
LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => {
|
||||||
diag.span_label(param_span, fluent::infer::declared_different);
|
diag.span_label(param_span, fluent::infer::declared_different);
|
||||||
|
@ -340,7 +347,10 @@ pub struct AddLifetimeParamsSuggestion<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddToDiagnostic for AddLifetimeParamsSuggestion<'_> {
|
impl AddToDiagnostic for AddLifetimeParamsSuggestion<'_> {
|
||||||
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
|
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
|
||||||
|
where
|
||||||
|
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
|
||||||
|
{
|
||||||
let mut mk_suggestion = || {
|
let mut mk_suggestion = || {
|
||||||
let (
|
let (
|
||||||
hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. },
|
hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. },
|
||||||
|
@ -439,7 +449,10 @@ pub struct IntroducesStaticBecauseUnmetLifetimeReq {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddToDiagnostic for IntroducesStaticBecauseUnmetLifetimeReq {
|
impl AddToDiagnostic for IntroducesStaticBecauseUnmetLifetimeReq {
|
||||||
fn add_to_diagnostic(mut self, diag: &mut rustc_errors::Diagnostic) {
|
fn add_to_diagnostic_with<F>(mut self, diag: &mut Diagnostic, _: F)
|
||||||
|
where
|
||||||
|
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
|
||||||
|
{
|
||||||
self.unmet_requirements
|
self.unmet_requirements
|
||||||
.push_span_label(self.binding_span, fluent::infer::msl_introduces_static);
|
.push_span_label(self.binding_span, fluent::infer::msl_introduces_static);
|
||||||
diag.span_note(self.unmet_requirements, fluent::infer::msl_unmet_req);
|
diag.span_note(self.unmet_requirements, fluent::infer::msl_unmet_req);
|
||||||
|
@ -451,7 +464,10 @@ pub struct ImplNote {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddToDiagnostic for ImplNote {
|
impl AddToDiagnostic for ImplNote {
|
||||||
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
|
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
|
||||||
|
where
|
||||||
|
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
|
||||||
|
{
|
||||||
match self.impl_span {
|
match self.impl_span {
|
||||||
Some(span) => diag.span_note(span, fluent::infer::msl_impl_note),
|
Some(span) => diag.span_note(span, fluent::infer::msl_impl_note),
|
||||||
None => diag.note(fluent::infer::msl_impl_note),
|
None => diag.note(fluent::infer::msl_impl_note),
|
||||||
|
@ -466,7 +482,10 @@ pub enum TraitSubdiag {
|
||||||
|
|
||||||
// FIXME(#100717) used in `Vec<TraitSubdiag>` so requires eager translation/list support
|
// FIXME(#100717) used in `Vec<TraitSubdiag>` so requires eager translation/list support
|
||||||
impl AddToDiagnostic for TraitSubdiag {
|
impl AddToDiagnostic for TraitSubdiag {
|
||||||
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
|
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
|
||||||
|
where
|
||||||
|
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
|
||||||
|
{
|
||||||
match self {
|
match self {
|
||||||
TraitSubdiag::Note { span } => {
|
TraitSubdiag::Note { span } => {
|
||||||
diag.span_note(span, "this has an implicit `'static` lifetime requirement");
|
diag.span_note(span, "this has an implicit `'static` lifetime requirement");
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use crate::infer::error_reporting::nice_region_error::find_anon_type;
|
use crate::infer::error_reporting::nice_region_error::find_anon_type;
|
||||||
use rustc_errors::{self, fluent, AddToDiagnostic, IntoDiagnosticArg};
|
use rustc_errors::{
|
||||||
|
self, fluent, AddToDiagnostic, Diagnostic, IntoDiagnosticArg, SubdiagnosticMessage,
|
||||||
|
};
|
||||||
use rustc_middle::ty::{self, TyCtxt};
|
use rustc_middle::ty::{self, TyCtxt};
|
||||||
use rustc_span::{symbol::kw, Span};
|
use rustc_span::{symbol::kw, Span};
|
||||||
|
|
||||||
|
@ -159,7 +161,10 @@ impl RegionExplanation<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddToDiagnostic for RegionExplanation<'_> {
|
impl AddToDiagnostic for RegionExplanation<'_> {
|
||||||
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
|
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
|
||||||
|
where
|
||||||
|
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
|
||||||
|
{
|
||||||
if let Some(span) = self.desc.span {
|
if let Some(span) = self.desc.span {
|
||||||
diag.span_note(span, fluent::infer::region_explanation);
|
diag.span_note(span, fluent::infer::region_explanation);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use rustc_errors::{fluent, AddToDiagnostic, ErrorGuaranteed, Handler, IntoDiagnostic};
|
use rustc_errors::{
|
||||||
|
fluent, AddToDiagnostic, Diagnostic, ErrorGuaranteed, Handler, IntoDiagnostic,
|
||||||
|
SubdiagnosticMessage,
|
||||||
|
};
|
||||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||||
use rustc_session::lint::Level;
|
use rustc_session::lint::Level;
|
||||||
use rustc_span::{Span, Symbol};
|
use rustc_span::{Span, Symbol};
|
||||||
|
@ -23,7 +26,10 @@ pub enum OverruledAttributeSub {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddToDiagnostic for OverruledAttributeSub {
|
impl AddToDiagnostic for OverruledAttributeSub {
|
||||||
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
|
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
|
||||||
|
where
|
||||||
|
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
|
||||||
|
{
|
||||||
match self {
|
match self {
|
||||||
OverruledAttributeSub::DefaultSource { id } => {
|
OverruledAttributeSub::DefaultSource { id } => {
|
||||||
diag.note(fluent::lint::default_source);
|
diag.note(fluent::lint::default_source);
|
||||||
|
@ -88,7 +94,10 @@ pub struct RequestedLevel {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddToDiagnostic for RequestedLevel {
|
impl AddToDiagnostic for RequestedLevel {
|
||||||
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
|
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
|
||||||
|
where
|
||||||
|
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
|
||||||
|
{
|
||||||
diag.note(fluent::lint::requested_level);
|
diag.note(fluent::lint::requested_level);
|
||||||
diag.set_arg(
|
diag.set_arg(
|
||||||
"level",
|
"level",
|
||||||
|
|
|
@ -10,27 +10,31 @@ use synstructure::Structure;
|
||||||
/// The central struct for constructing the `into_diagnostic` method from an annotated struct.
|
/// The central struct for constructing the `into_diagnostic` method from an annotated struct.
|
||||||
pub(crate) struct DiagnosticDerive<'a> {
|
pub(crate) struct DiagnosticDerive<'a> {
|
||||||
structure: Structure<'a>,
|
structure: Structure<'a>,
|
||||||
handler: syn::Ident,
|
|
||||||
builder: DiagnosticDeriveBuilder,
|
builder: DiagnosticDeriveBuilder,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DiagnosticDerive<'a> {
|
impl<'a> DiagnosticDerive<'a> {
|
||||||
pub(crate) fn new(diag: syn::Ident, handler: syn::Ident, structure: Structure<'a>) -> Self {
|
pub(crate) fn new(diag: syn::Ident, handler: syn::Ident, structure: Structure<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
builder: DiagnosticDeriveBuilder { diag, kind: DiagnosticDeriveKind::Diagnostic },
|
builder: DiagnosticDeriveBuilder {
|
||||||
handler,
|
diag,
|
||||||
|
kind: DiagnosticDeriveKind::Diagnostic { handler },
|
||||||
|
},
|
||||||
structure,
|
structure,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn into_tokens(self) -> TokenStream {
|
pub(crate) fn into_tokens(self) -> TokenStream {
|
||||||
let DiagnosticDerive { mut structure, handler, mut builder } = self;
|
let DiagnosticDerive { mut structure, mut builder } = self;
|
||||||
|
|
||||||
let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
|
let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
|
||||||
let preamble = builder.preamble(&variant);
|
let preamble = builder.preamble(&variant);
|
||||||
let body = builder.body(&variant);
|
let body = builder.body(&variant);
|
||||||
|
|
||||||
let diag = &builder.parent.diag;
|
let diag = &builder.parent.diag;
|
||||||
|
let DiagnosticDeriveKind::Diagnostic { handler } = &builder.parent.kind else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
let init = match builder.slug.value_ref() {
|
let init = match builder.slug.value_ref() {
|
||||||
None => {
|
None => {
|
||||||
span_err(builder.span, "diagnostic slug not specified")
|
span_err(builder.span, "diagnostic slug not specified")
|
||||||
|
@ -48,14 +52,17 @@ impl<'a> DiagnosticDerive<'a> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let formatting_init = &builder.formatting_init;
|
||||||
quote! {
|
quote! {
|
||||||
#init
|
#init
|
||||||
|
#formatting_init
|
||||||
#preamble
|
#preamble
|
||||||
#body
|
#body
|
||||||
#diag
|
#diag
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let DiagnosticDeriveKind::Diagnostic { handler } = &builder.kind else { unreachable!() };
|
||||||
structure.gen_impl(quote! {
|
structure.gen_impl(quote! {
|
||||||
gen impl<'__diagnostic_handler_sess, G>
|
gen impl<'__diagnostic_handler_sess, G>
|
||||||
rustc_errors::IntoDiagnostic<'__diagnostic_handler_sess, G>
|
rustc_errors::IntoDiagnostic<'__diagnostic_handler_sess, G>
|
||||||
|
@ -96,17 +103,18 @@ impl<'a> LintDiagnosticDerive<'a> {
|
||||||
let body = builder.body(&variant);
|
let body = builder.body(&variant);
|
||||||
|
|
||||||
let diag = &builder.parent.diag;
|
let diag = &builder.parent.diag;
|
||||||
|
let formatting_init = &builder.formatting_init;
|
||||||
quote! {
|
quote! {
|
||||||
#preamble
|
#preamble
|
||||||
|
#formatting_init
|
||||||
#body
|
#body
|
||||||
#diag
|
#diag
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let msg = builder.each_variant(&mut structure, |mut builder, variant| {
|
let msg = builder.each_variant(&mut structure, |mut builder, variant| {
|
||||||
// HACK(wafflelapkin): initialize slug (???)
|
// Collect the slug by generating the preamble.
|
||||||
let _preamble = builder.preamble(&variant);
|
let _ = builder.preamble(&variant);
|
||||||
|
|
||||||
match builder.slug.value_ref() {
|
match builder.slug.value_ref() {
|
||||||
None => {
|
None => {
|
||||||
|
@ -125,7 +133,10 @@ impl<'a> LintDiagnosticDerive<'a> {
|
||||||
let diag = &builder.diag;
|
let diag = &builder.diag;
|
||||||
structure.gen_impl(quote! {
|
structure.gen_impl(quote! {
|
||||||
gen impl<'__a> rustc_errors::DecorateLint<'__a, ()> for @Self {
|
gen impl<'__a> rustc_errors::DecorateLint<'__a, ()> for @Self {
|
||||||
fn decorate_lint<'__b>(self, #diag: &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()>) -> &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()> {
|
fn decorate_lint<'__b>(
|
||||||
|
self,
|
||||||
|
#diag: &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()>
|
||||||
|
) -> &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()> {
|
||||||
use rustc_errors::IntoDiagnosticArg;
|
use rustc_errors::IntoDiagnosticArg;
|
||||||
#implementation
|
#implementation
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,9 @@ use crate::diagnostics::error::{
|
||||||
DiagnosticDeriveError,
|
DiagnosticDeriveError,
|
||||||
};
|
};
|
||||||
use crate::diagnostics::utils::{
|
use crate::diagnostics::utils::{
|
||||||
bind_style_of_field, build_field_mapping, report_error_if_not_applied_to_span,
|
build_field_mapping, report_error_if_not_applied_to_span, report_type_error,
|
||||||
report_type_error, should_generate_set_arg, type_is_unit, type_matches_path, FieldInfo,
|
should_generate_set_arg, type_is_unit, type_matches_path, FieldInfo, FieldInnerTy, FieldMap,
|
||||||
FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
|
HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
|
||||||
};
|
};
|
||||||
use proc_macro2::{Ident, Span, TokenStream};
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
|
@ -17,9 +17,9 @@ use syn::{
|
||||||
use synstructure::{BindingInfo, Structure, VariantInfo};
|
use synstructure::{BindingInfo, Structure, VariantInfo};
|
||||||
|
|
||||||
/// What kind of diagnostic is being derived - a fatal/error/warning or a lint?
|
/// What kind of diagnostic is being derived - a fatal/error/warning or a lint?
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub(crate) enum DiagnosticDeriveKind {
|
pub(crate) enum DiagnosticDeriveKind {
|
||||||
Diagnostic,
|
Diagnostic { handler: syn::Ident },
|
||||||
LintDiagnostic,
|
LintDiagnostic,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,9 @@ pub(crate) struct DiagnosticDeriveVariantBuilder<'parent> {
|
||||||
/// The parent builder for the entire type.
|
/// The parent builder for the entire type.
|
||||||
pub parent: &'parent DiagnosticDeriveBuilder,
|
pub parent: &'parent DiagnosticDeriveBuilder,
|
||||||
|
|
||||||
|
/// Initialization of format strings for code suggestions.
|
||||||
|
pub formatting_init: TokenStream,
|
||||||
|
|
||||||
/// Span of the struct or the enum variant.
|
/// Span of the struct or the enum variant.
|
||||||
pub span: proc_macro::Span,
|
pub span: proc_macro::Span,
|
||||||
|
|
||||||
|
@ -88,19 +91,7 @@ impl DiagnosticDeriveBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for variant in structure.variants_mut() {
|
structure.bind_with(|_| synstructure::BindStyle::Move);
|
||||||
// First, change the binding style of each field based on the code that will be
|
|
||||||
// generated for the field - e.g. `set_arg` calls needs by-move bindings, whereas
|
|
||||||
// `set_primary_span` only needs by-ref.
|
|
||||||
variant.bind_with(|bi| bind_style_of_field(bi.ast()).0);
|
|
||||||
|
|
||||||
// Then, perform a stable sort on bindings which generates code for by-ref bindings
|
|
||||||
// before code generated for by-move bindings. Any code generated for the by-ref
|
|
||||||
// bindings which creates a reference to the by-move fields will happen before the
|
|
||||||
// by-move bindings move those fields and make them inaccessible.
|
|
||||||
variant.bindings_mut().sort_by_cached_key(|bi| bind_style_of_field(bi.ast()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let variants = structure.each_variant(|variant| {
|
let variants = structure.each_variant(|variant| {
|
||||||
let span = match structure.ast().data {
|
let span = match structure.ast().data {
|
||||||
syn::Data::Struct(..) => span,
|
syn::Data::Struct(..) => span,
|
||||||
|
@ -112,6 +103,7 @@ impl DiagnosticDeriveBuilder {
|
||||||
parent: &self,
|
parent: &self,
|
||||||
span,
|
span,
|
||||||
field_map: build_field_mapping(variant),
|
field_map: build_field_mapping(variant),
|
||||||
|
formatting_init: TokenStream::new(),
|
||||||
slug: None,
|
slug: None,
|
||||||
code: None,
|
code: None,
|
||||||
};
|
};
|
||||||
|
@ -143,16 +135,14 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
|
||||||
|
|
||||||
/// Generates calls to `span_label` and similar functions based on the attributes on fields or
|
/// Generates calls to `span_label` and similar functions based on the attributes on fields or
|
||||||
/// calls to `set_arg` when no attributes are present.
|
/// calls to `set_arg` when no attributes are present.
|
||||||
///
|
|
||||||
/// Expects use of `Self::each_variant` which will have sorted bindings so that by-ref bindings
|
|
||||||
/// (which may create references to by-move bindings) have their code generated first -
|
|
||||||
/// necessary as code for suggestions uses formatting machinery and the value of other fields
|
|
||||||
/// (any given field can be referenced multiple times, so must be accessed through a borrow);
|
|
||||||
/// and when passing fields to `add_subdiagnostic` or `set_arg` for Fluent, fields must be
|
|
||||||
/// accessed by-move.
|
|
||||||
pub fn body<'s>(&mut self, variant: &VariantInfo<'s>) -> TokenStream {
|
pub fn body<'s>(&mut self, variant: &VariantInfo<'s>) -> TokenStream {
|
||||||
let mut body = quote! {};
|
let mut body = quote! {};
|
||||||
for binding in variant.bindings() {
|
// Generate `set_arg` calls first..
|
||||||
|
for binding in variant.bindings().iter().filter(|bi| should_generate_set_arg(bi.ast())) {
|
||||||
|
body.extend(self.generate_field_code(binding));
|
||||||
|
}
|
||||||
|
// ..and then subdiagnostic additions.
|
||||||
|
for binding in variant.bindings().iter().filter(|bi| !should_generate_set_arg(bi.ast())) {
|
||||||
body.extend(self.generate_field_attrs_code(binding));
|
body.extend(self.generate_field_attrs_code(binding));
|
||||||
}
|
}
|
||||||
body
|
body
|
||||||
|
@ -274,24 +264,27 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
|
fn generate_field_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
|
||||||
|
let diag = &self.parent.diag;
|
||||||
|
|
||||||
let field = binding_info.ast();
|
let field = binding_info.ast();
|
||||||
let field_binding = &binding_info.binding;
|
let field_binding = &binding_info.binding;
|
||||||
|
|
||||||
if should_generate_set_arg(&field) {
|
|
||||||
let diag = &self.parent.diag;
|
|
||||||
let ident = field.ident.as_ref().unwrap();
|
let ident = field.ident.as_ref().unwrap();
|
||||||
// strip `r#` prefix, if present
|
let ident = format_ident!("{}", ident); // strip `r#` prefix, if present
|
||||||
let ident = format_ident!("{}", ident);
|
|
||||||
return quote! {
|
quote! {
|
||||||
#diag.set_arg(
|
#diag.set_arg(
|
||||||
stringify!(#ident),
|
stringify!(#ident),
|
||||||
#field_binding
|
#field_binding
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let needs_move = bind_style_of_field(&field).is_move();
|
fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
|
||||||
|
let field = binding_info.ast();
|
||||||
|
let field_binding = &binding_info.binding;
|
||||||
|
|
||||||
let inner_ty = FieldInnerTy::from_type(&field.ty);
|
let inner_ty = FieldInnerTy::from_type(&field.ty);
|
||||||
|
|
||||||
field
|
field
|
||||||
|
@ -304,10 +297,8 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
|
||||||
let (binding, needs_destructure) = if needs_clone {
|
let (binding, needs_destructure) = if needs_clone {
|
||||||
// `primary_span` can accept a `Vec<Span>` so don't destructure that.
|
// `primary_span` can accept a `Vec<Span>` so don't destructure that.
|
||||||
(quote! { #field_binding.clone() }, false)
|
(quote! { #field_binding.clone() }, false)
|
||||||
} else if needs_move {
|
|
||||||
(quote! { #field_binding }, true)
|
|
||||||
} else {
|
} else {
|
||||||
(quote! { *#field_binding }, true)
|
(quote! { #field_binding }, true)
|
||||||
};
|
};
|
||||||
|
|
||||||
let generated_code = self
|
let generated_code = self
|
||||||
|
@ -340,18 +331,15 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
|
||||||
let diag = &self.parent.diag;
|
let diag = &self.parent.diag;
|
||||||
let meta = attr.parse_meta()?;
|
let meta = attr.parse_meta()?;
|
||||||
|
|
||||||
if let Meta::Path(_) = meta {
|
|
||||||
let ident = &attr.path.segments.last().unwrap().ident;
|
let ident = &attr.path.segments.last().unwrap().ident;
|
||||||
let name = ident.to_string();
|
let name = ident.to_string();
|
||||||
let name = name.as_str();
|
match (&meta, name.as_str()) {
|
||||||
match name {
|
|
||||||
"skip_arg" => {
|
|
||||||
// Don't need to do anything - by virtue of the attribute existing, the
|
// Don't need to do anything - by virtue of the attribute existing, the
|
||||||
// `set_arg` call will not be generated.
|
// `set_arg` call will not be generated.
|
||||||
return Ok(quote! {});
|
(Meta::Path(_), "skip_arg") => return Ok(quote! {}),
|
||||||
}
|
(Meta::Path(_), "primary_span") => {
|
||||||
"primary_span" => match self.parent.kind {
|
match self.parent.kind {
|
||||||
DiagnosticDeriveKind::Diagnostic => {
|
DiagnosticDeriveKind::Diagnostic { .. } => {
|
||||||
report_error_if_not_applied_to_span(attr, &info)?;
|
report_error_if_not_applied_to_span(attr, &info)?;
|
||||||
|
|
||||||
return Ok(quote! {
|
return Ok(quote! {
|
||||||
|
@ -363,11 +351,51 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
|
||||||
diag.help("the `primary_span` field attribute is not valid for lint diagnostics")
|
diag.help("the `primary_span` field attribute is not valid for lint diagnostics")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"subdiagnostic" => return Ok(quote! { #diag.subdiagnostic(#binding); }),
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
(Meta::Path(_), "subdiagnostic") => {
|
||||||
|
return Ok(quote! { #diag.subdiagnostic(#binding); });
|
||||||
|
}
|
||||||
|
(Meta::NameValue(_), "subdiagnostic") => {
|
||||||
|
throw_invalid_attr!(attr, &meta, |diag| {
|
||||||
|
diag.help("`eager` is the only supported nested attribute for `subdiagnostic`")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(Meta::List(MetaList { ref nested, .. }), "subdiagnostic") => {
|
||||||
|
if nested.len() != 1 {
|
||||||
|
throw_invalid_attr!(attr, &meta, |diag| {
|
||||||
|
diag.help(
|
||||||
|
"`eager` is the only supported nested attribute for `subdiagnostic`",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let handler = match &self.parent.kind {
|
||||||
|
DiagnosticDeriveKind::Diagnostic { handler } => handler,
|
||||||
|
DiagnosticDeriveKind::LintDiagnostic => {
|
||||||
|
throw_invalid_attr!(attr, &meta, |diag| {
|
||||||
|
diag.help("eager subdiagnostics are not supported on lints")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let nested_attr = nested.first().expect("pop failed for single element list");
|
||||||
|
match nested_attr {
|
||||||
|
NestedMeta::Meta(meta @ Meta::Path(_))
|
||||||
|
if meta.path().segments.last().unwrap().ident.to_string().as_str()
|
||||||
|
== "eager" =>
|
||||||
|
{
|
||||||
|
return Ok(quote! { #diag.eager_subdiagnostic(#handler, #binding); });
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
throw_invalid_nested_attr!(attr, nested_attr, |diag| {
|
||||||
|
diag.help("`eager` is the only supported nested attribute for `subdiagnostic`")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
let (subdiag, slug) = self.parse_subdiag_attribute(attr)?;
|
let (subdiag, slug) = self.parse_subdiag_attribute(attr)?;
|
||||||
|
|
||||||
|
@ -389,7 +417,8 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
|
||||||
SubdiagnosticKind::Suggestion {
|
SubdiagnosticKind::Suggestion {
|
||||||
suggestion_kind,
|
suggestion_kind,
|
||||||
applicability: static_applicability,
|
applicability: static_applicability,
|
||||||
code,
|
code_field,
|
||||||
|
code_init,
|
||||||
} => {
|
} => {
|
||||||
let (span_field, mut applicability) = self.span_and_applicability_of_ty(info)?;
|
let (span_field, mut applicability) = self.span_and_applicability_of_ty(info)?;
|
||||||
|
|
||||||
|
@ -402,11 +431,12 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
|
||||||
.unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified });
|
.unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified });
|
||||||
let style = suggestion_kind.to_suggestion_style();
|
let style = suggestion_kind.to_suggestion_style();
|
||||||
|
|
||||||
|
self.formatting_init.extend(code_init);
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#diag.span_suggestion_with_style(
|
#diag.span_suggestion_with_style(
|
||||||
#span_field,
|
#span_field,
|
||||||
rustc_errors::fluent::#slug,
|
rustc_errors::fluent::#slug,
|
||||||
#code,
|
#code_field,
|
||||||
#applicability,
|
#applicability,
|
||||||
#style
|
#style
|
||||||
);
|
);
|
||||||
|
@ -451,7 +481,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
|
||||||
// If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`.
|
// If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`.
|
||||||
ty @ Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => {
|
ty @ Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => {
|
||||||
let binding = &info.binding.binding;
|
let binding = &info.binding.binding;
|
||||||
Ok((quote!(*#binding), None))
|
Ok((quote!(#binding), None))
|
||||||
}
|
}
|
||||||
// If `ty` is `(Span, Applicability)` then return tokens accessing those.
|
// If `ty` is `(Span, Applicability)` then return tokens accessing those.
|
||||||
Type::Tuple(tup) => {
|
Type::Tuple(tup) => {
|
||||||
|
|
|
@ -9,7 +9,7 @@ use diagnostic::{DiagnosticDerive, LintDiagnosticDerive};
|
||||||
pub(crate) use fluent::fluent_messages;
|
pub(crate) use fluent::fluent_messages;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::format_ident;
|
use quote::format_ident;
|
||||||
use subdiagnostic::SubdiagnosticDerive;
|
use subdiagnostic::SubdiagnosticDeriveBuilder;
|
||||||
use synstructure::Structure;
|
use synstructure::Structure;
|
||||||
|
|
||||||
/// Implements `#[derive(Diagnostic)]`, which allows for errors to be specified as a struct,
|
/// Implements `#[derive(Diagnostic)]`, which allows for errors to be specified as a struct,
|
||||||
|
@ -155,5 +155,5 @@ pub fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream {
|
||||||
/// diag.subdiagnostic(RawIdentifierSuggestion { span, applicability, ident });
|
/// diag.subdiagnostic(RawIdentifierSuggestion { span, applicability, ident });
|
||||||
/// ```
|
/// ```
|
||||||
pub fn session_subdiagnostic_derive(s: Structure<'_>) -> TokenStream {
|
pub fn session_subdiagnostic_derive(s: Structure<'_>) -> TokenStream {
|
||||||
SubdiagnosticDerive::new(s).into_tokens()
|
SubdiagnosticDeriveBuilder::new().into_tokens(s)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::diagnostics::error::{
|
||||||
DiagnosticDeriveError,
|
DiagnosticDeriveError,
|
||||||
};
|
};
|
||||||
use crate::diagnostics::utils::{
|
use crate::diagnostics::utils::{
|
||||||
build_field_mapping, report_error_if_not_applied_to_applicability,
|
build_field_mapping, new_code_ident, report_error_if_not_applied_to_applicability,
|
||||||
report_error_if_not_applied_to_span, FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce,
|
report_error_if_not_applied_to_span, FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce,
|
||||||
SpannedOption, SubdiagnosticKind,
|
SpannedOption, SubdiagnosticKind,
|
||||||
};
|
};
|
||||||
|
@ -15,19 +15,19 @@ use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta
|
||||||
use synstructure::{BindingInfo, Structure, VariantInfo};
|
use synstructure::{BindingInfo, Structure, VariantInfo};
|
||||||
|
|
||||||
/// The central struct for constructing the `add_to_diagnostic` method from an annotated struct.
|
/// The central struct for constructing the `add_to_diagnostic` method from an annotated struct.
|
||||||
pub(crate) struct SubdiagnosticDerive<'a> {
|
pub(crate) struct SubdiagnosticDeriveBuilder {
|
||||||
structure: Structure<'a>,
|
|
||||||
diag: syn::Ident,
|
diag: syn::Ident,
|
||||||
|
f: syn::Ident,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SubdiagnosticDerive<'a> {
|
impl SubdiagnosticDeriveBuilder {
|
||||||
pub(crate) fn new(structure: Structure<'a>) -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
let diag = format_ident!("diag");
|
let diag = format_ident!("diag");
|
||||||
Self { structure, diag }
|
let f = format_ident!("f");
|
||||||
|
Self { diag, f }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn into_tokens(self) -> TokenStream {
|
pub(crate) fn into_tokens<'a>(self, mut structure: Structure<'a>) -> TokenStream {
|
||||||
let SubdiagnosticDerive { mut structure, diag } = self;
|
|
||||||
let implementation = {
|
let implementation = {
|
||||||
let ast = structure.ast();
|
let ast = structure.ast();
|
||||||
let span = ast.span().unwrap();
|
let span = ast.span().unwrap();
|
||||||
|
@ -53,10 +53,11 @@ impl<'a> SubdiagnosticDerive<'a> {
|
||||||
|
|
||||||
structure.bind_with(|_| synstructure::BindStyle::Move);
|
structure.bind_with(|_| synstructure::BindStyle::Move);
|
||||||
let variants_ = structure.each_variant(|variant| {
|
let variants_ = structure.each_variant(|variant| {
|
||||||
let mut builder = SubdiagnosticDeriveBuilder {
|
let mut builder = SubdiagnosticDeriveVariantBuilder {
|
||||||
diag: &diag,
|
parent: &self,
|
||||||
variant,
|
variant,
|
||||||
span,
|
span,
|
||||||
|
formatting_init: TokenStream::new(),
|
||||||
fields: build_field_mapping(variant),
|
fields: build_field_mapping(variant),
|
||||||
span_field: None,
|
span_field: None,
|
||||||
applicability: None,
|
applicability: None,
|
||||||
|
@ -72,9 +73,17 @@ impl<'a> SubdiagnosticDerive<'a> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let diag = &self.diag;
|
||||||
|
let f = &self.f;
|
||||||
let ret = structure.gen_impl(quote! {
|
let ret = structure.gen_impl(quote! {
|
||||||
gen impl rustc_errors::AddToDiagnostic for @Self {
|
gen impl rustc_errors::AddToDiagnostic for @Self {
|
||||||
fn add_to_diagnostic(self, #diag: &mut rustc_errors::Diagnostic) {
|
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,
|
||||||
|
{
|
||||||
use rustc_errors::{Applicability, IntoDiagnosticArg};
|
use rustc_errors::{Applicability, IntoDiagnosticArg};
|
||||||
#implementation
|
#implementation
|
||||||
}
|
}
|
||||||
|
@ -88,15 +97,18 @@ impl<'a> SubdiagnosticDerive<'a> {
|
||||||
/// for the final generated method. This is a separate struct to `SubdiagnosticDerive`
|
/// for the final generated method. This is a separate struct to `SubdiagnosticDerive`
|
||||||
/// only to be able to destructure and split `self.builder` and the `self.structure` up to avoid a
|
/// only to be able to destructure and split `self.builder` and the `self.structure` up to avoid a
|
||||||
/// double mut borrow later on.
|
/// double mut borrow later on.
|
||||||
struct SubdiagnosticDeriveBuilder<'a> {
|
struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
||||||
/// The identifier to use for the generated `DiagnosticBuilder` instance.
|
/// The identifier to use for the generated `DiagnosticBuilder` instance.
|
||||||
diag: &'a syn::Ident,
|
parent: &'parent SubdiagnosticDeriveBuilder,
|
||||||
|
|
||||||
/// Info for the current variant (or the type if not an enum).
|
/// Info for the current variant (or the type if not an enum).
|
||||||
variant: &'a VariantInfo<'a>,
|
variant: &'a VariantInfo<'a>,
|
||||||
/// Span for the entire type.
|
/// Span for the entire type.
|
||||||
span: proc_macro::Span,
|
span: proc_macro::Span,
|
||||||
|
|
||||||
|
/// Initialization of format strings for code suggestions.
|
||||||
|
formatting_init: TokenStream,
|
||||||
|
|
||||||
/// Store a map of field name to its corresponding field. This is built on construction of the
|
/// Store a map of field name to its corresponding field. This is built on construction of the
|
||||||
/// derive builder.
|
/// derive builder.
|
||||||
fields: FieldMap,
|
fields: FieldMap,
|
||||||
|
@ -112,7 +124,7 @@ struct SubdiagnosticDeriveBuilder<'a> {
|
||||||
has_suggestion_parts: bool,
|
has_suggestion_parts: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> HasFieldMap for SubdiagnosticDeriveBuilder<'a> {
|
impl<'parent, 'a> HasFieldMap for SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
||||||
fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
|
fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
|
||||||
self.fields.get(field)
|
self.fields.get(field)
|
||||||
}
|
}
|
||||||
|
@ -156,7 +168,7 @@ impl<'a> FromIterator<&'a SubdiagnosticKind> for KindsStatistics {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SubdiagnosticDeriveBuilder<'a> {
|
impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
||||||
fn identify_kind(&mut self) -> Result<Vec<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> {
|
fn identify_kind(&mut self) -> Result<Vec<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> {
|
||||||
let mut kind_slugs = vec![];
|
let mut kind_slugs = vec![];
|
||||||
|
|
||||||
|
@ -187,7 +199,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
|
||||||
let ast = binding.ast();
|
let ast = binding.ast();
|
||||||
assert_eq!(ast.attrs.len(), 0, "field with attribute used as diagnostic arg");
|
assert_eq!(ast.attrs.len(), 0, "field with attribute used as diagnostic arg");
|
||||||
|
|
||||||
let diag = &self.diag;
|
let diag = &self.parent.diag;
|
||||||
let ident = ast.ident.as_ref().unwrap();
|
let ident = ast.ident.as_ref().unwrap();
|
||||||
// strip `r#` prefix, if present
|
// strip `r#` prefix, if present
|
||||||
let ident = format_ident!("{}", ident);
|
let ident = format_ident!("{}", ident);
|
||||||
|
@ -222,7 +234,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let generated = self
|
let generated = self
|
||||||
.generate_field_code_inner(kind_stats, attr, info)
|
.generate_field_code_inner(kind_stats, attr, info, inner_ty.will_iterate())
|
||||||
.unwrap_or_else(|v| v.to_compile_error());
|
.unwrap_or_else(|v| v.to_compile_error());
|
||||||
|
|
||||||
inner_ty.with(binding, generated)
|
inner_ty.with(binding, generated)
|
||||||
|
@ -235,13 +247,18 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
|
||||||
kind_stats: KindsStatistics,
|
kind_stats: KindsStatistics,
|
||||||
attr: &Attribute,
|
attr: &Attribute,
|
||||||
info: FieldInfo<'_>,
|
info: FieldInfo<'_>,
|
||||||
|
clone_suggestion_code: bool,
|
||||||
) -> Result<TokenStream, DiagnosticDeriveError> {
|
) -> Result<TokenStream, DiagnosticDeriveError> {
|
||||||
let meta = attr.parse_meta()?;
|
let meta = attr.parse_meta()?;
|
||||||
match meta {
|
match meta {
|
||||||
Meta::Path(path) => self.generate_field_code_inner_path(kind_stats, attr, info, path),
|
Meta::Path(path) => self.generate_field_code_inner_path(kind_stats, attr, info, path),
|
||||||
Meta::List(list @ MetaList { .. }) => {
|
Meta::List(list @ MetaList { .. }) => self.generate_field_code_inner_list(
|
||||||
self.generate_field_code_inner_list(kind_stats, attr, info, list)
|
kind_stats,
|
||||||
}
|
attr,
|
||||||
|
info,
|
||||||
|
list,
|
||||||
|
clone_suggestion_code,
|
||||||
|
),
|
||||||
_ => throw_invalid_attr!(attr, &meta),
|
_ => throw_invalid_attr!(attr, &meta),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,6 +362,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
|
||||||
attr: &Attribute,
|
attr: &Attribute,
|
||||||
info: FieldInfo<'_>,
|
info: FieldInfo<'_>,
|
||||||
list: MetaList,
|
list: MetaList,
|
||||||
|
clone_suggestion_code: bool,
|
||||||
) -> Result<TokenStream, DiagnosticDeriveError> {
|
) -> Result<TokenStream, DiagnosticDeriveError> {
|
||||||
let span = attr.span().unwrap();
|
let span = attr.span().unwrap();
|
||||||
let ident = &list.path.segments.last().unwrap().ident;
|
let ident = &list.path.segments.last().unwrap().ident;
|
||||||
|
@ -382,7 +400,8 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
|
||||||
match nested_name {
|
match nested_name {
|
||||||
"code" => {
|
"code" => {
|
||||||
let formatted_str = self.build_format(&value.value(), value.span());
|
let formatted_str = self.build_format(&value.value(), value.span());
|
||||||
code.set_once(formatted_str, span);
|
let code_field = new_code_ident();
|
||||||
|
code.set_once((code_field, formatted_str), span);
|
||||||
}
|
}
|
||||||
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
||||||
diag.help("`code` is the only valid nested attribute")
|
diag.help("`code` is the only valid nested attribute")
|
||||||
|
@ -390,14 +409,20 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some((code, _)) = code else {
|
let Some((code_field, formatted_str)) = code.value() else {
|
||||||
span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`")
|
span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`")
|
||||||
.emit();
|
.emit();
|
||||||
return Ok(quote! {});
|
return Ok(quote! {});
|
||||||
};
|
};
|
||||||
let binding = info.binding;
|
let binding = info.binding;
|
||||||
|
|
||||||
Ok(quote! { suggestions.push((#binding, #code)); })
|
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)); })
|
||||||
}
|
}
|
||||||
_ => throw_invalid_attr!(attr, &Meta::List(list), |diag| {
|
_ => throw_invalid_attr!(attr, &Meta::List(list), |diag| {
|
||||||
let mut span_attrs = vec![];
|
let mut span_attrs = vec![];
|
||||||
|
@ -442,13 +467,23 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
|
||||||
|
|
||||||
let span_field = self.span_field.value_ref();
|
let span_field = self.span_field.value_ref();
|
||||||
|
|
||||||
let diag = &self.diag;
|
let diag = &self.parent.diag;
|
||||||
|
let f = &self.parent.f;
|
||||||
let mut calls = TokenStream::new();
|
let mut calls = TokenStream::new();
|
||||||
for (kind, slug) in kind_slugs {
|
for (kind, slug) in kind_slugs {
|
||||||
|
let message = format_ident!("__message");
|
||||||
|
calls.extend(quote! { let #message = #f(#diag, rustc_errors::fluent::#slug.into()); });
|
||||||
|
|
||||||
let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
|
let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
|
||||||
let message = quote! { rustc_errors::fluent::#slug };
|
|
||||||
let call = match kind {
|
let call = match kind {
|
||||||
SubdiagnosticKind::Suggestion { suggestion_kind, applicability, code } => {
|
SubdiagnosticKind::Suggestion {
|
||||||
|
suggestion_kind,
|
||||||
|
applicability,
|
||||||
|
code_init,
|
||||||
|
code_field,
|
||||||
|
} => {
|
||||||
|
self.formatting_init.extend(code_init);
|
||||||
|
|
||||||
let applicability = applicability
|
let applicability = applicability
|
||||||
.value()
|
.value()
|
||||||
.map(|a| quote! { #a })
|
.map(|a| quote! { #a })
|
||||||
|
@ -457,8 +492,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
|
||||||
|
|
||||||
if let Some(span) = span_field {
|
if let Some(span) = span_field {
|
||||||
let style = suggestion_kind.to_suggestion_style();
|
let style = suggestion_kind.to_suggestion_style();
|
||||||
|
quote! { #diag.#name(#span, #message, #code_field, #applicability, #style); }
|
||||||
quote! { #diag.#name(#span, #message, #code, #applicability, #style); }
|
|
||||||
} else {
|
} else {
|
||||||
span_err(self.span, "suggestion without `#[primary_span]` field").emit();
|
span_err(self.span, "suggestion without `#[primary_span]` field").emit();
|
||||||
quote! { unreachable!(); }
|
quote! { unreachable!(); }
|
||||||
|
@ -499,6 +533,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
calls.extend(call);
|
calls.extend(call);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,11 +545,13 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
|
||||||
.map(|binding| self.generate_field_set_arg(binding))
|
.map(|binding| self.generate_field_set_arg(binding))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let formatting_init = &self.formatting_init;
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#init
|
#init
|
||||||
|
#formatting_init
|
||||||
#attr_args
|
#attr_args
|
||||||
#calls
|
|
||||||
#plain_args
|
#plain_args
|
||||||
|
#calls
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,16 +4,29 @@ use crate::diagnostics::error::{
|
||||||
use proc_macro::Span;
|
use proc_macro::Span;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote, ToTokens};
|
use quote::{format_ident, quote, ToTokens};
|
||||||
use std::cmp::Ordering;
|
use std::cell::RefCell;
|
||||||
use std::collections::{BTreeSet, HashMap};
|
use std::collections::{BTreeSet, HashMap};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use syn::{spanned::Spanned, Attribute, Field, Meta, Type, TypeTuple};
|
use syn::{spanned::Spanned, Attribute, Field, Meta, Type, TypeTuple};
|
||||||
use syn::{MetaList, MetaNameValue, NestedMeta, Path};
|
use syn::{MetaList, MetaNameValue, NestedMeta, Path};
|
||||||
use synstructure::{BindStyle, BindingInfo, VariantInfo};
|
use synstructure::{BindingInfo, VariantInfo};
|
||||||
|
|
||||||
use super::error::invalid_nested_attr;
|
use super::error::invalid_nested_attr;
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
pub static CODE_IDENT_COUNT: RefCell<u32> = RefCell::new(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an ident of the form `__code_N` where `N` is incremented once with every call.
|
||||||
|
pub(crate) fn new_code_ident() -> syn::Ident {
|
||||||
|
CODE_IDENT_COUNT.with(|count| {
|
||||||
|
let ident = format_ident!("__code_{}", *count.borrow());
|
||||||
|
*count.borrow_mut() += 1;
|
||||||
|
ident
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks whether the type name of `ty` matches `name`.
|
/// Checks whether the type name of `ty` matches `name`.
|
||||||
///
|
///
|
||||||
/// Given some struct at `a::b::c::Foo`, this will return true for `c::Foo`, `b::c::Foo`, or
|
/// Given some struct at `a::b::c::Foo`, this will return true for `c::Foo`, `b::c::Foo`, or
|
||||||
|
@ -142,6 +155,15 @@ impl<'ty> FieldInnerTy<'ty> {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if `FieldInnerTy::with` will result in iteration for this inner type (i.e.
|
||||||
|
/// that cloning might be required for values moved in the loop body).
|
||||||
|
pub(crate) fn will_iterate(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
FieldInnerTy::Vec(..) => true,
|
||||||
|
FieldInnerTy::Option(..) | FieldInnerTy::None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `Option` containing inner type if there is one.
|
/// Returns `Option` containing inner type if there is one.
|
||||||
pub(crate) fn inner_type(&self) -> Option<&'ty Type> {
|
pub(crate) fn inner_type(&self) -> Option<&'ty Type> {
|
||||||
match self {
|
match self {
|
||||||
|
@ -434,7 +456,12 @@ pub(super) enum SubdiagnosticKind {
|
||||||
Suggestion {
|
Suggestion {
|
||||||
suggestion_kind: SuggestionKind,
|
suggestion_kind: SuggestionKind,
|
||||||
applicability: SpannedOption<Applicability>,
|
applicability: SpannedOption<Applicability>,
|
||||||
code: TokenStream,
|
/// Identifier for variable used for formatted code, e.g. `___code_0`. Enables separation
|
||||||
|
/// of formatting and diagnostic emission so that `set_arg` calls can happen in-between..
|
||||||
|
code_field: syn::Ident,
|
||||||
|
/// Initialization logic for `code_field`'s variable, e.g.
|
||||||
|
/// `let __formatted_code = /* whatever */;`
|
||||||
|
code_init: TokenStream,
|
||||||
},
|
},
|
||||||
/// `#[multipart_suggestion{,_short,_hidden,_verbose}]`
|
/// `#[multipart_suggestion{,_short,_hidden,_verbose}]`
|
||||||
MultipartSuggestion {
|
MultipartSuggestion {
|
||||||
|
@ -469,7 +496,8 @@ impl SubdiagnosticKind {
|
||||||
SubdiagnosticKind::Suggestion {
|
SubdiagnosticKind::Suggestion {
|
||||||
suggestion_kind,
|
suggestion_kind,
|
||||||
applicability: None,
|
applicability: None,
|
||||||
code: TokenStream::new(),
|
code_field: new_code_ident(),
|
||||||
|
code_init: TokenStream::new(),
|
||||||
}
|
}
|
||||||
} else if let Some(suggestion_kind) =
|
} else if let Some(suggestion_kind) =
|
||||||
name.strip_prefix("multipart_suggestion").and_then(|s| s.parse().ok())
|
name.strip_prefix("multipart_suggestion").and_then(|s| s.parse().ok())
|
||||||
|
@ -548,9 +576,10 @@ impl SubdiagnosticKind {
|
||||||
};
|
};
|
||||||
|
|
||||||
match (nested_name, &mut kind) {
|
match (nested_name, &mut kind) {
|
||||||
("code", SubdiagnosticKind::Suggestion { .. }) => {
|
("code", SubdiagnosticKind::Suggestion { code_field, .. }) => {
|
||||||
let formatted_str = fields.build_format(&value.value(), value.span());
|
let formatted_str = fields.build_format(&value.value(), value.span());
|
||||||
code.set_once(formatted_str, span);
|
let code_init = quote! { let #code_field = #formatted_str; };
|
||||||
|
code.set_once(code_init, span);
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
"applicability",
|
"applicability",
|
||||||
|
@ -582,13 +611,13 @@ impl SubdiagnosticKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
match kind {
|
match kind {
|
||||||
SubdiagnosticKind::Suggestion { code: ref mut code_field, .. } => {
|
SubdiagnosticKind::Suggestion { ref code_field, ref mut code_init, .. } => {
|
||||||
*code_field = if let Some((code, _)) = code {
|
*code_init = if let Some(init) = code.value() {
|
||||||
code
|
init
|
||||||
} else {
|
} else {
|
||||||
span_err(span, "suggestion without `code = \"...\"`").emit();
|
span_err(span, "suggestion without `code = \"...\"`").emit();
|
||||||
quote! { "" }
|
quote! { let #code_field: String = unreachable!(); }
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
SubdiagnosticKind::Label
|
SubdiagnosticKind::Label
|
||||||
| SubdiagnosticKind::Note
|
| SubdiagnosticKind::Note
|
||||||
|
@ -620,65 +649,8 @@ impl quote::IdentFragment for SubdiagnosticKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper around `synstructure::BindStyle` which implements `Ord`.
|
|
||||||
#[derive(PartialEq, Eq)]
|
|
||||||
pub(super) struct OrderedBindStyle(pub(super) BindStyle);
|
|
||||||
|
|
||||||
impl OrderedBindStyle {
|
|
||||||
/// Is `BindStyle::Move` or `BindStyle::MoveMut`?
|
|
||||||
pub(super) fn is_move(&self) -> bool {
|
|
||||||
matches!(self.0, BindStyle::Move | BindStyle::MoveMut)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for OrderedBindStyle {
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
match (self.is_move(), other.is_move()) {
|
|
||||||
// If both `self` and `other` are the same, then ordering is equal.
|
|
||||||
(true, true) | (false, false) => Ordering::Equal,
|
|
||||||
// If `self` is not a move then it should be considered less than `other` (so that
|
|
||||||
// references are sorted first).
|
|
||||||
(false, _) => Ordering::Less,
|
|
||||||
// If `self` is a move then it must be greater than `other` (again, so that references
|
|
||||||
// are sorted first).
|
|
||||||
(true, _) => Ordering::Greater,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for OrderedBindStyle {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic
|
/// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic
|
||||||
/// call (like `span_label`).
|
/// call (like `span_label`).
|
||||||
pub(super) fn should_generate_set_arg(field: &Field) -> bool {
|
pub(super) fn should_generate_set_arg(field: &Field) -> bool {
|
||||||
field.attrs.is_empty()
|
field.attrs.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if `field` needs to have code generated in the by-move branch of the
|
|
||||||
/// generated derive rather than the by-ref branch.
|
|
||||||
pub(super) fn bind_style_of_field(field: &Field) -> OrderedBindStyle {
|
|
||||||
let generates_set_arg = should_generate_set_arg(field);
|
|
||||||
let is_multispan = type_matches_path(&field.ty, &["rustc_errors", "MultiSpan"]);
|
|
||||||
// FIXME(davidtwco): better support for one field needing to be in the by-move and
|
|
||||||
// by-ref branches.
|
|
||||||
let is_subdiagnostic = field
|
|
||||||
.attrs
|
|
||||||
.iter()
|
|
||||||
.map(|attr| attr.path.segments.last().unwrap().ident.to_string())
|
|
||||||
.any(|attr| attr == "subdiagnostic");
|
|
||||||
|
|
||||||
// `set_arg` calls take their argument by-move..
|
|
||||||
let needs_move = generates_set_arg
|
|
||||||
// If this is a `MultiSpan` field then it needs to be moved to be used by any
|
|
||||||
// attribute..
|
|
||||||
|| is_multispan
|
|
||||||
// If this a `#[subdiagnostic]` then it needs to be moved as the other diagnostic is
|
|
||||||
// unlikely to be `Copy`..
|
|
||||||
|| is_subdiagnostic;
|
|
||||||
|
|
||||||
OrderedBindStyle(if needs_move { BindStyle::Move } else { BindStyle::Ref })
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,19 +1,15 @@
|
||||||
use rustc_errors::AddToDiagnostic;
|
|
||||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||||
use rustc_session::Limit;
|
use rustc_session::Limit;
|
||||||
use rustc_span::{Span, Symbol};
|
use rustc_span::{Span, Symbol};
|
||||||
|
|
||||||
|
#[derive(Subdiagnostic)]
|
||||||
|
#[note(query_system::cycle_stack_middle)]
|
||||||
pub struct CycleStack {
|
pub struct CycleStack {
|
||||||
|
#[primary_span]
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub desc: String,
|
pub desc: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddToDiagnostic for CycleStack {
|
|
||||||
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
|
|
||||||
diag.span_note(self.span, &format!("...which requires {}...", self.desc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum HandleCycleError {
|
pub enum HandleCycleError {
|
||||||
Error,
|
Error,
|
||||||
|
@ -53,7 +49,7 @@ pub struct Cycle {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub stack_bottom: String,
|
pub stack_bottom: String,
|
||||||
#[subdiagnostic]
|
#[subdiagnostic(eager)]
|
||||||
pub cycle_stack: Vec<CycleStack>,
|
pub cycle_stack: Vec<CycleStack>,
|
||||||
#[subdiagnostic]
|
#[subdiagnostic]
|
||||||
pub stack_count: StackCount,
|
pub stack_count: StackCount,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#![feature(min_specialization)]
|
#![feature(min_specialization)]
|
||||||
#![feature(extern_types)]
|
#![feature(extern_types)]
|
||||||
#![allow(rustc::potential_query_instability)]
|
#![allow(rustc::potential_query_instability)]
|
||||||
// #![deny(rustc::untranslatable_diagnostic)]
|
#![deny(rustc::untranslatable_diagnostic)]
|
||||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
//! Validates syntax inside Rust code blocks (\`\`\`rust).
|
//! Validates syntax inside Rust code blocks (\`\`\`rust).
|
||||||
use rustc_data_structures::sync::{Lock, Lrc};
|
use rustc_data_structures::sync::{Lock, Lrc};
|
||||||
use rustc_errors::{
|
use rustc_errors::{
|
||||||
emitter::Emitter, translation::Translate, Applicability, Diagnostic, Handler,
|
emitter::Emitter,
|
||||||
LazyFallbackBundle,
|
translation::{to_fluent_args, Translate},
|
||||||
|
Applicability, Diagnostic, Handler, LazyFallbackBundle,
|
||||||
};
|
};
|
||||||
use rustc_parse::parse_stream_from_source_str;
|
use rustc_parse::parse_stream_from_source_str;
|
||||||
use rustc_session::parse::ParseSess;
|
use rustc_session::parse::ParseSess;
|
||||||
|
@ -193,7 +194,7 @@ impl Emitter for BufferEmitter {
|
||||||
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
|
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
|
||||||
let mut buffer = self.buffer.borrow_mut();
|
let mut buffer = self.buffer.borrow_mut();
|
||||||
|
|
||||||
let fluent_args = self.to_fluent_args(diag.args());
|
let fluent_args = to_fluent_args(diag.args());
|
||||||
let translated_main_message = self.translate_message(&diag.message[0].0, &fluent_args);
|
let translated_main_message = self.translate_message(&diag.message[0].0, &fluent_args);
|
||||||
|
|
||||||
buffer.messages.push(format!("error from rustc: {}", translated_main_message));
|
buffer.messages.push(format!("error from rustc: {}", translated_main_message));
|
||||||
|
|
|
@ -13,7 +13,7 @@ extern crate rustc_span;
|
||||||
|
|
||||||
use rustc_errors::{
|
use rustc_errors::{
|
||||||
AddToDiagnostic, IntoDiagnostic, Diagnostic, DiagnosticBuilder,
|
AddToDiagnostic, IntoDiagnostic, Diagnostic, DiagnosticBuilder,
|
||||||
ErrorGuaranteed, Handler, fluent
|
ErrorGuaranteed, Handler, fluent, SubdiagnosticMessage,
|
||||||
};
|
};
|
||||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
@ -52,7 +52,10 @@ impl<'a> IntoDiagnostic<'a, ErrorGuaranteed> for TranslatableInIntoDiagnostic {
|
||||||
pub struct UntranslatableInAddToDiagnostic;
|
pub struct UntranslatableInAddToDiagnostic;
|
||||||
|
|
||||||
impl AddToDiagnostic for UntranslatableInAddToDiagnostic {
|
impl AddToDiagnostic for UntranslatableInAddToDiagnostic {
|
||||||
fn add_to_diagnostic(self, diag: &mut Diagnostic) {
|
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
|
||||||
|
where
|
||||||
|
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
|
||||||
|
{
|
||||||
diag.note("untranslatable diagnostic");
|
diag.note("untranslatable diagnostic");
|
||||||
//~^ ERROR diagnostics should be created using translatable messages
|
//~^ ERROR diagnostics should be created using translatable messages
|
||||||
}
|
}
|
||||||
|
@ -61,7 +64,10 @@ impl AddToDiagnostic for UntranslatableInAddToDiagnostic {
|
||||||
pub struct TranslatableInAddToDiagnostic;
|
pub struct TranslatableInAddToDiagnostic;
|
||||||
|
|
||||||
impl AddToDiagnostic for TranslatableInAddToDiagnostic {
|
impl AddToDiagnostic for TranslatableInAddToDiagnostic {
|
||||||
fn add_to_diagnostic(self, diag: &mut Diagnostic) {
|
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
|
||||||
|
where
|
||||||
|
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
|
||||||
|
{
|
||||||
diag.note(fluent::compiletest::note);
|
diag.note(fluent::compiletest::note);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,13 @@ LL | #![deny(rustc::untranslatable_diagnostic)]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: diagnostics should be created using translatable messages
|
error: diagnostics should be created using translatable messages
|
||||||
--> $DIR/diagnostics.rs:56:14
|
--> $DIR/diagnostics.rs:59:14
|
||||||
|
|
|
|
||||||
LL | diag.note("untranslatable diagnostic");
|
LL | diag.note("untranslatable diagnostic");
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
||||||
error: diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls
|
error: diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls
|
||||||
--> $DIR/diagnostics.rs:70:25
|
--> $DIR/diagnostics.rs:76:25
|
||||||
|
|
|
|
||||||
LL | let _diag = handler.struct_err(fluent::compiletest::example);
|
LL | let _diag = handler.struct_err(fluent::compiletest::example);
|
||||||
| ^^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
|
@ -29,13 +29,13 @@ LL | #![deny(rustc::diagnostic_outside_of_impl)]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls
|
error: diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls
|
||||||
--> $DIR/diagnostics.rs:73:25
|
--> $DIR/diagnostics.rs:79:25
|
||||||
|
|
|
|
||||||
LL | let _diag = handler.struct_err("untranslatable diagnostic");
|
LL | let _diag = handler.struct_err("untranslatable diagnostic");
|
||||||
| ^^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
|
|
||||||
error: diagnostics should be created using translatable messages
|
error: diagnostics should be created using translatable messages
|
||||||
--> $DIR/diagnostics.rs:73:25
|
--> $DIR/diagnostics.rs:79:25
|
||||||
|
|
|
|
||||||
LL | let _diag = handler.struct_err("untranslatable diagnostic");
|
LL | let _diag = handler.struct_err("untranslatable diagnostic");
|
||||||
| ^^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
|
|
|
@ -678,3 +678,74 @@ enum ExampleEnum {
|
||||||
struct RawIdentDiagnosticArg {
|
struct RawIdentDiagnosticArg {
|
||||||
pub r#type: String,
|
pub r#type: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(compiletest::example)]
|
||||||
|
struct SubdiagnosticBad {
|
||||||
|
#[subdiagnostic(bad)]
|
||||||
|
//~^ ERROR `#[subdiagnostic(bad)]` is not a valid attribute
|
||||||
|
note: Note,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(compiletest::example)]
|
||||||
|
struct SubdiagnosticBadStr {
|
||||||
|
#[subdiagnostic = "bad"]
|
||||||
|
//~^ ERROR `#[subdiagnostic = ...]` is not a valid attribute
|
||||||
|
note: Note,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(compiletest::example)]
|
||||||
|
struct SubdiagnosticBadTwice {
|
||||||
|
#[subdiagnostic(bad, bad)]
|
||||||
|
//~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute
|
||||||
|
note: Note,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(compiletest::example)]
|
||||||
|
struct SubdiagnosticBadLitStr {
|
||||||
|
#[subdiagnostic("bad")]
|
||||||
|
//~^ ERROR `#[subdiagnostic("...")]` is not a valid attribute
|
||||||
|
note: Note,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(LintDiagnostic)]
|
||||||
|
#[diag(compiletest::example)]
|
||||||
|
struct SubdiagnosticEagerLint {
|
||||||
|
#[subdiagnostic(eager)]
|
||||||
|
//~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute
|
||||||
|
note: Note,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(compiletest::example)]
|
||||||
|
struct SubdiagnosticEagerCorrect {
|
||||||
|
#[subdiagnostic(eager)]
|
||||||
|
note: Note,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that formatting of `correct` in suggestion doesn't move the binding for that field, making
|
||||||
|
// the `set_arg` call a compile error; and that isn't worked around by moving the `set_arg` call
|
||||||
|
// after the `span_suggestion` call - which breaks eager translation.
|
||||||
|
|
||||||
|
#[derive(Subdiagnostic)]
|
||||||
|
#[suggestion_short(
|
||||||
|
parser::use_instead,
|
||||||
|
applicability = "machine-applicable",
|
||||||
|
code = "{correct}"
|
||||||
|
)]
|
||||||
|
pub(crate) struct SubdiagnosticWithSuggestion {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
invalid: String,
|
||||||
|
correct: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(compiletest::example)]
|
||||||
|
struct SubdiagnosticEagerSuggestion {
|
||||||
|
#[subdiagnostic(eager)]
|
||||||
|
sub: SubdiagnosticWithSuggestion,
|
||||||
|
}
|
||||||
|
|
|
@ -533,6 +533,46 @@ LL | #[label]
|
||||||
|
|
|
|
||||||
= help: `#[label]` and `#[suggestion]` can only be applied to fields
|
= help: `#[label]` and `#[suggestion]` can only be applied to fields
|
||||||
|
|
||||||
|
error: `#[subdiagnostic(bad)]` is not a valid attribute
|
||||||
|
--> $DIR/diagnostic-derive.rs:685:21
|
||||||
|
|
|
||||||
|
LL | #[subdiagnostic(bad)]
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
= help: `eager` is the only supported nested attribute for `subdiagnostic`
|
||||||
|
|
||||||
|
error: `#[subdiagnostic = ...]` is not a valid attribute
|
||||||
|
--> $DIR/diagnostic-derive.rs:693:5
|
||||||
|
|
|
||||||
|
LL | #[subdiagnostic = "bad"]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: `eager` is the only supported nested attribute for `subdiagnostic`
|
||||||
|
|
||||||
|
error: `#[subdiagnostic(...)]` is not a valid attribute
|
||||||
|
--> $DIR/diagnostic-derive.rs:701:5
|
||||||
|
|
|
||||||
|
LL | #[subdiagnostic(bad, bad)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: `eager` is the only supported nested attribute for `subdiagnostic`
|
||||||
|
|
||||||
|
error: `#[subdiagnostic("...")]` is not a valid attribute
|
||||||
|
--> $DIR/diagnostic-derive.rs:709:21
|
||||||
|
|
|
||||||
|
LL | #[subdiagnostic("bad")]
|
||||||
|
| ^^^^^
|
||||||
|
|
|
||||||
|
= help: `eager` is the only supported nested attribute for `subdiagnostic`
|
||||||
|
|
||||||
|
error: `#[subdiagnostic(...)]` is not a valid attribute
|
||||||
|
--> $DIR/diagnostic-derive.rs:717:5
|
||||||
|
|
|
||||||
|
LL | #[subdiagnostic(eager)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: eager subdiagnostics are not supported on lints
|
||||||
|
|
||||||
error: cannot find attribute `nonsense` in this scope
|
error: cannot find attribute `nonsense` in this scope
|
||||||
--> $DIR/diagnostic-derive.rs:55:3
|
--> $DIR/diagnostic-derive.rs:55:3
|
||||||
|
|
|
|
||||||
|
@ -607,7 +647,7 @@ LL | arg: impl IntoDiagnosticArg,
|
||||||
| ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg`
|
| ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg`
|
||||||
= note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: aborting due to 75 previous errors
|
error: aborting due to 80 previous errors
|
||||||
|
|
||||||
Some errors have detailed explanations: E0277, E0425.
|
Some errors have detailed explanations: E0277, E0425.
|
||||||
For more information about an error, try `rustc --explain E0277`.
|
For more information about an error, try `rustc --explain E0277`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue