Skip to content

Commit 4a166de

Browse files
committed
Auto merge of #9847 - weihanglo:issue-9840, r=Eh2406
Distinguish lockfile version req from normal dep in resolver error message Resolves #9840 This PR adds a new variant `OptVersionReq::Locked` as #9840 described. The new variant represents as a locked version requirement that contains an exact locked version and the original version requirement. Previously we use exact version req to synthesize locked version req. Now we make those need a truly locked to be `OptVersionReq::Locked`, including: - `[patch]`: previous used exact version req to emulate locked version, and it only need to lock dep version but not source ID, so we make an extra method `Dependency::lock_version` for it. - Dependencies from lock files: No need to change the call site.
2 parents a821e2c + 725420e commit 4a166de

File tree

7 files changed

+108
-26
lines changed

7 files changed

+108
-26
lines changed

src/cargo/core/dependency.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,6 @@ impl Dependency {
320320
/// Locks this dependency to depending on the specified package ID.
321321
pub fn lock_to(&mut self, id: PackageId) -> &mut Dependency {
322322
assert_eq!(self.inner.source_id, id.source_id());
323-
assert!(self.inner.req.matches(id.version()));
324323
trace!(
325324
"locking dep from `{}` with `{}` at {} to {}",
326325
self.package_name(),
@@ -329,7 +328,7 @@ impl Dependency {
329328
id
330329
);
331330
let me = Rc::make_mut(&mut self.inner);
332-
me.req = OptVersionReq::exact(id.version());
331+
me.req.lock_to(id.version());
333332

334333
// Only update the `precise` of this source to preserve other
335334
// information about dependency's source which may not otherwise be
@@ -340,10 +339,20 @@ impl Dependency {
340339
self
341340
}
342341

343-
/// Returns `true` if this is a "locked" dependency, basically whether it has
344-
/// an exact version req.
342+
/// Locks this dependency to a specified version.
343+
///
344+
/// Mainly used in dependency patching like `[patch]` or `[replace]`, which
345+
/// doesn't need to lock the entire dependency to a specific [`PackageId`].
346+
pub fn lock_version(&mut self, version: &semver::Version) -> &mut Dependency {
347+
let me = Rc::make_mut(&mut self.inner);
348+
me.req.lock_to(version);
349+
self
350+
}
351+
352+
/// Returns `true` if this is a "locked" dependency. Basically a locked
353+
/// dependency has an exact version req, but not vice versa.
345354
pub fn is_locked(&self) -> bool {
346-
self.inner.req.is_exact()
355+
self.inner.req.is_locked()
347356
}
348357

349358
/// Returns `false` if the dependency is only used to build the local package.

src/cargo/core/registry.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ use crate::core::{Dependency, PackageId, Source, SourceId, SourceMap, Summary};
55
use crate::sources::config::SourceConfigMap;
66
use crate::util::errors::CargoResult;
77
use crate::util::interning::InternedString;
8-
use crate::util::{profile, CanonicalUrl, Config, VersionReqExt};
8+
use crate::util::{profile, CanonicalUrl, Config};
99
use anyhow::{bail, Context as _};
1010
use log::{debug, trace};
11-
use semver::VersionReq;
1211
use url::Url;
1312

1413
/// Source of information about a group of packages.
@@ -765,8 +764,7 @@ fn lock(
765764
if locked.source_id() == dep.source_id() {
766765
dep.lock_to(locked);
767766
} else {
768-
let req = VersionReq::exact(locked.version());
769-
dep.set_version_req(req);
767+
dep.lock_version(locked.version());
770768
}
771769
return dep;
772770
}

src/cargo/core/resolver/errors.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -387,10 +387,16 @@ pub(crate) fn describe_path<'a>(
387387
} else {
388388
dep.name_in_toml().to_string()
389389
};
390+
let locked_version = dep
391+
.version_req()
392+
.locked_version()
393+
.map(|v| format!("(locked to {}) ", v))
394+
.unwrap_or_default();
395+
390396
write!(
391397
dep_path_desc,
392-
"\n ... which satisfies {}dependency `{}` of package `{}`",
393-
source_kind, requirement, pkg
398+
"\n ... which satisfies {}dependency `{}` {}of package `{}`",
399+
source_kind, requirement, locked_version, pkg
394400
)
395401
.unwrap();
396402
}

src/cargo/ops/cargo_install.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ fn installed_exact_package<T>(
681681
where
682682
T: Source,
683683
{
684-
if !dep.is_locked() {
684+
if !dep.version_req().is_exact() {
685685
// If the version isn't exact, we may need to update the registry and look for a newer
686686
// version - we can't know if the package is installed without doing so.
687687
return Ok(None);

src/cargo/util/semver_ext.rs

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use std::fmt::{self, Display};
55
pub enum OptVersionReq {
66
Any,
77
Req(VersionReq),
8+
/// The exact locked version and the original version requirement.
9+
Locked(Version, VersionReq),
810
}
911

1012
pub trait VersionExt {
@@ -49,22 +51,53 @@ impl OptVersionReq {
4951
cmp.op == Op::Exact && cmp.minor.is_some() && cmp.patch.is_some()
5052
}
5153
}
54+
OptVersionReq::Locked(..) => true,
55+
}
56+
}
57+
58+
pub fn lock_to(&mut self, version: &Version) {
59+
assert!(self.matches(version), "cannot lock {} to {}", self, version);
60+
use OptVersionReq::*;
61+
let version = version.clone();
62+
*self = match self {
63+
Any => Locked(version, VersionReq::STAR),
64+
Req(req) => Locked(version, req.clone()),
65+
Locked(_, req) => Locked(version, req.clone()),
66+
};
67+
}
68+
69+
pub fn is_locked(&self) -> bool {
70+
matches!(self, OptVersionReq::Locked(..))
71+
}
72+
73+
/// Gets the version to which this req is locked, if any.
74+
pub fn locked_version(&self) -> Option<&Version> {
75+
match self {
76+
OptVersionReq::Locked(version, _) => Some(version),
77+
_ => None,
5278
}
5379
}
5480

5581
pub fn matches(&self, version: &Version) -> bool {
5682
match self {
5783
OptVersionReq::Any => true,
5884
OptVersionReq::Req(req) => req.matches(version),
85+
OptVersionReq::Locked(v, _) => {
86+
v.major == version.major
87+
&& v.minor == version.minor
88+
&& v.patch == version.patch
89+
&& v.pre == version.pre
90+
}
5991
}
6092
}
6193
}
6294

6395
impl Display for OptVersionReq {
64-
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
96+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6597
match self {
66-
OptVersionReq::Any => formatter.write_str("*"),
67-
OptVersionReq::Req(req) => Display::fmt(req, formatter),
98+
OptVersionReq::Any => f.write_str("*"),
99+
OptVersionReq::Req(req) => Display::fmt(req, f),
100+
OptVersionReq::Locked(_, req) => Display::fmt(req, f),
68101
}
69102
}
70103
}
@@ -74,3 +107,40 @@ impl From<VersionReq> for OptVersionReq {
74107
OptVersionReq::Req(req)
75108
}
76109
}
110+
111+
#[cfg(test)]
112+
mod tests {
113+
use super::*;
114+
115+
#[test]
116+
fn locked_has_the_same_with_exact() {
117+
fn test_versions(target_ver: &str, vers: &[&str]) {
118+
let ver = Version::parse(target_ver).unwrap();
119+
let exact = OptVersionReq::exact(&ver);
120+
let mut locked = exact.clone();
121+
locked.lock_to(&ver);
122+
for v in vers {
123+
let v = Version::parse(v).unwrap();
124+
assert_eq!(exact.matches(&v), locked.matches(&v));
125+
}
126+
}
127+
128+
test_versions(
129+
"1.0.0",
130+
&["1.0.0", "1.0.1", "0.9.9", "0.10.0", "0.1.0", "1.0.0-pre"],
131+
);
132+
test_versions("0.9.0", &["0.9.0", "0.9.1", "1.9.0", "0.0.9", "0.9.0-pre"]);
133+
test_versions("0.0.2", &["0.0.2", "0.0.1", "0.0.3", "0.0.2-pre"]);
134+
test_versions(
135+
"0.1.0-beta2.a",
136+
&[
137+
"0.1.0-beta2.a",
138+
"0.9.1",
139+
"0.1.0",
140+
"0.1.1-beta2.a",
141+
"0.1.0-beta2",
142+
],
143+
);
144+
test_versions("0.1.0+meta", &["0.1.0", "0.1.0+meta", "0.1.0+any"]);
145+
}
146+
}

