diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ef492d48..051bf7bd 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -12,7 +12,6 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Use descriptive names for variables and closure parameters. Single letters are permitted only in these cases: (1) conventional names like `n` for count or `f` for formatter; (2) comparison closures like `|a, b|`; (3) trivial single-expression closures; (4) fold accumulators; (5) index variables `i`/`j`/`k` in short closures or index-based loops; and (6) test fixtures with identical roles. Single letters are never permitted in multi-line functions or closures. - Use `pipe-trait` to chain through unary functions such as constructors, `Some`, `Ok`, and free functions. Use it to flatten nested calls and to continue method chains. Do not use it for simple standalone calls; prefer `foo(value)` over `value.pipe(foo)`. - Prefer `where` clauses when a type has multiple trait bounds. -- Derive order: standard traits, then comparison traits, then `Hash`, then `derive_more`, then feature-gated derives. - For error types, only derive `Display` and `Error` from `derive_more` when each is actually needed. Not all displayable types are errors. - Minimize `unwrap()` in non-test code. Use proper error handling instead. - Prefer `#[cfg_attr(..., ignore = "reason")]` over `#[cfg(...)]` when skipping tests. Use `#[cfg]` on tests only when the code cannot compile under the condition, such as when it references types or functions that do not exist on other platforms. diff --git a/AGENTS.md b/AGENTS.md index ef492d48..051bf7bd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,7 +12,6 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Use descriptive names for variables and closure parameters. Single letters are permitted only in these cases: (1) conventional names like `n` for count or `f` for formatter; (2) comparison closures like `|a, b|`; (3) trivial single-expression closures; (4) fold accumulators; (5) index variables `i`/`j`/`k` in short closures or index-based loops; and (6) test fixtures with identical roles. Single letters are never permitted in multi-line functions or closures. - Use `pipe-trait` to chain through unary functions such as constructors, `Some`, `Ok`, and free functions. Use it to flatten nested calls and to continue method chains. Do not use it for simple standalone calls; prefer `foo(value)` over `value.pipe(foo)`. - Prefer `where` clauses when a type has multiple trait bounds. -- Derive order: standard traits, then comparison traits, then `Hash`, then `derive_more`, then feature-gated derives. - For error types, only derive `Display` and `Error` from `derive_more` when each is actually needed. Not all displayable types are errors. - Minimize `unwrap()` in non-test code. Use proper error handling instead. - Prefer `#[cfg_attr(..., ignore = "reason")]` over `#[cfg(...)]` when skipping tests. Use `#[cfg]` on tests only when the code cannot compile under the condition, such as when it references types or functions that do not exist on other platforms. diff --git a/CLAUDE.md b/CLAUDE.md index a6ba4eee..9eabaa1d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,7 +12,6 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Use descriptive names for variables and closure parameters. Single letters are permitted only in these cases: (1) conventional names like `n` for count or `f` for formatter; (2) comparison closures like `|a, b|`; (3) trivial single-expression closures; (4) fold accumulators; (5) index variables `i`/`j`/`k` in short closures or index-based loops; and (6) test fixtures with identical roles. Single letters are never permitted in multi-line functions or closures. - Use `pipe-trait` to chain through unary functions such as constructors, `Some`, `Ok`, and free functions. Use it to flatten nested calls and to continue method chains. Do not use it for simple standalone calls; prefer `foo(value)` over `value.pipe(foo)`. - Prefer `where` clauses when a type has multiple trait bounds. -- Derive order: standard traits, then comparison traits, then `Hash`, then `derive_more`, then feature-gated derives. - For error types, only derive `Display` and `Error` from `derive_more` when each is actually needed. Not all displayable types are errors. - Minimize `unwrap()` in non-test code. Use proper error handling instead. - Prefer `#[cfg_attr(..., ignore = "reason")]` over `#[cfg(...)]` when skipping tests. Use `#[cfg]` on tests only when the code cannot compile under the condition, such as when it references types or functions that do not exist on other platforms. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a3c570c4..d6859b25 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -71,16 +71,12 @@ pub use event::Event; ### Derive Macro Ordering -When deriving multiple traits, use this order and split across multiple `#[derive(...)]` lines for readability: +The order of trait names within each `#[derive(...)]` attribute is enforced automatically by the `perfectionist::derive_ordering` rule, configured for the `prefix_then_alphabetical` style. The configured `prefix` in `dylint.toml` lists the trait families in their project-preferred order: `Debug`, formatting / error derives (`Display`, `Error`), defaults (`Default`, `SmartDefault`), `Clone` / `Copy`, comparison and `Hash`, reference wrappers (`AsRef`, `AsMut`, `Deref`, `DerefMut`), conversions (`From`, `Into`, `TryFrom`, `TryInto`, `FromStr`), iteration, arithmetic operator pairs and folds, and integer-format derives. Any trait that is not in the `prefix` (project-specific derives such as `Setters` and `Parser`) falls in ASCII-case-insensitive alphabetical order after the prefix entries. -1. **Standard traits:** `Debug`, `Default`, `Clone`, `Copy` -2. **Comparison traits:** `PartialEq`, `Eq`, `PartialOrd`, `Ord` -3. **Hash** -4. **`derive_more` traits:** `Display`, `From`, `Into`, `Add`, `AddAssign`, etc. -5. **Feature-gated derives** on a separate `#[cfg_attr(...)]` line +The remaining conventions are not enforced by the rule and must be applied by hand. When a type derives many traits, split them across multiple `#[derive(...)]` lines for readability, and place feature-gated derives on a separate `#[cfg_attr(...)]` line. ```rust -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Display, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(From, Into, Add, AddAssign, Sub, SubAssign, Sum)] #[cfg_attr(feature = "json", derive(Deserialize, Serialize))] pub struct Bytes(u64); @@ -92,11 +88,11 @@ Use **descriptive names** for type parameters, not single letters: - `Size`, `Name`, `SizeGetter`, `HardlinksRecorder`, `Report` -Single-letter generics are acceptable only in very short, self-contained trait impls. +Single-letter generics are acceptable only in very short, self-contained trait impls. Enforced by `perfectionist::single_letter_generic`; the threshold for "very short" is the rule's `short_impl_max_lines` knob in `dylint.toml`. ### Variable and Closure Parameter Naming -Use **descriptive names** for variables and closure parameters by default. Single-letter names are permitted only in the specific cases listed below. +Use **descriptive names** for variables and closure parameters by default. Single-letter names are permitted only in the specific cases listed below. Enforced by `perfectionist::single_letter_let_binding`, `perfectionist::single_letter_function_param`, and `perfectionist::single_letter_closure_param`; the per-rule `allowed_idents` and `extra_trivial_callback_methods` knobs in `dylint.toml` reflect the exceptions documented here. #### When single-letter names are allowed diff --git a/Cargo.toml b/Cargo.toml index d5e761cc..83dd73d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,6 +88,9 @@ terminal_size = "0.4.4" text-block-macros = "0.2.0" zero-copy-pads = "0.2.0" +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(dylint_lib, values("perfectionist"))'] } + [dev-dependencies] build-fs-tree = "0.8.1" command-extra = "1.0.0" diff --git a/dylint.toml b/dylint.toml index d91405ef..f792a3fb 100644 --- a/dylint.toml +++ b/dylint.toml @@ -1,4 +1,26 @@ [workspace.metadata.dylint] libraries = [ - { git = "https://github.com/KSXGitHub/perfectionist", tag = "0.0.0-rc.7" }, + { git = "https://github.com/KSXGitHub/perfectionist", tag = "0.0.0-rc.14" }, ] + +["perfectionist::derive_ordering"] +style = "prefix_then_alphabetical" +prefix = [ + "Debug", "Display", "Error", + "Default", "SmartDefault", + "Clone", "Copy", + "PartialEq", "Eq", "PartialOrd", "Ord", + "Hash", + "AsRef", "AsMut", "Deref", "DerefMut", + "From", "Into", "TryFrom", "TryInto", "FromStr", + "IntoIterator", + "Add", "AddAssign", "Sub", "SubAssign", "Mul", "MulAssign", "Div", "DivAssign", + "Sum", "Product", + "LowerHex", "UpperHex", "Octal", +] + +["perfectionist::macro_argument_binding"] +deny_extra = ["debug_assert_op", "debug_assert_op_expr"] + +["perfectionist::single_letter_closure_param"] +extra_trivial_callback_methods = ["sort_reflection_by"] diff --git a/src/app/overlapping_arguments.rs b/src/app/overlapping_arguments.rs index ee2966a3..328b55bb 100644 --- a/src/app/overlapping_arguments.rs +++ b/src/app/overlapping_arguments.rs @@ -14,7 +14,7 @@ pub trait Api { type RealPathError; fn canonicalize(path: &Self::Argument) -> Result; fn is_real_dir(path: &Self::Argument) -> bool; - fn starts_with(a: &Self::RealPath, b: &Self::RealPath) -> bool; + fn starts_with(path: &Self::RealPath, prefix: &Self::RealPath) -> bool; } /// Implementation of [`Api`] that interacts with the real system. @@ -36,8 +36,8 @@ impl Api for RealApi { } #[inline] - fn starts_with(a: &Self::RealPath, b: &Self::RealPath) -> bool { - a.starts_with(b) + fn starts_with(path: &Self::RealPath, prefix: &Self::RealPath) -> bool { + path.starts_with(prefix) } } diff --git a/src/app/overlapping_arguments/test_remove_overlapping_paths.rs b/src/app/overlapping_arguments/test_remove_overlapping_paths.rs index 2a8a0e3c..7cbab6d2 100644 --- a/src/app/overlapping_arguments/test_remove_overlapping_paths.rs +++ b/src/app/overlapping_arguments/test_remove_overlapping_paths.rs @@ -64,8 +64,8 @@ impl Api for MockedApi { .all(|(link, _)| PathBuf::from(link).normalize() != path) } - fn starts_with(a: &Self::RealPath, b: &Self::RealPath) -> bool { - a.starts_with(b) + fn starts_with(path: &Self::RealPath, prefix: &Self::RealPath) -> bool { + path.starts_with(prefix) } } diff --git a/src/args.rs b/src/args.rs index b2713d9f..6ca380c5 100644 --- a/src/args.rs +++ b/src/args.rs @@ -17,7 +17,7 @@ use terminal_size::{Width, terminal_size}; use text_block_macros::text_block; /// The CLI arguments. -#[derive(Debug, SmartDefault, Setters, Clone, Parser)] +#[derive(Debug, SmartDefault, Clone, Parser, Setters)] #[clap( name = "pdu", diff --git a/src/args/depth.rs b/src/args/depth.rs index d4d5a4b1..375570a7 100644 --- a/src/args/depth.rs +++ b/src/args/depth.rs @@ -7,7 +7,7 @@ use std::{ const INFINITE: &str = "inf"; /// Maximum depth of the tree. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Display)] +#[derive(Debug, Display, Clone, Copy, PartialEq, Eq)] pub enum Depth { #[display("{INFINITE}")] Infinite, @@ -25,7 +25,7 @@ impl Depth { } /// Error that occurs when parsing a string as [`Depth`]. -#[derive(Debug, Display, Clone, PartialEq, Eq, Error)] +#[derive(Debug, Display, Error, Clone, PartialEq, Eq)] #[non_exhaustive] pub enum FromStrError { #[display("Value is neither {INFINITE:?} nor a positive integer: {_0}")] diff --git a/src/args/fraction.rs b/src/args/fraction.rs index 4145b74f..8a1639fc 100644 --- a/src/args/fraction.rs +++ b/src/args/fraction.rs @@ -6,11 +6,11 @@ use std::{ }; /// Floating-point value that is greater than or equal to 0 and less than 1. -#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd, AsRef, Deref, Display, Into)] +#[derive(Debug, Display, Default, Clone, Copy, PartialEq, PartialOrd, AsRef, Deref, Into)] pub struct Fraction(f32); /// Error that occurs when calling [`Fraction::new`]. -#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Error)] +#[derive(Debug, Display, Error, Clone, Copy, PartialEq, Eq)] pub enum ConversionError { /// Provided value is greater than or equal to 1. #[display("greater than or equal to 1")] @@ -42,7 +42,7 @@ impl TryFrom for Fraction { } /// Error that occurs when parsing a string as [`Fraction`]. -#[derive(Debug, Display, Clone, PartialEq, Eq, Error)] +#[derive(Debug, Display, Error, Clone, PartialEq, Eq)] pub enum FromStrError { ParseFloatError(ParseFloatError), Conversion(ConversionError), diff --git a/src/args/threads.rs b/src/args/threads.rs index 44810350..8f058b13 100644 --- a/src/args/threads.rs +++ b/src/args/threads.rs @@ -8,7 +8,7 @@ const AUTO: &str = "auto"; const MAX: &str = "max"; /// Number of rayon threads. -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Display)] +#[derive(Debug, Display, Default, Clone, Copy, PartialEq, Eq)] pub enum Threads { #[default] #[display("{AUTO}")] @@ -19,7 +19,7 @@ pub enum Threads { } /// Error that occurs when parsing a string as [`Threads`]. -#[derive(Debug, Display, Clone, PartialEq, Eq, Error)] +#[derive(Debug, Display, Error, Clone, PartialEq, Eq)] #[non_exhaustive] pub enum FromStrError { #[display("Value is neither {AUTO:?}, {MAX:?}, nor a number: {_0}")] diff --git a/src/bytes_format/parsed_value.rs b/src/bytes_format/parsed_value.rs index 89d8d3e0..d8aad461 100644 --- a/src/bytes_format/parsed_value.rs +++ b/src/bytes_format/parsed_value.rs @@ -1,7 +1,7 @@ -use derive_more::{Display, Error}; +use derive_more::Display; /// Return value of [`Formatter::parse_value`](super::Formatter::parse_value). -#[derive(Debug, Display, Clone, Copy, Error)] +#[derive(Debug, Display, Clone, Copy)] pub enum ParsedValue { /// When input value is less than `scale_base`. #[display("{value} ")] diff --git a/src/device.rs b/src/device.rs index 545cce12..8b43d9ba 100644 --- a/src/device.rs +++ b/src/device.rs @@ -23,7 +23,7 @@ impl DeviceBoundary { /// The device number of a filesystem. #[derive( - Debug, Display, LowerHex, UpperHex, Octal, Clone, Copy, PartialEq, Eq, Hash, From, Into, + Debug, Display, Clone, Copy, PartialEq, Eq, Hash, From, Into, LowerHex, UpperHex, Octal, )] #[cfg_attr(feature = "json", derive(Deserialize, Serialize))] pub struct DeviceNumber(u64); diff --git a/src/hardlink/hardlink_list.rs b/src/hardlink/hardlink_list.rs index 8879567d..8fe6dfd7 100644 --- a/src/hardlink/hardlink_list.rs +++ b/src/hardlink/hardlink_list.rs @@ -21,7 +21,7 @@ use pipe_trait::Pipe; use std::path::Path; /// Internal key used to uniquely identify an inode across all filesystems. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] struct InodeKey { /// Inode number within the device. ino: InodeNumber, diff --git a/src/hardlink/hardlink_list/summary.rs b/src/hardlink/hardlink_list/summary.rs index b6ca4aaa..79871e05 100644 --- a/src/hardlink/hardlink_list/summary.rs +++ b/src/hardlink/hardlink_list/summary.rs @@ -11,7 +11,7 @@ use std::{ use serde::{Deserialize, Serialize}; /// Summary from [`HardlinkList`] or [`Reflection`]. -#[derive(Debug, Default, Setters, Clone, Copy, PartialEq, Eq, Add, AddAssign, Sum)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Add, AddAssign, Sum, Setters)] #[cfg_attr(feature = "json", derive(Deserialize, Serialize))] #[setters(prefix = "with_")] #[non_exhaustive] diff --git a/src/inode.rs b/src/inode.rs index 2305dfb8..3842ad51 100644 --- a/src/inode.rs +++ b/src/inode.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; /// The inode number of a file or directory. #[derive( - Debug, Display, LowerHex, UpperHex, Octal, Clone, Copy, PartialEq, Eq, Hash, From, Into, + Debug, Display, Clone, Copy, PartialEq, Eq, Hash, From, Into, LowerHex, UpperHex, Octal, )] #[cfg_attr(feature = "json", derive(Deserialize, Serialize))] pub struct InodeNumber(u64); diff --git a/src/json_data/binary_version.rs b/src/json_data/binary_version.rs index 6b520dea..1fcb077a 100644 --- a/src/json_data/binary_version.rs +++ b/src/json_data/binary_version.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; pub const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION"); /// Version of the `pdu` program that created the input JSON. -#[derive(Debug, Clone, PartialEq, Eq, AsMut, AsRef, From, FromStr, Into)] +#[derive(Debug, Clone, PartialEq, Eq, AsRef, AsMut, From, Into, FromStr)] #[cfg_attr(feature = "json", derive(Deserialize, Serialize))] pub struct BinaryVersion(String); diff --git a/src/lib.rs b/src/lib.rs index f5a4d044..ee7ba0e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,8 @@ //! [`tree_builder::TreeBuilder`], [`data_tree::DataTree`], or [`visualizer::Visualizer`]. #![deny(warnings)] +#![cfg_attr(dylint_lib = "perfectionist", feature(register_tool))] +#![cfg_attr(dylint_lib = "perfectionist", register_tool(perfectionist))] #[cfg(feature = "json")] pub use serde; diff --git a/src/os_string_display.rs b/src/os_string_display.rs index e713aff6..e729db56 100644 --- a/src/os_string_display.rs +++ b/src/os_string_display.rs @@ -20,8 +20,8 @@ use serde::{Deserialize, Serialize}; Eq, PartialOrd, Ord, - AsMut, AsRef, + AsMut, Deref, DerefMut, From, diff --git a/src/reporter/error_report/text_report.rs b/src/reporter/error_report/text_report.rs index 48203d6b..75510e58 100644 --- a/src/reporter/error_report/text_report.rs +++ b/src/reporter/error_report/text_report.rs @@ -42,6 +42,6 @@ fn test() { ), }; let actual = TextReport(report).to_string(); - let expected = "[error] read_dir \"path/to/a/directory\": Something goes wrong (os error 420)"; + let expected = r#"[error] read_dir "path/to/a/directory": Something goes wrong (os error 420)"#; assert_eq!(actual, expected); } diff --git a/src/reporter/progress_and_error_reporter.rs b/src/reporter/progress_and_error_reporter.rs index 529f1dc3..04e3daca 100644 --- a/src/reporter/progress_and_error_reporter.rs +++ b/src/reporter/progress_and_error_reporter.rs @@ -47,7 +47,7 @@ where ReportProgress: Fn(ProgressReport) + Send + Sync + 'static, { let progress = Arc::new(ProgressReportState::default()); - let progress_thread = progress.clone(); + let progress_thread = Arc::clone(&progress); let progress_reporter_handle = spawn(move || { loop { sleep(progress_report_interval); diff --git a/src/reporter/progress_report.rs b/src/reporter/progress_report.rs index 6aa690b4..cbb875e5 100644 --- a/src/reporter/progress_report.rs +++ b/src/reporter/progress_report.rs @@ -3,7 +3,7 @@ use derive_setters::Setters; use std::fmt::Write; /// Scan progress. -#[derive(Debug, Default, Setters, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Setters)] #[setters(prefix = "with_")] pub struct ProgressReport { /// Number of scanned items. diff --git a/src/usage_md.rs b/src/usage_md.rs index 0e6e6445..d718568d 100644 --- a/src/usage_md.rs +++ b/src/usage_md.rs @@ -85,7 +85,7 @@ fn render_argument(out: &mut String, arg: &Arg) { .unwrap_or_else(|| arg.get_id().as_str()); let is_multiple = arg .get_num_args() - .map(|r| r.max_values() > 1) + .map(|range| range.max_values() > 1) .unwrap_or(false); let display_name = if arg.is_required_set() { if is_multiple { diff --git a/src/visualizer/proportion_bar.rs b/src/visualizer/proportion_bar.rs index 9e9cd19a..31a09c3c 100644 --- a/src/visualizer/proportion_bar.rs +++ b/src/visualizer/proportion_bar.rs @@ -4,7 +4,7 @@ use fmt_iter::repeat; use std::fmt::{Display, Error, Formatter}; /// Block of proportion bar. -#[derive(Debug, Clone, Copy, PartialEq, Eq, AsRef, Deref, Display, Into)] +#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, AsRef, Deref, Into)] pub struct ProportionBarBlock(char); macro_rules! make_const { diff --git a/src/visualizer/tree.rs b/src/visualizer/tree.rs index 133b58c1..ba0200b4 100644 --- a/src/visualizer/tree.rs +++ b/src/visualizer/tree.rs @@ -18,7 +18,7 @@ pub struct TreeSkeletalComponent { } /// String made by calling [`TreeSkeletalComponent::visualize`](TreeSkeletalComponent). -#[derive(Debug, Clone, Copy, PartialEq, Eq, AsRef, Deref, Display, Into)] +#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, AsRef, Deref, Into)] pub struct TreeSkeletalComponentVisualization(&'static str); impl TreeSkeletalComponent { diff --git a/template/ai-instructions/shared.md b/template/ai-instructions/shared.md index ef492d48..051bf7bd 100644 --- a/template/ai-instructions/shared.md +++ b/template/ai-instructions/shared.md @@ -12,7 +12,6 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Use descriptive names for variables and closure parameters. Single letters are permitted only in these cases: (1) conventional names like `n` for count or `f` for formatter; (2) comparison closures like `|a, b|`; (3) trivial single-expression closures; (4) fold accumulators; (5) index variables `i`/`j`/`k` in short closures or index-based loops; and (6) test fixtures with identical roles. Single letters are never permitted in multi-line functions or closures. - Use `pipe-trait` to chain through unary functions such as constructors, `Some`, `Ok`, and free functions. Use it to flatten nested calls and to continue method chains. Do not use it for simple standalone calls; prefer `foo(value)` over `value.pipe(foo)`. - Prefer `where` clauses when a type has multiple trait bounds. -- Derive order: standard traits, then comparison traits, then `Hash`, then `derive_more`, then feature-gated derives. - For error types, only derive `Display` and `Error` from `derive_more` when each is actually needed. Not all displayable types are errors. - Minimize `unwrap()` in non-test code. Use proper error handling instead. - Prefer `#[cfg_attr(..., ignore = "reason")]` over `#[cfg(...)]` when skipping tests. Use `#[cfg]` on tests only when the code cannot compile under the condition, such as when it references types or functions that do not exist on other platforms. diff --git a/tests/cli_errors.rs b/tests/cli_errors.rs index 5c863ff8..0f9a7b3f 100644 --- a/tests/cli_errors.rs +++ b/tests/cli_errors.rs @@ -168,8 +168,8 @@ fn fs_errors() { .map(|line| line.trim_start_matches('\r')) .collect(); let expected_stderr_lines = btreeset! { - "[error] read_dir \"./nested/0\": Permission denied (os error 13)", - "[error] read_dir \"./empty-dir\": Permission denied (os error 13)", + r#"[error] read_dir "./nested/0": Permission denied (os error 13)"#, + r#"[error] read_dir "./empty-dir": Permission denied (os error 13)"#, }; assert_eq!(actual_stderr_lines, expected_stderr_lines);