feat(lint): lint also workspace dependencies

This has a future performance that version requirments in
`[workspace.dependencies]` shoud avoid reparse
This commit is contained in:
Weihang Lo
2025-12-08 16:56:20 -05:00
parent 1464f97bc5
commit 864bf9677f
4 changed files with 125 additions and 0 deletions

View File

@@ -1340,6 +1340,13 @@ impl<'gctx> Workspace<'gctx> {
if self.gctx.cli_unstable().cargo_lints {
// Calls to lint functions go in here
implicit_minimum_version_req(
self.root_maybe().into(),
self.root_manifest(),
&cargo_lints,
&mut error_count,
self.gctx,
)?;
}
// This is a short term hack to allow `blanket_hint_mostly_unused`

View File

@@ -7,12 +7,14 @@ use annotate_snippets::Level;
use annotate_snippets::Patch;
use annotate_snippets::Snippet;
use cargo_platform::Platform;
use cargo_util_schemas::manifest::TomlDependency;
use cargo_util_schemas::manifest::TomlToolLints;
use toml::de::DeValue;
use crate::CargoResult;
use crate::GlobalContext;
use crate::core::Manifest;
use crate::core::MaybePackage;
use crate::core::Package;
use crate::util::OptVersionReq;
use crate::util::lints::Lint;
@@ -98,6 +100,14 @@ pub fn implicit_minimum_version_req(
ManifestFor::Package(pkg) => {
lint_package(pkg, manifest_path, lint_level, reason, error_count, gctx)
}
ManifestFor::Workspace(maybe_pkg) => lint_workspace(
maybe_pkg,
manifest_path,
lint_level,
reason,
error_count,
gctx,
),
}
}
@@ -151,6 +161,72 @@ pub fn lint_package(
Ok(())
}
pub fn lint_workspace(
maybe_pkg: &MaybePackage,
manifest_path: String,
lint_level: LintLevel,
reason: LintLevelReason,
error_count: &mut usize,
gctx: &GlobalContext,
) -> CargoResult<()> {
let document = maybe_pkg.document();
let contents = maybe_pkg.contents();
let toml = match maybe_pkg {
MaybePackage::Package(p) => p.manifest().normalized_toml(),
MaybePackage::Virtual(vm) => vm.normalized_toml(),
};
let dep_iter = toml
.workspace
.as_ref()
.and_then(|ws| ws.dependencies.as_ref())
.into_iter()
.flat_map(|deps| deps.iter())
.map(|(name, dep)| {
let name = name.as_str();
let ver = match dep {
TomlDependency::Simple(ver) => ver,
TomlDependency::Detailed(detailed) => {
let Some(ver) = detailed.version.as_ref() else {
return (name, OptVersionReq::Any);
};
ver
}
};
let req = semver::VersionReq::parse(ver)
.map(Into::into)
.unwrap_or(OptVersionReq::Any);
(name, req)
});
for (name_in_toml, version_req) in dep_iter {
let Some(suggested_req) = get_suggested_version_req(&version_req) else {
continue;
};
let key_path = ["workspace", "dependencies", name_in_toml];
let Some(span) = span_of_version_req(document, &key_path) else {
continue;
};
let report = report(
lint_level,
reason,
span,
contents,
&manifest_path,
&suggested_req,
);
if lint_level.is_error() {
*error_count += 1;
}
gctx.shell().print_report(&report, lint_level.force())?;
}
Ok(())
}
pub fn span_of_version_req<'doc>(
document: &'doc toml::Spanned<toml::de::DeTable<'static>>,
path: &[&str],

View File

@@ -23,6 +23,8 @@ pub const LINTS: &[Lint] = &[
pub enum ManifestFor<'a> {
/// Lint runs for a specific package.
Package(&'a Package),
/// Lint runs for workspace-level config.
Workspace(&'a MaybePackage),
}
impl ManifestFor<'_> {
@@ -33,6 +35,7 @@ impl ManifestFor<'_> {
p.manifest().edition(),
p.manifest().unstable_features(),
),
ManifestFor::Workspace(p) => lint.level(pkg_lints, p.edition(), p.unstable_features()),
}
}
}
@@ -43,6 +46,12 @@ impl<'a> From<&'a Package> for ManifestFor<'a> {
}
}
impl<'a> From<&'a MaybePackage> for ManifestFor<'a> {
fn from(value: &'a MaybePackage) -> ManifestFor<'a> {
ManifestFor::Workspace(value)
}
}
pub fn analyze_cargo_lints_table(
pkg: &Package,
path: &Path,

View File

@@ -1029,6 +1029,17 @@ workspace = true
p.cargo("check -Zcargo-lints")
.masquerade_as_nightly_cargo(&["cargo-lints"])
.with_stderr_data(str![[r#"
[WARNING] dependency version requirement without an explicit minimum version
--> Cargo.toml:7:7
|
7 | dep = "1"
| ^^^ missing full version components
|
[HELP] consider specifying full `major.minor.patch` version components
|
7 | dep = "1.0.0"
| ++++
= [NOTE] `cargo::implicit_minimum_version_req` is set to `warn` in `[lints]`
[UPDATING] `dummy-registry` index
[LOCKING] 1 package to latest compatible version
[DOWNLOADING] crates ...
@@ -1075,6 +1086,17 @@ edition = "2021"
p.cargo("check -Zcargo-lints")
.masquerade_as_nightly_cargo(&["cargo-lints"])
.with_stderr_data(str![[r#"
[WARNING] dependency version requirement without an explicit minimum version
--> Cargo.toml:7:7
|
7 | dep = "1"
| ^^^ missing full version components
|
[HELP] consider specifying full `major.minor.patch` version components
|
7 | dep = "1.0.0"
| ++++
= [NOTE] `cargo::implicit_minimum_version_req` is set to `warn` in `[lints]`
[CHECKING] member v0.0.0 ([ROOT]/foo/member)
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
@@ -1122,6 +1144,17 @@ workspace = true
p.cargo("check -Zcargo-lints")
.masquerade_as_nightly_cargo(&["cargo-lints"])
.with_stderr_data(str![[r#"
[WARNING] dependency version requirement without an explicit minimum version
--> Cargo.toml:7:7
|
7 | dep = "1"
| ^^^ missing full version components
|
[HELP] consider specifying full `major.minor.patch` version components
|
7 | dep = "1.0.0"
| ++++
= [NOTE] `cargo::implicit_minimum_version_req` is set to `warn` in `[lints]`
[WARNING] dependency version requirement without an explicit minimum version
--> member/Cargo.toml:7:7
|