mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-28 11:20:36 +00:00
feat: Add missing_dep_diagnostic
thanks to @Muscraft
This commit is contained in:
parent
3eb6bdf41e
commit
b28eef9651
@ -199,7 +199,11 @@ fn verify_feature_enabled(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_span(document: &ImDocument<String>, path: &[&str], get_value: bool) -> Option<Range<usize>> {
|
pub fn get_span(
|
||||||
|
document: &ImDocument<String>,
|
||||||
|
path: &[&str],
|
||||||
|
get_value: bool,
|
||||||
|
) -> Option<Range<usize>> {
|
||||||
let mut table = document.as_item().as_table_like()?;
|
let mut table = document.as_item().as_table_like()?;
|
||||||
let mut iter = path.into_iter().peekable();
|
let mut iter = path.into_iter().peekable();
|
||||||
while let Some(key) = iter.next() {
|
while let Some(key) = iter.next() {
|
||||||
@ -240,7 +244,7 @@ fn get_span(document: &ImDocument<String>, path: &[&str], get_value: bool) -> Op
|
|||||||
|
|
||||||
/// Gets the relative path to a manifest from the current working directory, or
|
/// Gets the relative path to a manifest from the current working directory, or
|
||||||
/// the absolute path of the manifest if a relative path cannot be constructed
|
/// the absolute path of the manifest if a relative path cannot be constructed
|
||||||
fn rel_cwd_manifest_path(path: &Path, gctx: &GlobalContext) -> String {
|
pub fn rel_cwd_manifest_path(path: &Path, gctx: &GlobalContext) -> String {
|
||||||
diff_paths(path, gctx.cwd())
|
diff_paths(path, gctx.cwd())
|
||||||
.unwrap_or_else(|| path.to_path_buf())
|
.unwrap_or_else(|| path.to_path_buf())
|
||||||
.display()
|
.display()
|
||||||
|
@ -15,6 +15,7 @@ use cargo_util_schemas::manifest::{RustVersion, StringOrBool};
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use lazycell::LazyCell;
|
use lazycell::LazyCell;
|
||||||
use pathdiff::diff_paths;
|
use pathdiff::diff_paths;
|
||||||
|
use toml_edit::ImDocument;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::core::compiler::{CompileKind, CompileTarget};
|
use crate::core::compiler::{CompileKind, CompileTarget};
|
||||||
@ -29,6 +30,7 @@ use crate::core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, Worksp
|
|||||||
use crate::sources::{CRATES_IO_INDEX, CRATES_IO_REGISTRY};
|
use crate::sources::{CRATES_IO_INDEX, CRATES_IO_REGISTRY};
|
||||||
use crate::util::errors::{CargoResult, ManifestError};
|
use crate::util::errors::{CargoResult, ManifestError};
|
||||||
use crate::util::interning::InternedString;
|
use crate::util::interning::InternedString;
|
||||||
|
use crate::util::lints::{get_span, rel_cwd_manifest_path};
|
||||||
use crate::util::{self, context::ConfigRelativePath, GlobalContext, IntoUrl, OptVersionReq};
|
use crate::util::{self, context::ConfigRelativePath, GlobalContext, IntoUrl, OptVersionReq};
|
||||||
|
|
||||||
mod embedded;
|
mod embedded;
|
||||||
@ -1459,7 +1461,14 @@ fn to_real_manifest(
|
|||||||
// need to check whether `dep_name` is stripped as unused dependency
|
// need to check whether `dep_name` is stripped as unused dependency
|
||||||
if let Err(ref err) = summary {
|
if let Err(ref err) = summary {
|
||||||
if let Some(missing_dep) = err.downcast_ref::<MissingDependencyError>() {
|
if let Some(missing_dep) = err.downcast_ref::<MissingDependencyError>() {
|
||||||
check_weak_optional_dep_unused(&original_toml, missing_dep)?;
|
missing_dep_diagnostic(
|
||||||
|
missing_dep,
|
||||||
|
&original_toml,
|
||||||
|
&document,
|
||||||
|
&contents,
|
||||||
|
manifest_file,
|
||||||
|
gctx,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
summary?
|
summary?
|
||||||
@ -1570,37 +1579,83 @@ fn to_real_manifest(
|
|||||||
Ok(manifest)
|
Ok(manifest)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_weak_optional_dep_unused(
|
fn missing_dep_diagnostic(
|
||||||
original_toml: &TomlManifest,
|
|
||||||
missing_dep: &MissingDependencyError,
|
missing_dep: &MissingDependencyError,
|
||||||
|
orig_toml: &TomlManifest,
|
||||||
|
document: &ImDocument<String>,
|
||||||
|
contents: &str,
|
||||||
|
manifest_file: &Path,
|
||||||
|
gctx: &GlobalContext,
|
||||||
) -> CargoResult<()> {
|
) -> CargoResult<()> {
|
||||||
if missing_dep.weak_optional {
|
let dep_name = missing_dep.dep_name;
|
||||||
// dev-dependencies are not allowed to be optional
|
let manifest_path = rel_cwd_manifest_path(manifest_file, gctx);
|
||||||
let mut orig_deps = vec![
|
let feature_value_span =
|
||||||
original_toml.dependencies.as_ref(),
|
get_span(&document, &["features", missing_dep.feature.as_str()], true).unwrap();
|
||||||
original_toml.build_dependencies.as_ref(),
|
|
||||||
];
|
|
||||||
for (_, platform) in original_toml.target.iter().flatten() {
|
|
||||||
orig_deps.extend(vec![
|
|
||||||
platform.dependencies.as_ref(),
|
|
||||||
platform.build_dependencies.as_ref(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
for deps in orig_deps {
|
|
||||||
if let Some(deps) = deps {
|
|
||||||
if deps.keys().any(|p| *p.as_str() == *missing_dep.dep_name) {
|
|
||||||
bail!(
|
|
||||||
"feature `{feature}` includes `{feature_value}`, but missing `dep:{dep_name}` to activate it",
|
|
||||||
feature = missing_dep.feature,
|
|
||||||
feature_value = missing_dep.feature_value,
|
|
||||||
dep_name = missing_dep.dep_name,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
let title = format!(
|
||||||
|
"feature `{}` includes `{}`, but `{}` is not a dependency",
|
||||||
|
missing_dep.feature, missing_dep.feature_value, &dep_name
|
||||||
|
);
|
||||||
|
let help = format!("enable the dependency with `dep:{dep_name}`");
|
||||||
|
let info_label = format!(
|
||||||
|
"`{}` is an unused optional dependency since no feature enables it",
|
||||||
|
&dep_name
|
||||||
|
);
|
||||||
|
let message = Level::Error.title(&title);
|
||||||
|
let snippet = Snippet::source(&contents)
|
||||||
|
.origin(&manifest_path)
|
||||||
|
.fold(true)
|
||||||
|
.annotation(Level::Error.span(feature_value_span.start..feature_value_span.end));
|
||||||
|
let message = if missing_dep.weak_optional {
|
||||||
|
let mut orig_deps = vec![
|
||||||
|
(
|
||||||
|
orig_toml.dependencies.as_ref(),
|
||||||
|
vec![DepKind::Normal.kind_table()],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
orig_toml.build_dependencies.as_ref(),
|
||||||
|
vec![DepKind::Build.kind_table()],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
for (name, platform) in orig_toml.target.iter().flatten() {
|
||||||
|
orig_deps.push((
|
||||||
|
platform.dependencies.as_ref(),
|
||||||
|
vec!["target", name, DepKind::Normal.kind_table()],
|
||||||
|
));
|
||||||
|
orig_deps.push((
|
||||||
|
platform.build_dependencies.as_ref(),
|
||||||
|
vec!["target", name, DepKind::Normal.kind_table()],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((_, toml_path)) = orig_deps.iter().find(|(deps, _)| {
|
||||||
|
if let Some(deps) = deps {
|
||||||
|
deps.keys().any(|p| *p.as_str() == *dep_name)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
let toml_path = toml_path
|
||||||
|
.iter()
|
||||||
|
.map(|s| *s)
|
||||||
|
.chain(std::iter::once(dep_name.as_str()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let dep_span = get_span(&document, &toml_path, false).unwrap();
|
||||||
|
|
||||||
|
message
|
||||||
|
.snippet(snippet.annotation(Level::Warning.span(dep_span).label(&info_label)))
|
||||||
|
.footer(Level::Help.title(&help))
|
||||||
|
} else {
|
||||||
|
message.snippet(snippet)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.snippet(snippet)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(err) = gctx.shell().print_message(message) {
|
||||||
|
return Err(err.into());
|
||||||
|
}
|
||||||
|
Err(AlreadyPrintedError::new(anyhow!("").into()).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_virtual_manifest(
|
fn to_virtual_manifest(
|
||||||
|
@ -256,11 +256,14 @@ fn invalid6() {
|
|||||||
p.cargo("check --features foo")
|
p.cargo("check --features foo")
|
||||||
.with_status(101)
|
.with_status(101)
|
||||||
.with_stderr_data(str![[r#"
|
.with_stderr_data(str![[r#"
|
||||||
|
[ERROR] feature `foo` includes `bar/baz`, but `bar` is not a dependency
|
||||||
|
--> Cargo.toml:9:23
|
||||||
|
|
|
||||||
|
9 | foo = ["bar/baz"]
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
||||||
|
|
||||||
Caused by:
|
|
||||||
feature `foo` includes `bar/baz`, but `bar` is not a dependency
|
|
||||||
|
|
||||||
"#]])
|
"#]])
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
@ -288,11 +291,14 @@ fn invalid7() {
|
|||||||
p.cargo("check --features foo")
|
p.cargo("check --features foo")
|
||||||
.with_status(101)
|
.with_status(101)
|
||||||
.with_stderr_data(str![[r#"
|
.with_stderr_data(str![[r#"
|
||||||
|
[ERROR] feature `foo` includes `bar/baz`, but `bar` is not a dependency
|
||||||
|
--> Cargo.toml:9:23
|
||||||
|
|
|
||||||
|
9 | foo = ["bar/baz"]
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
||||||
|
|
||||||
Caused by:
|
|
||||||
feature `foo` includes `bar/baz`, but `bar` is not a dependency
|
|
||||||
|
|
||||||
"#]])
|
"#]])
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
@ -254,11 +254,14 @@ fn inactive_weak_optional_dep() {
|
|||||||
.masquerade_as_nightly_cargo(&["cargo-lints", "edition2024"])
|
.masquerade_as_nightly_cargo(&["cargo-lints", "edition2024"])
|
||||||
.with_status(101)
|
.with_status(101)
|
||||||
.with_stderr_data(str![[r#"
|
.with_stderr_data(str![[r#"
|
||||||
|
[ERROR] feature `foo_feature` includes `dep_name?/dep_feature`, but `dep_name` is not a dependency
|
||||||
|
--> Cargo.toml:11:27
|
||||||
|
|
|
||||||
|
11 | foo_feature = ["dep_name?/dep_feature"]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
||||||
|
|
||||||
Caused by:
|
|
||||||
feature `foo_feature` includes `dep_name?/dep_feature`, but `dep_name` is not a dependency
|
|
||||||
|
|
||||||
"#]])
|
"#]])
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
@ -287,11 +290,19 @@ Caused by:
|
|||||||
.masquerade_as_nightly_cargo(&["cargo-lints", "edition2024"])
|
.masquerade_as_nightly_cargo(&["cargo-lints", "edition2024"])
|
||||||
.with_status(101)
|
.with_status(101)
|
||||||
.with_stderr_data(str![[r#"
|
.with_stderr_data(str![[r#"
|
||||||
|
[ERROR] feature `foo_feature` includes `dep_name?/dep_feature`, but `dep_name` is not a dependency
|
||||||
|
--> Cargo.toml:12:31
|
||||||
|
|
|
||||||
|
9 | dep_name = { version = "0.1.0", optional = true }
|
||||||
|
| -------- `dep_name` is an unused optional dependency since no feature enables it
|
||||||
|
10 |
|
||||||
|
11 | [features]
|
||||||
|
12 | foo_feature = ["dep_name?/dep_feature"]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= [HELP] enable the dependency with `dep:dep_name`
|
||||||
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
||||||
|
|
||||||
Caused by:
|
|
||||||
feature `foo_feature` includes `dep_name?/dep_feature`, but missing `dep:dep_name` to activate it
|
|
||||||
|
|
||||||
"#]])
|
"#]])
|
||||||
.run();
|
.run();
|
||||||
// Check target.'cfg(unix)'.dependencies can work
|
// Check target.'cfg(unix)'.dependencies can work
|
||||||
@ -319,11 +330,19 @@ Caused by:
|
|||||||
.masquerade_as_nightly_cargo(&["cargo-lints", "edition2024"])
|
.masquerade_as_nightly_cargo(&["cargo-lints", "edition2024"])
|
||||||
.with_status(101)
|
.with_status(101)
|
||||||
.with_stderr_data(str![[r#"
|
.with_stderr_data(str![[r#"
|
||||||
|
[ERROR] feature `foo_feature` includes `dep_name?/dep_feature`, but `dep_name` is not a dependency
|
||||||
|
--> Cargo.toml:12:27
|
||||||
|
|
|
||||||
|
9 | dep_name = { version = "0.1.0", optional = true }
|
||||||
|
| -------- `dep_name` is an unused optional dependency since no feature enables it
|
||||||
|
10 |
|
||||||
|
11 | [features]
|
||||||
|
12 | foo_feature = ["dep_name?/dep_feature"]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= [HELP] enable the dependency with `dep:dep_name`
|
||||||
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
||||||
|
|
||||||
Caused by:
|
|
||||||
feature `foo_feature` includes `dep_name?/dep_feature`, but missing `dep:dep_name` to activate it
|
|
||||||
|
|
||||||
"#]])
|
"#]])
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user