Skip to content

Commit 74390a4

Browse files
committed
Enhance error message for target auto-discovery
Enhance for following scenarios: 1. Target without `path` specified and cannot be found. 2. Target without `path` specified and cannot be found, but a file exists at the commonly wrong path, e.g. `example/a.rs`, `bench/b.rs`. 3. Found multiple candidate files and cannot infer which to use.
1 parent 3a3a071 commit 74390a4

File tree

1 file changed

+118
-8
lines changed

1 file changed

+118
-8
lines changed

src/cargo/util/toml/targets.rs

Lines changed: 118 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ use crate::util::restricted_names;
2525

2626
use anyhow::Context as _;
2727

28+
const DEFAULT_TEST_DIR_NAME: &'static str = "tests";
29+
const DEFAULT_BENCH_DIR_NAME: &'static str = "benches";
30+
const DEFAULT_EXAMPLE_DIR_NAME: &'static str = "examples";
31+
const DEFAULT_BIN_DIR_NAME: &'static str = "bin";
32+
2833
pub fn targets(
2934
features: &Features,
3035
manifest: &TomlManifest,
@@ -353,7 +358,10 @@ fn clean_bins(
353358
return Some(path);
354359
}
355360

356-
let path = package_root.join("src").join("bin").join("main.rs");
361+
let path = package_root
362+
.join("src")
363+
.join(DEFAULT_BIN_DIR_NAME)
364+
.join("main.rs");
357365
if path.exists() {
358366
return Some(path);
359367
}
@@ -370,7 +378,7 @@ fn clean_examples(
370378
warnings: &mut Vec<String>,
371379
errors: &mut Vec<String>,
372380
) -> CargoResult<Vec<Target>> {
373-
let inferred = infer_from_directory(&package_root.join("examples"));
381+
let inferred = infer_from_directory(&package_root.join(DEFAULT_EXAMPLE_DIR_NAME));
374382

375383
let targets = clean_targets(
376384
"example",
@@ -415,7 +423,7 @@ fn clean_tests(
415423
warnings: &mut Vec<String>,
416424
errors: &mut Vec<String>,
417425
) -> CargoResult<Vec<Target>> {
418-
let inferred = infer_from_directory(&package_root.join("tests"));
426+
let inferred = infer_from_directory(&package_root.join(DEFAULT_TEST_DIR_NAME));
419427

420428
let targets = clean_targets(
421429
"test",
@@ -590,7 +598,9 @@ fn inferred_bins(package_root: &Path, package_name: &str) -> Vec<(String, PathBu
590598
if main.exists() {
591599
result.push((package_name.to_string(), main));
592600
}
593-
result.extend(infer_from_directory(&package_root.join("src").join("bin")));
601+
result.extend(infer_from_directory(
602+
&package_root.join("src").join(DEFAULT_BIN_DIR_NAME),
603+
));
594604

595605
result
596606
}
@@ -812,6 +822,90 @@ fn configure(features: &Features, toml: &TomlTarget, target: &mut Target) -> Car
812822
Ok(())
813823
}
814824

825+
/// Build an error message for a target path that cannot be determined either
826+
/// by auto-discovery or specifiying.
827+
///
828+
/// This function tries to detect commonly wrong paths for targets:
829+
///
830+
/// test -> tests/*.rs, tests/*/main.rs
831+
/// bench -> benches/*.rs, benches/*/main.rs
832+
/// example -> examples/*.rs, examples/*/main.rs
833+
/// bin -> src/bin/*.rs, src/bin/*/main.rs
834+
///
835+
/// Note that the logic need to sync with [`infer_from_directory`] if changes.
836+
fn target_path_not_found_error_message(
837+
package_root: &Path,
838+
target: &TomlTarget,
839+
target_kind: &str,
840+
) -> String {
841+
fn possible_target_paths(name: &str, kind: &str, commonly_wrong: bool) -> [PathBuf; 2] {
842+
let mut target_path = PathBuf::new();
843+
match (kind, commonly_wrong) {
844+
// commonly wrong paths
845+
("test" | "bench" | "example", true) => target_path.push(kind),
846+
("bin", true) => {
847+
target_path.push("src");
848+
target_path.push("bins");
849+
}
850+
// default inferred paths
851+
("test", false) => target_path.push(DEFAULT_TEST_DIR_NAME),
852+
("bench", false) => target_path.push(DEFAULT_BENCH_DIR_NAME),
853+
("example", false) => target_path.push(DEFAULT_EXAMPLE_DIR_NAME),
854+
("bin", false) => {
855+
target_path.push("src");
856+
target_path.push(DEFAULT_BIN_DIR_NAME);
857+
}
858+
_ => unreachable!("invalid target kind: {}", kind),
859+
}
860+
target_path.push(name);
861+
862+
let target_path_file = {
863+
let mut path = target_path.clone();
864+
path.set_extension("rs");
865+
path
866+
};
867+
let target_path_subdir = {
868+
target_path.push("main.rs");
869+
target_path
870+
};
871+
return [target_path_file, target_path_subdir];
872+
}
873+
874+
let target_name = target.name();
875+
let commonly_wrong_paths = possible_target_paths(&target_name, target_kind, true);
876+
let possible_paths = possible_target_paths(&target_name, target_kind, false);
877+
let existing_wrong_path_index = match (
878+
package_root.join(&commonly_wrong_paths[0]).exists(),
879+
package_root.join(&commonly_wrong_paths[1]).exists(),
880+
) {
881+
(true, _) => Some(0),
882+
(_, true) => Some(1),
883+
_ => None,
884+
};
885+
886+
if let Some(i) = existing_wrong_path_index {
887+
return format!(
888+
"\
889+
can't find `{name}` {kind} at default paths, but found a file at `{wrong_path}`.
890+
Perhaps rename the file to `{possible_path}` for target auto-discovery, \
891+
or specify {kind}.path if you want to use a non-default path.",
892+
name = target_name,
893+
kind = target_kind,
894+
wrong_path = commonly_wrong_paths[i].display(),
895+
possible_path = possible_paths[i].display(),
896+
);
897+
}
898+
899+
format!(
900+
"can't find `{name}` {kind} at `{path_file}` or `{path_dir}`. \
901+
Please specify {kind}.path if you want to use a non-default path.",
902+
name = target_name,
903+
kind = target_kind,
904+
path_file = possible_paths[0].display(),
905+
path_dir = possible_paths[1].display(),
906+
)
907+
}
908+
815909
fn target_path(
816910
target: &TomlTarget,
817911
inferred: &[(String, PathBuf)],
@@ -835,16 +929,32 @@ fn target_path(
835929
let second = matching.next();
836930
match (first, second) {
837931
(Some(path), None) => Ok(path),
838-
(None, None) | (Some(_), Some(_)) => {
932+
(None, None) => {
933+
if edition == Edition::Edition2015 {
934+
if let Some(path) = legacy_path(target) {
935+
return Ok(path);
936+
}
937+
}
938+
Err(target_path_not_found_error_message(
939+
package_root,
940+
target,
941+
target_kind,
942+
))
943+
}
944+
(Some(p0), Some(p1)) => {
839945
if edition == Edition::Edition2015 {
840946
if let Some(path) = legacy_path(target) {
841947
return Ok(path);
842948
}
843949
}
844950
Err(format!(
845-
"can't find `{name}` {target_kind}, specify {target_kind}.path",
846-
name = name,
847-
target_kind = target_kind
951+
"\
952+
cannot infer path for `{}` {}
953+
Cargo doesn't know which to use because multiple target files found at `{}` and `{}`.",
954+
target.name(),
955+
target_kind,
956+
p0.strip_prefix(package_root).unwrap_or(&p0).display(),
957+
p1.strip_prefix(package_root).unwrap_or(&p1).display(),
848958
))
849959
}
850960
(None, Some(_)) => unreachable!(),

0 commit comments

Comments
 (0)