diff --git a/Cargo.toml b/Cargo.toml index c7ac8d0596..d489c9d54e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -215,6 +215,8 @@ unexpected_cfgs = { level = "warn", check-cfg = [ [workspace.lints.clippy] pedantic = { level = "warn", priority = -2 } +# Enabled at the crate level +disallowed_methods = "allow" # Allowed pedantic lints char_lit_as_u8 = "allow" collapsible_else_if = "allow" @@ -253,6 +255,7 @@ unused_peekable = "warn" # Diagnostics are not actionable: Enable once https://github.com/rust-lang/rust-clippy/issues/13774 is resolved. large_stack_arrays = "allow" + [profile.release] # Note that we set these explicitly, and these values # were chosen based on a trade-off between compile times diff --git a/clippy.toml b/clippy.toml index 539d63305b..c11a535aad 100644 --- a/clippy.toml +++ b/clippy.toml @@ -24,3 +24,20 @@ ignore-interior-mutability = [ # The expression is read-only. "ruff_python_ast::hashable::HashableExpr", ] + +disallowed-methods = [ + { path = "std::env::var", reason = "Use System::env_var instead in ty crates" }, + { path = "std::env::current_dir", reason = "Use System::current_directory instead in ty crates" }, + { path = "std::fs::read_to_string", reason = "Use System::read_to_string instead in ty crates" }, + { path = "std::fs::metadata", reason = "Use System::path_metadata instead in ty crates" }, + { path = "std::fs::canonicalize", reason = "Use System::canonicalize_path instead in ty crates" }, + { path = "dunce::canonicalize", reason = "Use System::canonicalize_path instead in ty crates" }, + { path = "std::fs::read_dir", reason = "Use System::read_directory instead in ty crates" }, + { path = "std::fs::write", reason = "Use WritableSystem::write_file instead in ty crates" }, + { path = "std::fs::create_dir_all", reason = "Use WritableSystem::create_directory_all instead in ty crates" }, + { path = "std::fs::File::create_new", reason = "Use WritableSystem::create_new_file instead in ty crates" }, + # Path methods that have System trait equivalents + { path = "std::path::Path::exists", reason = "Use System::path_exists instead in ty crates" }, + { path = "std::path::Path::is_dir", reason = "Use System::is_directory instead in ty crates" }, + { path = "std::path::Path::is_file", reason = "Use System::is_file instead in ty crates" }, +] diff --git a/crates/ruff_db/src/lib.rs b/crates/ruff_db/src/lib.rs index 2a152ace72..efc5b2c974 100644 --- a/crates/ruff_db/src/lib.rs +++ b/crates/ruff_db/src/lib.rs @@ -1,3 +1,8 @@ +#![warn( + clippy::disallowed_methods, + reason = "Prefer System trait methods over std methods" +)] + use crate::files::Files; use crate::system::System; use crate::vendored::VendoredFileSystem; @@ -65,6 +70,10 @@ pub trait Db: salsa::Database { /// to process work in parallel. For example, to index a directory or checking the files of a project. /// ty can still spawn more threads for other tasks, e.g. to wait for a Ctrl+C signal or /// watching the files for changes. +#[expect( + clippy::disallowed_methods, + reason = "We don't have access to System here, but this is also only used by the CLI and the server which always run on a real system." +)] pub fn max_parallelism() -> NonZeroUsize { std::env::var(EnvVars::TY_MAX_PARALLELISM) .or_else(|_| std::env::var(EnvVars::RAYON_NUM_THREADS)) diff --git a/crates/ruff_db/src/system.rs b/crates/ruff_db/src/system.rs index e8b3062f9f..8448cb9acb 100644 --- a/crates/ruff_db/src/system.rs +++ b/crates/ruff_db/src/system.rs @@ -46,7 +46,7 @@ pub type Result = std::io::Result; /// * File watching isn't supported. /// /// Abstracting the system also enables tests to use a more efficient in-memory file system. -pub trait System: Debug { +pub trait System: Debug + Sync + Send { /// Reads the metadata of the file or directory at `path`. /// /// This function will traverse symbolic links to query information about the destination file. @@ -197,6 +197,8 @@ pub trait System: Debug { fn as_any(&self) -> &dyn std::any::Any; fn as_any_mut(&mut self) -> &mut dyn std::any::Any; + + fn dyn_clone(&self) -> Box; } #[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] diff --git a/crates/ruff_db/src/system/os.rs b/crates/ruff_db/src/system/os.rs index dd6a8eea99..b9f7e6e3db 100644 --- a/crates/ruff_db/src/system/os.rs +++ b/crates/ruff_db/src/system/os.rs @@ -1,3 +1,5 @@ +#![allow(clippy::disallowed_methods)] + use super::walk_directory::{ self, DirectoryWalker, WalkDirectoryBuilder, WalkDirectoryConfiguration, WalkDirectoryVisitorBuilder, WalkState, @@ -255,6 +257,10 @@ impl System for OsSystem { fn env_var(&self, name: &str) -> std::result::Result { std::env::var(name) } + + fn dyn_clone(&self) -> Box { + Box::new(self.clone()) + } } impl OsSystem { diff --git a/crates/ruff_db/src/system/test.rs b/crates/ruff_db/src/system/test.rs index f595aadca7..73a05ba3d5 100644 --- a/crates/ruff_db/src/system/test.rs +++ b/crates/ruff_db/src/system/test.rs @@ -146,6 +146,10 @@ impl System for TestSystem { fn case_sensitivity(&self) -> CaseSensitivity { self.system().case_sensitivity() } + + fn dyn_clone(&self) -> Box { + Box::new(self.clone()) + } } impl Default for TestSystem { @@ -394,6 +398,13 @@ impl System for InMemorySystem { fn case_sensitivity(&self) -> CaseSensitivity { CaseSensitivity::CaseSensitive } + + fn dyn_clone(&self) -> Box { + Box::new(Self { + user_config_directory: Mutex::new(self.user_config_directory.lock().unwrap().clone()), + memory_fs: self.memory_fs.clone(), + }) + } } impl WritableSystem for InMemorySystem { diff --git a/crates/ty_combine/src/lib.rs b/crates/ty_combine/src/lib.rs index ab8b7a013b..d2033cadd5 100644 --- a/crates/ty_combine/src/lib.rs +++ b/crates/ty_combine/src/lib.rs @@ -1,3 +1,8 @@ +#![warn( + clippy::disallowed_methods, + reason = "Prefer System trait methods over std methods in ty crates" +)] + use std::{collections::HashMap, hash::BuildHasher}; use ordermap::OrderMap; diff --git a/crates/ty_ide/src/lib.rs b/crates/ty_ide/src/lib.rs index 17c9cc5623..922c551ebd 100644 --- a/crates/ty_ide/src/lib.rs +++ b/crates/ty_ide/src/lib.rs @@ -1,3 +1,7 @@ +#![warn( + clippy::disallowed_methods, + reason = "Prefer System trait methods over std methods in ty crates" +)] mod completion; mod doc_highlights; mod docstring; diff --git a/crates/ty_project/src/lib.rs b/crates/ty_project/src/lib.rs index f9809541b4..53fd6ad3da 100644 --- a/crates/ty_project/src/lib.rs +++ b/crates/ty_project/src/lib.rs @@ -1,3 +1,7 @@ +#![warn( + clippy::disallowed_methods, + reason = "Prefer System trait methods over std methods in ty crates" +)] use crate::glob::{GlobFilterCheckMode, IncludeResult}; use crate::metadata::options::{OptionDiagnostic, ToSettingsError}; use crate::walk::{ProjectFilesFilter, ProjectFilesWalker}; diff --git a/crates/ty_project/src/watch/watcher.rs b/crates/ty_project/src/watch/watcher.rs index f3789a526b..83e10bbede 100644 --- a/crates/ty_project/src/watch/watcher.rs +++ b/crates/ty_project/src/watch/watcher.rs @@ -1,3 +1,8 @@ +#![allow( + clippy::disallowed_methods, + reason = "This implementation is specific to real file systems." +)] + use notify::event::{CreateKind, MetadataKind, ModifyKind, RemoveKind, RenameMode}; use notify::{EventKind, RecommendedWatcher, RecursiveMode, Watcher as _, recommended_watcher}; diff --git a/crates/ty_python_semantic/src/lib.rs b/crates/ty_python_semantic/src/lib.rs index afd61f44ca..cf58ab93a6 100644 --- a/crates/ty_python_semantic/src/lib.rs +++ b/crates/ty_python_semantic/src/lib.rs @@ -1,3 +1,7 @@ +#![warn( + clippy::disallowed_methods, + reason = "Prefer System trait methods over std methods in ty crates" +)] use std::hash::BuildHasherDefault; use rustc_hash::FxHasher; diff --git a/crates/ty_python_semantic/src/module_resolver/list.rs b/crates/ty_python_semantic/src/module_resolver/list.rs index 2374a24212..0d0f95140c 100644 --- a/crates/ty_python_semantic/src/module_resolver/list.rs +++ b/crates/ty_python_semantic/src/module_resolver/list.rs @@ -363,6 +363,11 @@ fn is_python_extension(ext: &str) -> bool { #[cfg(test)] mod tests { + #![expect( + clippy::disallowed_methods, + reason = "These are tests, so it's fine to do I/O by-passing System." + )] + use camino::{Utf8Component, Utf8Path}; use ruff_db::Db as _; use ruff_db::files::{File, FilePath, FileRootKind}; diff --git a/crates/ty_python_semantic/src/module_resolver/resolver.rs b/crates/ty_python_semantic/src/module_resolver/resolver.rs index 6779a76042..86e7c9fe59 100644 --- a/crates/ty_python_semantic/src/module_resolver/resolver.rs +++ b/crates/ty_python_semantic/src/module_resolver/resolver.rs @@ -1115,6 +1115,10 @@ impl fmt::Display for RelaxedModuleName { #[cfg(test)] mod tests { + #![expect( + clippy::disallowed_methods, + reason = "These are tests, so it's fine to do I/O by-passing System." + )] use ruff_db::Db; use ruff_db::files::{File, FilePath, system_path_to_file}; use ruff_db::system::{DbWithTestSystem as _, DbWithWritableSystem as _}; diff --git a/crates/ty_python_semantic/src/module_resolver/typeshed.rs b/crates/ty_python_semantic/src/module_resolver/typeshed.rs index 5b67ae3c77..17b85397b4 100644 --- a/crates/ty_python_semantic/src/module_resolver/typeshed.rs +++ b/crates/ty_python_semantic/src/module_resolver/typeshed.rs @@ -304,6 +304,11 @@ impl fmt::Display for PyVersionRange { #[cfg(test)] mod tests { + #![expect( + clippy::disallowed_methods, + reason = "These are tests, so it's fine to do I/O by-passing System." + )] + use std::fmt::Write as _; use std::num::{IntErrorKind, NonZeroU16}; use std::path::Path; diff --git a/crates/ty_python_semantic/src/site_packages.rs b/crates/ty_python_semantic/src/site_packages.rs index 1e94670365..f7f84c8d7a 100644 --- a/crates/ty_python_semantic/src/site_packages.rs +++ b/crates/ty_python_semantic/src/site_packages.rs @@ -211,7 +211,7 @@ impl PythonEnvironment { match VirtualEnvironment::new(path, system) { Ok(venv) => Ok(Self::Virtual(venv)), // If there's not a `pyvenv.cfg` marker, attempt to inspect as a system environment - Err(SitePackagesDiscoveryError::NoPyvenvCfgFile(path, _)) + Err(SitePackagesDiscoveryError::NoPyvenvCfgFile(path, _, _)) if !path.origin.must_be_virtual_env() => { Ok(Self::System(SystemEnvironment::new(path))) @@ -378,7 +378,13 @@ impl VirtualEnvironment { let pyvenv_cfg = match system.read_to_string(&pyvenv_cfg_path) { Ok(pyvenv_cfg) => pyvenv_cfg, - Err(err) => return Err(SitePackagesDiscoveryError::NoPyvenvCfgFile(path, err)), + Err(err) => { + return Err(SitePackagesDiscoveryError::NoPyvenvCfgFile( + path, + err, + system.dyn_clone(), + )); + } }; let parsed_pyvenv_cfg = @@ -833,16 +839,26 @@ impl SystemEnvironment { #[derive(Debug)] pub enum SitePackagesDiscoveryError { /// `site-packages` discovery failed because the provided path couldn't be canonicalized. - CanonicalizationError(SystemPathBuf, SysPrefixPathOrigin, io::Error), + CanonicalizationError( + SystemPathBuf, + SysPrefixPathOrigin, + io::Error, + Box, + ), /// `site-packages` discovery failed because the provided path doesn't appear to point to /// a Python executable or a `sys.prefix` directory. - PathNotExecutableOrDirectory(SystemPathBuf, SysPrefixPathOrigin, Option), + PathNotExecutableOrDirectory( + SystemPathBuf, + SysPrefixPathOrigin, + Option, + Box, + ), /// `site-packages` discovery failed because the [`SysPrefixPathOrigin`] indicated that /// the provided path should point to the `sys.prefix` of a virtual environment, /// but there was no file at `/pyvenv.cfg`. - NoPyvenvCfgFile(SysPrefixPath, io::Error), + NoPyvenvCfgFile(SysPrefixPath, io::Error, Box), /// `site-packages` discovery failed because the `pyvenv.cfg` file could not be parsed. PyvenvCfgParseError(SystemPathBuf, PyvenvCfgParseErrorKind), @@ -852,11 +868,11 @@ pub enum SitePackagesDiscoveryError { /// would be relative to the `sys.prefix` path, and we tried to fallback to iterating /// through the `/lib` directory looking for a `site-packages` directory, /// but we came across some I/O error while trying to do so. - CouldNotReadLibDirectory(SysPrefixPath), + CouldNotReadLibDirectory(SysPrefixPath, Box), /// We looked everywhere we could think of for the `site-packages` directory, /// but none could be found despite our best endeavours. - NoSitePackagesDirFound(SysPrefixPath), + NoSitePackagesDirFound(SysPrefixPath, Box), } /// Enumeration of ways in which stdlib discovery can fail. @@ -864,13 +880,13 @@ pub enum SitePackagesDiscoveryError { pub enum StdlibDiscoveryError { /// We looked everywhere we could think of for the standard library's directory, /// but none could be found despite our best endeavours. - NoStdlibFound(SysPrefixPath), + NoStdlibFound(SysPrefixPath, Box), /// Stdlib discovery failed because we're on a Unix system, /// we weren't able to figure out from the `pyvenv.cfg` file exactly where the stdlib /// would be relative to the `sys.prefix` path, and we tried to fallback to iterating /// through the `/lib` directory looking for a stdlib directory, /// but we came across some I/O error while trying to do so. - CouldNotReadLibDirectory(SysPrefixPath, io::Error), + CouldNotReadLibDirectory(SysPrefixPath, io::Error, Box), /// We failed to resolve the value of `sys.prefix`. NoSysPrefixFound(SystemPathBuf), } @@ -878,14 +894,14 @@ pub enum StdlibDiscoveryError { impl std::error::Error for SitePackagesDiscoveryError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { - Self::CanonicalizationError(_, _, io_err) => Some(io_err), - Self::PathNotExecutableOrDirectory(_, _, io_err) => { + Self::CanonicalizationError(_, _, io_err, _) => Some(io_err), + Self::PathNotExecutableOrDirectory(_, _, io_err, _) => { io_err.as_ref().map(|e| e as &dyn std::error::Error) } - Self::NoPyvenvCfgFile(_, io_err) => Some(io_err), + Self::NoPyvenvCfgFile(_, io_err, _) => Some(io_err), Self::PyvenvCfgParseError(_, _) - | Self::CouldNotReadLibDirectory(_) - | Self::NoSitePackagesDirFound(_) => None, + | Self::CouldNotReadLibDirectory(_, _) + | Self::NoSitePackagesDirFound(_, _) => None, } } } @@ -893,10 +909,15 @@ impl std::error::Error for SitePackagesDiscoveryError { impl std::fmt::Display for SitePackagesDiscoveryError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::CanonicalizationError(given_path, origin, _) => { - display_error(f, origin, given_path, "Failed to canonicalize", None) - } - Self::PathNotExecutableOrDirectory(path, origin, _) => { + Self::CanonicalizationError(given_path, origin, _, system) => display_error( + f, + origin, + given_path, + "Failed to canonicalize", + None, + &**system, + ), + Self::PathNotExecutableOrDirectory(path, origin, _, system) => { let thing = if origin.must_point_directly_to_sys_prefix() { "directory on disk" } else { @@ -908,14 +929,16 @@ impl std::fmt::Display for SitePackagesDiscoveryError { path, &format!("Invalid {origin}"), Some(&format!("does not point to a {thing}")), + &**system, ) } - Self::NoPyvenvCfgFile(SysPrefixPath { inner, origin }, _) => display_error( + Self::NoPyvenvCfgFile(SysPrefixPath { inner, origin }, _, system) => display_error( f, origin, inner, &format!("Invalid {origin}"), Some("points to a broken venv with no pyvenv.cfg file"), + &**system, ), Self::PyvenvCfgParseError(path, kind) => { write!( @@ -923,14 +946,17 @@ impl std::fmt::Display for SitePackagesDiscoveryError { "Failed to parse the `pyvenv.cfg` file at `{path}` because {kind}" ) } - Self::CouldNotReadLibDirectory(SysPrefixPath { inner, origin }) => display_error( - f, - origin, - inner, - "Failed to iterate over the contents of the `lib`/`lib64` directories of the Python installation", - None, - ), - Self::NoSitePackagesDirFound(SysPrefixPath { inner, origin }) => display_error( + Self::CouldNotReadLibDirectory(SysPrefixPath { inner, origin }, system) => { + display_error( + f, + origin, + inner, + "Failed to iterate over the contents of the `lib`/`lib64` directories of the Python installation", + None, + &**system, + ) + } + Self::NoSitePackagesDirFound(SysPrefixPath { inner, origin }, system) => display_error( f, origin, inner, @@ -938,6 +964,7 @@ impl std::fmt::Display for SitePackagesDiscoveryError { Some( "Could not find a `site-packages` directory for this Python installation/executable", ), + &**system, ), } } @@ -946,8 +973,8 @@ impl std::fmt::Display for SitePackagesDiscoveryError { impl std::error::Error for StdlibDiscoveryError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { - Self::CouldNotReadLibDirectory(_, io_err) => Some(io_err), - Self::NoStdlibFound(_) => None, + Self::CouldNotReadLibDirectory(_, io_err, _) => Some(io_err), + Self::NoStdlibFound(_, _) => None, Self::NoSysPrefixFound(_) => None, } } @@ -962,19 +989,23 @@ impl std::fmt::Display for StdlibDiscoveryError { "Failed to resolve a `sys.prefix` from the `pyvenv.cfg` file at `{path}`" ) } - Self::CouldNotReadLibDirectory(SysPrefixPath { inner, origin }, _) => display_error( - f, - origin, - inner, - "Failed to iterate over the contents of the `lib` directory of the Python installation", - None, - ), - Self::NoStdlibFound(SysPrefixPath { inner, origin }) => display_error( + Self::CouldNotReadLibDirectory(SysPrefixPath { inner, origin }, _, system) => { + display_error( + f, + origin, + inner, + "Failed to iterate over the contents of the `lib` directory of the Python installation", + None, + &**system, + ) + } + Self::NoStdlibFound(SysPrefixPath { inner, origin }, system) => display_error( f, origin, inner, &format!("Invalid {origin}"), Some("Could not find a stdlib directory for this Python installation/executable"), + &**system, ), } } @@ -986,6 +1017,7 @@ fn display_error( given_path: &SystemPath, primary_message: &str, secondary_message: Option<&str>, + system: &dyn System, ) -> std::fmt::Result { let fallback: &mut dyn FnMut() -> std::fmt::Result = &mut || { f.write_str(primary_message)?; @@ -1003,7 +1035,7 @@ fn display_error( return fallback(); }; - let Ok(config_file_source) = std::fs::read_to_string((**config_file_path).as_ref()) else { + let Ok(config_file_source) = system.read_to_string(config_file_path) else { return fallback(); }; @@ -1099,7 +1131,10 @@ fn site_packages_directories_from_sys_prefix( .is_directory(&site_packages) .then(|| SitePackagesPaths::from([site_packages])) .ok_or_else(|| { - SitePackagesDiscoveryError::NoSitePackagesDirFound(sys_prefix_path.to_owned()) + SitePackagesDiscoveryError::NoSitePackagesDirFound( + sys_prefix_path.to_owned(), + system.dyn_clone(), + ) }); } @@ -1206,10 +1241,12 @@ fn site_packages_directories_from_sys_prefix( if found_at_least_one_lib_dir { Err(SitePackagesDiscoveryError::NoSitePackagesDirFound( sys_prefix_path.to_owned(), + system.dyn_clone(), )) } else { Err(SitePackagesDiscoveryError::CouldNotReadLibDirectory( sys_prefix_path.to_owned(), + system.dyn_clone(), )) } } else { @@ -1238,7 +1275,7 @@ fn real_stdlib_directory_from_sys_prefix( if cfg!(target_os = "windows") { let stdlib = sys_prefix_path.join("Lib"); return system.is_directory(&stdlib).then_some(stdlib).ok_or( - StdlibDiscoveryError::NoStdlibFound(sys_prefix_path.to_owned()), + StdlibDiscoveryError::NoStdlibFound(sys_prefix_path.to_owned(), system.dyn_clone()), ); } @@ -1274,7 +1311,11 @@ fn real_stdlib_directory_from_sys_prefix( // must be `lib`, not `lib64`, for the stdlib .read_directory(&sys_prefix_path.join(UnixLibDir::Lib)) .map_err(|io_err| { - StdlibDiscoveryError::CouldNotReadLibDirectory(sys_prefix_path.to_owned(), io_err) + StdlibDiscoveryError::CouldNotReadLibDirectory( + sys_prefix_path.to_owned(), + io_err, + system.dyn_clone(), + ) })? { let Ok(entry) = entry_result else { @@ -1299,6 +1340,7 @@ fn real_stdlib_directory_from_sys_prefix( } Err(StdlibDiscoveryError::NoStdlibFound( sys_prefix_path.to_owned(), + system.dyn_clone(), )) } @@ -1357,6 +1399,7 @@ impl SysPrefixPath { unvalidated_path.to_path_buf(), origin, None, + system.dyn_clone(), )); }; sys_prefix @@ -1376,12 +1419,14 @@ impl SysPrefixPath { unvalidated_path, origin, Some(io_err), + system.dyn_clone(), ) } else { SitePackagesDiscoveryError::CanonicalizationError( unvalidated_path, origin, io_err, + system.dyn_clone(), ) }; return Err(err); @@ -1393,6 +1438,7 @@ impl SysPrefixPath { unvalidated_path.to_path_buf(), origin, None, + system.dyn_clone(), )); } diff --git a/crates/ty_server/src/system.rs b/crates/ty_server/src/system.rs index 7f5ad0cdfc..46011daa44 100644 --- a/crates/ty_server/src/system.rs +++ b/crates/ty_server/src/system.rs @@ -298,6 +298,10 @@ impl System for LSPSystem { fn env_var(&self, name: &str) -> std::result::Result { self.native_system.env_var(name) } + + fn dyn_clone(&self) -> Box { + Box::new(self.clone()) + } } fn not_a_text_document(path: impl Display) -> std::io::Error { diff --git a/crates/ty_static/src/lib.rs b/crates/ty_static/src/lib.rs index 153591db70..65a46b6247 100644 --- a/crates/ty_static/src/lib.rs +++ b/crates/ty_static/src/lib.rs @@ -1,3 +1,7 @@ +#![warn( + clippy::disallowed_methods, + reason = "Prefer System trait methods over std methods in ty crates" +)] pub use env_vars::*; mod env_vars; diff --git a/crates/ty_test/src/db.rs b/crates/ty_test/src/db.rs index 1a47745e1d..100b26b5fb 100644 --- a/crates/ty_test/src/db.rs +++ b/crates/ty_test/src/db.rs @@ -268,6 +268,10 @@ impl System for MdtestSystem { fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self } + + fn dyn_clone(&self) -> Box { + Box::new(self.clone()) + } } impl WritableSystem for MdtestSystem { diff --git a/crates/ty_vendored/src/lib.rs b/crates/ty_vendored/src/lib.rs index e6fd7e0a0b..fa054443ff 100644 --- a/crates/ty_vendored/src/lib.rs +++ b/crates/ty_vendored/src/lib.rs @@ -1,3 +1,7 @@ +#![warn( + clippy::disallowed_methods, + reason = "Prefer System trait methods over std methods in ty crates" +)] use ruff_db::vendored::VendoredFileSystem; use std::sync::LazyLock; diff --git a/crates/ty_wasm/src/lib.rs b/crates/ty_wasm/src/lib.rs index 413699866a..7b2c970ea3 100644 --- a/crates/ty_wasm/src/lib.rs +++ b/crates/ty_wasm/src/lib.rs @@ -1231,6 +1231,10 @@ impl System for WasmSystem { fn as_any_mut(&mut self) -> &mut dyn Any { self } + + fn dyn_clone(&self) -> Box { + Box::new(self.clone()) + } } fn not_found() -> std::io::Error {