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

feat: Added remove unused imports assist #14723

Merged
merged 2 commits into from Aug 1, 2023
Merged

Conversation

obsgolem
Copy link
Contributor

@obsgolem obsgolem commented May 3, 2023

This resolves the most important part of #5131. I needed to make a couple of cosmetic changes to the search infrastructure to do this.

A few open questions:

  • Should imports that don't resolve to anything be considered unused? I figured probably not, but it would be a trivial change to make if we want it.
  • Is there a cleaner way to make the edits to the use list?
  • Is there a cleaner way to get the list of uses that intersect the current selection?
  • Is the performance acceptable? When testing this on itself, it takes a good couple seconds to perform the assist.
  • Is there a way to hide the rustc diagnostics that overlap with this functionality?

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 3, 2023
@obsgolem
Copy link
Contributor Author

obsgolem commented May 3, 2023

The tests are failing due to trailing whitespace. This whitespace is in my test cases, how do I bypass this? Or should I go through and make sure that the assist never leaves behind trailing whitespace?

@matklad
Copy link
Member

matklad commented May 3, 2023

Cleaning up trailing ws would be better! This is something ted should take care of for you, but it doesn't fully yet. Eg, see how insert_all fixes up whitespace on insertion.

Also, take a look at edit_in_place.rs --- "remove use" probably belongs there.

Is the performance acceptable? When testing this on itself, it takes a good couple seconds to perform the assist.

Yeah, we definitely should be better. At the same time, I think it's ok to land a poorly performing version first, it it is disabled via config (see AssistConfig). I haven't looked into why is it slow, but I think SearchScope::module_and_children(ctx.db(), m) might be one explanation. This means that, eg, for lib.rs will look at the whole crate, which is not great.

I think what we want perhaps is "not used in the current file". Like, yes, there's always a possibility that some module somewhere does use super::super::super::ImportedItem, but that feels like something we should lint against anyway.

@Veykril
Copy link
Member

Veykril commented May 3, 2023

SearchScope::module_and_children(ctx.db(), m) is most likely the cause as it forces all modules to be re-parsed if they aren't in the LRU cache.

@obsgolem
Copy link
Contributor Author

obsgolem commented May 4, 2023

I think what we want perhaps is "not used in the current file". Like, yes, there's always a possibility that some module somewhere does use super::super::super::ImportedItem, but that feels like something we should lint against anyway.

I am not sure I agree. Consider the test module. Uses in the test module shouldn't count as uses in the overall file.

I didn't realize that module_and_children included other files, I definitely agree that uses in other files shouldn't count.

@obsgolem obsgolem force-pushed the master branch 3 times, most recently from 56615d1 to fb0a675 Compare May 4, 2023 02:00
@bors
Copy link
Collaborator

bors commented May 6, 2023

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

@Veykril
Copy link
Member

Veykril commented May 9, 2023

Please remove the merge commit, we prefer rebases

@bors
Copy link
Collaborator

bors commented May 25, 2023

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

crates/ide-db/src/search.rs Outdated Show resolved Hide resolved
crates/ide-assists/src/handlers/remove_unused_imports.rs Outdated Show resolved Hide resolved
@lnicola
Copy link
Member

lnicola commented May 30, 2023

This would be nice as a diagnostic too 😅.

@Veykril
Copy link
Member

Veykril commented May 31, 2023

I didn't realize that module_and_children included other files, I definitely agree that uses in other files shouldn't count.

Did you change the behavior here?

@obsgolem
Copy link
Contributor Author

I don't think so (been a few weeks, not 100% sure); I might have just forgotten to fix that. I will try to take a look at that tonight.

@obsgolem
Copy link
Contributor Author

obsgolem commented Jun 5, 2023

Went ahead and added a function for this. I needed a way to split a TextRange on another subrange, but I don't know where to put unit tests for TextRange methods, thus I haven't fully tested the module function yet.

@Veykril
Copy link
Member

Veykril commented Jun 12, 2023

