Skip to content

Commit fefbb68

Browse files
committed
Auto merge of #5691 - Eh2406:hyphen-underscore, r=alexcrichton
Make index lookup robust to _ vs -, but don't let the user get it wrong. This does a brute force search thru combinations of hyphen and underscores to allow queries of crates to pass the wrong one. This is a small first step of fixing #2775 Where is best to add test?
2 parents 06721dd + 95b4640 commit fefbb68

File tree

15 files changed

+370
-76
lines changed

15 files changed

+370
-76
lines changed

src/bin/cargo/main.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,11 @@ fn find_closest(config: &Config, cmd: &str) -> Option<String> {
113113
let cmds = list_commands(config);
114114
// Only consider candidates with a lev_distance of 3 or less so we don't
115115
// suggest out-of-the-blue options.
116-
let mut filtered = cmds.iter()
117-
.map(|&(ref c, _)| (lev_distance(c, cmd), c))
116+
cmds.into_iter()
117+
.map(|(c, _)| (lev_distance(&c, cmd), c))
118118
.filter(|&(d, _)| d < 4)
119-
.collect::<Vec<_>>();
120-
filtered.sort_by(|a, b| a.0.cmp(&b.0));
121-
filtered.get(0).map(|slot| slot.1.clone())
119+
.min_by_key(|a| a.0)
120+
.map(|slot| slot.1)
122121
}
123122

124123
fn execute_external_subcommand(config: &Config, cmd: &str, args: &[&str]) -> CliResult {

src/cargo/core/registry.rs

+22-10
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ use sources::config::SourceConfigMap;
1414
/// See also `core::Source`.
1515
pub trait Registry {
1616
/// Attempt to find the packages that match a dependency request.
17-
fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()>;
17+
fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary), fuzzy: bool) -> CargoResult<()>;
1818

19-
fn query_vec(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
19+
fn query_vec(&mut self, dep: &Dependency, fuzzy: bool) -> CargoResult<Vec<Summary>> {
2020
let mut ret = Vec::new();
21-
self.query(dep, &mut |s| ret.push(s))?;
21+
self.query(dep, &mut |s| ret.push(s), fuzzy)?;
2222
Ok(ret)
2323
}
2424
}
@@ -395,7 +395,7 @@ http://doc.crates.io/specifying-dependencies.html#overriding-dependencies
395395
}
396396

397397
impl<'cfg> Registry for PackageRegistry<'cfg> {
398-
fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
398+
fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary), fuzzy: bool) -> CargoResult<()> {
399399
assert!(self.patches_locked);
400400
let (override_summary, n, to_warn) = {
401401
// Look for an override and get ready to query the real source.
@@ -476,15 +476,20 @@ impl<'cfg> Registry for PackageRegistry<'cfg> {
476476
// already selected, then we skip this `summary`.
477477
let locked = &self.locked;
478478
let all_patches = &self.patches_available;
479-
return source.query(dep, &mut |summary| {
479+
let callback = &mut |summary: Summary| {
480480
for patch in patches.iter() {
481481
let patch = patch.package_id().version();
482482
if summary.package_id().version() == patch {
483483
return;
484484
}
485485
}
486486
f(lock(locked, all_patches, summary))
487-
});
487+
};
488+
return if fuzzy {
489+
source.fuzzy_query(dep, callback)
490+
} else {
491+
source.query(dep, callback)
492+
};
488493
}
489494

490495
// If we have an override summary then we query the source
@@ -496,10 +501,17 @@ impl<'cfg> Registry for PackageRegistry<'cfg> {
496501
}
497502
let mut n = 0;
498503
let mut to_warn = None;
499-
source.query(dep, &mut |summary| {
500-
n += 1;
501-
to_warn = Some(summary);
502-
})?;
504+
{
505+
let callback = &mut |summary| {
506+
n += 1;
507+
to_warn = Some(summary);
508+
};
509+
if fuzzy {
510+
source.fuzzy_query(dep, callback)?;
511+
} else {
512+
source.query(dep, callback)?;
513+
}
514+
}
503515
(override_summary, n, to_warn)
504516
}
505517
}

