1use std::fmt;
2use std::fmt::Write as _;
3use std::task::Poll;
4
5use crate::core::{Dependency, PackageId, Registry, Summary};
6use crate::sources::IndexSummary;
7use crate::sources::source::QueryKind;
8use crate::util::edit_distance::{closest, edit_distance};
9use crate::util::errors::CargoResult;
10use crate::util::{GlobalContext, OptVersionReq, VersionExt};
11use anyhow::Error;
12
13use super::context::ResolverContext;
14use super::types::{ConflictMap, ConflictReason};
15
16pub struct ResolveError {
18 cause: Error,
19 package_path: Vec<PackageId>,
20}
21
22impl ResolveError {
23 pub fn new<E: Into<Error>>(cause: E, package_path: Vec<PackageId>) -> Self {
24 Self {
25 cause: cause.into(),
26 package_path,
27 }
28 }
29
30 pub fn package_path(&self) -> &[PackageId] {
33 &self.package_path
34 }
35}
36
37impl std::error::Error for ResolveError {
38 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
39 self.cause.source()
40 }
41}
42
43impl fmt::Debug for ResolveError {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 self.cause.fmt(f)
46 }
47}
48
49impl fmt::Display for ResolveError {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 self.cause.fmt(f)
52 }
53}
54
55pub type ActivateResult<T> = Result<T, ActivateError>;
56
57#[derive(Debug)]
58pub enum ActivateError {
59 Fatal(anyhow::Error),
60 Conflict(PackageId, ConflictReason),
61}
62
63impl From<::anyhow::Error> for ActivateError {
64 fn from(t: ::anyhow::Error) -> Self {
65 ActivateError::Fatal(t)
66 }
67}
68
69impl From<(PackageId, ConflictReason)> for ActivateError {
70 fn from(t: (PackageId, ConflictReason)) -> Self {
71 ActivateError::Conflict(t.0, t.1)
72 }
73}
74
75pub(super) fn activation_error(
76 resolver_ctx: &ResolverContext,
77 registry: &mut dyn Registry,
78 parent: &Summary,
79 dep: &Dependency,
80 conflicting_activations: &ConflictMap,
81 candidates: &[Summary],
82 gctx: Option<&GlobalContext>,
83) -> ResolveError {
84 let to_resolve_err = |err| {
85 ResolveError::new(
86 err,
87 resolver_ctx
88 .parents
89 .path_to_bottom(&parent.package_id())
90 .into_iter()
91 .map(|(node, _)| node)
92 .cloned()
93 .collect(),
94 )
95 };
96
97 if !candidates.is_empty() {
98 let mut msg = format!("failed to select a version for `{}`.", dep.package_name());
99 msg.push_str("\n ... required by ");
100 msg.push_str(&describe_path_in_context(
101 resolver_ctx,
102 &parent.package_id(),
103 ));
104
105 msg.push_str("\nversions that meet the requirements `");
106 msg.push_str(&dep.version_req().to_string());
107 msg.push_str("` ");
108
109 if let Some(v) = dep.version_req().locked_version() {
110 msg.push_str("(locked to ");
111 msg.push_str(&v.to_string());
112 msg.push_str(") ");
113 }
114
115 msg.push_str("are: ");
116 msg.push_str(
117 &candidates
118 .iter()
119 .map(|v| v.version())
120 .map(|v| v.to_string())
121 .collect::<Vec<_>>()
122 .join(", "),
123 );
124
125 let mut conflicting_activations: Vec<_> = conflicting_activations.iter().collect();
126 conflicting_activations.sort_unstable();
127 conflicting_activations.reverse();
131 let mut has_semver = false;
133
134 for (p, r) in &conflicting_activations {
135 match r {
136 ConflictReason::Semver => {
137 has_semver = true;
138 }
139 ConflictReason::Links(link) => {
140 msg.push_str("\n\npackage `");
141 msg.push_str(&*dep.package_name());
142 msg.push_str("` links to the native library `");
143 msg.push_str(link);
144 msg.push_str("`, but it conflicts with a previous package which links to `");
145 msg.push_str(link);
146 msg.push_str("` as well:\n");
147 msg.push_str(&describe_path_in_context(resolver_ctx, p));
148 msg.push_str("\nOnly one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. ");
149 msg.push_str("Try to adjust your dependencies so that only one package uses the `links = \"");
150 msg.push_str(link);
151 msg.push_str("\"` value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links.");
152 }
153 ConflictReason::MissingFeature(feature) => {
154 msg.push_str("\n\npackage `");
155 msg.push_str(&*p.name());
156 msg.push_str("` depends on `");
157 msg.push_str(&*dep.package_name());
158 msg.push_str("` with feature `");
159 msg.push_str(feature);
160 msg.push_str("` but `");
161 msg.push_str(&*dep.package_name());
162 msg.push_str("` does not have that feature.\n");
163 let latest = candidates.last().expect("in the non-empty branch");
164 if let Some(closest) = closest(feature, latest.features().keys(), |k| k) {
165 msg.push_str(" package `");
166 msg.push_str(&*dep.package_name());
167 msg.push_str("` does have feature `");
168 msg.push_str(closest);
169 msg.push_str("`\n");
170 } else if !latest.features().is_empty() {
171 let mut features: Vec<_> =
172 latest.features().keys().map(|f| f.as_str()).collect();
173 features.sort();
174 msg.push_str(" available features: ");
175 msg.push_str(&features.join(", "));
176 msg.push_str("\n");
177 }
178 }
180 ConflictReason::RequiredDependencyAsFeature(feature) => {
181 msg.push_str("\n\npackage `");
182 msg.push_str(&*p.name());
183 msg.push_str("` depends on `");
184 msg.push_str(&*dep.package_name());
185 msg.push_str("` with feature `");
186 msg.push_str(feature);
187 msg.push_str("` but `");
188 msg.push_str(&*dep.package_name());
189 msg.push_str("` does not have that feature.\n");
190 msg.push_str(
191 " A required dependency with that name exists, \
192 but only optional dependencies can be used as features.\n",
193 );
194 }
196 ConflictReason::NonImplicitDependencyAsFeature(feature) => {
197 msg.push_str("\n\npackage `");
198 msg.push_str(&*p.name());
199 msg.push_str("` depends on `");
200 msg.push_str(&*dep.package_name());
201 msg.push_str("` with feature `");
202 msg.push_str(feature);
203 msg.push_str("` but `");
204 msg.push_str(&*dep.package_name());
205 msg.push_str("` does not have that feature.\n");
206 msg.push_str(
207 " An optional dependency with that name exists, \
208 but that dependency uses the \"dep:\" \
209 syntax in the features table, so it does not have an \
210 implicit feature with that name.\n",
211 );
212 }
214 }
215 }
216
217 if has_semver {
218 msg.push_str("\n\nall possible versions conflict with previously selected packages.");
220 for (p, r) in &conflicting_activations {
221 if let ConflictReason::Semver = r {
222 msg.push_str("\n\n previously selected ");
223 msg.push_str(&describe_path_in_context(resolver_ctx, p));
224 }
225 }
226 }
227
228 msg.push_str("\n\nfailed to select a version for `");
229 msg.push_str(&*dep.package_name());
230 msg.push_str("` which could resolve this conflict");
231
232 return to_resolve_err(anyhow::format_err!("{}", msg));
233 }
234
235 let mut msg = String::new();
238 let mut hints = String::new();
239 if let Some(version_candidates) = rejected_versions(registry, dep) {
240 let version_candidates = match version_candidates {
241 Ok(c) => c,
242 Err(e) => return to_resolve_err(e),
243 };
244
245 let locked_version = dep
246 .version_req()
247 .locked_version()
248 .map(|v| format!(" (locked to {})", v))
249 .unwrap_or_default();
250 let _ = writeln!(
251 &mut msg,
252 "failed to select a version for the requirement `{} = \"{}\"`{}",
253 dep.package_name(),
254 dep.version_req(),
255 locked_version
256 );
257 for candidate in version_candidates {
258 match candidate {
259 IndexSummary::Candidate(summary) => {
260 let _ = writeln!(&mut msg, " version {} is unavailable", summary.version());
263 }
264 IndexSummary::Yanked(summary) => {
265 let _ = writeln!(&mut msg, " version {} is yanked", summary.version());
266 }
267 IndexSummary::Offline(summary) => {
268 let _ = writeln!(&mut msg, " version {} is not cached", summary.version());
269 }
270 IndexSummary::Unsupported(summary, schema_version) => {
271 if let Some(rust_version) = summary.rust_version() {
272 let _ = writeln!(
275 &mut msg,
276 " version {} requires cargo {}",
277 summary.version(),
278 rust_version
279 );
280 } else {
281 let _ = writeln!(
282 &mut msg,
283 " version {} requires a Cargo version that supports index version {}",
284 summary.version(),
285 schema_version
286 );
287 }
288 }
289 IndexSummary::Invalid(summary) => {
290 let _ = writeln!(
291 &mut msg,
292 " version {}'s index entry is invalid",
293 summary.version()
294 );
295 }
296 }
297 }
298 } else if let Some(candidates) = alt_versions(registry, dep) {
299 let candidates = match candidates {
300 Ok(c) => c,
301 Err(e) => return to_resolve_err(e),
302 };
303 let versions = {
304 let mut versions = candidates
305 .iter()
306 .take(3)
307 .map(|cand| cand.version().to_string())
308 .collect::<Vec<_>>();
309
310 if candidates.len() > 3 {
311 versions.push("...".into());
312 }
313
314 versions.join(", ")
315 };
316
317 let locked_version = dep
318 .version_req()
319 .locked_version()
320 .map(|v| format!(" (locked to {})", v))
321 .unwrap_or_default();
322
323 let _ = writeln!(
324 &mut msg,
325 "failed to select a version for the requirement `{} = \"{}\"`{}",
326 dep.package_name(),
327 dep.version_req(),
328 locked_version,
329 );
330 let _ = writeln!(
331 &mut msg,
332 "candidate versions found which didn't match: {versions}",
333 );
334
335 if let Some(pre) = candidates.iter().find(|c| c.version().is_prerelease()) {
337 let _ = write!(
338 &mut hints,
339 "\nif you are looking for the prerelease package it needs to be specified explicitly"
340 );
341 let _ = write!(
342 &mut hints,
343 "\n {} = {{ version = \"{}\" }}",
344 pre.name(),
345 pre.version()
346 );
347 }
348
349 if dep.source_id().is_path() && dep.version_req().is_locked() {
353 let _ = write!(
354 &mut hints,
355 "\nconsider running `cargo update` to update \
356 a path dependency's locked version",
357 );
358 }
359
360 if registry.is_replaced(dep.source_id()) {
361 let _ = write!(
362 &mut hints,
363 "\nperhaps a crate was updated and forgotten to be re-vendored?"
364 );
365 }
366 } else if let Some(name_candidates) = alt_names(registry, dep) {
367 let name_candidates = match name_candidates {
368 Ok(c) => c,
369 Err(e) => return to_resolve_err(e),
370 };
371 let _ = writeln!(&mut msg, "no matching package found",);
372 let _ = writeln!(&mut msg, "searched package name: `{}`", dep.package_name());
373 let mut names = name_candidates
374 .iter()
375 .take(3)
376 .map(|c| c.1.name().as_str())
377 .collect::<Vec<_>>();
378
379 if name_candidates.len() > 3 {
380 names.push("...");
381 }
382 let suggestions =
385 names
386 .iter()
387 .enumerate()
388 .fold(String::default(), |acc, (i, el)| match i {
389 0 => acc + el,
390 i if names.len() - 1 == i && name_candidates.len() <= 3 => acc + " or " + el,
391 _ => acc + ", " + el,
392 });
393 let _ = writeln!(&mut msg, "perhaps you meant: {suggestions}");
394 } else {
395 let _ = writeln!(
396 &mut msg,
397 "no matching package named `{}` found",
398 dep.package_name()
399 );
400 }
401
402 let mut location_searched_msg = registry.describe_source(dep.source_id());
403 if location_searched_msg.is_empty() {
404 location_searched_msg = format!("{}", dep.source_id());
405 }
406 let _ = writeln!(&mut msg, "location searched: {}", location_searched_msg);
407 let _ = write!(
408 &mut msg,
409 "required by {}",
410 describe_path_in_context(resolver_ctx, &parent.package_id()),
411 );
412
413 if let Some(gctx) = gctx {
414 if let Some(offline_flag) = gctx.offline_flag() {
415 let _ = write!(
416 &mut hints,
417 "\nAs a reminder, you're using offline mode ({offline_flag}) \
418 which can sometimes cause surprising resolution failures, \
419 if this error is too confusing you may wish to retry \
420 without `{offline_flag}`.",
421 );
422 }
423 }
424
425 to_resolve_err(anyhow::format_err!("{msg}{hints}"))
426}
427
428fn alt_versions(
432 registry: &mut dyn Registry,
433 dep: &Dependency,
434) -> Option<CargoResult<Vec<Summary>>> {
435 let mut wild_dep = dep.clone();
436 wild_dep.set_version_req(OptVersionReq::Any);
437
438 let candidates = loop {
439 match registry.query_vec(&wild_dep, QueryKind::Exact) {
440 Poll::Ready(Ok(candidates)) => break candidates,
441 Poll::Ready(Err(e)) => return Some(Err(e)),
442 Poll::Pending => match registry.block_until_ready() {
443 Ok(()) => continue,
444 Err(e) => return Some(Err(e)),
445 },
446 }
447 };
448 let mut candidates: Vec<_> = candidates.into_iter().map(|s| s.into_summary()).collect();
449 candidates.sort_unstable_by(|a, b| b.version().cmp(a.version()));
450 if candidates.is_empty() {
451 None
452 } else {
453 Some(Ok(candidates))
454 }
455}
456
457fn rejected_versions(
459 registry: &mut dyn Registry,
460 dep: &Dependency,
461) -> Option<CargoResult<Vec<IndexSummary>>> {
462 let mut version_candidates = loop {
463 match registry.query_vec(&dep, QueryKind::RejectedVersions) {
464 Poll::Ready(Ok(candidates)) => break candidates,
465 Poll::Ready(Err(e)) => return Some(Err(e)),
466 Poll::Pending => match registry.block_until_ready() {
467 Ok(()) => continue,
468 Err(e) => return Some(Err(e)),
469 },
470 }
471 };
472 version_candidates.sort_unstable_by_key(|a| a.as_summary().version().clone());
473 if version_candidates.is_empty() {
474 None
475 } else {
476 Some(Ok(version_candidates))
477 }
478}
479
480fn alt_names(
483 registry: &mut dyn Registry,
484 dep: &Dependency,
485) -> Option<CargoResult<Vec<(usize, Summary)>>> {
486 let mut wild_dep = dep.clone();
487 wild_dep.set_version_req(OptVersionReq::Any);
488
489 let name_candidates = loop {
490 match registry.query_vec(&wild_dep, QueryKind::AlternativeNames) {
491 Poll::Ready(Ok(candidates)) => break candidates,
492 Poll::Ready(Err(e)) => return Some(Err(e)),
493 Poll::Pending => match registry.block_until_ready() {
494 Ok(()) => continue,
495 Err(e) => return Some(Err(e)),
496 },
497 }
498 };
499 let mut name_candidates: Vec<_> = name_candidates
500 .into_iter()
501 .map(|s| s.into_summary())
502 .collect();
503 name_candidates.sort_unstable_by_key(|a| a.name());
504 name_candidates.dedup_by(|a, b| a.name() == b.name());
505 let mut name_candidates: Vec<_> = name_candidates
506 .into_iter()
507 .filter_map(|n| Some((edit_distance(&*wild_dep.package_name(), &*n.name(), 3)?, n)))
508 .collect();
509 name_candidates.sort_by_key(|o| o.0);
510
511 if name_candidates.is_empty() {
512 None
513 } else {
514 Some(Ok(name_candidates))
515 }
516}
517
518pub(super) fn describe_path_in_context(cx: &ResolverContext, id: &PackageId) -> String {
521 let iter = cx
522 .parents
523 .path_to_bottom(id)
524 .into_iter()
525 .map(|(p, d)| (p, d.and_then(|d| d.iter().next())));
526 describe_path(iter)
527}
528
529pub(crate) fn describe_path<'a>(
539 mut path: impl Iterator<Item = (&'a PackageId, Option<&'a Dependency>)>,
540) -> String {
541 use std::fmt::Write;
542
543 if let Some(p) = path.next() {
544 let mut dep_path_desc = format!("package `{}`", p.0);
545 for (pkg, dep) in path {
546 let dep = dep.unwrap();
547 let source_kind = if dep.source_id().is_path() {
548 "path "
549 } else if dep.source_id().is_git() {
550 "git "
551 } else {
552 ""
553 };
554 let requirement = if source_kind.is_empty() {
555 format!("{} = \"{}\"", dep.name_in_toml(), dep.version_req())
556 } else {
557 dep.name_in_toml().to_string()
558 };
559 let locked_version = dep
560 .version_req()
561 .locked_version()
562 .map(|v| format!("(locked to {}) ", v))
563 .unwrap_or_default();
564
565 write!(
566 dep_path_desc,
567 "\n ... which satisfies {}dependency `{}` {}of package `{}`",
568 source_kind, requirement, locked_version, pkg
569 )
570 .unwrap();
571 }
572
573 return dep_path_desc;
574 }
575
576 String::new()
577}