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

Bug #21221: Show candidates for names not in scope #31674

Merged
merged 1 commit into from
Feb 20, 2016

Conversation

VladUreche
Copy link
Contributor

This commit adds functionality that allows the name resolution pass
to search for entities (traits/types/enums/structs) by name, in
order to show recommendations along with the errors.

For now, only E0405 and E0412 have suggestions attached, as per the
request in bug #21221, but it's likely other errors can also benefit
from the ability to generate suggestions.

@rust-highfive
Copy link
Collaborator

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @Aatch (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

@@ -91,7 +91,7 @@ fn parse_expected(last_nonfollow_error: Option<usize>,
(which, line)
};

debug!("line={} which={:?} kind={:?} msg={:?}", line_num, which, kind, msg);
println!("line={} which={:?} kind={:?} msg={:?}", line_num, which, kind, msg);
Copy link
Contributor

Choose a reason for hiding this comment

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

This should probably stay debug!(...);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops, sorry, will fix in a moment.

@VladUreche VladUreche force-pushed the issue/21221 branch 2 times, most recently from 5155347 to 5870906 Compare February 15, 2016 16:18
@VladUreche
Copy link
Contributor Author

The previous commit failed to build after the rebase on the latest master, as a couple of names changed in the name resolution data structures. Should be fixed now.

suggestion_str.push_str(&path_strings[0]);
suggestion_str.push_str("`, which you need to import with the `use` keyword.");
suggestion_vec.push(suggestion_str);
} else {
Copy link
Contributor

Choose a reason for hiding this comment

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

change to else if !paths.is_empty() to remove the empty if-block above

@VladUreche
Copy link
Contributor Author

@oli-obk Thank you for the suggestions! I will push an update asap.

@bors
Copy link
Contributor

bors commented Feb 17, 2016

☔ The latest upstream changes (presumably #31685) made this pull request unmergeable. Please resolve the merge conflicts.

@VladUreche
Copy link
Contributor Author

Thanks Bors, nice helpful robot :)
I addressed the merge in the last commit.

@nikomatsakis
Copy link
Contributor

cc @jseyfried @petrochenkov, resident resolve experts :)

}
}
}

pub fn def_id(&self) -> DefId {
self.def_id_opt().expect(&format!("attempted .def_id() on invalid def: {:?}", self))
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: put these on two separate lines (lining up the .) -- seems a bit long for one line to me

@nikomatsakis
Copy link
Contributor

OK, did a read through and left some notes -- this looks pretty good to me, actually. I like the suggestion infrastructure, I wonder if there are other places it could profitably be employed.

I left some nits and some larger comments.

@VladUreche
Copy link
Contributor Author

@jseyfried, can you please have a look at the conditions now?
And in particular, I'm not sure if all modules from outside crates have is_external_crate set to true or just the crate itself -- to be sure, I propagated is_extern in the worklist: VladUreche@ddeb147#diff-aeb0880081a991f34aef2ab889e1fb7aR3614

I will address merging the CandidateAccessibility and CandidateSuggestions structs now.

@VladUreche
Copy link
Contributor Author

@nikomatsakis, the commit messages should be much shorter now: VladUreche@ddeb147#diff-aeb0880081a991f34aef2ab889e1fb7aL3735

// declared as public
lookup_results_everything.push(path.clone());
if !in_module_is_extern ||
name_binding.is_public() && in_module_is_accessible {
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be just !in_module_is_extern || name_binding.is_public()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, since we prune the search by not adding to the worklist. Good point!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After making the changes, I realized why I did not go with pruning:

        session.fileline_help(
            span,
            &format!("there are {} other candidates that are not \
            accessible.", not_accessible_count),
        );

When pruning the search, we do not count all inaccessible definitions with the given name.

Copy link
Contributor

Choose a reason for hiding this comment

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

Right now we are collecting all crate local items in lookup_results_accessible, so lookup_results_everything has all the paths that lookup_results_accessible has except for inaccessible external items. I don't think we should report inaccessible external items under any circumstances, so there's no need for the two lists.

Copy link
Contributor

Choose a reason for hiding this comment

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

If you want two lists, I think lookup_results_accessible should have all accessible items and lookup_results_everything should also have inaccessible items from the current crate. In that case, you'd need that ancestor checking method to distinguish between inaccessible and accessible items in the current crate. I don't think it's worth it to implement this distinction.

@VladUreche
Copy link
Contributor Author

@jseyfried, the last commit merges the two data structures for storing candidates.
The pruning technique you suggested works only if we're okay with:

  • not knowing how many inaccessible definitions exist (and/or whether they exist at all)
  • not knowing what they are (in case there is no accessible definition)
    If that's okay, I can get the pruning working again.

Otherwise, once you had a look at the code, I can squash the commits again.

@jseyfried
Copy link
Contributor

I think we should always report all items in the current crate and all accessible items in external crates and we should never report inaccessible items from external crates.

@jseyfried
Copy link
Contributor

Also, feel free to squash whenever

@VladUreche
Copy link
Contributor Author

Thanks! I removed the inaccessible definitions list and re-enabled the pruning. I'll have the new commit ready in a few minutes.

@VladUreche VladUreche force-pushed the issue/21221 branch 2 times, most recently from e180806 to 2c613a5 Compare February 18, 2016 23:20
/// mod bar { trait T {}; };
/// // make private T accessible through an import:
/// pub use bar::T as T;
/// }
Copy link
Contributor

Choose a reason for hiding this comment

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

A couple things:

  • To pub use something, it has to be pub, so this example would be an error unless trait T was pub.
  • Since we don't care about accessibility in the current crate, we would report foo::bar::T, right?
  • name_binding.is_import() is can only be true for bindings in the current crate since our crate metadata does not distinguish between public imports and public items. If this code were in an external crate, we would report foo::T as desired.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That simplifies things a lot. I was under the impression that you can "reveal" private parts of a crate through pub use statements. If imports are only visible on the current crate, we're perfectly fine ignoring them 👍

Thanks a lot for explaining this!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To answer the 2nd bullet point, yes, we would report foo::bar::T.

Copy link
Contributor

Choose a reason for hiding this comment

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

No problem, I probably should have mentioned this earlier :)
pub use statements actually can "reveal" private parts of a crate, for example

mod foo { pub trait T {} }
pub use foo::T; // FYI, we wouldn't be able to do this if `trait T` were not `pub`

Here, T would not be accessible to other traits if we didn't pub use foo::T.
What I mean by not distinguishing between public imports and public items is that when we load this external crate, both bindings for T will be !binding.is_import() -- that is, we "forget" if bindings came from items or from imports. In other words, we wouldn't be able to distinguish the above external crate from

mod foo { pub use T; }
pub trait T;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Which, if I'm interpreting well, means we should report external_crate::T as a valid suggestion. Let me add this as a test case 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

Indeed, good idea

@VladUreche
Copy link
Contributor Author

@jseyfried, thanks for the latest round of feedback. I removed in_module_is_accessible and made trait T private in src/test/compile-fail/issue-21221-2.rs. The commit is squashed :)

@jseyfried
Copy link
Contributor

Alright, this look good to me -- thanks @VladUreche!

@nikomatsakis We decided to report all items from the current crate (ignoring re-exports) and all accessible items from external crates.

This commit adds functionality that allows the name resolution pass
to search for entities (traits/types/enums/structs) by name, in
order to show recommendations along with the errors.

For now, only E0405 and E0412 have suggestions attached, as per the
request in bug rust-lang#21221, but it's likely other errors can also benefit
from the ability to generate suggestions.
@VladUreche
Copy link
Contributor Author

The last commit adds a test case which checks the re-exports from outside crates.
@jseyfried, @nikomatsakis thanks a lot for the feedback!

Just for the record, there are five possible extensions I'm aware of:

  1. Crate local re-exports (including renames)
  2. Filtering paths (e.g. if the programmer wrote ops::Mul we wouldn't suggest foo::Mul unless there's no trait Mul that starts with prefix ops)
  3. fuzzy matching (e.g. ops::Muk results in a suggestion of ops::Mul)
  4. Ordering by "distance" from the current module
  5. Command-line option to avoid the 5-candidate limit

@nikomatsakis
Copy link
Contributor

@bors r+

@bors
Copy link
Contributor

bors commented Feb 19, 2016

📌 Commit 88af8fa has been approved by nikomatsakis

@nikomatsakis
Copy link
Contributor

@VladUreche thanks for sticking with it :)

@bors
Copy link
Contributor

bors commented Feb 20, 2016

⌛ Testing commit 88af8fa with merge cfabd17...

bors added a commit that referenced this pull request Feb 20, 2016
This commit adds functionality that allows the name resolution pass
to search for entities (traits/types/enums/structs) by name, in
order to show recommendations along with the errors.

For now, only E0405 and E0412 have suggestions attached, as per the
request in bug #21221, but it's likely other errors can also benefit
from the ability to generate suggestions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants