Skip to content

Commit 864bf96

Browse files
committed
feat(lint): lint also workspace dependencies
This has a future performance that version requirments in `[workspace.dependencies]` shoud avoid reparse
1 parent 1464f97 commit 864bf96

File tree

4 files changed

+125
-0
lines changed

4 files changed

+125
-0
lines changed

src/cargo/core/workspace.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,6 +1340,13 @@ impl<'gctx> Workspace<'gctx> {
13401340

13411341
if self.gctx.cli_unstable().cargo_lints {
13421342
// Calls to lint functions go in here
1343+
implicit_minimum_version_req(
1344+
self.root_maybe().into(),
1345+
self.root_manifest(),
1346+
&cargo_lints,
1347+
&mut error_count,
1348+
self.gctx,
1349+
)?;
13431350
}
13441351

13451352
// This is a short term hack to allow `blanket_hint_mostly_unused`

src/cargo/util/lints/implicit_minimum_version_req.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ use annotate_snippets::Level;
77
use annotate_snippets::Patch;
88
use annotate_snippets::Snippet;
99
use cargo_platform::Platform;
10+
use cargo_util_schemas::manifest::TomlDependency;
1011
use cargo_util_schemas::manifest::TomlToolLints;
1112
use toml::de::DeValue;
1213

1314
use crate::CargoResult;
1415
use crate::GlobalContext;
1516
use crate::core::Manifest;
17+
use crate::core::MaybePackage;
1618
use crate::core::Package;
1719
use crate::util::OptVersionReq;
1820
use crate::util::lints::Lint;
@@ -98,6 +100,14 @@ pub fn implicit_minimum_version_req(
98100
ManifestFor::Package(pkg) => {
99101
lint_package(pkg, manifest_path, lint_level, reason, error_count, gctx)
100102
}
103+
ManifestFor::Workspace(maybe_pkg) => lint_workspace(
104+
maybe_pkg,
105+
manifest_path,
106+
lint_level,
107+
reason,
108+
error_count,
109+
gctx,
110+
),
101111
}
102112
}
103113

@@ -151,6 +161,72 @@ pub fn lint_package(
151161
Ok(())
152162
}
153163

164+
pub fn lint_workspace(
165+
maybe_pkg: &MaybePackage,
166+
manifest_path: String,
167+
lint_level: LintLevel,
168+
reason: LintLevelReason,
169+
error_count: &mut usize,
170+
gctx: &GlobalContext,
171+
) -> CargoResult<()> {
172+
let document = maybe_pkg.document();
173+
let contents = maybe_pkg.contents();
174+
let toml = match maybe_pkg {
175+
MaybePackage::Package(p) => p.manifest().normalized_toml(),
176+
MaybePackage::Virtual(vm) => vm.normalized_toml(),
177+
};
178+
let dep_iter = toml
179+
.workspace
180+
.as_ref()
181+
.and_then(|ws| ws.dependencies.as_ref())
182+
.into_iter()
183+
.flat_map(|deps| deps.iter())
184+
.map(|(name, dep)| {
185+
let name = name.as_str();
186+
let ver = match dep {
187+
TomlDependency::Simple(ver) => ver,
188+
TomlDependency::Detailed(detailed) => {
189+
let Some(ver) = detailed.version.as_ref() else {
190+
return (name, OptVersionReq::Any);
191+
};
192+
ver
193+
}
194+
};
195+
let req = semver::VersionReq::parse(ver)
196+
.map(Into::into)
197+
.unwrap_or(OptVersionReq::Any);
198+
(name, req)
199+
});
200+
201+
for (name_in_toml, version_req) in dep_iter {
202+
let Some(suggested_req) = get_suggested_version_req(&version_req) else {
203+
continue;
204+
};
205+
206+
let key_path = ["workspace", "dependencies", name_in_toml];
207+
208+
let Some(span) = span_of_version_req(document, &key_path) else {
209+
continue;
210+
};
211+
212+
let report = report(
213+
lint_level,
214+
reason,
215+
span,
216+
contents,
217+
&manifest_path,
218+
&suggested_req,
219+
);
220+
221+
if lint_level.is_error() {
222+
*error_count += 1;
223+
}
224+
gctx.shell().print_report(&report, lint_level.force())?;
225+
}
226+
227+
Ok(())
228+
}
229+
154230
pub fn span_of_version_req<'doc>(
155231
document: &'doc toml::Spanned<toml::de::DeTable<'static>>,
156232
path: &[&str],

src/cargo/util/lints/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ pub const LINTS: &[Lint] = &[
2323
pub enum ManifestFor<'a> {
2424
/// Lint runs for a specific package.
2525
Package(&'a Package),
26+
/// Lint runs for workspace-level config.
27+
Workspace(&'a MaybePackage),
2628
}
2729

2830
impl ManifestFor<'_> {
@@ -33,6 +35,7 @@ impl ManifestFor<'_> {
3335
p.manifest().edition(),
3436
p.manifest().unstable_features(),
3537
),
38+
ManifestFor::Workspace(p) => lint.level(pkg_lints, p.edition(), p.unstable_features()),
3639
}
3740
}
3841
}
@@ -43,6 +46,12 @@ impl<'a> From<&'a Package> for ManifestFor<'a> {
4346
}
4447
}
4548

49+
impl<'a> From<&'a MaybePackage> for ManifestFor<'a> {
50+
fn from(value: &'a MaybePackage) -> ManifestFor<'a> {
51+
ManifestFor::Workspace(value)
52+
}
53+
}
54+
4655
pub fn analyze_cargo_lints_table(
4756
pkg: &Package,
4857
path: &Path,

tests/testsuite/lints/implicit_minimum_version_req.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,17 @@ workspace = true
10291029
p.cargo("check -Zcargo-lints")
10301030
.masquerade_as_nightly_cargo(&["cargo-lints"])
10311031
.with_stderr_data(str![[r#"
1032+
[WARNING] dependency version requirement without an explicit minimum version
1033+
--> Cargo.toml:7:7
1034+
|
1035+
7 | dep = "1"
1036+
| ^^^ missing full version components
1037+
|
1038+
[HELP] consider specifying full `major.minor.patch` version components
1039+
|
1040+
7 | dep = "1.0.0"
1041+
| ++++
1042+
= [NOTE] `cargo::implicit_minimum_version_req` is set to `warn` in `[lints]`
10321043
[UPDATING] `dummy-registry` index
10331044
[LOCKING] 1 package to latest compatible version
10341045
[DOWNLOADING] crates ...
@@ -1075,6 +1086,17 @@ edition = "2021"
10751086
p.cargo("check -Zcargo-lints")
10761087
.masquerade_as_nightly_cargo(&["cargo-lints"])
10771088
.with_stderr_data(str![[r#"
1089+
[WARNING] dependency version requirement without an explicit minimum version
1090+
--> Cargo.toml:7:7
1091+
|
1092+
7 | dep = "1"
1093+
| ^^^ missing full version components
1094+
|
1095+
[HELP] consider specifying full `major.minor.patch` version components
1096+
|
1097+
7 | dep = "1.0.0"
1098+
| ++++
1099+
= [NOTE] `cargo::implicit_minimum_version_req` is set to `warn` in `[lints]`
10781100
[CHECKING] member v0.0.0 ([ROOT]/foo/member)
10791101
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
10801102
@@ -1122,6 +1144,17 @@ workspace = true
11221144
p.cargo("check -Zcargo-lints")
11231145
.masquerade_as_nightly_cargo(&["cargo-lints"])
11241146
.with_stderr_data(str![[r#"
1147+
[WARNING] dependency version requirement without an explicit minimum version
1148+
--> Cargo.toml:7:7
1149+
|
1150+
7 | dep = "1"
1151+
| ^^^ missing full version components
1152+
|
1153+
[HELP] consider specifying full `major.minor.patch` version components
1154+
|
1155+
7 | dep = "1.0.0"
1156+
| ++++
1157+
= [NOTE] `cargo::implicit_minimum_version_req` is set to `warn` in `[lints]`
11251158
[WARNING] dependency version requirement without an explicit minimum version
11261159
--> member/Cargo.toml:7:7
11271160
|

0 commit comments

Comments
 (0)