From a775004322df90a564c58e702364c10e6f0192a4 Mon Sep 17 00:00:00 2001 From: Charles Lew Date: Sun, 6 Nov 2022 14:43:25 +0800 Subject: [PATCH] Migrate diagnostics list output to use icu list formatter. --- Cargo.lock | 176 +++++++++++++++++- compiler/rustc_baked_icu_data/Cargo.toml | 1 + compiler/rustc_baked_icu_data/src/lib.rs | 26 ++- compiler/rustc_error_messages/Cargo.toml | 5 + compiler/rustc_error_messages/src/lib.rs | 92 ++++++++- compiler/rustc_errors/src/diagnostic.rs | 3 + compiler/rustc_errors/src/diagnostic_impls.rs | 21 +-- 7 files changed, 296 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e1359b9e9e40..c9a6a40d04b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -211,7 +211,7 @@ checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" dependencies = [ "lazy_static", "memchr", - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -1707,6 +1707,73 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a" +[[package]] +name = "icu_list" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c40218275f081c4493f190357c5395647b06734c2dc3dcb41cc099a0f60168b1" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider", + "regex-automata 0.2.0", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b3de5d99a0e275fe6193b9586dbf37364daebc0d39c89b5cf8376a53b789e8" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_provider" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f911086e3c521a8a824d4f8bfd87769645ced2f07ff913b521c0d793be07100" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_adapters" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "980c71d8a91b246ebbb97847178a4b816eea39d1d550c70ee566384555bb6545" +dependencies = [ + "icu_locid", + "icu_provider", + "tinystr", + "yoke", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38cf6f5b65cf81f0b4298da647101acbfe6ae0e25263f92bd7a22597e9d6d606" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "idna" version = "0.2.0" @@ -2034,6 +2101,12 @@ dependencies = [ "walkdir", ] +[[package]] +name = "litemap" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f34a3f4798fac63fb48cf277eefa38f94d3443baff555bb98e4f56bc9092368e" + [[package]] name = "lld-wrapper" version = "0.1.0" @@ -2100,7 +2173,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -2927,6 +3000,15 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-automata" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9368763f5a9b804326f3af749e16f9abf378d227bcdee7634b13d8f17793782" +dependencies = [ + "memchr", +] + [[package]] name = "regex-syntax" version = "0.6.26" @@ -3202,6 +3284,16 @@ dependencies = [ "rustc_span", ] +[[package]] +name = "rustc_baked_icu_data" +version = "0.0.0" +dependencies = [ + "icu_list", + "icu_locid", + "icu_provider", + "litemap", +] + [[package]] name = "rustc_borrowck" version = "0.0.0" @@ -3422,13 +3514,18 @@ version = "0.0.0" dependencies = [ "fluent-bundle", "fluent-syntax", + "icu_list", + "icu_locid", + "icu_provider_adapters", "intl-memoizer", + "rustc_baked_icu_data", "rustc_data_structures", "rustc_macros", "rustc_serialize", "rustc_span", "tracing", "unic-langid", + "writeable", ] [[package]] @@ -4934,6 +5031,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8aeafdfd935e4a7fe16a91ab711fa52d54df84f9c8f7ca5837a9d1d902ef4c2" dependencies = [ "displaydoc", + "zerovec", ] [[package]] @@ -5546,6 +5644,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +[[package]] +name = "writeable" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8e6ab4f5da1b24daf2c590cfac801bacb27b15b4f050e84eb60149ea726f06b" + [[package]] name = "xattr" version = "0.2.2" @@ -5598,3 +5702,71 @@ checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1" dependencies = [ "winapi", ] + +[[package]] +name = "yoke" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fe1d55ca72c32d573bfbd5cb2f0ca65a497854c44762957a6d3da96041a5184" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1346e4cd025ae818b88566eac7eb65ab33a994ea55f355c86889af2e7e56b14e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e9355fccf72b04b7deaa99ce7a0f6630530acf34045391b74460fcd714de54" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e8aa86add9ddbd2409c1ed01e033cd457d79b1b1229b64922c25095c595e829" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d919a74c17749ccb17beaf6405562e413cd94e98ba52ca1e64bbe7eefbd8b8" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490e5f878c2856225e884c35927e7ea6db3c24cdb7229b72542c7526ad7ed49e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] diff --git a/compiler/rustc_baked_icu_data/Cargo.toml b/compiler/rustc_baked_icu_data/Cargo.toml index c4b49e939e91..6fe7ab8ea0da 100644 --- a/compiler/rustc_baked_icu_data/Cargo.toml +++ b/compiler/rustc_baked_icu_data/Cargo.toml @@ -5,5 +5,6 @@ edition = "2021" [dependencies] icu_list = "1.0.0" +icu_locid = "1.0.0" icu_provider = "1.0.1" litemap = "0.6.0" diff --git a/compiler/rustc_baked_icu_data/src/lib.rs b/compiler/rustc_baked_icu_data/src/lib.rs index 0eff969e14a3..76c9bc97346f 100644 --- a/compiler/rustc_baked_icu_data/src/lib.rs +++ b/compiler/rustc_baked_icu_data/src/lib.rs @@ -2,16 +2,30 @@ //! #![allow(elided_lifetimes_in_paths)] -// generated with: -// ```text -// icu4x-datagen -W --pretty --fingerprint --use-separate-crates --cldr-tag latest --icuexport-tag latest \ -// --format mod -l en es fr it ja pt ru tr zh-Hans zh-Hant -k list/and@1 -o src/data -// ``` +/* generated with: +```text +icu4x-datagen -W --pretty --fingerprint --use-separate-crates --cldr-tag latest --icuexport-tag latest \ +--format mod -l en es fr it ja pt ru tr zh-Hans zh-Hant -k list/and@1 -o src/data +``` +*/ mod data; pub use data::BakedDataProvider; -pub fn baked_data_provider() -> BakedDataProvider { +pub const fn baked_data_provider() -> BakedDataProvider { data::BakedDataProvider } + +pub mod supported_locales { + pub const EN: icu_locid::Locale = icu_locid::locale!("en"); + pub const ES: icu_locid::Locale = icu_locid::locale!("es"); + pub const FR: icu_locid::Locale = icu_locid::locale!("fr"); + pub const IT: icu_locid::Locale = icu_locid::locale!("it"); + pub const JA: icu_locid::Locale = icu_locid::locale!("ja"); + pub const PT: icu_locid::Locale = icu_locid::locale!("pt"); + pub const RU: icu_locid::Locale = icu_locid::locale!("ru"); + pub const TR: icu_locid::Locale = icu_locid::locale!("tr"); + pub const ZH_HANS: icu_locid::Locale = icu_locid::locale!("zh-Hans"); + pub const ZH_HANT: icu_locid::Locale = icu_locid::locale!("zh-Hant"); +} diff --git a/compiler/rustc_error_messages/Cargo.toml b/compiler/rustc_error_messages/Cargo.toml index 9945f337995d..bb6edfb09032 100644 --- a/compiler/rustc_error_messages/Cargo.toml +++ b/compiler/rustc_error_messages/Cargo.toml @@ -9,9 +9,14 @@ edition = "2021" fluent-bundle = "0.15.2" fluent-syntax = "0.11" intl-memoizer = "0.5.1" +rustc_baked_icu_data = { path = "../rustc_baked_icu_data" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } rustc_macros = { path = "../rustc_macros" } tracing = "0.1" unic-langid = { version = "0.9.0", features = ["macros"] } +icu_list = "1.0.0" +writeable = "0.5.0" +icu_locid = "1.0.0" +icu_provider_adapters = "1.0.0" diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 0b1b75471a66..d026f5c8daf3 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -10,6 +10,7 @@ extern crate tracing; use fluent_bundle::FluentResource; use fluent_syntax::parser::ParserError; +use icu_provider_adapters::fallback::{LocaleFallbackProvider, LocaleFallbacker}; use rustc_data_structures::sync::Lrc; use rustc_macros::{fluent_messages, Decodable, Encodable}; use rustc_span::Span; @@ -30,8 +31,7 @@ use intl_memoizer::concurrent::IntlLangMemoizer; #[cfg(not(parallel_compiler))] use intl_memoizer::IntlLangMemoizer; -pub use fluent_bundle::{self, FluentArgs, FluentError, FluentValue}; - +pub use fluent_bundle::{self, types::FluentType, FluentArgs, FluentError, FluentValue}; pub use unic_langid::{langid, LanguageIdentifier}; // Generates `DEFAULT_LOCALE_RESOURCES` static and `fluent_generated` module. @@ -541,3 +541,91 @@ impl From> for MultiSpan { MultiSpan::from_spans(spans) } } + +fn icu_locale_from_unic_langid(lang: LanguageIdentifier) -> Option { + icu_locid::Locale::try_from_bytes(lang.to_string().as_bytes()).ok() +} + +pub fn fluent_value_from_str_list_sep_by_and<'source>( + l: Vec>, +) -> FluentValue<'source> { + // Fluent requires 'static value here for its AnyEq usages. + #[derive(Clone, PartialEq, Debug)] + struct FluentStrListSepByAnd(Vec); + + impl FluentType for FluentStrListSepByAnd { + fn duplicate(&self) -> Box { + Box::new(self.clone()) + } + + fn as_string(&self, intls: &intl_memoizer::IntlLangMemoizer) -> Cow<'static, str> { + let result = intls + .with_try_get::((), |list_formatter| { + list_formatter.format_to_string(self.0.iter()) + }) + .unwrap(); + Cow::Owned(result) + } + + #[cfg(not(parallel_compiler))] + fn as_string_threadsafe( + &self, + _intls: &intl_memoizer::concurrent::IntlLangMemoizer, + ) -> Cow<'static, str> { + unreachable!("`as_string_threadsafe` is not used in non-parallel rustc") + } + + #[cfg(parallel_compiler)] + fn as_string_threadsafe( + &self, + intls: &intl_memoizer::concurrent::IntlLangMemoizer, + ) -> Cow<'static, str> { + let result = intls + .with_try_get::((), |list_formatter| { + list_formatter.format_to_string(self.0.iter()) + }) + .unwrap(); + Cow::Owned(result) + } + } + + struct MemoizableListFormatter(icu_list::ListFormatter); + + impl std::ops::Deref for MemoizableListFormatter { + type Target = icu_list::ListFormatter; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl intl_memoizer::Memoizable for MemoizableListFormatter { + type Args = (); + type Error = (); + + fn construct(lang: LanguageIdentifier, _args: Self::Args) -> Result + where + Self: Sized, + { + let baked_data_provider = rustc_baked_icu_data::baked_data_provider(); + let locale_fallbacker = LocaleFallbacker::try_new_unstable(&baked_data_provider); + let data_provider = LocaleFallbackProvider::new_with_fallbacker( + &baked_data_provider, + locale_fallbacker, + ); + let locale = icu_locale_from_unic_langid(lang) + .unwrap_or_else(|| rustc_baked_icu_data::supported_locales::EN); + let list_formatter = icu_list::ListFormatter::try_new_and_with_length_unstable( + &data_provider, + &locale.into(), + icu_list::ListLength::Wide, + ) + .expect("Failed to create list formatter"); + + Ok(MemoizableListFormatter(list_formatter)) + } + } + + let l = l.into_iter().map(|x| x.into_owned()).collect(); + + FluentValue::Custom(Box::new(FluentStrListSepByAnd(l))) +} diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 66c986977ecc..2c1d0037aa64 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -4,6 +4,7 @@ use crate::{ SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle, }; use rustc_data_structures::fx::FxHashMap; +use rustc_error_messages::fluent_value_from_str_list_sep_by_and; use rustc_error_messages::FluentValue; use rustc_lint_defs::{Applicability, LintExpectationId}; use rustc_span::edition::LATEST_STABLE_EDITION; @@ -34,6 +35,7 @@ pub type DiagnosticArgName<'source> = Cow<'source, str>; pub enum DiagnosticArgValue<'source> { Str(Cow<'source, str>), Number(usize), + StrListSepByAnd(Vec>), } /// Converts a value of a type into a `DiagnosticArg` (typically a field of an `IntoDiagnostic` @@ -58,6 +60,7 @@ impl<'source> Into> for DiagnosticArgValue<'source> { match self { DiagnosticArgValue::Str(s) => From::from(s), DiagnosticArgValue::Number(n) => From::from(n), + DiagnosticArgValue::StrListSepByAnd(l) => fluent_value_from_str_list_sep_by_and(l), } } } diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index c6035705e39f..6716339eaa48 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -11,7 +11,6 @@ use rustc_target::abi::TargetDataLayoutErrors; use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTriple}; use std::borrow::Cow; use std::fmt; -use std::fmt::Write; use std::num::ParseIntError; use std::path::{Path, PathBuf}; use std::process::ExitStatus; @@ -191,23 +190,9 @@ impl From> for DiagnosticSymbolList { impl IntoDiagnosticArg for DiagnosticSymbolList { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - // FIXME: replace the logic here with a real list formatter - let symbols = match &self.0[..] { - [symbol] => format!("`{symbol}`"), - [symbol, last] => { - format!("`{symbol}` and `{last}`",) - } - [symbols @ .., last] => { - let mut result = String::new(); - for symbol in symbols { - write!(result, "`{symbol}`, ").unwrap(); - } - write!(result, "and `{last}`").unwrap(); - result - } - [] => unreachable!(), - }; - DiagnosticArgValue::Str(Cow::Owned(symbols)) + DiagnosticArgValue::StrListSepByAnd( + self.0.into_iter().map(|sym| Cow::Owned(format!("`{sym}`"))).collect(), + ) } }