Skip to content

Commit e28aa19

Browse files
bors[bot]Veykril
andauthored
Merge #10439
10439: fix: fix insert_use incorrectly merging glob imports r=Veykril a=Veykril Fixes #6800 bors r+ Co-authored-by: Lukas Wirth <[email protected]>
2 parents ebe6c38 + 5b0c91d commit e28aa19

File tree

3 files changed

+103
-92
lines changed

3 files changed

+103
-92
lines changed

crates/ide_db/src/helpers/insert_use.rs

Lines changed: 76 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Handle syntactic aspects of inserting a new `use`.
1+
//! Handle syntactic aspects of inserting a new `use` item.
22
#[cfg(test)]
33
mod tests;
44

@@ -103,81 +103,6 @@ impl ImportScope {
103103
ImportScope::Block(block) => ImportScope::Block(block.clone_for_update()),
104104
}
105105
}
106-
107-
fn guess_granularity_from_scope(&self) -> ImportGranularityGuess {
108-
// The idea is simple, just check each import as well as the import and its precedent together for
109-
// whether they fulfill a granularity criteria.
110-
let use_stmt = |item| match item {
111-
ast::Item::Use(use_) => {
112-
let use_tree = use_.use_tree()?;
113-
Some((use_tree, use_.visibility(), use_.attrs()))
114-
}
115-
_ => None,
116-
};
117-
let mut use_stmts = match self {
118-
ImportScope::File(f) => f.items(),
119-
ImportScope::Module(m) => m.items(),
120-
ImportScope::Block(b) => b.items(),
121-
}
122-
.filter_map(use_stmt);
123-
let mut res = ImportGranularityGuess::Unknown;
124-
let (mut prev, mut prev_vis, mut prev_attrs) = match use_stmts.next() {
125-
Some(it) => it,
126-
None => return res,
127-
};
128-
loop {
129-
if let Some(use_tree_list) = prev.use_tree_list() {
130-
if use_tree_list.use_trees().any(|tree| tree.use_tree_list().is_some()) {
131-
// Nested tree lists can only occur in crate style, or with no proper style being enforced in the file.
132-
break ImportGranularityGuess::Crate;
133-
} else {
134-
// Could still be crate-style so continue looking.
135-
res = ImportGranularityGuess::CrateOrModule;
136-
}
137-
}
138-
139-
let (curr, curr_vis, curr_attrs) = match use_stmts.next() {
140-
Some(it) => it,
141-
None => break res,
142-
};
143-
if eq_visibility(prev_vis, curr_vis.clone()) && eq_attrs(prev_attrs, curr_attrs.clone())
144-
{
145-
if let Some((prev_path, curr_path)) = prev.path().zip(curr.path()) {
146-
if let Some((prev_prefix, _)) = common_prefix(&prev_path, &curr_path) {
147-
if prev.use_tree_list().is_none() && curr.use_tree_list().is_none() {
148-
let prefix_c = prev_prefix.qualifiers().count();
149-
let curr_c = curr_path.qualifiers().count() - prefix_c;
150-
let prev_c = prev_path.qualifiers().count() - prefix_c;
151-
if curr_c == 1 && prev_c == 1 {
152-
// Same prefix, only differing in the last segment and no use tree lists so this has to be of item style.
153-
break ImportGranularityGuess::Item;
154-
} else {
155-
// Same prefix and no use tree list but differs in more than one segment at the end. This might be module style still.
156-
res = ImportGranularityGuess::ModuleOrItem;
157-
}
158-
} else {
159-
// Same prefix with item tree lists, has to be module style as it
160-
// can't be crate style since the trees wouldn't share a prefix then.
161-
break ImportGranularityGuess::Module;
162-
}
163-
}
164-
}
165-
}
166-
prev = curr;
167-
prev_vis = curr_vis;
168-
prev_attrs = curr_attrs;
169-
}
170-
}
171-
}
172-
173-
#[derive(PartialEq, PartialOrd, Debug, Clone, Copy)]
174-
enum ImportGranularityGuess {
175-
Unknown,
176-
Item,
177-
Module,
178-
ModuleOrItem,
179-
Crate,
180-
CrateOrModule,
181106
}
182107