src/cargo/core/resolver/mod.rs

+46-13
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ use core::PackageIdSpec;
5959
use core::{Dependency, PackageId, Registry, Summary};
6060
use util::config::Config;
6161
use util::errors::{CargoError, CargoResult};
62+
use util::lev_distance::lev_distance;
6263
use util::profile;
6364

6465
use self::context::{Activations, Context};
@@ -230,7 +231,9 @@ fn activate_deps_loop(
230231
// to amortize the cost of the current time lookup.
231232
ticks += 1;
232233
if let Some(config) = config {
233-
if config.shell().is_err_tty() && !printed && ticks % 1000 == 0
234+
if config.shell().is_err_tty()
235+
&& !printed
236+
&& ticks % 1000 == 0
234237
&& start.elapsed() - deps_time > time_to_print
235238
{
236239
printed = true;
@@ -857,12 +860,14 @@ fn activation_error(
857860
msg.push_str("\nversions that meet the requirements `");
858861
msg.push_str(&dep.version_req().to_string());
859862
msg.push_str("` are: ");
860-
msg.push_str(&candidates
861-
.iter()
862-
.map(|v| v.summary.version())
863-
.map(|v| v.to_string())
864-
.collect::<Vec<_>>()
865-
.join(", "));
863+
msg.push_str(
864+
&candidates
865+
.iter()
866+
.map(|v| v.summary.version())
867+
.map(|v| v.to_string())
868+
.collect::<Vec<_>>()
869+
.join(", "),
870+
);
866871

867872
let mut conflicting_activations: Vec<_> = conflicting_activations.iter().collect();
868873
conflicting_activations.sort_unstable();
@@ -922,17 +927,16 @@ fn activation_error(
922927
return format_err!("{}", msg);
923928
}
924929

925-
// Once we're all the way down here, we're definitely lost in the
926-
// weeds! We didn't actually find any candidates, so we need to
930+
// We didn't actually find any candidates, so we need to
927931
// give an error message that nothing was found.
928932
//
929-
// Note that we re-query the registry with a new dependency that
930-
// allows any version so we can give some nicer error reporting
931-
// which indicates a few versions that were actually found.
933+
// Maybe the user mistyped the ver_req? Like `dep="2"` when `dep="0.2"`
934+
// was meant. So we re-query the registry with `deb="*"` so we can
935+
// list a few versions that were actually found.
932936
let all_req = semver::VersionReq::parse("*").unwrap();
933937
let mut new_dep = dep.clone();
934938
new_dep.set_version_req(all_req);
935-
let mut candidates = match registry.query_vec(&new_dep) {
939+
let mut candidates = match registry.query_vec(&new_dep, false) {
936940
Ok(candidates) => candidates,
937941
Err(e) => return e,
938942
};
@@ -977,12 +981,41 @@ fn activation_error(
977981

978982
msg
979983
} else {
984+
// Maybe the user mistyped the name? Like `dep-thing` when `Dep_Thing`
985+
// was meant. So we try asking the registry for a `fuzzy` search for suggestions.
986+
let mut candidates = Vec::new();
987+
if let Err(e) = registry.query(&new_dep, &mut |s| candidates.push(s.name()), true) {
988+
return e;
989+
};
990+
candidates.sort_unstable();
991+
candidates.dedup();
992+
let mut candidates: Vec<_> = candidates
993+
.iter()
994+
.map(|n| (lev_distance(&*new_dep.name(), &*n), n))
995+
.filter(|&(d, _)| d < 4)
996+
.collect();
997+
candidates.sort_by_key(|o| o.0);
980998
let mut msg = format!(
981999
"no matching package named `{}` found\n\
9821000
location searched: {}\n",
9831001
dep.name(),
9841002
dep.source_id()
9851003
);
1004+
if !candidates.is_empty() {
1005+
let mut names = candidates
1006+
.iter()
1007+
.take(3)
1008+
.map(|c| c.1.as_str())
1009+
.collect::<Vec<_>>();
1010+
1011+
if candidates.len() > 3 {
1012+
names.push("...");
1013+
}
1014+
1015+
msg.push_str("did you mean: ");
1016+
msg.push_str(&names.join(", "));
1017+
msg.push_str("\n");
1018+
}
9861019
msg.push_str("required by ");
9871020
msg.push_str(&describe_path(&graph.path_to_top(parent.package_id())));
9881021

src/cargo/core/resolver/types.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl<'a> RegistryQueryer<'a> {
5151
summary: s,
5252
replace: None,
5353
});
54-
})?;
54+
}, false)?;
5555
for candidate in ret.iter_mut() {
5656
let summary = &candidate.summary;
5757

@@ -65,7 +65,7 @@ impl<'a> RegistryQueryer<'a> {
6565
};
6666
debug!("found an override for {} {}", dep.name(), dep.version_req());
6767

68-
let mut summaries = self.registry.query_vec(dep)?.into_iter();
68+
let mut summaries = self.registry.query_vec(dep, false)?.into_iter();
6969
let s = summaries.next().ok_or_else(|| {
7070
format_err!(
7171
"no matching package for override `{}` found\n\

src/cargo/core/source/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ pub trait Source {
2525
/// Attempt to find the packages that match a dependency request.
2626
fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()>;
2727

28+
/// Attempt to find the packages that are close to a dependency request.
29+
/// Each source gets to define what `close` means for it.
30+
/// path/git sources may return all dependencies that are at that uri.
31+
/// where as an Index source may return dependencies that have the same canonicalization.
32+
fn fuzzy_query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()>;
33+
2834
fn query_vec(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
2935
let mut ret = Vec::new();
3036
self.query(dep, &mut |s| ret.push(s))?;
@@ -79,6 +85,11 @@ impl<'a, T: Source + ?Sized + 'a> Source for Box<T> {
7985
(**self).query(dep, f)
8086
}
8187

88+
/// Forwards to `Source::query`
89+
fn fuzzy_query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
90+
(**self).fuzzy_query(dep, f)
91+
}
92+
8293
/// Forwards to `Source::source_id`
8394
fn source_id(&self) -> &SourceId {
8495
(**self).source_id()

src/cargo/sources/directory.rs

+8
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ impl<'cfg> Source for DirectorySource<'cfg> {
5454
Ok(())
5555
}
5656

57+
fn fuzzy_query(&mut self, _dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
58+
let packages = self.packages.values().map(|p| &p.0);
59+
for summary in packages.map(|pkg| pkg.summary().clone()) {
60+
f(summary);
61+
}
62+
Ok(())
63+
}
64+
5765
fn supports_checksums(&self) -> bool {
5866
true
5967
}

src/cargo/sources/git/source.rs

+7
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,13 @@ impl<'cfg> Source for GitSource<'cfg> {
130130
src.query(dep, f)
131131
}
132132

133+
fn fuzzy_query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
134+
let src = self.path_source
135+
.as_mut()
136+
.expect("BUG: update() must be called before query()");
137+
src.fuzzy_query(dep, f)
138+
}
139+
133140
fn supports_checksums(&self) -> bool {
134141
false
135142
}

src/cargo/sources/path.rs

+7
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,13 @@ impl<'cfg> Source for PathSource<'cfg> {
508508
Ok(())
509509
}
510510

511+
fn fuzzy_query(&mut self, _dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
512+
for s in self.packages.iter().map(|p| p.summary()) {
513+
f(s.clone())
514+
}
515+
Ok(())
516+
}
517+
511518
fn supports_checksums(&self) -> bool {
512519
false
513520
}

0 commit comments

Comments
 (0)