66// positives.
77
88#![ feature( iter_collect_into) ]
9+ #![ feature( let_chains) ]
910#![ warn(
1011 trivial_casts,
1112 trivial_numeric_casts,
@@ -87,8 +88,6 @@ impl Crate {
8788 ) ;
8889 }
8990
90- let shared_target_dir = clippy_project_root ( ) . join ( "target/lintcheck/shared_target_dir" ) ;
91-
9291 let cargo_home = env ! ( "CARGO_HOME" ) ;
9392
9493 // `src/lib.rs` -> `target/lintcheck/sources/crate-1.2.3/src/lib.rs`
@@ -132,7 +131,7 @@ impl Crate {
132131 // The wrapper is set to `lintcheck` itself so we can force enable linting and ignore certain crates
133132 // (see `crate::driver`)
134133 let status = cmd
135- . env ( "CARGO_TARGET_DIR" , shared_target_dir. join ( "recursive" ) )
134+ . env ( "CARGO_TARGET_DIR" , shared_target_dir ( "recursive" ) )
136135 . env ( "RUSTC_WRAPPER" , env:: current_exe ( ) . unwrap ( ) )
137136 // Pass the absolute path so `crate::driver` can find `clippy-driver`, as it's executed in various
138137 // different working directories
@@ -150,9 +149,10 @@ impl Crate {
150149 cmd. arg ( "--message-format=json" ) ;
151150 }
152151
152+ let shared_target_dir = shared_target_dir ( & format ! ( "_{thread_index:?}" ) ) ;
153153 let all_output = cmd
154154 // use the looping index to create individual target dirs
155- . env ( "CARGO_TARGET_DIR" , shared_target_dir. join ( format ! ( "_{thread_index:?}" ) ) )
155+ . env ( "CARGO_TARGET_DIR" , shared_target_dir. as_os_str ( ) )
156156 // Roughly equivalent to `cargo clippy`/`cargo clippy --fix`
157157 . env ( "RUSTC_WORKSPACE_WRAPPER" , clippy_driver_path)
158158 . output ( )
@@ -186,7 +186,10 @@ impl Crate {
186186 // get all clippy warnings and ICEs
187187 let mut entries: Vec < ClippyCheckOutput > = Message :: parse_stream ( stdout. as_bytes ( ) )
188188 . filter_map ( |msg| match msg {
189- Ok ( Message :: CompilerMessage ( message) ) => ClippyWarning :: new ( message. message , & self . base_url ) ,
189+ Ok ( Message :: CompilerMessage ( message) ) => ClippyWarning :: new (
190+ normalize_diag ( message. message , shared_target_dir. to_str ( ) . unwrap ( ) ) ,
191+ & self . base_url ,
192+ ) ,
190193 _ => None ,
191194 } )
192195 . map ( ClippyCheckOutput :: ClippyWarning )
@@ -202,6 +205,31 @@ impl Crate {
202205 }
203206}
204207
208+ /// The target directory can sometimes be stored in the file name of spans.
209+ /// This is problematic since the directory in constructed from the thread
210+ /// ID and also used in our CI to determine if two lint emissions are the
211+ /// same or not. This function simply normalizes the `_<thread_id>` to `_*`.
212+ fn normalize_diag (
213+ mut message : cargo_metadata:: diagnostic:: Diagnostic ,
214+ thread_target_dir : & str ,
215+ ) -> cargo_metadata:: diagnostic:: Diagnostic {
216+ let mut dir_found = false ;
217+ message
218+ . spans
219+ . iter_mut ( )
220+ . filter ( |span| span. file_name . starts_with ( thread_target_dir) )
221+ . for_each ( |span| {
222+ dir_found = true ;
223+ span. file_name
224+ . replace_range ( 0 ..thread_target_dir. len ( ) , shared_target_dir ( "_*" ) . to_str ( ) . unwrap ( ) ) ;
225+ } ) ;
226+
227+ if dir_found && let Some ( rendered) = & mut message. rendered {
228+ * rendered = rendered. replace ( thread_target_dir, shared_target_dir ( "_*" ) . to_str ( ) . unwrap ( ) ) ;
229+ }
230+ message
231+ }
232+
205233/// Builds clippy inside the repo to make sure we have a clippy executable we can use.
206234fn build_clippy ( ) -> String {
207235 let output = Command :: new ( "cargo" )
@@ -388,6 +416,15 @@ fn clippy_project_root() -> &'static Path {
388416 Path :: new ( env ! ( "CARGO_MANIFEST_DIR" ) ) . parent ( ) . unwrap ( )
389417}
390418
419+ /// The qualifier can be used to separate different threads from another. By
420+ /// default it should be set to `_<thread_id>`
421+ #[ must_use]
422+ fn shared_target_dir ( qualifier : & str ) -> PathBuf {
423+ clippy_project_root ( )
424+ . join ( "target/lintcheck/shared_target_dir" )
425+ . join ( qualifier)
426+ }
427+
391428#[ test]
392429fn lintcheck_test ( ) {
393430 let args = [
0 commit comments