diff --git a/Cargo.toml b/Cargo.toml index b2cbf9134..a4fff779f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -430,3 +430,6 @@ unnecessary_box_returns = "warn" unnecessary_join = "warn" unnecessary_wraps = "warn" unnested_or_patterns = "warn" + +[lints] +workspace = true diff --git a/benches/execution.rs b/benches/execution.rs index 0152de240..56d4fc4fc 100644 --- a/benches/execution.rs +++ b/benches/execution.rs @@ -1,10 +1,15 @@ +#![allow(clippy::iter_over_hash_type)] + +extern crate alloc; + +use core::hint::black_box; use criterion::{ Bencher, BenchmarkGroup, BenchmarkId, Criterion, Throughput, criterion_group, criterion_main, measurement::WallTime, }; use rustpython_compiler::Mode; use rustpython_vm::{Interpreter, PyResult, Settings}; -use std::{collections::HashMap, hint::black_box, path::Path}; +use std::{collections::HashMap, path::Path}; /// `true` when the benchmarks are executed by the CodSpeed runner. /// @@ -15,8 +20,8 @@ fn is_codspeed() -> bool { std::env::var_os("CODSPEED_ENV").is_some() } -fn bench_cpython_code(b: &mut Bencher, source: &str) { - let c_str_source_head = std::ffi::CString::new(source).unwrap(); +fn bench_cpython_code(b: &mut Bencher<'_>, source: &str) { + let c_str_source_head = alloc::ffi::CString::new(source).unwrap(); let c_str_source = c_str_source_head.as_c_str(); pyo3::Python::attach(|py| { b.iter(|| { @@ -27,7 +32,7 @@ fn bench_cpython_code(b: &mut Bencher, source: &str) { }) } -fn bench_rustpython_code(b: &mut Bencher, name: &str, source: &str) { +fn bench_rustpython_code(b: &mut Bencher<'_>, name: &str, source: &str) { // NOTE: Take long time. let mut settings = Settings::default(); settings.path_list.push("Lib/".to_string()); @@ -41,13 +46,17 @@ fn bench_rustpython_code(b: &mut Bencher, name: &str, source: &str) { b.iter(|| { let code = vm.compile(source, Mode::Exec, name).unwrap(); let scope = vm.new_scope_with_builtins(); - let res: PyResult = vm.run_code_obj(code.clone(), scope); + let res: PyResult = vm.run_code_obj(code, scope); vm.unwrap_pyresult(res); }) }) } -pub fn benchmark_file_execution(group: &mut BenchmarkGroup, name: &str, contents: &str) { +pub fn benchmark_file_execution( + group: &mut BenchmarkGroup<'_, WallTime>, + name: &str, + contents: &str, +) { if !is_codspeed() { group.bench_function(BenchmarkId::new(name, "cpython"), |b| { bench_cpython_code(b, contents) @@ -58,7 +67,11 @@ pub fn benchmark_file_execution(group: &mut BenchmarkGroup, name: &str }); } -pub fn benchmark_file_parsing(group: &mut BenchmarkGroup, name: &str, contents: &str) { +pub fn benchmark_file_parsing( + group: &mut BenchmarkGroup<'_, WallTime>, + name: &str, + contents: &str, +) { group.throughput(Throughput::Bytes(contents.len() as u64)); group.bench_function(BenchmarkId::new("rustpython", name), |b| { b.iter(|| ruff_python_parser::parse_module(contents).unwrap()) @@ -81,7 +94,7 @@ pub fn benchmark_file_parsing(group: &mut BenchmarkGroup, name: &str, } } -pub fn benchmark_pystone(group: &mut BenchmarkGroup, contents: String) { +pub fn benchmark_pystone(group: &mut BenchmarkGroup<'_, WallTime>, contents: String) { // Default is 50_000. This takes a while, so reduce it to 30k. for idx in (10_000..=30_000).step_by(10_000) { let code_with_loops = format!("LOOPS = {idx}\n{contents}"); diff --git a/benches/microbenchmarks.rs b/benches/microbenchmarks.rs index 9f7f70c02..4d48278e4 100644 --- a/benches/microbenchmarks.rs +++ b/benches/microbenchmarks.rs @@ -5,10 +5,7 @@ use criterion::{ use pyo3::types::PyAnyMethods; use rustpython_compiler::Mode; use rustpython_vm::{AsObject, Interpreter, PyResult, Settings}; -use std::{ - fs, io, - path::{Path, PathBuf}, -}; +use std::{fs, io, path::Path}; // List of microbenchmarks to skip. // @@ -36,7 +33,7 @@ pub struct MicroBenchmark { iterate: bool, } -fn bench_cpython_code(group: &mut BenchmarkGroup, bench: &MicroBenchmark) { +fn bench_cpython_code(group: &mut BenchmarkGroup<'_, WallTime>, bench: &MicroBenchmark) { pyo3::Python::attach(|py| { let setup_name = format!("{}_setup", bench.name); let setup_code = cpy_compile_code(py, &bench.setup, &setup_name).unwrap(); @@ -49,8 +46,8 @@ fn bench_cpython_code(group: &mut BenchmarkGroup, bench: &MicroBenchma let exec = builtins.getattr("exec").expect("no exec in builtins"); let bench_func = |(globals, locals): &mut ( - pyo3::Bound, - pyo3::Bound, + pyo3::Bound<'_, pyo3::types::PyDict>, + pyo3::Bound<'_, pyo3::types::PyDict>, )| { let res = exec.call((&code, &*globals, &*locals), None); if let Err(e) = res { @@ -107,7 +104,7 @@ fn cpy_compile_code<'a>( .expect("compile() should return a code object")) } -fn bench_rustpython_code(group: &mut BenchmarkGroup, bench: &MicroBenchmark) { +fn bench_rustpython_code(group: &mut BenchmarkGroup<'_, WallTime>, bench: &MicroBenchmark) { let mut settings = Settings::default(); settings.path_list.push("Lib/".to_string()); settings.write_bytecode = false; @@ -208,11 +205,11 @@ pub fn criterion_benchmark(c: &mut Criterion) { .unwrap() .collect::>() .unwrap(); - let paths: Vec = dirs.iter().map(|p| p.path()).collect(); - let benchmarks: Vec = paths - .into_iter() - .map(|p| { + let benchmarks: Vec = dirs + .iter() + .map(|d| { + let p = d.path(); let name = p.file_name().unwrap().to_os_string(); let contents = fs::read_to_string(p).unwrap(); let iterate = contents.contains("ITERATIONS"); diff --git a/crates/codegen/src/ir.rs b/crates/codegen/src/ir.rs index 2c7aaedc0..790846a40 100644 --- a/crates/codegen/src/ir.rs +++ b/crates/codegen/src/ir.rs @@ -1253,8 +1253,7 @@ fn assemble_location_info( let mut prev_line = first_line; let mut loc = no_linetable_location(); let mut size = 0; - for i in 0..instr_sequence.instr_used { - let entry = &instr_sequence.instrs[i]; + for entry in instr_sequence.instrs.iter().take(instr_sequence.instr_used) { let instr_loc = entry.info.instruction_linetable_location(); if !same_location(loc, instr_loc) { assemble_emit_location(&mut linetable, loc, size, &mut prev_line, debug_ranges)?; @@ -2772,8 +2771,7 @@ impl Blocks { let mut block_idx = BlockIdx(0); while block_idx != BlockIdx::NULL { let block = &self[block_idx]; - for i in 0..block.instruction_used { - let instr = &block.instructions[i]; + for instr in block.instructions.iter().take(block.instruction_used) { if instr.instr.has_const() { let index = u32::from(instr.arg) as usize; debug_assert!(index < nconsts); @@ -6596,8 +6594,11 @@ fn cfg_from_instruction_sequence( if let Some(annotations_code) = &annotations_code { debug_assert!(annotations_code.label_map.is_none()); debug_assert_eq!(annotations_code.label_map_allocation, 0); - for j in 0..annotations_code.instr_used { - let ann_entry = annotations_code.instrs[j]; + for ann_entry in annotations_code + .instrs + .iter() + .take(annotations_code.instr_used) + { debug_assert!(!ann_entry.info.instr.has_target()); let mut info = ann_entry.info; info.target = BlockIdx::NULL; diff --git a/crates/common/src/float_ops.rs b/crates/common/src/float_ops.rs index ac4e6b0bf..f519cdaba 100644 --- a/crates/common/src/float_ops.rs +++ b/crates/common/src/float_ops.rs @@ -1,4 +1,4 @@ -use core::f64; +use core::f64::consts::LOG10_2; use malachite_bigint::{BigInt, ToBigInt}; use num_traits::{Signed, ToPrimitive}; @@ -240,9 +240,8 @@ pub fn round_float_digits(x: f64, ndigits: i32) -> Option { return Some(x); } - const NDIGITS_MAX: i32 = - ((f64::MANTISSA_DIGITS as i32 - f64::MIN_EXP) as f64 * f64::consts::LOG10_2) as i32; - const NDIGITS_MIN: i32 = -(((f64::MAX_EXP + 1) as f64 * f64::consts::LOG10_2) as i32); + const NDIGITS_MAX: i32 = ((f64::MANTISSA_DIGITS as i32 - f64::MIN_EXP) as f64 * LOG10_2) as i32; + const NDIGITS_MIN: i32 = -(((f64::MAX_EXP + 1) as f64 * LOG10_2) as i32); if ndigits > NDIGITS_MAX { return Some(x); diff --git a/crates/common/src/wtf8_index.rs b/crates/common/src/wtf8_index.rs index 4b2e5ebee..a45033d98 100644 --- a/crates/common/src/wtf8_index.rs +++ b/crates/common/src/wtf8_index.rs @@ -146,13 +146,13 @@ impl Wtf8Index { }; let mut index = group_min << 6; let mut pos = base; - for entry in 0..entries { - let at = base + self.groups[group_min].ofs[entry] as usize; + for (i, &entry) in self.groups[group_min].ofs.iter().enumerate().take(entries) { + let at = base + entry as usize; if at >= bytepos { break; } pos = at; - index = (group_min << 6) + (entry << 2) + 1; + index = (group_min << 6) + (i << 2) + 1; } while pos < bytepos { pos = next_pos(data, pos); diff --git a/crates/host_env/Cargo.toml b/crates/host_env/Cargo.toml index bf2cc2a71..8b3f0dd37 100644 --- a/crates/host_env/Cargo.toml +++ b/crates/host_env/Cargo.toml @@ -50,7 +50,7 @@ nix = { workspace = true } rustix = { workspace = true } [target.'cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))'.dependencies] -num_cpus = "1.17.0" +num_cpus = { workspace = true } [target.'cfg(not(any(target_os = "ios", target_os = "android", target_os = "windows", target_arch = "wasm32", target_os = "redox")))'.dependencies] mac_address = { workspace = true } @@ -67,8 +67,8 @@ which = { workspace = true } termios = { workspace = true } [target.'cfg(any(unix, windows))'.dependencies] -memmap2 = "0.9.10" -libloading = "0.9" +memmap2 = { workspace = true } +libloading = { workspace = true } [target.'cfg(all(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"), not(any(target_env = "musl", target_env = "sgx"))))'.dependencies] libffi = { workspace = true, features = ["system"] } diff --git a/crates/host_env/src/multiprocessing.rs b/crates/host_env/src/multiprocessing.rs index 255369f7c..f61a136e9 100644 --- a/crates/host_env/src/multiprocessing.rs +++ b/crates/host_env/src/multiprocessing.rs @@ -179,10 +179,11 @@ impl SemHandle { /// A handle rebuilt from the integer a `SemLock` stored. /// + /// # Safety /// `Drop` closes the semaphore. A caller that still owns that close /// must forget this value. #[inline] - pub unsafe fn from_raw(raw: *mut sem_t) -> Self { + pub const unsafe fn from_raw(raw: *mut sem_t) -> Self { Self { raw } } diff --git a/crates/host_env/src/os.rs b/crates/host_env/src/os.rs index e37dd1d0c..6678572bf 100644 --- a/crates/host_env/src/os.rs +++ b/crates/host_env/src/os.rs @@ -235,7 +235,6 @@ pub fn rename( /// /// On Windows, this supports the full u32 range including STATUS_CONTROL_C_EXIT (0xC000013A). /// On other platforms, only the lower 8 bits are used. -#[must_use] pub fn exit_code(code: u32) -> ExitCode { #[cfg(windows)] { diff --git a/crates/literal/Cargo.toml b/crates/literal/Cargo.toml index 60350f793..2a80fc633 100644 --- a/crates/literal/Cargo.toml +++ b/crates/literal/Cargo.toml @@ -8,6 +8,9 @@ repository = { workspace = true } license = { workspace = true } rust-version = { workspace = true } +[lints] +workspace = true + [dependencies] rustpython-unicode = { workspace = true } rustpython-wtf8 = { workspace = true } diff --git a/crates/literal/src/complex.rs b/crates/literal/src/complex.rs index bc193f0f2..78f3fc023 100644 --- a/crates/literal/src/complex.rs +++ b/crates/literal/src/complex.rs @@ -33,6 +33,7 @@ fn component_to_string(value: f64) -> String { } /// Convert a complex number to a string. +#[must_use] pub fn to_string(re: f64, im: f64) -> String { let mut im_part = component_to_string(im); im_part.push('j'); @@ -41,9 +42,8 @@ pub fn to_string(re: f64, im: f64) -> String { let re_part = if re == 0.0 { if re.is_sign_positive() { return im_part; - } else { - "-0".to_owned() } + "-0".to_owned() } else { component_to_string(re) }; diff --git a/crates/literal/src/escape.rs b/crates/literal/src/escape.rs index 50dce8b26..29f0e82bc 100644 --- a/crates/literal/src/escape.rs +++ b/crates/literal/src/escape.rs @@ -9,6 +9,7 @@ pub enum Quote { impl Quote { #[inline] + #[must_use] pub const fn swap(self) -> Self { match self { Self::Single => Self::Double, @@ -17,6 +18,7 @@ impl Quote { } #[inline] + #[must_use] pub const fn to_byte(&self) -> u8 { match self { Self::Single => b'\'', @@ -25,6 +27,7 @@ impl Quote { } #[inline] + #[must_use] pub const fn to_char(&self) -> char { match self { Self::Single => '\'', @@ -96,20 +99,24 @@ pub struct UnicodeEscape<'a> { impl<'a> UnicodeEscape<'a> { #[inline] + #[must_use] pub const fn with_forced_quote(source: &'a Wtf8, quote: Quote) -> Self { let layout = EscapeLayout { quote, len: None }; Self { source, layout } } #[inline] + #[must_use] pub fn with_preferred_quote(source: &'a Wtf8, quote: Quote) -> Self { let layout = Self::repr_layout(source, quote); Self { source, layout } } #[inline] + #[must_use] pub fn new_repr(source: &'a Wtf8) -> Self { Self::with_preferred_quote(source, Quote::Single) } #[inline] + #[must_use] pub const fn str_repr<'r>(&'a self) -> StrRepr<'r, 'a> { StrRepr(self) } @@ -125,6 +132,7 @@ impl StrRepr<'_, '_> { formatter.write_char(quote) } + #[must_use] pub fn to_string(&self) -> Option { let mut s = String::with_capacity(self.0.layout().len?); self.write(&mut s).unwrap(); @@ -141,6 +149,7 @@ impl core::fmt::Display for StrRepr<'_, '_> { impl UnicodeEscape<'_> { const REPR_RESERVED_LEN: usize = 2; // for quotes + #[must_use] pub fn repr_layout(source: &Wtf8, preferred_quote: Quote) -> EscapeLayout { Self::output_layout_with_checker(source, preferred_quote, |a, b| { Some((a as isize).checked_add(b as isize)? as usize) @@ -284,36 +293,43 @@ pub struct AsciiEscape<'a> { impl<'a> AsciiEscape<'a> { #[inline] + #[must_use] pub const fn new(source: &'a [u8], layout: EscapeLayout) -> Self { Self { source, layout } } #[inline] + #[must_use] pub const fn with_forced_quote(source: &'a [u8], quote: Quote) -> Self { let layout = EscapeLayout { quote, len: None }; Self { source, layout } } #[inline] + #[must_use] pub fn with_preferred_quote(source: &'a [u8], quote: Quote) -> Self { let layout = Self::repr_layout(source, quote); Self { source, layout } } #[inline] + #[must_use] pub fn new_repr(source: &'a [u8]) -> Self { Self::with_preferred_quote(source, Quote::Single) } #[inline] + #[must_use] pub const fn bytes_repr<'r>(&'a self) -> BytesRepr<'r, 'a> { BytesRepr(self) } } impl AsciiEscape<'_> { + #[must_use] pub fn repr_layout(source: &[u8], preferred_quote: Quote) -> EscapeLayout { Self::output_layout_with_checker(source, preferred_quote, 3, |a, b| { Some((a as isize).checked_add(b as isize)? as usize) }) } + #[must_use] pub fn named_repr_layout(source: &[u8], name: &str) -> EscapeLayout { Self::output_layout_with_checker(source, Quote::Single, name.len() + 2 + 3, |a, b| { Some((a as isize).checked_add(b as isize)? as usize) @@ -436,6 +452,7 @@ impl BytesRepr<'_, '_> { formatter.write_char(quote) } + #[must_use] pub fn to_string(&self) -> Option { let mut s = String::with_capacity(self.0.layout().len?); self.write(&mut s).unwrap(); diff --git a/crates/literal/src/float.rs b/crates/literal/src/float.rs index 2db4fd084..8a0a18fc9 100644 --- a/crates/literal/src/float.rs +++ b/crates/literal/src/float.rs @@ -2,13 +2,14 @@ use crate::format::Case; use alloc::borrow::ToOwned; use alloc::format; use alloc::string::{String, ToString}; -use core::f64; use num_traits::Zero; +#[must_use] pub fn parse_str(literal: &str) -> Option { parse_inner(literal.trim().as_bytes()) } +#[must_use] pub fn parse_bytes(literal: &[u8]) -> Option { parse_inner(literal.trim_ascii()) } @@ -25,6 +26,7 @@ fn parse_inner(literal: &[u8]) -> Option { f64::from_lexical_with_options::(literal, &Options::new()).ok() } +#[must_use] pub fn is_integer(v: f64) -> bool { v.is_finite() && v.fract() == 0.0 } @@ -47,6 +49,7 @@ fn format_inf(case: Case) -> String { inf.to_string() } +#[must_use] pub const fn decimal_point_or_empty(precision: usize, alternate_form: bool) -> &'static str { match (precision, alternate_form) { (0, true) => ".", @@ -70,15 +73,18 @@ pub const FMT_MAX_PRECISION: usize = u16::MAX as usize; pub const FMT_MAX_EXP_PRECISION: usize = u16::MAX as usize - 1; #[inline] +#[must_use] pub fn clamp_fmt_precision(precision: usize) -> usize { core::cmp::min(precision, FMT_MAX_PRECISION) } #[inline] +#[must_use] pub fn clamp_exp_precision(precision: usize) -> usize { core::cmp::min(precision, FMT_MAX_EXP_PRECISION) } +#[must_use] pub fn format_fixed(precision: usize, magnitude: f64, case: Case, alternate_form: bool) -> String { match magnitude { magnitude if magnitude.is_finite() => { @@ -103,6 +109,7 @@ pub fn format_fixed(precision: usize, magnitude: f64, case: Case, alternate_form // Formats floats into Python style exponent notation, by first formatting in Rust style // exponent notation (`1.0000e0`), then convert to Python style (`1.0000e+00`). +#[must_use] pub fn format_exponent( precision: usize, magnitude: f64, @@ -166,6 +173,7 @@ fn remove_trailing_decimal_point(s: String) -> String { s } +#[must_use] pub fn format_general( precision: usize, magnitude: f64, @@ -176,7 +184,7 @@ pub fn format_general( match magnitude { magnitude if magnitude.is_finite() => { let exp_precision = clamp_exp_precision(precision.saturating_sub(1)); - let r_exp = format!("{:.*e}", exp_precision, magnitude); + let r_exp = format!("{magnitude:.exp_precision$e}"); let mut parts = r_exp.splitn(2, 'e'); let base = parts.next().unwrap(); let exponent = parts.next().unwrap().parse::().unwrap(); @@ -230,7 +238,7 @@ pub(crate) fn prefer_cpython_tie_repr(s: String, value: f64) -> String { let mut candidate = s.clone(); candidate.replace_range( - digit_pos..digit_pos + 1, + digit_pos..=digit_pos, core::str::from_utf8(&[decremented]).unwrap(), ); if parse_str(&candidate).is_none_or(|parsed| parsed.to_bits() != value.to_bits()) { @@ -266,9 +274,7 @@ fn parse_decimal_rational(s: &str) -> Option<(u128, u32)> { }; let significand = mantissa.strip_prefix('-').unwrap_or(mantissa); let dot_pos = significand.find('.'); - let frac_digits = dot_pos - .map(|pos| significand.len().saturating_sub(pos + 1)) - .unwrap_or(0); + let frac_digits = dot_pos.map_or(0, |pos| significand.len().saturating_sub(pos + 1)); let mut digits = String::with_capacity(significand.len()); for ch in significand.chars() { if ch != '.' { @@ -317,6 +323,7 @@ fn decimal_distance_to_f64(s: &str, value: f64) -> Option { } // TODO: rewrite using format_general +#[must_use] pub fn to_string(value: f64) -> String { let lit = format!("{value:e}"); if let Some(position) = lit.find('e') { @@ -339,37 +346,7 @@ pub fn to_string(value: f64) -> String { } } -#[cfg(test)] -mod tests { - use super::to_string; - - #[test] - fn repr_uses_cpython_tie_digit_for_power_of_two() { - assert_eq!(to_string(2.0f64.powi(-25)), "2.9802322387695312e-08"); - assert_eq!(to_string((-2.0f64).powi(-25)), "-2.9802322387695312e-08"); - assert_eq!(to_string(2.0f64.powi(-26)), "1.4901161193847656e-08"); - assert_eq!( - to_string(2.0f64.powi(-14) - 2.0f64.powi(-25)), - "6.1005353927612305e-05" - ); - } - - #[test] - fn repr_normal_range_uses_cpython_tie_digit() { - // Rust's shortest formatter yields "161852602146008.13" for this - // value; round-half-to-even (what `repr` uses) picks "…08.12". - assert_eq!( - to_string(f64::from_bits(0x42e26687db6b9b04)), - "161852602146008.12" - ); - // Non-tie values are left untouched. - assert_eq!(to_string(1.5), "1.5"); - assert_eq!(to_string(0.1), "0.1"); - assert_eq!(to_string(12.34), "12.34"); - assert_eq!(to_string(100.0), "100.0"); - } -} - +#[must_use] pub fn from_hex(s: &str) -> Option { if let Ok(f) = hexf_parse::parse_hexf64(s, false) { return Some(f); @@ -418,6 +395,7 @@ pub fn from_hex(s: &str) -> Option { } } +#[must_use] pub fn to_hex(value: f64) -> String { let bits = value.to_bits(); let sign_fmt = if bits >> 63 != 0 { "-" } else { "" }; @@ -440,55 +418,86 @@ pub fn to_hex(value: f64) -> String { } } -#[test] -fn test_to_hex() { - use rand::RngExt; - assert_eq!(to_hex(f64::from_bits(1)), "0x0.0000000000001p-1022"); - assert_eq!(to_hex(f64::from_bits(2)), "0x0.0000000000002p-1022"); - assert_eq!(to_hex(-f64::from_bits(1)), "-0x0.0000000000001p-1022"); - assert_eq!(to_hex(f64::MIN_POSITIVE), "0x1.0000000000000p-1022"); - for _ in 0..20000 { - let bytes = rand::rng().random::(); - let f = f64::from_bits(bytes); - if !f.is_finite() { - continue; +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn repr_uses_cpython_tie_digit_for_power_of_two() { + assert_eq!(to_string(2.0f64.powi(-25)), "2.9802322387695312e-08"); + assert_eq!(to_string((-2.0f64).powi(-25)), "-2.9802322387695312e-08"); + assert_eq!(to_string(2.0f64.powi(-26)), "1.4901161193847656e-08"); + assert_eq!( + to_string(2.0f64.powi(-14) - 2.0f64.powi(-25)), + "6.1005353927612305e-05" + ); + } + + #[test] + fn repr_normal_range_uses_cpython_tie_digit() { + // Rust's shortest formatter yields "161852602146008.13" for this + // value; round-half-to-even (what `repr` uses) picks "…08.12". + assert_eq!( + to_string(f64::from_bits(0x42e26687db6b9b04)), + "161852602146008.12" + ); + // Non-tie values are left untouched. + assert_eq!(to_string(1.5), "1.5"); + assert_eq!(to_string(0.1), "0.1"); + assert_eq!(to_string(12.34), "12.34"); + assert_eq!(to_string(100.0), "100.0"); + } + + #[test] + fn to_hex_works() { + use rand::RngExt; + assert_eq!(to_hex(f64::from_bits(1)), "0x0.0000000000001p-1022"); + assert_eq!(to_hex(f64::from_bits(2)), "0x0.0000000000002p-1022"); + assert_eq!(to_hex(-f64::from_bits(1)), "-0x0.0000000000001p-1022"); + assert_eq!(to_hex(f64::MIN_POSITIVE), "0x1.0000000000000p-1022"); + for _ in 0..20000 { + let bytes = rand::rng().random::(); + let f = f64::from_bits(bytes); + if !f.is_finite() { + continue; + } + let hex = to_hex(f); + // println!("{} -> {}", f, hex); + let roundtrip = hexf_parse::parse_hexf64(&hex, false).unwrap(); + // println!(" -> {}", roundtrip); + assert!(f == roundtrip, "{f} {hex} {roundtrip}"); } - let hex = to_hex(f); - // println!("{} -> {}", f, hex); - let roundtrip = hexf_parse::parse_hexf64(&hex, false).unwrap(); - // println!(" -> {}", roundtrip); - assert!(f == roundtrip, "{f} {hex} {roundtrip}"); + } + + #[test] + fn remove_trailing_zeros_works() { + assert!(remove_trailing_zeros(String::from("100")) == *"1"); + assert!(remove_trailing_zeros(String::from("100.00")) == *"100."); + + // leave leading zeros untouched + assert!(remove_trailing_zeros(String::from("001")) == *"001"); + + // leave strings untouched if they don't end with 0 + assert!(remove_trailing_zeros(String::from("101")) == *"101"); + } + + #[test] + fn remove_trailing_decimal_point_works() { + assert!(remove_trailing_decimal_point(String::from("100.")) == *"100"); + assert!(remove_trailing_decimal_point(String::from("1.")) == *"1"); + + // leave leading decimal points untouched + assert!(remove_trailing_decimal_point(String::from(".5")) == *".5"); + } + + #[test] + fn maybe_remove_trailing_redundant_chars_works() { + assert!(maybe_remove_trailing_redundant_chars(String::from("100."), true) == *"100."); + assert!(maybe_remove_trailing_redundant_chars(String::from("100."), false) == *"100"); + assert!(maybe_remove_trailing_redundant_chars(String::from("1."), false) == *"1"); + assert!(maybe_remove_trailing_redundant_chars(String::from("10.0"), false) == *"10"); + + // don't truncate integers + assert!(maybe_remove_trailing_redundant_chars(String::from("1000"), false) == *"1000"); } } - -#[test] -fn test_remove_trailing_zeros() { - assert!(remove_trailing_zeros(String::from("100")) == *"1"); - assert!(remove_trailing_zeros(String::from("100.00")) == *"100."); - - // leave leading zeros untouched - assert!(remove_trailing_zeros(String::from("001")) == *"001"); - - // leave strings untouched if they don't end with 0 - assert!(remove_trailing_zeros(String::from("101")) == *"101"); -} - -#[test] -fn test_remove_trailing_decimal_point() { - assert!(remove_trailing_decimal_point(String::from("100.")) == *"100"); - assert!(remove_trailing_decimal_point(String::from("1.")) == *"1"); - - // leave leading decimal points untouched - assert!(remove_trailing_decimal_point(String::from(".5")) == *".5"); -} - -#[test] -fn test_maybe_remove_trailing_redundant_chars() { - assert!(maybe_remove_trailing_redundant_chars(String::from("100."), true) == *"100."); - assert!(maybe_remove_trailing_redundant_chars(String::from("100."), false) == *"100"); - assert!(maybe_remove_trailing_redundant_chars(String::from("1."), false) == *"1"); - assert!(maybe_remove_trailing_redundant_chars(String::from("10.0"), false) == *"10"); - - // don't truncate integers - assert!(maybe_remove_trailing_redundant_chars(String::from("1000"), false) == *"1000"); -} diff --git a/crates/stdlib/src/_sqlite3.rs b/crates/stdlib/src/_sqlite3.rs index c6fe0516a..7f3fc1606 100644 --- a/crates/stdlib/src/_sqlite3.rs +++ b/crates/stdlib/src/_sqlite3.rs @@ -2179,7 +2179,7 @@ mod _sqlite3 { .flat_map(|x| x.to_uppercase()) .collect::(); if let Some(converter) = converters().get_item_opt(&col_name, vm)? { - cast_map.push(Some(converter.clone())); + cast_map.push(Some(converter)); continue; } } @@ -2189,7 +2189,7 @@ mod _sqlite3 { if let Some(decltype) = decltype.split_terminator(&[' ', '(']).next() { let decltype = decltype.to_uppercase(); if let Some(converter) = converters().get_item_opt(&decltype, vm)? { - cast_map.push(Some(converter.clone())); + cast_map.push(Some(converter)); continue; } } diff --git a/crates/stdlib/src/hashlib.rs b/crates/stdlib/src/hashlib.rs index 591604b7b..c111abaa2 100644 --- a/crates/stdlib/src/hashlib.rs +++ b/crates/stdlib/src/hashlib.rs @@ -262,7 +262,6 @@ pub(crate) mod _hashlib { } #[repr(C, align(16))] - #[repr(align(16))] struct RawHashState { words: [MaybeUninit; backend::HASH_STATE_STORAGE_WORDS], } @@ -369,7 +368,6 @@ pub(crate) mod _hashlib { } #[repr(C, align(16))] - #[repr(align(16))] struct RawHmacState { words: [MaybeUninit; backend::HMAC_STATE_STORAGE_WORDS], } diff --git a/crates/vm/src/exceptions.rs b/crates/vm/src/exceptions.rs index 2db1bc7ec..4195b366d 100644 --- a/crates/vm/src/exceptions.rs +++ b/crates/vm/src/exceptions.rs @@ -838,7 +838,7 @@ impl PyRef { if key_str.as_bytes().starts_with(b"__") { continue; } - self.as_object().set_attr(&key_str, value.clone(), vm)?; + self.as_object().set_attr(&key_str, value, vm)?; } } Ok(vm.ctx.none()) diff --git a/crates/vm/src/gc_state.rs b/crates/vm/src/gc_state.rs index d7677f5fe..30fa1032c 100644 --- a/crates/vm/src/gc_state.rs +++ b/crates/vm/src/gc_state.rs @@ -681,8 +681,8 @@ impl GcState { // Reset counts for generations whose objects were promoted away. // For gen2 (oldest), survivors stay in-place so don't reset gen2 count. let reset_end = if generation >= 2 { 2 } else { generation + 1 }; - for i in 0..reset_end { - self.counts[i].store(0, Ordering::Relaxed); + for count in self.counts.iter().take(reset_end) { + count.store(0, Ordering::Relaxed); } let duration = elapsed_secs(start_time); @@ -857,8 +857,8 @@ impl GcState { drop(gen_locks); self.promote_survivors(generation, &survivor_refs); let reset_end = if generation >= 2 { 2 } else { generation + 1 }; - for i in 0..reset_end { - self.counts[i].store(0, Ordering::Relaxed); + for count in self.counts.iter().take(reset_end) { + count.store(0, Ordering::Relaxed); } let duration = elapsed_secs(start_time); @@ -880,8 +880,8 @@ impl GcState { if unreachable_refs.is_empty() { self.promote_survivors(generation, &survivor_refs); let reset_end = if generation >= 2 { 2 } else { generation + 1 }; - for i in 0..reset_end { - self.counts[i].store(0, Ordering::Relaxed); + for count in self.counts.iter().take(reset_end) { + count.store(0, Ordering::Relaxed); } let duration = elapsed_secs(start_time); @@ -1107,8 +1107,8 @@ impl GcState { // Reset counts for generations whose objects were promoted away. // For gen2 (oldest), survivors stay in-place so don't reset gen2 count. let reset_end = if generation >= 2 { 2 } else { generation + 1 }; - for i in 0..reset_end { - self.counts[i].store(0, Ordering::Relaxed); + for count in self.counts.iter().take(reset_end) { + count.store(0, Ordering::Relaxed); } let duration = elapsed_secs(start_time); diff --git a/crates/vm/src/stdlib/_ctypes/structure.rs b/crates/vm/src/stdlib/_ctypes/structure.rs index 39ab31a27..474d9eaca 100644 --- a/crates/vm/src/stdlib/_ctypes/structure.rs +++ b/crates/vm/src/stdlib/_ctypes/structure.rs @@ -417,7 +417,6 @@ impl PyCStructType { // Create CField descriptor with padding-adjusted offset let field_type_ref = field_type - .clone() .downcast::() .map_err(|_| vm.new_type_error("_fields_ type must be a ctypes type"))?; diff --git a/crates/vm/src/stdlib/_ctypes/union.rs b/crates/vm/src/stdlib/_ctypes/union.rs index 2acf70144..e03200041 100644 --- a/crates/vm/src/stdlib/_ctypes/union.rs +++ b/crates/vm/src/stdlib/_ctypes/union.rs @@ -274,7 +274,6 @@ impl PyCUnionType { // For Union, all fields start at offset 0 let field_type_ref = field_type - .clone() .downcast::() .map_err(|_| vm.new_type_error("_fields_ type must be a ctypes type"))?; diff --git a/crates/vm/src/stdlib/_io.rs b/crates/vm/src/stdlib/_io.rs index e90cac5ea..3bec5b299 100644 --- a/crates/vm/src/stdlib/_io.rs +++ b/crates/vm/src/stdlib/_io.rs @@ -537,7 +537,7 @@ mod _io { for line in it.iter(vm)? { let line = line?; let line_len = line.length(vm)?; - ret.push(line.clone()); + ret.push(line); full_len += line_len; if full_len > hint { break; diff --git a/crates/vm/src/stdlib/gc.rs b/crates/vm/src/stdlib/gc.rs index a73d2adfa..42b74eb33 100644 --- a/crates/vm/src/stdlib/gc.rs +++ b/crates/vm/src/stdlib/gc.rs @@ -238,7 +238,7 @@ mod gc { continue; } if refers_to_target(obj.as_ref()) { - result.push(obj.clone()); + result.push(obj); } } diff --git a/crates/vm/src/stdlib/sys/monitoring.rs b/crates/vm/src/stdlib/sys/monitoring.rs index 28374ea7d..87fb6cb7e 100644 --- a/crates/vm/src/stdlib/sys/monitoring.rs +++ b/crates/vm/src/stdlib/sys/monitoring.rs @@ -324,28 +324,33 @@ pub(crate) fn instrument_code(code: &Py, events: u32) { // Phase 1: Remove INSTRUMENTED_LINE → restore from side-table if let Some(data) = monitoring_data.as_mut() { - for i in 0..len { - if data.line_opcodes[i] != 0 { - let original = Instruction::try_from(data.line_opcodes[i]) - .expect("invalid opcode in line side-table"); + for (i, opcode) in data.line_opcodes.iter_mut().enumerate().take(len) { + if *opcode != 0 { + let original = + Instruction::try_from(*opcode).expect("invalid opcode in line side-table"); unsafe { code.code.instructions.replace_op(i, original); } - data.line_opcodes[i] = 0; + *opcode = 0; } } } // Phase 2: Remove INSTRUMENTED_INSTRUCTION → restore from side-table if let Some(data) = monitoring_data.as_mut() { - for i in 0..len { - if data.per_instruction_opcodes[i] != 0 { - let original = Instruction::try_from(data.per_instruction_opcodes[i]) + for (i, opcode) in data + .per_instruction_opcodes + .iter_mut() + .enumerate() + .take(len) + { + if *opcode != 0 { + let original = Instruction::try_from(*opcode) .expect("invalid opcode in instruction side-table"); unsafe { code.code.instructions.replace_op(i, original); } - data.per_instruction_opcodes[i] = 0; + *opcode = 0; } } } @@ -812,9 +817,9 @@ fn all_events(vm: &VirtualMachine) -> PyResult { .filter_map(|(event_id, event_name)| { let event_bit = 1u32 << event_id; let mut tools_mask = 0u8; - for tool in 0..TOOL_LIMIT { - if (state.global_events[tool] & event_bit) != 0 { - tools_mask |= 1 << tool; + for (i, tool) in state.global_events.iter().enumerate().take(TOOL_LIMIT) { + if (tool & event_bit) != 0 { + tools_mask |= 1 << i; } } if tools_mask != 0 { diff --git a/crates/wtf8/Cargo.toml b/crates/wtf8/Cargo.toml index 20bf82489..0afc826f5 100644 --- a/crates/wtf8/Cargo.toml +++ b/crates/wtf8/Cargo.toml @@ -8,6 +8,9 @@ rust-version.workspace = true repository.workspace = true license.workspace = true +[lints] +workspace = true + [dependencies] ascii = { workspace = true, features = ["alloc"] } bstr = { workspace = true, features = ["alloc"] } diff --git a/crates/wtf8/src/concat.rs b/crates/wtf8/src/concat.rs index 055a401d6..200dabfde 100644 --- a/crates/wtf8/src/concat.rs +++ b/crates/wtf8/src/concat.rs @@ -67,7 +67,7 @@ impl Wtf8Concat for CodePoint { /// display output will be replaced with U+FFFD. Use direct [`Wtf8Concat`] /// impls for surrogate-preserving concatenation. #[allow(dead_code)] -pub struct DisplayAsWtf8(pub T); +pub(crate) struct DisplayAsWtf8(pub T); impl Wtf8Concat for DisplayAsWtf8 { #[inline] @@ -112,9 +112,9 @@ impl Wtf8Concat for Box { } } -impl Wtf8Concat for Cow<'_, T> +impl Wtf8Concat for Cow<'_, T> where - T: ToOwned, + T: ToOwned + Wtf8Concat + ?Sized, { #[inline] fn fmt_wtf8(&self, buf: &mut Wtf8Buf) { diff --git a/crates/wtf8/src/core_char.rs b/crates/wtf8/src/core_char.rs index 66f321d8a..69a0f1934 100644 --- a/crates/wtf8/src/core_char.rs +++ b/crates/wtf8/src/core_char.rs @@ -3,8 +3,8 @@ use core::slice; -pub const MAX_LEN_UTF8: usize = 4; -pub const MAX_LEN_UTF16: usize = 2; +pub(crate) const MAX_LEN_UTF8: usize = 4; +pub(crate) const MAX_LEN_UTF16: usize = 2; // UTF-8 ranges and tags for encoding characters const TAG_CONT: u8 = 0b1000_0000; @@ -17,7 +17,7 @@ const MAX_THREE_B: u32 = 0x10000; #[inline] #[must_use] -pub const fn len_utf8(code: u32) -> usize { +pub(crate) const fn len_utf8(code: u32) -> usize { match code { ..MAX_ONE_B => 1, ..MAX_TWO_B => 2, @@ -47,7 +47,7 @@ const fn len_utf16(code: u32) -> usize { /// A buffer of length four is large enough to encode any `char`. #[doc(hidden)] #[inline] -pub fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] { +pub(crate) fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] { let len = len_utf8(code); match (len, &mut *dst) { (1, [a, ..]) => { @@ -91,7 +91,7 @@ pub fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] { /// A buffer of length 2 is large enough to encode any `char`. #[doc(hidden)] #[inline] -pub fn encode_utf16_raw(mut code: u32, dst: &mut [u16]) -> &mut [u16] { +pub(crate) fn encode_utf16_raw(mut code: u32, dst: &mut [u16]) -> &mut [u16] { let len = len_utf16(code); match (len, &mut *dst) { (1, [a, ..]) => { diff --git a/crates/wtf8/src/core_str.rs b/crates/wtf8/src/core_str.rs index 56f715cf7..18c9472c7 100644 --- a/crates/wtf8/src/core_str.rs +++ b/crates/wtf8/src/core_str.rs @@ -30,7 +30,7 @@ pub(super) const fn utf8_is_cont_byte(byte: u8) -> bool { /// /// `bytes` must produce a valid UTF-8-like (UTF-8 or WTF-8) string #[inline] -pub unsafe fn next_code_point<'a, I: Iterator>(bytes: &mut I) -> Option { +pub(crate) unsafe fn next_code_point<'a, I: Iterator>(bytes: &mut I) -> Option { // Decode UTF-8 let x = *bytes.next()?; if x < 128 { @@ -73,7 +73,7 @@ pub unsafe fn next_code_point<'a, I: Iterator>(bytes: &mut I) -> /// /// `bytes` must produce a valid UTF-8-like (UTF-8 or WTF-8) string #[inline] -pub unsafe fn next_code_point_reverse<'a, I>(bytes: &mut I) -> Option +pub(crate) unsafe fn next_code_point_reverse<'a, I>(bytes: &mut I) -> Option where I: DoubleEndedIterator, { diff --git a/crates/wtf8/src/lib.rs b/crates/wtf8/src/lib.rs index 7625e6f18..69c8586dd 100644 --- a/crates/wtf8/src/lib.rs +++ b/crates/wtf8/src/lib.rs @@ -96,17 +96,19 @@ impl CodePoint { /// /// `value` must be less than or equal to 0x10FFFF. #[inline] - pub const unsafe fn from_u32_unchecked(value: u32) -> CodePoint { - CodePoint { value } + #[must_use] + pub const unsafe fn from_u32_unchecked(value: u32) -> Self { + Self { value } } /// Creates a new `CodePoint` if the value is a valid code point. /// /// Returns `None` if `value` is above 0x10FFFF. #[inline] - pub const fn from_u32(value: u32) -> Option { + #[must_use] + pub const fn from_u32(value: u32) -> Option { match value { - 0..=0x10FFFF => Some(CodePoint { value }), + 0..=0x10FFFF => Some(Self { value }), _ => None, } } @@ -115,20 +117,23 @@ impl CodePoint { /// /// Since all Unicode scalar values are code points, this always succeeds. #[inline] - pub const fn from_char(value: char) -> CodePoint { - CodePoint { + #[must_use] + pub const fn from_char(value: char) -> Self { + Self { value: value as u32, } } /// Returns the numeric value of the code point. #[inline] + #[must_use] pub const fn to_u32(self) -> u32 { self.value } /// Returns the numeric value of the code point if it is a leading surrogate. #[inline] + #[must_use] pub const fn to_lead_surrogate(self) -> Option { match self.value { lead @ 0xD800..=0xDBFF => Some(LeadSurrogate(lead as u16)), @@ -138,6 +143,7 @@ impl CodePoint { /// Returns the numeric value of the code point if it is a trailing surrogate. #[inline] + #[must_use] pub const fn to_trail_surrogate(self) -> Option { match self.value { trail @ 0xDC00..=0xDFFF => Some(TrailSurrogate(trail as u16)), @@ -149,6 +155,7 @@ impl CodePoint { /// /// Returns `None` if the code point is a surrogate (from U+D800 to U+DFFF). #[inline] + #[must_use] pub const fn to_char(self) -> Option { match self.value { 0xD800..=0xDFFF => None, @@ -161,6 +168,7 @@ impl CodePoint { /// Returns `'\u{FFFD}'` (the replacement character “�”) /// if the code point is a surrogate (from U+D800 to U+DFFF). #[inline] + #[must_use] pub fn to_char_lossy(self) -> char { self.to_char().unwrap_or('\u{FFFD}') } @@ -173,10 +181,12 @@ impl CodePoint { unsafe { Wtf8::from_mut_bytes_unchecked(encode_utf8_raw(self.value, dst)) } } + #[must_use] pub const fn len_wtf8(&self) -> usize { len_utf8(self.value) } + #[must_use] pub fn is_ascii(&self) -> bool { self.is_char_and(|c| c.is_ascii()) } @@ -230,6 +240,7 @@ pub struct LeadSurrogate(u16); pub struct TrailSurrogate(u16); impl LeadSurrogate { + #[must_use] pub const fn merge(self, trail: TrailSurrogate) -> char { decode_surrogate_pair(self.0, trail.0) } @@ -288,14 +299,16 @@ impl fmt::Display for Wtf8Buf { impl Wtf8Buf { /// Creates a new, empty WTF-8 string. #[inline] - pub fn new() -> Wtf8Buf { - Wtf8Buf::default() + #[must_use] + pub fn new() -> Self { + Self::default() } /// Creates a new, empty WTF-8 string with pre-allocated capacity for `capacity` bytes. #[inline] - pub fn with_capacity(capacity: usize) -> Wtf8Buf { - Wtf8Buf { + #[must_use] + pub fn with_capacity(capacity: usize) -> Self { + Self { bytes: Vec::with_capacity(capacity), } } @@ -306,8 +319,9 @@ impl Wtf8Buf { /// /// `value` must contain valid WTF-8. #[inline] - pub const unsafe fn from_bytes_unchecked(value: Vec) -> Wtf8Buf { - Wtf8Buf { bytes: value } + #[must_use] + pub const unsafe fn from_bytes_unchecked(value: Vec) -> Self { + Self { bytes: value } } /// Create a WTF-8 string from a WTF-8 byte vec. @@ -324,13 +338,14 @@ impl Wtf8Buf { /// /// Since WTF-8 is a superset of UTF-8, this always succeeds. #[inline] - pub fn from_string(string: String) -> Wtf8Buf { - Wtf8Buf { + #[must_use] + pub fn from_string(string: String) -> Self { + Self { bytes: string.into_bytes(), } } - pub fn join(sep: impl AsRef, iter: I) -> Wtf8Buf + pub fn join(sep: impl AsRef, iter: I) -> Self where I: IntoIterator, S: AsRef, @@ -339,7 +354,7 @@ impl Wtf8Buf { let mut iter = iter.into_iter(); let mut buf = match iter.next() { Some(first) => first.as_ref().to_owned(), - None => return Wtf8Buf::new(), + None => return Self::new(), }; for part in iter { buf.push_wtf8(sep); @@ -356,9 +371,10 @@ impl Wtf8Buf { /// /// This is lossless: calling `.encode_wide()` on the resulting string /// will always return the original code units. - pub fn from_wide(v: &[u16]) -> Wtf8Buf { - let mut string = Wtf8Buf::with_capacity(v.len()); - for item in char::decode_utf16(v.iter().cloned()) { + #[must_use] + pub fn from_wide(v: &[u16]) -> Self { + let mut string = Self::with_capacity(v.len()); + for item in char::decode_utf16(v.iter().copied()) { match item { Ok(ch) => string.push_char(ch), Err(surrogate) => { @@ -375,6 +391,7 @@ impl Wtf8Buf { } #[inline] + #[must_use] pub fn as_slice(&self) -> &Wtf8 { unsafe { Wtf8::from_bytes_unchecked(&self.bytes) } } @@ -452,12 +469,14 @@ impl Wtf8Buf { } #[inline] + #[must_use] pub fn leak<'a>(self) -> &'a mut Wtf8 { unsafe { Wtf8::from_mut_bytes_unchecked(self.bytes.leak()) } } /// Returns the number of bytes that this string buffer can hold without reallocating. #[inline] + #[must_use] pub const fn capacity(&self) -> usize { self.bytes.capacity() } @@ -521,6 +540,7 @@ impl Wtf8Buf { /// Consumes the WTF-8 string and tries to convert it to a vec of bytes. #[inline] + #[must_use] pub fn into_bytes(self) -> Vec { self.bytes } @@ -532,7 +552,7 @@ impl Wtf8Buf { /// If the contents are not well-formed UTF-8 /// (that is, if the string contains surrogates), /// the original WTF-8 string is returned instead. - pub fn into_string(self) -> Result { + pub fn into_string(self) -> Result { if self.is_utf8() { Ok(unsafe { String::from_utf8_unchecked(self.bytes) }) } else { @@ -545,6 +565,7 @@ impl Wtf8Buf { /// This does not copy the data (but may overwrite parts of it in place). /// /// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”) + #[must_use] pub fn into_string_lossy(mut self) -> String { let mut pos = 0; while let Some((surrogate_pos, _)) = self.next_surrogate(pos) { @@ -558,15 +579,17 @@ impl Wtf8Buf { /// Converts this `Wtf8Buf` into a boxed `Wtf8`. #[inline] + #[must_use] pub fn into_box(self) -> Box { // SAFETY: relies on `Wtf8` being `repr(transparent)`. unsafe { mem::transmute(self.bytes.into_boxed_slice()) } } /// Converts a `Box` into a `Wtf8Buf`. - pub fn from_box(boxed: Box) -> Wtf8Buf { + #[must_use] + pub fn from_box(boxed: Box) -> Self { let bytes: Box<[u8]> = unsafe { mem::transmute(boxed) }; - Wtf8Buf { + Self { bytes: bytes.into_vec(), } } @@ -577,8 +600,8 @@ impl Wtf8Buf { /// This replaces surrogate code point pairs with supplementary code points, /// like concatenating ill-formed UTF-16 strings effectively would. impl FromIterator for Wtf8Buf { - fn from_iter>(iter: T) -> Wtf8Buf { - let mut string = Wtf8Buf::new(); + fn from_iter>(iter: T) -> Self { + let mut string = Self::new(); string.extend(iter); string } @@ -613,7 +636,7 @@ impl> Extend for Wtf8Buf { impl> FromIterator for Wtf8Buf { fn from_iter>(iter: T) -> Self { - let mut buf = Wtf8Buf::new(); + let mut buf = Self::new(); iter.into_iter().for_each(|w| buf.push_wtf8(w.as_ref())); buf } @@ -633,19 +656,19 @@ impl AsRef for Wtf8Buf { impl From for Wtf8Buf { fn from(s: String) -> Self { - Wtf8Buf::from_string(s) + Self::from_string(s) } } impl From<&str> for Wtf8Buf { fn from(s: &str) -> Self { - Wtf8Buf::from_string(s.to_owned()) + Self::from_string(s.to_owned()) } } impl From for Wtf8Buf { fn from(s: ascii::AsciiString) -> Self { - Wtf8Buf::from_string(s.into()) + Self::from_string(s.into()) } } @@ -658,8 +681,8 @@ pub struct Wtf8 { bytes: [u8], } -impl AsRef for Wtf8 { - fn as_ref(&self) -> &Wtf8 { +impl AsRef for Wtf8 { + fn as_ref(&self) -> &Self { self } } @@ -730,9 +753,8 @@ impl fmt::Display for Wtf8 { let s = unsafe { str::from_utf8_unchecked(&wtf8_bytes[pos..]) }; if pos == 0 { return s.fmt(formatter); - } else { - return formatter.write_str(s); } + return formatter.write_str(s); } } } @@ -757,7 +779,7 @@ impl Wtf8 { /// /// Since WTF-8 is a superset of UTF-8, this always succeeds. #[inline] - pub fn new + ?Sized>(value: &S) -> &Wtf8 { + pub fn new + ?Sized>(value: &S) -> &Self { value.as_ref() } @@ -767,9 +789,10 @@ impl Wtf8 { /// /// `value` must contain valid WTF-8. #[inline] - pub const unsafe fn from_bytes_unchecked(value: &[u8]) -> &Wtf8 { + #[must_use] + pub const unsafe fn from_bytes_unchecked(value: &[u8]) -> &Self { // SAFETY: start with &[u8], end with fancy &[u8] - unsafe { &*(value as *const [u8] as *const Wtf8) } + unsafe { &*(value as *const [u8] as *const Self) } } /// Creates a mutable WTF-8 slice from a mutable WTF-8 byte slice. @@ -777,15 +800,16 @@ impl Wtf8 { /// Since the byte slice is not checked for valid WTF-8, this functions is /// marked unsafe. #[inline] - const unsafe fn from_mut_bytes_unchecked(value: &mut [u8]) -> &mut Wtf8 { + const unsafe fn from_mut_bytes_unchecked(value: &mut [u8]) -> &mut Self { // SAFETY: start with &mut [u8], end with fancy &mut [u8] - unsafe { &mut *(value as *mut [u8] as *mut Wtf8) } + unsafe { &mut *(value as *mut [u8] as *mut Self) } } /// Create a WTF-8 slice from a WTF-8 byte slice. // // whoops! using WTF-8 for interchange! #[inline] + #[must_use] pub fn from_bytes(b: &[u8]) -> Option<&Self> { let mut rest = b; while let Err(e) = core::str::from_utf8(rest) { @@ -793,7 +817,7 @@ impl Wtf8 { let _ = Self::decode_surrogate(rest)?; rest = &rest[3..]; } - Some(unsafe { Wtf8::from_bytes_unchecked(b) }) + Some(unsafe { Self::from_bytes_unchecked(b) }) } fn decode_surrogate(b: &[u8]) -> Option { @@ -805,11 +829,13 @@ impl Wtf8 { /// Returns the length, in WTF-8 bytes. #[inline] + #[must_use] pub const fn len(&self) -> usize { self.bytes.len() } #[inline] + #[must_use] pub const fn is_empty(&self) -> bool { self.bytes.is_empty() } @@ -821,6 +847,7 @@ impl Wtf8 { /// /// Panics if `position` is beyond the end of the string. #[inline] + #[must_use] pub const fn ascii_byte_at(&self, position: usize) -> u8 { match self.bytes[position] { ascii_byte @ 0x00..=0x7F => ascii_byte, @@ -830,6 +857,7 @@ impl Wtf8 { /// Returns an iterator for the string’s code points. #[inline] + #[must_use] pub fn code_points(&self) -> Wtf8CodePoints<'_> { Wtf8CodePoints { bytes: self.bytes.iter(), @@ -838,6 +866,7 @@ impl Wtf8 { /// Returns an iterator for the string’s code points and their indices. #[inline] + #[must_use] pub fn code_point_indices(&self) -> Wtf8CodePointIndices<'_> { Wtf8CodePointIndices { front_offset: 0, @@ -847,6 +876,7 @@ impl Wtf8 { /// Access raw bytes of WTF-8 data #[inline] + #[must_use] pub const fn as_bytes(&self) -> &[u8] { &self.bytes } @@ -862,6 +892,7 @@ impl Wtf8 { } /// Creates an owned `Wtf8Buf` from a borrowed `Wtf8`. + #[must_use] pub fn to_wtf8_buf(&self) -> Wtf8Buf { Wtf8Buf { bytes: self.bytes.to_vec(), @@ -874,6 +905,7 @@ impl Wtf8 { /// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”). /// /// This only copies the data if necessary (if it contains any surrogate). + #[must_use] pub fn to_string_lossy(&self) -> Cow<'_, str> { let Some((surrogate_pos, _)) = self.next_surrogate(0) else { return Cow::Borrowed(unsafe { str::from_utf8_unchecked(&self.bytes) }); @@ -905,6 +937,7 @@ impl Wtf8 { /// calling `Wtf8Buf::from_ill_formed_utf16` on the resulting code units /// would always return the original WTF-8 string. #[inline] + #[must_use] pub fn encode_wide(&self) -> EncodeWide<'_> { EncodeWide { code_points: self.code_points(), @@ -912,6 +945,7 @@ impl Wtf8 { } } + #[must_use] pub const fn chunks(&self) -> Wtf8Chunks<'_> { Wtf8Chunks { wtf8: self } } @@ -956,19 +990,22 @@ impl Wtf8 { } } + #[must_use] pub fn is_code_point_boundary(&self, index: usize) -> bool { is_code_point_boundary(self, index) } /// Boxes this `Wtf8`. #[inline] - pub fn into_box(&self) -> Box { + #[must_use] + pub fn into_box(&self) -> Box { let boxed: Box<[u8]> = self.bytes.into(); unsafe { mem::transmute(boxed) } } /// Creates a boxed, empty `Wtf8`. - pub fn empty_box() -> Box { + #[must_use] + pub fn empty_box() -> Box { let boxed: Box<[u8]> = Default::default(); unsafe { mem::transmute(boxed) } } @@ -984,6 +1021,7 @@ impl Wtf8 { } #[inline] + #[must_use] pub fn to_ascii_lowercase(&self) -> Wtf8Buf { Wtf8Buf { bytes: self.bytes.to_ascii_lowercase(), @@ -991,12 +1029,14 @@ impl Wtf8 { } #[inline] + #[must_use] pub fn to_ascii_uppercase(&self) -> Wtf8Buf { Wtf8Buf { bytes: self.bytes.to_ascii_uppercase(), } } + #[must_use] pub fn to_lowercase(&self) -> Wtf8Buf { let mut buf = Wtf8Buf::with_capacity(self.len()); for chunk in self.chunks() { @@ -1008,6 +1048,7 @@ impl Wtf8 { buf } + #[must_use] pub fn to_uppercase(&self) -> Wtf8Buf { let mut buf = Wtf8Buf::with_capacity(self.len()); for chunk in self.chunks() { @@ -1020,57 +1061,63 @@ impl Wtf8 { } #[inline] + #[must_use] pub const fn is_ascii(&self) -> bool { self.bytes.is_ascii() } #[inline] + #[must_use] pub fn is_utf8(&self) -> bool { self.next_surrogate(0).is_none() } #[inline] + #[must_use] pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool { self.bytes.eq_ignore_ascii_case(&other.bytes) } - pub fn split(&self, pat: &Wtf8) -> impl Iterator { + pub fn split(&self, pat: &Self) -> impl Iterator { self.as_bytes() .split_str(pat) - .map(|w| unsafe { Wtf8::from_bytes_unchecked(w) }) + .map(|w| unsafe { Self::from_bytes_unchecked(w) }) } - pub fn splitn(&self, n: usize, pat: &Wtf8) -> impl Iterator { + pub fn splitn(&self, n: usize, pat: &Self) -> impl Iterator { self.as_bytes() .splitn_str(n, pat) - .map(|w| unsafe { Wtf8::from_bytes_unchecked(w) }) + .map(|w| unsafe { Self::from_bytes_unchecked(w) }) } - pub fn rsplit(&self, pat: &Wtf8) -> impl Iterator { + pub fn rsplit(&self, pat: &Self) -> impl Iterator { self.as_bytes() .rsplit_str(pat) - .map(|w| unsafe { Wtf8::from_bytes_unchecked(w) }) + .map(|w| unsafe { Self::from_bytes_unchecked(w) }) } - pub fn rsplitn(&self, n: usize, pat: &Wtf8) -> impl Iterator { + pub fn rsplitn(&self, n: usize, pat: &Self) -> impl Iterator { self.as_bytes() .rsplitn_str(n, pat) - .map(|w| unsafe { Wtf8::from_bytes_unchecked(w) }) + .map(|w| unsafe { Self::from_bytes_unchecked(w) }) } + #[must_use] pub fn trim(&self) -> &Self { let w = self.bytes.trim(); - unsafe { Wtf8::from_bytes_unchecked(w) } + unsafe { Self::from_bytes_unchecked(w) } } + #[must_use] pub fn trim_start(&self) -> &Self { let w = self.bytes.trim_start(); - unsafe { Wtf8::from_bytes_unchecked(w) } + unsafe { Self::from_bytes_unchecked(w) } } + #[must_use] pub fn trim_end(&self) -> &Self { let w = self.bytes.trim_end(); - unsafe { Wtf8::from_bytes_unchecked(w) } + unsafe { Self::from_bytes_unchecked(w) } } pub fn trim_start_matches(&self, f: impl Fn(CodePoint) -> bool) -> &Self { @@ -1109,26 +1156,30 @@ impl Wtf8 { self.trim_start_matches(&f).trim_end_matches(&f) } - pub fn find(&self, pat: &Wtf8) -> Option { + #[must_use] + pub fn find(&self, pat: &Self) -> Option { memchr::memmem::find(self.as_bytes(), pat.as_bytes()) } - pub fn rfind(&self, pat: &Wtf8) -> Option { + #[must_use] + pub fn rfind(&self, pat: &Self) -> Option { memchr::memmem::rfind(self.as_bytes(), pat.as_bytes()) } - pub fn find_iter(&self, pat: &Wtf8) -> impl Iterator { + pub fn find_iter(&self, pat: &Self) -> impl Iterator { memchr::memmem::find_iter(self.as_bytes(), pat.as_bytes()) } - pub fn rfind_iter(&self, pat: &Wtf8) -> impl Iterator { + pub fn rfind_iter(&self, pat: &Self) -> impl Iterator { memchr::memmem::rfind_iter(self.as_bytes(), pat.as_bytes()) } - pub fn contains(&self, pat: &Wtf8) -> bool { + #[must_use] + pub fn contains(&self, pat: &Self) -> bool { self.bytes.contains_str(pat) } + #[must_use] pub fn contains_code_point(&self, pat: CodePoint) -> bool { self.bytes .contains_str(pat.encode_wtf8(&mut [0; MAX_LEN_UTF8])) @@ -1154,27 +1205,28 @@ impl Wtf8 { } } - pub fn ends_with(&self, w: impl AsRef) -> bool { + pub fn ends_with(&self, w: impl AsRef) -> bool { self.bytes.ends_with_str(w.as_ref()) } - pub fn starts_with(&self, w: impl AsRef) -> bool { + pub fn starts_with(&self, w: impl AsRef) -> bool { self.bytes.starts_with_str(w.as_ref()) } - pub fn strip_prefix(&self, w: impl AsRef) -> Option<&Self> { + pub fn strip_prefix(&self, w: impl AsRef) -> Option<&Self> { self.bytes .strip_prefix(w.as_ref().as_bytes()) - .map(|w| unsafe { Wtf8::from_bytes_unchecked(w) }) + .map(|w| unsafe { Self::from_bytes_unchecked(w) }) } - pub fn strip_suffix(&self, w: impl AsRef) -> Option<&Self> { + pub fn strip_suffix(&self, w: impl AsRef) -> Option<&Self> { self.bytes .strip_suffix(w.as_ref().as_bytes()) - .map(|w| unsafe { Wtf8::from_bytes_unchecked(w) }) + .map(|w| unsafe { Self::from_bytes_unchecked(w) }) } - pub fn replace(&self, from: &Wtf8, to: &Wtf8) -> Wtf8Buf { + #[must_use] + pub fn replace(&self, from: &Self, to: &Self) -> Wtf8Buf { if from.is_empty() { return self.insert_at_boundaries(to, usize::MAX); } @@ -1182,7 +1234,8 @@ impl Wtf8 { unsafe { Wtf8Buf::from_bytes_unchecked(w) } } - pub fn replacen(&self, from: &Wtf8, to: &Wtf8, n: usize) -> Wtf8Buf { + #[must_use] + pub fn replacen(&self, from: &Self, to: &Self, n: usize) -> Wtf8Buf { if from.is_empty() { return self.insert_at_boundaries(to, n); } @@ -1198,7 +1251,7 @@ impl Wtf8 { /// *byte*, so it splits a multi-byte code point down the middle and leaves /// bytes that are no longer WTF-8 at all. A needle that is not empty is /// safe there, because a WTF-8 sequence never starts inside another one. - fn insert_at_boundaries(&self, to: &Wtf8, limit: usize) -> Wtf8Buf { + fn insert_at_boundaries(&self, to: &Self, limit: usize) -> Wtf8Buf { let mut result = Wtf8Buf::with_capacity(self.len()); let mut inserted = 0; @@ -1237,11 +1290,11 @@ impl AsRef<[u8]> for Wtf8 { /// Panics when `begin` and `end` do not point to code point boundaries, /// or point beyond the end of the string. impl ops::Index> for Wtf8 { - type Output = Wtf8; + type Output = Self; #[inline] #[track_caller] - fn index(&self, range: ops::Range) -> &Wtf8 { + fn index(&self, range: ops::Range) -> &Self { // is_code_point_boundary checks that the index is in [0, .len()] if range.start <= range.end && is_code_point_boundary(self, range.start) @@ -1261,11 +1314,11 @@ impl ops::Index> for Wtf8 { /// Panics when `begin` is not at a code point boundary, /// or is beyond the end of the string. impl ops::Index> for Wtf8 { - type Output = Wtf8; + type Output = Self; #[inline] #[track_caller] - fn index(&self, range: ops::RangeFrom) -> &Wtf8 { + fn index(&self, range: ops::RangeFrom) -> &Self { // is_code_point_boundary checks that the index is in [0, .len()] if is_code_point_boundary(self, range.start) { unsafe { slice_unchecked(self, range.start, self.len()) } @@ -1282,11 +1335,11 @@ impl ops::Index> for Wtf8 { /// Panics when `end` is not at a code point boundary, /// or is beyond the end of the string. impl ops::Index> for Wtf8 { - type Output = Wtf8; + type Output = Self; #[inline] #[track_caller] - fn index(&self, range: ops::RangeTo) -> &Wtf8 { + fn index(&self, range: ops::RangeTo) -> &Self { // is_code_point_boundary checks that the index is in [0, .len()] if is_code_point_boundary(self, range.end) { unsafe { slice_unchecked(self, 0, range.end) } @@ -1297,10 +1350,10 @@ impl ops::Index> for Wtf8 { } impl ops::Index for Wtf8 { - type Output = Wtf8; + type Output = Self; #[inline] - fn index(&self, _range: ops::RangeFull) -> &Wtf8 { + fn index(&self, _range: ops::RangeFull) -> &Self { self } } @@ -1351,9 +1404,10 @@ pub fn check_utf8_boundary(slice: &Wtf8, index: usize) { } if slice.bytes[index + 1] >= 0xA0 { // There's a surrogate after index. Now check before index. - if index >= 3 && slice.bytes[index - 3] == 0xED && slice.bytes[index - 2] >= 0xA0 { - panic!("byte index {index} lies between surrogate codepoints"); - } + assert!( + !(index >= 3 && slice.bytes[index - 3] == 0xED && slice.bytes[index - 2] >= 0xA0), + "byte index {index} lies between surrogate codepoints" + ); } } @@ -1363,6 +1417,7 @@ pub fn check_utf8_boundary(slice: &Wtf8, index: usize) { /// /// `begin` and `end` must be within bounds and on codepoint boundaries. #[inline] +#[must_use] pub const unsafe fn slice_unchecked(s: &Wtf8, begin: usize, end: usize) -> &Wtf8 { // SAFETY: memory layout of a &[u8] and &Wtf8 are the same unsafe { @@ -1431,6 +1486,7 @@ impl DoubleEndedIterator for Wtf8CodePoints<'_> { } impl<'a> Wtf8CodePoints<'a> { + #[must_use] pub fn as_wtf8(&self) -> &'a Wtf8 { unsafe { Wtf8::from_bytes_unchecked(self.bytes.as_slice()) } } @@ -1578,6 +1634,7 @@ impl Hash for CodePoint { /// # Safety /// /// `value` must be valid WTF-8. +#[must_use] pub unsafe fn from_boxed_wtf8_unchecked(value: Box<[u8]>) -> Box { unsafe { Box::from_raw(Box::into_raw(value) as *mut Wtf8) } } @@ -1628,7 +1685,7 @@ impl From> for Box { impl From> for Box<[u8]> { fn from(w: Box) -> Self { - unsafe { Box::from_raw(Box::into_raw(w) as *mut [u8]) } + unsafe { Self::from_raw(Box::into_raw(w) as *mut [u8]) } } } @@ -1640,7 +1697,7 @@ impl From for Box { impl From> for Wtf8Buf { fn from(w: Box) -> Self { - Wtf8Buf::from_box(w) + Self::from_box(w) } } diff --git a/examples/call_between_rust_and_python.rs b/examples/call_between_rust_and_python.rs index 3a7c0ce61..32b5a61b3 100644 --- a/examples/call_between_rust_and_python.rs +++ b/examples/call_between_rust_and_python.rs @@ -34,7 +34,7 @@ mod rust_py_module { s: String, python_person: PythonPerson, _vm: &VirtualMachine, - ) -> PyResult { + ) -> RustStruct { println!( "Calling standalone rust function from python passing args: num: {}, @@ -42,9 +42,9 @@ string: {}, python_person.name: {}", num, s, python_person.name ); - Ok(RustStruct { + RustStruct { numbers: NumVec(vec![1, 2, 3, 4]), - }) + } } #[derive(Debug, Clone)] diff --git a/examples/freeze/main.rs b/examples/freeze/main.rs index ab26a2bc8..ffbc45639 100644 --- a/examples/freeze/main.rs +++ b/examples/freeze/main.rs @@ -4,6 +4,7 @@ fn main() -> vm::PyResult<()> { vm::Interpreter::without_stdlib(Default::default()).enter(run) } +#[expect(clippy::unnecessary_wraps)] fn run(vm: &vm::VirtualMachine) -> vm::PyResult<()> { let scope = vm.new_scope_with_builtins(); diff --git a/examples/mini_repl.rs b/examples/mini_repl.rs index edbd6e149..1f8095aa9 100644 --- a/examples/mini_repl.rs +++ b/examples/mini_repl.rs @@ -6,7 +6,7 @@ use rustpython_vm as vm; // these are needed for special memory shenanigans to let us share a variable with Python and Rust -use std::sync::atomic::{AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, Ordering}; // This has to be a macro because it uses the py_compile macro, // which compiles python source to optimized bytecode at compile time, so that diff --git a/examples/parse_folder.rs b/examples/parse_folder.rs index 7ece0c740..05a3b2f36 100644 --- a/examples/parse_folder.rs +++ b/examples/parse_folder.rs @@ -8,11 +8,12 @@ extern crate env_logger; #[macro_use] extern crate log; +use core::time::Duration; use ruff_python_parser::parse_module; use rustpython_compiler::ast; use std::{ path::{Path, PathBuf}, - time::{Duration, Instant}, + time::Instant, }; fn main() { diff --git a/src/interpreter.rs b/src/interpreter.rs index b9346ca1b..a8b03cba0 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -6,6 +6,7 @@ pub trait InterpreterBuilderExt { /// /// Requires the `stdlib` feature to be enabled. #[cfg(feature = "stdlib")] + #[must_use] fn init_stdlib(self) -> Self; } diff --git a/src/lib.rs b/src/lib.rs index 918e33d79..ecad0a128 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,6 +84,7 @@ compile_error!( /// **Note**: This function provides no way to further initialize the VM after the builder is applied. /// All VM initialization (adding native modules, init hooks, etc.) must be done through the /// [`InterpreterBuilder`] parameter before calling this function. +#[must_use] pub fn run(mut builder: InterpreterBuilder) -> ExitCode { env_logger::init(); @@ -369,7 +370,7 @@ fn run_rustpython(vm: &VirtualMachine, run_mode: RunMode) -> PyResult<()> { RunMode::InstallPip(installer) => install_pip(installer, scope.clone(), vm), RunMode::Script(script_path) => { // pymain_run_file_obj - debug!("Running script {}", script_path); + debug!("Running script {script_path}"); run_file(vm, scope.clone(), &script_path) } RunMode::Repl => Ok(()), @@ -467,7 +468,7 @@ mod tests { } #[test] - fn test_run_script() { + fn run_script() { interpreter().enter(|vm| { vm.unwrap_pyresult((|| { let scope = vm.new_scope_with_main()?; diff --git a/src/main.rs b/src/main.rs index 01818ed97..32a8ccbdd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ use rustpython::{InterpreterBuilder, InterpreterBuilderExt}; #[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; +#[must_use] pub fn main() -> std::process::ExitCode { let mut config = InterpreterBuilder::new(); #[cfg(feature = "stdlib")] diff --git a/src/settings.rs b/src/settings.rs index da36d9888..975204c00 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,9 +1,10 @@ -use lexopt::Arg::*; +use core::cmp; +use core::num::NonZeroI32; +use core::str::FromStr; +use lexopt::Arg::{Long, Short, Value}; use lexopt::ValueExt; use rustpython_vm::{Settings, vm::CheckHashPycsMode}; -use std::num::NonZeroI32; -use std::str::FromStr; -use std::{cmp, env}; +use std::env; pub enum RunMode { Script(String), @@ -113,7 +114,7 @@ fn parse_args() -> Result<(CliArgs, RunMode, Vec), lexopt::Error> { let mut args = CliArgs::default(); let mut parser = lexopt::Parser::from_env(); fn argv(argv0: String, mut parser: lexopt::Parser) -> Result, lexopt::Error> { - std::iter::once(Ok(argv0)) + core::iter::once(Ok(argv0)) .chain(parser.raw_args()?.map(|arg| arg.string())) .collect() } @@ -532,7 +533,7 @@ fn dedent(input: &str) -> String { .collect(); // Find maximum common whitespace prefix, if any. - for line in deblanked.iter() { + for line in &deblanked { if line.is_empty() { continue; } diff --git a/src/shell/helper.rs b/src/shell/helper.rs index c84c5437d..15fc75563 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -6,7 +6,7 @@ use rustpython_vm::{ identifier, }; -pub struct ShellHelper<'vm> { +pub(super) struct ShellHelper<'vm> { vm: &'vm VirtualMachine, globals: PyDictRef, } @@ -54,7 +54,7 @@ fn split_idents_on_dot(line: &str) -> Option<(usize, Vec)> { } impl<'vm> ShellHelper<'vm> { - pub const fn new(vm: &'vm VirtualMachine, globals: PyDictRef) -> Self { + pub(super) const fn new(vm: &'vm VirtualMachine, globals: PyDictRef) -> Self { ShellHelper { vm, globals } } @@ -159,7 +159,7 @@ cfg_select! { &self, line: &str, pos: usize, - _ctx: &Context, + _ctx: &Context<'_>, ) -> rustyline::Result<(usize, Vec)> { Ok(self .complete_opt(&line[0..pos]) diff --git a/tests/benchmark_scripts.rs b/tests/benchmark_scripts.rs index 89166a025..786c70942 100644 --- a/tests/benchmark_scripts.rs +++ b/tests/benchmark_scripts.rs @@ -1,4 +1,7 @@ // cspell:ignore pyperformance nqueens pidigits + +#![allow(clippy::tests_outside_test_module)] + use rustpython::{InterpreterBuilder, InterpreterBuilderExt}; use std::{fs, path::Path};