diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index 15bb496dbc2..55023a49967 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -1,14 +1,16 @@ use std::fmt; use std::fmt::Write as _; +use std::fs::read_dir; +use std::path::{Path, PathBuf}; 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 anyhow::{Error, anyhow}; use super::context::ResolverContext; use super::types::{ConflictMap, ConflictReason}; @@ -385,23 +387,69 @@ pub(super) fn activation_error( }); let _ = writeln!(&mut msg, "perhaps you meant: {suggestions}"); } else { - let _ = writeln!( - &mut msg, - "no matching package named `{}` found", - dep.package_name() - ); - } + let package_path = resolver_ctx + .parents + .path_to_bottom(&parent.package_id()) + .into_iter() + .map(|(pkg_id, _)| pkg_id.clone()) + .collect::>(); + + let sid = dep.source_id(); + let path = match dep.source_id().url().to_file_path() { + Ok(path) => path, + Err(_) => { + return ResolveError { + cause: anyhow!( + "no matching package found named '{}' found", + dep.package_name() + ), + package_path, + }; + } + }; + let requested = dep.package_name().as_str(); + + if let Some(gctx) = gctx { + let root = inspect_root_package(&mut msg, &path.as_path(), &gctx, sid); + let recursive = + inspect_recursive_packages(&mut msg, &path.as_path(), &gctx, sid, requested); + + let _ = writeln!( + &mut msg, + "no matching package named '{}' found at '{}/'", + requested, + path.display() + ); + + let _ = writeln!( + &mut msg, + "required by {}", + describe_path_in_context(resolver_ctx, &parent.package_id()), + ); + + let mut help_name = None; + let mut help_path = None; - let mut location_searched_msg = registry.describe_source(dep.source_id()); - if location_searched_msg.is_empty() { - location_searched_msg = format!("{}", dep.source_id()); + if let Some(root_pkg) = &root { + help_name = Some(root_pkg.name.clone()); + help_path = Some(path.clone()); + } + + if let Some(recursive_pkg) = &recursive { + help_name = Some(recursive_pkg.name.clone()); + help_path = Some(recursive_pkg.path.clone()); + } + + if let (Some(name), Some(pkg_path)) = (help_name, help_path) { + let _ = write!( + &mut msg, + "help: package `{}` exists at `{}`", + name, + pkg_path.display() + ); + } + } } - 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() { @@ -568,3 +616,93 @@ pub(crate) fn describe_path<'a>( String::new() } + +#[derive(Debug)] +struct RootPackageInfo { + name: String, +} + +#[derive(Debug)] +struct RecursivePackageInfo { + name: String, + path: PathBuf, +} + +fn inspect_root_package( + msg: &mut String, + path: &Path, + gctx: &GlobalContext, + sid: SourceId, +) -> Option { + let mut ps = PathSource::new(path, sid, gctx); + + ps.root_package().expect("failed to get the root"); + + if let Err(e) = ps.load() { + msg.push_str(&e.to_string()); + msg.push('\n'); + } + + let pkg = ps + .read_package() + .expect("failed to read the package in root"); + + Some(RootPackageInfo { + name: pkg.name().to_string(), + }) +} + +fn inspect_recursive_packages( + msg: &mut String, + path: &Path, + gctx: &GlobalContext, + sid: SourceId, + requested: &str, +) -> Option { + let mut rps = RecursivePathSource::new(path, sid, gctx); + + if let Err(e) = rps.load() { + msg.push_str(&e.to_string()); + msg.push('\n'); + return None; + } + + let pkgs = rps + .read_packages() + .expect("failed to read the packages recursively"); + + for pkg in pkgs { + if pkg.name() == requested { + let manifest = pkg.manifest_path(); + let pkg_dir = manifest + .parent() + .expect("failed to take the parent path") + .to_path_buf(); + + return Some(RecursivePackageInfo { + name: pkg.name().to_string(), + path: pkg_dir, + }); + // } else { + // let list = rps.list_files(&pkg).unwrap(); + // println!("{:?}", list); + } + } + + None +} + +fn _inspect_else_packages(path: &Path) { + let entry_path = read_dir(path); + let _entry_result = entry_path + .expect("failed to get the path") + .map(|f| { + f.expect("failed to get the path") + .path() + .to_string_lossy() + .to_string() + }) + .collect::>(); + + // let walkdir = WalkDir::new(entry_path); +} diff --git a/src/cargo/sources/path.rs b/src/cargo/sources/path.rs index 8e754e03fa9..bd90debaad0 100644 --- a/src/cargo/sources/path.rs +++ b/src/cargo/sources/path.rs @@ -123,7 +123,7 @@ impl<'gctx> PathSource<'gctx> { Ok(()) } - fn read_package(&self) -> CargoResult { + pub fn read_package(&self) -> CargoResult { let path = self.path.join("Cargo.toml"); let pkg = ops::read_package(&path, self.source_id, self.gctx)?; Ok(pkg) diff --git a/tests/testsuite/path.rs b/tests/testsuite/path.rs index 118c55409fc..3ace5bfd3e3 100644 --- a/tests/testsuite/path.rs +++ b/tests/testsuite/path.rs @@ -1919,3 +1919,151 @@ 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 = [] + + [workspace] + + [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 +location searched: [ROOT]/foo/crates/bar +required by package `foo v0.5.0 ([ROOT]/foo)` + +"#]]) + .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 = [] + + [workspace] + + [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] failed to load manifest for dependency `definitely_not_bar` + +Caused by: + failed to read `[ROOT]/foo/crates/bar/Cargo.toml` + +Caused by: + [NOT_FOUND] + +"#]]) + .run(); +} + +#[cargo_test] +fn invalid_manifest_in_path() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + edition = "2015" + authors = [] + + [workspace] + + [dependencies] + definitely_not_bar = { path = "crates/bar" } + "#, + ) + .file("src/lib.rs", "") + .file( + "crates/bar/alice/Cargo.toml", + r#" + [package] + name = "alice" + version = "0.5.0" + edition = "2015" + authors = [] + "#, + ) + .file("crates/bar/alice/src/lib.rs", "") + .file( + "crates/bar/bob/Cargo.toml", + r#" + [package] + name = "bob" + version = "0.5.0" + edition = "2015" + authors = [] + "#, + ) + .file("crates/bar/bob/src/lib.rs", "") + .build(); + + p.cargo("generate-lockfile") + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to load manifest for dependency `definitely_not_bar` + +Caused by: + failed to read `[ROOT]/foo/crates/bar/Cargo.toml` + +Caused by: + [NOT_FOUND] + +"#]]) + .run(); +}