183108
/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
@@ -189,7 +114,7 @@ pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) {
189114
ImportGranularity::Item | ImportGranularity::Preserve => None,
190115
};
191116
if !cfg.enforce_granularity {
192-
let file_granularity = scope.guess_granularity_from_scope();
117+
let file_granularity = guess_granularity_from_scope(scope);
193118
mb = match file_granularity {
194119
ImportGranularityGuess::Unknown => mb,
195120
ImportGranularityGuess::Item => None,
@@ -271,6 +196,80 @@ impl ImportGroup {
271196
}
272197
}
273198

199+
#[derive(PartialEq, PartialOrd, Debug, Clone, Copy)]
200+
enum ImportGranularityGuess {
201+
Unknown,
202+
Item,
203+
Module,
204+
ModuleOrItem,
205+
Crate,
206+
CrateOrModule,
207+
}
208+
209+
fn guess_granularity_from_scope(scope: &ImportScope) -> ImportGranularityGuess {
210+
// The idea is simple, just check each import as well as the import and its precedent together for
211+
// whether they fulfill a granularity criteria.
212+
let use_stmt = |item| match item {
213+
ast::Item::Use(use_) => {
214+
let use_tree = use_.use_tree()?;
215+
Some((use_tree, use_.visibility(), use_.attrs()))
216+
}
217+
_ => None,
218+
};
219+
let mut use_stmts = match scope {
220+
ImportScope::File(f) => f.items(),
221+
ImportScope::Module(m) => m.items(),
222+
ImportScope::Block(b) => b.items(),
223+
}
224+
.filter_map(use_stmt);
225+
let mut res = ImportGranularityGuess::Unknown;
226+
let (mut prev, mut prev_vis, mut prev_attrs) = match use_stmts.next() {
227+
Some(it) => it,
228+
None => return res,
229+
};
230+
loop {
231+
if let Some(use_tree_list) = prev.use_tree_list() {
232+
if use_tree_list.use_trees().any(|tree| tree.use_tree_list().is_some()) {
233+
// Nested tree lists can only occur in crate style, or with no proper style being enforced in the file.
234+
break ImportGranularityGuess::Crate;
235+
} else {
236+
// Could still be crate-style so continue looking.
237+
res = ImportGranularityGuess::CrateOrModule;
238+
}
239+
}
240+
241+
let (curr, curr_vis, curr_attrs) = match use_stmts.next() {
242+
Some(it) => it,
243+
None => break res,
244+
};
245+
if eq_visibility(prev_vis, curr_vis.clone()) && eq_attrs(prev_attrs, curr_attrs.clone()) {
246+
if let Some((prev_path, curr_path)) = prev.path().zip(curr.path()) {
247+
if let Some((prev_prefix, _)) = common_prefix(&prev_path, &curr_path) {
248+
if prev.use_tree_list().is_none() && curr.use_tree_list().is_none() {
249+
let prefix_c = prev_prefix.qualifiers().count();
250+
let curr_c = curr_path.qualifiers().count() - prefix_c;
251+
let prev_c = prev_path.qualifiers().count() - prefix_c;
252+
if curr_c == 1 && prev_c == 1 {
253+
// Same prefix, only differing in the last segment and no use tree lists so this has to be of item style.
254+
break ImportGranularityGuess::Item;
255+
} else {
256+
// Same prefix and no use tree list but differs in more than one segment at the end. This might be module style still.
257+
res = ImportGranularityGuess::ModuleOrItem;
258+
}
259+
} else {
260+
// Same prefix with item tree lists, has to be module style as it
261+
// can't be crate style since the trees wouldn't share a prefix then.
262+
break ImportGranularityGuess::Module;
263+
}
264+
}
265+
}
266+
}
267+
prev = curr;
268+
prev_vis = curr_vis;
269+
prev_attrs = curr_attrs;
270+
}
271+
}
272+
274273
fn insert_use_(
275274
scope: &ImportScope,
276275
insert_path: &ast::Path,

crates/ide_db/src/helpers/insert_use/tests.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,7 @@ fn merge_mod_into_glob() {
612612

613613
#[test]
614614
fn merge_self_glob() {
615+
cov_mark::check!(merge_self_glob);
615616
check_with_config(
616617
"self",
617618
r"use self::*;",
@@ -627,6 +628,17 @@ fn merge_self_glob() {
627628
// FIXME: have it emit `use {self, *}`?
628629
}
629630

631+
#[test]
632+
fn merge_glob() {
633+
check_crate(
634+
"syntax::SyntaxKind",
635+
r"
636+
use syntax::{SyntaxKind::*};",
637+
r"
638+
use syntax::{SyntaxKind::{*, self}};",
639+
)
640+
}
641+
630642
#[test]
631643
fn merge_glob_nested() {
632644
check_crate(
@@ -931,5 +943,5 @@ fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior
931943
fn check_guess(ra_fixture: &str, expected: ImportGranularityGuess) {
932944
let syntax = ast::SourceFile::parse(ra_fixture).tree().syntax().clone();
933945
let file = super::ImportScope::from(syntax).unwrap();
934-
assert_eq!(file.guess_granularity_from_scope(), expected);
946+
assert_eq!(super::guess_granularity_from_scope(&file), expected);
935947
}

crates/ide_db/src/helpers/merge_imports.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ pub enum MergeBehavior {
1919
}
2020

2121
impl MergeBehavior {
22-
#[inline]
2322
fn is_tree_allowed(&self, tree: &ast::UseTree) -> bool {
2423
match self {
2524
MergeBehavior::Crate => true,
@@ -114,7 +113,7 @@ fn recursive_merge(
114113
let rhs_path = rhs_path?;
115114
let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?;
116115
if lhs_prefix == lhs_path && rhs_prefix == rhs_path {
117-
let tree_is_self = |tree: ast::UseTree| {
116+
let tree_is_self = |tree: &ast::UseTree| {
118117
tree.path().as_ref().map(path_is_self).unwrap_or(false)
119118
};
120119
// Check if only one of the two trees has a tree list, and
@@ -123,7 +122,7 @@ fn recursive_merge(
123122
// the list is already included in the other one via `self`.
124123
let tree_contains_self = |tree: &ast::UseTree| {
125124
tree.use_tree_list()
126-
.map(|tree_list| tree_list.use_trees().any(tree_is_self))
125+
.map(|tree_list| tree_list.use_trees().any(|it| tree_is_self(&it)))
127126
.unwrap_or(false)
128127
};
129128
match (tree_contains_self(lhs_t), tree_contains_self(&rhs_t)) {
@@ -141,17 +140,18 @@ fn recursive_merge(
141140
// glob import of said module see the `merge_self_glob` or
142141
// `merge_mod_into_glob` tests.
143142
if lhs_t.star_token().is_some() || rhs_t.star_token().is_some() {
144-
*lhs_t = make::use_tree(
145-
make::path_unqualified(make::path_segment_self()),
146-
None,
147-
None,
148-
false,
149-
);
150-
use_trees.insert(idx, make::use_tree_glob());
151-
continue;
152-
}
153-
154-
if lhs_t.use_tree_list().is_none() && rhs_t.use_tree_list().is_none() {
143+
if tree_is_self(lhs_t) || tree_is_self(&rhs_t) {
144+
cov_mark::hit!(merge_self_glob);
145+
*lhs_t = make::use_tree(
146+
make::path_unqualified(make::path_segment_self()),
147+
None,
148+
None,
149+
false,
150+
);
151+
use_trees.insert(idx, make::use_tree_glob());
152+
continue;
153+
}
154+
} else if lhs_t.use_tree_list().is_none() && rhs_t.use_tree_list().is_none() {
155155
continue;
156156
}
157157
}

0 commit comments

Comments
 (0)