Sorry for the long silence, have been busy and sick (and slowly reviewing other PRs here), will try to get back to this this week.

Comment on lines 149 to 191
fn split_at_subrange(
first: TextRange,
second: TextRange,
) -> (TextRange, Option<TextRange>) {
let intersect = first.intersect(second);
if let Some(intersect) = intersect {
let start_range = TextRange::new(first.start(), intersect.start());

if intersect.end() < first.end() {
(start_range, Some(TextRange::new(intersect.end(), first.end())))
} else {
(start_range, None)
}
} else {
(first, None)
}
}

if let Some(range) = range {
let mut ranges = vec![range];

for child in module.children(db) {
let rng = match child.definition_source(db).value {
ModuleSource::SourceFile(_) => continue,
ModuleSource::Module(it) => it.syntax().text_range(),
ModuleSource::BlockExpr(it) => it.syntax().text_range(),
};
let mut new_ranges = Vec::new();
for old_range in ranges.iter_mut() {
let split = split_at_subrange(old_range.clone(), rng);
*old_range = split.0;
new_ranges.extend(split.1);
}

ranges.append(&mut new_ranges);
}

for range in ranges {
entries.insert(file_id, Some(range));
}
} else {
entries.insert(file_id, range);
}
Copy link
Member

Choose a reason for hiding this comment

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

Let's not add this stuff here, it's fine if we include inline child modules in a modules search range (in fact, the current logic here will remove block expressions if the declare items which is also definitely unexpected).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Without this we can have a mismatch between the rustc warning and the assist. Is that ok? I can fix the block expression thing just by ignoring that case in the match.

Copy link
Member

Choose a reason for hiding this comment

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

As in an import that is not used in the module but in the child would silence the assist?

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 is correct. use_in_submodule_doesnt_count tests this. If you remove this code then that test will fail.

Copy link
Member

Choose a reason for hiding this comment

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

Well, I also just realized that this doesn't work here since SearchScope's entries is a hashmap (so the code here currently only sets the search range to the last one). I think it would be better to just construct the whole module search scope then and have the remove_unused_imports assist split the searchscope accordingly into multiple for each range of the module then (like is done here right now)

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 moved this to remove_unused_imports.rs and made it return a Vec<SearchScope>. This function could still easily go in search.rs I think.

@Veykril Veykril added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 22, 2023
@obsgolem
Copy link
Contributor Author

obsgolem commented Jul 9, 2023

Apologies for the delay. Final Fantasy 16, then life got me. Gonna update this tonight.

@adamhp
Copy link

adamhp commented Jul 21, 2023

@obsgolem @Veykril Anything left on this? 🙂

@Veykril
Copy link
Member

Veykril commented Aug 1, 2023

@bors r+

@bors
Copy link
Collaborator

bors commented Aug 1, 2023

📌 Commit 7a87a35 has been approved by Veykril

It is now in the queue for this repository.

@bors
Copy link
Collaborator

bors commented Aug 1, 2023

⌛ Testing commit 7a87a35 with merge 62dcf39...

@bors
Copy link
Collaborator

bors commented Aug 1, 2023

☀️ Test successful - checks-actions
Approved by: Veykril
Pushing 62dcf39 to master...

@bors bors merged commit 62dcf39 into rust-lang:master Aug 1, 2023
10 checks passed
@lnicola lnicola changed the title Added remove unused imports assist feat: Added remove unused imports assist Aug 1, 2023
@lnicola
Copy link
Member

lnicola commented Aug 7, 2023

remove-unused-imports.mp4

@lem0nify
Copy link

lem0nify commented Aug 9, 2023

finally!
but why not to add this to vscode's Organize Imports (Shift + Alt + O) action?

@heksesang
Copy link

This feature also seems to remove imports of attributes like async_trait which are actually in use. And there seems to be no test of how this works with imported attributes in the test suite?

@obsgolem
Copy link
Contributor Author

@heksesang Probably want to put in an issue for that. That might be a limitation of the current find usages code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants