Skip to main content

rustc_errors/
translation.rs

1use std::borrow::Cow;
2use std::env;
3use std::error::Report;
4use std::sync::Arc;
5
6pub use rustc_error_messages::{FluentArgs, LazyFallbackBundle};
7use rustc_error_messages::{langid, register_functions};
8use tracing::{debug, trace};
9
10use crate::error::{TranslateError, TranslateErrorKind};
11use crate::fluent_bundle::FluentResource;
12use crate::{DiagArg, DiagMessage, FluentBundle, Style, fluent_bundle};
13
14/// Convert diagnostic arguments (a rustc internal type that exists to implement
15/// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation.
16///
17/// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then
18/// passed around as a reference thereafter.
19pub fn to_fluent_args<'iter>(iter: impl Iterator<Item = DiagArg<'iter>>) -> FluentArgs<'static> {
20    let mut args = if let Some(size) = iter.size_hint().1 {
21        FluentArgs::with_capacity(size)
22    } else {
23        FluentArgs::new()
24    };
25
26    for (k, v) in iter {
27        args.set(k.clone(), v.clone());
28    }
29
30    args
31}
32
33#[derive(#[automatically_derived]
impl ::core::clone::Clone for Translator {
    #[inline]
    fn clone(&self) -> Translator {
        Translator {
            fluent_bundle: ::core::clone::Clone::clone(&self.fluent_bundle),
            fallback_fluent_bundle: ::core::clone::Clone::clone(&self.fallback_fluent_bundle),
        }
    }
}Clone)]
34pub struct Translator {
35    /// Localized diagnostics for the locale requested by the user. If no language was requested by
36    /// the user then this will be `None` and `fallback_fluent_bundle` should be used.
37    pub fluent_bundle: Option<Arc<FluentBundle>>,
38    /// Return `FluentBundle` with localized diagnostics for the default locale of the compiler.
39    /// Used when the user has not requested a specific language or when a localized diagnostic is
40    /// unavailable for the requested locale.
41    pub fallback_fluent_bundle: LazyFallbackBundle,
42}
43
44impl Translator {
45    pub fn with_fallback_bundle(
46        resources: Vec<&'static str>,
47        with_directionality_markers: bool,
48    ) -> Translator {
49        Translator {
50            fluent_bundle: None,
51            fallback_fluent_bundle: crate::fallback_fluent_bundle(
52                resources,
53                with_directionality_markers,
54            ),
55        }
56    }
57
58    /// Convert `DiagMessage`s to a string, performing translation if necessary.
59    pub fn translate_messages(
60        &self,
61        messages: &[(DiagMessage, Style)],
62        args: &FluentArgs<'_>,
63    ) -> Cow<'_, str> {
64        Cow::Owned(
65            messages
66                .iter()
67                .map(|(m, _)| self.translate_message(m, args).map_err(Report::new).unwrap())
68                .collect::<String>(),
69        )
70    }
71
72    /// Convert a `DiagMessage` to a string, performing translation if necessary.
73    pub fn translate_message<'a>(
74        &'a self,
75        message: &'a DiagMessage,
76        args: &'a FluentArgs<'_>,
77    ) -> Result<Cow<'a, str>, TranslateError<'a>> {
78        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_errors/src/translation.rs:78",
                        "rustc_errors::translation", ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_errors/src/translation.rs"),
                        ::tracing_core::__macro_support::Option::Some(78u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_errors::translation"),
                        ::tracing_core::field::FieldSet::new(&["message", "args"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&message) as
                                            &dyn Value)),
                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&args) as
                                            &dyn Value))])
            });
    } else { ; }
};trace!(?message, ?args);
79        let (identifier, attr) = match message {
80            DiagMessage::Str(msg) => {
81                return Ok(Cow::Borrowed(msg));
82            }
83            DiagMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
84            // This translates an inline fluent diagnostic message
85            // It does this by creating a new `FluentBundle` with only one message,
86            // and then translating using this bundle.
87            DiagMessage::Inline(msg) => {
88                const GENERATED_MSG_ID: &str = "generated_msg";
89                let resource =
90                    FluentResource::try_new(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0} = {1}\n", GENERATED_MSG_ID,
                msg))
    })format!("{GENERATED_MSG_ID} = {msg}\n")).unwrap();
91                let mut bundle = fluent_bundle::FluentBundle::new(<[_]>::into_vec(::alloc::boxed::box_new([{
                    #[allow(dead_code)]
                    enum ProcMacroHack { Value = ("\"en-US\"", 0).1, }
                    macro_rules! proc_macro_call {
                        () =>
                        {
                            unsafe
                            {
                                $crate :: LanguageIdentifier ::
                                from_raw_parts_unchecked(unsafe
                                {
                                    $crate :: subtags :: Language ::
                                    from_raw_unchecked(28261u64)
                                }, None,
                                Some(unsafe
                                {
                                    $crate :: subtags :: Region :: from_raw_unchecked(21333u32)
                                }), None)
                            }
                        }
                    }
                    unsafe {
                        ::unic_langid_macros::LanguageIdentifier::from_raw_parts_unchecked(unsafe
                                {
                                ::unic_langid_macros::subtags::Language::from_raw_unchecked(28261u64)
                            }, None,
                            Some(unsafe {
                                    ::unic_langid_macros::subtags::Region::from_raw_unchecked(21333u32)
                                }), None)
                    }
                }]))vec![langid!("en-US")]);
