-
Notifications
You must be signed in to change notification settings - Fork 910
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
base: master
Are you sure you want to change the base?
Changes from 2 commits
6a1142d
adc8828
bfa696f
5f96cf1
660aa46
5a662f8
838e998
1fde348
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -218,7 +218,7 @@ pub(crate) fn normalize_use_trees_with_granularity( | |||||||||||||
ImportGranularity::Item => return flatten_use_trees(use_trees, ImportGranularity::Item), | ||||||||||||||
ImportGranularity::Preserve => return use_trees, | ||||||||||||||
ImportGranularity::Crate => SharedPrefix::Crate, | ||||||||||||||
ImportGranularity::Module => SharedPrefix::Module, | ||||||||||||||
ImportGranularity::Module | ImportGranularity::ModuleCondensed => SharedPrefix::Module, | ||||||||||||||
ImportGranularity::One => SharedPrefix::One, | ||||||||||||||
}; | ||||||||||||||
|
||||||||||||||
|
@@ -244,9 +244,65 @@ pub(crate) fn normalize_use_trees_with_granularity( | |||||||||||||
} | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
if import_granularity == ImportGranularity::ModuleCondensed { | ||||||||||||||
condense_use_trees(&mut result); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
result | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
/// Merge singleton imports into parent modules' use-trees where it does't | ||||||||||||||
/// introduce nested braces. | ||||||||||||||
fn condense_use_trees(use_trees: &mut Vec<UseTree>) { | ||||||||||||||
// Sort by path length to put singletons after the trees into which they | ||||||||||||||
// may be absorbed. | ||||||||||||||
use_trees.sort_unstable_by_key(|tree| tree.path.len()); | ||||||||||||||
|
||||||||||||||
// Search for singletons back-to-front to preserve the just mentioned | ||||||||||||||
// relative order between absorbing trees and singletons. Note that a | ||||||||||||||
// singleton that is found and merged into _some_ tree is `swap_remove`d | ||||||||||||||
// from the `use_trees` vector. | ||||||||||||||
for n in (0..use_trees.len()).rev() { | ||||||||||||||
let singleton = &use_trees[n]; | ||||||||||||||
|
||||||||||||||
if !singleton.is_singleton() || singleton.attrs.is_some() || singleton.contains_comment() { | ||||||||||||||
continue; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
let mut best_index_and_len = None; | ||||||||||||||
|
||||||||||||||
// Search front-to-back for a tree to merge the singleton into. If | ||||||||||||||
// multiple trees are equally good candidates, prefer the earliest one. | ||||||||||||||
Comment on lines
+282
to
+283
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added a unit test (with an explanation). |
||||||||||||||
// Like above, these precautions help preserve the relative order | ||||||||||||||
// between absorbing trees and singletons. | ||||||||||||||
for curr_index in 0..n { | ||||||||||||||
let curr_tree = &use_trees[curr_index]; | ||||||||||||||
let curr_len = curr_tree.shared_prefix_len(singleton); | ||||||||||||||
if best_index_and_len.map_or(false, |(_, best_len)| best_len >= curr_len) { | ||||||||||||||
continue; | ||||||||||||||
} | ||||||||||||||
if curr_len < 1 | ||||||||||||||
|| !(curr_tree.is_singleton() || curr_len + 1 == curr_tree.path.len()) | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I think so. |
||||||||||||||
|| curr_tree.attrs.is_some() | ||||||||||||||
|| curr_tree.contains_comment() | ||||||||||||||
|| !curr_tree.same_visibility(singleton) | ||||||||||||||
{ | ||||||||||||||
continue; | ||||||||||||||
} | ||||||||||||||
best_index_and_len = Some((curr_index, curr_len)); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
if let Some((best_index, _)) = best_index_and_len { | ||||||||||||||
let singleton = use_trees.swap_remove(n); | ||||||||||||||
use_trees[best_index].merge(&singleton, SharedPrefix::Crate); | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
// Put the trees back in their preferred order. | ||||||||||||||
use_trees.sort(); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
fn flatten_use_trees( | ||||||||||||||
use_trees: Vec<UseTree>, | ||||||||||||||
import_granularity: ImportGranularity, | ||||||||||||||
|
@@ -669,6 +725,18 @@ impl UseTree { | |||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
fn shared_prefix_len(&self, other: &UseTree) -> usize { | ||||||||||||||
let mut n = 0; | ||||||||||||||
while n < self.path.len() && n < other.path.len() && self.path[n] == other.path[n] { | ||||||||||||||
n += 1; | ||||||||||||||
} | ||||||||||||||
n | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice! |
||||||||||||||
} | ||||||||||||||
|
||||||||||||||
fn is_singleton(&self) -> bool { | ||||||||||||||
!matches!(self.path.last().unwrap().kind, UseSegmentKind::List(_)) | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we find a way to write this without There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure. (FWIW, that pattern appears elsewhere in the file, though.) |
||||||||||||||
} | ||||||||||||||
|
||||||||||||||
fn flatten(self, import_granularity: ImportGranularity) -> Vec<UseTree> { | ||||||||||||||
if self.path.is_empty() || self.contains_comment() { | ||||||||||||||
return vec![self]; | ||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// rustfmt-imports_granularity: ModuleCondensed | ||
|
||
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}; | ||
|
||
#[cfg(test)] | ||
use foo::{a::b, c::d}; | ||
use foo::e; | ||
|
||
use bar::{ | ||
// comment | ||
a::b, | ||
// more comment | ||
c::d, | ||
e::f, | ||
}; | ||
|
||
use b::{f::g, h::{i, j} /* After b::h group */}; | ||
use b::e; | ||
use b::{/* Before b::l group */ l::{self, m, n::o, p::*}, q}; | ||
use b::d; | ||
use b::r; // After b::r | ||
use b::q::{self /* After b::q::self */}; | ||
use b::u::{ | ||
a, | ||
b, | ||
}; | ||
use b::t::{ | ||
// Before b::t::a | ||
a, | ||
b, | ||
}; | ||
use b::s::{ | ||
a, | ||
b, // After b::s::b | ||
}; | ||
use b::v::{ | ||
// Before b::v::a | ||
a, | ||
// Before b::v::b | ||
b, | ||
}; | ||
use b::t::{/* Before b::t::self */ self}; | ||
use b::c; |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,50 @@ | ||||
// rustfmt-imports_granularity: ModuleCondensed | ||||
|
||||
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}; | ||||
Comment on lines
+3
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, 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 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}; There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 I think this case may be the culprit: Line 955 in 6356fca
That is, a single identifier (like If you agree with my analysis, then maybe the sort order is outside the scope of this PR? EDIT: Ignoring |
||||
|
||||
use foo::e; | ||||
#[cfg(test)] | ||||
use foo::{a::b, c::d}; | ||||
|
||||
use bar::{ | ||||
// comment | ||||
a::b, | ||||
// more comment | ||||
c::d, | ||||
e::f, | ||||
}; | ||||
|
||||
use b::q::{self /* After b::q::self */}; | ||||
use b::r; // After b::r | ||||
use b::s::{ | ||||
a, | ||||
b, // After b::s::b | ||||
}; | ||||
use b::t::{/* Before b::t::self */ self}; | ||||
use b::t::{ | ||||
// Before b::t::a | ||||
a, | ||||
b, | ||||
}; | ||||
use b::u::{a, b}; | ||||
use b::v::{ | ||||
// Before b::v::a | ||||
a, | ||||
// Before b::v::b | ||||
b, | ||||
}; | ||||
use b::{c, d, e}; | ||||
use b::{ | ||||
f::g, | ||||
h::{i, j}, /* After b::h group */ | ||||
}; | ||||
use b::{ | ||||
/* Before b::l group */ l::{self, m, n::o, p::*}, | ||||
q, | ||||
}; |
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 would prefer if you use iterators over using indices, perhaps using
iter_mut
andinto_slice
. If that seems too complicated, then it's okay for this to remain using indices.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 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:
0..n
. So we would need to somehow recover the bound from the outer loop. (We could useenumerate
, but I'm not sure that would be more clear.)n
getsswap_remove
d, andswap_remove
requires that the index be passed.