-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Initial auto import action implementation #2887
Conversation
e28307f
to
396f352
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Method usages are not imported, they are found in the database, but find_use_path does not return any import paths for them
You can't import methods, so that seems fine?
Some import paths returned by the find_use_path method end up in core:: or alloc:: instead of std:, for example: core::fmt::Debug instead of std::fmt::Debug.
This is not an error techically, but still looks weird.
I think the main problem here is that we unconditionally add core
and alloc
to the extern prelude; I think rustc only allows using them if you use #[no_std]
? Otherwise, we might need special handling for this in the path finder.
crates/ra_ide/src/imports_locator.rs
Outdated
.filter_map(|import_candidate| self.get_name_definition(db, &import_candidate)) | ||
.filter_map(|name_definition_to_import| { | ||
if let NameKind::Def(module_def) = name_definition_to_import.kind { | ||
module_with_name_to_import.find_use_path(db, module_def) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note that the use path may actually end in a different segment than the original name, if it's using a reexport, so we should probably filter that out, or change the actual path in the assist, or maybe later add a parameter to find_use_path
that disregards such paths
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm afraid I cannot imagine this myself, do you have any code snippet I can try out and fix it?
crates/ra_assists/src/lib.rs
Outdated
&mut self, | ||
name_to_import: InFile<&NameRef>, | ||
module_with_name_to_import: Module, | ||
) -> Option<Vec<ModPath>>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
obviously it would be nicest if we didn't need this at all, but if we need it, maybe we can make the interface smaller? i.e. just directly wrapping the index lookup, a function that takes a Name
and returns a vector of ModuleDef
s, instead of also putting the path generation in there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be a very temporary trait before we decide how to properly extract the functionality we need into a separate crate.
But I like the idea in general, although I have a few questions:
classify_name_ref
requiresInFile<&ast::NameRef>
,classify_name
requiresInFile<&ast::Name>
, so not sure how I can passName
only there.
I'm also not sure how to get theName
in the first place, since for the case
fn main() {
let zz = HasMap<|>::new();
}
there's no NAME
around the coursor, only NAME_REF
.
So it looks like I should require InFile<&ast::NameRef>
as an input here.
- The biggest issue here are the tests: since I have to keep them in the
ra_assists
, I have to create a mock trait implementation. I can return something as a validModPath
there, but how can I create theModuleDef
in thera_assists
?
Moveover, I cannot get any randomModuleDef
, I need only those that thefind_use_path
method will consider as impotable.
Is there a way to get one with those properties?
If not, maybe we should focus on the code extraction instead to remove these hacks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think classify_name_ref
should be called outside that function (I just now realize that that's where we check whether the name resolves); that's part of making it smaller. classify_name
gets called on the import candidate, not the name we're searching for. I actually meant a hir::Name
, not an ast::Name
, though probably a String
would be just fine (it's getting converted to one anyway). Conceptually, since this is just an index lookup, it shouldn't need a file location or AST node.
For tests, I think we could write a small implementation that just does a linear search through all defs in the crate by name? The advantage for tests would be that we'd be testing a bigger part of the functionality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As for classify_name_ref
, I think you actually don't need 90% of what it does; since you already are on an ast::Path
, analyzer.resolve_path
should be enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have implemented all the suggestions, the interface looks way better now, thanks!
One thing that bothers me though: I had to reexport a ModuleDefId
in since I could not find a better way to extract the LocalModuleId
that I needed to implement the linear search through all defs in the crate for the test impl ImportsLocator for TestImportsLocator
.
My bad, I've meant "functions" of course. If you put enough debug prints in |
Ah, the problem there is this FIXME: |
It looks like we can use But when I try to import something from
|
You need to explictely use |
TIL. Thanks for the info :) |
We do add the |
Can confirm now: |
fec493c
to
1a78991
Compare
Interesting, I've written a test that tries to import a struct that is available in the three modules under the same name:
The test picks the firts result assist proposes and it looks like all three platforms had gotten a different import. |
Whaaa, looks like we can use use core::fmt; The following does work though: trait T: core::fmt::Debug {} I won't use playground to test these kinds of things, it might be doing something funny with sysroot to make extern crates magically available. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finally took a look at this!
I really love how this looks like! I especialy like how all various components like indexes, import and name classification just magically work together. it's beautiful!
r=me with AsName
re-export removed
crates/ra_hir/src/lib.rs
Outdated
}; | ||
pub use hir_expand::{ | ||
name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, Origin, | ||
name::{AsName, Name}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AsName should be private to hir
. I think we basically go ast::NameRef -> hir::Name -> String
anyway, so we should be able to short-circuit it and go directly to -> String
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, that's a brain fart, .syntax().to_string()
provides the same output and should be used instead, thanks.
On a related topic: is it safe to reexport hir_def::ModuleDefId
? I have to use it to implement the mock trait test implementation suggested here: #2887 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not, but I guess we can live with it for test, and should fix once we do that ide_db
thing. But it is a good idea to add a FIXME
there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, will add.
Maybe there's a better way to do the things, I'm still realtively bad with the APIs the codebase provides.
I use it here: https://github.com/rust-analyzer/rust-analyzer/pull/2887/files#diff-001b3b1412c33fee802c22b0f848dcd7R270 to continue searching the nested modules for the definitions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still realtively bad with the APIs the codebase provides.
It's rather that everything above ra_syntax
and bellow ra_ide
is a mess, API wise. I like our syntax tree API, and I love AnalysisHost
/ Analysis
plain data based APIs, but everything in the middle is questionable...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to continue searching the nested modules for the definitions.
I feel we just need, shrug, a module visitor, because many tests repeat this recursive collection pattern
}; | ||
let lib_results = { | ||
let mut query = Query::new(name_to_import.to_string()); | ||
query.libs(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One day we should fix this to look only at the direct deps
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, another point to improve to not to forget about.
I'll gather all those and mention in the related ticket.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's some more discrepancies between the current symbol index and what we'd ideally want for autoimport: We want macro-generated defs, and for any def reexported under a new name we'd also want to index it under that name. (Also, if we do restrict it to direct dependencies, we need to include anything that's reexported in one of them.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, ideally we should include macro-generated stuff into the index. I think that one day™ we'll just throw-away the current syntax-based indexing and replace it with crate_def_map based one.
bors r+ ⭐ 🚀 ⭐ |
2887: Initial auto import action implementation r=matklad a=SomeoneToIgnore Closes #2180 Adds an auto import action implementation. This implementation is not ideal and has a few limitations: * The import search functionality should be moved into a separate crate accessible from ra_assists. This requires a lot of changes and a preliminary design. Currently the functionality is provided as a trait impl, more on that here: #2180 (comment) * Due to the design desicion from the previous item, no doctests are run for the new aciton (look for a new FIXME in the PR) * For the same reason, I have to create the mock trait implementaion to test the assist * Ideally, I think we should have this feature as a diagnostics (that detects an absense of an import) that has a corresponding quickfix action that gets evaluated on demand. Curretly we perform the import search every time we resolve the import which looks suboptimal. This requires `classify_name_ref` to be moved from ra_ide, so not done currently. A few improvements to the imports mechanism to be considered later: * Constants like `ra_syntax::SyntaxKind::NAME` are not imported, because they are not present in the database * Method usages are not imported, they are found in the database, but `find_use_path` does not return any import paths for them * Some import paths returned by the `find_use_path` method end up in `core::` or `alloc::` instead of `std:`, for example: `core::fmt::Debug` instead of `std::fmt::Debug`. This is not an error techically, but still looks weird. * No detection of cases where a trait should be imported in order to be able to call a method * Improve `auto_import_text_edit` functionality: refactor it and move away from the place it is now, add better logic for merging the new import with already existing imports Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
Build succeeded
|
2978: Auto import functions r=flodiebold a=SomeoneToIgnore A follow up for #2887 (comment) I've used the logic for conversion from the https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_hir_def/src/item_scope.rs#L169 method. I'm not fond of how the conversion is implemented and for my needs, I can simply replace the `hir_def::item_scope::ItemInNs::Types(item.into())` with `hir_def::item_scope::ItemInNs::Values(item.into())` and it will work, so I can use this approach instead, if you find it a better one. Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
Closes #2180
Adds an auto import action implementation.
This implementation is not ideal and has a few limitations:
The import search functionality should be moved into a separate crate accessible from ra_assists.
This requires a lot of changes and a preliminary design.
Currently the functionality is provided as a trait impl, more on that here: Auto import #2180 (comment)
Due to the design desicion from the previous item, no doctests are run for the new aciton (look for a new FIXME in the PR)
For the same reason, I have to create the mock trait implementaion to test the assist
Ideally, I think we should have this feature as a diagnostics (that detects an absense of an import) that has a corresponding quickfix action that gets evaluated on demand.
Curretly we perform the import search every time we resolve the import which looks suboptimal.
This requires
classify_name_ref
to be moved from ra_ide, so not done currently.A few improvements to the imports mechanism to be considered later:
Constants like
ra_syntax::SyntaxKind::NAME
are not imported, because they are not present in the databaseMethod usages are not imported, they are found in the database, but
find_use_path
does not return any import paths for themSome import paths returned by the
find_use_path
method end up incore::
oralloc::
instead ofstd:
, for example:core::fmt::Debug
instead ofstd::fmt::Debug
.This is not an error techically, but still looks weird.
No detection of cases where a trait should be imported in order to be able to call a method
Improve
auto_import_text_edit
functionality: refactor it and move away from the place it is now, add better logic for merging the new import with already existing imports