aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists/src')
-rw-r--r--crates/assists/src/assist_config.rs16
-rw-r--r--crates/assists/src/assist_context.rs253
-rw-r--r--crates/assists/src/ast_transform.rs213
-rw-r--r--crates/assists/src/handlers/add_explicit_type.rs207
-rw-r--r--crates/assists/src/handlers/add_missing_impl_members.rs814
-rw-r--r--crates/assists/src/handlers/add_turbo_fish.rs164
-rw-r--r--crates/assists/src/handlers/apply_demorgan.rs93
-rw-r--r--crates/assists/src/handlers/auto_import.rs965
-rw-r--r--crates/assists/src/handlers/change_visibility.rs212
-rw-r--r--crates/assists/src/handlers/convert_integer_literal.rs268
-rw-r--r--crates/assists/src/handlers/early_return.rs515
-rw-r--r--crates/assists/src/handlers/expand_glob_import.rs907
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs520
-rw-r--r--crates/assists/src/handlers/extract_variable.rs588
-rw-r--r--crates/assists/src/handlers/fill_match_arms.rs746
-rw-r--r--crates/assists/src/handlers/fix_visibility.rs607
-rw-r--r--crates/assists/src/handlers/flip_binexpr.rs134
-rw-r--r--crates/assists/src/handlers/flip_comma.rs84
-rw-r--r--crates/assists/src/handlers/flip_trait_bound.rs121
-rw-r--r--crates/assists/src/handlers/generate_default_from_enum_variant.rs175
-rw-r--r--crates/assists/src/handlers/generate_derive.rs132
-rw-r--r--crates/assists/src/handlers/generate_from_impl_for_enum.rs201
-rw-r--r--crates/assists/src/handlers/generate_function.rs1059
-rw-r--r--crates/assists/src/handlers/generate_impl.rs148
-rw-r--r--crates/assists/src/handlers/generate_new.rs421
-rw-r--r--crates/assists/src/handlers/infer_function_return_type.rs345
-rw-r--r--crates/assists/src/handlers/inline_function.rs202
-rw-r--r--crates/assists/src/handlers/inline_local_variable.rs724
-rw-r--r--crates/assists/src/handlers/introduce_named_lifetime.rs315
-rw-r--r--crates/assists/src/handlers/invert_if.rs146
-rw-r--r--crates/assists/src/handlers/merge_imports.rs343
-rw-r--r--crates/assists/src/handlers/merge_match_arms.rs248
-rw-r--r--crates/assists/src/handlers/move_bounds.rs152
-rw-r--r--crates/assists/src/handlers/move_guard.rs367
-rw-r--r--crates/assists/src/handlers/move_module_to_file.rs145
-rw-r--r--crates/assists/src/handlers/pull_assignment_up.rs400
-rw-r--r--crates/assists/src/handlers/qualify_path.rs1212
-rw-r--r--crates/assists/src/handlers/raw_string.rs512
-rw-r--r--crates/assists/src/handlers/remove_dbg.rs421
-rw-r--r--crates/assists/src/handlers/remove_mut.rs37
-rw-r--r--crates/assists/src/handlers/remove_unused_param.rs288
-rw-r--r--crates/assists/src/handlers/reorder_fields.rs227
-rw-r--r--crates/assists/src/handlers/reorder_impl.rs201
-rw-r--r--crates/assists/src/handlers/replace_derive_with_manual_impl.rs401
-rw-r--r--crates/assists/src/handlers/replace_if_let_with_match.rs530
-rw-r--r--crates/assists/src/handlers/replace_impl_trait_with_generic.rs168
-rw-r--r--crates/assists/src/handlers/replace_let_with_if_let.rs101
-rw-r--r--crates/assists/src/handlers/replace_qualified_name_with_use.rs678
-rw-r--r--crates/assists/src/handlers/replace_string_with_char.rs137
-rw-r--r--crates/assists/src/handlers/replace_unwrap_with_match.rs188
-rw-r--r--crates/assists/src/handlers/split_import.rs79
-rw-r--r--crates/assists/src/handlers/toggle_ignore.rs98
-rw-r--r--crates/assists/src/handlers/unmerge_use.rs231
-rw-r--r--crates/assists/src/handlers/unwrap_block.rs582
-rw-r--r--crates/assists/src/handlers/wrap_return_type_in_result.rs1158
-rw-r--r--crates/assists/src/lib.rs230
-rw-r--r--crates/assists/src/tests.rs236
-rw-r--r--crates/assists/src/tests/generated.rs1184
-rw-r--r--crates/assists/src/utils.rs250
59 files changed, 0 insertions, 22099 deletions
diff --git a/crates/assists/src/assist_config.rs b/crates/assists/src/assist_config.rs
deleted file mode 100644
index 9cabf037c..000000000
--- a/crates/assists/src/assist_config.rs
+++ /dev/null
@@ -1,16 +0,0 @@
1//! Settings for tweaking assists.
2//!
3//! The fun thing here is `SnippetCap` -- this type can only be created in this
4//! module, and we use to statically check that we only produce snippet
5//! assists if we are allowed to.
6
7use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap};
8
9use crate::AssistKind;
10
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct AssistConfig {
13 pub snippet_cap: Option<SnippetCap>,
14 pub allowed: Option<Vec<AssistKind>>,
15 pub insert_use: InsertUseConfig,
16}
diff --git a/crates/assists/src/assist_context.rs b/crates/assists/src/assist_context.rs
deleted file mode 100644
index 8d93edba2..000000000
--- a/crates/assists/src/assist_context.rs
+++ /dev/null
@@ -1,253 +0,0 @@
1//! See `AssistContext`
2
3use std::mem;
4
5use hir::Semantics;
6use ide_db::{
7 base_db::{AnchoredPathBuf, FileId, FileRange},
8 helpers::SnippetCap,
9};
10use ide_db::{
11 label::Label,
12 source_change::{FileSystemEdit, SourceChange},
13 RootDatabase,
14};
15use syntax::{
16 algo::{self, find_node_at_offset, SyntaxRewriter},
17 AstNode, AstToken, SourceFile, SyntaxElement, SyntaxKind, SyntaxToken, TextRange, TextSize,
18 TokenAtOffset,
19};
20use text_edit::{TextEdit, TextEditBuilder};
21
22use crate::{assist_config::AssistConfig, Assist, AssistId, AssistKind, GroupLabel};
23
24/// `AssistContext` allows to apply an assist or check if it could be applied.
25///
26/// Assists use a somewhat over-engineered approach, given the current needs.
27/// The assists workflow consists of two phases. In the first phase, a user asks
28/// for the list of available assists. In the second phase, the user picks a
29/// particular assist and it gets applied.
30///
31/// There are two peculiarities here:
32///
33/// * first, we ideally avoid computing more things then necessary to answer "is
34/// assist applicable" in the first phase.
35/// * second, when we are applying assist, we don't have a guarantee that there
36/// weren't any changes between the point when user asked for assists and when
37/// they applied a particular assist. So, when applying assist, we need to do
38/// all the checks from scratch.
39///
40/// To avoid repeating the same code twice for both "check" and "apply"
41/// functions, we use an approach reminiscent of that of Django's function based
42/// views dealing with forms. Each assist receives a runtime parameter,
43/// `resolve`. It first check if an edit is applicable (potentially computing
44/// info required to compute the actual edit). If it is applicable, and
45/// `resolve` is `true`, it then computes the actual edit.
46///
47/// So, to implement the original assists workflow, we can first apply each edit
48/// with `resolve = false`, and then applying the selected edit again, with
49/// `resolve = true` this time.
50///
51/// Note, however, that we don't actually use such two-phase logic at the
52/// moment, because the LSP API is pretty awkward in this place, and it's much
53/// easier to just compute the edit eagerly :-)
54pub(crate) struct AssistContext<'a> {
55 pub(crate) config: &'a AssistConfig,
56 pub(crate) sema: Semantics<'a, RootDatabase>,
57 pub(crate) frange: FileRange,
58 source_file: SourceFile,
59}
60
61impl<'a> AssistContext<'a> {
62 pub(crate) fn new(
63 sema: Semantics<'a, RootDatabase>,
64 config: &'a AssistConfig,
65 frange: FileRange,
66 ) -> AssistContext<'a> {
67 let source_file = sema.parse(frange.file_id);
68 AssistContext { config, sema, frange, source_file }
69 }
70
71 pub(crate) fn db(&self) -> &RootDatabase {
72 self.sema.db
73 }
74
75 // NB, this ignores active selection.
76 pub(crate) fn offset(&self) -> TextSize {
77 self.frange.range.start()
78 }
79
80 pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
81 self.source_file.syntax().token_at_offset(self.offset())
82 }
83 pub(crate) fn find_token_syntax_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToken> {
84 self.token_at_offset().find(|it| it.kind() == kind)
85 }
86 pub(crate) fn find_token_at_offset<T: AstToken>(&self) -> Option<T> {
87 self.token_at_offset().find_map(T::cast)
88 }
89 pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> {
90 find_node_at_offset(self.source_file.syntax(), self.offset())
91 }
92 pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> {
93 self.sema.find_node_at_offset_with_descend(self.source_file.syntax(), self.offset())
94 }
95 pub(crate) fn covering_element(&self) -> SyntaxElement {
96 self.source_file.syntax().covering_element(self.frange.range)
97 }
98 // FIXME: remove
99 pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement {
100 self.source_file.syntax().covering_element(range)
101 }
102}
103
104pub(crate) struct Assists {
105 resolve: bool,
106 file: FileId,
107 buf: Vec<Assist>,
108 allowed: Option<Vec<AssistKind>>,
109}
110
111impl Assists {
112 pub(crate) fn new(ctx: &AssistContext, resolve: bool) -> Assists {
113 Assists {
114 resolve,
115 file: ctx.frange.file_id,
116 buf: Vec::new(),
117 allowed: ctx.config.allowed.clone(),
118 }
119 }
120
121 pub(crate) fn finish(mut self) -> Vec<Assist> {
122 self.buf.sort_by_key(|assist| assist.target.len());
123 self.buf
124 }
125
126 pub(crate) fn add(
127 &mut self,
128 id: AssistId,
129 label: impl Into<String>,
130 target: TextRange,
131 f: impl FnOnce(&mut AssistBuilder),
132 ) -> Option<()> {
133 if !self.is_allowed(&id) {
134 return None;
135 }
136 let label = Label::new(label.into());
137 let assist = Assist { id, label, group: None, target, source_change: None };
138 self.add_impl(assist, f)
139 }
140
141 pub(crate) fn add_group(
142 &mut self,
143 group: &GroupLabel,
144 id: AssistId,
145 label: impl Into<String>,
146 target: TextRange,
147 f: impl FnOnce(&mut AssistBuilder),
148 ) -> Option<()> {
149 if !self.is_allowed(&id) {
150 return None;
151 }
152 let label = Label::new(label.into());
153 let assist = Assist { id, label, group: Some(group.clone()), target, source_change: None };
154 self.add_impl(assist, f)
155 }
156
157 fn add_impl(&mut self, mut assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
158 let source_change = if self.resolve {
159 let mut builder = AssistBuilder::new(self.file);
160 f(&mut builder);
161 Some(builder.finish())
162 } else {
163 None
164 };
165 assist.source_change = source_change.clone();
166
167 self.buf.push(assist);
168 Some(())
169 }
170
171 fn is_allowed(&self, id: &AssistId) -> bool {
172 match &self.allowed {
173 Some(allowed) => allowed.iter().any(|kind| kind.contains(id.1)),
174 None => true,
175 }
176 }
177}
178
179pub(crate) struct AssistBuilder {
180 edit: TextEditBuilder,
181 file_id: FileId,
182 source_change: SourceChange,
183}
184
185impl AssistBuilder {
186 pub(crate) fn new(file_id: FileId) -> AssistBuilder {
187 AssistBuilder { edit: TextEdit::builder(), file_id, source_change: SourceChange::default() }
188 }
189
190 pub(crate) fn edit_file(&mut self, file_id: FileId) {
191 self.commit();
192 self.file_id = file_id;
193 }
194
195 fn commit(&mut self) {
196 let edit = mem::take(&mut self.edit).finish();
197 if !edit.is_empty() {
198 self.source_change.insert_source_edit(self.file_id, edit);
199 }
200 }
201
202 /// Remove specified `range` of text.
203 pub(crate) fn delete(&mut self, range: TextRange) {
204 self.edit.delete(range)
205 }
206 /// Append specified `text` at the given `offset`
207 pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) {
208 self.edit.insert(offset, text.into())
209 }
210 /// Append specified `snippet` at the given `offset`
211 pub(crate) fn insert_snippet(
212 &mut self,
213 _cap: SnippetCap,
214 offset: TextSize,
215 snippet: impl Into<String>,
216 ) {
217 self.source_change.is_snippet = true;
218 self.insert(offset, snippet);
219 }
220 /// Replaces specified `range` of text with a given string.
221 pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
222 self.edit.replace(range, replace_with.into())
223 }
224 /// Replaces specified `range` of text with a given `snippet`.
225 pub(crate) fn replace_snippet(
226 &mut self,
227 _cap: SnippetCap,
228 range: TextRange,
229 snippet: impl Into<String>,
230 ) {
231 self.source_change.is_snippet = true;
232 self.replace(range, snippet);
233 }
234 pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
235 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
236 }
237 pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
238 if let Some(node) = rewriter.rewrite_root() {
239 let new = rewriter.rewrite(&node);
240 algo::diff(&node, &new).into_text_edit(&mut self.edit);
241 }
242 }
243 pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) {
244 let file_system_edit =
245 FileSystemEdit::CreateFile { dst: dst.clone(), initial_contents: content.into() };
246 self.source_change.push_file_system_edit(file_system_edit);
247 }
248
249 fn finish(mut self) -> SourceChange {
250 self.commit();
251 mem::take(&mut self.source_change)
252 }
253}
diff --git a/crates/assists/src/ast_transform.rs b/crates/assists/src/ast_transform.rs
deleted file mode 100644
index 4a3ed7783..000000000
--- a/crates/assists/src/ast_transform.rs
+++ /dev/null
@@ -1,213 +0,0 @@
1//! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined.
2use hir::{HirDisplay, PathResolution, SemanticsScope};
3use ide_db::helpers::mod_path_to_ast;
4use rustc_hash::FxHashMap;
5use syntax::{
6 algo::SyntaxRewriter,
7 ast::{self, AstNode},
8 SyntaxNode,
9};
10
11pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
12 SyntaxRewriter::from_fn(|element| match element {
13 syntax::SyntaxElement::Node(n) => {
14 let replacement = transformer.get_substitution(&n, transformer)?;
15 Some(replacement.into())
16 }
17 _ => None,
18 })
19 .rewrite_ast(&node)
20}
21
22/// `AstTransform` helps with applying bulk transformations to syntax nodes.
23///
24/// This is mostly useful for IDE code generation. If you paste some existing
25/// code into a new context (for example, to add method overrides to an `impl`
26/// block), you generally want to appropriately qualify the names, and sometimes
27/// you might want to substitute generic parameters as well:
28///
29/// ```
30/// mod x {
31/// pub struct A;
32/// pub trait T<U> { fn foo(&self, _: U) -> A; }
33/// }
34///
35/// mod y {
36/// use x::T;
37///
38/// impl T<()> for () {
39/// // If we invoke **Add Missing Members** here, we want to copy-paste `foo`.
40/// // But we want a slightly-modified version of it:
41/// fn foo(&self, _: ()) -> x::A {}
42/// }
43/// }
44/// ```
45///
46/// So, a single `AstTransform` describes such function from `SyntaxNode` to
47/// `SyntaxNode`. Note that the API here is a bit too high-order and high-brow.
48/// We'd want to somehow express this concept simpler, but so far nobody got to
49/// simplifying this!
50pub trait AstTransform<'a> {
51 fn get_substitution(
52 &self,
53 node: &SyntaxNode,
54 recur: &dyn AstTransform<'a>,
55 ) -> Option<SyntaxNode>;
56
57 fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a>
58 where
59 Self: Sized + 'a,
60 {
61 Box::new(Or(Box::new(self), Box::new(other)))
62 }
63}
64
65struct Or<'a>(Box<dyn AstTransform<'a> + 'a>, Box<dyn AstTransform<'a> + 'a>);
66
67impl<'a> AstTransform<'a> for Or<'a> {
68 fn get_substitution(
69 &self,
70 node: &SyntaxNode,
71 recur: &dyn AstTransform<'a>,
72 ) -> Option<SyntaxNode> {
73 self.0.get_substitution(node, recur).or_else(|| self.1.get_substitution(node, recur))
74 }
75}
76
77pub struct SubstituteTypeParams<'a> {
78 source_scope: &'a SemanticsScope<'a>,
79 substs: FxHashMap<hir::TypeParam, ast::Type>,
80}
81
82impl<'a> SubstituteTypeParams<'a> {
83 pub fn for_trait_impl(
84 source_scope: &'a SemanticsScope<'a>,
85 // FIXME: there's implicit invariant that `trait_` and `source_scope` match...
86 trait_: hir::Trait,
87 impl_def: ast::Impl,
88 ) -> SubstituteTypeParams<'a> {
89 let substs = get_syntactic_substs(impl_def).unwrap_or_default();
90 let generic_def: hir::GenericDef = trait_.into();
91 let substs_by_param: FxHashMap<_, _> = generic_def
92 .type_params(source_scope.db)
93 .into_iter()
94 // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky
95 .skip(1)
96 // The actual list of trait type parameters may be longer than the one
97 // used in the `impl` block due to trailing default type parameters.
98 // For that case we extend the `substs` with an empty iterator so we
99 // can still hit those trailing values and check if they actually have
100 // a default type. If they do, go for that type from `hir` to `ast` so
101 // the resulting change can be applied correctly.
102 .zip(substs.into_iter().map(Some).chain(std::iter::repeat(None)))
103 .filter_map(|(k, v)| match v {
104 Some(v) => Some((k, v)),
105 None => {
106 let default = k.default(source_scope.db)?;
107 Some((
108 k,
109 ast::make::ty(
110 &default
111 .display_source_code(source_scope.db, source_scope.module()?.into())
112 .ok()?,
113 ),
114 ))
115 }
116 })
117 .collect();
118 return SubstituteTypeParams { source_scope, substs: substs_by_param };
119
120 // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the
121 // trait ref, and then go from the types in the substs back to the syntax).
122 fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Vec<ast::Type>> {
123 let target_trait = impl_def.trait_()?;
124 let path_type = match target_trait {
125 ast::Type::PathType(path) => path,
126 _ => return None,
127 };
128 let generic_arg_list = path_type.path()?.segment()?.generic_arg_list()?;
129
130 let mut result = Vec::new();
131 for generic_arg in generic_arg_list.generic_args() {
132 match generic_arg {
133 ast::GenericArg::TypeArg(type_arg) => result.push(type_arg.ty()?),
134 ast::GenericArg::AssocTypeArg(_)
135 | ast::GenericArg::LifetimeArg(_)
136 | ast::GenericArg::ConstArg(_) => (),
137 }
138 }
139
140 Some(result)
141 }
142 }
143}
144
145impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> {
146 fn get_substitution(
147 &self,
148 node: &SyntaxNode,
149 _recur: &dyn AstTransform<'a>,
150 ) -> Option<SyntaxNode> {
151 let type_ref = ast::Type::cast(node.clone())?;
152 let path = match &type_ref {
153 ast::Type::PathType(path_type) => path_type.path()?,
154 _ => return None,
155 };
156 let resolution = self.source_scope.speculative_resolve(&path)?;
157 match resolution {
158 hir::PathResolution::TypeParam(tp) => Some(self.substs.get(&tp)?.syntax().clone()),
159 _ => None,
160 }
161 }
162}
163
164pub struct QualifyPaths<'a> {
165 target_scope: &'a SemanticsScope<'a>,
166 source_scope: &'a SemanticsScope<'a>,
167}
168
169impl<'a> QualifyPaths<'a> {
170 pub fn new(target_scope: &'a SemanticsScope<'a>, source_scope: &'a SemanticsScope<'a>) -> Self {
171 Self { target_scope, source_scope }
172 }
173}
174
175impl<'a> AstTransform<'a> for QualifyPaths<'a> {
176 fn get_substitution(
177 &self,
178 node: &SyntaxNode,
179 recur: &dyn AstTransform<'a>,
180 ) -> Option<SyntaxNode> {
181 // FIXME handle value ns?
182 let from = self.target_scope.module()?;
183 let p = ast::Path::cast(node.clone())?;
184 if p.segment().and_then(|s| s.param_list()).is_some() {
185 // don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway
186 return None;
187 }
188 let resolution = self.source_scope.speculative_resolve(&p)?;
189 match resolution {
190 PathResolution::Def(def) => {
191 let found_path = from.find_use_path(self.source_scope.db.upcast(), def)?;
192 let mut path = mod_path_to_ast(&found_path);
193
194 let type_args = p
195 .segment()
196 .and_then(|s| s.generic_arg_list())
197 .map(|arg_list| apply(recur, arg_list));
198 if let Some(type_args) = type_args {
199 let last_segment = path.segment().unwrap();
200 path = path.with_segment(last_segment.with_generic_args(type_args))
201 }
202
203 Some(path.syntax().clone())
204 }
205 PathResolution::Local(_)
206 | PathResolution::TypeParam(_)
207 | PathResolution::SelfType(_)
208 | PathResolution::ConstParam(_) => None,
209 PathResolution::Macro(_) => None,
210 PathResolution::AssocItem(_) => None,
211 }
212 }
213}
diff --git a/crates/assists/src/handlers/add_explicit_type.rs b/crates/assists/src/handlers/add_explicit_type.rs
deleted file mode 100644
index cb1548cef..000000000
--- a/crates/assists/src/handlers/add_explicit_type.rs
+++ /dev/null
@@ -1,207 +0,0 @@
1use hir::HirDisplay;
2use syntax::{
3 ast::{self, AstNode, LetStmt, NameOwner},
4 TextRange,
5};
6
7use crate::{AssistContext, AssistId, AssistKind, Assists};
8
9// Assist: add_explicit_type
10//
11// Specify type for a let binding.
12//
13// ```
14// fn main() {
15// let x$0 = 92;
16// }
17// ```
18// ->
19// ```
20// fn main() {
21// let x: i32 = 92;
22// }
23// ```
24pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
25 let let_stmt = ctx.find_node_at_offset::<LetStmt>()?;
26 let module = ctx.sema.scope(let_stmt.syntax()).module()?;
27 let expr = let_stmt.initializer()?;
28 // Must be a binding
29 let pat = match let_stmt.pat()? {
30 ast::Pat::IdentPat(bind_pat) => bind_pat,
31 _ => return None,
32 };
33 let pat_range = pat.syntax().text_range();
34 // The binding must have a name
35 let name = pat.name()?;
36 let name_range = name.syntax().text_range();
37 let stmt_range = let_stmt.syntax().text_range();
38 let eq_range = let_stmt.eq_token()?.text_range();
39 // Assist should only be applicable if cursor is between 'let' and '='
40 let let_range = TextRange::new(stmt_range.start(), eq_range.start());
41 let cursor_in_range = let_range.contains_range(ctx.frange.range);
42 if !cursor_in_range {
43 return None;
44 }
45 // Assist not applicable if the type has already been specified
46 // and it has no placeholders
47 let ascribed_ty = let_stmt.ty();
48 if let Some(ty) = &ascribed_ty {
49 if ty.syntax().descendants().find_map(ast::InferType::cast).is_none() {
50 return None;
51 }
52 }
53 // Infer type
54 let ty = ctx.sema.type_of_expr(&expr)?;
55
56 if ty.contains_unknown() || ty.is_closure() {
57 return None;
58 }
59
60 let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?;
61 acc.add(
62 AssistId("add_explicit_type", AssistKind::RefactorRewrite),
63 format!("Insert explicit type `{}`", inferred_type),
64 pat_range,
65 |builder| match ascribed_ty {
66 Some(ascribed_ty) => {
67 builder.replace(ascribed_ty.syntax().text_range(), inferred_type);
68 }
69 None => {
70 builder.insert(name_range.end(), format!(": {}", inferred_type));
71 }
72 },
73 )
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
81
82 #[test]
83 fn add_explicit_type_target() {
84 check_assist_target(add_explicit_type, "fn f() { let a$0 = 1; }", "a");
85 }
86
87 #[test]
88 fn add_explicit_type_works_for_simple_expr() {
89 check_assist(add_explicit_type, "fn f() { let a$0 = 1; }", "fn f() { let a: i32 = 1; }");
90 }
91
92 #[test]
93 fn add_explicit_type_works_for_underscore() {
94 check_assist(add_explicit_type, "fn f() { let a$0: _ = 1; }", "fn f() { let a: i32 = 1; }");
95 }
96
97 #[test]
98 fn add_explicit_type_works_for_nested_underscore() {
99 check_assist(
100 add_explicit_type,
101 r#"
102 enum Option<T> {
103 Some(T),
104 None
105 }
106
107 fn f() {
108 let a$0: Option<_> = Option::Some(1);
109 }"#,
110 r#"
111 enum Option<T> {
112 Some(T),
113 None
114 }
115
116 fn f() {
117 let a: Option<i32> = Option::Some(1);
118 }"#,
119 );
120 }
121
122 #[test]
123 fn add_explicit_type_works_for_macro_call() {
124 check_assist(
125 add_explicit_type,
126 r"macro_rules! v { () => {0u64} } fn f() { let a$0 = v!(); }",
127 r"macro_rules! v { () => {0u64} } fn f() { let a: u64 = v!(); }",
128 );
129 }
130
131 #[test]
132 fn add_explicit_type_works_for_macro_call_recursive() {
133 check_assist(
134 add_explicit_type,
135 r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a$0 = v!(); }"#,
136 r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a: u64 = v!(); }"#,
137 );
138 }
139
140 #[test]
141 fn add_explicit_type_not_applicable_if_ty_not_inferred() {
142 check_assist_not_applicable(add_explicit_type, "fn f() { let a$0 = None; }");
143 }
144
145 #[test]
146 fn add_explicit_type_not_applicable_if_ty_already_specified() {
147 check_assist_not_applicable(add_explicit_type, "fn f() { let a$0: i32 = 1; }");
148 }
149
150 #[test]
151 fn add_explicit_type_not_applicable_if_specified_ty_is_tuple() {
152 check_assist_not_applicable(add_explicit_type, "fn f() { let a$0: (i32, i32) = (3, 4); }");
153 }
154
155 #[test]
156 fn add_explicit_type_not_applicable_if_cursor_after_equals() {
157 check_assist_not_applicable(
158 add_explicit_type,
159 "fn f() {let a =$0 match 1 {2 => 3, 3 => 5};}",
160 )
161 }
162
163 #[test]
164 fn add_explicit_type_not_applicable_if_cursor_before_let() {
165 check_assist_not_applicable(
166 add_explicit_type,
167 "fn f() $0{let a = match 1 {2 => 3, 3 => 5};}",
168 )
169 }
170
171 #[test]
172 fn closure_parameters_are_not_added() {
173 check_assist_not_applicable(
174 add_explicit_type,
175 r#"
176fn main() {
177 let multiply_by_two$0 = |i| i * 3;
178 let six = multiply_by_two(2);
179}"#,
180 )
181 }
182
183 #[test]
184 fn default_generics_should_not_be_added() {
185 check_assist(
186 add_explicit_type,
187 r#"
188struct Test<K, T = u8> {
189 k: K,
190 t: T,
191}
192
193fn main() {
194 let test$0 = Test { t: 23u8, k: 33 };
195}"#,
196 r#"
197struct Test<K, T = u8> {
198 k: K,
199 t: T,
200}
201
202fn main() {
203 let test: Test<i32> = Test { t: 23u8, k: 33 };
204}"#,
205 );
206 }
207}
diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs
deleted file mode 100644
index 63cea754d..000000000
--- a/crates/assists/src/handlers/add_missing_impl_members.rs
+++ /dev/null
@@ -1,814 +0,0 @@
1use ide_db::traits::resolve_target_trait;
2use syntax::ast::{self, AstNode};
3
4use crate::{
5 assist_context::{AssistContext, Assists},
6 utils::add_trait_assoc_items_to_impl,
7 utils::DefaultMethods,
8 utils::{filter_assoc_items, render_snippet, Cursor},
9 AssistId, AssistKind,
10};
11
12// Assist: add_impl_missing_members
13//
14// Adds scaffold for required impl members.
15//
16// ```
17// trait Trait<T> {
18// type X;
19// fn foo(&self) -> T;
20// fn bar(&self) {}
21// }
22//
23// impl Trait<u32> for () {$0
24//
25// }
26// ```
27// ->
28// ```
29// trait Trait<T> {
30// type X;
31// fn foo(&self) -> T;
32// fn bar(&self) {}
33// }
34//
35// impl Trait<u32> for () {
36// $0type X;
37//
38// fn foo(&self) -> u32 {
39// todo!()
40// }
41// }
42// ```
43pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
44 add_missing_impl_members_inner(
45 acc,
46 ctx,
47 DefaultMethods::No,
48 "add_impl_missing_members",
49 "Implement missing members",
50 )
51}
52
53// Assist: add_impl_default_members
54//
55// Adds scaffold for overriding default impl members.
56//
57// ```
58// trait Trait {
59// type X;
60// fn foo(&self);
61// fn bar(&self) {}
62// }
63//
64// impl Trait for () {
65// type X = ();
66// fn foo(&self) {}$0
67//
68// }
69// ```
70// ->
71// ```
72// trait Trait {
73// type X;
74// fn foo(&self);
75// fn bar(&self) {}
76// }
77//
78// impl Trait for () {
79// type X = ();
80// fn foo(&self) {}
81//
82// $0fn bar(&self) {}
83// }
84// ```
85pub(crate) fn add_missing_default_members(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
86 add_missing_impl_members_inner(
87 acc,
88 ctx,
89 DefaultMethods::Only,
90 "add_impl_default_members",
91 "Implement default members",
92 )
93}
94
95fn add_missing_impl_members_inner(
96 acc: &mut Assists,
97 ctx: &AssistContext,
98 mode: DefaultMethods,
99 assist_id: &'static str,
100 label: &'static str,
101) -> Option<()> {
102 let _p = profile::span("add_missing_impl_members_inner");
103 let impl_def = ctx.find_node_at_offset::<ast::Impl>()?;
104 let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;
105
106 let missing_items = filter_assoc_items(
107 ctx.db(),
108 &ide_db::traits::get_missing_assoc_items(&ctx.sema, &impl_def),
109 mode,
110 );
111
112 if missing_items.is_empty() {
113 return None;
114 }
115
116 let target = impl_def.syntax().text_range();
117 acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| {
118 let target_scope = ctx.sema.scope(impl_def.syntax());
119 let (new_impl_def, first_new_item) =
120 add_trait_assoc_items_to_impl(&ctx.sema, missing_items, trait_, impl_def, target_scope);
121 match ctx.config.snippet_cap {
122 None => builder.replace(target, new_impl_def.to_string()),
123 Some(cap) => {
124 let mut cursor = Cursor::Before(first_new_item.syntax());
125 let placeholder;
126 if let ast::AssocItem::Fn(func) = &first_new_item {
127 if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) {
128 if m.syntax().text() == "todo!()" {
129 placeholder = m;
130 cursor = Cursor::Replace(placeholder.syntax());
131 }
132 }
133 }
134 builder.replace_snippet(
135 cap,
136 target,
137 render_snippet(cap, new_impl_def.syntax(), cursor),
138 )
139 }
140 };
141 })
142}
143
144#[cfg(test)]
145mod tests {
146 use crate::tests::{check_assist, check_assist_not_applicable};
147
148 use super::*;
149
150 #[test]
151 fn test_add_missing_impl_members() {
152 check_assist(
153 add_missing_impl_members,
154 r#"
155trait Foo {
156 type Output;
157
158 const CONST: usize = 42;
159
160 fn foo(&self);
161 fn bar(&self);
162 fn baz(&self);
163}
164
165struct S;
166
167impl Foo for S {
168 fn bar(&self) {}
169$0
170}"#,
171 r#"
172trait Foo {
173 type Output;
174
175 const CONST: usize = 42;
176
177 fn foo(&self);
178 fn bar(&self);
179 fn baz(&self);
180}
181
182struct S;
183
184impl Foo for S {
185 fn bar(&self) {}
186
187 $0type Output;
188
189 const CONST: usize = 42;
190
191 fn foo(&self) {
192 todo!()
193 }
194
195 fn baz(&self) {
196 todo!()
197 }
198}"#,
199 );
200 }
201
202 #[test]
203 fn test_copied_overriden_members() {
204 check_assist(
205 add_missing_impl_members,
206 r#"
207trait Foo {
208 fn foo(&self);
209 fn bar(&self) -> bool { true }
210 fn baz(&self) -> u32 { 42 }
211}
212
213struct S;
214
215impl Foo for S {
216 fn bar(&self) {}
217$0
218}"#,
219 r#"
220trait Foo {
221 fn foo(&self);
222 fn bar(&self) -> bool { true }
223 fn baz(&self) -> u32 { 42 }
224}
225
226struct S;
227
228impl Foo for S {
229 fn bar(&self) {}
230
231 fn foo(&self) {
232 ${0:todo!()}
233 }
234}"#,
235 );
236 }
237
238 #[test]
239 fn test_empty_impl_def() {
240 check_assist(
241 add_missing_impl_members,
242 r#"
243trait Foo { fn foo(&self); }
244struct S;
245impl Foo for S { $0 }"#,
246 r#"
247trait Foo { fn foo(&self); }
248struct S;
249impl Foo for S {
250 fn foo(&self) {
251 ${0:todo!()}
252 }
253}"#,
254 );
255 }
256
257 #[test]
258 fn test_impl_def_without_braces() {
259 check_assist(
260 add_missing_impl_members,
261 r#"
262trait Foo { fn foo(&self); }
263struct S;
264impl Foo for S$0"#,
265 r#"
266trait Foo { fn foo(&self); }
267struct S;
268impl Foo for S {
269 fn foo(&self) {
270 ${0:todo!()}
271 }
272}"#,
273 );
274 }
275
276 #[test]
277 fn fill_in_type_params_1() {
278 check_assist(
279 add_missing_impl_members,
280 r#"
281trait Foo<T> { fn foo(&self, t: T) -> &T; }
282struct S;
283impl Foo<u32> for S { $0 }"#,
284 r#"
285trait Foo<T> { fn foo(&self, t: T) -> &T; }
286struct S;
287impl Foo<u32> for S {
288 fn foo(&self, t: u32) -> &u32 {
289 ${0:todo!()}
290 }
291}"#,
292 );
293 }
294
295 #[test]
296 fn fill_in_type_params_2() {
297 check_assist(
298 add_missing_impl_members,
299 r#"
300trait Foo<T> { fn foo(&self, t: T) -> &T; }
301struct S;
302impl<U> Foo<U> for S { $0 }"#,
303 r#"
304trait Foo<T> { fn foo(&self, t: T) -> &T; }
305struct S;
306impl<U> Foo<U> for S {
307 fn foo(&self, t: U) -> &U {
308 ${0:todo!()}
309 }
310}"#,
311 );
312 }
313
314 #[test]
315 fn test_cursor_after_empty_impl_def() {
316 check_assist(
317 add_missing_impl_members,
318 r#"
319trait Foo { fn foo(&self); }
320struct S;
321impl Foo for S {}$0"#,
322 r#"
323trait Foo { fn foo(&self); }
324struct S;
325impl Foo for S {
326 fn foo(&self) {
327 ${0:todo!()}
328 }
329}"#,
330 )
331 }
332
333 #[test]
334 fn test_qualify_path_1() {
335 check_assist(
336 add_missing_impl_members,
337 r#"
338mod foo {
339 pub struct Bar;
340 trait Foo { fn foo(&self, bar: Bar); }
341}
342struct S;
343impl foo::Foo for S { $0 }"#,
344 r#"
345mod foo {
346 pub struct Bar;
347 trait Foo { fn foo(&self, bar: Bar); }
348}
349struct S;
350impl foo::Foo for S {
351 fn foo(&self, bar: foo::Bar) {
352 ${0:todo!()}
353 }
354}"#,
355 );
356 }
357
358 #[test]
359 fn test_qualify_path_2() {
360 check_assist(
361 add_missing_impl_members,
362 r#"
363mod foo {
364 pub mod bar {
365 pub struct Bar;
366 pub trait Foo { fn foo(&self, bar: Bar); }
367 }
368}
369
370use foo::bar;
371
372struct S;
373impl bar::Foo for S { $0 }"#,
374 r#"
375mod foo {
376 pub mod bar {
377 pub struct Bar;
378 pub trait Foo { fn foo(&self, bar: Bar); }
379 }
380}
381
382use foo::bar;
383
384struct S;
385impl bar::Foo for S {
386 fn foo(&self, bar: bar::Bar) {
387 ${0:todo!()}
388 }
389}"#,
390 );
391 }
392
393 #[test]
394 fn test_qualify_path_generic() {
395 check_assist(
396 add_missing_impl_members,
397 r#"
398mod foo {
399 pub struct Bar<T>;
400 trait Foo { fn foo(&self, bar: Bar<u32>); }
401}
402struct S;
403impl foo::Foo for S { $0 }"#,
404 r#"
405mod foo {
406 pub struct Bar<T>;
407 trait Foo { fn foo(&self, bar: Bar<u32>); }
408}
409struct S;
410impl foo::Foo for S {
411 fn foo(&self, bar: foo::Bar<u32>) {
412 ${0:todo!()}
413 }
414}"#,
415 );
416 }
417
418 #[test]
419 fn test_qualify_path_and_substitute_param() {
420 check_assist(
421 add_missing_impl_members,
422 r#"
423mod foo {
424 pub struct Bar<T>;
425 trait Foo<T> { fn foo(&self, bar: Bar<T>); }
426}
427struct S;
428impl foo::Foo<u32> for S { $0 }"#,
429 r#"
430mod foo {
431 pub struct Bar<T>;
432 trait Foo<T> { fn foo(&self, bar: Bar<T>); }
433}
434struct S;
435impl foo::Foo<u32> for S {
436 fn foo(&self, bar: foo::Bar<u32>) {
437 ${0:todo!()}
438 }
439}"#,
440 );
441 }
442
443 #[test]
444 fn test_substitute_param_no_qualify() {
445 // when substituting params, the substituted param should not be qualified!
446 check_assist(
447 add_missing_impl_members,
448 r#"
449mod foo {
450 trait Foo<T> { fn foo(&self, bar: T); }
451 pub struct Param;
452}
453struct Param;
454struct S;
455impl foo::Foo<Param> for S { $0 }"#,
456 r#"
457mod foo {
458 trait Foo<T> { fn foo(&self, bar: T); }
459 pub struct Param;
460}
461struct Param;
462struct S;
463impl foo::Foo<Param> for S {
464 fn foo(&self, bar: Param) {
465 ${0:todo!()}
466 }
467}"#,
468 );
469 }
470
471 #[test]
472 fn test_qualify_path_associated_item() {
473 check_assist(
474 add_missing_impl_members,
475 r#"
476mod foo {
477 pub struct Bar<T>;
478 impl Bar<T> { type Assoc = u32; }
479 trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); }
480}
481struct S;
482impl foo::Foo for S { $0 }"#,
483 r#"
484mod foo {
485 pub struct Bar<T>;
486 impl Bar<T> { type Assoc = u32; }
487 trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); }
488}
489struct S;
490impl foo::Foo for S {
491 fn foo(&self, bar: foo::Bar<u32>::Assoc) {
492 ${0:todo!()}
493 }
494}"#,
495 );
496 }
497
498 #[test]
499 fn test_qualify_path_nested() {
500 check_assist(
501 add_missing_impl_members,
502 r#"
503mod foo {
504 pub struct Bar<T>;
505 pub struct Baz;
506 trait Foo { fn foo(&self, bar: Bar<Baz>); }
507}
508struct S;
509impl foo::Foo for S { $0 }"#,
510 r#"
511mod foo {
512 pub struct Bar<T>;
513 pub struct Baz;
514 trait Foo { fn foo(&self, bar: Bar<Baz>); }
515}
516struct S;
517impl foo::Foo for S {
518 fn foo(&self, bar: foo::Bar<foo::Baz>) {
519 ${0:todo!()}
520 }
521}"#,
522 );
523 }
524
525 #[test]
526 fn test_qualify_path_fn_trait_notation() {
527 check_assist(
528 add_missing_impl_members,
529 r#"
530mod foo {
531 pub trait Fn<Args> { type Output; }
532 trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
533}
534struct S;
535impl foo::Foo for S { $0 }"#,
536 r#"
537mod foo {
538 pub trait Fn<Args> { type Output; }
539 trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
540}
541struct S;
542impl foo::Foo for S {
543 fn foo(&self, bar: dyn Fn(u32) -> i32) {
544 ${0:todo!()}
545 }
546}"#,
547 );
548 }
549
550 #[test]
551 fn test_empty_trait() {
552 check_assist_not_applicable(
553 add_missing_impl_members,
554 r#"
555trait Foo;
556struct S;
557impl Foo for S { $0 }"#,
558 )
559 }
560
561 #[test]
562 fn test_ignore_unnamed_trait_members_and_default_methods() {
563 check_assist_not_applicable(
564 add_missing_impl_members,
565 r#"
566trait Foo {
567 fn (arg: u32);
568 fn valid(some: u32) -> bool { false }
569}
570struct S;
571impl Foo for S { $0 }"#,
572 )
573 }
574
575 #[test]
576 fn test_with_docstring_and_attrs() {
577 check_assist(
578 add_missing_impl_members,
579 r#"
580#[doc(alias = "test alias")]
581trait Foo {
582 /// doc string
583 type Output;
584
585 #[must_use]
586 fn foo(&self);
587}
588struct S;
589impl Foo for S {}$0"#,
590 r#"
591#[doc(alias = "test alias")]
592trait Foo {
593 /// doc string
594 type Output;
595
596 #[must_use]
597 fn foo(&self);
598}
599struct S;
600impl Foo for S {
601 $0type Output;
602
603 fn foo(&self) {
604 todo!()
605 }
606}"#,
607 )
608 }
609
610 #[test]
611 fn test_default_methods() {
612 check_assist(
613 add_missing_default_members,
614 r#"
615trait Foo {
616 type Output;
617
618 const CONST: usize = 42;
619
620 fn valid(some: u32) -> bool { false }
621 fn foo(some: u32) -> bool;
622}
623struct S;
624impl Foo for S { $0 }"#,
625 r#"
626trait Foo {
627 type Output;
628
629 const CONST: usize = 42;
630
631 fn valid(some: u32) -> bool { false }
632 fn foo(some: u32) -> bool;
633}
634struct S;
635impl Foo for S {
636 $0fn valid(some: u32) -> bool { false }
637}"#,
638 )
639 }
640
641 #[test]
642 fn test_generic_single_default_parameter() {
643 check_assist(
644 add_missing_impl_members,
645 r#"
646trait Foo<T = Self> {
647 fn bar(&self, other: &T);
648}
649
650struct S;
651impl Foo for S { $0 }"#,
652 r#"
653trait Foo<T = Self> {
654 fn bar(&self, other: &T);
655}
656
657struct S;
658impl Foo for S {
659 fn bar(&self, other: &Self) {
660 ${0:todo!()}
661 }
662}"#,
663 )
664 }
665
666 #[test]
667 fn test_generic_default_parameter_is_second() {
668 check_assist(
669 add_missing_impl_members,
670 r#"
671trait Foo<T1, T2 = Self> {
672 fn bar(&self, this: &T1, that: &T2);
673}
674
675struct S<T>;
676impl Foo<T> for S<T> { $0 }"#,
677 r#"
678trait Foo<T1, T2 = Self> {
679 fn bar(&self, this: &T1, that: &T2);
680}
681
682struct S<T>;
683impl Foo<T> for S<T> {
684 fn bar(&self, this: &T, that: &Self) {
685 ${0:todo!()}
686 }
687}"#,
688 )
689 }
690
691 #[test]
692 fn test_assoc_type_bounds_are_removed() {
693 check_assist(
694 add_missing_impl_members,
695 r#"
696trait Tr {
697 type Ty: Copy + 'static;
698}
699
700impl Tr for ()$0 {
701}"#,
702 r#"
703trait Tr {
704 type Ty: Copy + 'static;
705}
706
707impl Tr for () {
708 $0type Ty;
709}"#,
710 )
711 }
712
713 #[test]
714 fn test_whitespace_fixup_preserves_bad_tokens() {
715 check_assist(
716 add_missing_impl_members,
717 r#"
718trait Tr {
719 fn foo();
720}
721
722impl Tr for ()$0 {
723 +++
724}"#,
725 r#"
726trait Tr {
727 fn foo();
728}
729
730impl Tr for () {
731 fn foo() {
732 ${0:todo!()}
733 }
734 +++
735}"#,
736 )
737 }
738
739 #[test]
740 fn test_whitespace_fixup_preserves_comments() {
741 check_assist(
742 add_missing_impl_members,
743 r#"
744trait Tr {
745 fn foo();
746}
747
748impl Tr for ()$0 {
749 // very important
750}"#,
751 r#"
752trait Tr {
753 fn foo();
754}
755
756impl Tr for () {
757 fn foo() {
758 ${0:todo!()}
759 }
760 // very important
761}"#,
762 )
763 }
764
765 #[test]
766 fn weird_path() {
767 check_assist(
768 add_missing_impl_members,
769 r#"
770trait Test {
771 fn foo(&self, x: crate)
772}
773impl Test for () {
774 $0
775}
776"#,
777 r#"
778trait Test {
779 fn foo(&self, x: crate)
780}
781impl Test for () {
782 fn foo(&self, x: crate) {
783 ${0:todo!()}
784 }
785}
786"#,
787 )
788 }
789
790 #[test]
791 fn missing_generic_type() {
792 check_assist(
793 add_missing_impl_members,
794 r#"
795trait Foo<BAR> {
796 fn foo(&self, bar: BAR);
797}
798impl Foo for () {
799 $0
800}
801"#,
802 r#"
803trait Foo<BAR> {
804 fn foo(&self, bar: BAR);
805}
806impl Foo for () {
807 fn foo(&self, bar: BAR) {
808 ${0:todo!()}
809 }
810}
811"#,
812 )
813 }
814}
diff --git a/crates/assists/src/handlers/add_turbo_fish.rs b/crates/assists/src/handlers/add_turbo_fish.rs
deleted file mode 100644
index 8e9ea4fad..000000000
--- a/crates/assists/src/handlers/add_turbo_fish.rs
+++ /dev/null
@@ -1,164 +0,0 @@
1use ide_db::defs::{Definition, NameRefClass};
2use syntax::{ast, AstNode, SyntaxKind, T};
3use test_utils::mark;
4
5use crate::{
6 assist_context::{AssistContext, Assists},
7 AssistId, AssistKind,
8};
9
10// Assist: add_turbo_fish
11//
12// Adds `::<_>` to a call of a generic method or function.
13//
14// ```
15// fn make<T>() -> T { todo!() }
16// fn main() {
17// let x = make$0();
18// }
19// ```
20// ->
21// ```
22// fn make<T>() -> T { todo!() }
23// fn main() {
24// let x = make::<${0:_}>();
25// }
26// ```
27pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
28 let ident = ctx.find_token_syntax_at_offset(SyntaxKind::IDENT).or_else(|| {
29 let arg_list = ctx.find_node_at_offset::<ast::ArgList>()?;
30 if arg_list.args().count() > 0 {
31 return None;
32 }
33 mark::hit!(add_turbo_fish_after_call);
34 arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT)
35 })?;
36 let next_token = ident.next_token()?;
37 if next_token.kind() == T![::] {
38 mark::hit!(add_turbo_fish_one_fish_is_enough);
39 return None;
40 }
41 let name_ref = ast::NameRef::cast(ident.parent())?;
42 let def = match NameRefClass::classify(&ctx.sema, &name_ref)? {
43 NameRefClass::Definition(def) => def,
44 NameRefClass::ExternCrate(_) | NameRefClass::FieldShorthand { .. } => return None,
45 };
46 let fun = match def {
47 Definition::ModuleDef(hir::ModuleDef::Function(it)) => it,
48 _ => return None,
49 };
50 let generics = hir::GenericDef::Function(fun).params(ctx.sema.db);
51 if generics.is_empty() {
52 mark::hit!(add_turbo_fish_non_generic);
53 return None;
54 }
55 acc.add(
56 AssistId("add_turbo_fish", AssistKind::RefactorRewrite),
57 "Add `::<>`",
58 ident.text_range(),
59 |builder| match ctx.config.snippet_cap {
60 Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"),
61 None => builder.insert(ident.text_range().end(), "::<_>"),
62 },
63 )
64}
65
66#[cfg(test)]
67mod tests {
68 use crate::tests::{check_assist, check_assist_not_applicable};
69
70 use super::*;
71 use test_utils::mark;
72
73 #[test]
74 fn add_turbo_fish_function() {
75 check_assist(
76 add_turbo_fish,
77 r#"
78fn make<T>() -> T {}
79fn main() {
80 make$0();
81}
82"#,
83 r#"
84fn make<T>() -> T {}
85fn main() {
86 make::<${0:_}>();
87}
88"#,
89 );
90 }
91
92 #[test]
93 fn add_turbo_fish_after_call() {
94 mark::check!(add_turbo_fish_after_call);
95 check_assist(
96 add_turbo_fish,
97 r#"
98fn make<T>() -> T {}
99fn main() {
100 make()$0;
101}
102"#,
103 r#"
104fn make<T>() -> T {}
105fn main() {
106 make::<${0:_}>();
107}
108"#,
109 );
110 }
111
112 #[test]
113 fn add_turbo_fish_method() {
114 check_assist(
115 add_turbo_fish,
116 r#"
117struct S;
118impl S {
119 fn make<T>(&self) -> T {}
120}
121fn main() {
122 S.make$0();
123}
124"#,
125 r#"
126struct S;
127impl S {
128 fn make<T>(&self) -> T {}
129}
130fn main() {
131 S.make::<${0:_}>();
132}
133"#,
134 );
135 }
136
137 #[test]
138 fn add_turbo_fish_one_fish_is_enough() {
139 mark::check!(add_turbo_fish_one_fish_is_enough);
140 check_assist_not_applicable(
141 add_turbo_fish,
142 r#"
143fn make<T>() -> T {}
144fn main() {
145 make$0::<()>();
146}
147"#,
148 );
149 }
150
151 #[test]
152 fn add_turbo_fish_non_generic() {
153 mark::check!(add_turbo_fish_non_generic);
154 check_assist_not_applicable(
155 add_turbo_fish,
156 r#"
157fn make() -> () {}
158fn main() {
159 make$0();
160}
161"#,
162 );
163 }
164}
diff --git a/crates/assists/src/handlers/apply_demorgan.rs b/crates/assists/src/handlers/apply_demorgan.rs
deleted file mode 100644
index ed4d11455..000000000
--- a/crates/assists/src/handlers/apply_demorgan.rs
+++ /dev/null
@@ -1,93 +0,0 @@
1use syntax::ast::{self, AstNode};
2
3use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};
4
5// Assist: apply_demorgan
6//
7// Apply https://en.wikipedia.org/wiki/De_Morgan%27s_laws[De Morgan's law].
8// This transforms expressions of the form `!l || !r` into `!(l && r)`.
9// This also works with `&&`. This assist can only be applied with the cursor
10// on either `||` or `&&`, with both operands being a negation of some kind.
11// This means something of the form `!x` or `x != y`.
12//
13// ```
14// fn main() {
15// if x != 4 ||$0 !y {}
16// }
17// ```
18// ->
19// ```
20// fn main() {
21// if !(x == 4 && y) {}
22// }
23// ```
24pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
25 let expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
26 let op = expr.op_kind()?;
27 let op_range = expr.op_token()?.text_range();
28 let opposite_op = opposite_logic_op(op)?;
29 let cursor_in_range = op_range.contains_range(ctx.frange.range);
30 if !cursor_in_range {
31 return None;
32 }
33
34 let lhs = expr.lhs()?;
35 let lhs_range = lhs.syntax().text_range();
36 let not_lhs = invert_boolean_expression(lhs);
37
38 let rhs = expr.rhs()?;
39 let rhs_range = rhs.syntax().text_range();
40 let not_rhs = invert_boolean_expression(rhs);
41
42 acc.add(
43 AssistId("apply_demorgan", AssistKind::RefactorRewrite),
44 "Apply De Morgan's law",
45 op_range,
46 |edit| {
47 edit.replace(op_range, opposite_op);
48 edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
49 edit.replace(rhs_range, format!("{})", not_rhs.syntax().text()));
50 },
51 )
52}
53
54// Return the opposite text for a given logical operator, if it makes sense
55fn opposite_logic_op(kind: ast::BinOp) -> Option<&'static str> {
56 match kind {
57 ast::BinOp::BooleanOr => Some("&&"),
58 ast::BinOp::BooleanAnd => Some("||"),
59 _ => None,
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 use crate::tests::{check_assist, check_assist_not_applicable};
68
69 #[test]
70 fn demorgan_turns_and_into_or() {
71 check_assist(apply_demorgan, "fn f() { !x &&$0 !x }", "fn f() { !(x || x) }")
72 }
73
74 #[test]
75 fn demorgan_turns_or_into_and() {
76 check_assist(apply_demorgan, "fn f() { !x ||$0 !x }", "fn f() { !(x && x) }")
77 }
78
79 #[test]
80 fn demorgan_removes_inequality() {
81 check_assist(apply_demorgan, "fn f() { x != x ||$0 !x }", "fn f() { !(x == x && x) }")
82 }
83
84 #[test]
85 fn demorgan_general_case() {
86 check_assist(apply_demorgan, "fn f() { x ||$0 x }", "fn f() { !(!x && !x) }")
87 }
88
89 #[test]
90 fn demorgan_doesnt_apply_with_cursor_not_on_op() {
91 check_assist_not_applicable(apply_demorgan, "fn f() { $0 !x || !x }")
92 }
93}
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
deleted file mode 100644
index 4e2a4fcd9..000000000
--- a/crates/assists/src/handlers/auto_import.rs
+++ /dev/null
@@ -1,965 +0,0 @@
1use ide_db::helpers::{
2 import_assets::{ImportAssets, ImportCandidate},
3 insert_use::{insert_use, ImportScope},
4 mod_path_to_ast,
5};
6use syntax::ast;
7
8use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
9
10// Feature: Auto Import
11//
12// Using the `auto-import` assist it is possible to insert missing imports for unresolved items.
13// When inserting an import it will do so in a structured manner by keeping imports grouped,
14// separated by a newline in the following order:
15//
16// - `std` and `core`
17// - External Crates
18// - Current Crate, paths prefixed by `crate`
19// - Current Module, paths prefixed by `self`
20// - Super Module, paths prefixed by `super`
21//
22// Example:
23// ```rust
24// use std::fs::File;
25//
26// use itertools::Itertools;
27// use syntax::ast;
28//
29// use crate::utils::insert_use;
30//
31// use self::auto_import;
32//
33// use super::AssistContext;
34// ```
35//
36// .Merge Behaviour
37//
38// It is possible to configure how use-trees are merged with the `importMergeBehaviour` setting.
39// It has the following configurations:
40//
41// - `full`: This setting will cause auto-import to always completely merge use-trees that share the
42// same path prefix while also merging inner trees that share the same path-prefix. This kind of
43// nesting is only supported in Rust versions later than 1.24.
44// - `last`: This setting will cause auto-import to merge use-trees as long as the resulting tree
45// will only contain a nesting of single segment paths at the very end.
46// - `none`: This setting will cause auto-import to never merge use-trees keeping them as simple
47// paths.
48//
49// In `VS Code` the configuration for this is `rust-analyzer.assist.importMergeBehaviour`.
50//
51// .Import Prefix
52//
53// The style of imports in the same crate is configurable through the `importPrefix` setting.
54// It has the following configurations:
55//
56// - `by_crate`: This setting will force paths to be always absolute, starting with the `crate`
57// prefix, unless the item is defined outside of the current crate.
58// - `by_self`: This setting will force paths that are relative to the current module to always
59// start with `self`. This will result in paths that always start with either `crate`, `self`,
60// `super` or an extern crate identifier.
61// - `plain`: This setting does not impose any restrictions in imports.
62//
63// In `VS Code` the configuration for this is `rust-analyzer.assist.importPrefix`.
64
65// Assist: auto_import
66//
67// If the name is unresolved, provides all possible imports for it.
68//
69// ```
70// fn main() {
71// let map = HashMap$0::new();
72// }
73// # pub mod std { pub mod collections { pub struct HashMap { } } }
74// ```
75// ->
76// ```
77// use std::collections::HashMap;
78//
79// fn main() {
80// let map = HashMap::new();
81// }
82// # pub mod std { pub mod collections { pub struct HashMap { } } }
83// ```
84pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
85 let import_assets =
86 if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
87 ImportAssets::for_regular_path(path_under_caret, &ctx.sema)
88 } else if let Some(method_under_caret) =
89 ctx.find_node_at_offset_with_descend::<ast::MethodCallExpr>()
90 {
91 ImportAssets::for_method_call(method_under_caret, &ctx.sema)
92 } else {
93 None
94 }?;
95 let proposed_imports = import_assets.search_for_imports(&ctx.sema, &ctx.config.insert_use);
96 if proposed_imports.is_empty() {
97 return None;
98 }
99
100 let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range;
101 let group = import_group_message(import_assets.import_candidate());
102 let scope =
103 ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), &ctx.sema)?;
104 for (import, _) in proposed_imports {
105 acc.add_group(
106 &group,
107 AssistId("auto_import", AssistKind::QuickFix),
108 format!("Import `{}`", &import),
109 range,
110 |builder| {
111 let rewriter =
112 insert_use(&scope, mod_path_to_ast(&import), ctx.config.insert_use.merge);
113 builder.rewrite(rewriter);
114 },
115 );
116 }
117 Some(())
118}
119
120fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel {
121 let name = match import_candidate {
122 ImportCandidate::Path(candidate) => format!("Import {}", &candidate.name),
123 ImportCandidate::TraitAssocItem(candidate) => {
124 format!("Import a trait for item {}", &candidate.name)
125 }
126 ImportCandidate::TraitMethod(candidate) => {
127 format!("Import a trait for method {}", &candidate.name)
128 }
129 };
130 GroupLabel(name)
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
137
138 #[test]
139 fn applicable_when_found_an_import_partial() {
140 check_assist(
141 auto_import,
142 r"
143 mod std {
144 pub mod fmt {
145 pub struct Formatter;
146 }
147 }
148
149 use std::fmt;
150
151 $0Formatter
152 ",
153 r"
154 mod std {
155 pub mod fmt {
156 pub struct Formatter;
157 }
158 }
159
160 use std::fmt::{self, Formatter};
161
162 Formatter
163 ",
164 );
165 }
166
167 #[test]
168 fn applicable_when_found_an_import() {
169 check_assist(
170 auto_import,
171 r"
172 $0PubStruct
173
174 pub mod PubMod {
175 pub struct PubStruct;
176 }
177 ",
178 r"
179 use PubMod::PubStruct;
180
181 PubStruct
182
183 pub mod PubMod {
184 pub struct PubStruct;
185 }
186 ",
187 );
188 }
189
190 #[test]
191 fn applicable_when_found_an_import_in_macros() {
192 check_assist(
193 auto_import,
194 r"
195 macro_rules! foo {
196 ($i:ident) => { fn foo(a: $i) {} }
197 }
198 foo!(Pub$0Struct);
199
200 pub mod PubMod {
201 pub struct PubStruct;
202 }
203 ",
204 r"
205 use PubMod::PubStruct;
206
207 macro_rules! foo {
208 ($i:ident) => { fn foo(a: $i) {} }
209 }
210 foo!(PubStruct);
211
212 pub mod PubMod {
213 pub struct PubStruct;
214 }
215 ",
216 );
217 }
218
219 #[test]
220 fn auto_imports_are_merged() {
221 check_assist(
222 auto_import,
223 r"
224 use PubMod::PubStruct1;
225
226 struct Test {
227 test: Pub$0Struct2<u8>,
228 }
229
230 pub mod PubMod {
231 pub struct PubStruct1;
232 pub struct PubStruct2<T> {
233 _t: T,
234 }
235 }
236 ",
237 r"
238 use PubMod::{PubStruct1, PubStruct2};
239
240 struct Test {
241 test: PubStruct2<u8>,
242 }
243
244 pub mod PubMod {
245 pub struct PubStruct1;
246 pub struct PubStruct2<T> {
247 _t: T,
248 }
249 }
250 ",
251 );
252 }
253
254 #[test]
255 fn applicable_when_found_multiple_imports() {
256 check_assist(
257 auto_import,
258 r"
259 PubSt$0ruct
260
261 pub mod PubMod1 {
262 pub struct PubStruct;
263 }
264 pub mod PubMod2 {
265 pub struct PubStruct;
266 }
267 pub mod PubMod3 {
268 pub struct PubStruct;
269 }
270 ",
271 r"
272 use PubMod3::PubStruct;
273
274 PubStruct
275
276 pub mod PubMod1 {
277 pub struct PubStruct;
278 }
279 pub mod PubMod2 {
280 pub struct PubStruct;
281 }
282 pub mod PubMod3 {
283 pub struct PubStruct;
284 }
285 ",
286 );
287 }
288
289 #[test]
290 fn not_applicable_for_already_imported_types() {
291 check_assist_not_applicable(
292 auto_import,
293 r"
294 use PubMod::PubStruct;
295
296 PubStruct$0
297
298 pub mod PubMod {
299 pub struct PubStruct;
300 }
301 ",
302 );
303 }
304
305 #[test]
306 fn not_applicable_for_types_with_private_paths() {
307 check_assist_not_applicable(
308 auto_import,
309 r"
310 PrivateStruct$0
311
312 pub mod PubMod {
313 struct PrivateStruct;
314 }
315 ",
316 );
317 }
318
319 #[test]
320 fn not_applicable_when_no_imports_found() {
321 check_assist_not_applicable(
322 auto_import,
323 "
324 PubStruct$0",
325 );
326 }
327
328 #[test]
329 fn not_applicable_in_import_statements() {
330 check_assist_not_applicable(
331 auto_import,
332 r"
333 use PubStruct$0;
334
335 pub mod PubMod {
336 pub struct PubStruct;
337 }",
338 );
339 }
340
341 #[test]
342 fn function_import() {
343 check_assist(
344 auto_import,
345 r"
346 test_function$0
347
348 pub mod PubMod {
349 pub fn test_function() {};
350 }
351 ",
352 r"
353 use PubMod::test_function;
354
355 test_function
356
357 pub mod PubMod {
358 pub fn test_function() {};
359 }
360 ",
361 );
362 }
363
364 #[test]
365 fn macro_import() {
366 check_assist(
367 auto_import,
368 r"
369//- /lib.rs crate:crate_with_macro
370#[macro_export]
371macro_rules! foo {
372 () => ()
373}
374
375//- /main.rs crate:main deps:crate_with_macro
376fn main() {
377 foo$0
378}
379",
380 r"use crate_with_macro::foo;
381
382fn main() {
383 foo
384}
385",
386 );
387 }
388
389 #[test]
390 fn auto_import_target() {
391 check_assist_target(
392 auto_import,
393 r"
394 struct AssistInfo {
395 group_label: Option<$0GroupLabel>,
396 }
397
398 mod m { pub struct GroupLabel; }
399 ",
400 "GroupLabel",
401 )
402 }
403
404 #[test]
405 fn not_applicable_when_path_start_is_imported() {
406 check_assist_not_applicable(
407 auto_import,
408 r"
409 pub mod mod1 {
410 pub mod mod2 {
411 pub mod mod3 {
412 pub struct TestStruct;
413 }
414 }
415 }
416
417 use mod1::mod2;
418 fn main() {
419 mod2::mod3::TestStruct$0
420 }
421 ",
422 );
423 }
424
425 #[test]
426 fn not_applicable_for_imported_function() {
427 check_assist_not_applicable(
428 auto_import,
429 r"
430 pub mod test_mod {
431 pub fn test_function() {}
432 }
433
434 use test_mod::test_function;
435 fn main() {
436 test_function$0
437 }
438 ",
439 );
440 }
441
442 #[test]
443 fn associated_struct_function() {
444 check_assist(
445 auto_import,
446 r"
447 mod test_mod {
448 pub struct TestStruct {}
449 impl TestStruct {
450 pub fn test_function() {}
451 }
452 }
453
454 fn main() {
455 TestStruct::test_function$0
456 }
457 ",
458 r"
459 use test_mod::TestStruct;
460
461 mod test_mod {
462 pub struct TestStruct {}
463 impl TestStruct {
464 pub fn test_function() {}
465 }
466 }
467
468 fn main() {
469 TestStruct::test_function
470 }
471 ",
472 );
473 }
474
475 #[test]
476 fn associated_struct_const() {
477 check_assist(
478 auto_import,
479 r"
480 mod test_mod {
481 pub struct TestStruct {}
482 impl TestStruct {
483 const TEST_CONST: u8 = 42;
484 }
485 }
486
487 fn main() {
488 TestStruct::TEST_CONST$0
489 }
490 ",
491 r"
492 use test_mod::TestStruct;
493
494 mod test_mod {
495 pub struct TestStruct {}
496 impl TestStruct {
497 const TEST_CONST: u8 = 42;
498 }
499 }
500
501 fn main() {
502 TestStruct::TEST_CONST
503 }
504 ",
505 );
506 }
507
508 #[test]
509 fn associated_trait_function() {
510 check_assist(
511 auto_import,
512 r"
513 mod test_mod {
514 pub trait TestTrait {
515 fn test_function();
516 }
517 pub struct TestStruct {}
518 impl TestTrait for TestStruct {
519 fn test_function() {}
520 }
521 }
522
523 fn main() {
524 test_mod::TestStruct::test_function$0
525 }
526 ",
527 r"
528 use test_mod::TestTrait;
529
530 mod test_mod {
531 pub trait TestTrait {
532 fn test_function();
533 }
534 pub struct TestStruct {}
535 impl TestTrait for TestStruct {
536 fn test_function() {}
537 }
538 }
539
540 fn main() {
541 test_mod::TestStruct::test_function
542 }
543 ",
544 );
545 }
546
547 #[test]
548 fn not_applicable_for_imported_trait_for_function() {
549 check_assist_not_applicable(
550 auto_import,
551 r"
552 mod test_mod {
553 pub trait TestTrait {
554 fn test_function();
555 }
556 pub trait TestTrait2 {
557 fn test_function();
558 }
559 pub enum TestEnum {
560 One,
561 Two,
562 }
563 impl TestTrait2 for TestEnum {
564 fn test_function() {}
565 }
566 impl TestTrait for TestEnum {
567 fn test_function() {}
568 }
569 }
570
571 use test_mod::TestTrait2;
572 fn main() {
573 test_mod::TestEnum::test_function$0;
574 }
575 ",
576 )
577 }
578
579 #[test]
580 fn associated_trait_const() {
581 check_assist(
582 auto_import,
583 r"
584 mod test_mod {
585 pub trait TestTrait {
586 const TEST_CONST: u8;
587 }
588 pub struct TestStruct {}
589 impl TestTrait for TestStruct {
590 const TEST_CONST: u8 = 42;
591 }
592 }
593
594 fn main() {
595 test_mod::TestStruct::TEST_CONST$0
596 }
597 ",
598 r"
599 use test_mod::TestTrait;
600
601 mod test_mod {
602 pub trait TestTrait {
603 const TEST_CONST: u8;
604 }
605 pub struct TestStruct {}
606 impl TestTrait for TestStruct {
607 const TEST_CONST: u8 = 42;
608 }
609 }
610
611 fn main() {
612 test_mod::TestStruct::TEST_CONST
613 }
614 ",
615 );
616 }
617
618 #[test]
619 fn not_applicable_for_imported_trait_for_const() {
620 check_assist_not_applicable(
621 auto_import,
622 r"
623 mod test_mod {
624 pub trait TestTrait {
625 const TEST_CONST: u8;
626 }
627 pub trait TestTrait2 {
628 const TEST_CONST: f64;
629 }
630 pub enum TestEnum {
631 One,
632 Two,
633 }
634 impl TestTrait2 for TestEnum {
635 const TEST_CONST: f64 = 42.0;
636 }
637 impl TestTrait for TestEnum {
638 const TEST_CONST: u8 = 42;
639 }
640 }
641
642 use test_mod::TestTrait2;
643 fn main() {
644 test_mod::TestEnum::TEST_CONST$0;
645 }
646 ",
647 )
648 }
649
650 #[test]
651 fn trait_method() {
652 check_assist(
653 auto_import,
654 r"
655 mod test_mod {
656 pub trait TestTrait {
657 fn test_method(&self);
658 }
659 pub struct TestStruct {}
660 impl TestTrait for TestStruct {
661 fn test_method(&self) {}
662 }
663 }
664
665 fn main() {
666 let test_struct = test_mod::TestStruct {};
667 test_struct.test_meth$0od()
668 }
669 ",
670 r"
671 use test_mod::TestTrait;
672
673 mod test_mod {
674 pub trait TestTrait {
675 fn test_method(&self);
676 }
677 pub struct TestStruct {}
678 impl TestTrait for TestStruct {
679 fn test_method(&self) {}
680 }
681 }
682
683 fn main() {
684 let test_struct = test_mod::TestStruct {};
685 test_struct.test_method()
686 }
687 ",
688 );
689 }
690
691 #[test]
692 fn trait_method_cross_crate() {
693 check_assist(
694 auto_import,
695 r"
696 //- /main.rs crate:main deps:dep
697 fn main() {
698 let test_struct = dep::test_mod::TestStruct {};
699 test_struct.test_meth$0od()
700 }
701 //- /dep.rs crate:dep
702 pub mod test_mod {
703 pub trait TestTrait {
704 fn test_method(&self);
705 }
706 pub struct TestStruct {}
707 impl TestTrait for TestStruct {
708 fn test_method(&self) {}
709 }
710 }
711 ",
712 r"
713 use dep::test_mod::TestTrait;
714
715 fn main() {
716 let test_struct = dep::test_mod::TestStruct {};
717 test_struct.test_method()
718 }
719 ",
720 );
721 }
722
723 #[test]
724 fn assoc_fn_cross_crate() {
725 check_assist(
726 auto_import,
727 r"
728 //- /main.rs crate:main deps:dep
729 fn main() {
730 dep::test_mod::TestStruct::test_func$0tion
731 }
732 //- /dep.rs crate:dep
733 pub mod test_mod {
734 pub trait TestTrait {
735 fn test_function();
736 }
737 pub struct TestStruct {}
738 impl TestTrait for TestStruct {
739 fn test_function() {}
740 }
741 }
742 ",
743 r"
744 use dep::test_mod::TestTrait;
745
746 fn main() {
747 dep::test_mod::TestStruct::test_function
748 }
749 ",
750 );
751 }
752
753 #[test]
754 fn assoc_const_cross_crate() {
755 check_assist(
756 auto_import,
757 r"
758 //- /main.rs crate:main deps:dep
759 fn main() {
760 dep::test_mod::TestStruct::CONST$0
761 }
762 //- /dep.rs crate:dep
763 pub mod test_mod {
764 pub trait TestTrait {
765 const CONST: bool;
766 }
767 pub struct TestStruct {}
768 impl TestTrait for TestStruct {
769 const CONST: bool = true;
770 }
771 }
772 ",
773 r"
774 use dep::test_mod::TestTrait;
775
776 fn main() {
777 dep::test_mod::TestStruct::CONST
778 }
779 ",
780 );
781 }
782
783 #[test]
784 fn assoc_fn_as_method_cross_crate() {
785 check_assist_not_applicable(
786 auto_import,
787 r"
788 //- /main.rs crate:main deps:dep
789 fn main() {
790 let test_struct = dep::test_mod::TestStruct {};
791 test_struct.test_func$0tion()
792 }
793 //- /dep.rs crate:dep
794 pub mod test_mod {
795 pub trait TestTrait {
796 fn test_function();
797 }
798 pub struct TestStruct {}
799 impl TestTrait for TestStruct {
800 fn test_function() {}
801 }
802 }
803 ",
804 );
805 }
806
807 #[test]
808 fn private_trait_cross_crate() {
809 check_assist_not_applicable(
810 auto_import,
811 r"
812 //- /main.rs crate:main deps:dep
813 fn main() {
814 let test_struct = dep::test_mod::TestStruct {};
815 test_struct.test_meth$0od()
816 }
817 //- /dep.rs crate:dep
818 pub mod test_mod {
819 trait TestTrait {
820 fn test_method(&self);
821 }
822 pub struct TestStruct {}
823 impl TestTrait for TestStruct {
824 fn test_method(&self) {}
825 }
826 }
827 ",
828 );
829 }
830
831 #[test]
832 fn not_applicable_for_imported_trait_for_method() {
833 check_assist_not_applicable(
834 auto_import,
835 r"
836 mod test_mod {
837 pub trait TestTrait {
838 fn test_method(&self);
839 }
840 pub trait TestTrait2 {
841 fn test_method(&self);
842 }
843 pub enum TestEnum {
844 One,
845 Two,
846 }
847 impl TestTrait2 for TestEnum {
848 fn test_method(&self) {}
849 }
850 impl TestTrait for TestEnum {
851 fn test_method(&self) {}
852 }
853 }
854
855 use test_mod::TestTrait2;
856 fn main() {
857 let one = test_mod::TestEnum::One;
858 one.test$0_method();
859 }
860 ",
861 )
862 }
863
864 #[test]
865 fn dep_import() {
866 check_assist(
867 auto_import,
868 r"
869//- /lib.rs crate:dep
870pub struct Struct;
871
872//- /main.rs crate:main deps:dep
873fn main() {
874 Struct$0
875}
876",
877 r"use dep::Struct;
878
879fn main() {
880 Struct
881}
882",
883 );
884 }
885
886 #[test]
887 fn whole_segment() {
888 // Tests that only imports whose last segment matches the identifier get suggested.
889 check_assist(
890 auto_import,
891 r"
892//- /lib.rs crate:dep
893pub mod fmt {
894 pub trait Display {}
895}
896
897pub fn panic_fmt() {}
898
899//- /main.rs crate:main deps:dep
900struct S;
901
902impl f$0mt::Display for S {}
903",
904 r"use dep::fmt;
905
906struct S;
907
908impl fmt::Display for S {}
909",
910 );
911 }
912
913 #[test]
914 fn macro_generated() {
915 // Tests that macro-generated items are suggested from external crates.
916 check_assist(
917 auto_import,
918 r"
919//- /lib.rs crate:dep
920macro_rules! mac {
921 () => {
922 pub struct Cheese;
923 };
924}
925
926mac!();
927
928//- /main.rs crate:main deps:dep
929fn main() {
930 Cheese$0;
931}
932",
933 r"use dep::Cheese;
934
935fn main() {
936 Cheese;
937}
938",
939 );
940 }
941
942 #[test]
943 fn casing() {
944 // Tests that differently cased names don't interfere and we only suggest the matching one.
945 check_assist(
946 auto_import,
947 r"
948//- /lib.rs crate:dep
949pub struct FMT;
950pub struct fmt;
951
952//- /main.rs crate:main deps:dep
953fn main() {
954 FMT$0;
955}
956",
957 r"use dep::FMT;
958
959fn main() {
960 FMT;
961}
962",
963 );
964 }
965}
diff --git a/crates/assists/src/handlers/change_visibility.rs b/crates/assists/src/handlers/change_visibility.rs
deleted file mode 100644
index ac8c44124..000000000
--- a/crates/assists/src/handlers/change_visibility.rs
+++ /dev/null
@@ -1,212 +0,0 @@
1use syntax::{
2 ast::{self, NameOwner, VisibilityOwner},
3 AstNode,
4 SyntaxKind::{CONST, ENUM, FN, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, VISIBILITY},
5 T,
6};
7use test_utils::mark;
8
9use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
10
11// Assist: change_visibility
12//
13// Adds or changes existing visibility specifier.
14//
15// ```
16// $0fn frobnicate() {}
17// ```
18// ->
19// ```
20// pub(crate) fn frobnicate() {}
21// ```
22pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
23 if let Some(vis) = ctx.find_node_at_offset::<ast::Visibility>() {
24 return change_vis(acc, vis);
25 }
26 add_vis(acc, ctx)
27}
28
29fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
30 let item_keyword = ctx.token_at_offset().find(|leaf| {
31 matches!(
32 leaf.kind(),
33 T![const]
34 | T![static]
35 | T![fn]
36 | T![mod]
37 | T![struct]
38 | T![enum]
39 | T![trait]
40 | T![type]
41 )
42 });
43
44 let (offset, target) = if let Some(keyword) = item_keyword {
45 let parent = keyword.parent();
46 let def_kws = vec![CONST, STATIC, TYPE_ALIAS, FN, MODULE, STRUCT, ENUM, TRAIT];
47 // Parent is not a definition, can't add visibility
48 if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) {
49 return None;
50 }
51 // Already have visibility, do nothing
52 if parent.children().any(|child| child.kind() == VISIBILITY) {
53 return None;
54 }
55 (vis_offset(&parent), keyword.text_range())
56 } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() {
57 let field = field_name.syntax().ancestors().find_map(ast::RecordField::cast)?;
58 if field.name()? != field_name {
59 mark::hit!(change_visibility_field_false_positive);
60 return None;
61 }
62 if field.visibility().is_some() {
63 return None;
64 }
65 (vis_offset(field.syntax()), field_name.syntax().text_range())
66 } else if let Some(field) = ctx.find_node_at_offset::<ast::TupleField>() {
67 if field.visibility().is_some() {
68 return None;
69 }
70 (vis_offset(field.syntax()), field.syntax().text_range())
71 } else {
72 return None;
73 };
74
75 acc.add(
76 AssistId("change_visibility", AssistKind::RefactorRewrite),
77 "Change visibility to pub(crate)",
78 target,
79 |edit| {
80 edit.insert(offset, "pub(crate) ");
81 },
82 )
83}
84
85fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
86 if vis.syntax().text() == "pub" {
87 let target = vis.syntax().text_range();
88 return acc.add(
89 AssistId("change_visibility", AssistKind::RefactorRewrite),
90 "Change Visibility to pub(crate)",
91 target,
92 |edit| {
93 edit.replace(vis.syntax().text_range(), "pub(crate)");
94 },
95 );
96 }
97 if vis.syntax().text() == "pub(crate)" {
98 let target = vis.syntax().text_range();
99 return acc.add(
100 AssistId("change_visibility", AssistKind::RefactorRewrite),
101 "Change visibility to pub",
102 target,
103 |edit| {
104 edit.replace(vis.syntax().text_range(), "pub");
105 },
106 );
107 }
108 None
109}
110
111#[cfg(test)]
112mod tests {
113 use test_utils::mark;
114
115 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
116
117 use super::*;
118
119 #[test]
120 fn change_visibility_adds_pub_crate_to_items() {
121 check_assist(change_visibility, "$0fn foo() {}", "pub(crate) fn foo() {}");
122 check_assist(change_visibility, "f$0n foo() {}", "pub(crate) fn foo() {}");
123 check_assist(change_visibility, "$0struct Foo {}", "pub(crate) struct Foo {}");
124 check_assist(change_visibility, "$0mod foo {}", "pub(crate) mod foo {}");
125 check_assist(change_visibility, "$0trait Foo {}", "pub(crate) trait Foo {}");
126 check_assist(change_visibility, "m$0od {}", "pub(crate) mod {}");
127 check_assist(change_visibility, "unsafe f$0n foo() {}", "pub(crate) unsafe fn foo() {}");
128 }
129
130 #[test]
131 fn change_visibility_works_with_struct_fields() {
132 check_assist(
133 change_visibility,
134 r"struct S { $0field: u32 }",
135 r"struct S { pub(crate) field: u32 }",
136 );
137 check_assist(change_visibility, r"struct S ( $0u32 )", r"struct S ( pub(crate) u32 )");
138 }
139
140 #[test]
141 fn change_visibility_field_false_positive() {
142 mark::check!(change_visibility_field_false_positive);
143 check_assist_not_applicable(
144 change_visibility,
145 r"struct S { field: [(); { let $0x = ();}] }",
146 )
147 }
148
149 #[test]
150 fn change_visibility_pub_to_pub_crate() {
151 check_assist(change_visibility, "$0pub fn foo() {}", "pub(crate) fn foo() {}")
152 }
153
154 #[test]
155 fn change_visibility_pub_crate_to_pub() {
156 check_assist(change_visibility, "$0pub(crate) fn foo() {}", "pub fn foo() {}")
157 }
158
159 #[test]
160 fn change_visibility_const() {
161 check_assist(change_visibility, "$0const FOO = 3u8;", "pub(crate) const FOO = 3u8;");
162 }
163
164 #[test]
165 fn change_visibility_static() {
166 check_assist(change_visibility, "$0static FOO = 3u8;", "pub(crate) static FOO = 3u8;");
167 }
168
169 #[test]
170 fn change_visibility_type_alias() {
171 check_assist(change_visibility, "$0type T = ();", "pub(crate) type T = ();");
172 }
173
174 #[test]
175 fn change_visibility_handles_comment_attrs() {
176 check_assist(
177 change_visibility,
178 r"
179 /// docs
180
181 // comments
182
183 #[derive(Debug)]
184 $0struct Foo;
185 ",
186 r"
187 /// docs
188
189 // comments
190
191 #[derive(Debug)]
192 pub(crate) struct Foo;
193 ",
194 )
195 }
196
197 #[test]
198 fn not_applicable_for_enum_variants() {
199 check_assist_not_applicable(
200 change_visibility,
201 r"mod foo { pub enum Foo {Foo1} }
202 fn main() { foo::Foo::Foo1$0 } ",
203 );
204 }
205
206 #[test]
207 fn change_visibility_target() {
208 check_assist_target(change_visibility, "$0fn foo() {}", "fn");
209 check_assist_target(change_visibility, "pub(crate)$0 fn foo() {}", "pub(crate)");
210 check_assist_target(change_visibility, "struct S { $0field: u32 }", "field");
211 }
212}
diff --git a/crates/assists/src/handlers/convert_integer_literal.rs b/crates/assists/src/handlers/convert_integer_literal.rs
deleted file mode 100644
index a8a819cfc..000000000
--- a/crates/assists/src/handlers/convert_integer_literal.rs
+++ /dev/null
@@ -1,268 +0,0 @@
1use syntax::{ast, ast::Radix, AstToken};
2
3use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
4
5// Assist: convert_integer_literal
6//
7// Converts the base of integer literals to other bases.
8//
9// ```
10// const _: i32 = 10$0;
11// ```
12// ->
13// ```
14// const _: i32 = 0b1010;
15// ```
16pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
17 let literal = ctx.find_node_at_offset::<ast::Literal>()?;
18 let literal = match literal.kind() {
19 ast::LiteralKind::IntNumber(it) => it,
20 _ => return None,
21 };
22 let radix = literal.radix();
23 let value = literal.value()?;
24 let suffix = literal.suffix();
25
26 let range = literal.syntax().text_range();
27 let group_id = GroupLabel("Convert integer base".into());
28
29 for &target_radix in Radix::ALL {
30 if target_radix == radix {
31 continue;
32 }
33
34 let mut converted = match target_radix {
35 Radix::Binary => format!("0b{:b}", value),
36 Radix::Octal => format!("0o{:o}", value),
37 Radix::Decimal => value.to_string(),
38 Radix::Hexadecimal => format!("0x{:X}", value),
39 };
40
41 let label = format!("Convert {} to {}{}", literal, converted, suffix.unwrap_or_default());
42
43 // Appends the type suffix back into the new literal if it exists.
44 if let Some(suffix) = suffix {
45 converted.push_str(suffix);
46 }
47
48 acc.add_group(
49 &group_id,
50 AssistId("convert_integer_literal", AssistKind::RefactorInline),
51 label,
52 range,
53 |builder| builder.replace(range, converted),
54 );
55 }
56
57 Some(())
58}
59
60#[cfg(test)]
61mod tests {
62 use crate::tests::{check_assist_by_label, check_assist_not_applicable, check_assist_target};
63
64 use super::*;
65
66 #[test]
67 fn binary_target() {
68 check_assist_target(convert_integer_literal, "const _: i32 = 0b1010$0;", "0b1010");
69 }
70
71 #[test]
72 fn octal_target() {
73 check_assist_target(convert_integer_literal, "const _: i32 = 0o12$0;", "0o12");
74 }
75
76 #[test]
77 fn decimal_target() {
78 check_assist_target(convert_integer_literal, "const _: i32 = 10$0;", "10");
79 }
80
81 #[test]
82 fn hexadecimal_target() {
83 check_assist_target(convert_integer_literal, "const _: i32 = 0xA$0;", "0xA");
84 }
85
86 #[test]
87 fn binary_target_with_underscores() {
88 check_assist_target(convert_integer_literal, "const _: i32 = 0b10_10$0;", "0b10_10");
89 }
90
91 #[test]
92 fn octal_target_with_underscores() {
93 check_assist_target(convert_integer_literal, "const _: i32 = 0o1_2$0;", "0o1_2");
94 }
95
96 #[test]
97 fn decimal_target_with_underscores() {
98 check_assist_target(convert_integer_literal, "const _: i32 = 1_0$0;", "1_0");
99 }
100
101 #[test]
102 fn hexadecimal_target_with_underscores() {
103 check_assist_target(convert_integer_literal, "const _: i32 = 0x_A$0;", "0x_A");
104 }
105
106 #[test]
107 fn convert_decimal_integer() {
108 let before = "const _: i32 = 1000$0;";
109
110 check_assist_by_label(
111 convert_integer_literal,
112 before,
113 "const _: i32 = 0b1111101000;",
114 "Convert 1000 to 0b1111101000",
115 );
116
117 check_assist_by_label(
118 convert_integer_literal,
119 before,
120 "const _: i32 = 0o1750;",
121 "Convert 1000 to 0o1750",
122 );
123
124 check_assist_by_label(
125 convert_integer_literal,
126 before,
127 "const _: i32 = 0x3E8;",
128 "Convert 1000 to 0x3E8",
129 );
130 }
131
132 #[test]
133 fn convert_hexadecimal_integer() {
134 let before = "const _: i32 = 0xFF$0;";
135
136 check_assist_by_label(
137 convert_integer_literal,
138 before,
139 "const _: i32 = 0b11111111;",
140 "Convert 0xFF to 0b11111111",
141 );
142
143 check_assist_by_label(
144 convert_integer_literal,
145 before,
146 "const _: i32 = 0o377;",
147 "Convert 0xFF to 0o377",
148 );
149
150 check_assist_by_label(
151 convert_integer_literal,
152 before,
153 "const _: i32 = 255;",
154 "Convert 0xFF to 255",
155 );
156 }
157
158 #[test]
159 fn convert_binary_integer() {
160 let before = "const _: i32 = 0b11111111$0;";
161
162 check_assist_by_label(
163 convert_integer_literal,
164 before,
165 "const _: i32 = 0o377;",
166 "Convert 0b11111111 to 0o377",
167 );
168
169 check_assist_by_label(
170 convert_integer_literal,
171 before,
172 "const _: i32 = 255;",
173 "Convert 0b11111111 to 255",
174 );
175
176 check_assist_by_label(
177 convert_integer_literal,
178 before,
179 "const _: i32 = 0xFF;",
180 "Convert 0b11111111 to 0xFF",
181 );
182 }
183
184 #[test]
185 fn convert_octal_integer() {
186 let before = "const _: i32 = 0o377$0;";
187
188 check_assist_by_label(
189 convert_integer_literal,
190 before,
191 "const _: i32 = 0b11111111;",
192 "Convert 0o377 to 0b11111111",
193 );
194
195 check_assist_by_label(
196 convert_integer_literal,
197 before,
198 "const _: i32 = 255;",
199 "Convert 0o377 to 255",
200 );
201
202 check_assist_by_label(
203 convert_integer_literal,
204 before,
205 "const _: i32 = 0xFF;",
206 "Convert 0o377 to 0xFF",
207 );
208 }
209
210 #[test]
211 fn convert_integer_with_underscores() {
212 let before = "const _: i32 = 1_00_0$0;";
213
214 check_assist_by_label(
215 convert_integer_literal,
216 before,
217 "const _: i32 = 0b1111101000;",
218 "Convert 1_00_0 to 0b1111101000",
219 );
220
221 check_assist_by_label(
222 convert_integer_literal,
223 before,
224 "const _: i32 = 0o1750;",
225 "Convert 1_00_0 to 0o1750",
226 );
227
228 check_assist_by_label(
229 convert_integer_literal,
230 before,
231 "const _: i32 = 0x3E8;",
232 "Convert 1_00_0 to 0x3E8",
233 );
234 }
235
236 #[test]
237 fn convert_integer_with_suffix() {
238 let before = "const _: i32 = 1000i32$0;";
239
240 check_assist_by_label(
241 convert_integer_literal,
242 before,
243 "const _: i32 = 0b1111101000i32;",
244 "Convert 1000i32 to 0b1111101000i32",
245 );
246
247 check_assist_by_label(
248 convert_integer_literal,
249 before,
250 "const _: i32 = 0o1750i32;",
251 "Convert 1000i32 to 0o1750i32",
252 );
253
254 check_assist_by_label(
255 convert_integer_literal,
256 before,
257 "const _: i32 = 0x3E8i32;",
258 "Convert 1000i32 to 0x3E8i32",
259 );
260 }
261
262 #[test]
263 fn convert_overflowing_literal() {
264 let before = "const _: i32 =
265 111111111111111111111111111111111111111111111111111111111111111111111111$0;";
266 check_assist_not_applicable(convert_integer_literal, before);
267 }
268}
diff --git a/crates/assists/src/handlers/early_return.rs b/crates/assists/src/handlers/early_return.rs
deleted file mode 100644
index 8bbbb7ed5..000000000
--- a/crates/assists/src/handlers/early_return.rs
+++ /dev/null
@@ -1,515 +0,0 @@
1use std::{iter::once, ops::RangeInclusive};
2
3use syntax::{
4 algo::replace_children,
5 ast::{
6 self,
7 edit::{AstNodeEdit, IndentLevel},
8 make,
9 },
10 AstNode,
11 SyntaxKind::{FN, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE},
12 SyntaxNode,
13};
14
15use crate::{
16 assist_context::{AssistContext, Assists},
17 utils::invert_boolean_expression,
18 AssistId, AssistKind,
19};
20
21// Assist: convert_to_guarded_return
22//
23// Replace a large conditional with a guarded return.
24//
25// ```
26// fn main() {
27// $0if cond {
28// foo();
29// bar();
30// }
31// }
32// ```
33// ->
34// ```
35// fn main() {
36// if !cond {
37// return;
38// }
39// foo();
40// bar();
41// }
42// ```
43pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
44 let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
45 if if_expr.else_branch().is_some() {
46 return None;
47 }
48
49 let cond = if_expr.condition()?;
50
51 // Check if there is an IfLet that we can handle.
52 let if_let_pat = match cond.pat() {
53 None => None, // No IfLet, supported.
54 Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => {
55 let path = pat.path()?;
56 match path.qualifier() {
57 None => {
58 let bound_ident = pat.fields().next().unwrap();
59 Some((path, bound_ident))
60 }
61 Some(_) => return None,
62 }
63 }
64 Some(_) => return None, // Unsupported IfLet.
65 };
66
67 let cond_expr = cond.expr()?;
68 let then_block = if_expr.then_branch()?;
69
70 let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;
71
72 if parent_block.tail_expr()? != if_expr.clone().into() {
73 return None;
74 }
75
76 // check for early return and continue
77 let first_in_then_block = then_block.syntax().first_child()?;
78 if ast::ReturnExpr::can_cast(first_in_then_block.kind())
79 || ast::ContinueExpr::can_cast(first_in_then_block.kind())
80 || first_in_then_block
81 .children()
82 .any(|x| ast::ReturnExpr::can_cast(x.kind()) || ast::ContinueExpr::can_cast(x.kind()))
83 {
84 return None;
85 }
86
87 let parent_container = parent_block.syntax().parent()?;
88
89 let early_expression: ast::Expr = match parent_container.kind() {
90 WHILE_EXPR | LOOP_EXPR => make::expr_continue(),
91 FN => make::expr_return(),
92 _ => return None,
93 };
94
95 if then_block.syntax().first_child_or_token().map(|t| t.kind() == L_CURLY).is_none() {
96 return None;
97 }
98
99 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;
100
101 let target = if_expr.syntax().text_range();
102 acc.add(
103 AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite),
104 "Convert to guarded return",
105 target,
106 |edit| {
107 let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
108 let new_block = match if_let_pat {
109 None => {
110 // If.
111 let new_expr = {
112 let then_branch =
113 make::block_expr(once(make::expr_stmt(early_expression).into()), None);
114 let cond = invert_boolean_expression(cond_expr);
115 make::expr_if(make::condition(cond, None), then_branch, None)
116 .indent(if_indent_level)
117 };
118 replace(new_expr.syntax(), &then_block, &parent_block, &if_expr)
119 }
120 Some((path, bound_ident)) => {
121 // If-let.
122 let match_expr = {
123 let happy_arm = {
124 let pat = make::tuple_struct_pat(
125 path,
126 once(make::ident_pat(make::name("it")).into()),
127 );
128 let expr = {
129 let name_ref = make::name_ref("it");
130 let segment = make::path_segment(name_ref);
131 let path = make::path_unqualified(segment);
132 make::expr_path(path)
133 };
134 make::match_arm(once(pat.into()), expr)
135 };
136
137 let sad_arm = make::match_arm(
138 // FIXME: would be cool to use `None` or `Err(_)` if appropriate
139 once(make::wildcard_pat().into()),
140 early_expression,
141 );
142
143 make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
144 };
145
146 let let_stmt = make::let_stmt(
147 make::ident_pat(make::name(&bound_ident.syntax().to_string())).into(),
148 Some(match_expr),
149 );
150 let let_stmt = let_stmt.indent(if_indent_level);
151 replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr)
152 }
153 };
154 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());
155
156 fn replace(
157 new_expr: &SyntaxNode,
158 then_block: &ast::BlockExpr,
159 parent_block: &ast::BlockExpr,
160 if_expr: &ast::IfExpr,
161 ) -> SyntaxNode {
162 let then_block_items = then_block.dedent(IndentLevel(1));
163 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
164 let end_of_then =
165 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
166 end_of_then.prev_sibling_or_token().unwrap()
167 } else {
168 end_of_then
169 };
170 let mut then_statements = new_expr.children_with_tokens().chain(
171 then_block_items
172 .syntax()
173 .children_with_tokens()
174 .skip(1)
175 .take_while(|i| *i != end_of_then),
176 );
177 replace_children(
178 &parent_block.syntax(),
179 RangeInclusive::new(
180 if_expr.clone().syntax().clone().into(),
181 if_expr.syntax().clone().into(),
182 ),
183 &mut then_statements,
184 )
185 }
186 },
187 )
188}
189
190#[cfg(test)]
191mod tests {
192 use crate::tests::{check_assist, check_assist_not_applicable};
193
194 use super::*;
195
196 #[test]
197 fn convert_inside_fn() {
198 check_assist(
199 convert_to_guarded_return,
200 r#"
201 fn main() {
202 bar();
203 if$0 true {
204 foo();
205
206 //comment
207 bar();
208 }
209 }
210 "#,
211 r#"
212 fn main() {
213 bar();
214 if !true {
215 return;
216 }
217 foo();
218
219 //comment
220 bar();
221 }
222 "#,
223 );
224 }
225
226 #[test]
227 fn convert_let_inside_fn() {
228 check_assist(
229 convert_to_guarded_return,
230 r#"
231 fn main(n: Option<String>) {
232 bar();
233 if$0 let Some(n) = n {
234 foo(n);
235
236 //comment
237 bar();
238 }
239 }
240 "#,
241 r#"
242 fn main(n: Option<String>) {
243 bar();
244 let n = match n {
245 Some(it) => it,
246 _ => return,
247 };
248 foo(n);
249
250 //comment
251 bar();
252 }
253 "#,
254 );
255 }
256
257 #[test]
258 fn convert_if_let_result() {
259 check_assist(
260 convert_to_guarded_return,
261 r#"
262 fn main() {
263 if$0 let Ok(x) = Err(92) {
264 foo(x);
265 }
266 }
267 "#,
268 r#"
269 fn main() {
270 let x = match Err(92) {
271 Ok(it) => it,
272 _ => return,
273 };
274 foo(x);
275 }
276 "#,
277 );
278 }
279
280 #[test]
281 fn convert_let_ok_inside_fn() {
282 check_assist(
283 convert_to_guarded_return,
284 r#"
285 fn main(n: Option<String>) {
286 bar();
287 if$0 let Ok(n) = n {
288 foo(n);
289
290 //comment
291 bar();
292 }
293 }
294 "#,
295 r#"
296 fn main(n: Option<String>) {
297 bar();
298 let n = match n {
299 Ok(it) => it,
300 _ => return,
301 };
302 foo(n);
303
304 //comment
305 bar();
306 }
307 "#,
308 );
309 }
310
311 #[test]
312 fn convert_inside_while() {
313 check_assist(
314 convert_to_guarded_return,
315 r#"
316 fn main() {
317 while true {
318 if$0 true {
319 foo();
320 bar();
321 }
322 }
323 }
324 "#,
325 r#"
326 fn main() {
327 while true {
328 if !true {
329 continue;
330 }
331 foo();
332 bar();
333 }
334 }
335 "#,
336 );
337 }
338
339 #[test]
340 fn convert_let_inside_while() {
341 check_assist(
342 convert_to_guarded_return,
343 r#"
344 fn main() {
345 while true {
346 if$0 let Some(n) = n {
347 foo(n);
348 bar();
349 }
350 }
351 }
352 "#,
353 r#"
354 fn main() {
355 while true {
356 let n = match n {
357 Some(it) => it,
358 _ => continue,
359 };
360 foo(n);
361 bar();
362 }
363 }
364 "#,
365 );
366 }
367
368 #[test]
369 fn convert_inside_loop() {
370 check_assist(
371 convert_to_guarded_return,
372 r#"
373 fn main() {
374 loop {
375 if$0 true {
376 foo();
377 bar();
378 }
379 }
380 }
381 "#,
382 r#"
383 fn main() {
384 loop {
385 if !true {
386 continue;
387 }
388 foo();
389 bar();
390 }
391 }
392 "#,
393 );
394 }
395
396 #[test]
397 fn convert_let_inside_loop() {
398 check_assist(
399 convert_to_guarded_return,
400 r#"
401 fn main() {
402 loop {
403 if$0 let Some(n) = n {
404 foo(n);
405 bar();
406 }
407 }
408 }
409 "#,
410 r#"
411 fn main() {
412 loop {
413 let n = match n {
414 Some(it) => it,
415 _ => continue,
416 };
417 foo(n);
418 bar();
419 }
420 }
421 "#,
422 );
423 }
424
425 #[test]
426 fn ignore_already_converted_if() {
427 check_assist_not_applicable(
428 convert_to_guarded_return,
429 r#"
430 fn main() {
431 if$0 true {
432 return;
433 }
434 }
435 "#,
436 );
437 }
438
439 #[test]
440 fn ignore_already_converted_loop() {
441 check_assist_not_applicable(
442 convert_to_guarded_return,
443 r#"
444 fn main() {
445 loop {
446 if$0 true {
447 continue;
448 }
449 }
450 }
451 "#,
452 );
453 }
454
455 #[test]
456 fn ignore_return() {
457 check_assist_not_applicable(
458 convert_to_guarded_return,
459 r#"
460 fn main() {
461 if$0 true {
462 return
463 }
464 }
465 "#,
466 );
467 }
468
469 #[test]
470 fn ignore_else_branch() {
471 check_assist_not_applicable(
472 convert_to_guarded_return,
473 r#"
474 fn main() {
475 if$0 true {
476 foo();
477 } else {
478 bar()
479 }
480 }
481 "#,
482 );
483 }
484
485 #[test]
486 fn ignore_statements_aftert_if() {
487 check_assist_not_applicable(
488 convert_to_guarded_return,
489 r#"
490 fn main() {
491 if$0 true {
492 foo();
493 }
494 bar();
495 }
496 "#,
497 );
498 }
499
500 #[test]
501 fn ignore_statements_inside_if() {
502 check_assist_not_applicable(
503 convert_to_guarded_return,
504 r#"
505 fn main() {
506 if false {
507 if$0 true {
508 foo();
509 }
510 }
511 }
512 "#,
513 );
514 }
515}
diff --git a/crates/assists/src/handlers/expand_glob_import.rs b/crates/assists/src/handlers/expand_glob_import.rs
deleted file mode 100644
index 5fe617ba4..000000000
--- a/crates/assists/src/handlers/expand_glob_import.rs
+++ /dev/null
@@ -1,907 +0,0 @@
1use either::Either;
2use hir::{AssocItem, MacroDef, Module, ModuleDef, Name, PathResolution, ScopeDef};
3use ide_db::{
4 defs::{Definition, NameRefClass},
5 search::SearchScope,
6};
7use syntax::{
8 algo::SyntaxRewriter,
9 ast::{self, make},
10 AstNode, Direction, SyntaxNode, SyntaxToken, T,
11};
12
13use crate::{
14 assist_context::{AssistContext, Assists},
15 AssistId, AssistKind,
16};
17
18// Assist: expand_glob_import
19//
20// Expands glob imports.
21//
22// ```
23// mod foo {
24// pub struct Bar;
25// pub struct Baz;
26// }
27//
28// use foo::*$0;
29//
30// fn qux(bar: Bar, baz: Baz) {}
31// ```
32// ->
33// ```
34// mod foo {
35// pub struct Bar;
36// pub struct Baz;
37// }
38//
39// use foo::{Baz, Bar};
40//
41// fn qux(bar: Bar, baz: Baz) {}
42// ```
43pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
44 let star = ctx.find_token_syntax_at_offset(T![*])?;
45 let (parent, mod_path) = find_parent_and_path(&star)?;
46 let target_module = match ctx.sema.resolve_path(&mod_path)? {
47 PathResolution::Def(ModuleDef::Module(it)) => it,
48 _ => return None,
49 };
50
51 let current_scope = ctx.sema.scope(&star.parent());
52 let current_module = current_scope.module()?;
53
54 let refs_in_target = find_refs_in_mod(ctx, target_module, Some(current_module))?;
55 let imported_defs = find_imported_defs(ctx, star)?;
56 let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs);
57
58 let target = parent.clone().either(|n| n.syntax().clone(), |n| n.syntax().clone());
59 acc.add(
60 AssistId("expand_glob_import", AssistKind::RefactorRewrite),
61 "Expand glob import",
62 target.text_range(),
63 |builder| {
64 let mut rewriter = SyntaxRewriter::default();
65 replace_ast(&mut rewriter, parent, mod_path, names_to_import);
66 builder.rewrite(rewriter);
67 },
68 )
69}
70
71fn find_parent_and_path(
72 star: &SyntaxToken,
73) -> Option<(Either<ast::UseTree, ast::UseTreeList>, ast::Path)> {
74 return star.ancestors().find_map(|n| {
75 find_use_tree_list(n.clone())
76 .and_then(|(u, p)| Some((Either::Right(u), p)))
77 .or_else(|| find_use_tree(n).and_then(|(u, p)| Some((Either::Left(u), p))))
78 });
79
80 fn find_use_tree_list(n: SyntaxNode) -> Option<(ast::UseTreeList, ast::Path)> {
81 let use_tree_list = ast::UseTreeList::cast(n)?;
82 let path = use_tree_list.parent_use_tree().path()?;
83 Some((use_tree_list, path))
84 }
85
86 fn find_use_tree(n: SyntaxNode) -> Option<(ast::UseTree, ast::Path)> {
87 let use_tree = ast::UseTree::cast(n)?;
88 let path = use_tree.path()?;
89 Some((use_tree, path))
90 }
91}
92
93#[derive(Debug, PartialEq, Clone)]
94enum Def {
95 ModuleDef(ModuleDef),
96 MacroDef(MacroDef),
97}
98
99impl Def {
100 fn is_referenced_in(&self, ctx: &AssistContext) -> bool {
101 let def = match self {
102 Def::ModuleDef(def) => Definition::ModuleDef(*def),
103 Def::MacroDef(def) => Definition::Macro(*def),
104 };
105
106 let search_scope = SearchScope::single_file(ctx.frange.file_id);
107 def.usages(&ctx.sema).in_scope(search_scope).at_least_one()
108 }
109}
110
111#[derive(Debug, Clone)]
112struct Ref {
113 // could be alias
114 visible_name: Name,
115 def: Def,
116}
117
118impl Ref {
119 fn from_scope_def(name: Name, scope_def: ScopeDef) -> Option<Self> {
120 match scope_def {
121 ScopeDef::ModuleDef(def) => Some(Ref { visible_name: name, def: Def::ModuleDef(def) }),
122 ScopeDef::MacroDef(def) => Some(Ref { visible_name: name, def: Def::MacroDef(def) }),
123 _ => None,
124 }
125 }
126}
127
128#[derive(Debug, Clone)]
129struct Refs(Vec<Ref>);
130
131impl Refs {
132 fn used_refs(&self, ctx: &AssistContext) -> Refs {
133 Refs(
134 self.0
135 .clone()
136 .into_iter()
137 .filter(|r| {
138 if let Def::ModuleDef(ModuleDef::Trait(tr)) = r.def {
139 if tr
140 .items(ctx.db())
141 .into_iter()
142 .find(|ai| {
143 if let AssocItem::Function(f) = *ai {
144 Def::ModuleDef(ModuleDef::Function(f)).is_referenced_in(ctx)
145 } else {
146 false
147 }
148 })
149 .is_some()
150 {
151 return true;
152 }
153 }
154
155 r.def.is_referenced_in(ctx)
156 })
157 .collect(),
158 )
159 }
160
161 fn filter_out_by_defs(&self, defs: Vec<Def>) -> Refs {
162 Refs(self.0.clone().into_iter().filter(|r| !defs.contains(&r.def)).collect())
163 }
164}
165
166fn find_refs_in_mod(
167 ctx: &AssistContext,
168 module: Module,
169 visible_from: Option<Module>,
170) -> Option<Refs> {
171 if let Some(from) = visible_from {
172 if !is_mod_visible_from(ctx, module, from) {
173 return None;
174 }
175 }
176
177 let module_scope = module.scope(ctx.db(), visible_from);
178 let refs = module_scope.into_iter().filter_map(|(n, d)| Ref::from_scope_def(n, d)).collect();
179 Some(Refs(refs))
180}
181
182fn is_mod_visible_from(ctx: &AssistContext, module: Module, from: Module) -> bool {
183 match module.parent(ctx.db()) {
184 Some(parent) => {
185 parent.visibility_of(ctx.db(), &ModuleDef::Module(module)).map_or(true, |vis| {
186 vis.is_visible_from(ctx.db(), from.into()) && is_mod_visible_from(ctx, parent, from)
187 })
188 }
189 None => true,
190 }
191}
192
193// looks for name refs in parent use block's siblings
194//
195// mod bar {
196// mod qux {
197// struct Qux;
198// }
199//
200// pub use qux::Qux;
201// }
202//
203// ↓ ---------------
204// use foo::*$0;
205// use baz::Baz;
206// ↑ ---------------
207fn find_imported_defs(ctx: &AssistContext, star: SyntaxToken) -> Option<Vec<Def>> {
208 let parent_use_item_syntax =
209 star.ancestors().find_map(|n| if ast::Use::can_cast(n.kind()) { Some(n) } else { None })?;
210
211 Some(
212 [Direction::Prev, Direction::Next]
213 .iter()
214 .map(|dir| {
215 parent_use_item_syntax
216 .siblings(dir.to_owned())
217 .filter(|n| ast::Use::can_cast(n.kind()))
218 })
219 .flatten()
220 .filter_map(|n| Some(n.descendants().filter_map(ast::NameRef::cast)))
221 .flatten()
222 .filter_map(|r| match NameRefClass::classify(&ctx.sema, &r)? {
223 NameRefClass::Definition(Definition::ModuleDef(def)) => Some(Def::ModuleDef(def)),
224 NameRefClass::Definition(Definition::Macro(def)) => Some(Def::MacroDef(def)),
225 _ => None,
226 })
227 .collect(),
228 )
229}
230
231fn find_names_to_import(
232 ctx: &AssistContext,
233 refs_in_target: Refs,
234 imported_defs: Vec<Def>,
235) -> Vec<Name> {
236 let used_refs = refs_in_target.used_refs(ctx).filter_out_by_defs(imported_defs);
237 used_refs.0.iter().map(|r| r.visible_name.clone()).collect()
238}
239
240fn replace_ast(
241 rewriter: &mut SyntaxRewriter,
242 parent: Either<ast::UseTree, ast::UseTreeList>,
243 path: ast::Path,
244 names_to_import: Vec<Name>,
245) {
246 let existing_use_trees = match parent.clone() {
247 Either::Left(_) => vec![],
248 Either::Right(u) => u
249 .use_trees()
250 .filter(|n|
251 // filter out star
252 n.star_token().is_none())
253 .collect(),
254 };
255
256 let new_use_trees: Vec<ast::UseTree> = names_to_import
257 .iter()
258 .map(|n| {
259 let path = make::path_unqualified(make::path_segment(make::name_ref(&n.to_string())));
260 make::use_tree(path, None, None, false)
261 })
262 .collect();
263
264 let use_trees = [&existing_use_trees[..], &new_use_trees[..]].concat();
265
266 match use_trees.as_slice() {
267 [name] => {
268 if let Some(end_path) = name.path() {
269 rewriter.replace_ast(
270 &parent.left_or_else(|tl| tl.parent_use_tree()),
271 &make::use_tree(make::path_concat(path, end_path), None, None, false),
272 );
273 }
274 }
275 names => match &parent {
276 Either::Left(parent) => rewriter.replace_ast(
277 parent,
278 &make::use_tree(path, Some(make::use_tree_list(names.to_owned())), None, false),
279 ),
280 Either::Right(parent) => {
281 rewriter.replace_ast(parent, &make::use_tree_list(names.to_owned()))
282 }
283 },
284 };
285}
286
287#[cfg(test)]
288mod tests {
289 use crate::tests::{check_assist, check_assist_not_applicable};
290
291 use super::*;
292
293 #[test]
294 fn expanding_glob_import() {
295 check_assist(
296 expand_glob_import,
297 r"
298mod foo {
299 pub struct Bar;
300 pub struct Baz;
301 pub struct Qux;
302
303 pub fn f() {}
304}
305
306use foo::*$0;
307
308fn qux(bar: Bar, baz: Baz) {
309 f();
310}
311",
312 r"
313mod foo {
314 pub struct Bar;
315 pub struct Baz;
316 pub struct Qux;
317
318 pub fn f() {}
319}
320
321use foo::{Baz, Bar, f};
322
323fn qux(bar: Bar, baz: Baz) {
324 f();
325}
326",
327 )
328 }
329
330 #[test]
331 fn expanding_glob_import_with_existing_explicit_names() {
332 check_assist(
333 expand_glob_import,
334 r"
335mod foo {
336 pub struct Bar;
337 pub struct Baz;
338 pub struct Qux;
339
340 pub fn f() {}
341}
342
343use foo::{*$0, f};
344
345fn qux(bar: Bar, baz: Baz) {
346 f();
347}
348",
349 r"
350mod foo {
351 pub struct Bar;
352 pub struct Baz;
353 pub struct Qux;
354
355 pub fn f() {}
356}
357
358use foo::{f, Baz, Bar};
359
360fn qux(bar: Bar, baz: Baz) {
361 f();
362}
363",
364 )
365 }
366
367 #[test]
368 fn expanding_glob_import_with_existing_uses_in_same_module() {
369 check_assist(
370 expand_glob_import,
371 r"
372mod foo {
373 pub struct Bar;
374 pub struct Baz;
375 pub struct Qux;
376
377 pub fn f() {}
378}
379
380use foo::Bar;
381use foo::{*$0, f};
382
383fn qux(bar: Bar, baz: Baz) {
384 f();
385}
386",
387 r"
388mod foo {
389 pub struct Bar;
390 pub struct Baz;
391 pub struct Qux;
392
393 pub fn f() {}
394}
395
396use foo::Bar;
397use foo::{f, Baz};
398
399fn qux(bar: Bar, baz: Baz) {
400 f();
401}
402",
403 )
404 }
405
406 #[test]
407 fn expanding_nested_glob_import() {
408 check_assist(
409 expand_glob_import,
410 r"
411mod foo {
412 pub mod bar {
413 pub struct Bar;
414 pub struct Baz;
415 pub struct Qux;
416
417 pub fn f() {}
418 }
419
420 pub mod baz {
421 pub fn g() {}
422 }
423}
424
425use foo::{bar::{*$0, f}, baz::*};
426
427fn qux(bar: Bar, baz: Baz) {
428 f();
429 g();
430}
431",
432 r"
433mod foo {
434 pub mod bar {
435 pub struct Bar;
436 pub struct Baz;
437 pub struct Qux;
438
439 pub fn f() {}
440 }
441
442 pub mod baz {
443 pub fn g() {}
444 }
445}
446
447use foo::{bar::{f, Baz, Bar}, baz::*};
448
449fn qux(bar: Bar, baz: Baz) {
450 f();
451 g();
452}
453",
454 );
455
456 check_assist(
457 expand_glob_import,
458 r"
459mod foo {
460 pub mod bar {
461 pub struct Bar;
462 pub struct Baz;
463 pub struct Qux;
464
465 pub fn f() {}
466 }
467
468 pub mod baz {
469 pub fn g() {}
470 }
471}
472
473use foo::{bar::{Bar, Baz, f}, baz::*$0};
474
475fn qux(bar: Bar, baz: Baz) {
476 f();
477 g();
478}
479",
480 r"
481mod foo {
482 pub mod bar {
483 pub struct Bar;
484 pub struct Baz;
485 pub struct Qux;
486
487 pub fn f() {}
488 }
489
490 pub mod baz {
491 pub fn g() {}
492 }
493}
494
495use foo::{bar::{Bar, Baz, f}, baz::g};
496
497fn qux(bar: Bar, baz: Baz) {
498 f();
499 g();
500}
501",
502 );
503
504 check_assist(
505 expand_glob_import,
506 r"
507mod foo {
508 pub mod bar {
509 pub struct Bar;
510 pub struct Baz;
511 pub struct Qux;
512
513 pub fn f() {}
514 }
515
516 pub mod baz {
517 pub fn g() {}
518
519 pub mod qux {
520 pub fn h() {}
521 pub fn m() {}
522
523 pub mod q {
524 pub fn j() {}
525 }
526 }
527 }
528}
529
530use foo::{
531 bar::{*, f},
532 baz::{g, qux::*$0}
533};
534
535fn qux(bar: Bar, baz: Baz) {
536 f();
537 g();
538 h();
539 q::j();
540}
541",
542 r"
543mod foo {
544 pub mod bar {
545 pub struct Bar;
546 pub struct Baz;
547 pub struct Qux;
548
549 pub fn f() {}
550 }
551
552 pub mod baz {
553 pub fn g() {}
554
555 pub mod qux {
556 pub fn h() {}
557 pub fn m() {}
558
559 pub mod q {
560 pub fn j() {}
561 }
562 }
563 }
564}
565
566use foo::{
567 bar::{*, f},
568 baz::{g, qux::{q, h}}
569};
570
571fn qux(bar: Bar, baz: Baz) {
572 f();
573 g();
574 h();
575 q::j();
576}
577",
578 );
579
580 check_assist(
581 expand_glob_import,
582 r"
583mod foo {
584 pub mod bar {
585 pub struct Bar;
586 pub struct Baz;
587 pub struct Qux;
588
589 pub fn f() {}
590 }
591
592 pub mod baz {
593 pub fn g() {}
594
595 pub mod qux {
596 pub fn h() {}
597 pub fn m() {}
598
599 pub mod q {
600 pub fn j() {}
601 }
602 }
603 }
604}
605
606use foo::{
607 bar::{*, f},
608 baz::{g, qux::{h, q::*$0}}
609};
610
611fn qux(bar: Bar, baz: Baz) {
612 f();
613 g();
614 h();
615 j();
616}
617",
618 r"
619mod foo {
620 pub mod bar {
621 pub struct Bar;
622 pub struct Baz;
623 pub struct Qux;
624
625 pub fn f() {}
626 }
627
628 pub mod baz {
629 pub fn g() {}
630
631 pub mod qux {
632 pub fn h() {}
633 pub fn m() {}
634
635 pub mod q {
636 pub fn j() {}
637 }
638 }
639 }
640}
641
642use foo::{
643 bar::{*, f},
644 baz::{g, qux::{h, q::j}}
645};
646
647fn qux(bar: Bar, baz: Baz) {
648 f();
649 g();
650 h();
651 j();
652}
653",
654 );
655
656 check_assist(
657 expand_glob_import,
658 r"
659mod foo {
660 pub mod bar {
661 pub struct Bar;
662 pub struct Baz;
663 pub struct Qux;
664
665 pub fn f() {}
666 }
667
668 pub mod baz {
669 pub fn g() {}
670
671 pub mod qux {
672 pub fn h() {}
673 pub fn m() {}
674
675 pub mod q {
676 pub fn j() {}
677 }
678 }
679 }
680}
681
682use foo::{
683 bar::{*, f},
684 baz::{g, qux::{q::j, *$0}}
685};
686
687fn qux(bar: Bar, baz: Baz) {
688 f();
689 g();
690 h();
691 j();
692}
693",
694 r"
695mod foo {
696 pub mod bar {
697 pub struct Bar;
698 pub struct Baz;
699 pub struct Qux;
700
701 pub fn f() {}
702 }
703
704 pub mod baz {
705 pub fn g() {}
706
707 pub mod qux {
708 pub fn h() {}
709 pub fn m() {}
710
711 pub mod q {
712 pub fn j() {}
713 }
714 }
715 }
716}
717
718use foo::{
719 bar::{*, f},
720 baz::{g, qux::{q::j, h}}
721};
722
723fn qux(bar: Bar, baz: Baz) {
724 f();
725 g();
726 h();
727 j();
728}
729",
730 );
731 }
732
733 #[test]
734 fn expanding_glob_import_with_macro_defs() {
735 // FIXME: this is currently fails because `Definition::find_usages` ignores macros
736 // https://github.com/rust-analyzer/rust-analyzer/issues/3484
737 //
738 // check_assist(
739 // expand_glob_import,
740 // r"
741 // //- /lib.rs crate:foo
742 // #[macro_export]
743 // macro_rules! bar {
744 // () => ()
745 // }
746
747 // pub fn baz() {}
748
749 // //- /main.rs crate:main deps:foo
750 // use foo::*$0;
751
752 // fn main() {
753 // bar!();
754 // baz();
755 // }
756 // ",
757 // r"
758 // use foo::{bar, baz};
759
760 // fn main() {
761 // bar!();
762 // baz();
763 // }
764 // ",
765 // )
766 }
767
768 #[test]
769 fn expanding_glob_import_with_trait_method_uses() {
770 check_assist(
771 expand_glob_import,
772 r"
773//- /lib.rs crate:foo
774pub trait Tr {
775 fn method(&self) {}
776}
777impl Tr for () {}
778
779//- /main.rs crate:main deps:foo
780use foo::*$0;
781
782fn main() {
783 ().method();
784}
785",
786 r"
787use foo::Tr;
788
789fn main() {
790 ().method();
791}
792",
793 );
794
795 check_assist(
796 expand_glob_import,
797 r"
798//- /lib.rs crate:foo
799pub trait Tr {
800 fn method(&self) {}
801}
802impl Tr for () {}
803
804pub trait Tr2 {
805 fn method2(&self) {}
806}
807impl Tr2 for () {}
808
809//- /main.rs crate:main deps:foo
810use foo::*$0;
811
812fn main() {
813 ().method();
814}
815",
816 r"
817use foo::Tr;
818
819fn main() {
820 ().method();
821}
822",
823 );
824 }
825
826 #[test]
827 fn expanding_is_not_applicable_if_target_module_is_not_accessible_from_current_scope() {
828 check_assist_not_applicable(
829 expand_glob_import,
830 r"
831mod foo {
832 mod bar {
833 pub struct Bar;
834 }
835}
836
837use foo::bar::*$0;
838
839fn baz(bar: Bar) {}
840",
841 );
842
843 check_assist_not_applicable(
844 expand_glob_import,
845 r"
846mod foo {
847 mod bar {
848 pub mod baz {
849 pub struct Baz;
850 }
851 }
852}
853
854use foo::bar::baz::*$0;
855
856fn qux(baz: Baz) {}
857",
858 );
859 }
860
861 #[test]
862 fn expanding_is_not_applicable_if_cursor_is_not_in_star_token() {
863 check_assist_not_applicable(
864 expand_glob_import,
865 r"
866 mod foo {
867 pub struct Bar;
868 pub struct Baz;
869 pub struct Qux;
870 }
871
872 use foo::Bar$0;
873
874 fn qux(bar: Bar, baz: Baz) {}
875 ",
876 )
877 }
878
879 #[test]
880 fn expanding_glob_import_single_nested_glob_only() {
881 check_assist(
882 expand_glob_import,
883 r"
884mod foo {
885 pub struct Bar;
886}
887
888use foo::{*$0};
889
890struct Baz {
891 bar: Bar
892}
893",
894 r"
895mod foo {
896 pub struct Bar;
897}
898
899use foo::Bar;
900
901struct Baz {
902 bar: Bar
903}
904",
905 );
906 }
907}
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
deleted file mode 100644
index e3ef04932..000000000
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ /dev/null
@@ -1,520 +0,0 @@
1use std::iter;
2
3use either::Either;
4use hir::{AsName, Module, ModuleDef, Name, Variant};
5use ide_db::{
6 defs::Definition,
7 helpers::{
8 insert_use::{insert_use, ImportScope},
9 mod_path_to_ast,
10 },
11 search::FileReference,
12 RootDatabase,
13};
14use rustc_hash::FxHashSet;
15use syntax::{
16 algo::{find_node_at_offset, SyntaxRewriter},
17 ast::{self, edit::IndentLevel, make, AstNode, NameOwner, VisibilityOwner},
18 SourceFile, SyntaxElement, SyntaxNode, T,
19};
20
21use crate::{AssistContext, AssistId, AssistKind, Assists};
22
23// Assist: extract_struct_from_enum_variant
24//
25// Extracts a struct from enum variant.
26//
27// ```
28// enum A { $0One(u32, u32) }
29// ```
30// ->
31// ```
32// struct One(pub u32, pub u32);
33//
34// enum A { One(One) }
35// ```
36pub(crate) fn extract_struct_from_enum_variant(
37 acc: &mut Assists,
38 ctx: &AssistContext,
39) -> Option<()> {
40 let variant = ctx.find_node_at_offset::<ast::Variant>()?;
41 let field_list = extract_field_list_if_applicable(&variant)?;
42
43 let variant_name = variant.name()?;
44 let variant_hir = ctx.sema.to_def(&variant)?;
45 if existing_definition(ctx.db(), &variant_name, &variant_hir) {
46 return None;
47 }
48
49 let enum_ast = variant.parent_enum();
50 let enum_hir = ctx.sema.to_def(&enum_ast)?;
51 let target = variant.syntax().text_range();
52 acc.add(
53 AssistId("extract_struct_from_enum_variant", AssistKind::RefactorRewrite),
54 "Extract struct from enum variant",
55 target,
56 |builder| {
57 let variant_hir_name = variant_hir.name(ctx.db());
58 let enum_module_def = ModuleDef::from(enum_hir);