#![feature(let_chains)]
#![feature(once_cell)]
-#![feature(path_try_exists)]
+#![feature(rustc_attrs)]
#![feature(type_alias_impl_trait)]
use fluent_bundle::FluentResource;
use fluent_syntax::parser::ParserError;
use rustc_data_structures::sync::Lrc;
-use rustc_macros::{Decodable, Encodable};
+use rustc_macros::{fluent_messages, Decodable, Encodable};
use rustc_span::Span;
use std::borrow::Cow;
use std::error::Error;
use tracing::{instrument, trace};
#[cfg(not(parallel_compiler))]
-use std::lazy::Lazy;
+use std::cell::LazyCell as Lazy;
#[cfg(parallel_compiler)]
-use std::lazy::SyncLazy as Lazy;
+use std::sync::LazyLock as Lazy;
#[cfg(parallel_compiler)]
use intl_memoizer::concurrent::IntlLangMemoizer;
pub use fluent_bundle::{FluentArgs, FluentError, FluentValue};
pub use unic_langid::{langid, LanguageIdentifier};
-pub static DEFAULT_LOCALE_RESOURCES: &'static [&'static str] =
- &[include_str!("../locales/en-US/typeck.ftl"), include_str!("../locales/en-US/parser.ftl")];
+// Generates `DEFAULT_LOCALE_RESOURCES` static and `fluent_generated` module.
+fluent_messages! {
+ parser => "../locales/en-US/parser.ftl",
+ typeck => "../locales/en-US/typeck.ftl",
+ builtin_macros => "../locales/en-US/builtin_macros.ftl",
+}
+
+pub use fluent_generated::{self as fluent, DEFAULT_LOCALE_RESOURCES};
pub type FluentBundle = fluent_bundle::bundle::FluentBundle<FluentResource, IntlLangMemoizer>;
/// Identifier for the Fluent message/attribute corresponding to a diagnostic message.
type FluentId = Cow<'static, str>;
+/// Abstraction over a message in a subdiagnostic (i.e. label, note, help, etc) to support both
+/// translatable and non-translatable diagnostic messages.
+///
+/// Translatable messages for subdiagnostics are typically attributes attached to a larger Fluent
+/// message so messages of this type must be combined with a `DiagnosticMessage` (using
+/// `DiagnosticMessage::with_subdiagnostic_message`) before rendering. However, subdiagnostics from
+/// the `SessionSubdiagnostic` derive refer to Fluent identifiers directly.
+#[rustc_diagnostic_item = "SubdiagnosticMessage"]
+pub enum SubdiagnosticMessage {
+ /// Non-translatable diagnostic message.
+ // FIXME(davidtwco): can a `Cow<'static, str>` be used here?
+ Str(String),
+ /// Identifier of a Fluent message. Instances of this variant are generated by the
+ /// `SessionSubdiagnostic` derive.
+ FluentIdentifier(FluentId),
+ /// Attribute of a Fluent message. Needs to be combined with a Fluent identifier to produce an
+ /// actual translated message. Instances of this variant are generated by the `fluent_messages`
+ /// macro.
+ ///
+ /// <https://projectfluent.org/fluent/guide/attributes.html>
+ FluentAttr(FluentId),
+}
+
+impl SubdiagnosticMessage {
+ /// Create a `SubdiagnosticMessage` for the provided Fluent attribute.
+ pub fn attr(id: impl Into<FluentId>) -> Self {
+ SubdiagnosticMessage::FluentAttr(id.into())
+ }
+
+ /// Create a `SubdiagnosticMessage` for the provided Fluent identifier.
+ pub fn message(id: impl Into<FluentId>) -> Self {
+ SubdiagnosticMessage::FluentIdentifier(id.into())
+ }
+}
+
+/// `From` impl that enables existing diagnostic calls to functions which now take
+/// `impl Into<SubdiagnosticMessage>` to continue to work as before.
+impl<S: Into<String>> From<S> for SubdiagnosticMessage {
+ fn from(s: S) -> Self {
+ SubdiagnosticMessage::Str(s.into())
+ }
+}
+
/// Abstraction over a message in a diagnostic to support both translatable and non-translatable
/// diagnostic messages.
///
/// Intended to be removed once diagnostics are entirely translatable.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
+#[rustc_diagnostic_item = "DiagnosticMessage"]
pub enum DiagnosticMessage {
/// Non-translatable diagnostic message.
// FIXME(davidtwco): can a `Cow<'static, str>` be used here?
}
impl DiagnosticMessage {
+ /// Given a `SubdiagnosticMessage` which may contain a Fluent attribute, create a new
+ /// `DiagnosticMessage` that combines that attribute with the Fluent identifier of `self`.
+ ///
+ /// - If the `SubdiagnosticMessage` is non-translatable then return the message as a
+ /// `DiagnosticMessage`.
+ /// - If `self` is non-translatable then return `self`'s message.
+ pub fn with_subdiagnostic_message(&self, sub: SubdiagnosticMessage) -> Self {
+ let attr = match sub {
+ SubdiagnosticMessage::Str(s) => return DiagnosticMessage::Str(s.clone()),
+ SubdiagnosticMessage::FluentIdentifier(id) => {
+ return DiagnosticMessage::FluentIdentifier(id, None);
+ }
+ SubdiagnosticMessage::FluentAttr(attr) => attr,
+ };
+
+ match self {
+ DiagnosticMessage::Str(s) => DiagnosticMessage::Str(s.clone()),
+ DiagnosticMessage::FluentIdentifier(id, _) => {
+ DiagnosticMessage::FluentIdentifier(id.clone(), Some(attr))
+ }
+ }
+ }
+
/// Returns the `String` contained within the `DiagnosticMessage::Str` variant, assuming that
/// this diagnostic message is of the legacy, non-translatable variety. Panics if this
/// assumption does not hold.
}
/// Create a `DiagnosticMessage` for the provided Fluent identifier.
- pub fn fluent(id: impl Into<FluentId>) -> Self {
+ pub fn new(id: impl Into<FluentId>) -> Self {
DiagnosticMessage::FluentIdentifier(id.into(), None)
}
-
- /// Create a `DiagnosticMessage` for the provided Fluent identifier and attribute.
- pub fn fluent_attr(id: impl Into<FluentId>, attr: impl Into<FluentId>) -> Self {
- DiagnosticMessage::FluentIdentifier(id.into(), Some(attr.into()))
- }
}
/// `From` impl that enables existing diagnostic calls to functions which now take