Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit bc577dc

Browse files
authoredMar 13, 2025
fix(run): Disambiguate bins from different packages that share a name (#15298)
### What does this PR try to resolve? This builds on the work done in #15199 to improve target selection errors to also disambiguate when the same binary name is used in multiple packages. This also makes the errors from #15199 more consistent with the rustc style guide and reduces code duplication. Fixes #13312 ### How should we test and review this PR? This is a first pass and does not do the full `--package foo --bin bar` syntax. I wanted to focus on the basic functionality before we iterated on it, e.g. investigating how well we can predict the CLI flags, how much noise they might add, etc. ### Additional information
2 parents 611b7c4 + d29a7cb commit bc577dc

File tree

6 files changed

+297
-216
lines changed

6 files changed

+297
-216
lines changed
 

‎src/cargo/ops/cargo_compile/unit_generator.rs

+37-42
Original file line numberDiff line numberDiff line change
@@ -259,37 +259,40 @@ impl<'a> UnitGenerator<'a, '_> {
259259
};
260260
let proposals = self.filter_targets(filter, true, mode);
261261
if proposals.is_empty() {
262-
let targets = self
263-
.packages
264-
.iter()
265-
.flat_map(|pkg| {
266-
pkg.targets()
267-
.iter()
268-
.filter(|target| is_expected_kind(target))
269-
})
270-
.collect::<Vec<_>>();
271-
let suggestion = closest_msg(target_name, targets.iter(), |t| t.name(), "target");
262+
let mut targets = std::collections::BTreeMap::new();
263+
for (pkg, target) in self.packages.iter().flat_map(|pkg| {
264+
pkg.targets()
265+
.iter()
266+
.filter(|target| is_expected_kind(target))
267+
.map(move |t| (pkg, t))
268+
}) {
269+
targets
270+
.entry(target.name())
271+
.or_insert_with(Vec::new)
272+
.push((pkg, target));
273+
}
274+
275+
let suggestion = closest_msg(target_name, targets.keys(), |t| t, "target");
272276
let targets_elsewhere = self.get_targets_from_other_packages(filter)?;
273-
let need_append_targets_elsewhere = !targets_elsewhere.is_empty();
274-
let append_targets_elsewhere = |msg: &mut String, prefix: &str| {
277+
let append_targets_elsewhere = |msg: &mut String| {
275278
let mut available_msg = Vec::new();
276-
for (package, targets) in targets_elsewhere {
279+
for (package, targets) in &targets_elsewhere {
277280
if !targets.is_empty() {
278281
available_msg.push(format!(
279-
"help: Available {target_desc} in `{package}` package:"
282+
"help: available {target_desc} in `{package}` package:"
280283
));
281284
for target in targets {
282285
available_msg.push(format!(" {target}"));
283286
}
284287
}
285288
}
286289
if !available_msg.is_empty() {
287-
write!(msg, "{prefix}{}", available_msg.join("\n"))?;
290+
write!(msg, "\n{}", available_msg.join("\n"))?;
288291
}
289292
CargoResult::Ok(())
290293
};
291294

292-
let unmatched_packages = || match self.spec {
295+
let unmatched_packages = match self.spec {
293296
Packages::Default | Packages::OptOut(_) | Packages::All(_) => {
294297
"default-run packages".to_owned()
295298
}
@@ -305,33 +308,25 @@ impl<'a> UnitGenerator<'a, '_> {
305308
}
306309
};
307310

311+
let named = if is_glob { "matches pattern" } else { "named" };
312+
308313
let mut msg = String::new();
309-
if !suggestion.is_empty() {
310-
write!(
311-
msg,
312-
"no {} target {} `{}` in {}{}",
313-
target_desc,
314-
if is_glob { "matches pattern" } else { "named" },
315-
target_name,
316-
unmatched_packages(),
317-
suggestion,
318-
)?;
319-
append_targets_elsewhere(&mut msg, "\n")?;
320-
} else {
321-
writeln!(
322-
msg,
323-
"no {} target {} `{}` in {}.",
324-
target_desc,
325-
if is_glob { "matches pattern" } else { "named" },
326-
target_name,
327-
unmatched_packages()
328-
)?;
329-
330-
append_targets_elsewhere(&mut msg, "")?;
331-
if !targets.is_empty() && !need_append_targets_elsewhere {
332-
writeln!(msg, "Available {} targets:", target_desc)?;
333-
for target in targets {
334-
writeln!(msg, " {}", target.name())?;
314+
write!(
315+
msg,
316+
"no {target_desc} target {named} `{target_name}` in {unmatched_packages}{suggestion}",
317+
)?;
318+
if !targets_elsewhere.is_empty() {
319+
append_targets_elsewhere(&mut msg)?;
320+
} else if suggestion.is_empty() && !targets.is_empty() {
321+
write!(msg, "\nhelp: available {} targets:", target_desc)?;
322+
for (target_name, pkgs) in targets {
323+
if pkgs.len() == 1 {
324+
write!(msg, "\n {target_name}")?;
325+
} else {
326+
for (pkg, _) in pkgs {
327+
let pkg_name = pkg.name();
328+
write!(msg, "\n {target_name} in package {pkg_name}")?;
329+
}
335330
}
336331
}
337332
}

‎src/cargo/ops/cargo_run.rs

+14-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::ffi::OsString;
2+
use std::fmt::Write as _;
23
use std::iter;
34
use std::path::Path;
45

@@ -69,10 +70,20 @@ pub fn run(
6970
names.join(", ")
7071
)
7172
} else {
72-
anyhow::bail!(
73-
"`cargo run` can run at most one executable, but \
73+
let mut message = "`cargo run` can run at most one executable, but \
7474
multiple were specified"
75-
)
75+
.to_owned();
76+
write!(&mut message, "\nhelp: available targets:")?;
77+
for (pkg, bin) in &bins {
78+
write!(
79+
&mut message,
80+
"\n {} `{}` in package `{}`",
81+
bin.kind().description(),
82+
bin.name(),
83+
pkg.name()
84+
)?;
85+
}
86+
anyhow::bail!(message)
7687
}
7788
}
7889

‎tests/testsuite/build.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -1337,11 +1337,10 @@ fn cargo_compile_with_filename() {
13371337
p.cargo("build --bin bin.rs")
13381338
.with_status(101)
13391339
.with_stderr_data(str![[r#"
1340-
[ERROR] no bin target named `bin.rs` in default-run packages.
1341-
Available bin targets:
1340+
[ERROR] no bin target named `bin.rs` in default-run packages
1341+
[HELP] available bin targets:
13421342
a
13431343
1344-
13451344
"#]])
13461345
.run();
13471346

@@ -1358,11 +1357,10 @@ Available bin targets:
13581357
p.cargo("build --example example.rs")
13591358
.with_status(101)
13601359
.with_stderr_data(str![[r#"
1361-
[ERROR] no example target named `example.rs` in default-run packages.
1362-
Available example targets:
1360+
[ERROR] no example target named `example.rs` in default-run packages
1361+
[HELP] available example targets:
13631362
a
13641363
1365-
13661364
"#]])
13671365
.run();
13681366

There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.