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 ImportGranularity::ModuleCondensed #5781

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

smoelius
Copy link

Given that imports_granularity is unstable, is this something you would consider merging?

From the docs I added:

ModuleCondensed:

Like Module, but singleton imports are included in parent modules' use statements where it doesn't introduce nested braces.

use foo::b::{f, g};
use foo::{a, b, c, d::e};
use qux::{h, i};

I have long perceived this to be Clippy's convention. I ran this on Clippy's source code and about 2/3 of the files were left unchanged. Since this supposed convention has been enforced by hand, I consider this confirmation.

@smoelius
Copy link
Author

@flip1995 Sorry to ping you, but would you be able to comment on whether this is Clippy's preferred convention?

@flip1995
Copy link
Member

We don't really have a import convention in Clippy, other than that we don't allow wildcard imports, enforced by one of our pedantic lints.

That being said, I like this convention and would adapt it for Clippy. Especially when 2/3 of Clippy follow it already.

@calebcartwright
Copy link
Member

calebcartwright commented Jun 19, 2023

is this something you would consider merging?

In principle yes. I don't envision us adding any variants ourselves but we're open to supporting others so long as someone submits the PR like this and it doesn't add any undue maintenance burden for us. We're unlikely to be able to spend any review cycles on this for the foreseeable future though.

Haven't looked at the specifics of the proposed change yet, but would suggest including a #[unstable_variant] attr on the new config variant if you haven't already done so. Stabilization of imports granularity is still a few releases out, but it's definitely on the near term horizon and probably better to go ahead and get that attr on the new variant so that we don't accidentally forget

@smoelius1
Copy link

smoelius1 commented Jun 20, 2023

@calebcartwright Thank you very much for your reply.

I am currently travelling, but will add the unstable_variant attribute no later than next week.

@smoelius
Copy link
Author

Haven't looked at the specifics of the proposed change yet, but would suggest including a #[unstable_variant] attr on the new config variant if you haven't already done so.

Done. Please let me know what else is needed. Thanks.

@calebcartwright
Copy link
Member

@fee1-dead @CosmicHorrorDev - I know you're both busy (with both Rust and life things 😄), but just tagging this as one for you to potentially review in case you're interested and get around to it before Yacin and/or myself.

I'm in favor of supporting the style associated with the variant, but haven't had a chance to look at the associated implementation

Copy link
Member

@fee1-dead fee1-dead left a comment

Choose a reason for hiding this comment

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

In addition, could you add unit test cases to test this behavior? thanks.

src/imports.rs Outdated
Comment on lines 729 to 733
let mut n = 0;
while n < self.path.len() && n < other.path.len() && self.path[n] == other.path[n] {
n += 1;
}
n
Copy link
Member

Choose a reason for hiding this comment

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

Using an iterator e.g. something like below should work better?

Suggested change
let mut n = 0;
while n < self.path.len() && n < other.path.len() && self.path[n] == other.path[n] {
n += 1;
}
n
self.path.iter().zip(other.iter()).take_while(|(a, b)| a == b).count()

Copy link
Author

Choose a reason for hiding this comment

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

Nice!

Comment on lines +275 to +276
// Search front-to-back for a tree to merge the singleton into. If
// multiple trees are equally good candidates, prefer the earliest one.
Copy link
Member

Choose a reason for hiding this comment

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

Could you add this as a unit test/comment the part in the ui test that is exercising the "prefer the earliest one" logic?

Copy link
Author

Choose a reason for hiding this comment

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

I added a unit test (with an explanation).

