diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 5b9419b0..09b77dc1 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -43,8 +43,14 @@ jobs: - os: windows-latest host_target: i686-pc-windows-msvc runs-on: ${{ matrix.os }} + # Run tests under a directory with a space in it to double check the windows path heuristic + defaults: + run: + working-directory: "dir with spaces/ui test" steps: - uses: actions/checkout@v3 + with: + path: "dir with spaces/ui test" - uses: dtolnay/rust-toolchain@stable - name: Build run: cargo build --verbose diff --git a/Cargo.toml b/Cargo.toml index e624de7b..cd1ee493 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,8 +30,7 @@ distance = "0.4.0" [dependencies.regex] version = "1.5.5" default-features = false -# Features chosen to match those required by env_logger, to avoid rebuilds -features = ["perf", "std"] +features = ["perf", "std", "unicode-gencat"] [dependencies.color-eyre] version = "0.6.1" diff --git a/src/config.rs b/src/config.rs index 90cc6c78..dbdd6e17 100644 --- a/src/config.rs +++ b/src/config.rs @@ -17,11 +17,12 @@ pub struct Config { /// `None` to run on the host, otherwise a target triple pub target: Option, /// Filters applied to stderr output before processing it. - /// By default contains a filter for replacing backslashes with regular slashes. - /// On windows, contains a filter to replace `\n` with `\r\n`. + /// By default contains a filter for replacing backslashes in paths with + /// regular slashes. + /// On windows, contains a filter to remove `\r`. pub stderr_filters: Filter, /// Filters applied to stdout output before processing it. - /// On windows, contains a filter to replace `\n` with `\r\n`. + /// On windows, contains a filter to remove `\r`. pub stdout_filters: Filter, /// The folder in which to start searching for .rs files pub root_dir: PathBuf, @@ -58,7 +59,7 @@ impl Config { host: None, target: None, stderr_filters: vec![ - (Match::Exact(vec![b'\\']), b"/"), + (Match::PathBackslash, b"/"), #[cfg(windows)] (Match::Exact(vec![b'\r']), b""), ], diff --git a/src/lib.rs b/src/lib.rs index 5f0b1a53..60d2d400 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,8 +12,9 @@ use bstr::ByteSlice; pub use color_eyre; use color_eyre::eyre::{eyre, Result}; use crossbeam_channel::{unbounded, Receiver, Sender}; +use lazy_static::lazy_static; use parser::{ErrorMatch, Revisioned}; -use regex::bytes::Regex; +use regex::bytes::{Captures, Regex}; use rustc_stderr::{Diagnostics, Level, Message}; use status_emitter::StatusEmitter; use std::borrow::Cow; @@ -49,12 +50,33 @@ pub enum Match { Regex(Regex), /// If the exact byte sequence is found, the filter applies Exact(Vec), + /// Uses a heuristic to find backslashes in windows style paths + PathBackslash, } impl Match { fn replace_all<'a>(&self, text: &'a [u8], replacement: &[u8]) -> Cow<'a, [u8]> { match self { Match::Regex(regex) => regex.replace_all(text, replacement), Match::Exact(needle) => text.replace(needle, replacement).into(), + Match::PathBackslash => { + lazy_static! { + static ref PATH_RE: Regex = Regex::new( + r#"(?x) + (?: + # Match paths to files with extensions that don't include spaces + \\(?:[\pL\pN.\-_']+[/\\])*[\pL\pN.\-_']+\.\pL+ + | + # Allow spaces in absolute paths + [A-Z]:\\(?:[\pL\pN.\-_'\ ]+[/\\])+ + )"#, + ) + .unwrap(); + } + + PATH_RE.replace_all(text, |caps: &Captures<'_>| { + caps[0].replace(r"\", replacement) + }) + } } } } @@ -1051,8 +1073,8 @@ fn normalize( text = text.replace(lib_path, "RUSTLIB"); } - for (regex, replacement) in filters { - text = regex.replace_all(&text, replacement).into_owned(); + for (rule, replacement) in filters { + text = rule.replace_all(&text, replacement).into_owned(); } for (from, to) in comments diff --git a/tests/integrations/basic/Cargo.stderr b/tests/integrations/basic/Cargo.stderr index 4b1dc256..5aec599d 100644 --- a/tests/integrations/basic/Cargo.stderr +++ b/tests/integrations/basic/Cargo.stderr @@ -5,7 +5,8 @@ tests/actual_tests/executable.rs ... ok tests/actual_tests/foomp-rustfix.rs ... ok tests/actual_tests/foomp.rs ... ok tests/actual_tests/unicode.rs ... ok +tests/actual_tests/windows_paths.rs ... ok tests/actual_tests/subdir/aux_proc_macro.rs ... ok -test result: ok. 7 tests passed, 0 ignored, 0 filtered out +test result: ok. 8 tests passed, 0 ignored, 0 filtered out diff --git a/tests/integrations/basic/tests/actual_tests/windows_paths.rs b/tests/integrations/basic/tests/actual_tests/windows_paths.rs new file mode 100644 index 00000000..b1826f6d --- /dev/null +++ b/tests/integrations/basic/tests/actual_tests/windows_paths.rs @@ -0,0 +1,25 @@ +fn main() { + let () = "escapes stay as backslashes: \t\r\n"; + //~^ ERROR: mismatched types + + let () = r"absolute: C:\foo\file.rs"; + //~^ ERROR: mismatched types + let () = r"absolute, spaces: C:\foo bar\file.rs"; + //~^ ERROR: mismatched types + let () = r"absolute, spaces, dir: C:\foo bar\some dir\"; + //~^ ERROR: mismatched types + let () = r"absolute, spaces, no extension: C:\foo bar\some file"; + //~^ ERROR: mismatched types + + let () = r"relative: foo\file.rs"; + //~^ ERROR: mismatched types + + let () = r"unicode: Ryū\file.rs"; + //~^ ERROR: mismatched types + + let () = r"mixed seperators: C:\foo/../bar\"; + //~^ ERROR: mismatched types + + let () = r"unsupported: foo\bar"; + //~^ ERROR: mismatched types +} diff --git a/tests/integrations/basic/tests/actual_tests/windows_paths.stderr b/tests/integrations/basic/tests/actual_tests/windows_paths.stderr new file mode 100644 index 00000000..109a894c --- /dev/null +++ b/tests/integrations/basic/tests/actual_tests/windows_paths.stderr @@ -0,0 +1,75 @@ +error[E0308]: mismatched types + --> $DIR/windows_paths.rs:2:9 + | +2 | let () = "escapes stay as backslashes: \t\r\n"; + | ^^ ------------------------------------- this expression has type `&str` + | | + | expected `str`, found `()` + +error[E0308]: mismatched types + --> $DIR/windows_paths.rs:5:9 + | +5 | let () = r"absolute: C:/foo/file.rs"; + | ^^ --------------------------- this expression has type `&str` + | | + | expected `str`, found `()` + +error[E0308]: mismatched types + --> $DIR/windows_paths.rs:7:9 + | +7 | let () = r"absolute, spaces: C:/foo bar/file.rs"; + | ^^ --------------------------------------- this expression has type `&str` + | | + | expected `str`, found `()` + +error[E0308]: mismatched types + --> $DIR/windows_paths.rs:9:9 + | +9 | let () = r"absolute, spaces, dir: C:/foo bar/some dir/"; + | ^^ ---------------------------------------------- this expression has type `&str` + | | + | expected `str`, found `()` + +error[E0308]: mismatched types + --> $DIR/windows_paths.rs:11:9 + | +11 | let () = r"absolute, spaces, no extension: C:/foo bar/some file"; + | ^^ ------------------------------------------------------- this expression has type `&str` + | | + | expected `str`, found `()` + +error[E0308]: mismatched types + --> $DIR/windows_paths.rs:14:9 + | +14 | let () = r"relative: foo/file.rs"; + | ^^ ------------------------ this expression has type `&str` + | | + | expected `str`, found `()` + +error[E0308]: mismatched types + --> $DIR/windows_paths.rs:17:9 + | +17 | let () = r"unicode: Ryū/file.rs"; + | ^^ ----------------------- this expression has type `&str` + | | + | expected `str`, found `()` + +error[E0308]: mismatched types + --> $DIR/windows_paths.rs:20:9 + | +20 | let () = r"mixed seperators: C:/foo/../bar/"; + | ^^ ----------------------------------- this expression has type `&str` + | | + | expected `str`, found `()` + +error[E0308]: mismatched types + --> $DIR/windows_paths.rs:23:9 + | +23 | let () = r"unsupported: foo\bar"; + | ^^ ----------------------- this expression has type `&str` + | | + | expected `str`, found `()` + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0308`.