Use workspace rand and rust-version in ruff_unicode_names2 (#29261)

Summary
--

This updates `ruff_unicode_names2` and `ruff_unicode_names2_generator`
to use our workspace `rand` dependency and `rust-version`, prompted by
#29257 and #29359. Bumping the Rust version also introduces a handful of
Clippy diagnostic that Clippy was also able to fix automatically. I
originally pinned both of these to stay closer to the upstream version,
but we've already diverged a bit, and the required changes were quite
small anyway, so this seems like the better way to go.

This also allows us to drop the `std` feature on `rand`, a transitive
dependency on `zerocopy`, and the accompanying allowlist entry for its
build script.

While I was here, I also switched to our workspace edition, removed
unused feature-gated code and unnecessary suppressions, enabled
formatting and typo checks, and enabled doctests.

These are the changes from `rand` for reference:

- https://rust-random.github.io/book/update-0.10.html
- https://rust-random.github.io/book/update-0.9.html

Test Plan
--

Existing tests

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
Brent WestbrookandAlex Waygood authored and GitHub committed 2026-10-14 15:56:00 +00:00
1 parent 523d83c941
commit bfedc614b7
12 files changed
+33 -135

No files matched your search

-1
View File
@@ -73,7 +73,6 @@ allow-build-scripts = [
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
"wit-bindgen",
"zerocopy",
"zmij",
"zstd-safe",
"zstd-sys",
Generated
+2 -46
View File
@@ -2634,15 +2634,6 @@ dependencies = [
"zerovec",
]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "predicates"
version = "3.1.4"
@@ -2893,8 +2884,6 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core 0.6.4",
]
@@ -2909,24 +2898,11 @@ dependencies = [
"rand_core 0.10.0",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core 0.6.4",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.16",
]
[[package]]
name = "rand_core"
@@ -3791,7 +3767,7 @@ name = "ruff_unicode_names2"
version = "0.0.17"
dependencies = [
"phf 0.11.3",
"rand 0.8.5",
"rand 0.10.2",
"ruff_unicode_names2_generator",
]
@@ -3800,7 +3776,7 @@ name = "ruff_unicode_names2_generator"
version = "0.0.17"
dependencies = [
"phf_codegen",
"rand 0.8.5",
"rand 0.10.2",
]
[[package]]
@@ -5893,26 +5869,6 @@ dependencies = [
"synstructure",
]
[[package]]
name = "zerocopy"
version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "zerofrom"
version = "0.1.6"
+2 -2
View File
@@ -2,8 +2,8 @@
# https://github.com/crate-ci/typos/issues/868
extend-exclude = [
"crates/ty_vendored/vendor/**/*",
"crates/ruff_unicode_names2/**/*",
"crates/ruff_unicode_names2_generator/**/*",
"crates/ruff_unicode_names2/data/NameAliases.txt",
"crates/ruff_unicode_names2/data/UnicodeData.txt",
"**/resources/**/*",
"**/snapshots/**/*",
"crates/ruff_linter/src/rules/flake8_implicit_str_concat/rules/collection_literal.rs",
+1 -11
View File
@@ -17,20 +17,10 @@ phf = { workspace = true }
ruff_unicode_names2_generator = { workspace = true }
[dev-dependencies]
rand = { version = "0.8.5", default-features = false, features = ["std", "std_rng"] }
[lints.rust]
# Ruff uses std; retain upstream's optional no_std code without enabling it.
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(feature, values("no_std"))'] }
[lints.clippy]
# Preserve the vendored source.
unnecessary_map_or = "allow"
question_mark = "allow"
rand = { workspace = true }
[lib]
name = "unicode_names2"
# Upstream unit tests, benchmarks, and macro doctests require nightly Rust.
test = false
doctest = false
bench = false
+1 -1
View File
@@ -12,7 +12,7 @@ use std::char;
fn main() {
let number = (0u32..0x10FFFF)
.filter(|x| char::from_u32(*x).map_or(false, |c| unicode_names2::name(c).is_some()))
.filter(|x| char::from_u32(*x).is_some_and(|c| unicode_names2::name(c).is_some()))
.count();
println!("there are {} named code points", number)
-2
View File
@@ -1,2 +0,0 @@
# Preserve the vendored source formatting.
disable_all_formatting = true
+6 -29
View File
@@ -1,9 +1,7 @@
//! Convert between characters and their standard names.
//!
//! This crate provides two functions for mapping from a `char` to the
//! name given by the Unicode standard (17.0). There are no runtime
//! requirements so this is usable with only `core` (this requires
//! specifying the `no_std` cargo feature). The tables are heavily
//! name given by the Unicode standard (17.0). The tables are heavily
//! compressed, but still large (500KB), and still offer efficient
//! `O(1)` look-ups in both directions (more precisely, `O(length of
//! name)`).
@@ -47,37 +45,24 @@
//! }
//! ```
#![cfg_attr(feature = "no_std", no_std)]
#![cfg_attr(test, feature(test))]
#![deny(missing_docs, unsafe_code)]
#[cfg(all(test, feature = "no_std"))]
#[macro_use]
extern crate std;
use core::{char, fmt};
use generated::{
MAX_NAME_LENGTH, PHRASEBOOK_OFFSETS1, PHRASEBOOK_OFFSETS2, PHRASEBOOK_OFFSET_SHIFT,
MAX_NAME_LENGTH, PHRASEBOOK_OFFSET_SHIFT, PHRASEBOOK_OFFSETS1, PHRASEBOOK_OFFSETS2,
};
#[allow(dead_code)]
#[rustfmt::skip]
#[allow(clippy::all)]
mod generated {
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
}
#[allow(dead_code)]
#[rustfmt::skip]
#[allow(clippy::all)]
mod generated_phf {
include!(concat!(env!("OUT_DIR"), "/generated_phf.rs"));
}
#[allow(dead_code)]
mod jamo;
/// A map of unicode aliases to their corresponding values.
/// Generated in generator
#[allow(dead_code)]
static ALIASES: phf::Map<&'static [u8], char> =
include!(concat!(env!("OUT_DIR"), "/generated_alias.rs"));
@@ -363,10 +348,7 @@ pub fn character(search_name: &str) -> Option<char> {
_ => return None,
}
}
let ch = match char::from_u32(v) {
Some(ch) => ch,
None => return None,
};
let ch = char::from_u32(v)?;
// check if the resulting code is indeed in the known ranges
if is_cjk_unified_ideograph(ch) {
@@ -422,7 +404,7 @@ pub fn character(search_name: &str) -> Option<char> {
mod tests {
use super::*;
use rand::{
distributions::{Distribution, Standard},
distr::{Distribution, StandardUniform},
prelude::{SeedableRng, StdRng},
};
use std::char;
@@ -616,7 +598,7 @@ mod tests {
fn name_10000_invalid(b: &mut Bencher) {
// be consistent across runs, but avoid sequential/caching.
let mut rng = StdRng::seed_from_u64(0x12345678);
let chars: Vec<char> = Standard
let chars: Vec<char> = StandardUniform
.sample_iter(&mut rng)
.take(10000)
.filter_map(|c| match c {
@@ -655,7 +637,7 @@ mod tests {
// be consistent across runs, but avoid sequential/caching.
let mut rng = StdRng::seed_from_u64(0x12345678);
let names: Vec<_> = Standard
let names: Vec<_> = StandardUniform
.sample_iter(&mut rng)
.take(10000)
.filter_map(name)
@@ -669,8 +651,3 @@ mod tests {
})
}
}
#[cfg(all(feature = "no_std", not(test)))]
mod std {
pub use core::{clone, fmt, marker};
}
@@ -3,8 +3,8 @@ name = "ruff_unicode_names2_generator"
version = "0.0.17"
description = "This is an internal component crate of Ruff"
authors = { workspace = true }
edition = "2018"
rust-version = "1.65.0"
edition = { workspace = true }
rust-version = { workspace = true }
homepage = { workspace = true }
documentation = { workspace = true }
repository = { workspace = true }
@@ -12,14 +12,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
phf_codegen = { workspace = true }
# Preserve the RNG used by upstream's table generator.
rand = { version = "0.8.5", default-features = false, features = ["std", "std_rng"] }
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(feature, values("timing"))'] }
[lints.rustdoc]
bare_urls = "allow"
rand = { workspace = true }
[lib]
name = "unicode_names2_generator"
@@ -1,2 +0,0 @@
# Preserve the vendored source formatting.
disable_all_formatting = true
@@ -11,7 +11,7 @@ impl Context {
where
F: Fn(&T) -> String,
{
w!(self, "pub static {}: &'static [{}] = &[", name, ty);
w!(self, "pub static {}: &[{}] = &[", name, ty);
let mut width = LINE_LIMIT;
for e in elements.iter() {
@@ -41,7 +41,7 @@ impl Context {
pub fn write_plain_string(&mut self, name: &str, data: &str) {
assert!(!data.contains('\\'));
w!(self, "pub static {}: &'static str = \"", name);
w!(self, "pub static {}: &str = \"", name);
for chunk in data.as_bytes().chunks(LINE_LIMIT - 6) {
w!(self, "\\\n ");
@@ -1,10 +1,9 @@
use formatting::Context;
use std::{
char, cmp,
collections::{hash_map, HashMap},
collections::{HashMap, hash_map},
fs::{self, File},
io::{self, prelude::*, BufWriter},
iter::repeat,
io::{self, BufWriter, prelude::*},
path::Path,
};
@@ -156,7 +155,7 @@ fn create_lexicon_and_offsets(
// insert the suffixes of this word which saves about
// 10KB (we could theoretically insert all substrings,
// upto a certain length, but this only saves ~300
// bytes or so and is noticably slower).
// bytes or so and is noticeably slower).
for i in 1..n.len() {
if t.insert(n[i..].bytes(), Some(offset + i), true).0 {
// once we've found a string that's already
@@ -238,7 +237,7 @@ fn bin_data(dat: &[u32]) -> (Vec<u32>, Vec<u32>, usize) {
fn write_codepoint_maps(ctxt: &mut Context, codepoint_names: Vec<(char, &str)>) {
let (lexicon_string, mut lexicon_words) = create_lexicon_and_offsets(codepoint_names.clone());
let num_escapes = (lexicon_words.len() + 255) / 256;
let num_escapes = lexicon_words.len().div_ceil(256);
// we reserve the high bit (end of word) and 127,126... for
// non-space splits. The high bit saves about 10KB, and doing the
@@ -293,9 +292,11 @@ fn write_codepoint_maps(ctxt: &mut Context, codepoint_names: Vec<(char, &str)>)
previous_len = len;
}
assert!(word_encodings
assert!(
word_encodings
.insert(word, vec![hi as u32, lo as u32])
.is_none());
.is_none()
);
}
// don't forget the last one.
lexicon_ordered_lengths.push((lexicon_offsets.len(), previous_len));
@@ -306,7 +307,7 @@ fn write_codepoint_maps(ctxt: &mut Context, codepoint_names: Vec<(char, &str)>)
// this is a map from `char` -> the index in phrasebook. it is
// currently huge, but it has a lot of 0's, so we compress it
// using the binning, below.
let mut phrasebook_offsets = repeat(0).take(0x10FFFF + 1).collect::<Vec<_>>();
let mut phrasebook_offsets: Vec<_> = std::iter::repeat_n(0, 0x10FFFF + 1).collect();
let mut longest_name = 0;
for &(cp, name) in codepoint_names.iter() {
longest_name = cmp::max(name.len(), longest_name);
@@ -1,10 +1,9 @@
//! Computes a perfect hash table using [the CHD
//! algorithm](http://cmph.sourceforge.net/papers/esa09.pdf).
//!
//! Strongly inspired by https://github.com/sfackler/rust-phf
//! Strongly inspired by <https://github.com/sfackler/rust-phf>
use rand::prelude::{Rng, SeedableRng, SliceRandom, StdRng};
use std::iter::repeat;
use rand::prelude::{RngExt, SeedableRng, SliceRandom, StdRng};
static NOVAL: char = '\0';
@@ -51,7 +50,7 @@ fn try_phf_table(
.collect();
let table_len = hashes.len();
let buckets_len = (table_len + lambda - 1) / lambda;
let buckets_len = table_len.div_ceil(lambda);
// group the elements into buckets of lambda (on average, for a
// good hash) based on the suffix of their hash.
@@ -67,8 +66,8 @@ fn try_phf_table(
// value for `foo` is "just" `map[displace(hash(foo))]`, where
// `displace` uses the pair of displacements that we computed
// (stored in `disps`).
let mut map = repeat(NOVAL).take(table_len).collect::<Vec<_>>();
let mut disps = repeat((0, 0)).take(buckets_len).collect::<Vec<_>>();
let mut map: Vec<_> = std::iter::repeat_n(NOVAL, table_len).collect();
let mut disps: Vec<_> = std::iter::repeat_n((0, 0), buckets_len).collect();
// the set of index -> value mappings for the next bucket to be
// placed; we need it separate because it may not work, so we may
@@ -80,7 +79,7 @@ fn try_phf_table(
// having to clear the whole the whole array each time (just
// compare against the generation). A u64 won't overflow.
let mut generation = 0;
let mut try_map = repeat(0u64).take(table_len).collect::<Vec<_>>();
let mut try_map: Vec<_> = std::iter::repeat_n(0u64, table_len).collect();
// the placed (index, codepoint) pairs of the current bucket, to
// be placed into the main map if the whole bucket fits.
let mut values_to_add = vec![];
@@ -145,25 +144,12 @@ pub fn create_phf(
max_tries: usize,
) -> (u64, Vec<(u32, u32)>, Vec<char>) {
let mut rng = StdRng::seed_from_u64(0xf0f0f0f0);
#[cfg(feature = "timing")]
let start = time::Instant::now();
for i in 0..(max_tries) {
#[cfg(feature = "timing")]
let my_start = time::Instant::now();
#[cfg(feature = "timing")]
println!("PHF #{}: starting {:.2}", i, my_start - start);
#[cfg(not(feature = "timing"))]
println!("PHF #{}", i);
let seed = rng.gen();
let seed = rng.random();
if let Some((disp, map)) = try_phf_table(data, lambda, seed, &mut rng) {
#[cfg(feature = "timing")]
println!(
"PHF took: total {:.2} s, successive {:.2} s",
start.elapsed(),
my_start.elapsed()
);
return (seed, disp, map);
}
}