From bc389539a64773fc7b28ecaa30875cbe2bbfd971 Mon Sep 17 00:00:00 2001 From: Joe Neeman Date: Thu, 16 Oct 2025 11:41:52 -0500 Subject: [PATCH] Port the output-filename-collision message --- src/cargo/core/compiler/build_runner/mod.rs | 100 ++++++++++---------- tests/testsuite/collisions.rs | 63 ++++++------ tests/testsuite/doc.rs | 40 ++++---- 3 files changed, 95 insertions(+), 108 deletions(-) diff --git a/src/cargo/core/compiler/build_runner/mod.rs b/src/cargo/core/compiler/build_runner/mod.rs index 1bee67f23..4bd2f732f 100644 --- a/src/cargo/core/compiler/build_runner/mod.rs +++ b/src/cargo/core/compiler/build_runner/mod.rs @@ -9,6 +9,7 @@ use crate::core::compiler::compilation::{self, UnitOutput}; use crate::core::compiler::{self, Unit, artifact}; use crate::util::cache_lock::CacheLockMode; use crate::util::errors::CargoResult; +use annotate_snippets::{Level, Message}; use anyhow::{Context as _, bail}; use cargo_util::paths; use filetime::FileTime; @@ -515,58 +516,56 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> { #[tracing::instrument(skip_all)] fn check_collisions(&self) -> CargoResult<()> { let mut output_collisions = HashMap::new(); - let describe_collision = |unit: &Unit, other_unit: &Unit, path: &PathBuf| -> String { + let describe_collision = |unit: &Unit, other_unit: &Unit| -> String { format!( - "The {} target `{}` in package `{}` has the same output \ - filename as the {} target `{}` in package `{}`.\n\ - Colliding filename is: {}\n", + "the {} target `{}` in package `{}` has the same output filename as the {} target `{}` in package `{}`", unit.target.kind().description(), unit.target.name(), unit.pkg.package_id(), other_unit.target.kind().description(), other_unit.target.name(), other_unit.pkg.package_id(), - path.display() ) }; - let suggestion = "Consider changing their names to be unique or compiling them separately.\n\ - This may become a hard error in the future; see \ - ."; - let rustdoc_suggestion = "This is a known bug where multiple crates with the same name use\n\ - the same path; see ."; + let suggestion = [ + Level::NOTE.message("this may become a hard error in the future; see "), + Level::HELP.message("consider changing their names to be unique or compiling them separately") + ]; + let rustdoc_suggestion = [ + Level::NOTE.message("this is a known bug where multiple crates with the same name use the same path; see ") + ]; let report_collision = |unit: &Unit, other_unit: &Unit, path: &PathBuf, - suggestion: &str| + messages: &[Message<'_>]| -> CargoResult<()> { if unit.target.name() == other_unit.target.name() { - self.bcx.gctx.shell().warn(format!( - "output filename collision.\n\ - {}\ - The targets should have unique names.\n\ - {}", - describe_collision(unit, other_unit, path), - suggestion - )) + self.bcx.gctx.shell().print_report( + &[Level::WARNING + .secondary_title(format!("output filename collision at {}", path.display())) + .elements( + [Level::NOTE.message(describe_collision(unit, other_unit))] + .into_iter() + .chain(messages.iter().cloned()), + )], + false, + ) } else { - self.bcx.gctx.shell().warn(format!( - "output filename collision.\n\ - {}\ - The output filenames should be unique.\n\ - {}\n\ - If this looks unexpected, it may be a bug in Cargo. Please file a bug report at\n\ - https://github.com/rust-lang/cargo/issues/ with as much information as you\n\ - can provide.\n\ - cargo {} running on `{}` target `{}`\n\ - First unit: {:?}\n\ - Second unit: {:?}", - describe_collision(unit, other_unit, path), - suggestion, - crate::version(), - self.bcx.host_triple(), - self.bcx.target_data.short_name(&unit.kind), - unit, - other_unit)) + self.bcx.gctx.shell().print_report( + &[Level::WARNING + .secondary_title(format!("output filename collision at {}", path.display())) + .elements([ + Level::NOTE.message(describe_collision(unit, other_unit)), + Level::NOTE.message("if this looks unexpected, it may be a bug in Cargo. Please file a bug \ + report at https://github.com/rust-lang/cargo/issues/ with as much information as you \ + can provide."), + Level::NOTE.message(format!("cargo {} running on `{}` target `{}`", + crate::version(), self.bcx.host_triple(), self.bcx.target_data.short_name(&unit.kind))), + Level::NOTE.message(format!("first unit: {unit:?}")), + Level::NOTE.message(format!("second unit: {other_unit:?}")), + ])], + false, + ) } }; @@ -623,26 +622,31 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> { if unit.mode.is_doc() { // See https://github.com/rust-lang/rust/issues/56169 // and https://github.com/rust-lang/rust/issues/61378 - report_collision(unit, other_unit, &output.path, rustdoc_suggestion)?; + report_collision(unit, other_unit, &output.path, &rustdoc_suggestion)?; } else { - report_collision(unit, other_unit, &output.path, suggestion)?; + report_collision(unit, other_unit, &output.path, &suggestion)?; } } if let Some(hardlink) = output.hardlink.as_ref() { if let Some(other_unit) = output_collisions.insert(hardlink.clone(), unit) { - report_collision(unit, other_unit, hardlink, suggestion)?; + report_collision(unit, other_unit, hardlink, &suggestion)?; } } if let Some(ref export_path) = output.export_path { if let Some(other_unit) = output_collisions.insert(export_path.clone(), unit) { - self.bcx.gctx.shell().warn(format!( - "`--artifact-dir` filename collision.\n\ - {}\ - The exported filenames should be unique.\n\ - {}", - describe_collision(unit, other_unit, export_path), - suggestion - ))?; + self.bcx.gctx.shell().print_report( + &[Level::WARNING + .secondary_title(format!( + "`--artifact-dir` filename collision at {}", + export_path.display() + )) + .elements( + [Level::NOTE.message(describe_collision(unit, other_unit))] + .into_iter() + .chain(suggestion.iter().cloned()), + )], + false, + )?; } } } diff --git a/tests/testsuite/collisions.rs b/tests/testsuite/collisions.rs index 2062913c2..7a54f57f1 100644 --- a/tests/testsuite/collisions.rs +++ b/tests/testsuite/collisions.rs @@ -55,12 +55,11 @@ fn collision_dylib() { p.cargo("build -j=1") .with_stderr_data(&format!("\ ... -[WARNING] output filename collision. -The lib target `a` in package `b v1.0.0 ([ROOT]/foo/b)` has the same output filename as the lib target `a` in package `a v1.0.0 ([ROOT]/foo/a)`. -Colliding filename is: [ROOT]/foo/target/debug/deps/{}a{} -The targets should have unique names. -Consider changing their names to be unique or compiling them separately. -This may become a hard error in the future; see . +[WARNING] output filename collision at [ROOT]/foo/target/debug/deps/{}a{} + | + = [NOTE] the lib target `a` in package `b v1.0.0 ([ROOT]/foo/b)` has the same output filename as the lib target `a` in package `a v1.0.0 ([ROOT]/foo/a)` + = [NOTE] this may become a hard error in the future; see + = [HELP] consider changing their names to be unique or compiling them separately ... ", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX)) .run(); @@ -88,12 +87,11 @@ fn collision_example() { p.cargo("build --examples -j=1") .with_stderr_data(str![[r#" ... -[WARNING] output filename collision. -The example target `ex1` in package `b v1.0.0 ([ROOT]/foo/b)` has the same output filename as the example target `ex1` in package `a v1.0.0 ([ROOT]/foo/a)`. -Colliding filename is: [ROOT]/foo/target/debug/examples/ex1[EXE] -The targets should have unique names. -Consider changing their names to be unique or compiling them separately. -This may become a hard error in the future; see . +[WARNING] output filename collision at [ROOT]/foo/target/debug/examples/ex1[EXE] + | + = [NOTE] the example target `ex1` in package `b v1.0.0 ([ROOT]/foo/b)` has the same output filename as the example target `ex1` in package `a v1.0.0 ([ROOT]/foo/a)` + = [NOTE] this may become a hard error in the future; see + = [HELP] consider changing their names to be unique or compiling them separately ... "#]]) @@ -119,12 +117,11 @@ fn collision_export() { p.cargo("build -j1 --artifact-dir=out -Z unstable-options --bins --examples") .masquerade_as_nightly_cargo(&["artifact-dir"]) .with_stderr_data(str![[r#" -[WARNING] `--artifact-dir` filename collision. -The example target `foo` in package `foo v1.0.0 ([ROOT]/foo)` has the same output filename as the bin target `foo` in package `foo v1.0.0 ([ROOT]/foo)`. -Colliding filename is: [ROOT]/foo/out/foo[EXE] -The exported filenames should be unique. -Consider changing their names to be unique or compiling them separately. -This may become a hard error in the future; see . +[WARNING] `--artifact-dir` filename collision at [ROOT]/foo/out/foo[EXE] + | + = [NOTE] the example target `foo` in package `foo v1.0.0 ([ROOT]/foo)` has the same output filename as the bin target `foo` in package `foo v1.0.0 ([ROOT]/foo)` + = [NOTE] this may become a hard error in the future; see + = [HELP] consider changing their names to be unique or compiling them separately ... "#]]) @@ -165,12 +162,10 @@ fn collision_doc() { p.cargo("doc -j=1") .with_stderr_data(str![[r#" ... -[WARNING] output filename collision. -The lib target `foo` in package `foo2 v0.1.0 ([ROOT]/foo/foo2)` has the same output filename as the lib target `foo` in package `foo v0.1.0 ([ROOT]/foo)`. -Colliding filename is: [ROOT]/foo/target/doc/foo/index.html -The targets should have unique names. -This is a known bug where multiple crates with the same name use -the same path; see . +[WARNING] output filename collision at [ROOT]/foo/target/doc/foo/index.html + | + = [NOTE] the lib target `foo` in package `foo2 v0.1.0 ([ROOT]/foo/foo2)` has the same output filename as the lib target `foo` in package `foo v0.1.0 ([ROOT]/foo)` + = [NOTE] this is a known bug where multiple crates with the same name use the same path; see ... "#]]) @@ -449,12 +444,10 @@ fn collision_doc_sources() { [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) -[WARNING] output filename collision. -The lib target `bar` in package `bar v1.0.0` has the same output filename as the lib target `bar` in package `bar v1.0.0 ([ROOT]/foo/bar)`. -Colliding filename is: [ROOT]/foo/target/doc/bar/index.html -The targets should have unique names. -This is a known bug where multiple crates with the same name use -the same path; see . +[WARNING] output filename collision at [ROOT]/foo/target/doc/bar/index.html + | + = [NOTE] the lib target `bar` in package `bar v1.0.0` has the same output filename as the lib target `bar` in package `bar v1.0.0 ([ROOT]/foo/bar)` + = [NOTE] this is a known bug where multiple crates with the same name use the same path; see [CHECKING] bar v1.0.0 ([ROOT]/foo/bar) [DOCUMENTING] bar v1.0.0 ([ROOT]/foo/bar) [DOCUMENTING] bar v1.0.0 @@ -576,12 +569,10 @@ fn collision_with_root() { [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] foo-macro v1.0.0 (registry `dummy-registry`) -[WARNING] output filename collision. -The lib target `foo_macro` in package `foo-macro v1.0.0` has the same output filename as the lib target `foo_macro` in package `foo-macro v1.0.0 ([ROOT]/foo/foo-macro)`. -Colliding filename is: [ROOT]/foo/target/doc/foo_macro/index.html -The targets should have unique names. -This is a known bug where multiple crates with the same name use -the same path; see . +[WARNING] output filename collision at [ROOT]/foo/target/doc/foo_macro/index.html + | + = [NOTE] the lib target `foo_macro` in package `foo-macro v1.0.0` has the same output filename as the lib target `foo_macro` in package `foo-macro v1.0.0 ([ROOT]/foo/foo-macro)` + = [NOTE] this is a known bug where multiple crates with the same name use the same path; see [CHECKING] foo-macro v1.0.0 [DOCUMENTING] foo-macro v1.0.0 [CHECKING] abc v1.0.0 ([ROOT]/foo/abc) diff --git a/tests/testsuite/doc.rs b/tests/testsuite/doc.rs index bf5d6e309..88d4f27c0 100644 --- a/tests/testsuite/doc.rs +++ b/tests/testsuite/doc.rs @@ -297,12 +297,10 @@ fn doc_multiple_targets_same_name() { p.cargo("doc --workspace") .with_stderr_data(str![[r#" -[WARNING] output filename collision. -The bin target `foo_lib` in package `foo v0.1.0 ([ROOT]/foo/foo)` has the same output filename as the lib target `foo_lib` in package `bar v0.1.0 ([ROOT]/foo/bar)`. -Colliding filename is: [ROOT]/foo/target/doc/foo_lib/index.html -The targets should have unique names. -This is a known bug where multiple crates with the same name use -the same path; see . +[WARNING] output filename collision at [ROOT]/foo/target/doc/foo_lib/index.html + | + = [NOTE] this is a known bug where multiple crates with the same name use the same path; see + = [NOTE] the bin target `foo_lib` in package `foo v0.1.0 ([ROOT]/foo/foo)` has the same output filename as the lib target `foo_lib` in package `bar v0.1.0 ([ROOT]/foo/bar)` [DOCUMENTING] bar v0.1.0 ([ROOT]/foo/bar) [DOCUMENTING] foo v0.1.0 ([ROOT]/foo/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s @@ -513,12 +511,10 @@ fn doc_lib_bin_same_name_documents_named_bin_when_requested() { // The checking/documenting lines are sometimes swapped since they run // concurrently. .with_stderr_data(str![[r#" -[WARNING] output filename collision. -The bin target `foo` in package `foo v0.0.1 ([ROOT]/foo)` has the same output filename as the lib target `foo` in package `foo v0.0.1 ([ROOT]/foo)`. -Colliding filename is: [ROOT]/foo/target/doc/foo/index.html -The targets should have unique names. -This is a known bug where multiple crates with the same name use -the same path; see . +[WARNING] output filename collision at [ROOT]/foo/target/doc/foo/index.html + | + = [NOTE] the bin target `foo` in package `foo v0.0.1 ([ROOT]/foo)` has the same output filename as the lib target `foo` in package `foo v0.0.1 ([ROOT]/foo)` + = [NOTE] this is a known bug where multiple crates with the same name use the same path; see [CHECKING] foo v0.0.1 ([ROOT]/foo) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s @@ -557,12 +553,10 @@ fn doc_lib_bin_same_name_documents_bins_when_requested() { // The checking/documenting lines are sometimes swapped since they run // concurrently. .with_stderr_data(str![[r#" -[WARNING] output filename collision. -The bin target `foo` in package `foo v0.0.1 ([ROOT]/foo)` has the same output filename as the lib target `foo` in package `foo v0.0.1 ([ROOT]/foo)`. -Colliding filename is: [ROOT]/foo/target/doc/foo/index.html -The targets should have unique names. -This is a known bug where multiple crates with the same name use -the same path; see . +[WARNING] output filename collision at [ROOT]/foo/target/doc/foo/index.html + | + = [NOTE] the bin target `foo` in package `foo v0.0.1 ([ROOT]/foo)` has the same output filename as the lib target `foo` in package `foo v0.0.1 ([ROOT]/foo)` + = [NOTE] this is a known bug where multiple crates with the same name use the same path; see [CHECKING] foo v0.0.1 ([ROOT]/foo) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s @@ -1408,12 +1402,10 @@ fn doc_all_member_dependency_same_name() { [LOCKING] 1 package to latest compatible version [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) -[WARNING] output filename collision. -The lib target `bar` in package `bar v0.1.0` has the same output filename as the lib target `bar` in package `bar v0.1.0 ([ROOT]/foo/bar)`. -Colliding filename is: [ROOT]/foo/target/doc/bar/index.html -The targets should have unique names. -This is a known bug where multiple crates with the same name use -the same path; see . +[WARNING] output filename collision at [ROOT]/foo/target/doc/bar/index.html + | + = [NOTE] the lib target `bar` in package `bar v0.1.0` has the same output filename as the lib target `bar` in package `bar v0.1.0 ([ROOT]/foo/bar)` + = [NOTE] this is a known bug where multiple crates with the same name use the same path; see [DOCUMENTING] bar v0.1.0 [CHECKING] bar v0.1.0 [DOCUMENTING] bar v0.1.0 ([ROOT]/foo/bar)