sync: once_cell::sync::Lazy -> std::sync::LazyLock

Rust 1.80 stabilized `std::sync::LazyLock`, which replaces
`once_cell::sync::Lazy`. There is one exception in
`src/env_dispatch.rs`, which still uses the `once_cell` variant, since
the code there relies on `Lazy::get`, which also exists for `LazyLock`,
but will only be stabilized in Rust 1.94, so we can't use it yet.

Part of #12289
This commit is contained in:
Daniel Rainer
2026-01-06 19:48:04 +01:00
committed by Johannes Altmanninger
parent 99109278a6
commit 80e1942980
24 changed files with 66 additions and 78 deletions

2
Cargo.lock generated
View File

@@ -221,7 +221,6 @@ dependencies = [
"fish-wchar",
"fish-widecharwidth",
"libc",
"once_cell",
"rsconf",
"widestring",
]
@@ -231,7 +230,6 @@ name = "fish-gettext"
version = "0.0.0"
dependencies = [
"fish-gettext-maps",
"once_cell",
"phf 0.13.1",
]

View File

@@ -11,7 +11,6 @@ fish-common.workspace = true
fish-wchar.workspace = true
fish-widecharwidth.workspace = true
libc.workspace = true
once_cell.workspace = true
widestring.workspace = true
[build-dependencies]

View File

@@ -5,9 +5,11 @@
use fish_wchar::prelude::*;
use fish_widecharwidth::{WcLookupTable, WcWidth};
use once_cell::sync::Lazy;
use std::cmp;
use std::sync::atomic::{AtomicIsize, Ordering};
use std::sync::{
LazyLock,
atomic::{AtomicIsize, Ordering},
};
/// Width of ambiguous East Asian characters and, as of TR11, all private-use characters.
/// 1 is the typical default, but we accept any non-negative override via `$fish_ambiguous_width`.
@@ -25,7 +27,7 @@
// For some reason, this is declared here and exposed here, but is set in `env_dispatch`.
pub static FISH_EMOJI_WIDTH: AtomicIsize = AtomicIsize::new(1);
static WC_LOOKUP_TABLE: Lazy<WcLookupTable> = Lazy::new(WcLookupTable::new);
static WC_LOOKUP_TABLE: LazyLock<WcLookupTable> = LazyLock::new(WcLookupTable::new);
/// A safe wrapper around the system `wcwidth()` function
#[cfg(not(cygwin))]

View File

@@ -8,7 +8,6 @@ license.workspace = true
[dependencies]
fish-gettext-maps.workspace = true
once_cell.workspace = true
phf.workspace = true
[lints]

View File

