···7171 }
72727373 pub fn print(&self) -> String {
7474- self.pretty_explanation()
7474+ self.explanation_for_missing_version()
7575+ .or_else(|| self.explanation_for_complex_failure())
7576 .unwrap_or_else(|| self.fallback_explanation())
7677 }
77787979+ /// This catches the case in which we try adding a dependency's version that
8080+ /// doesn't exist. That produces a very simple decision tree with just two
8181+ /// nodes and can have an ad hoc explanation, telling the user the version
8282+ /// they tried adding doesn't exist!
8383+ ///
8484+ fn explanation_for_missing_version(&self) -> Option<String> {
8585+ if let DerivationTree::External(External::FromDependencyOf(
8686+ base,
8787+ _,
8888+ package,
8989+ package_ranges,
9090+ )) = &self.derivation_tree
9191+ && *base == self.root_package_name
9292+ {
9393+ let pretty_range = pretty_range(package_ranges);
9494+ let message = if is_single_version(package_ranges) {
9595+ format!("The package `{package}` doesn't have a version {pretty_range}.")
9696+ } else {
9797+ format!("The package `{package}` has no versions in the range {pretty_range}.",)
9898+ };
9999+ Some(message)
100100+ } else {
101101+ None
102102+ }
103103+ }
104104+78105 /// Tries and print a pretty explanation for the given resolution tree.
79106 /// If for some reason our heuristic to produce a nice error message fails
80107 /// we return `None` so we can still produce a good enough error message!
81108 ///
8282- fn pretty_explanation(&self) -> Option<String> {
109109+ fn explanation_for_complex_failure(&self) -> Option<String> {
83110 let root_package_index = self.nodes.get(self.root_package_name.as_str())?;
84111 let unresolvable_nodes = self.find_unresolvable_nodes();
85112 if unresolvable_nodes.is_empty() {
···357384 })
358385 .join(" or ")
359386}
387387+388388+fn is_single_version(range: &Ranges<Version>) -> bool {
389389+ // Note: at the time of writing this, `Ranges` has a method called
390390+ // `as_singleton` which, according to its doc, should do the same thing.
391391+ // However, it strangely seems to consider as a single version ranges like
392392+ // this one `> 11.0.0 and <= 12.0.0`. To me this doesn't read as a single
393393+ // version!
394394+395395+ // The range needs to have exactly one segment that includes exactly one
396396+ // version number.
397397+ let mut segments = range.iter();
398398+ if let Some((Included(lower), Included(upper))) = segments.next()
399399+ && segments.next().is_none()
400400+ {
401401+ lower == upper
402402+ } else {
403403+ false
404404+ }
405405+}
···11+---
22+source: compiler-core/src/dependency.rs
33+expression: message
44+---
55+error: Dependency resolution failed
66+77+The package `wibble` has no versions in the range >= 2.0.1.
···11---
22source: compiler-core/src/dependency.rs
33-assertion_line: 1091
43expression: message
55-snapshot_kind: text
64---
55+error: Dependency resolution failed
66+77There's no compatible version of `woo`:
88 - You require woo >= 2.0.0 and < 3.0.0
99 - You require wibble >= 1.0.0 and < 2.0.0