diff --git a/Cargo.lock b/Cargo.lock index 230765615..fe889cc3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4461,6 +4461,7 @@ dependencies = [ "base64 0.21.7", "cache-key", "cargo-util", + "fs-err", "git2", "glob", "hex", diff --git a/clippy.toml b/clippy.toml index 79cd82af4..c82e7aefe 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,4 +1,32 @@ doc-valid-idents = [ "PyPI", ".." # Include the defaults -] +] + +disallowed-types = [ + "std::fs::DirEntry", + "std::fs::File", + "std::fs::OpenOptions", + "std::fs::ReadDir", +] + +disallowed-methods = [ + "std::fs::canonicalize", + "std::fs::copy", + "std::fs::create_dir", + "std::fs::create_dir_all", + "std::fs::hard_link", + "std::fs::metadata", + "std::fs::read", + "std::fs::read_dir", + "std::fs::read_link", + "std::fs::read_to_string", + "std::fs::remove_dir", + "std::fs::remove_dir_all", + "std::fs::remove_file", + "std::fs::rename", + "std::fs::set_permissions", + "std::fs::soft_link", + "std::fs::symlink_metadata", + "std::fs::write", +] \ No newline at end of file diff --git a/crates/uv-dev/src/render_benchmarks.rs b/crates/uv-dev/src/render_benchmarks.rs index a6bceb1fb..6f0899738 100644 --- a/crates/uv-dev/src/render_benchmarks.rs +++ b/crates/uv-dev/src/render_benchmarks.rs @@ -17,7 +17,7 @@ pub(crate) struct RenderBenchmarksArgs { } pub(crate) fn render_benchmarks(args: &RenderBenchmarksArgs) -> Result<()> { - let mut results: BenchmarkResults = serde_json::from_slice(&std::fs::read(&args.path)?)?; + let mut results: BenchmarkResults = serde_json::from_slice(&fs_err::read(&args.path)?)?; // Replace the command with a shorter name. (The command typically includes the benchmark name, // but we assume we're running over a single benchmark here.) @@ -85,7 +85,7 @@ fn render_to_png(data: &str, path: &Path, fontdb: &fontdb::Database) -> Result<( pixmap.as_mut(), ) .ok_or_else(|| anyhow!("failed to render"))?; - std::fs::create_dir_all(path.parent().unwrap())?; + fs_err::create_dir_all(path.parent().unwrap())?; pixmap.save_png(path)?; Ok(()) } diff --git a/crates/uv-distribution/src/source/mod.rs b/crates/uv-distribution/src/source/mod.rs index 11f0e59f9..078283e4e 100644 --- a/crates/uv-distribution/src/source/mod.rs +++ b/crates/uv-distribution/src/source/mod.rs @@ -980,7 +980,7 @@ impl<'a, T: BuildContext> SourceDistCachedBuilder<'a, T> { /// Read an existing HTTP-cached [`Manifest`], if it exists. pub(crate) fn read_http_manifest(cache_entry: &CacheEntry) -> Result, Error> { - match std::fs::File::open(cache_entry.path()) { + match fs_err::File::open(cache_entry.path()) { Ok(file) => { let data = DataWithCachePolicy::from_reader(file)?.data; Ok(Some(rmp_serde::from_slice::(&data)?)) @@ -998,7 +998,7 @@ pub(crate) fn read_timestamp_manifest( modified: ArchiveTimestamp, ) -> Result, Error> { // If the cache entry is up-to-date, return it. - match std::fs::read(cache_entry.path()) { + match fs_err::read(cache_entry.path()) { Ok(cached) => { let cached = rmp_serde::from_slice::>(&cached)?; if cached.timestamp == modified.timestamp() { diff --git a/crates/uv-extract/src/vendor/cloneable_seekable_reader.rs b/crates/uv-extract/src/vendor/cloneable_seekable_reader.rs index b2c58e07f..720b3826d 100644 --- a/crates/uv-extract/src/vendor/cloneable_seekable_reader.rs +++ b/crates/uv-extract/src/vendor/cloneable_seekable_reader.rs @@ -117,6 +117,7 @@ impl HasLength for BufReader { } } +#[allow(clippy::disallowed_types)] impl HasLength for std::fs::File { fn len(&self) -> u64 { self.metadata().unwrap().len() diff --git a/crates/uv-git/Cargo.toml b/crates/uv-git/Cargo.toml index af4b23e24..f6078fac8 100644 --- a/crates/uv-git/Cargo.toml +++ b/crates/uv-git/Cargo.toml @@ -32,6 +32,7 @@ sha1 = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } url = { workspace = true } +fs-err = { workspace = true } [features] vendored-libgit2 = ["git2/vendored-libgit2"] diff --git a/crates/uv-git/src/git.rs b/crates/uv-git/src/git.rs index 89147f035..b98b692f4 100644 --- a/crates/uv-git/src/git.rs +++ b/crates/uv-git/src/git.rs @@ -388,7 +388,7 @@ impl<'a> GitCheckout<'a> { // // TODO(git2): remove this when git2 supports shallow clone correctly if database.repo.is_shallow() { - std::fs::copy( + fs_err::copy( database.repo.path().join("shallow"), r.path().join("shallow"), )?; diff --git a/crates/uv/Cargo.toml b/crates/uv/Cargo.toml index b9324eeb9..23534cbde 100644 --- a/crates/uv/Cargo.toml +++ b/crates/uv/Cargo.toml @@ -97,3 +97,6 @@ pypi = [] git = [] # Introduces a dependency on Maturin. maturin = [] + +[build-dependencies] +fs-err = { workspace = true } diff --git a/crates/uv/build.rs b/crates/uv/build.rs index f192acd86..28c01bb4f 100644 --- a/crates/uv/build.rs +++ b/crates/uv/build.rs @@ -1,4 +1,5 @@ -use std::{fs, path::Path, process::Command}; +use fs_err as fs; +use std::{path::Path, process::Command}; fn main() { // The workspace root directory is not available without walking up the tree diff --git a/crates/uv/src/commands/pip_compile.rs b/crates/uv/src/commands/pip_compile.rs index 5136be096..359c93aaa 100644 --- a/crates/uv/src/commands/pip_compile.rs +++ b/crates/uv/src/commands/pip_compile.rs @@ -482,11 +482,13 @@ fn cmd(include_index_url: bool, include_find_links: bool) -> String { } /// A multi-casting writer that writes to both the standard output and an output file, if present. +#[allow(clippy::disallowed_types)] struct OutputWriter { stdout: Option>, output_file: Option>, } +#[allow(clippy::disallowed_types)] impl OutputWriter { /// Create a new output writer. fn new(include_stdout: bool, output_file: Option<&Path>) -> Result { diff --git a/crates/uv/tests/pip_compile.rs b/crates/uv/tests/pip_compile.rs index 4d4db4285..1a39b966b 100644 --- a/crates/uv/tests/pip_compile.rs +++ b/crates/uv/tests/pip_compile.rs @@ -1,4 +1,5 @@ #![cfg(all(feature = "python", feature = "pypi"))] +#![cfg_attr(feature = "cargo-clippy", allow(clippy::disallowed_types))] use std::fs; use std::path::PathBuf; diff --git a/crates/uv/tests/pip_sync.rs b/crates/uv/tests/pip_sync.rs index 3b439fed3..981b9faee 100644 --- a/crates/uv/tests/pip_sync.rs +++ b/crates/uv/tests/pip_sync.rs @@ -1,7 +1,7 @@ #![cfg(all(feature = "python", feature = "pypi"))] +use fs_err as fs; use std::env::consts::EXE_SUFFIX; -use std::fs; use std::path::Path; use std::process::Command; diff --git a/crates/uv/tests/pip_uninstall.rs b/crates/uv/tests/pip_uninstall.rs index 64fcf2881..1fd837464 100644 --- a/crates/uv/tests/pip_uninstall.rs +++ b/crates/uv/tests/pip_uninstall.rs @@ -328,7 +328,7 @@ fn missing_record() -> Result<()> { unimplemented!("Only Windows and Unix are supported") }) .unwrap(); - std::fs::remove_file(dist_info.join("RECORD"))?; + fs_err::remove_file(dist_info.join("RECORD"))?; let dist_info_str = regex::escape(&format!( "RECORD file not found at: {}", diff --git a/crates/uv/tests/venv.rs b/crates/uv/tests/venv.rs index 27722258d..adcf3fb41 100644 --- a/crates/uv/tests/venv.rs +++ b/crates/uv/tests/venv.rs @@ -550,8 +550,8 @@ fn windows_shims() -> Result<()> { assert!(py38.to_str().unwrap().contains("3.8")); // Write the shim script that forwards the arguments to the python3.8 installation. - std::fs::create_dir(&shim_path)?; - std::fs::write( + fs_err::create_dir(&shim_path)?; + fs_err::write( shim_path.child("python.bat"), format!("@echo off\r\n{}/python.exe %*", py38.display()), )?;