src/cargo/util/toml/mod.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,16 +1553,15 @@ impl TomlManifest {
15531553
}
15541554

15551555
let mut dep = replacement.to_dependency(spec.name().as_str(), cx, None)?;
1556-
{
1557-
let version = spec.version().ok_or_else(|| {
1558-
anyhow!(
1559-
"replacements must specify a version \
1560-
to replace, but `{}` does not",
1561-
spec
1562-
)
1563-
})?;
1564-
dep.set_version_req(VersionReq::exact(version));
1565-
}
1556+
let version = spec.version().ok_or_else(|| {
1557+
anyhow!(
1558+
"replacements must specify a version \
1559+
to replace, but `{}` does not",
1560+
spec
1561+
)
1562+
})?;
1563+
dep.set_version_req(VersionReq::exact(version))
1564+
.lock_version(version);
15661565
replace.push((spec, dep));
15671566
}
15681567
Ok(replace)

tests/testsuite/build_script.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1000,7 +1000,7 @@ fn links_duplicates_old_registry() {
10001000
but a native library can be linked only once
10011001
10021002
package `bar v0.1.0`
1003-
... which satisfies dependency `bar = \"=0.1.0\"` of package `foo v0.1.0 ([..]foo)`
1003+
... which satisfies dependency `bar = \"^0.1\"` (locked to 0.1.0) of package `foo v0.1.0 ([..]foo)`
10041004
links to native library `a`
10051005
10061006
package `foo v0.1.0 ([..]foo)`

0 commit comments

Comments
 (0)