Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add suggestions for misspelled method names #44297

Merged
merged 2 commits into from
Sep 26, 2017

Conversation

laumann
Copy link
Contributor

@laumann laumann commented Sep 3, 2017

Use the syntax::util::lev_distance module to provide suggestions when a
named method cannot be found.

Part of #30197

@rust-highfive
Copy link
Collaborator

r? @eddyb

(rust_highfive has picked a reviewer for you, use r? to override)

@laumann
Copy link
Contributor Author

laumann commented Sep 3, 2017

I had some concerns when submitting this - not sure if this is the best approach.

  • Trait methods are not suggested
  • lev_candidates should maybe be Vec<CandidateSource> instead of Vec<DefId>

@eddyb
Copy link
Member

eddyb commented Sep 4, 2017

r? @nikomatsakis

@rust-highfive rust-highfive assigned nikomatsakis and unassigned eddyb Sep 4, 2017
@arielb1
Copy link
Contributor

arielb1 commented Sep 4, 2017

If you want to integrate traits, to avoid issues like #43149 you want to

  • add an allow_similar_names flag to ProbeContext
  • if it's present, make ProbeContext::impl_or_trait_item pick items with names similar enough to this name:
    fn impl_or_trait_item(&self, def_id: DefId) -> Vec<ty::AssociatedItem> {
    if let Some(name) = self.method_name {
    self.fcx.associated_item(def_id, name).map_or(Vec::new(), |x| vec![x])
    } else {
    self.tcx.associated_items(def_id).collect()
    }
    }
  • Do something like ProbeContext::probe_for_return_type to find all similar methods that can be used:
    pub fn probe_for_return_type(&self,
    span: Span,
    mode: Mode,
    return_type: Ty<'tcx>,
    self_ty: Ty<'tcx>,
    scope_expr_id: ast::NodeId)
    -> Vec<ty::AssociatedItem> {
    debug!("probe(self_ty={:?}, return_type={}, scope_expr_id={})",
    self_ty,
    return_type,
    scope_expr_id);
    let method_names =
    self.probe_op(span, mode, None, Some(return_type), IsSuggestion(true),
    self_ty, scope_expr_id, ProbeScope::TraitsInScope,
    |probe_cx| Ok(probe_cx.candidate_method_names()))
    .unwrap_or(vec![]);
    method_names
    .iter()
    .flat_map(|&method_name| {
    self.probe_op(
    span, mode, Some(method_name), Some(return_type),
    IsSuggestion(true), self_ty, scope_expr_id,
    ProbeScope::TraitsInScope, |probe_cx| probe_cx.pick()
    ).ok().map(|pick| pick.item)
    })
    .collect()
    }

@laumann
Copy link
Contributor Author

laumann commented Sep 5, 2017

@arielb1 Thanks for the suggestion! I'll try to do that...

@nikomatsakis
Copy link
Contributor

r? @arielb1 -- you seem to be on top of this =) feel free to re-assign me though...

@rust-highfive rust-highfive assigned arielb1 and unassigned nikomatsakis Sep 5, 2017
@arielb1 arielb1 added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Sep 5, 2017
@aidanhs
Copy link
Member

aidanhs commented Sep 13, 2017

Hi @laumann, just wanted to check in and make sure this is still on your radar!

@laumann
Copy link
Contributor Author

laumann commented Sep 14, 2017

@aidanhs Hi, yes, it is, I'm sorry for the delay, but I've had little time to work on it, since submitting the PR. I was looking at it last night, implementing @arielb1's suggestions and I have it working to the point where the suggestions are found, they just need to be emitted.

I got stuck at how to turn a ty::AssociatedItem into a CandidateSource - I don't think it's hard, I just need to look at it :-)

Sorry again!

@arielb1
Copy link
Contributor

arielb1 commented Sep 14, 2017

@laumann

You are supposed to have the Candidate that gave rise to the AssociatedItem, and use it to call candidate_source.

@arielb1
Copy link
Contributor

arielb1 commented Sep 14, 2017

And actually you don't want a CandidateSource here - an AssociatedItem is enough.

@laumann laumann force-pushed the suggest-misspelt-methods branch from a54f408 to 4a84be4 Compare September 14, 2017 20:26
@laumann
Copy link
Contributor Author

laumann commented Sep 14, 2017

@arielb1 I've amended the commit and rebased it on top of master, but haven't yet run the tests. I'm really not sure if I've taken the right approach though :-/ It seems to be the right point to hook in, but I suspect that candidate assembly might include too many things that pick_core() would then get rid of.

// Toggle the flag that allows for similar names to be found
// and search again
self.allow_similar_names = true;
self.inherent_candidates.clear();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be self.reset();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

let mut lev_candidates = Vec::with_capacity(self.inherent_candidates.len() +
self.extension_candidates.len());
for lev_cand in self.inherent_candidates.iter().chain(self.extension_candidates.iter()) {
lev_candidates.push(lev_cand.item);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to try to pick each candidate, to avoid showing unusable candidates.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how to do this best. To pick, them I'd have to extract all the close matching names, run a probe for each and call pick() ?

@laumann laumann force-pushed the suggest-misspelt-methods branch 4 times, most recently from a3768c2 to 6fbf991 Compare September 17, 2017 20:19
let mut pcx = ProbeContext::new(self.fcx, span, self.mode, self.method_name,
self.return_type, vec![]);
pcx.allow_similar_names = true;
pcx.steps = steps;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if this might cause issues. The constructor for ProbeContext wants a Vec, but internally it's wrapped in an Rc.

Copy link
Contributor

@arielb1 arielb1 Sep 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that shouldn't be a problem. maybe change the constructor to take an Rc for cleanness?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, done.

@laumann
Copy link
Contributor Author

laumann commented Sep 19, 2017

@arielb1 I think it's ready for review now.


if !lev_candidates.is_empty() {
use rustc_data_structures::fx::FxHashSet;
let mut dedup = FxHashSet();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this dedup thing is redundant now...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be - candidate_method_names performs deduplication.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

@arielb1 arielb1 added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Sep 19, 2017
@@ -799,7 +807,38 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
return Err(MethodError::PrivateMatch(def, out_of_scope_traits));
}

debug!("Probing for method names similar to {:?}",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be better to extract this section to a separate method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@laumann laumann force-pushed the suggest-misspelt-methods branch from 68a6212 to 452524e Compare September 20, 2017 13:59
@laumann
Copy link
Contributor Author

laumann commented Sep 20, 2017

Should I do another rebase?

Use the syntax::util::lev_distance module to provide suggestions when a
named method cannot be found.

Part of rust-lang#30197
@laumann laumann force-pushed the suggest-misspelt-methods branch from 452524e to 09defbc Compare September 21, 2017 10:06
@arielb1
Copy link
Contributor

arielb1 commented Sep 21, 2017

@laumann

There's no need to - bors will automatically merge - but you can rebase if you want.

It turns out that when working with levenshtein distance, we report the 1 best name that matches, as in:

fn suggest_field_name(variant: &'tcx ty::VariantDef,
field: &Spanned<ast::Name>,
skip: Vec<InternedString>)
-> Option<Symbol> {
let name = field.node.as_str();
let names = variant.fields.iter().filter_map(|field| {
// ignore already set fields and private fields from non-local crates
if skip.iter().any(|x| *x == field.name.as_str()) ||
(variant.did.krate != LOCAL_CRATE && field.vis != Visibility::Public) {
None
} else {
Some(&field.name)
}
});
find_best_match_for_name(names, &name, None)
}

@laumann laumann force-pushed the suggest-misspelt-methods branch from 24a0af8 to 8eff63f Compare September 21, 2017 11:09
@arielb1
Copy link
Contributor

arielb1 commented Sep 21, 2017

@bors r+

@bors
Copy link
Contributor

bors commented Sep 21, 2017

📌 Commit 8eff63f has been approved by arielb1

@bors
Copy link
Contributor

bors commented Sep 21, 2017

⌛ Testing commit 8eff63fdc12d153dd3301f0743a9f62204de64dd with merge a5c611a02164c07d86eccc2ddc99c4012aa5cffe...

@bors
Copy link
Contributor

bors commented Sep 21, 2017

💔 Test failed - status-appveyor

@arielb1
Copy link
Contributor

arielb1 commented Sep 21, 2017

[00:03:11] tidy error: /checkout/src/librustc_typeck/check/method/probe.rs:1136: line longer than 100 chars

@laumann laumann force-pushed the suggest-misspelt-methods branch from 8eff63f to 5d72356 Compare September 21, 2017 14:25
@arielb1
Copy link
Contributor

arielb1 commented Sep 21, 2017

test needs updating:

[00:39:41] ---- [ui] ui/suggestions/suggest-methods.rs stdout ----
[00:39:41] 	normalized stderr:
[00:39:41] error[E0599]: no method named `bat` found for type `Foo` in the current scope
[00:39:41]   --> $DIR/suggest-methods.rs:28:7
[00:39:41]    |
[00:39:41] 28 |     f.bat(1.0);
[00:39:41]    |       ^^^
[00:39:41]    |
[00:39:41]    = help: did you mean `bar`?
[00:39:41] 
[00:39:41] error[E0599]: no method named `is_emtpy` found for type `std::string::String` in the current scope
[00:39:41]   --> $DIR/suggest-methods.rs:31:15
[00:39:41]    |
[00:39:41] 31 |     let _ = s.is_emtpy();
[00:39:41]    |               ^^^^^^^^
[00:39:41]    |
[00:39:41]    = help: did you mean `is_empty`?
[00:39:41] 
[00:39:41] error[E0599]: no method named `count_eos` found for type `u32` in the current scope
[00:39:41]   --> $DIR/suggest-methods.rs:35:19
[00:39:41]    |
[00:39:41] 35 |     let _ = 63u32.count_eos();
[00:39:41]    |                   ^^^^^^^^^
[00:39:41]    |
[00:39:41]    = help: did you mean `count_zeros`?
[00:39:41] 
[00:39:41] error[E0599]: no method named `count_o` found for type `u32` in the current scope
[00:39:41]   --> $DIR/suggest-methods.rs:38:19
[00:39:41]    |
[00:39:41] 38 |     let _ = 63u32.count_o();
[00:39:41]    |                   ^^^^^^^
[00:39:41] 
[00:39:41] error: aborting due to 4 previous errors
[00:39:41] 
[00:39:41] 
[00:39:41] 
[00:39:41] expected stderr:
[00:39:41] error[E0599]: no method named `bat` found for type `Foo` in the current scope
[00:39:41]   --> $DIR/suggest-methods.rs:28:7
[00:39:41]    |
[00:39:41] 28 |     f.bat(1.0);
[00:39:41]    |       ^^^
[00:39:41]    |
[00:39:41]    = help: did you mean `bar`?
[00:39:41] 
[00:39:41] error[E0599]: no method named `is_emtpy` found for type `std::string::String` in the current scope
[00:39:41]   --> $DIR/suggest-methods.rs:31:15
[00:39:41]    |
[00:39:41] 31 |     let _ = s.is_emtpy();
[00:39:41]    |               ^^^^^^^^
[00:39:41]    |
[00:39:41]    = help: did you mean `is_empty`?
[00:39:41] 
[00:39:41] error[E0599]: no method named `count_eos` found for type `u32` in the current scope
[00:39:41]   --> $DIR/suggest-methods.rs:34:19
[00:39:41]    |
[00:39:41] 35 |     let _ = 63u32.count_eos();
[00:39:41]    |                   ^^^^^^^^^
[00:39:41]    |
[00:39:41]    = help: did you mean `count_ones`?
[00:39:41] 
[00:39:41] error[E0599]: no method named `count_o` found for type `u32` in the current scope
[00:39:41]   --> $DIR/suggest-methods.rs:35:19
[00:39:41]    |
[00:39:41] 38 |     let _ = 63u32.count_o();
[00:39:41]    |                   ^^^^^^^
[00:39:41] 
[00:39:41] error: aborting due to 4 previous errors
[00:39:41] 
[00:39:41] 
[00:39:41] 
[00:39:41] diff of stderr:
[00:39:41] 
[00:39:41]  error[E0599]: no method named `bat` found for type `Foo` in the current scope
[00:39:41]    --> $DIR/suggest-methods.rs:28:7
[00:39:41]     |
[00:39:41]  28 |     f.bat(1.0);
[00:39:41]     |       ^^^
[00:39:41]     |
[00:39:41]     = help: did you mean `bar`?
[00:39:41]  
[00:39:41]  error[E0599]: no method named `is_emtpy` found for type `std::string::String` in the current scope
[00:39:41]    --> $DIR/suggest-methods.rs:31:15
[00:39:41]     |
[00:39:41]  31 |     let _ = s.is_emtpy();
[00:39:41]     |               ^^^^^^^^
[00:39:41]     |
[00:39:41]     = help: did you mean `is_empty`?
[00:39:41]  
[00:39:41]  error[E0599]: no method named `count_eos` found for type `u32` in the current scope
[00:39:41] -  --> $DIR/suggest-methods.rs:34:19
[00:39:41] +  --> $DIR/suggest-methods.rs:35:19
[00:39:41]     |
[00:39:41]  35 |     let _ = 63u32.count_eos();
[00:39:41]     |                   ^^^^^^^^^
[00:39:41]     |
[00:39:41] -   = help: did you mean `count_ones`?
[00:39:41] +   = help: did you mean `count_zeros`?
[00:39:41]  
[00:39:41]  error[E0599]: no method named `count_o` found for type `u32` in the current scope
[00:39:41] -  --> $DIR/suggest-methods.rs:35:19
[00:39:41] +  --> $DIR/suggest-methods.rs:38:19
[00:39:41]     |
[00:39:41]  38 |     let _ = 63u32.count_o();
[00:39:41]     |                   ^^^^^^^
[00:39:41]  
[00:39:41]  error: aborting due to 4 previous errors
[00:39:41]  
[00:39:41] 
[00:39:41] The actual stderr differed from the expected stderr.
[00:39:41] Actual stderr saved to /checkout/obj/build/x86_64-unknown-linux-gnu/test/ui/suggestions/suggest-methods.stderr
[00:39:41] To update references, run this command from build directory:
[00:39:41] /checkout/src/test/ui/update-references.sh '/checkout/obj/build/x86_64-unknown-linux-gnu/test/ui' 'suggestions/suggest-methods.rs'
[00:39:41] 
[00:39:41] error: 1 errors occurred comparing output.
[00:39:41] status: exit code: 101
[00:39:41] command: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2/bin/rustc" "/checkout/src/test/ui/suggestions/suggest-methods.rs" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/ui" "--target=x86_64-unknown-linux-gnu" "-C" "prefer-dynamic" "-o" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/ui/suggestions/suggest-methods.stage2-x86_64-unknown-linux-gnu" "-Crpath" "-O" "-Lnative=/checkout/obj/build/x86_64-unknown-linux-gnu/native/rust-test-helpers" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/ui/suggestions/suggest-methods.stage2-x86_64-unknown-linux-gnu.ui.libaux" "-A" "unused"
[00:39:41] stdout:
[00:39:41] ------------------------------------------
[00:39:41] 
[00:39:41] ------------------------------------------
[00:39:41] stderr:
[00:39:41] ------------------------------------------
[00:39:41] error[E0599]: no method named `bat` found for type `Foo` in the current scope
[00:39:41]   --> /checkout/src/test/ui/suggestions/suggest-methods.rs:28:7
[00:39:41]    |
[00:39:41] 28 |     f.bat(1.0);
[00:39:41]    |       ^^^
[00:39:41]    |
[00:39:41]    = help: did you mean `bar`?
[00:39:41] 
[00:39:41] error[E0599]: no method named `is_emtpy` found for type `std::string::String` in the current scope
[00:39:41]   --> /checkout/src/test/ui/suggestions/suggest-methods.rs:31:15
[00:39:41]    |
[00:39:41] 31 |     let _ = s.is_emtpy();
[00:39:41]    |               ^^^^^^^^
[00:39:41]    |
[00:39:41]    = help: did you mean `is_empty`?
[00:39:41] 
[00:39:41] error[E0599]: no method named `count_eos` found for type `u32` in the current scope
[00:39:41]   --> /checkout/src/test/ui/suggestions/suggest-methods.rs:35:19
[00:39:41]    |
[00:39:41] 35 |     let _ = 63u32.count_eos();
[00:39:41]    |                   ^^^^^^^^^
[00:39:41]    |
[00:39:41]    = help: did you mean `count_zeros`?
[00:39:41] 
[00:39:41] error[E0599]: no method named `count_o` found for type `u32` in the current scope
[00:39:41]   --> /checkout/src/test/ui/suggestions/suggest-methods.rs:38:19
[00:39:41]    |
[00:39:41] 38 |     let _ = 63u32.count_o();
[00:39:41]    |                   ^^^^^^^
[00:39:41] 
[00:39:41] error: aborting due to 4 previous errors
[00:39:41] 
[00:39:41] 
[00:39:41] ------------------------------------------
[00:39:41] 
[00:39:41] thread '[ui] ui/suggestions/suggest-methods.rs' panicked at 'explicit panic', /checkout/src/tools/compiletest/src/runtest.rs:2433:8
[00:39:41] note: Run with `RUST_BACKTRACE=1` for a backtrace.
[00:39:41] 
[00:39:41] 
[00:39:41] failures:
[00:39:41]     [ui] ui/suggestions/suggest-methods.rs
[00:39:41] 
[00:39:41] test result: �[31mFAILED�(B�[m. 400 passed; 1 failed; 1 ignored; 0 measured; 0 filtered out

@laumann laumann force-pushed the suggest-misspelt-methods branch 2 times, most recently from b2685dd to 10098e2 Compare September 21, 2017 20:47
@arielb1
Copy link
Contributor

arielb1 commented Sep 24, 2017

@bors r+

@bors
Copy link
Contributor

bors commented Sep 24, 2017

📌 Commit 10098e2 has been approved by arielb1

@laumann laumann force-pushed the suggest-misspelt-methods branch from 10098e2 to 4963394 Compare September 24, 2017 17:59
The convention for suggesting close matches is to provide at most one match (the
closest one). Change the suggestions for misspelt method names to obey that.
@laumann
Copy link
Contributor Author

laumann commented Sep 24, 2017

@arielb1 @goffrie It should be OK now, I think.

@arielb1
Copy link
Contributor

arielb1 commented Sep 25, 2017

@bors r+

@bors
Copy link
Contributor

bors commented Sep 25, 2017

📌 Commit 4963394 has been approved by arielb1

@bors
Copy link
Contributor

bors commented Sep 25, 2017

⌛ Testing commit 4963394 with merge 82ae968...

bors added a commit that referenced this pull request Sep 25, 2017
Add suggestions for misspelled method names

Use the syntax::util::lev_distance module to provide suggestions when a
named method cannot be found.

Part of #30197
@bors
Copy link
Contributor

bors commented Sep 26, 2017

☀️ Test successful - status-appveyor, status-travis
Approved by: arielb1
Pushing 82ae968 to master...

@bors bors merged commit 4963394 into rust-lang:master Sep 26, 2017
@laumann laumann deleted the suggest-misspelt-methods branch September 26, 2017 07:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants