aboutsummaryrefslogtreecommitdiff
path: root/crates/assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists')
-rw-r--r--crates/assists/Cargo.toml17
-rw-r--r--crates/assists/src/assist_context.rs4
-rw-r--r--crates/assists/src/handlers/expand_glob_import.rs732
-rw-r--r--crates/assists/src/handlers/invert_if.rs18
-rw-r--r--crates/assists/src/utils.rs21
5 files changed, 664 insertions, 128 deletions
diff --git a/crates/assists/Cargo.toml b/crates/assists/Cargo.toml
index a560a35c7..264125651 100644
--- a/crates/assists/Cargo.toml
+++ b/crates/assists/Cargo.toml
@@ -1,6 +1,7 @@
1[package] 1[package]
2name = "assists" 2name = "assists"
3version = "0.0.0" 3version = "0.0.0"
4description = "TBD"
4license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
5authors = ["rust-analyzer developers"] 6authors = ["rust-analyzer developers"]
6edition = "2018" 7edition = "2018"
@@ -13,11 +14,11 @@ rustc-hash = "1.1.0"
13itertools = "0.9.0" 14itertools = "0.9.0"
14either = "1.5.3" 15either = "1.5.3"
15 16
16stdx = { path = "../stdx" } 17stdx = { path = "../stdx", version = "0.0.0" }
17syntax = { path = "../syntax" } 18syntax = { path = "../syntax", version = "0.0.0" }
18text_edit = { path = "../text_edit" } 19text_edit = { path = "../text_edit", version = "0.0.0" }
19profile = { path = "../profile" } 20profile = { path = "../profile", version = "0.0.0" }
20base_db = { path = "../base_db" } 21base_db = { path = "../base_db", version = "0.0.0" }
21ide_db = { path = "../ide_db" } 22ide_db = { path = "../ide_db", version = "0.0.0" }
22hir = { path = "../hir" } 23hir = { path = "../hir", version = "0.0.0" }
23test_utils = { path = "../test_utils" } 24test_utils = { path = "../test_utils", version = "0.0.0" }
diff --git a/crates/assists/src/assist_context.rs b/crates/assists/src/assist_context.rs
index 11c171fc2..bf520069e 100644
--- a/crates/assists/src/assist_context.rs
+++ b/crates/assists/src/assist_context.rs
@@ -73,10 +73,6 @@ impl<'a> AssistContext<'a> {
73 self.sema.db 73 self.sema.db
74 } 74 }
75 75
76 pub(crate) fn source_file(&self) -> &SourceFile {
77 &self.source_file
78 }
79
80 // NB, this ignores active selection. 76 // NB, this ignores active selection.
81 pub(crate) fn offset(&self) -> TextSize { 77 pub(crate) fn offset(&self) -> TextSize {
82 self.frange.range.start() 78 self.frange.range.start()
diff --git a/crates/assists/src/handlers/expand_glob_import.rs b/crates/assists/src/handlers/expand_glob_import.rs
index 81d0af2f3..b39d040f6 100644
--- a/crates/assists/src/handlers/expand_glob_import.rs
+++ b/crates/assists/src/handlers/expand_glob_import.rs
@@ -1,10 +1,10 @@
1use either::Either; 1use either::Either;
2use hir::{AssocItem, MacroDef, ModuleDef, Name, PathResolution, ScopeDef, SemanticsScope}; 2use hir::{AssocItem, MacroDef, Module, ModuleDef, Name, PathResolution, ScopeDef};
3use ide_db::{ 3use ide_db::{
4 defs::{classify_name_ref, Definition, NameRefClass}, 4 defs::{classify_name_ref, Definition, NameRefClass},
5 RootDatabase, 5 search::SearchScope,
6}; 6};
7use syntax::{algo, ast, match_ast, AstNode, SyntaxNode, SyntaxToken, T}; 7use syntax::{algo, ast, AstNode, Direction, SyntaxNode, SyntaxToken, T};
8 8
9use crate::{ 9use crate::{
10 assist_context::{AssistBuilder, AssistContext, Assists}, 10 assist_context::{AssistBuilder, AssistContext, Assists},
@@ -38,140 +38,259 @@ use crate::{
38// ``` 38// ```
39pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 39pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
40 let star = ctx.find_token_at_offset(T![*])?; 40 let star = ctx.find_token_at_offset(T![*])?;
41 let mod_path = find_mod_path(&star)?; 41 let (parent, mod_path) = find_parent_and_path(&star)?;
42 let module = match ctx.sema.resolve_path(&mod_path)? { 42 let target_module = match ctx.sema.resolve_path(&mod_path)? {
43 PathResolution::Def(ModuleDef::Module(it)) => it, 43 PathResolution::Def(ModuleDef::Module(it)) => it,
44 _ => return None, 44 _ => return None,
45 }; 45 };
46 46
47 let source_file = ctx.source_file(); 47 let current_scope = ctx.sema.scope(&star.parent());
48 let scope = ctx.sema.scope_at_offset(source_file.syntax(), ctx.offset()); 48 let current_module = current_scope.module()?;
49 49
50 let defs_in_mod = find_defs_in_mod(ctx, scope, module)?; 50 let refs_in_target = find_refs_in_mod(ctx, target_module, Some(current_module))?;
51 let name_refs_in_source_file = 51 let imported_defs = find_imported_defs(ctx, star)?;
52 source_file.syntax().descendants().filter_map(ast::NameRef::cast).collect(); 52 let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs);
53 let used_names = find_used_names(ctx, defs_in_mod, name_refs_in_source_file);
54 53
55 let parent = star.parent().parent()?; 54 let target = parent.clone().either(|n| n.syntax().clone(), |n| n.syntax().clone());
56 acc.add( 55 acc.add(
57 AssistId("expand_glob_import", AssistKind::RefactorRewrite), 56 AssistId("expand_glob_import", AssistKind::RefactorRewrite),
58 "Expand glob import", 57 "Expand glob import",
59 parent.text_range(), 58 target.text_range(),
60 |builder| { 59 |builder| {
61 replace_ast(builder, &parent, mod_path, used_names); 60 replace_ast(builder, parent, mod_path, names_to_import);
62 }, 61 },
63 ) 62 )
64} 63}
65 64
66fn find_mod_path(star: &SyntaxToken) -> Option<ast::Path> { 65fn find_parent_and_path(
67 star.ancestors().find_map(|n| ast::UseTree::cast(n).and_then(|u| u.path())) 66 star: &SyntaxToken,
67) -> Option<(Either<ast::UseTree, ast::UseTreeList>, ast::Path)> {
68 return star.ancestors().find_map(|n| {
69 find_use_tree_list(n.clone())
70 .and_then(|(u, p)| Some((Either::Right(u), p)))
71 .or_else(|| find_use_tree(n).and_then(|(u, p)| Some((Either::Left(u), p))))
72 });
73
74 fn find_use_tree_list(n: SyntaxNode) -> Option<(ast::UseTreeList, ast::Path)> {
75 let use_tree_list = ast::UseTreeList::cast(n)?;
76 let path = use_tree_list.parent_use_tree().path()?;
77 Some((use_tree_list, path))
78 }
79
80 fn find_use_tree(n: SyntaxNode) -> Option<(ast::UseTree, ast::Path)> {
81 let use_tree = ast::UseTree::cast(n)?;
82 let path = use_tree.path()?;
83 Some((use_tree, path))
84 }
68} 85}
69 86
70#[derive(PartialEq)] 87#[derive(Debug, PartialEq, Clone)]
71enum Def { 88enum Def {
72 ModuleDef(ModuleDef), 89 ModuleDef(ModuleDef),
73 MacroDef(MacroDef), 90 MacroDef(MacroDef),
74} 91}
75 92
76impl Def { 93impl Def {
77 fn name(&self, db: &RootDatabase) -> Option<Name> { 94 fn is_referenced_in(&self, ctx: &AssistContext) -> bool {
78 match self { 95 let def = match self {
79 Def::ModuleDef(def) => def.name(db), 96 Def::ModuleDef(def) => Definition::ModuleDef(*def),
80 Def::MacroDef(def) => def.name(db), 97 Def::MacroDef(def) => Definition::Macro(*def),
98 };
99
100 let search_scope = SearchScope::single_file(ctx.frange.file_id);
101 def.usages(&ctx.sema).in_scope(search_scope).at_least_one()
102 }
103}
104
105#[derive(Debug, Clone)]
106struct Ref {
107 // could be alias
108 visible_name: Name,
109 def: Def,
110}
111
112impl Ref {
113 fn from_scope_def(name: Name, scope_def: ScopeDef) -> Option<Self> {
114 match scope_def {
115 ScopeDef::ModuleDef(def) => Some(Ref { visible_name: name, def: Def::ModuleDef(def) }),
116 ScopeDef::MacroDef(def) => Some(Ref { visible_name: name, def: Def::MacroDef(def) }),
117 _ => None,
81 } 118 }
82 } 119 }
83} 120}
84 121
85fn find_defs_in_mod( 122#[derive(Debug, Clone)]
123struct Refs(Vec<Ref>);
124
125impl Refs {
126 fn used_refs(&self, ctx: &AssistContext) -> Refs {
127 Refs(
128 self.0
129 .clone()
130 .into_iter()
131 .filter(|r| {
132 if let Def::ModuleDef(ModuleDef::Trait(tr)) = r.def {
133 if tr
134 .items(ctx.db())
135 .into_iter()
136 .find(|ai| {
137 if let AssocItem::Function(f) = *ai {
138 Def::ModuleDef(ModuleDef::Function(f)).is_referenced_in(ctx)
139 } else {
140 false
141 }
142 })
143 .is_some()
144 {
145 return true;
146 }
147 }
148
149 r.def.is_referenced_in(ctx)
150 })
151 .collect(),
152 )
153 }
154
155 fn filter_out_by_defs(&self, defs: Vec<Def>) -> Refs {
156 Refs(self.0.clone().into_iter().filter(|r| !defs.contains(&r.def)).collect())
157 }
158}
159
160fn find_refs_in_mod(
86 ctx: &AssistContext, 161 ctx: &AssistContext,
87 from: SemanticsScope<'_>, 162 module: Module,
88 module: hir::Module, 163 visible_from: Option<Module>,
89) -> Option<Vec<Def>> { 164) -> Option<Refs> {
90 let module_scope = module.scope(ctx.db(), from.module()); 165 if let Some(from) = visible_from {
91 166 if !is_mod_visible_from(ctx, module, from) {
92 let mut defs = vec![]; 167 return None;
93 for (_, def) in module_scope {
94 match def {
95 ScopeDef::ModuleDef(def) => defs.push(Def::ModuleDef(def)),
96 ScopeDef::MacroDef(def) => defs.push(Def::MacroDef(def)),
97 _ => continue,
98 } 168 }
99 } 169 }
100 170
101 Some(defs) 171 let module_scope = module.scope(ctx.db(), visible_from);
172 let refs = module_scope.into_iter().filter_map(|(n, d)| Ref::from_scope_def(n, d)).collect();
173 Some(Refs(refs))
102} 174}
103 175
104fn find_used_names( 176fn is_mod_visible_from(ctx: &AssistContext, module: Module, from: Module) -> bool {
105 ctx: &AssistContext, 177 match module.parent(ctx.db()) {
106 defs_in_mod: Vec<Def>, 178 Some(parent) => {
107 name_refs_in_source_file: Vec<ast::NameRef>, 179 parent.visibility_of(ctx.db(), &ModuleDef::Module(module)).map_or(true, |vis| {
108) -> Vec<Name> { 180 vis.is_visible_from(ctx.db(), from.into()) && is_mod_visible_from(ctx, parent, from)
109 let defs_in_source_file = name_refs_in_source_file 181 })
110 .iter() 182 }
111 .filter_map(|r| classify_name_ref(&ctx.sema, r)) 183 None => true,
112 .filter_map(|rc| match rc { 184 }
113 NameRefClass::Definition(Definition::ModuleDef(def)) => Some(Def::ModuleDef(def)), 185}
114 NameRefClass::Definition(Definition::Macro(def)) => Some(Def::MacroDef(def)),
115 _ => None,
116 })
117 .collect::<Vec<Def>>();
118 186
119 defs_in_mod 187// looks for name refs in parent use block's siblings
120 .iter() 188//
121 .filter(|def| { 189// mod bar {
122 if let Def::ModuleDef(ModuleDef::Trait(tr)) = def { 190// mod qux {
123 for item in tr.items(ctx.db()) { 191// struct Qux;
124 if let AssocItem::Function(f) = item { 192// }
125 if defs_in_source_file.contains(&Def::ModuleDef(ModuleDef::Function(f))) { 193//
126 return true; 194// pub use qux::Qux;
127 } 195// }
128 } 196//
129 } 197// ↓ ---------------
130 } 198// use foo::*<|>;
199// use baz::Baz;
200// ↑ ---------------
201fn find_imported_defs(ctx: &AssistContext, star: SyntaxToken) -> Option<Vec<Def>> {
202 let parent_use_item_syntax =
203 star.ancestors().find_map(|n| if ast::Use::can_cast(n.kind()) { Some(n) } else { None })?;
204
205 Some(
206 [Direction::Prev, Direction::Next]
207 .iter()
208 .map(|dir| {
209 parent_use_item_syntax
210 .siblings(dir.to_owned())
211 .filter(|n| ast::Use::can_cast(n.kind()))
212 })
213 .flatten()
214 .filter_map(|n| Some(n.descendants().filter_map(ast::NameRef::cast)))
215 .flatten()
216 .filter_map(|r| match classify_name_ref(&ctx.sema, &r)? {
217 NameRefClass::Definition(Definition::ModuleDef(def)) => Some(Def::ModuleDef(def)),
218 NameRefClass::Definition(Definition::Macro(def)) => Some(Def::MacroDef(def)),
219 _ => None,
220 })
221 .collect(),
222 )
223}
131 224
132 defs_in_source_file.contains(def) 225fn find_names_to_import(
133 }) 226 ctx: &AssistContext,
134 .filter_map(|d| d.name(ctx.db())) 227 refs_in_target: Refs,
135 .collect() 228 imported_defs: Vec<Def>,
229) -> Vec<Name> {
230 let used_refs = refs_in_target.used_refs(ctx).filter_out_by_defs(imported_defs);
231 used_refs.0.iter().map(|r| r.visible_name.clone()).collect()
136} 232}
137 233
138fn replace_ast( 234fn replace_ast(
139 builder: &mut AssistBuilder, 235 builder: &mut AssistBuilder,
140 node: &SyntaxNode, 236 parent: Either<ast::UseTree, ast::UseTreeList>,
141 path: ast::Path, 237 path: ast::Path,
142 used_names: Vec<Name>, 238 names_to_import: Vec<Name>,
143) { 239) {
144 let replacement: Either<ast::UseTree, ast::UseTreeList> = match used_names.as_slice() { 240 let existing_use_trees = match parent.clone() {
145 [name] => Either::Left(ast::make::use_tree( 241 Either::Left(_) => vec![],
146 ast::make::path_from_text(&format!("{}::{}", path, name)), 242 Either::Right(u) => u
147 None, 243 .use_trees()
148 None, 244 .filter(|n|
149 false, 245 // filter out star
150 )), 246 n.star_token().is_none())
151 names => Either::Right(ast::make::use_tree_list(names.iter().map(|n| { 247 .collect(),
152 ast::make::use_tree(ast::make::path_from_text(&n.to_string()), None, None, false)
153 }))),
154 }; 248 };
155 249
156 let mut replace_node = |replacement: Either<ast::UseTree, ast::UseTreeList>| { 250 let new_use_trees: Vec<ast::UseTree> = names_to_import
157 algo::diff(node, &replacement.either(|u| u.syntax().clone(), |ut| ut.syntax().clone())) 251 .iter()
252 .map(|n| ast::make::use_tree(ast::make::path_from_text(&n.to_string()), None, None, false))
253 .collect();
254
255 let use_trees = [&existing_use_trees[..], &new_use_trees[..]].concat();
256
257 match use_trees.as_slice() {
258 [name] => {
259 if let Some(end_path) = name.path() {
260 let replacement = ast::make::use_tree(
261 ast::make::path_from_text(&format!("{}::{}", path, end_path)),
262 None,
263 None,
264 false,
265 );
266
267 algo::diff(
268 &parent.either(|n| n.syntax().clone(), |n| n.syntax().clone()),
269 replacement.syntax(),
270 )
271 .into_text_edit(builder.text_edit_builder());
272 }
273 }
274 names => {
275 let replacement = match parent {
276 Either::Left(_) => ast::make::use_tree(
277 path,
278 Some(ast::make::use_tree_list(names.to_owned())),
279 None,
280 false,
281 )
282 .syntax()
283 .clone(),
284 Either::Right(_) => ast::make::use_tree_list(names.to_owned()).syntax().clone(),
285 };
286
287 algo::diff(
288 &parent.either(|n| n.syntax().clone(), |n| n.syntax().clone()),
289 &replacement,
290 )
158 .into_text_edit(builder.text_edit_builder()); 291 .into_text_edit(builder.text_edit_builder());
159 };
160
161 match_ast! {
162 match node {
163 ast::UseTree(use_tree) => {
164 replace_node(replacement);
165 },
166 ast::UseTreeList(use_tree_list) => {
167 replace_node(replacement);
168 },
169 ast::Use(use_item) => {
170 builder.replace_ast(use_item, ast::make::use_(replacement.left_or_else(|ut| ast::make::use_tree(path, Some(ut), None, false))));
171 },
172 _ => {},
173 } 292 }
174 } 293 };
175} 294}
176 295
177#[cfg(test)] 296#[cfg(test)]
@@ -245,7 +364,46 @@ mod foo {
245 pub fn f() {} 364 pub fn f() {}
246} 365}
247 366
248use foo::{Baz, Bar, f}; 367use foo::{f, Baz, Bar};
368
369fn qux(bar: Bar, baz: Baz) {
370 f();
371}
372",
373 )
374 }
375
376 #[test]
377 fn expanding_glob_import_with_existing_uses_in_same_module() {
378 check_assist(
379 expand_glob_import,
380 r"
381mod foo {
382 pub struct Bar;
383 pub struct Baz;
384 pub struct Qux;
385
386 pub fn f() {}
387}
388
389use foo::Bar;
390use foo::{*<|>, f};
391
392fn qux(bar: Bar, baz: Baz) {
393 f();
394}
395",
396 r"
397mod foo {
398 pub struct Bar;
399 pub struct Baz;
400 pub struct Qux;
401
402 pub fn f() {}
403}
404
405use foo::Bar;
406use foo::{f, Baz};
249 407
250fn qux(bar: Bar, baz: Baz) { 408fn qux(bar: Bar, baz: Baz) {
251 f(); 409 f();
@@ -260,7 +418,7 @@ fn qux(bar: Bar, baz: Baz) {
260 expand_glob_import, 418 expand_glob_import,
261 r" 419 r"
262mod foo { 420mod foo {
263 mod bar { 421 pub mod bar {
264 pub struct Bar; 422 pub struct Bar;
265 pub struct Baz; 423 pub struct Baz;
266 pub struct Qux; 424 pub struct Qux;
@@ -268,7 +426,7 @@ mod foo {
268 pub fn f() {} 426 pub fn f() {}
269 } 427 }
270 428
271 mod baz { 429 pub mod baz {
272 pub fn g() {} 430 pub fn g() {}
273 } 431 }
274} 432}
@@ -282,7 +440,7 @@ fn qux(bar: Bar, baz: Baz) {
282", 440",
283 r" 441 r"
284mod foo { 442mod foo {
285 mod bar { 443 pub mod bar {
286 pub struct Bar; 444 pub struct Bar;
287 pub struct Baz; 445 pub struct Baz;
288 pub struct Qux; 446 pub struct Qux;
@@ -290,55 +448,359 @@ mod foo {
290 pub fn f() {} 448 pub fn f() {}
291 } 449 }
292 450
293 mod baz { 451 pub mod baz {
294 pub fn g() {} 452 pub fn g() {}
295 } 453 }
296} 454}
297 455
298use foo::{bar::{Baz, Bar, f}, baz::*}; 456use foo::{bar::{f, Baz, Bar}, baz::*};
299 457
300fn qux(bar: Bar, baz: Baz) { 458fn qux(bar: Bar, baz: Baz) {
301 f(); 459 f();
302 g(); 460 g();
303} 461}
304", 462",
305 ) 463 );
464
465 check_assist(
466 expand_glob_import,
467 r"
468mod foo {
469 pub mod bar {
470 pub struct Bar;
471 pub struct Baz;
472 pub struct Qux;
473
474 pub fn f() {}
475 }
476
477 pub mod baz {
478 pub fn g() {}
479 }
480}
481
482use foo::{bar::{Bar, Baz, f}, baz::*<|>};
483
484fn qux(bar: Bar, baz: Baz) {
485 f();
486 g();
487}
488",
489 r"
490mod foo {
491 pub mod bar {
492 pub struct Bar;
493 pub struct Baz;
494 pub struct Qux;
495
496 pub fn f() {}
497 }
498
499 pub mod baz {
500 pub fn g() {}
501 }
502}
503
504use foo::{bar::{Bar, Baz, f}, baz::g};
505
506fn qux(bar: Bar, baz: Baz) {
507 f();
508 g();
509}
510",
511 );
512
513 check_assist(
514 expand_glob_import,
515 r"
516mod foo {
517 pub mod bar {
518 pub struct Bar;
519 pub struct Baz;
520 pub struct Qux;
521
522 pub fn f() {}
523 }
524
525 pub mod baz {
526 pub fn g() {}
527
528 pub mod qux {
529 pub fn h() {}
530 pub fn m() {}
531
532 pub mod q {
533 pub fn j() {}
534 }
535 }
536 }
537}
538
539use foo::{
540 bar::{*, f},
541 baz::{g, qux::*<|>}
542};
543
544fn qux(bar: Bar, baz: Baz) {
545 f();
546 g();
547 h();
548 q::j();
549}
550",
551 r"
552mod foo {
553 pub mod bar {
554 pub struct Bar;
555 pub struct Baz;
556 pub struct Qux;
557
558 pub fn f() {}
559 }
560
561 pub mod baz {
562 pub fn g() {}
563
564 pub mod qux {
565 pub fn h() {}
566 pub fn m() {}
567
568 pub mod q {
569 pub fn j() {}
570 }
571 }
572 }
573}
574
575use foo::{
576 bar::{*, f},
577 baz::{g, qux::{q, h}}
578};
579
580fn qux(bar: Bar, baz: Baz) {
581 f();
582 g();
583 h();
584 q::j();
585}
586",
587 );
588
589 check_assist(
590 expand_glob_import,
591 r"
592mod foo {
593 pub mod bar {
594 pub struct Bar;
595 pub struct Baz;
596 pub struct Qux;
597
598 pub fn f() {}
599 }
600
601 pub mod baz {
602 pub fn g() {}
603
604 pub mod qux {
605 pub fn h() {}
606 pub fn m() {}
607
608 pub mod q {
609 pub fn j() {}
610 }
611 }
612 }
613}
614
615use foo::{
616 bar::{*, f},
617 baz::{g, qux::{h, q::*<|>}}
618};
619
620fn qux(bar: Bar, baz: Baz) {
621 f();
622 g();
623 h();
624 j();
625}
626",
627 r"
628mod foo {
629 pub mod bar {
630 pub struct Bar;
631 pub struct Baz;
632 pub struct Qux;
633
634 pub fn f() {}
635 }
636
637 pub mod baz {
638 pub fn g() {}
639
640 pub mod qux {
641 pub fn h() {}
642 pub fn m() {}
643
644 pub mod q {
645 pub fn j() {}
646 }
647 }
648 }
649}
650
651use foo::{
652 bar::{*, f},
653 baz::{g, qux::{h, q::j}}
654};
655
656fn qux(bar: Bar, baz: Baz) {
657 f();
658 g();
659 h();
660 j();
661}
662",
663 );
664
665 check_assist(
666 expand_glob_import,
667 r"
668mod foo {
669 pub mod bar {
670 pub struct Bar;
671 pub struct Baz;
672 pub struct Qux;
673
674 pub fn f() {}
675 }
676
677 pub mod baz {
678 pub fn g() {}
679
680 pub mod qux {
681 pub fn h() {}
682 pub fn m() {}
683
684 pub mod q {
685 pub fn j() {}
686 }
687 }
688 }
689}
690
691use foo::{
692 bar::{*, f},
693 baz::{g, qux::{q::j, *<|>}}
694};
695
696fn qux(bar: Bar, baz: Baz) {
697 f();
698 g();
699 h();
700 j();
701}
702",
703 r"
704mod foo {
705 pub mod bar {
706 pub struct Bar;
707 pub struct Baz;
708 pub struct Qux;
709
710 pub fn f() {}
711 }
712
713 pub mod baz {
714 pub fn g() {}
715
716 pub mod qux {
717 pub fn h() {}
718 pub fn m() {}
719
720 pub mod q {
721 pub fn j() {}
722 }
723 }
724 }
725}
726
727use foo::{
728 bar::{*, f},
729 baz::{g, qux::{q::j, h}}
730};
731
732fn qux(bar: Bar, baz: Baz) {
733 f();
734 g();
735 h();
736 j();
737}
738",
739 );
306 } 740 }
307 741
308 #[test] 742 #[test]
309 fn expanding_glob_import_with_macro_defs() { 743 fn expanding_glob_import_with_macro_defs() {
744 // FIXME: this is currently fails because `Definition::find_usages` ignores macros
745 // https://github.com/rust-analyzer/rust-analyzer/issues/3484
746 //
747 // check_assist(
748 // expand_glob_import,
749 // r"
750 // //- /lib.rs crate:foo
751 // #[macro_export]
752 // macro_rules! bar {
753 // () => ()
754 // }
755
756 // pub fn baz() {}
757
758 // //- /main.rs crate:main deps:foo
759 // use foo::*<|>;
760
761 // fn main() {
762 // bar!();
763 // baz();
764 // }
765 // ",
766 // r"
767 // use foo::{bar, baz};
768
769 // fn main() {
770 // bar!();
771 // baz();
772 // }
773 // ",
774 // )
775 }
776
777 #[test]
778 fn expanding_glob_import_with_trait_method_uses() {
310 check_assist( 779 check_assist(
311 expand_glob_import, 780 expand_glob_import,
312 r" 781 r"
313//- /lib.rs crate:foo 782//- /lib.rs crate:foo
314#[macro_export] 783pub trait Tr {
315macro_rules! bar { 784 fn method(&self) {}
316 () => ()
317} 785}
318 786impl Tr for () {}
319pub fn baz() {}
320 787
321//- /main.rs crate:main deps:foo 788//- /main.rs crate:main deps:foo
322use foo::*<|>; 789use foo::*<|>;
323 790
324fn main() { 791fn main() {
325 bar!(); 792 ().method();
326 baz();
327} 793}
328", 794",
329 r" 795 r"
330use foo::{bar, baz}; 796use foo::Tr;
331 797
332fn main() { 798fn main() {
333 bar!(); 799 ().method();
334 baz();
335} 800}
336", 801",
337 ) 802 );
338 }
339 803
340 #[test]
341 fn expanding_glob_import_with_trait_method_uses() {
342 check_assist( 804 check_assist(
343 expand_glob_import, 805 expand_glob_import,
344 r" 806 r"
@@ -348,6 +810,11 @@ pub trait Tr {
348} 810}
349impl Tr for () {} 811impl Tr for () {}
350 812
813pub trait Tr2 {
814 fn method2(&self) {}
815}
816impl Tr2 for () {}
817
351//- /main.rs crate:main deps:foo 818//- /main.rs crate:main deps:foo
352use foo::*<|>; 819use foo::*<|>;
353 820
@@ -362,7 +829,42 @@ fn main() {
362 ().method(); 829 ().method();
363} 830}
364", 831",
365 ) 832 );
833 }
834
835 #[test]
836 fn expanding_is_not_applicable_if_target_module_is_not_accessible_from_current_scope() {
837 check_assist_not_applicable(
838 expand_glob_import,
839 r"
840mod foo {
841 mod bar {
842 pub struct Bar;
843 }
844}
845
846use foo::bar::*<|>;
847
848fn baz(bar: Bar) {}
849",
850 );
851
852 check_assist_not_applicable(
853 expand_glob_import,
854 r"
855mod foo {
856 mod bar {
857 pub mod baz {
858 pub struct Baz;
859 }
860 }
861}
862
863use foo::bar::baz::*<|>;
864
865fn qux(baz: Baz) {}
866",
867 );
366 } 868 }
367 869
368 #[test] 870 #[test]
diff --git a/crates/assists/src/handlers/invert_if.rs b/crates/assists/src/handlers/invert_if.rs
index f0e047538..294256297 100644
--- a/crates/assists/src/handlers/invert_if.rs
+++ b/crates/assists/src/handlers/invert_if.rs
@@ -106,4 +106,22 @@ mod tests {
106 "fn f() { i<|>f let Some(_) = Some(1) { 1 } else { 0 } }", 106 "fn f() { i<|>f let Some(_) = Some(1) { 1 } else { 0 } }",
107 ) 107 )
108 } 108 }
109
110 #[test]
111 fn invert_if_option_case() {
112 check_assist(
113 invert_if,
114 "fn f() { if<|> doc_style.is_some() { Class::DocComment } else { Class::Comment } }",
115 "fn f() { if doc_style.is_none() { Class::Comment } else { Class::DocComment } }",
116 )
117 }
118
119 #[test]
120 fn invert_if_result_case() {
121 check_assist(
122 invert_if,
123 "fn f() { i<|>f doc_style.is_err() { Class::Err } else { Class::Ok } }",
124 "fn f() { if doc_style.is_ok() { Class::Ok } else { Class::Err } }",
125 )
126 }
109} 127}
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index d071d6502..e15c982e7 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -11,7 +11,7 @@ use syntax::{
11 ast::{self, make, NameOwner}, 11 ast::{self, make, NameOwner},
12 AstNode, Direction, 12 AstNode, Direction,
13 SyntaxKind::*, 13 SyntaxKind::*,
14 SyntaxNode, TextSize, T, 14 SyntaxNode, SyntaxText, TextSize, T,
15}; 15};
16 16
17use crate::assist_config::SnippetCap; 17use crate::assist_config::SnippetCap;
@@ -179,6 +179,25 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
179 ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()), 179 ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()),
180 _ => None, 180 _ => None,
181 }, 181 },
182 ast::Expr::MethodCallExpr(mce) => {
183 const IS_SOME_TEXT: &str = "is_some";
184 const IS_NONE_TEXT: &str = "is_none";
185 const IS_OK_TEXT: &str = "is_ok";
186 const IS_ERR_TEXT: &str = "is_err";
187
188 let name = mce.name_ref()?;
189 let name_text = name.text();
190
191 let caller = || -> Option<SyntaxText> { Some(mce.receiver()?.syntax().text()) };
192
193 match name_text {
194 x if x == IS_SOME_TEXT => make::expr_method_call(IS_NONE_TEXT, caller),
195 x if x == IS_NONE_TEXT => make::expr_method_call(IS_SOME_TEXT, caller),
196 x if x == IS_OK_TEXT => make::expr_method_call(IS_ERR_TEXT, caller),
197 x if x == IS_ERR_TEXT => make::expr_method_call(IS_OK_TEXT, caller),
198 _ => None,
199 }
200 }
182 ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::PrefixOp::Not => pe.expr(), 201 ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::PrefixOp::Not => pe.expr(),
183 // FIXME: 202 // FIXME:
184 // ast::Expr::Literal(true | false ) 203 // ast::Expr::Literal(true | false )