Skip to content

Commit 1d54a2b

Browse files
committed
fix(resolver): Report invalid index entries
While #14897 reported packages with an unsupported index schema version, that only worked if the changes in the schema version did not cause errors in deserializing `IndexPackage` or in generating a `Summary`. This extends that change by recoverying on error with a more lax, incomplete parse of `IndexPackage` which should always generate a valid `Summary`. To help with a buggy Index, we also will report as many as we can. This does not provide a way to report to users or log on cache reads if the index entry is not at least `{"name": "<string>", "vers": "<semver>"}`. As a side effect, the index cache will include more "invalid" index entries. That should be ok as we will ignore the invalid entry in the cache when loading it. Ignoring of invalid entries dates back to #6880 when the index cache was introduced. Fixes #10623 Fixes #14894
1 parent 933ba6e commit 1d54a2b

File tree

4 files changed

+82
-8
lines changed

4 files changed

+82
-8
lines changed

src/cargo/core/resolver/errors.rs

+7
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,13 @@ pub(super) fn activation_error(
271271
);
272272
}
273273
}
274+
IndexSummary::Invalid(summary) => {
275+
let _ = writeln!(
276+
&mut msg,
277+
" version {}'s index entry is invalid",
278+
summary.version()
279+
);
280+
}
274281
}
275282
}
276283
} else if let Some(candidates) = alt_versions(registry, dep) {

src/cargo/sources/registry/index/mod.rs

+62-4
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ pub enum IndexSummary {
135135
Offline(Summary),
136136
/// From a newer schema version and is likely incomplete or inaccurate
137137
Unsupported(Summary, u32),
138+
/// An error was encountered despite being a supported schema version
139+
Invalid(Summary),
138140
}
139141

140142
impl IndexSummary {
@@ -144,7 +146,8 @@ impl IndexSummary {
144146
IndexSummary::Candidate(sum)
145147
| IndexSummary::Yanked(sum)
146148
| IndexSummary::Offline(sum)
147-
| IndexSummary::Unsupported(sum, _) => sum,
149+
| IndexSummary::Unsupported(sum, _)
150+
| IndexSummary::Invalid(sum) => sum,
148151
}
149152
}
150153

@@ -154,7 +157,8 @@ impl IndexSummary {
154157
IndexSummary::Candidate(sum)
155158
| IndexSummary::Yanked(sum)
156159
| IndexSummary::Offline(sum)
157-
| IndexSummary::Unsupported(sum, _) => sum,
160+
| IndexSummary::Unsupported(sum, _)
161+
| IndexSummary::Invalid(sum) => sum,
158162
}
159163
}
160164

@@ -164,6 +168,7 @@ impl IndexSummary {
164168
IndexSummary::Yanked(s) => IndexSummary::Yanked(f(s)),
165169
IndexSummary::Offline(s) => IndexSummary::Offline(f(s)),
166170
IndexSummary::Unsupported(s, v) => IndexSummary::Unsupported(f(s), v.clone()),
171+
IndexSummary::Invalid(s) => IndexSummary::Invalid(f(s)),
167172
}
168173
}
169174

@@ -282,6 +287,22 @@ impl IndexPackage<'_> {
282287
}
283288
}
284289

290+
#[derive(Deserialize, Serialize)]
291+
struct IndexPackageMinimum {
292+
name: InternedString,
293+
vers: Version,
294+
}
295+
296+
#[derive(Deserialize, Serialize, Default)]
297+
struct IndexPackageRustVersion {
298+
rust_version: Option<RustVersion>,
299+
}
300+
301+
#[derive(Deserialize, Serialize, Default)]
302+
struct IndexPackageV {
303+
v: Option<u32>,
304+
}
305+
285306
/// A dependency as encoded in the [`IndexPackage`] index JSON.
286307
#[derive(Deserialize, Serialize, Clone)]
287308
pub struct RegistryDependency<'a> {
@@ -729,10 +750,45 @@ impl IndexSummary {
729750
// between different versions that understand the index differently.
730751
// Make sure to consider the INDEX_V_MAX and CURRENT_CACHE_VERSION
731752
// values carefully when making changes here.
732-
let index: IndexPackage<'_> = serde_json::from_slice(line)?;
753+
let index_summary = (|| {
754+
let index = serde_json::from_slice::<IndexPackage<'_>>(line)?;
755+
let summary = index.to_summary(source_id)?;
756+
Ok((index, summary))
757+
})();
758+
let (index, summary, valid) = match index_summary {
759+
Ok((index, summary)) => (index, summary, true),
760+
Err(err) => {
761+
let Ok(IndexPackageMinimum { name, vers }) =
762+
serde_json::from_slice::<IndexPackageMinimum>(line)
763+
else {
764+
// If we can't recover, prefer the original error
765+
return Err(err);
766+
};
767+
tracing::info!(
768+
"recoverying from failed parse of registry package {name}@{vers}: {err}"
769+
);
770+
let IndexPackageRustVersion { rust_version } =
771+
serde_json::from_slice::<IndexPackageRustVersion>(line).unwrap_or_default();
772+
let IndexPackageV { v } =
773+
serde_json::from_slice::<IndexPackageV>(line).unwrap_or_default();
774+
let index = IndexPackage {
775+
name,
776+
vers,
777+
rust_version,
778+
v,
779+
deps: Default::default(),
780+
features: Default::default(),
781+
features2: Default::default(),
782+
cksum: Default::default(),
783+
yanked: Default::default(),
784+
links: Default::default(),
785+
};
786+
let summary = index.to_summary(source_id)?;
787+
(index, summary, false)
788+
}
789+
};
733790
let v = index.v.unwrap_or(1);
734791
tracing::trace!("json parsed registry {}/{}", index.name, index.vers);
735-
let summary = index.to_summary(source_id)?;
736792

737793
let v_max = if bindeps {
738794
INDEX_V_MAX + 1
@@ -742,6 +798,8 @@ impl IndexSummary {
742798

743799
if v_max < v {
744800
Ok(IndexSummary::Unsupported(summary, v))
801+
} else if !valid {
802+
Ok(IndexSummary::Invalid(summary))
745803
} else if index.yanked.unwrap_or(false) {
746804
Ok(IndexSummary::Yanked(summary))
747805
} else {

src/cargo/sources/registry/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,9 @@ impl<'gctx> Source for RegistrySource<'gctx> {
834834
summary.version()
835835
);
836836
}
837+
IndexSummary::Invalid(summary) => {
838+
tracing::debug!("invalid ({} {})", summary.name(), summary.version());
839+
}
837840
IndexSummary::Offline(summary) => {
838841
tracing::debug!("offline ({} {})", summary.name(), summary.version());
839842
}

tests/testsuite/registry.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -3012,10 +3012,13 @@ fn invalid_json_lines_error() {
30123012
.with_stderr_data(str![[r#"
30133013
[UPDATING] `dummy-registry` index
30143014
[ERROR] failed to select a version for the requirement `foo = "^0.1.1"`
3015-
candidate versions found which didn't match: 0.2.0, 0.1.0
3015+
version 0.1.3 requires cargo 1.2345
3016+
version 0.1.4 requires a Cargo version that supports index version 1000000000
3017+
version 0.1.5's index entry is invalid
3018+
version 0.1.6 requires a Cargo version that supports index version 1000000000
3019+
version 0.1.7's index entry is invalid
30163020
location searched: `dummy-registry` index (which is replacing registry `crates-io`)
30173021
required by package `a v0.5.0 ([ROOT]/foo)`
3018-
perhaps a crate was updated and forgotten to be re-vendored?
30193022
30203023
"#]])
30213024
.run();
@@ -3024,10 +3027,13 @@ perhaps a crate was updated and forgotten to be re-vendored?
30243027
.with_stderr_data(str![[r#"
30253028
[UPDATING] `dummy-registry` index
30263029
[ERROR] failed to select a version for the requirement `foo = "^0.1.1"`
3027-
candidate versions found which didn't match: 0.2.0, 0.1.0
3030+
version 0.1.3 requires cargo 1.2345
3031+
version 0.1.4 requires a Cargo version that supports index version 1000000000
3032+
version 0.1.5's index entry is invalid
3033+
version 0.1.6 requires a Cargo version that supports index version 1000000000
3034+
version 0.1.7's index entry is invalid
30283035
location searched: `dummy-registry` index (which is replacing registry `crates-io`)
30293036
required by package `a v0.5.0 ([ROOT]/foo)`
3030-
perhaps a crate was updated and forgotten to be re-vendored?
30313037
30323038
"#]])
30333039
.run();

0 commit comments

Comments
 (0)