diff options
Diffstat (limited to 'crates/assists')
-rw-r--r-- | crates/assists/Cargo.toml | 17 | ||||
-rw-r--r-- | crates/assists/src/assist_context.rs | 4 | ||||
-rw-r--r-- | crates/assists/src/handlers/expand_glob_import.rs | 732 | ||||
-rw-r--r-- | crates/assists/src/handlers/invert_if.rs | 18 | ||||
-rw-r--r-- | crates/assists/src/utils.rs | 21 |
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] |
2 | name = "assists" | 2 | name = "assists" |
3 | version = "0.0.0" | 3 | version = "0.0.0" |
4 | description = "TBD" | ||
4 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
5 | authors = ["rust-analyzer developers"] | 6 | authors = ["rust-analyzer developers"] |
6 | edition = "2018" | 7 | edition = "2018" |
@@ -13,11 +14,11 @@ rustc-hash = "1.1.0" | |||
13 | itertools = "0.9.0" | 14 | itertools = "0.9.0" |
14 | either = "1.5.3" | 15 | either = "1.5.3" |
15 | 16 | ||
16 | stdx = { path = "../stdx" } | 17 | stdx = { path = "../stdx", version = "0.0.0" } |
17 | syntax = { path = "../syntax" } | 18 | syntax = { path = "../syntax", version = "0.0.0" } |
18 | text_edit = { path = "../text_edit" } | 19 | text_edit = { path = "../text_edit", version = "0.0.0" } |
19 | profile = { path = "../profile" } | 20 | profile = { path = "../profile", version = "0.0.0" } |
20 | base_db = { path = "../base_db" } | 21 | base_db = { path = "../base_db", version = "0.0.0" } |
21 | ide_db = { path = "../ide_db" } | 22 | ide_db = { path = "../ide_db", version = "0.0.0" } |
22 | hir = { path = "../hir" } | 23 | hir = { path = "../hir", version = "0.0.0" } |
23 | test_utils = { path = "../test_utils" } | 24 | test_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 @@ | |||
1 | use either::Either; | 1 | use either::Either; |
2 | use hir::{AssocItem, MacroDef, ModuleDef, Name, PathResolution, ScopeDef, SemanticsScope}; | 2 | use hir::{AssocItem, MacroDef, Module, ModuleDef, Name, PathResolution, ScopeDef}; |
3 | use ide_db::{ | 3 | use ide_db::{ |
4 | defs::{classify_name_ref, Definition, NameRefClass}, | 4 | defs::{classify_name_ref, Definition, NameRefClass}, |
5 | RootDatabase, | 5 | search::SearchScope, |
6 | }; | 6 | }; |
7 | use syntax::{algo, ast, match_ast, AstNode, SyntaxNode, SyntaxToken, T}; | 7 | use syntax::{algo, ast, AstNode, Direction, SyntaxNode, SyntaxToken, T}; |
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | assist_context::{AssistBuilder, AssistContext, Assists}, | 10 | assist_context::{AssistBuilder, AssistContext, Assists}, |
@@ -38,140 +38,259 @@ use crate::{ | |||
38 | // ``` | 38 | // ``` |
39 | pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 39 | pub(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 | ||
66 | fn find_mod_path(star: &SyntaxToken) -> Option<ast::Path> { | 65 | fn 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)] |
71 | enum Def { | 88 | enum Def { |
72 | ModuleDef(ModuleDef), | 89 | ModuleDef(ModuleDef), |
73 | MacroDef(MacroDef), | 90 | MacroDef(MacroDef), |
74 | } | 91 | } |
75 | 92 | ||
76 | impl Def { | 93 | impl 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)] | ||
106 | struct Ref { | ||
107 | // could be alias | ||
108 | visible_name: Name, | ||
109 | def: Def, | ||
110 | } | ||
111 | |||
112 | impl 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 | ||
85 | fn find_defs_in_mod( | 122 | #[derive(Debug, Clone)] |
123 | struct Refs(Vec<Ref>); | ||
124 | |||
125 | impl 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 | |||
160 | fn 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 | ||
104 | fn find_used_names( | 176 | fn 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 | // ↑ --------------- | ||
201 | fn 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) | 225 | fn 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 | ||
138 | fn replace_ast( | 234 | fn 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 | ||
248 | use foo::{Baz, Bar, f}; | 367 | use foo::{f, Baz, Bar}; |
368 | |||
369 | fn 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" | ||
381 | mod foo { | ||
382 | pub struct Bar; | ||
383 | pub struct Baz; | ||
384 | pub struct Qux; | ||
385 | |||
386 | pub fn f() {} | ||
387 | } | ||
388 | |||
389 | use foo::Bar; | ||
390 | use foo::{*<|>, f}; | ||
391 | |||
392 | fn qux(bar: Bar, baz: Baz) { | ||
393 | f(); | ||
394 | } | ||
395 | ", | ||
396 | r" | ||
397 | mod foo { | ||
398 | pub struct Bar; | ||
399 | pub struct Baz; | ||
400 | pub struct Qux; | ||
401 | |||
402 | pub fn f() {} | ||
403 | } | ||
404 | |||
405 | use foo::Bar; | ||
406 | use foo::{f, Baz}; | ||
249 | 407 | ||
250 | fn qux(bar: Bar, baz: Baz) { | 408 | fn 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" |
262 | mod foo { | 420 | mod 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" |
284 | mod foo { | 442 | mod 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 | ||
298 | use foo::{bar::{Baz, Bar, f}, baz::*}; | 456 | use foo::{bar::{f, Baz, Bar}, baz::*}; |
299 | 457 | ||
300 | fn qux(bar: Bar, baz: Baz) { | 458 | fn 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" | ||
468 | mod 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 | |||
482 | use foo::{bar::{Bar, Baz, f}, baz::*<|>}; | ||
483 | |||
484 | fn qux(bar: Bar, baz: Baz) { | ||
485 | f(); | ||
486 | g(); | ||
487 | } | ||
488 | ", | ||
489 | r" | ||
490 | mod 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 | |||
504 | use foo::{bar::{Bar, Baz, f}, baz::g}; | ||
505 | |||
506 | fn qux(bar: Bar, baz: Baz) { | ||
507 | f(); | ||
508 | g(); | ||
509 | } | ||
510 | ", | ||
511 | ); | ||
512 | |||
513 | check_assist( | ||
514 | expand_glob_import, | ||
515 | r" | ||
516 | mod 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 | |||
539 | use foo::{ | ||
540 | bar::{*, f}, | ||
541 | baz::{g, qux::*<|>} | ||
542 | }; | ||
543 | |||
544 | fn qux(bar: Bar, baz: Baz) { | ||
545 | f(); | ||
546 | g(); | ||
547 | h(); | ||
548 | q::j(); | ||
549 | } | ||
550 | ", | ||
551 | r" | ||
552 | mod 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 | |||
575 | use foo::{ | ||
576 | bar::{*, f}, | ||
577 | baz::{g, qux::{q, h}} | ||
578 | }; | ||
579 | |||
580 | fn 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" | ||
592 | mod 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 | |||
615 | use foo::{ | ||
616 | bar::{*, f}, | ||
617 | baz::{g, qux::{h, q::*<|>}} | ||
618 | }; | ||
619 | |||
620 | fn qux(bar: Bar, baz: Baz) { | ||
621 | f(); | ||
622 | g(); | ||
623 | h(); | ||
624 | j(); | ||
625 | } | ||
626 | ", | ||
627 | r" | ||
628 | mod 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 | |||
651 | use foo::{ | ||
652 | bar::{*, f}, | ||
653 | baz::{g, qux::{h, q::j}} | ||
654 | }; | ||
655 | |||
656 | fn 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" | ||
668 | mod 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 | |||
691 | use foo::{ | ||
692 | bar::{*, f}, | ||
693 | baz::{g, qux::{q::j, *<|>}} | ||
694 | }; | ||
695 | |||
696 | fn qux(bar: Bar, baz: Baz) { | ||
697 | f(); | ||
698 | g(); | ||
699 | h(); | ||
700 | j(); | ||
701 | } | ||
702 | ", | ||
703 | r" | ||
704 | mod 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 | |||
727 | use foo::{ | ||
728 | bar::{*, f}, | ||
729 | baz::{g, qux::{q::j, h}} | ||
730 | }; | ||
731 | |||
732 | fn 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] | 783 | pub trait Tr { |
315 | macro_rules! bar { | 784 | fn method(&self) {} |
316 | () => () | ||
317 | } | 785 | } |
318 | 786 | impl Tr for () {} | |
319 | pub fn baz() {} | ||
320 | 787 | ||
321 | //- /main.rs crate:main deps:foo | 788 | //- /main.rs crate:main deps:foo |
322 | use foo::*<|>; | 789 | use foo::*<|>; |
323 | 790 | ||
324 | fn main() { | 791 | fn main() { |
325 | bar!(); | 792 | ().method(); |
326 | baz(); | ||
327 | } | 793 | } |
328 | ", | 794 | ", |
329 | r" | 795 | r" |
330 | use foo::{bar, baz}; | 796 | use foo::Tr; |
331 | 797 | ||
332 | fn main() { | 798 | fn 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 | } |
349 | impl Tr for () {} | 811 | impl Tr for () {} |
350 | 812 | ||
813 | pub trait Tr2 { | ||
814 | fn method2(&self) {} | ||
815 | } | ||
816 | impl Tr2 for () {} | ||
817 | |||
351 | //- /main.rs crate:main deps:foo | 818 | //- /main.rs crate:main deps:foo |
352 | use foo::*<|>; | 819 | use 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" | ||
840 | mod foo { | ||
841 | mod bar { | ||
842 | pub struct Bar; | ||
843 | } | ||
844 | } | ||
845 | |||
846 | use foo::bar::*<|>; | ||
847 | |||
848 | fn baz(bar: Bar) {} | ||
849 | ", | ||
850 | ); | ||
851 | |||
852 | check_assist_not_applicable( | ||
853 | expand_glob_import, | ||
854 | r" | ||
855 | mod foo { | ||
856 | mod bar { | ||
857 | pub mod baz { | ||
858 | pub struct Baz; | ||
859 | } | ||
860 | } | ||
861 | } | ||
862 | |||
863 | use foo::bar::baz::*<|>; | ||
864 | |||
865 | fn 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 | ||
17 | use crate::assist_config::SnippetCap; | 17 | use 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 ) |