src/imports.rs Outdated
Comment on lines 266 to 267
for n in (0..use_trees.len()).rev() {
let singleton = &use_trees[n];
Copy link
Member

Choose a reason for hiding this comment

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

I would prefer if you use iterators over using indices, perhaps using iter_mut and into_slice. If that seems too complicated, then it's okay for this to remain using indices.

Copy link
Author

Choose a reason for hiding this comment

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

I'm not opposed to the idea, but I'm having a hard time seeing how it could work (probably my failure of imagination).

My difficulties include:

  • The inner loop must only consider the trees at indices 0..n. So we would need to somehow recover the bound from the outer loop. (We could use enumerate, but I'm not sure that would be more clear.)
  • If the inner loop selects a candidate for absorption, the tree at index n gets swap_removed, and swap_remove requires that the index be passed.
  • I'm assuming you mean that the inner loop should also use an iterator. In that case, the inner loop iterator would need to be mutable. But then wouldn't we run into borrow checker problems, with immutable and mutable iterators trying to refer to the same vector?

src/imports.rs Outdated
continue;
}
if curr_len < 1
|| !(curr_tree.is_singleton() || curr_len + 1 == curr_tree.path.len())
Copy link
Member

Choose a reason for hiding this comment

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

This logic is a bit hard to read. Does the below preserve the same behavior?

Suggested change
|| !(curr_tree.is_singleton() || curr_len + 1 == curr_tree.path.len())
|| !curr_tree.is_singleton() && curr_len + 1 != curr_tree.path.len()

Copy link
Author

Choose a reason for hiding this comment

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

Yes, I think so.

@smoelius
Copy link
Author

smoelius commented Jul 4, 2023

In addition, could you add unit test cases to test this behavior? thanks.

Done. (Apologies, as I hadn't noticed the existing unit tests.)

@smoelius
Copy link
Author

smoelius commented Jul 4, 2023

Sorry for the force push (minor grammatical adjustment). It should be good now.

Copy link
Member

@fee1-dead fee1-dead left a comment

Choose a reason for hiding this comment

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

LGTM

@smoelius
Copy link
Author

smoelius commented Oct 5, 2023

We're unlikely to be able to spend any review cycles on this for the foreseeable future though.

Point very well taken, but I thought I would check in. Any chance of merging this?

@smoelius
Copy link
Author

smoelius commented Jan 6, 2024

I thought I would check in again. Any chance of merging this?

Copy link
Contributor

@ytmimi ytmimi left a comment

Choose a reason for hiding this comment

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

@smoelius I don't think we're ready to move forward with this one just yet. I haven't spent too much time reviewing the details of condense_use_trees, but by looking at the test cases, the way the relative order of imports changes strikes me as odd. Do you think there's a way to better preserve the order?

Also, reorder_imports=true is on by default. can we add test cases where we set reorder_imports=false. I'd want to see what the output looks like in that case.

src/imports.rs Outdated
}

fn is_singleton(&self) -> bool {
!matches!(self.path.last().unwrap().kind, UseSegmentKind::List(_))
Copy link
Contributor

Choose a reason for hiding this comment

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

can we find a way to write this without unwrap()

Copy link
Author

Choose a reason for hiding this comment

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

Sure. (FWIW, that pattern appears elsewhere in the file, though.)

Comment on lines +3 to +9
use a::g::{h, i};
use a::j::k::{self, l};
use a::j::{self, m};
use a::n::{o::p, q};
use a::{b::c, d::e, f};
pub use a::{r::s, t};
use b::{self, c::d};
Copy link
Contributor

Choose a reason for hiding this comment

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

This is what we started with:

use a::{b::c, d::e};
use a::{f, g::{h, i}};
use a::{j::{self, k::{self, l}, m}, n::{o::p, q}};
pub use a::{r::s, t};
use b::{c::d, self};

I'm finding it a odd that the relative order of the import got moved around like they did. For example use a::{b::c, d::e}; was the first import, but then it got moved down to the 5th import after merging. I think I would have expected the output to look more like this:

use a::{b::c, d::e, f};
use a::g::{h, i};
use a::j::{self, m};
use a::j::k::{self, l};
use a::n::{o::p, q};
pub use a::{r::s, t};
use b::{self, c::d};

I forget if visibility impacts import sorting. If it doesn't, I also would have expected pub use a::{r::s, t}; to be the second import. Something like this:

use a::{b::c, d::e, f};
pub use a::{r::s, t};
use a::g::{h, i};
use a::j::{self, m};
use a::j::k::{self, l};
use a::n::{o::p, q};
use b::{self, c::d};

Copy link
Author

@smoelius smoelius Jan 10, 2024

Choose a reason for hiding this comment

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

For both of the examples given after "I think I would have expected...", vanilla rustfmt reorders them as in the referenced code (playground).

I think this case may be the culprit:

(Ident(..), _) => Ordering::Less,

That is, a single identifier (like g) is considered "less" than a list (like {b::c, d::e, f}).

If you agree with my analysis, then maybe the sort order is outside the scope of this PR?

EDIT: Ignoring reorder_imports still needs to be addressed, of course.

@smoelius
Copy link
Author

smoelius commented Jan 8, 2024

Also, reorder_imports=true is on by default. can we add test cases where we set reorder_imports=false. I'd want to see what the output looks like in that case.

I added such a test case. I also took the liberty of adding such a test case for imports_granularity: Module. At least for the given input, the results are the same for both imports_granularity: Module and imports_granularity: ModuleCondensed.

Apologies, as I did not realize there was a reorder_imports option and so did not consider how it would interact with imports_granularity. I will give it some thought.

@smoelius
Copy link
Author

@ytmimi I think imports_granularity: ModuleCondensed now works sensibly when reorder_imports is off.

Also, it appears that imports_granularity has an effect only when either reorder_imports is on, or group_imports is not Preserve (the default). So I updated the reorder_imports: false tests to use group_imports: One.

@smoelius
Copy link
Author

@ytmimi Would it be possible to give this PR another look?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants