1use std::fmt;
2use std::fmt::Write as _;
3use std::task::Poll;
4
5use crate::core::{Dependency, PackageId, Registry, Summary};
6use crate::sources::source::QueryKind;
7use crate::sources::IndexSummary;
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 }
171 }
173 ConflictReason::RequiredDependencyAsFeature(feature) => {
174 msg.push_str("\n\npackage `");
175 msg.push_str(&*p.name());
176 msg.push_str("` depends on `");
177 msg.push_str(&*dep.package_name());
178 msg.push_str("` with feature `");
179 msg.push_str(feature);
180 msg.push_str("` but `");
181 msg.push_str(&*dep.package_name());
182 msg.push_str("` does not have that feature.\n");
183 msg.push_str(
184 " A required dependency with that name exists, \
185 but only optional dependencies can be used as features.\n",
186 );
187 }
189 ConflictReason::NonImplicitDependencyAsFeature(feature) => {
190 msg.push_str("\n\npackage `");
191 msg.push_str(&*p.name());
192 msg.push_str("` depends on `");
193 msg.push_str(&*dep.package_name());
194 msg.push_str("` with feature `");
195 msg.push_str(feature);
196 msg.push_str("` but `");
197 msg.push_str(&*dep.package_name());
198 msg.push_str("` does not have that feature.\n");
199 msg.push_str(
200 " An optional dependency with that name exists, \
201 but that dependency uses the \"dep:\" \
202 syntax in the features table, so it does not have an \
203 implicit feature with that name.\n",
204 );
205 }
207 }
208 }
209
210 if has_semver {
211 msg.push_str("\n\nall possible versions conflict with previously selected packages.");
213 for (p, r) in &conflicting_activations {
214 if let ConflictReason::Semver = r {
215 msg.push_str("\n\n previously selected ");
216 msg.push_str(&describe_path_in_context(resolver_ctx, p));
217 }
218 }
219 }
220
221 msg.push_str("\n\nfailed to select a version for `");
222 msg.push_str(&*dep.package_name());
223 msg.push_str("` which could resolve this conflict");
224
225 return to_resolve_err(anyhow::format_err!("{}", msg));
226 }
227
228 let mut msg = String::new();
231 let mut hints = String::new();
232 if let Some(version_candidates) = rejected_versions(registry, dep) {
233 let version_candidates = match version_candidates {
234 Ok(c) => c,
235 Err(e) => return to_resolve_err(e),
236 };
237
238 let locked_version = dep
239 .version_req()
240 .locked_version()
241 .map(|v| format!(" (locked to {})", v))
242 .unwrap_or_default();
243 let _ = writeln!(
244 &mut msg,
245 "failed to select a version for the requirement `{} = \"{}\"`{}",
246 dep.package_name(),
247 dep.version_req(),
248 locked_version
249 );
250 for candidate in version_candidates {
251 match candidate {
252 IndexSummary::Candidate(summary) => {
253 let _ = writeln!(&mut msg, " version {} is unavailable", summary.version());
256 }
257 IndexSummary::Yanked(summary) => {
258 let _ = writeln!(&mut msg, " version {} is yanked", summary.version());
259 }
260 IndexSummary::Offline(summary) => {
261 let _ = writeln!(&mut msg, " version {} is not cached", summary.version());
262 }
263 IndexSummary::Unsupported(summary, schema_version) => {
264 if let Some(rust_version) = summary.rust_version() {
265 let _ = writeln!(
268 &mut msg,
269 " version {} requires cargo {}",
270 summary.version(),
271 rust_version
272 );
273 } else {
274 let _ = writeln!(
275 &mut msg,
276 " version {} requires a Cargo version that supports index version {}",
277 summary.version(),
278 schema_version
279 );
280 }
281 }
282 IndexSummary::Invalid(summary) => {
283 let _ = writeln!(
284 &mut msg,
285 " version {}'s index entry is invalid",
286 summary.version()
287 );
288 }
289 }
290 }
291 } else if let Some(candidates) = alt_versions(registry, dep) {
292 let candidates = match candidates {
293 Ok(c) => c,
294 Err(e) => return to_resolve_err(e),
295 };
296 let versions = {
297 let mut versions = candidates
298 .iter()
299 .take(3)
300 .map(|cand| cand.version().to_string())
301 .collect::<Vec<_>>();
302
303 if candidates.len() > 3 {
304 versions.push("...".into());
305 }
306
307 versions.join(", ")
308 };
309
310 let locked_version = dep
311 .version_req()
312 .locked_version()
313 .map(|v| format!(" (locked to {})", v))
314 .unwrap_or_default();
315
316 let _ = writeln!(
317 &mut msg,
318 "failed to select a version for the requirement `{} = \"{}\"`{}",
319 dep.package_name(),
320 dep.version_req(),
321 locked_version,
322 );
323 let _ = writeln!(
324 &mut msg,
325 "candidate versions found which didn't match: {versions}",
326 );
327
328 if let Some(pre) = candidates.iter().find(|c| c.version().is_prerelease()) {
330 let _ = write!(&mut hints, "\nif you are looking for the prerelease package it needs to be specified explicitly");
331 let _ = write!(
332 &mut hints,
333 "\n {} = {{ version = \"{}\" }}",
334 pre.name(),
335 pre.version()
336 );
337 }
338
339 if dep.source_id().is_path() && dep.version_req().is_locked() {
343 let _ = write!(
344 &mut hints,
345 "\nconsider running `cargo update` to update \
346 a path dependency's locked version",
347 );
348 }
349
350 if registry.is_replaced(dep.source_id()) {
351 let _ = write!(
352 &mut hints,
353 "\nperhaps a crate was updated and forgotten to be re-vendored?"
354 );
355 }
356 } else if let Some(name_candidates) = alt_names(registry, dep) {
357 let name_candidates = match name_candidates {
358 Ok(c) => c,
359 Err(e) => return to_resolve_err(e),
360 };
361 let _ = writeln!(&mut msg, "no matching package found",);
362 let _ = writeln!(&mut msg, "searched package name: `{}`", dep.package_name());
363 let mut names = name_candidates
364 .iter()
365 .take(3)
366 .map(|c| c.1.name().as_str())
367 .collect::<Vec<_>>();
368
369 if name_candidates.len() > 3 {
370 names.push("...");
371 }
372 let suggestions =
375 names
376 .iter()
377 .enumerate()
378 .fold(String::default(), |acc, (i, el)| match i {
379 0 => acc + el,
380 i if names.len() - 1 == i && name_candidates.len() <= 3 => acc + " or " + el,
381 _ => acc + ", " + el,
382 });
383 let _ = writeln!(&mut msg, "perhaps you meant: {suggestions}");
384 } else {
385 let _ = writeln!(
386 &mut msg,
387 "no matching package named `{}` found",
388 dep.package_name()
389 );
390 }
391
392 let mut location_searched_msg = registry.describe_source(dep.source_id());
393 if location_searched_msg.is_empty() {
394 location_searched_msg = format!("{}", dep.source_id());
395 }
396 let _ = writeln!(&mut msg, "location searched: {}", location_searched_msg);
397 let _ = write!(
398 &mut msg,
399 "required by {}",
400 describe_path_in_context(resolver_ctx, &parent.package_id()),
401 );
402
403 if let Some(gctx) = gctx {
404 if let Some(offline_flag) = gctx.offline_flag() {
405 let _ = write!(
406 &mut hints,
407 "\nAs a reminder, you're using offline mode ({offline_flag}) \
408 which can sometimes cause surprising resolution failures, \
409 if this error is too confusing you may wish to retry \
410 without `{offline_flag}`.",
411 );
412 }
413 }
414
415 to_resolve_err(anyhow::format_err!("{msg}{hints}"))
416}
417
418fn alt_versions(
422 registry: &mut dyn Registry,
423 dep: &Dependency,
424) -> Option<CargoResult<Vec<Summary>>> {
425 let mut wild_dep = dep.clone();
426 wild_dep.set_version_req(OptVersionReq::Any);
427
428 let candidates = loop {
429 match registry.query_vec(&wild_dep, QueryKind::Exact) {
430 Poll::Ready(Ok(candidates)) => break candidates,
431 Poll::Ready(Err(e)) => return Some(Err(e)),
432 Poll::Pending => match registry.block_until_ready() {
433 Ok(()) => continue,
434 Err(e) => return Some(Err(e)),
435 },
436 }
437 };
438 let mut candidates: Vec<_> = candidates.into_iter().map(|s| s.into_summary()).collect();
439 candidates.sort_unstable_by(|a, b| b.version().cmp(a.version()));
440 if candidates.is_empty() {
441 None
442 } else {
443 Some(Ok(candidates))
444 }
445}
446
447fn rejected_versions(
449 registry: &mut dyn Registry,
450 dep: &Dependency,
451) -> Option<CargoResult<Vec<IndexSummary>>> {
452 let mut version_candidates = loop {
453 match registry.query_vec(&dep, QueryKind::RejectedVersions) {
454 Poll::Ready(Ok(candidates)) => break candidates,
455 Poll::Ready(Err(e)) => return Some(Err(e)),
456 Poll::Pending => match registry.block_until_ready() {
457 Ok(()) => continue,
458 Err(e) => return Some(Err(e)),
459 },
460 }
461 };
462 version_candidates.sort_unstable_by_key(|a| a.as_summary().version().clone());
463 if version_candidates.is_empty() {
464 None
465 } else {
466 Some(Ok(version_candidates))
467 }
468}
469
470fn alt_names(
473 registry: &mut dyn Registry,
474 dep: &Dependency,
475) -> Option<CargoResult<Vec<(usize, Summary)>>> {
476 let mut wild_dep = dep.clone();
477 wild_dep.set_version_req(OptVersionReq::Any);
478
479 let name_candidates = loop {
480 match registry.query_vec(&wild_dep, QueryKind::AlternativeNames) {
481 Poll::Ready(Ok(candidates)) => break candidates,
482 Poll::Ready(Err(e)) => return Some(Err(e)),
483 Poll::Pending => match registry.block_until_ready() {
484 Ok(()) => continue,
485 Err(e) => return Some(Err(e)),
486 },
487 }
488 };
489 let mut name_candidates: Vec<_> = name_candidates
490 .into_iter()
491 .map(|s| s.into_summary())
492 .collect();
493 name_candidates.sort_unstable_by_key(|a| a.name());
494 name_candidates.dedup_by(|a, b| a.name() == b.name());
495 let mut name_candidates: Vec<_> = name_candidates
496 .into_iter()
497 .filter_map(|n| Some((edit_distance(&*wild_dep.package_name(), &*n.name(), 3)?, n)))
498 .collect();
499 name_candidates.sort_by_key(|o| o.0);
500
501 if name_candidates.is_empty() {
502 None
503 } else {
504 Some(Ok(name_candidates))
505 }
506}
507
508pub(super) fn describe_path_in_context(cx: &ResolverContext, id: &PackageId) -> String {
511 let iter = cx
512 .parents
513 .path_to_bottom(id)
514 .into_iter()
515 .map(|(p, d)| (p, d.and_then(|d| d.iter().next())));
516 describe_path(iter)
517}
518
519pub(crate) fn describe_path<'a>(
529 mut path: impl Iterator<Item = (&'a PackageId, Option<&'a Dependency>)>,
530) -> String {
531 use std::fmt::Write;
532
533 if let Some(p) = path.next() {
534 let mut dep_path_desc = format!("package `{}`", p.0);
535 for (pkg, dep) in path {
536 let dep = dep.unwrap();
537 let source_kind = if dep.source_id().is_path() {
538 "path "
539 } else if dep.source_id().is_git() {
540 "git "
541 } else {
542 ""
543 };
544 let requirement = if source_kind.is_empty() {
545 format!("{} = \"{}\"", dep.name_in_toml(), dep.version_req())
546 } else {
547 dep.name_in_toml().to_string()
548 };
549 let locked_version = dep
550 .version_req()
551 .locked_version()
552 .map(|v| format!("(locked to {}) ", v))
553 .unwrap_or_default();
554
555 write!(
556 dep_path_desc,
557 "\n ... which satisfies {}dependency `{}` {}of package `{}`",
558 source_kind, requirement, locked_version, pkg
559 )
560 .unwrap();
561 }
562
563 return dep_path_desc;
564 }
565
566 String::new()
567}