@@ -1,5 +1,4 @@
use fish_gettext_maps::CATALOGS;
use once_cell::sync::Lazy;
use std::{
collections::HashMap,
sync::{LazyLock, Mutex},
@@ -7,8 +6,8 @@
type Catalog = &'static phf::Map<&'static str, &'static str>;
static LANGUAGE_PRECEDENCE: Lazy<Mutex<Vec<(&'static str, Catalog)>>> =
Lazy::new(|| Mutex::new(vec![]));
static LANGUAGE_PRECEDENCE: LazyLock<Mutex<Vec<(&'static str, Catalog)>>> =
LazyLock::new(|| Mutex::new(vec![]));
pub fn gettext(message_str: &'static str) -> Option<&'static str> {
let language_precedence = LANGUAGE_PRECEDENCE.lock().unwrap();

View File

@@ -1,15 +1,14 @@
use std::{
collections::HashSet,
sync::{Mutex, MutexGuard},
sync::{LazyLock, Mutex, MutexGuard},
};
use crate::prelude::*;
use once_cell::sync::Lazy;
use crate::parse_constants::SourceRange;
use pcre2::utf32::Regex;
static ABBRS: Lazy<Mutex<AbbreviationSet>> = Lazy::new(|| Mutex::new(Default::default()));
static ABBRS: LazyLock<Mutex<AbbreviationSet>> = LazyLock::new(|| Mutex::new(Default::default()));
pub fn with_abbrs<R>(cb: impl FnOnce(&AbbreviationSet) -> R) -> R {
let abbrs_g = ABBRS.lock().unwrap();

View File

@@ -2,13 +2,12 @@
use crate::util::get_seeded_rng;
use crate::wutil;
use once_cell::sync::Lazy;
use rand::rngs::SmallRng;
use rand::{Rng, RngCore};
use std::sync::Mutex;
use std::sync::{LazyLock, Mutex};
static RNG: Lazy<Mutex<SmallRng>> =
Lazy::new(|| Mutex::new(get_seeded_rng(rand::rng().next_u64())));
static RNG: LazyLock<Mutex<SmallRng>> =
LazyLock::new(|| Mutex::new(get_seeded_rng(rand::rng().next_u64())));
pub fn random(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> BuiltinResult {
let cmd = argv[0];

View File

@@ -10,9 +10,9 @@ mod test_expressions {
Error, Options, file_id_for_path, fish_wcswidth, lwstat, waccess, wcstod::wcstod,
wcstoi_opts, wstat,
};
use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::os::unix::prelude::*;
use std::sync::LazyLock;
#[derive(Copy, Clone, PartialEq, Eq)]
pub(super) enum Token {
@@ -182,7 +182,7 @@ fn token_for_string(str: &wstr) -> Token {
TOKEN_INFOS.get(str).copied().unwrap_or(Token::Unknown)
}
static TOKEN_INFOS: Lazy<HashMap<&'static wstr, Token>> = Lazy::new(|| {
static TOKEN_INFOS: LazyLock<HashMap<&'static wstr, Token>> = LazyLock::new(|| {
let pairs = [
(L!(""), Token::Unknown),
(L!("!"), Token::UnaryBoolean(UnaryBooleanToken::Bang)),

View File

@@ -1,9 +1,8 @@
use std::cmp::Ordering;
use std::{cmp::Ordering, sync::LazyLock};
use libc::{RLIM_INFINITY, c_uint, rlim_t};
use nix::errno::Errno;
use nix::sys::resource::Resource as ResourceEnum;
use once_cell::sync::Lazy;
use crate::wutil::perror;
use fish_fallback::{fish_wcswidth, wcscasecmp};
@@ -434,7 +433,7 @@ fn new(
}
/// Array of resource_t structs, describing all known resource types.
static RESOURCE_ARR: Lazy<Box<[Resource]>> = Lazy::new(|| {
static RESOURCE_ARR: LazyLock<Box<[Resource]>> = LazyLock::new(|| {
let resources_info = [
(
limits::SBSIZE,

View File

@@ -4,7 +4,7 @@
mem,
ops::{Deref, DerefMut},
sync::{
Mutex, MutexGuard,
LazyLock, Mutex, MutexGuard,
atomic::{self, AtomicUsize},
},
time::{Duration, Instant},
@@ -55,7 +55,6 @@
};
use bitflags::bitflags;
use fish_wchar::WExt;
use once_cell::sync::Lazy;
// Completion description strings, mostly for different types of files, such as sockets, block
// devices, etc.
@@ -451,7 +450,7 @@ struct CompletionEntryIndex {
/// Completion "wrapper" support. The map goes from wrapping-command to wrapped-command-list.
type WrapperMap = HashMap<WString, Vec<WString>>;
static WRAPPER_MAP: Lazy<Mutex<WrapperMap>> = Lazy::new(|| Mutex::new(HashMap::new()));
static WRAPPER_MAP: LazyLock<Mutex<WrapperMap>> = LazyLock::new(|| Mutex::new(HashMap::new()));
/// Clear the [`CompleteFlags::AUTO_SPACE`] flag, and set [`CompleteFlags::NO_SPACE`] appropriately
/// depending on the suffix of the string.
@@ -606,8 +605,8 @@ struct Completer<'ctx> {
condition_cache: HashMap<WString, bool>,
}
static COMPLETION_AUTOLOADER: Lazy<Mutex<Autoload>> =
Lazy::new(|| Mutex::new(Autoload::new(L!("fish_complete_path"))));
static COMPLETION_AUTOLOADER: LazyLock<Mutex<Autoload>> =
LazyLock::new(|| Mutex::new(Autoload::new(L!("fish_complete_path"))));
impl<'ctx> Completer<'ctx> {
pub fn new(ctx: &'ctx OperationContext<'ctx>, flags: CompletionRequestOptions) -> Self {

View File

@@ -28,13 +28,13 @@
use crate::wutil::{fish_wcstol, wgetcwd};
use libc::{c_int, uid_t};
use once_cell::sync::{Lazy, OnceCell};
use once_cell::sync::OnceCell;
use std::collections::HashMap;
use std::ffi::CStr;
use std::mem::MaybeUninit;
use std::os::unix::prelude::*;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::{Arc, LazyLock};
/// Set when a universal variable has been modified but not yet been written to disk via sync().
static UVARS_LOCALLY_MODIFIED: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
@@ -591,7 +591,7 @@ fn setup_user(global_exported_mode: EnvSetMode, vars: &EnvStack) {
}
}
pub(crate) static FALLBACK_PATH: Lazy<&[WString]> = Lazy::new(|| {
pub(crate) static FALLBACK_PATH: LazyLock<&[WString]> = LazyLock::new(|| {
// _CS_PATH: colon-separated paths to find POSIX utilities. Same as USER_CS_PATH.
let cs_path = libc::_CS_PATH;

View File

@@ -15,13 +15,13 @@
use crate::threads::{is_forked_child, is_main_thread};
use crate::wutil::fish_wcstol_radix;
use once_cell::sync::Lazy;
use std::cell::{RefCell, UnsafeCell};
use std::collections::HashSet;
use std::ffi::CString;
use std::marker::PhantomData;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::sync::LazyLock;
#[cfg(not(target_has_atomic = "64"))]
use portable_atomic::AtomicU64;
@@ -294,7 +294,7 @@ fn next(&mut self) -> Option<EnvNodeRef> {
}
}
static GLOBAL_NODE: Lazy<EnvNodeRef> = Lazy::new(|| EnvNodeRef::new(false, None));
static GLOBAL_NODE: LazyLock<EnvNodeRef> = LazyLock::new(|| EnvNodeRef::new(false, None));
/// Recursive helper to snapshot a series of nodes.
fn copy_node_chain(node: &EnvNodeRef) -> EnvNodeRef {

View File

@@ -231,6 +231,7 @@ pub fn env_dispatch_var_change(milieu: VarChangeMilieu, key: &wstr, vars: &EnvSt
let suppress_repaint = milieu.is_repainting || !milieu.global_or_universal;
// We want to ignore variable changes until the dispatch table is explicitly initialized.
// TODO(MSRV>=1.94): Use std::sync::LazyLock. (LazyLock::get is stabilized in Rust 1.94)
if let Some(dispatch_table) = Lazy::get(&VAR_DISPATCH_TABLE) {
dispatch_table.dispatch(key, vars, suppress_repaint);
}

View File

@@ -14,10 +14,9 @@
use crate::parser_keywords::parser_keywords_is_reserved;
use crate::prelude::*;
use crate::wutil::dir_iter::DirIter;
use once_cell::sync::Lazy;
use std::collections::{HashMap, HashSet};
use std::num::NonZeroU32;
use std::sync::{Arc, Mutex};
use std::sync::{Arc, LazyLock, Mutex};
#[derive(Clone)]
pub struct FunctionProperties {
@@ -100,7 +99,7 @@ fn allow_autoload(&self, name: &wstr) -> bool {
}
/// The big set of all functions.
static FUNCTION_SET: Lazy<Mutex<FunctionSet>> = Lazy::new(|| {
static FUNCTION_SET: LazyLock<Mutex<FunctionSet>> = LazyLock::new(|| {
Mutex::new(FunctionSet {
funcs: HashMap::new(),
autoload_tombstones: HashSet::new(),

View File

@@ -10,10 +10,9 @@
use crate::prelude::*;
use crate::reader::{Reader, reader_reset_interrupted};
use crate::threads::assert_is_main_thread;
use once_cell::sync::Lazy;
use std::mem;
use std::sync::{
Mutex, MutexGuard,
LazyLock, Mutex, MutexGuard,
atomic::{AtomicU32, Ordering},
};
@@ -228,8 +227,8 @@ pub struct InputMappingSet {
/// Access the singleton input mapping set.
pub fn input_mappings() -> MutexGuard<'static, InputMappingSet> {
static INPUT_MAPPINGS: Lazy<Mutex<InputMappingSet>> =
Lazy::new(|| Mutex::new(InputMappingSet::default()));
static INPUT_MAPPINGS: LazyLock<Mutex<InputMappingSet>> =
LazyLock::new(|| Mutex::new(InputMappingSet::default()));
INPUT_MAPPINGS.lock().unwrap()
}

View File

@@ -18,11 +18,10 @@
use libc::{EAGAIN, EINTR, ENOENT, ENOTDIR, EPIPE, EWOULDBLOCK, STDOUT_FILENO};
use nix::fcntl::OFlag;
use nix::sys::stat::Mode;
use once_cell::sync::Lazy;
use std::fs::File;
use std::io;
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd};
use std::sync::{Arc, Mutex, MutexGuard};
use std::sync::{Arc, LazyLock, Mutex, MutexGuard};
/// separated_buffer_t represents a buffer of output from commands, prepared to be turned into a
/// variable. For example, command substitutions output into one of these. Most commands just
@@ -935,7 +934,7 @@ pub fn is_stdin_closed(&self) -> bool {
const OPEN_MASK: Mode = Mode::from_bits_truncate(0o666);
/// Provide the fd monitor used for background fillthread operations.
static FD_MONITOR: Lazy<FdMonitor> = Lazy::new(FdMonitor::new);
static FD_MONITOR: LazyLock<FdMonitor> = LazyLock::new(FdMonitor::new);
pub fn fd_monitor() -> &'static FdMonitor {
&FD_MONITOR

View File

@@ -3,15 +3,14 @@
//! Works like the killring in emacs and readline. The killring is cut and paste with a memory of
//! previous cuts.
use once_cell::sync::Lazy;
use std::collections::VecDeque;
use std::sync::Mutex;
use std::sync::{LazyLock, Mutex};
use crate::prelude::*;
struct KillRing(VecDeque<WString>);
static KILL_RING: Lazy<Mutex<KillRing>> = Lazy::new(|| Mutex::new(KillRing::new()));
static KILL_RING: LazyLock<Mutex<KillRing>> = LazyLock::new(|| Mutex::new(KillRing::new()));
impl KillRing {
/// Create a new killring.

View File

@@ -1,6 +1,5 @@
use fish_wchar::{L, WString, wstr};
use once_cell::sync::Lazy;
use std::sync::Mutex;
use std::sync::{LazyLock, Mutex};
/// Use this function to localize a message.
/// The [`MaybeStatic`] wrapper type allows avoiding allocating and leaking a new [`wstr`] when no
@@ -17,8 +16,8 @@ fn gettext(message: MaybeStatic) -> &'static wstr {
MaybeStatic::Static(s) => s,
MaybeStatic::Local(s) => s,
};
static MESSAGE_TO_NARROW: Lazy<Mutex<HashMap<&'static wstr, NarrowMessage>>> =
Lazy::new(|| Mutex::new(HashMap::default()));
static MESSAGE_TO_NARROW: LazyLock<Mutex<HashMap<&'static wstr, NarrowMessage>>> =
LazyLock::new(|| Mutex::new(HashMap::default()));
let mut message_to_narrow = MESSAGE_TO_NARROW.lock().unwrap();
if !message_to_narrow.contains_key(message_wstr) {
let message_wstr: &'static wstr = match message {
@@ -38,8 +37,8 @@ fn gettext(message: MaybeStatic) -> &'static wstr {
#[cfg(feature = "localize-messages")]
{
if let Some(localized_str) = fish_gettext::gettext(message_str) {
static LOCALIZATION_TO_WIDE: Lazy<Mutex<HashMap<&'static str, &'static wstr>>> =
Lazy::new(|| Mutex::new(HashMap::default()));
static LOCALIZATION_TO_WIDE: LazyLock<Mutex<HashMap<&'static str, &'static wstr>>> =
LazyLock::new(|| Mutex::new(HashMap::default()));
let mut localizations_to_wide = LOCALIZATION_TO_WIDE.lock().unwrap();
if !localizations_to_wide.contains_key(localized_str) {
let localization_wstr =

View File

@@ -1,9 +1,8 @@
use super::{localizable_consts, localizable_string, wgettext, wgettext_fmt};
use crate::env::{EnvStack, Environment};
use fish_wchar::{L, WString, wstr};
use once_cell::sync::Lazy;
use std::collections::{HashMap, HashSet};
use std::sync::Mutex;
use std::sync::{LazyLock, Mutex};
#[derive(PartialEq, Eq, Clone, Copy)]
enum LanguagePrecedenceOrigin {
@@ -318,8 +317,8 @@ fn update_precedence<'a, 'b, 'c: 'a + 'b, LocalizationLanguage: Copy + 'a>(
///
/// This struct should be updated when the relevant variables change or `status language` is used
/// to modify the localization state.
static LOCALIZATION_STATE: Lazy<Mutex<LocalizationState>> =
Lazy::new(|| Mutex::new(LocalizationState::new()));
static LOCALIZATION_STATE: LazyLock<Mutex<LocalizationState>> =
LazyLock::new(|| Mutex::new(LocalizationState::new()));
/// Call this when one of `LANGUAGE`, `LC_ALL`, `LC_MESSAGES`, `LANG` changes.
/// Updates internal state such that the correct localizations will be used in subsequent

View File

@@ -10,11 +10,11 @@
use crate::wutil::{normalize_path, path_normalize_for_cd, waccess, wdirname, wstat};
use errno::{Errno, errno, set_errno};
use libc::{EACCES, ENOENT, ENOTDIR, F_OK, X_OK};
use once_cell::sync::Lazy;
use std::ffi::OsStr;
use std::io::ErrorKind;
use std::mem::MaybeUninit;
use std::os::unix::prelude::*;
use std::sync::LazyLock;
/// Returns the user configuration directory for fish. If the directory or one of its parents
/// doesn't exist, they are first created.
@@ -729,20 +729,20 @@ pub fn path_remoteness(path: &wstr) -> DirRemoteness {
}
fn get_data_directory() -> &'static BaseDirectory {
static DIR: Lazy<BaseDirectory> =
Lazy::new(|| make_base_directory(L!("XDG_DATA_HOME"), L!("/.local/share/fish")));
static DIR: LazyLock<BaseDirectory> =
LazyLock::new(|| make_base_directory(L!("XDG_DATA_HOME"), L!("/.local/share/fish")));
&DIR
}
fn get_cache_directory() -> &'static BaseDirectory {
static DIR: Lazy<BaseDirectory> =
Lazy::new(|| make_base_directory(L!("XDG_CACHE_HOME"), L!("/.cache/fish")));
static DIR: LazyLock<BaseDirectory> =
LazyLock::new(|| make_base_directory(L!("XDG_CACHE_HOME"), L!("/.cache/fish")));
&DIR
}
fn get_config_directory() -> &'static BaseDirectory {
static DIR: Lazy<BaseDirectory> =
Lazy::new(|| make_base_directory(L!("XDG_CONFIG_HOME"), L!("/.config/fish")));
static DIR: LazyLock<BaseDirectory> =
LazyLock::new(|| make_base_directory(L!("XDG_CONFIG_HOME"), L!("/.config/fish")));
&DIR
}

View File

@@ -28,7 +28,6 @@
SIGINT, SIGKILL, SIGPIPE, SIGQUIT, SIGSEGV, SIGSYS, SIGTTOU, STDOUT_FILENO, WCONTINUED,
WEXITSTATUS, WIFCONTINUED, WIFEXITED, WIFSIGNALED, WIFSTOPPED, WNOHANG, WTERMSIG, WUNTRACED,
};
use once_cell::sync::Lazy;
#[cfg(not(target_has_atomic = "64"))]
use portable_atomic::AtomicU64;
use std::cell::{Cell, Ref, RefCell, RefMut};
@@ -40,7 +39,7 @@
#[cfg(target_has_atomic = "64")]
use std::sync::atomic::AtomicU64;
use std::sync::atomic::{AtomicU8, Ordering};
use std::sync::{Arc, Mutex, OnceLock};
use std::sync::{Arc, LazyLock, Mutex, OnceLock};
/// Types of processes.
#[derive(Default)]
@@ -1615,8 +1614,8 @@ fn process_clean_after_marking(parser: &Parser, interactive: bool) -> bool {
pub fn have_proc_stat() -> bool {
// Check for /proc/self/stat to see if we are running with Linux-style procfs.
static HAVE_PROC_STAT_RESULT: Lazy<bool> =
Lazy::new(|| fs::metadata("/proc/self/stat").is_ok());
static HAVE_PROC_STAT_RESULT: LazyLock<bool> =
LazyLock::new(|| fs::metadata("/proc/self/stat").is_ok());
*HAVE_PROC_STAT_RESULT
}

View File

@@ -24,7 +24,6 @@
};
use nix::fcntl::OFlag;
use nix::sys::stat::Mode;
use once_cell::sync::Lazy;
#[cfg(not(target_has_atomic = "64"))]
use portable_atomic::AtomicU64;
use std::borrow::Cow;
@@ -43,7 +42,7 @@
#[cfg(target_has_atomic = "64")]
use std::sync::atomic::AtomicU64;
use std::sync::atomic::{AtomicI32, AtomicU8, AtomicU32, Ordering};
use std::sync::{Arc, Mutex, MutexGuard};
use std::sync::{Arc, LazyLock, Mutex, MutexGuard};
use std::time::{Duration, Instant};
use errno::{Errno, errno};
@@ -175,8 +174,8 @@ enum ExitState {
static EXIT_STATE: AtomicU8 = AtomicU8::new(ExitState::None as u8);
pub static SHELL_MODES: Lazy<Mutex<libc::termios>> =
Lazy::new(|| Mutex::new(unsafe { std::mem::zeroed() }));
pub static SHELL_MODES: LazyLock<Mutex<libc::termios>> =
LazyLock::new(|| Mutex::new(unsafe { std::mem::zeroed() }));
/// The valid terminal modes on startup.
/// Warning: this is read from the SIGTERM handler! Hence the raw global.
@@ -184,8 +183,8 @@ enum ExitState {
once_cell::sync::OnceCell::new();
/// Mode we use to execute programs.
static TTY_MODES_FOR_EXTERNAL_CMDS: Lazy<Mutex<libc::termios>> =
Lazy::new(|| Mutex::new(unsafe { std::mem::zeroed() }));
static TTY_MODES_FOR_EXTERNAL_CMDS: LazyLock<Mutex<libc::termios>> =
LazyLock::new(|| Mutex::new(unsafe { std::mem::zeroed() }));
static RUN_COUNT: AtomicU64 = AtomicU64::new(0);

View File

@@ -11,8 +11,10 @@
use crate::tty_handoff::{safe_deactivate_tty_protocols, safe_mark_tty_invalid};
use crate::wutil::{fish_wcstoi, perror};
use errno::{errno, set_errno};
use once_cell::sync::Lazy;
use std::sync::atomic::{AtomicI32, Ordering};
use std::sync::{
LazyLock,
atomic::{AtomicI32, Ordering},
};
/// Store the "main" pid. This allows us to reliably determine if we are in a forked child.
static MAIN_PID: AtomicI32 = AtomicI32::new(0);
@@ -278,7 +280,7 @@ pub fn signal_handle(sig: Signal) {
sigaction(sig, &act, std::ptr::null_mut());
}
pub static signals_to_default: Lazy<libc::sigset_t> = Lazy::new(|| {
pub static signals_to_default: LazyLock<libc::sigset_t> = LazyLock::new(|| {
let mut set = MaybeUninit::uninit();
unsafe { libc::sigemptyset(set.as_mut_ptr()) };
for data in SIGNAL_TABLE.iter() {

View File

@@ -2,10 +2,10 @@
use fish_common::char_offset;
use libc::X_OK;
use once_cell::sync::Lazy;
use std::cmp::Ordering;
use std::collections::HashSet;
use std::os::unix::fs::MetadataExt;
use std::sync::LazyLock;
use crate::common::{
UnescapeFlags, UnescapeStringStyle, WILDCARD_RESERVED_BASE, WSL,
@@ -375,12 +375,12 @@ fn wildcard_test_flags_then_complete(
// regular file *excludes* broken links - we have no use for them as commands.
let is_regular_file = entry.check_type().is_some_and(|x| x == DirEntryType::Reg);
let is_executable = Lazy::new(|| is_regular_file && waccess(filepath, X_OK) == 0);
let is_executable = LazyLock::new(|| is_regular_file && waccess(filepath, X_OK) == 0);
if executables_only && !*is_executable {
return false;
}
let filepath_stat = Lazy::new(|| lwstat(filepath));
let filepath_stat = LazyLock::new(|| lwstat(filepath));
// For executables on Cygwin, prefer the name without the .exe, to match
// better with Unix names, but only if there isn't also a file without that