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