Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 146 additions & 26 deletions src/cargo/core/resolver/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ use std::fmt;
use std::fmt::Write as _;
use std::task::Poll;

use crate::core::{Dependency, PackageId, Registry, Summary};
use crate::sources::IndexSummary;
use crate::core::{Dependency, PackageId, Registry, SourceId, Summary};
use crate::sources::source::QueryKind;
use crate::sources::{IndexSummary, PathSource, RecursivePathSource};
use crate::util::edit_distance::{closest, edit_distance};
use crate::util::errors::CargoResult;
use crate::util::{GlobalContext, OptVersionReq, VersionExt};
use anyhow::Error;
use std::path::{Path, PathBuf};

use super::context::ResolverContext;
use super::types::{ConflictMap, ConflictReason};
Expand Down Expand Up @@ -392,33 +393,103 @@ pub(super) fn activation_error(
});
let _ = writeln!(&mut msg, "perhaps you meant: {suggestions}");
} else {
let _ = writeln!(
let mut write_not_found = || {
let _ = writeln!(
&mut msg,
"no matching package named `{}` found",
dep.package_name()
);
};

if dep.source_id().is_path() {
let path = dep
.source_id()
.url()
.to_file_path()
.expect("file dependency source url should have a path");

if let Some(context) = gctx {
let sid = dep.source_id();

let root_package = inspect_root_package(Path::new(&path), context, sid);
let requested = dep.package_name().as_str();

let recursive_packages =
inspect_recursive_packages(Path::new(&path), context, sid, requested);

if let Some(package_name) = root_package {
let _ = writeln!(
&mut msg,
"no matching package named `{}` found at `{}`",
dep.package_name(),
path.display()
);

let _ = writeln!(
&mut msg,
"note: required by {}",
describe_path_in_context(resolver_ctx, &parent.package_id()),
);

let _ = write!(
&mut msg,
"help: package `{}` exists at `{}`",
package_name,
path.display()
);
} else if let Some((package_name, package_path)) = recursive_packages {
let _ = writeln!(
&mut msg,
"no matching package named `{}` found at `{}`",
dep.package_name(),
path.display()
);

let _ = writeln!(
&mut msg,
"note: required by {}",
describe_path_in_context(resolver_ctx, &parent.package_id()),
);

let _ = write!(
&mut msg,
"help: package `{}` exists at `{}`",
package_name,
package_path.display()
);
} else {
write_not_found()
}
} else {
write_not_found()
}
} else {
write_not_found()
}
}

if !dep.source_id().is_path() {
let mut location_searched_msg = registry.describe_source(dep.source_id());
if location_searched_msg.is_empty() {
location_searched_msg = format!("{}", dep.source_id());
}
let _ = writeln!(&mut msg, "location searched: {}", location_searched_msg);
let _ = write!(
&mut msg,
"no matching package named `{}` found",
dep.package_name()
"required by {}",
describe_path_in_context(resolver_ctx, &parent.package_id()),
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was moved from out of the else block, into it.

}

let mut location_searched_msg = registry.describe_source(dep.source_id());
if location_searched_msg.is_empty() {
location_searched_msg = format!("{}", dep.source_id());
}
let _ = writeln!(&mut msg, "location searched: {}", location_searched_msg);
let _ = write!(
&mut msg,
"required by {}",
describe_path_in_context(resolver_ctx, &parent.package_id()),
);

if let Some(gctx) = gctx {
if let Some(offline_flag) = gctx.offline_flag() {
let _ = write!(
&mut hints,
"\nAs a reminder, you're using offline mode ({offline_flag}) \
which can sometimes cause surprising resolution failures, \
if this error is too confusing you may wish to retry \
without `{offline_flag}`.",
);
if let Some(gctx) = gctx {
if let Some(offline_flag) = gctx.offline_flag() {
let _ = write!(
&mut hints,
"\nAs a reminder, you're using offline mode ({offline_flag}) \
which can sometimes cause surprising resolution failures, \
if this error is too confusing you may wish to retry \
without `{offline_flag}`.",
);
}
}
}

Expand Down Expand Up @@ -575,3 +646,52 @@ pub(crate) fn describe_path<'a>(

String::new()
}

fn inspect_root_package(path: &Path, gctx: &GlobalContext, sid: SourceId) -> Option<String> {
let mut ps = PathSource::new(path, sid, gctx);

if ps.load().is_err() {
return None;
}

let pkg = ps
.root_package()
.expect("path source should have a root package");

let package_name = pkg.name().to_string();

Some(package_name)
}

fn inspect_recursive_packages(
path: &Path,
gctx: &GlobalContext,
sid: SourceId,
requested: &str,
) -> Option<(String, PathBuf)> {
let mut rps = RecursivePathSource::new(path, sid, gctx);

if rps.load().is_err() {
return None;
}

let pkgs = rps
.read_packages()
.expect("path source should read the packages");

for pkg in pkgs {
if pkg.name() == requested {
let manifest = pkg.manifest_path();
let pkg_dir = manifest
.parent()
.expect("manifest path should have a parent")
.to_path_buf();

let name = pkg.name().to_string();
let path = pkg_dir;

return Some((name, path));
}
}
return None;
}
2 changes: 1 addition & 1 deletion src/cargo/sources/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ impl<'gctx> PathSource<'gctx> {
Ok(())
}

fn read_package(&self) -> CargoResult<Package> {
pub fn read_package(&self) -> CargoResult<Package> {
let path = self.path.join("Cargo.toml");
let pkg = ops::read_package(&path, self.source_id, self.gctx)?;
Ok(pkg)
Expand Down
6 changes: 3 additions & 3 deletions tests/testsuite/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1277,9 +1277,9 @@ fn cargo_compile_with_dep_name_mismatch() {
p.cargo("build")
.with_status(101)
.with_stderr_data(str![[r#"
[ERROR] no matching package named `notquitebar` found
location searched: [ROOT]/foo/bar
required by package `foo v0.0.1 ([ROOT]/foo)`
[ERROR] no matching package named `notquitebar` found at `[ROOT]/foo/bar`
[NOTE] required by package `foo v0.0.1 ([ROOT]/foo)`
[HELP] package `bar` exists at `[ROOT]/foo/bar`

"#]])
.run();
Expand Down
85 changes: 83 additions & 2 deletions tests/testsuite/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1673,8 +1673,7 @@ fn invalid_path_dep_in_workspace_with_lockfile() {
[ERROR] no matching package found
searched package name: `bar`
perhaps you meant: foo
location searched: [ROOT]/foo/foo
required by package `foo v0.5.0 ([ROOT]/foo/foo)`


"#]])
.run();
Expand Down Expand Up @@ -1919,3 +1918,85 @@ foo v1.0.0 ([ROOT]/foo)
"#]])
.run();
}

#[cargo_test]
fn invalid_package_name_in_path() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.5.0"
edition = "2015"
authors = []

[dependencies]
definitely_not_bar = { path = "crates/bar" }
"#,
)
.file("src/lib.rs", "")
.file(
"crates/bar/Cargo.toml",
r#"
[package]
name = "bar"
version = "0.5.0"
edition = "2015"
authors = []
"#,
)
.file("crates/bar/src/lib.rs", "")
.build();

p.cargo("generate-lockfile")
.with_status(101)
.with_stderr_data(str![[r#"
[ERROR] no matching package named `definitely_not_bar` found at `[ROOT]/foo/crates/bar`
[NOTE] required by package `foo v0.5.0 ([ROOT]/foo)`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming this test is failing. Tests should be atomic which includes tests passing. This should be showing the existing behavior so that the following commit then changes that behavior so we can see the new behavior.

[HELP] package `bar` exists at `[ROOT]/foo/crates/bar`

"#]])
.run();
}

#[cargo_test]
fn invalid_package_in_subdirectory() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.5.0"
edition = "2015"
authors = []

[dependencies]
definitely_not_bar = { path = "crates/bar" }
"#,
)
.file("src/lib.rs", "")
.file(
"crates/bar/definitely_not_bar/Cargo.toml",
r#"
[package]
name = "definitely_not_bar"
version = "0.5.0"
edition = "2015"
authors = []
"#,
)
.file("crates/bar/definitely_not_bar/src/lib.rs", "")
.build();

p.cargo("generate-lockfile")
.with_status(101)
.with_stderr_data(str![[r#"
[ERROR] no matching package named `definitely_not_bar` found at `[ROOT]/foo/crates/bar`
[NOTE] required by package `foo v0.5.0 ([ROOT]/foo)`
[HELP] package `bar` exists at `[ROOT]/foo/crates/bar/definitely_not_bar`

"#]])
.run();
}
6 changes: 2 additions & 4 deletions tests/testsuite/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2207,8 +2207,7 @@ fn use_semver_package_incorrectly_http() {
use_semver_package_incorrectly(str![[r#"
[ERROR] failed to select a version for the requirement `a = "^0.1"`
candidate versions found which didn't match: 0.1.1-alpha.0
location searched: [ROOT]/foo/a
required by package `b v0.1.0 ([ROOT]/foo/b)`

if you are looking for the prerelease package it needs to be specified explicitly
a = { version = "0.1.1-alpha.0" }

Expand All @@ -2220,8 +2219,7 @@ fn use_semver_package_incorrectly_git() {
use_semver_package_incorrectly(str![[r#"
[ERROR] failed to select a version for the requirement `a = "^0.1"`
candidate versions found which didn't match: 0.1.1-alpha.0
location searched: [ROOT]/foo/a
required by package `b v0.1.0 ([ROOT]/foo/b)`

if you are looking for the prerelease package it needs to be specified explicitly
a = { version = "0.1.1-alpha.0" }

Expand Down
Loading