92                bundle.set_use_isolating(false);
93                bundle.add_resource(resource).unwrap();
94                register_functions(&mut bundle);
95                let message = bundle.get_message(GENERATED_MSG_ID).unwrap();
96                let value = message.value().unwrap();
97
98                let mut errs = ::alloc::vec::Vec::new()vec![];
99                let translated = bundle.format_pattern(value, Some(args), &mut errs).to_string();
100                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_errors/src/translation.rs:100",
                        "rustc_errors::translation", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_errors/src/translation.rs"),
                        ::tracing_core::__macro_support::Option::Some(100u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_errors::translation"),
                        ::tracing_core::field::FieldSet::new(&["translated",
                                        "errs"], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&translated)
                                            as &dyn Value)),
                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&errs) as
                                            &dyn Value))])
            });
    } else { ; }
};debug!(?translated, ?errs);
101                return if errs.is_empty() {
102                    Ok(Cow::Owned(translated))
103                } else {
104                    Err(TranslateError::fluent(&Cow::Borrowed(GENERATED_MSG_ID), args, errs))
105                };
106            }
107        };
108        let translate_with_bundle =
109            |bundle: &'a FluentBundle| -> Result<Cow<'_, str>, TranslateError<'_>> {
110                let message = bundle
111                    .get_message(identifier)
112                    .ok_or(TranslateError::message(identifier, args))?;
113                let value = match attr {
114                    Some(attr) => message
115                        .get_attribute(attr)
116                        .ok_or(TranslateError::attribute(identifier, args, attr))?
117                        .value(),
118                    None => message.value().ok_or(TranslateError::value(identifier, args))?,
119                };
120                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_errors/src/translation.rs:120",
                        "rustc_errors::translation", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_errors/src/translation.rs"),
                        ::tracing_core::__macro_support::Option::Some(120u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_errors::translation"),
                        ::tracing_core::field::FieldSet::new(&["message", "value"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&message) as
                                            &dyn Value)),
                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&value) as
                                            &dyn Value))])
            });
    } else { ; }
};debug!(?message, ?value);
121
122                let mut errs = ::alloc::vec::Vec::new()vec![];
123                let translated = bundle.format_pattern(value, Some(args), &mut errs);
124                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_errors/src/translation.rs:124",
                        "rustc_errors::translation", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_errors/src/translation.rs"),
                        ::tracing_core::__macro_support::Option::Some(124u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_errors::translation"),
                        ::tracing_core::field::FieldSet::new(&["translated",
                                        "errs"], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&translated)
                                            as &dyn Value)),
                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&errs) as
                                            &dyn Value))])
            });
    } else { ; }
};debug!(?translated, ?errs);
125                if errs.is_empty() {
126                    Ok(translated)
127                } else {
128                    Err(TranslateError::fluent(identifier, args, errs))
129                }
130            };
131
132        try {
133            match self.fluent_bundle.as_ref().map(|b| translate_with_bundle(b)) {
134                // The primary bundle was present and translation succeeded
135                Some(Ok(t)) => t,
136
137                // If `translate_with_bundle` returns `Err` with the primary bundle, this is likely
138                // just that the primary bundle doesn't contain the message being translated, so
139                // proceed to the fallback bundle.
140                Some(Err(
141                    primary @ TranslateError::One {
142                        kind: TranslateErrorKind::MessageMissing, ..
143                    },
144                )) => translate_with_bundle(&self.fallback_fluent_bundle)
145                    .map_err(|fallback| primary.and(fallback))?,
146
147                // Always yeet out for errors on debug (unless
148                // `RUSTC_TRANSLATION_NO_DEBUG_ASSERT` is set in the environment - this allows
149                // local runs of the test suites, of builds with debug assertions, to test the
150                // behaviour in a normal build).
151                Some(Err(primary))
152                    if truecfg!(debug_assertions)
153                        && env::var("RUSTC_TRANSLATION_NO_DEBUG_ASSERT").is_err() =>
154                {
155                    do yeet primary
156                }
157
158                // ..otherwise, for end users, an error about this wouldn't be useful or actionable, so
159                // just hide it and try with the fallback bundle.
160                Some(Err(primary)) => translate_with_bundle(&self.fallback_fluent_bundle)
161                    .map_err(|fallback| primary.and(fallback))?,
162
163                // The primary bundle is missing, proceed to the fallback bundle
164                None => translate_with_bundle(&self.fallback_fluent_bundle)
165                    .map_err(|fallback| TranslateError::primary(identifier, args).and(fallback))?,
166            }
167        }
168    }
169}
170
171/// This macro creates a translatable `DiagMessage` from a literal string.
172/// It should be used in places where a translatable message is needed, but struct diagnostics are undesired.
173///
174/// This is a macro because in the future we may want to globally register these messages.
175#[macro_export]
176macro_rules! inline_fluent {
177    ($inline: literal) => {
178        rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed($inline))
179    };
180}