aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/assists/src/assist_context.rs38
-rw-r--r--crates/assists/src/handlers/add_custom_impl.rs284
-rw-r--r--crates/assists/src/handlers/add_missing_impl_members.rs92
-rw-r--r--crates/assists/src/handlers/change_return_type_to_result.rs1091
-rw-r--r--crates/assists/src/handlers/convert_integer_literal.rs370
-rw-r--r--crates/assists/src/handlers/expand_glob_import.rs76
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs67
-rw-r--r--crates/assists/src/handlers/reorder_fields.rs4
-rw-r--r--crates/assists/src/handlers/replace_derive_with_manual_impl.rs398
-rw-r--r--crates/assists/src/handlers/wrap_return_type_in_result.rs1158
-rw-r--r--crates/assists/src/lib.rs8
-rw-r--r--crates/assists/src/tests.rs25
-rw-r--r--crates/assists/src/tests/generated.rs68
-rw-r--r--crates/assists/src/utils.rs92
-rw-r--r--crates/completion/src/completions/postfix.rs12
-rw-r--r--crates/ide/src/syntax_highlighting.rs9
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs4
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html56
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html8
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_injection.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html12
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html30
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html6
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs28
-rw-r--r--crates/rust-analyzer/src/to_proto.rs1
-rw-r--r--crates/syntax/src/algo.rs5
-rw-r--r--crates/syntax/src/ast/make.rs4
-rw-r--r--editors/code/rust.tmGrammar.json47
-rw-r--r--editors/code/src/snippets.ts23
-rw-r--r--xtask/tests/tidy.rs7
30 files changed, 2053 insertions, 1972 deletions
diff --git a/crates/assists/src/assist_context.rs b/crates/assists/src/assist_context.rs
index fcfe2d6ee..69499ea32 100644
--- a/crates/assists/src/assist_context.rs
+++ b/crates/assists/src/assist_context.rs
@@ -208,7 +208,7 @@ pub(crate) struct AssistBuilder {
208 edit: TextEditBuilder, 208 edit: TextEditBuilder,
209 file_id: FileId, 209 file_id: FileId,
210 is_snippet: bool, 210 is_snippet: bool,
211 change: SourceChange, 211 source_file_edits: Vec<SourceFileEdit>,
212} 212}
213 213
214impl AssistBuilder { 214impl AssistBuilder {
@@ -217,20 +217,27 @@ impl AssistBuilder {
217 edit: TextEdit::builder(), 217 edit: TextEdit::builder(),
218 file_id, 218 file_id,
219 is_snippet: false, 219 is_snippet: false,
220 change: SourceChange::default(), 220 source_file_edits: Vec::default(),
221 } 221 }
222 } 222 }
223 223
224 pub(crate) fn edit_file(&mut self, file_id: FileId) { 224 pub(crate) fn edit_file(&mut self, file_id: FileId) {
225 self.commit();
225 self.file_id = file_id; 226 self.file_id = file_id;
226 } 227 }
227 228
228 fn commit(&mut self) { 229 fn commit(&mut self) {
229 let edit = mem::take(&mut self.edit).finish(); 230 let edit = mem::take(&mut self.edit).finish();
230 if !edit.is_empty() { 231 if !edit.is_empty() {
231 let new_edit = SourceFileEdit { file_id: self.file_id, edit }; 232 match self.source_file_edits.binary_search_by_key(&self.file_id, |edit| edit.file_id) {
232 assert!(!self.change.source_file_edits.iter().any(|it| it.file_id == new_edit.file_id)); 233 Ok(idx) => self.source_file_edits[idx]
233 self.change.source_file_edits.push(new_edit); 234 .edit
235 .union(edit)
236 .expect("overlapping edits for same file"),
237 Err(idx) => self
238 .source_file_edits
239 .insert(idx, SourceFileEdit { file_id: self.file_id, edit }),
240 }
234 } 241 }
235 } 242 }
236 243
@@ -270,23 +277,18 @@ impl AssistBuilder {
270 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) 277 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
271 } 278 }
272 pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { 279 pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
273 let node = rewriter.rewrite_root().unwrap(); 280 if let Some(node) = rewriter.rewrite_root() {
274 let new = rewriter.rewrite(&node); 281 let new = rewriter.rewrite(&node);
275 algo::diff(&node, &new).into_text_edit(&mut self.edit); 282 algo::diff(&node, &new).into_text_edit(&mut self.edit);
276 } 283 }
277
278 // FIXME: kill this API
279 /// Get access to the raw `TextEditBuilder`.
280 pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder {
281 &mut self.edit
282 } 284 }
283 285
284 fn finish(mut self) -> SourceChange { 286 fn finish(mut self) -> SourceChange {
285 self.commit(); 287 self.commit();
286 let mut change = mem::take(&mut self.change); 288 SourceChange {
287 if self.is_snippet { 289 source_file_edits: mem::take(&mut self.source_file_edits),
288 change.is_snippet = true; 290 file_system_edits: Default::default(),
291 is_snippet: self.is_snippet,
289 } 292 }
290 change
291 } 293 }
292} 294}
diff --git a/crates/assists/src/handlers/add_custom_impl.rs b/crates/assists/src/handlers/add_custom_impl.rs
deleted file mode 100644
index 669dd9b21..000000000
--- a/crates/assists/src/handlers/add_custom_impl.rs
+++ /dev/null
@@ -1,284 +0,0 @@
1use ide_db::imports_locator;
2use itertools::Itertools;
3use syntax::{
4 ast::{self, make, AstNode},
5 Direction, SmolStr,
6 SyntaxKind::{IDENT, WHITESPACE},
7 TextRange, TextSize,
8};
9
10use crate::{
11 assist_config::SnippetCap,
12 assist_context::{AssistBuilder, AssistContext, Assists},
13 utils::mod_path_to_ast,
14 AssistId, AssistKind,
15};
16
17// Assist: add_custom_impl
18//
19// Adds impl block for derived trait.
20//
21// ```
22// #[derive(Deb<|>ug, Display)]
23// struct S;
24// ```
25// ->
26// ```
27// #[derive(Display)]
28// struct S;
29//
30// impl Debug for S {
31// $0
32// }
33// ```
34pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
35 let attr = ctx.find_node_at_offset::<ast::Attr>()?;
36
37 let attr_name = attr
38 .syntax()
39 .descendants_with_tokens()
40 .filter(|t| t.kind() == IDENT)
41 .find_map(syntax::NodeOrToken::into_token)
42 .filter(|t| t.text() == "derive")?
43 .text()
44 .clone();
45
46 let trait_token =
47 ctx.token_at_offset().find(|t| t.kind() == IDENT && *t.text() != attr_name)?;
48 let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text())));
49
50 let annotated = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?;
51 let annotated_name = annotated.syntax().text().to_string();
52 let insert_pos = annotated.syntax().parent()?.text_range().end();
53
54 let current_module = ctx.sema.scope(annotated.syntax()).module()?;
55 let current_crate = current_module.krate();
56
57 let found_traits = imports_locator::find_imports(&ctx.sema, current_crate, trait_token.text())
58 .into_iter()
59 .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate {
60 either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_),
61 _ => None,
62 })
63 .flat_map(|trait_| {
64 current_module
65 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
66 .as_ref()
67 .map(mod_path_to_ast)
68 .zip(Some(trait_))
69 });
70
71 let mut no_traits_found = true;
72 for (trait_path, _trait) in found_traits.inspect(|_| no_traits_found = false) {
73 add_assist(acc, ctx.config.snippet_cap, &attr, &trait_path, &annotated_name, insert_pos)?;
74 }
75 if no_traits_found {
76 add_assist(acc, ctx.config.snippet_cap, &attr, &trait_path, &annotated_name, insert_pos)?;
77 }
78 Some(())
79}
80
81fn add_assist(
82 acc: &mut Assists,
83 snippet_cap: Option<SnippetCap>,
84 attr: &ast::Attr,
85 trait_path: &ast::Path,
86 annotated_name: &str,
87 insert_pos: TextSize,
88) -> Option<()> {
89 let target = attr.syntax().text_range();
90 let input = attr.token_tree()?;
91 let label = format!("Add custom impl `{}` for `{}`", trait_path, annotated_name);
92 let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?;
93
94 acc.add(AssistId("add_custom_impl", AssistKind::Refactor), label, target, |builder| {
95 update_attribute(builder, &input, &trait_name, &attr);
96 match snippet_cap {
97 Some(cap) => {
98 builder.insert_snippet(
99 cap,
100 insert_pos,
101 format!("\n\nimpl {} for {} {{\n $0\n}}", trait_path, annotated_name),
102 );
103 }
104 None => {
105 builder.insert(
106 insert_pos,
107 format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name),
108 );
109 }
110 }
111 })
112}
113
114fn update_attribute(
115 builder: &mut AssistBuilder,
116 input: &ast::TokenTree,
117 trait_name: &ast::NameRef,
118 attr: &ast::Attr,
119) {
120 let new_attr_input = input
121 .syntax()
122 .descendants_with_tokens()
123 .filter(|t| t.kind() == IDENT)
124 .filter_map(|t| t.into_token().map(|t| t.text().clone()))
125 .filter(|t| t != trait_name.text())
126 .collect::<Vec<SmolStr>>();
127 let has_more_derives = !new_attr_input.is_empty();
128
129 if has_more_derives {
130 let new_attr_input = format!("({})", new_attr_input.iter().format(", "));
131 builder.replace(input.syntax().text_range(), new_attr_input);
132 } else {
133 let attr_range = attr.syntax().text_range();
134 builder.delete(attr_range);
135
136 let line_break_range = attr
137 .syntax()
138 .next_sibling_or_token()
139 .filter(|t| t.kind() == WHITESPACE)
140 .map(|t| t.text_range())
141 .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0)));
142 builder.delete(line_break_range);
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use crate::tests::{check_assist, check_assist_not_applicable};
149
150 use super::*;
151
152 #[test]
153 fn add_custom_impl_qualified() {
154 check_assist(
155 add_custom_impl,
156 "
157mod fmt {
158 pub trait Debug {}
159}
160
161#[derive(Debu<|>g)]
162struct Foo {
163 bar: String,
164}
165",
166 "
167mod fmt {
168 pub trait Debug {}
169}
170
171struct Foo {
172 bar: String,
173}
174
175impl fmt::Debug for Foo {
176 $0
177}
178",
179 )
180 }
181 #[test]
182 fn add_custom_impl_for_unique_input() {
183 check_assist(
184 add_custom_impl,
185 "
186#[derive(Debu<|>g)]
187struct Foo {
188 bar: String,
189}
190 ",
191 "
192struct Foo {
193 bar: String,
194}
195
196impl Debug for Foo {
197 $0
198}
199 ",
200 )
201 }
202
203 #[test]
204 fn add_custom_impl_for_with_visibility_modifier() {
205 check_assist(
206 add_custom_impl,
207 "
208#[derive(Debug<|>)]
209pub struct Foo {
210 bar: String,
211}
212 ",
213 "
214pub struct Foo {
215 bar: String,
216}
217
218impl Debug for Foo {
219 $0
220}
221 ",
222 )
223 }
224
225 #[test]
226 fn add_custom_impl_when_multiple_inputs() {
227 check_assist(
228 add_custom_impl,
229 "
230#[derive(Display, Debug<|>, Serialize)]
231struct Foo {}
232 ",
233 "
234#[derive(Display, Serialize)]
235struct Foo {}
236
237impl Debug for Foo {
238 $0
239}
240 ",
241 )
242 }
243
244 #[test]
245 fn test_ignore_derive_macro_without_input() {
246 check_assist_not_applicable(
247 add_custom_impl,
248 "
249#[derive(<|>)]
250struct Foo {}
251 ",
252 )
253 }
254
255 #[test]
256 fn test_ignore_if_cursor_on_param() {
257 check_assist_not_applicable(
258 add_custom_impl,
259 "
260#[derive<|>(Debug)]
261struct Foo {}
262 ",
263 );
264
265 check_assist_not_applicable(
266 add_custom_impl,
267 "
268#[derive(Debug)<|>]
269struct Foo {}
270 ",
271 )
272 }
273
274 #[test]
275 fn test_ignore_if_not_derive() {
276 check_assist_not_applicable(
277 add_custom_impl,
278 "
279#[allow(non_camel_<|>case_types)]
280struct Foo {}
281 ",
282 )
283 }
284}
diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs
index b82fb30ad..bbb71e261 100644
--- a/crates/assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/assists/src/handlers/add_missing_impl_members.rs
@@ -1,27 +1,14 @@
1use hir::HasSource; 1use ide_db::traits::resolve_target_trait;
2use ide_db::traits::{get_missing_assoc_items, resolve_target_trait}; 2use syntax::ast::{self, AstNode};
3use syntax::{
4 ast::{
5 self,
6 edit::{self, AstNodeEdit, IndentLevel},
7 make, AstNode, NameOwner,
8 },
9 SmolStr,
10};
11 3
12use crate::{ 4use crate::{
13 assist_context::{AssistContext, Assists}, 5 assist_context::{AssistContext, Assists},
14 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, 6 utils::add_trait_assoc_items_to_impl,
15 utils::{render_snippet, Cursor}, 7 utils::DefaultMethods,
8 utils::{filter_assoc_items, render_snippet, Cursor},
16 AssistId, AssistKind, 9 AssistId, AssistKind,
17}; 10};
18 11
19#[derive(PartialEq)]
20enum AddMissingImplMembersMode {
21 DefaultMethodsOnly,
22 NoDefaultMethods,
23}
24
25// Assist: add_impl_missing_members 12// Assist: add_impl_missing_members
26// 13//
27// Adds scaffold for required impl members. 14// Adds scaffold for required impl members.
@@ -55,7 +42,7 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -
55 add_missing_impl_members_inner( 42 add_missing_impl_members_inner(
56 acc, 43 acc,
57 ctx, 44 ctx,
58 AddMissingImplMembersMode::NoDefaultMethods, 45 DefaultMethods::No,
59 "add_impl_missing_members", 46 "add_impl_missing_members",
60 "Implement missing members", 47 "Implement missing members",
61 ) 48 )
@@ -97,7 +84,7 @@ pub(crate) fn add_missing_default_members(acc: &mut Assists, ctx: &AssistContext
97 add_missing_impl_members_inner( 84 add_missing_impl_members_inner(
98 acc, 85 acc,
99 ctx, 86 ctx,
100 AddMissingImplMembersMode::DefaultMethodsOnly, 87 DefaultMethods::Only,
101 "add_impl_default_members", 88 "add_impl_default_members",
102 "Implement default members", 89 "Implement default members",
103 ) 90 )
@@ -106,7 +93,7 @@ pub(crate) fn add_missing_default_members(acc: &mut Assists, ctx: &AssistContext
106fn add_missing_impl_members_inner( 93fn add_missing_impl_members_inner(
107 acc: &mut Assists, 94 acc: &mut Assists,
108 ctx: &AssistContext, 95 ctx: &AssistContext,
109 mode: AddMissingImplMembersMode, 96 mode: DefaultMethods,
110 assist_id: &'static str, 97 assist_id: &'static str,
111 label: &'static str, 98 label: &'static str,
112) -> Option<()> { 99) -> Option<()> {
@@ -114,32 +101,11 @@ fn add_missing_impl_members_inner(
114 let impl_def = ctx.find_node_at_offset::<ast::Impl>()?; 101 let impl_def = ctx.find_node_at_offset::<ast::Impl>()?;
115 let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?; 102 let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;
116 103
117 let def_name = |item: &ast::AssocItem| -> Option<SmolStr> { 104 let missing_items = filter_assoc_items(
118 match item { 105 ctx.db(),
119 ast::AssocItem::Fn(def) => def.name(), 106 &ide_db::traits::get_missing_assoc_items(&ctx.sema, &impl_def),
120 ast::AssocItem::TypeAlias(def) => def.name(), 107 mode,
121 ast::AssocItem::Const(def) => def.name(), 108 );
122 ast::AssocItem::MacroCall(_) => None,
123 }
124 .map(|it| it.text().clone())
125 };
126
127 let missing_items = get_missing_assoc_items(&ctx.sema, &impl_def)
128 .iter()
129 .map(|i| match i {
130 hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(ctx.db()).value),
131 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(ctx.db()).value),
132 hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(ctx.db()).value),
133 })
134 .filter(|t| def_name(&t).is_some())
135 .filter(|t| match t {
136 ast::AssocItem::Fn(def) => match mode {
137 AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(),
138 AddMissingImplMembersMode::NoDefaultMethods => def.body().is_none(),
139 },
140 _ => mode == AddMissingImplMembersMode::NoDefaultMethods,
141 })
142 .collect::<Vec<_>>();
143 109
144 if missing_items.is_empty() { 110 if missing_items.is_empty() {
145 return None; 111 return None;
@@ -147,29 +113,9 @@ fn add_missing_impl_members_inner(
147 113
148 let target = impl_def.syntax().text_range(); 114 let target = impl_def.syntax().text_range();
149 acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| { 115 acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| {
150 let impl_item_list = impl_def.assoc_item_list().unwrap_or_else(make::assoc_item_list);
151
152 let n_existing_items = impl_item_list.assoc_items().count();
153 let source_scope = ctx.sema.scope_for_def(trait_);
154 let target_scope = ctx.sema.scope(impl_def.syntax()); 116 let target_scope = ctx.sema.scope(impl_def.syntax());
155 let ast_transform = QualifyPaths::new(&target_scope, &source_scope) 117 let (new_impl_def, first_new_item) =
156 .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone())); 118 add_trait_assoc_items_to_impl(&ctx.sema, missing_items, trait_, impl_def, target_scope);
157
158 let items = missing_items
159 .into_iter()
160 .map(|it| ast_transform::apply(&*ast_transform, it))
161 .map(|it| match it {
162 ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)),
163 ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()),
164 _ => it,
165 })
166 .map(|it| edit::remove_attrs_and_docs(&it));
167
168 let new_impl_item_list = impl_item_list.append_items(items);
169 let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list);
170 let first_new_item =
171 new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap();
172
173 match ctx.config.snippet_cap { 119 match ctx.config.snippet_cap {
174 None => builder.replace(target, new_impl_def.to_string()), 120 None => builder.replace(target, new_impl_def.to_string()),
175 Some(cap) => { 121 Some(cap) => {
@@ -193,14 +139,6 @@ fn add_missing_impl_members_inner(
193 }) 139 })
194} 140}
195 141
196fn add_body(fn_def: ast::Fn) -> ast::Fn {
197 if fn_def.body().is_some() {
198 return fn_def;
199 }
200 let body = make::block_expr(None, Some(make::expr_todo())).indent(IndentLevel(1));
201 fn_def.with_body(body)
202}
203
204#[cfg(test)] 142#[cfg(test)]
205mod tests { 143mod tests {
206 use crate::tests::{check_assist, check_assist_not_applicable}; 144 use crate::tests::{check_assist, check_assist_not_applicable};
diff --git a/crates/assists/src/handlers/change_return_type_to_result.rs b/crates/assists/src/handlers/change_return_type_to_result.rs
deleted file mode 100644
index 76f33a5b6..000000000
--- a/crates/assists/src/handlers/change_return_type_to_result.rs
+++ /dev/null
@@ -1,1091 +0,0 @@
1use std::iter;
2
3use syntax::{
4 ast::{self, make, BlockExpr, Expr, LoopBodyOwner},
5 match_ast, AstNode, SyntaxNode,
6};
7use test_utils::mark;
8
9use crate::{AssistContext, AssistId, AssistKind, Assists};
10
11// Assist: change_return_type_to_result
12//
13// Change the function's return type to Result.
14//
15// ```
16// fn foo() -> i32<|> { 42i32 }
17// ```
18// ->
19// ```
20// fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
21// ```
22pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
23 let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
24 let parent = ret_type.syntax().parent()?;
25 let block_expr = match_ast! {
26 match parent {
27 ast::Fn(func) => func.body()?,
28 ast::ClosureExpr(closure) => match closure.body()? {
29 Expr::BlockExpr(block) => block,
30 // closures require a block when a return type is specified
31 _ => return None,
32 },
33 _ => return None,
34 }
35 };
36
37 let type_ref = &ret_type.ty()?;
38 let ret_type_str = type_ref.syntax().text().to_string();
39 let first_part_ret_type = ret_type_str.splitn(2, '<').next();
40 if let Some(ret_type_first_part) = first_part_ret_type {
41 if ret_type_first_part.ends_with("Result") {
42 mark::hit!(change_return_type_to_result_simple_return_type_already_result);
43 return None;
44 }
45 }
46
47 acc.add(
48 AssistId("change_return_type_to_result", AssistKind::RefactorRewrite),
49 "Wrap return type in Result",
50 type_ref.syntax().text_range(),
51 |builder| {
52 let mut tail_return_expr_collector = TailReturnCollector::new();
53 tail_return_expr_collector.collect_jump_exprs(&block_expr, false);
54 tail_return_expr_collector.collect_tail_exprs(&block_expr);
55
56 for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap {
57 let ok_wrapped = make::expr_call(
58 make::expr_path(make::path_unqualified(make::path_segment(make::name_ref(
59 "Ok",
60 )))),
61 make::arg_list(iter::once(ret_expr_arg.clone())),
62 );
63 builder.replace_ast(ret_expr_arg, ok_wrapped);
64 }
65
66 match ctx.config.snippet_cap {
67 Some(cap) => {
68 let snippet = format!("Result<{}, ${{0:_}}>", type_ref);
69 builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet)
70 }
71 None => builder
72 .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)),
73 }
74 },
75 )
76}
77
78struct TailReturnCollector {
79 exprs_to_wrap: Vec<ast::Expr>,
80}
81
82impl TailReturnCollector {
83 fn new() -> Self {
84 Self { exprs_to_wrap: vec![] }
85 }
86 /// Collect all`return` expression
87 fn collect_jump_exprs(&mut self, block_expr: &BlockExpr, collect_break: bool) {
88 let statements = block_expr.statements();
89 for stmt in statements {
90 let expr = match &stmt {
91 ast::Stmt::ExprStmt(stmt) => stmt.expr(),
92 ast::Stmt::LetStmt(stmt) => stmt.initializer(),
93 ast::Stmt::Item(_) => continue,
94 };
95 if let Some(expr) = &expr {
96 self.handle_exprs(expr, collect_break);
97 }
98 }
99
100 // Browse tail expressions for each block
101 if let Some(expr) = block_expr.expr() {
102 if let Some(last_exprs) = get_tail_expr_from_block(&expr) {
103 for last_expr in last_exprs {
104 let last_expr = match last_expr {
105 NodeType::Node(expr) => expr,
106 NodeType::Leaf(expr) => expr.syntax().clone(),
107 };
108
109 if let Some(last_expr) = Expr::cast(last_expr.clone()) {
110 self.handle_exprs(&last_expr, collect_break);
111 } else if let Some(expr_stmt) = ast::Stmt::cast(last_expr) {
112 let expr_stmt = match &expr_stmt {
113 ast::Stmt::ExprStmt(stmt) => stmt.expr(),
114 ast::Stmt::LetStmt(stmt) => stmt.initializer(),
115 ast::Stmt::Item(_) => None,
116 };
117 if let Some(expr) = &expr_stmt {
118 self.handle_exprs(expr, collect_break);
119 }
120 }
121 }
122 }
123 }
124 }
125
126 fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) {
127 match expr {
128 Expr::BlockExpr(block_expr) => {
129 self.collect_jump_exprs(&block_expr, collect_break);
130 }
131 Expr::ReturnExpr(ret_expr) => {
132 if let Some(ret_expr_arg) = &ret_expr.expr() {
133 self.exprs_to_wrap.push(ret_expr_arg.clone());
134 }
135 }
136 Expr::BreakExpr(break_expr) if collect_break => {
137 if let Some(break_expr_arg) = &break_expr.expr() {
138 self.exprs_to_wrap.push(break_expr_arg.clone());
139 }
140 }
141 Expr::IfExpr(if_expr) => {
142 for block in if_expr.blocks() {
143 self.collect_jump_exprs(&block, collect_break);
144 }
145 }
146 Expr::LoopExpr(loop_expr) => {
147 if let Some(block_expr) = loop_expr.loop_body() {
148 self.collect_jump_exprs(&block_expr, collect_break);
149 }
150 }
151 Expr::ForExpr(for_expr) => {
152 if let Some(block_expr) = for_expr.loop_body() {
153 self.collect_jump_exprs(&block_expr, collect_break);
154 }
155 }
156 Expr::WhileExpr(while_expr) => {
157 if let Some(block_expr) = while_expr.loop_body() {
158 self.collect_jump_exprs(&block_expr, collect_break);
159 }
160 }
161 Expr::MatchExpr(match_expr) => {
162 if let Some(arm_list) = match_expr.match_arm_list() {
163 arm_list.arms().filter_map(|match_arm| match_arm.expr()).for_each(|expr| {
164 self.handle_exprs(&expr, collect_break);
165 });
166 }
167 }
168 _ => {}
169 }
170 }
171
172 fn collect_tail_exprs(&mut self, block: &BlockExpr) {
173 if let Some(expr) = block.expr() {
174 self.handle_exprs(&expr, true);
175 self.fetch_tail_exprs(&expr);
176 }
177 }
178
179 fn fetch_tail_exprs(&mut self, expr: &Expr) {
180 if let Some(exprs) = get_tail_expr_from_block(expr) {
181 for node_type in &exprs {
182 match node_type {
183 NodeType::Leaf(expr) => {
184 self.exprs_to_wrap.push(expr.clone());
185 }
186 NodeType::Node(expr) => {
187 if let Some(last_expr) = Expr::cast(expr.clone()) {
188 self.fetch_tail_exprs(&last_expr);
189 }
190 }
191 }
192 }
193 }
194 }
195}
196
197#[derive(Debug)]
198enum NodeType {
199 Leaf(ast::Expr),
200 Node(SyntaxNode),
201}
202
203/// Get a tail expression inside a block
204fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> {
205 match expr {
206 Expr::IfExpr(if_expr) => {
207 let mut nodes = vec![];
208 for block in if_expr.blocks() {
209 if let Some(block_expr) = block.expr() {
210 if let Some(tail_exprs) = get_tail_expr_from_block(&block_expr) {
211 nodes.extend(tail_exprs);
212 }
213 } else if let Some(last_expr) = block.syntax().last_child() {
214 nodes.push(NodeType::Node(last_expr));
215 } else {
216 nodes.push(NodeType::Node(block.syntax().clone()));
217 }
218 }
219 Some(nodes)
220 }
221 Expr::LoopExpr(loop_expr) => {
222 loop_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
223 }
224 Expr::ForExpr(for_expr) => {
225 for_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
226 }
227 Expr::WhileExpr(while_expr) => {
228 while_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
229 }
230 Expr::BlockExpr(block_expr) => {
231 block_expr.expr().map(|lc| vec![NodeType::Node(lc.syntax().clone())])
232 }
233 Expr::MatchExpr(match_expr) => {
234 let arm_list = match_expr.match_arm_list()?;
235 let arms: Vec<NodeType> = arm_list
236 .arms()
237 .filter_map(|match_arm| match_arm.expr())
238 .map(|expr| match expr {
239 Expr::ReturnExpr(ret_expr) => NodeType::Node(ret_expr.syntax().clone()),
240 Expr::BreakExpr(break_expr) => NodeType::Node(break_expr.syntax().clone()),
241 _ => match expr.syntax().last_child() {
242 Some(last_expr) => NodeType::Node(last_expr),
243 None => NodeType::Node(expr.syntax().clone()),
244 },
245 })
246 .collect();
247
248 Some(arms)
249 }
250 Expr::BreakExpr(expr) => expr.expr().map(|e| vec![NodeType::Leaf(e)]),
251 Expr::ReturnExpr(ret_expr) => Some(vec![NodeType::Node(ret_expr.syntax().clone())]),
252
253 Expr::CallExpr(_)
254 | Expr::Literal(_)
255 | Expr::TupleExpr(_)
256 | Expr::ArrayExpr(_)
257 | Expr::ParenExpr(_)
258 | Expr::PathExpr(_)
259 | Expr::RecordExpr(_)
260 | Expr::IndexExpr(_)
261 | Expr::MethodCallExpr(_)
262 | Expr::AwaitExpr(_)
263 | Expr::CastExpr(_)
264 | Expr::RefExpr(_)
265 | Expr::PrefixExpr(_)
266 | Expr::RangeExpr(_)
267 | Expr::BinExpr(_)
268 | Expr::MacroCall(_)
269 | Expr::BoxExpr(_) => Some(vec![NodeType::Leaf(expr.clone())]),
270 _ => None,
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use crate::tests::{check_assist, check_assist_not_applicable};
277
278 use super::*;
279
280 #[test]
281 fn change_return_type_to_result_simple() {
282 check_assist(
283 change_return_type_to_result,
284 r#"fn foo() -> i3<|>2 {
285 let test = "test";
286 return 42i32;
287 }"#,
288 r#"fn foo() -> Result<i32, ${0:_}> {
289 let test = "test";
290 return Ok(42i32);
291 }"#,
292 );
293 }
294
295 #[test]
296 fn change_return_type_to_result_simple_closure() {
297 check_assist(
298 change_return_type_to_result,
299 r#"fn foo() {
300 || -> i32<|> {
301 let test = "test";
302 return 42i32;
303 };
304 }"#,
305 r#"fn foo() {
306 || -> Result<i32, ${0:_}> {
307 let test = "test";
308 return Ok(42i32);
309 };
310 }"#,
311 );
312 }
313
314 #[test]
315 fn change_return_type_to_result_simple_return_type_bad_cursor() {
316 check_assist_not_applicable(
317 change_return_type_to_result,
318 r#"fn foo() -> i32 {
319 let test = "test";<|>
320 return 42i32;
321 }"#,
322 );
323 }
324
325 #[test]
326 fn change_return_type_to_result_simple_return_type_bad_cursor_closure() {
327 check_assist_not_applicable(
328 change_return_type_to_result,
329 r#"fn foo() {
330 || -> i32 {
331 let test = "test";<|>
332 return 42i32;
333 };
334 }"#,
335 );
336 }
337
338 #[test]
339 fn change_return_type_to_result_closure_non_block() {
340 check_assist_not_applicable(
341 change_return_type_to_result,
342 r#"fn foo() {
343 || -> i<|>32 3;
344 }"#,
345 );
346 }
347
348 #[test]
349 fn change_return_type_to_result_simple_return_type_already_result_std() {
350 check_assist_not_applicable(
351 change_return_type_to_result,
352 r#"fn foo() -> std::result::Result<i32<|>, String> {
353 let test = "test";
354 return 42i32;
355 }"#,
356 );
357 }
358
359 #[test]
360 fn change_return_type_to_result_simple_return_type_already_result() {
361 mark::check!(change_return_type_to_result_simple_return_type_already_result);
362 check_assist_not_applicable(
363 change_return_type_to_result,
364 r#"fn foo() -> Result<i32<|>, String> {
365 let test = "test";
366 return 42i32;
367 }"#,
368 );
369 }
370
371 #[test]
372 fn change_return_type_to_result_simple_return_type_already_result_closure() {
373 check_assist_not_applicable(
374 change_return_type_to_result,
375 r#"fn foo() {
376 || -> Result<i32<|>, String> {
377 let test = "test";
378 return 42i32;
379 };
380 }"#,
381 );
382 }
383
384 #[test]
385 fn change_return_type_to_result_simple_with_cursor() {
386 check_assist(
387 change_return_type_to_result,
388 r#"fn foo() -> <|>i32 {
389 let test = "test";
390 return 42i32;
391 }"#,
392 r#"fn foo() -> Result<i32, ${0:_}> {
393 let test = "test";
394 return Ok(42i32);
395 }"#,
396 );
397 }
398
399 #[test]
400 fn change_return_type_to_result_simple_with_tail() {
401 check_assist(
402 change_return_type_to_result,
403 r#"fn foo() -><|> i32 {
404 let test = "test";
405 42i32
406 }"#,
407 r#"fn foo() -> Result<i32, ${0:_}> {
408 let test = "test";
409 Ok(42i32)
410 }"#,
411 );
412 }
413
414 #[test]
415 fn change_return_type_to_result_simple_with_tail_closure() {
416 check_assist(
417 change_return_type_to_result,
418 r#"fn foo() {
419 || -><|> i32 {
420 let test = "test";
421 42i32
422 };
423 }"#,
424 r#"fn foo() {
425 || -> Result<i32, ${0:_}> {
426 let test = "test";
427 Ok(42i32)
428 };
429 }"#,
430 );
431 }
432
433 #[test]
434 fn change_return_type_to_result_simple_with_tail_only() {
435 check_assist(
436 change_return_type_to_result,
437 r#"fn foo() -> i32<|> {
438 42i32
439 }"#,
440 r#"fn foo() -> Result<i32, ${0:_}> {
441 Ok(42i32)
442 }"#,
443 );
444 }
445
446 #[test]
447 fn change_return_type_to_result_simple_with_tail_block_like() {
448 check_assist(
449 change_return_type_to_result,
450 r#"fn foo() -> i32<|> {
451 if true {
452 42i32
453 } else {
454 24i32
455 }
456 }"#,
457 r#"fn foo() -> Result<i32, ${0:_}> {
458 if true {
459 Ok(42i32)
460 } else {
461 Ok(24i32)
462 }
463 }"#,
464 );
465 }
466
467 #[test]
468 fn change_return_type_to_result_simple_without_block_closure() {
469 check_assist(
470 change_return_type_to_result,
471 r#"fn foo() {
472 || -> i32<|> {
473 if true {
474 42i32
475 } else {
476 24i32
477 }
478 };
479 }"#,
480 r#"fn foo() {
481 || -> Result<i32, ${0:_}> {
482 if true {
483 Ok(42i32)
484 } else {
485 Ok(24i32)
486 }
487 };
488 }"#,
489 );
490 }
491
492 #[test]
493 fn change_return_type_to_result_simple_with_nested_if() {
494 check_assist(
495 change_return_type_to_result,
496 r#"fn foo() -> i32<|> {
497 if true {
498 if false {
499 1
500 } else {
501 2
502 }
503 } else {
504 24i32
505 }
506 }"#,
507 r#"fn foo() -> Result<i32, ${0:_}> {
508 if true {
509 if false {
510 Ok(1)
511 } else {
512 Ok(2)
513 }
514 } else {
515 Ok(24i32)
516 }
517 }"#,
518 );
519 }
520
521 #[test]
522 fn change_return_type_to_result_simple_with_await() {
523 check_assist(
524 change_return_type_to_result,
525 r#"async fn foo() -> i<|>32 {
526 if true {
527 if false {
528 1.await
529 } else {
530 2.await
531 }
532 } else {
533 24i32.await
534 }
535 }"#,
536 r#"async fn foo() -> Result<i32, ${0:_}> {
537 if true {
538 if false {
539 Ok(1.await)
540 } else {
541 Ok(2.await)
542 }
543 } else {
544 Ok(24i32.await)
545 }
546 }"#,
547 );
548 }
549
550 #[test]
551 fn change_return_type_to_result_simple_with_array() {
552 check_assist(
553 change_return_type_to_result,
554 r#"fn foo() -> [i32;<|> 3] {
555 [1, 2, 3]
556 }"#,
557 r#"fn foo() -> Result<[i32; 3], ${0:_}> {
558 Ok([1, 2, 3])
559 }"#,
560 );
561 }
562
563 #[test]
564 fn change_return_type_to_result_simple_with_cast() {
565 check_assist(
566 change_return_type_to_result,
567 r#"fn foo() -<|>> i32 {
568 if true {
569 if false {
570 1 as i32
571 } else {
572 2 as i32
573 }
574 } else {
575 24 as i32
576 }
577 }"#,
578 r#"fn foo() -> Result<i32, ${0:_}> {
579 if true {
580 if false {
581 Ok(1 as i32)
582 } else {
583 Ok(2 as i32)
584 }
585 } else {
586 Ok(24 as i32)
587 }
588 }"#,
589 );
590 }
591
592 #[test]
593 fn change_return_type_to_result_simple_with_tail_block_like_match() {
594 check_assist(
595 change_return_type_to_result,
596 r#"fn foo() -> i32<|> {
597 let my_var = 5;
598 match my_var {
599 5 => 42i32,
600 _ => 24i32,
601 }
602 }"#,
603 r#"fn foo() -> Result<i32, ${0:_}> {
604 let my_var = 5;
605 match my_var {
606 5 => Ok(42i32),
607 _ => Ok(24i32),
608 }
609 }"#,
610 );
611 }
612
613 #[test]
614 fn change_return_type_to_result_simple_with_loop_with_tail() {
615 check_assist(
616 change_return_type_to_result,
617 r#"fn foo() -> i32<|> {
618 let my_var = 5;
619 loop {
620 println!("test");
621 5
622 }
623
624 my_var
625 }"#,
626 r#"fn foo() -> Result<i32, ${0:_}> {
627 let my_var = 5;
628 loop {
629 println!("test");
630 5
631 }
632
633 Ok(my_var)
634 }"#,
635 );
636 }
637
638 #[test]
639 fn change_return_type_to_result_simple_with_loop_in_let_stmt() {
640 check_assist(
641 change_return_type_to_result,
642 r#"fn foo() -> i32<|> {
643 let my_var = let x = loop {
644 break 1;
645 };
646
647 my_var
648 }"#,
649 r#"fn foo() -> Result<i32, ${0:_}> {
650 let my_var = let x = loop {
651 break 1;
652 };
653
654 Ok(my_var)
655 }"#,
656 );
657 }
658
659 #[test]
660 fn change_return_type_to_result_simple_with_tail_block_like_match_return_expr() {
661 check_assist(
662 change_return_type_to_result,
663 r#"fn foo() -> i32<|> {
664 let my_var = 5;
665 let res = match my_var {
666 5 => 42i32,
667 _ => return 24i32,
668 };
669
670 res
671 }"#,
672 r#"fn foo() -> Result<i32, ${0:_}> {
673 let my_var = 5;
674 let res = match my_var {
675 5 => 42i32,
676 _ => return Ok(24i32),
677 };
678
679 Ok(res)
680 }"#,
681 );
682
683 check_assist(
684 change_return_type_to_result,
685 r#"fn foo() -> i32<|> {
686 let my_var = 5;
687 let res = if my_var == 5 {
688 42i32
689 } else {
690 return 24i32;
691 };
692
693 res
694 }"#,
695 r#"fn foo() -> Result<i32, ${0:_}> {
696 let my_var = 5;
697 let res = if my_var == 5 {
698 42i32
699 } else {
700 return Ok(24i32);
701 };
702
703 Ok(res)
704 }"#,
705 );
706 }
707
708 #[test]
709 fn change_return_type_to_result_simple_with_tail_block_like_match_deeper() {
710 check_assist(
711 change_return_type_to_result,
712 r#"fn foo() -> i32<|> {
713 let my_var = 5;
714 match my_var {
715 5 => {
716 if true {
717 42i32
718 } else {
719 25i32
720 }
721 },
722 _ => {
723 let test = "test";
724 if test == "test" {
725 return bar();
726 }
727 53i32
728 },
729 }
730 }"#,
731 r#"fn foo() -> Result<i32, ${0:_}> {
732 let my_var = 5;
733 match my_var {
734 5 => {
735 if true {
736 Ok(42i32)
737 } else {
738 Ok(25i32)
739 }
740 },
741 _ => {
742 let test = "test";
743 if test == "test" {
744 return Ok(bar());
745 }
746 Ok(53i32)
747 },
748 }
749 }"#,
750 );
751 }
752
753 #[test]
754 fn change_return_type_to_result_simple_with_tail_block_like_early_return() {
755 check_assist(
756 change_return_type_to_result,
757 r#"fn foo() -> i<|>32 {
758 let test = "test";
759 if test == "test" {
760 return 24i32;
761 }
762 53i32
763 }"#,
764 r#"fn foo() -> Result<i32, ${0:_}> {
765 let test = "test";
766 if test == "test" {
767 return Ok(24i32);
768 }
769 Ok(53i32)
770 }"#,
771 );
772 }
773
774 #[test]
775 fn change_return_type_to_result_simple_with_closure() {
776 check_assist(
777 change_return_type_to_result,
778 r#"fn foo(the_field: u32) -><|> u32 {
779 let true_closure = || {
780 return true;
781 };
782 if the_field < 5 {
783 let mut i = 0;
784
785
786 if true_closure() {
787 return 99;
788 } else {
789 return 0;
790 }
791 }
792
793 the_field
794 }"#,
795 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
796 let true_closure = || {
797 return true;
798 };
799 if the_field < 5 {
800 let mut i = 0;
801
802
803 if true_closure() {
804 return Ok(99);
805 } else {
806 return Ok(0);
807 }
808 }
809
810 Ok(the_field)
811 }"#,
812 );
813
814 check_assist(
815 change_return_type_to_result,
816 r#"fn foo(the_field: u32) -> u32<|> {
817 let true_closure = || {
818 return true;
819 };
820 if the_field < 5 {
821 let mut i = 0;
822
823
824 if true_closure() {
825 return 99;
826 } else {
827 return 0;
828 }
829 }
830 let t = None;
831
832 t.unwrap_or_else(|| the_field)
833 }"#,
834 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
835 let true_closure = || {
836 return true;
837 };
838 if the_field < 5 {
839 let mut i = 0;
840
841
842 if true_closure() {
843 return Ok(99);
844 } else {
845 return Ok(0);
846 }
847 }
848 let t = None;
849
850 Ok(t.unwrap_or_else(|| the_field))
851 }"#,
852 );
853 }
854
855 #[test]
856 fn change_return_type_to_result_simple_with_weird_forms() {
857 check_assist(
858 change_return_type_to_result,
859 r#"fn foo() -> i32<|> {
860 let test = "test";
861 if test == "test" {
862 return 24i32;
863 }
864 let mut i = 0;
865 loop {
866 if i == 1 {
867 break 55;
868 }
869 i += 1;
870 }
871 }"#,
872 r#"fn foo() -> Result<i32, ${0:_}> {
873 let test = "test";
874 if test == "test" {
875 return Ok(24i32);
876 }
877 let mut i = 0;
878 loop {
879 if i == 1 {
880 break Ok(55);
881 }
882 i += 1;
883 }
884 }"#,
885 );
886
887 check_assist(
888 change_return_type_to_result,
889 r#"fn foo() -> i32<|> {
890 let test = "test";
891 if test == "test" {
892 return 24i32;
893 }
894 let mut i = 0;
895 loop {
896 loop {
897 if i == 1 {
898 break 55;
899 }
900 i += 1;
901 }
902 }
903 }"#,
904 r#"fn foo() -> Result<i32, ${0:_}> {
905 let test = "test";
906 if test == "test" {
907 return Ok(24i32);
908 }
909 let mut i = 0;
910 loop {
911 loop {
912 if i == 1 {
913 break Ok(55);
914 }
915 i += 1;
916 }
917 }
918 }"#,
919 );
920
921 check_assist(
922 change_return_type_to_result,
923 r#"fn foo() -> i3<|>2 {
924 let test = "test";
925 let other = 5;
926 if test == "test" {
927 let res = match other {
928 5 => 43,
929 _ => return 56,
930 };
931 }
932 let mut i = 0;
933 loop {
934 loop {
935 if i == 1 {
936 break 55;
937 }
938 i += 1;
939 }
940 }
941 }"#,
942 r#"fn foo() -> Result<i32, ${0:_}> {
943 let test = "test";
944 let other = 5;
945 if test == "test" {
946 let res = match other {
947 5 => 43,
948 _ => return Ok(56),
949 };
950 }
951 let mut i = 0;
952 loop {
953 loop {
954 if i == 1 {
955 break Ok(55);
956 }
957 i += 1;
958 }
959 }
960 }"#,
961 );
962
963 check_assist(
964 change_return_type_to_result,
965 r#"fn foo(the_field: u32) -> u32<|> {
966 if the_field < 5 {
967 let mut i = 0;
968 loop {
969 if i > 5 {
970 return 55u32;
971 }
972 i += 3;
973 }
974
975 match i {
976 5 => return 99,
977 _ => return 0,
978 };
979 }
980
981 the_field
982 }"#,
983 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
984 if the_field < 5 {
985 let mut i = 0;
986 loop {
987 if i > 5 {
988 return Ok(55u32);
989 }
990 i += 3;
991 }
992
993 match i {
994 5 => return Ok(99),
995 _ => return Ok(0),
996 };
997 }
998
999 Ok(the_field)
1000 }"#,
1001 );
1002
1003 check_assist(
1004 change_return_type_to_result,
1005 r#"fn foo(the_field: u32) -> u3<|>2 {
1006 if the_field < 5 {
1007 let mut i = 0;
1008
1009 match i {
1010 5 => return 99,
1011 _ => return 0,
1012 }
1013 }
1014
1015 the_field
1016 }"#,
1017 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1018 if the_field < 5 {
1019 let mut i = 0;
1020
1021 match i {
1022 5 => return Ok(99),
1023 _ => return Ok(0),
1024 }
1025 }
1026
1027 Ok(the_field)
1028 }"#,
1029 );
1030
1031 check_assist(
1032 change_return_type_to_result,
1033 r#"fn foo(the_field: u32) -> u32<|> {
1034 if the_field < 5 {
1035 let mut i = 0;
1036
1037 if i == 5 {
1038 return 99
1039 } else {
1040 return 0
1041 }
1042 }
1043
1044 the_field
1045 }"#,
1046 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1047 if the_field < 5 {
1048 let mut i = 0;
1049
1050 if i == 5 {
1051 return Ok(99)
1052 } else {
1053 return Ok(0)
1054 }
1055 }
1056
1057 Ok(the_field)
1058 }"#,
1059 );
1060
1061 check_assist(
1062 change_return_type_to_result,
1063 r#"fn foo(the_field: u32) -> <|>u32 {
1064 if the_field < 5 {
1065 let mut i = 0;
1066
1067 if i == 5 {
1068 return 99;
1069 } else {
1070 return 0;
1071 }
1072 }
1073
1074 the_field
1075 }"#,
1076 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1077 if the_field < 5 {
1078 let mut i = 0;
1079
1080 if i == 5 {
1081 return Ok(99);
1082 } else {
1083 return Ok(0);
1084 }
1085 }
1086
1087 Ok(the_field)
1088 }"#,
1089 );
1090 }
1091}
diff --git a/crates/assists/src/handlers/convert_integer_literal.rs b/crates/assists/src/handlers/convert_integer_literal.rs
index 1094ed3f3..667115382 100644
--- a/crates/assists/src/handlers/convert_integer_literal.rs
+++ b/crates/assists/src/handlers/convert_integer_literal.rs
@@ -129,34 +129,6 @@ mod tests {
129 ); 129 );
130 } 130 }
131 131
132 // Decimal numbers under 3 digits have a special case where they return early because we can't fit a
133 // other base's prefix, so we have a separate test for that.
134 #[test]
135 fn convert_small_decimal_integer() {
136 let before = "const _: i32 = 10<|>;";
137
138 check_assist_by_label(
139 convert_integer_literal,
140 before,
141 "const _: i32 = 0b1010;",
142 "Convert 10 to 0b1010",
143 );
144
145 check_assist_by_label(
146 convert_integer_literal,
147 before,
148 "const _: i32 = 0o12;",
149 "Convert 10 to 0o12",
150 );
151
152 check_assist_by_label(
153 convert_integer_literal,
154 before,
155 "const _: i32 = 0xA;",
156 "Convert 10 to 0xA",
157 );
158 }
159
160 #[test] 132 #[test]
161 fn convert_hexadecimal_integer() { 133 fn convert_hexadecimal_integer() {
162 let before = "const _: i32 = 0xFF<|>;"; 134 let before = "const _: i32 = 0xFF<|>;";
@@ -236,7 +208,7 @@ mod tests {
236 } 208 }
237 209
238 #[test] 210 #[test]
239 fn convert_decimal_integer_with_underscores() { 211 fn convert_integer_with_underscores() {
240 let before = "const _: i32 = 1_00_0<|>;"; 212 let before = "const _: i32 = 1_00_0<|>;";
241 213
242 check_assist_by_label( 214 check_assist_by_label(
@@ -262,111 +234,7 @@ mod tests {
262 } 234 }
263 235
264 #[test] 236 #[test]
265 fn convert_small_decimal_integer_with_underscores() { 237 fn convert_integer_with_suffix() {
266 let before = "const _: i32 = 1_0<|>;";
267
268 check_assist_by_label(
269 convert_integer_literal,
270 before,
271 "const _: i32 = 0b1010;",
272 "Convert 1_0 to 0b1010",
273 );
274
275 check_assist_by_label(
276 convert_integer_literal,
277 before,
278 "const _: i32 = 0o12;",
279 "Convert 1_0 to 0o12",
280 );
281
282 check_assist_by_label(
283 convert_integer_literal,
284 before,
285 "const _: i32 = 0xA;",
286 "Convert 1_0 to 0xA",
287 );
288 }
289
290 #[test]
291 fn convert_hexadecimal_integer_with_underscores() {
292 let before = "const _: i32 = 0x_F_F<|>;";
293
294 check_assist_by_label(
295 convert_integer_literal,
296 before,
297 "const _: i32 = 0b11111111;",
298 "Convert 0x_F_F to 0b11111111",
299 );
300
301 check_assist_by_label(
302 convert_integer_literal,
303 before,
304 "const _: i32 = 0o377;",
305 "Convert 0x_F_F to 0o377",
306 );
307
308 check_assist_by_label(
309 convert_integer_literal,
310 before,
311 "const _: i32 = 255;",
312 "Convert 0x_F_F to 255",
313 );
314 }
315
316 #[test]
317 fn convert_binary_integer_with_underscores() {
318 let before = "const _: i32 = 0b1111_1111<|>;";
319
320 check_assist_by_label(
321 convert_integer_literal,
322 before,
323 "const _: i32 = 0o377;",
324 "Convert 0b1111_1111 to 0o377",
325 );
326
327 check_assist_by_label(
328 convert_integer_literal,
329 before,
330 "const _: i32 = 255;",
331 "Convert 0b1111_1111 to 255",
332 );
333
334 check_assist_by_label(
335 convert_integer_literal,
336 before,
337 "const _: i32 = 0xFF;",
338 "Convert 0b1111_1111 to 0xFF",
339 );
340 }
341
342 #[test]
343 fn convert_octal_integer_with_underscores() {
344 let before = "const _: i32 = 0o3_77<|>;";
345
346 check_assist_by_label(
347 convert_integer_literal,
348 before,
349 "const _: i32 = 0b11111111;",
350 "Convert 0o3_77 to 0b11111111",
351 );
352
353 check_assist_by_label(
354 convert_integer_literal,
355 before,
356 "const _: i32 = 255;",
357 "Convert 0o3_77 to 255",
358 );
359
360 check_assist_by_label(
361 convert_integer_literal,
362 before,
363 "const _: i32 = 0xFF;",
364 "Convert 0o3_77 to 0xFF",
365 );
366 }
367
368 #[test]
369 fn convert_decimal_integer_with_suffix() {
370 let before = "const _: i32 = 1000i32<|>;"; 238 let before = "const _: i32 = 1000i32<|>;";
371 239
372 check_assist_by_label( 240 check_assist_by_label(
@@ -392,240 +260,6 @@ mod tests {
392 } 260 }
393 261
394 #[test] 262 #[test]
395 fn convert_small_decimal_integer_with_suffix() {
396 let before = "const _: i32 = 10i32<|>;";
397
398 check_assist_by_label(
399 convert_integer_literal,
400 before,
401 "const _: i32 = 0b1010i32;",
402 "Convert 10i32 to 0b1010i32",
403 );
404
405 check_assist_by_label(
406 convert_integer_literal,
407 before,
408 "const _: i32 = 0o12i32;",
409 "Convert 10i32 to 0o12i32",
410 );
411
412 check_assist_by_label(
413 convert_integer_literal,
414 before,
415 "const _: i32 = 0xAi32;",
416 "Convert 10i32 to 0xAi32",
417 );
418 }
419
420 #[test]
421 fn convert_hexadecimal_integer_with_suffix() {
422 let before = "const _: i32 = 0xFFi32<|>;";
423
424 check_assist_by_label(
425 convert_integer_literal,
426 before,
427 "const _: i32 = 0b11111111i32;",
428 "Convert 0xFFi32 to 0b11111111i32",
429 );
430
431 check_assist_by_label(
432 convert_integer_literal,
433 before,
434 "const _: i32 = 0o377i32;",
435 "Convert 0xFFi32 to 0o377i32",
436 );
437
438 check_assist_by_label(
439 convert_integer_literal,
440 before,
441 "const _: i32 = 255i32;",
442 "Convert 0xFFi32 to 255i32",
443 );
444 }
445
446 #[test]
447 fn convert_binary_integer_with_suffix() {
448 let before = "const _: i32 = 0b11111111i32<|>;";
449
450 check_assist_by_label(
451 convert_integer_literal,
452 before,
453 "const _: i32 = 0o377i32;",
454 "Convert 0b11111111i32 to 0o377i32",
455 );
456
457 check_assist_by_label(
458 convert_integer_literal,
459 before,
460 "const _: i32 = 255i32;",
461 "Convert 0b11111111i32 to 255i32",
462 );
463
464 check_assist_by_label(
465 convert_integer_literal,
466 before,
467 "const _: i32 = 0xFFi32;",
468 "Convert 0b11111111i32 to 0xFFi32",
469 );
470 }
471
472 #[test]
473 fn convert_octal_integer_with_suffix() {
474 let before = "const _: i32 = 0o377i32<|>;";
475
476 check_assist_by_label(
477 convert_integer_literal,
478 before,
479 "const _: i32 = 0b11111111i32;",
480 "Convert 0o377i32 to 0b11111111i32",
481 );
482
483 check_assist_by_label(
484 convert_integer_literal,
485 before,
486 "const _: i32 = 255i32;",
487 "Convert 0o377i32 to 255i32",
488 );
489
490 check_assist_by_label(
491 convert_integer_literal,
492 before,
493 "const _: i32 = 0xFFi32;",
494 "Convert 0o377i32 to 0xFFi32",
495 );
496 }
497
498 #[test]
499 fn convert_decimal_integer_with_underscores_and_suffix() {
500 let before = "const _: i32 = 1_00_0i32<|>;";
501
502 check_assist_by_label(
503 convert_integer_literal,
504 before,
505 "const _: i32 = 0b1111101000i32;",
506 "Convert 1_00_0i32 to 0b1111101000i32",
507 );
508
509 check_assist_by_label(
510 convert_integer_literal,
511 before,
512 "const _: i32 = 0o1750i32;",
513 "Convert 1_00_0i32 to 0o1750i32",
514 );
515
516 check_assist_by_label(
517 convert_integer_literal,
518 before,
519 "const _: i32 = 0x3E8i32;",
520 "Convert 1_00_0i32 to 0x3E8i32",
521 );
522 }
523
524 #[test]
525 fn convert_small_decimal_integer_with_underscores_and_suffix() {
526 let before = "const _: i32 = 1_0i32<|>;";
527
528 check_assist_by_label(
529 convert_integer_literal,
530 before,
531 "const _: i32 = 0b1010i32;",
532 "Convert 1_0i32 to 0b1010i32",
533 );
534
535 check_assist_by_label(
536 convert_integer_literal,
537 before,
538 "const _: i32 = 0o12i32;",
539 "Convert 1_0i32 to 0o12i32",
540 );
541
542 check_assist_by_label(
543 convert_integer_literal,
544 before,
545 "const _: i32 = 0xAi32;",
546 "Convert 1_0i32 to 0xAi32",
547 );
548 }
549
550 #[test]
551 fn convert_hexadecimal_integer_with_underscores_and_suffix() {
552 let before = "const _: i32 = 0x_F_Fi32<|>;";
553
554 check_assist_by_label(
555 convert_integer_literal,
556 before,
557 "const _: i32 = 0b11111111i32;",
558 "Convert 0x_F_Fi32 to 0b11111111i32",
559 );
560
561 check_assist_by_label(
562 convert_integer_literal,
563 before,
564 "const _: i32 = 0o377i32;",
565 "Convert 0x_F_Fi32 to 0o377i32",
566 );
567
568 check_assist_by_label(
569 convert_integer_literal,
570 before,
571 "const _: i32 = 255i32;",
572 "Convert 0x_F_Fi32 to 255i32",
573 );
574 }
575
576 #[test]
577 fn convert_binary_integer_with_underscores_and_suffix() {
578 let before = "const _: i32 = 0b1111_1111i32<|>;";
579
580 check_assist_by_label(
581 convert_integer_literal,
582 before,
583 "const _: i32 = 0o377i32;",
584 "Convert 0b1111_1111i32 to 0o377i32",
585 );
586
587 check_assist_by_label(
588 convert_integer_literal,
589 before,
590 "const _: i32 = 255i32;",
591 "Convert 0b1111_1111i32 to 255i32",
592 );
593
594 check_assist_by_label(
595 convert_integer_literal,
596 before,
597 "const _: i32 = 0xFFi32;",
598 "Convert 0b1111_1111i32 to 0xFFi32",
599 );
600 }
601
602 #[test]
603 fn convert_octal_integer_with_underscores_and_suffix() {
604 let before = "const _: i32 = 0o3_77i32<|>;";
605
606 check_assist_by_label(
607 convert_integer_literal,
608 before,
609 "const _: i32 = 0b11111111i32;",
610 "Convert 0o3_77i32 to 0b11111111i32",
611 );
612
613 check_assist_by_label(
614 convert_integer_literal,
615 before,
616 "const _: i32 = 255i32;",
617 "Convert 0o3_77i32 to 255i32",
618 );
619
620 check_assist_by_label(
621 convert_integer_literal,
622 before,
623 "const _: i32 = 0xFFi32;",
624 "Convert 0o3_77i32 to 0xFFi32",
625 );
626 }
627
628 #[test]
629 fn convert_overflowing_literal() { 263 fn convert_overflowing_literal() {
630 let before = "const _: i32 = 264 let before = "const _: i32 =
631 111111111111111111111111111111111111111111111111111111111111111111111111<|>;"; 265 111111111111111111111111111111111111111111111111111111111111111111111111<|>;";
diff --git a/crates/assists/src/handlers/expand_glob_import.rs b/crates/assists/src/handlers/expand_glob_import.rs
index 853266395..f51a9a4ad 100644
--- a/crates/assists/src/handlers/expand_glob_import.rs
+++ b/crates/assists/src/handlers/expand_glob_import.rs
@@ -5,13 +5,13 @@ use ide_db::{
5 search::SearchScope, 5 search::SearchScope,
6}; 6};
7use syntax::{ 7use syntax::{
8 algo, 8 algo::SyntaxRewriter,
9 ast::{self, make}, 9 ast::{self, make},
10 AstNode, Direction, SyntaxNode, SyntaxToken, T, 10 AstNode, Direction, SyntaxNode, SyntaxToken, T,
11}; 11};
12 12
13use crate::{ 13use crate::{
14 assist_context::{AssistBuilder, AssistContext, Assists}, 14 assist_context::{AssistContext, Assists},
15 AssistId, AssistKind, 15 AssistId, AssistKind,
16}; 16};
17 17
@@ -61,7 +61,9 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Opti
61 "Expand glob import", 61 "Expand glob import",
62 target.text_range(), 62 target.text_range(),
63 |builder| { 63 |builder| {
64 replace_ast(builder, parent, mod_path, names_to_import); 64 let mut rewriter = SyntaxRewriter::default();
65 replace_ast(&mut rewriter, parent, mod_path, names_to_import);
66 builder.rewrite(rewriter);
65 }, 67 },
66 ) 68 )
67} 69}
@@ -236,7 +238,7 @@ fn find_names_to_import(
236} 238}
237 239
238fn replace_ast( 240fn replace_ast(
239 builder: &mut AssistBuilder, 241 rewriter: &mut SyntaxRewriter,
240 parent: Either<ast::UseTree, ast::UseTreeList>, 242 parent: Either<ast::UseTree, ast::UseTreeList>,
241 path: ast::Path, 243 path: ast::Path,
242 names_to_import: Vec<Name>, 244 names_to_import: Vec<Name>,
@@ -264,32 +266,21 @@ fn replace_ast(
264 match use_trees.as_slice() { 266 match use_trees.as_slice() {
265 [name] => { 267 [name] => {
266 if let Some(end_path) = name.path() { 268 if let Some(end_path) = name.path() {
267 let replacement = 269 rewriter.replace_ast(
268 make::use_tree(make::path_concat(path, end_path), None, None, false); 270 &parent.left_or_else(|tl| tl.parent_use_tree()),
269 271 &make::use_tree(make::path_concat(path, end_path), None, None, false),
270 algo::diff( 272 );
271 &parent.either(|n| n.syntax().clone(), |n| n.syntax().clone()),
272 replacement.syntax(),
273 )
274 .into_text_edit(builder.text_edit_builder());
275 } 273 }
276 } 274 }
277 names => { 275 names => match &parent {
278 let replacement = match parent { 276 Either::Left(parent) => rewriter.replace_ast(
279 Either::Left(_) => { 277 parent,
280 make::use_tree(path, Some(make::use_tree_list(names.to_owned())), None, false) 278 &make::use_tree(path, Some(make::use_tree_list(names.to_owned())), None, false),
281 .syntax() 279 ),
282 .clone() 280 Either::Right(parent) => {
283 } 281 rewriter.replace_ast(parent, &make::use_tree_list(names.to_owned()))
284 Either::Right(_) => make::use_tree_list(names.to_owned()).syntax().clone(), 282 }
285 }; 283 },
286
287 algo::diff(
288 &parent.either(|n| n.syntax().clone(), |n| n.syntax().clone()),
289 &replacement,
290 )
291 .into_text_edit(builder.text_edit_builder());
292 }
293 }; 284 };
294} 285}
295 286
@@ -884,4 +875,33 @@ fn qux(baz: Baz) {}
884 ", 875 ",
885 ) 876 )
886 } 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::{*<|>};
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 }
887} 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
index 14209b771..84662d832 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -345,6 +345,73 @@ fn another_fn() {
345 ); 345 );
346 } 346 }
347 347
348 #[test]
349 fn test_several_files() {
350 check_assist(
351 extract_struct_from_enum_variant,
352 r#"
353//- /main.rs
354enum E {
355 <|>V(i32, i32)
356}
357mod foo;
358
359//- /foo.rs
360use crate::E;
361fn f() {
362 let e = E::V(9, 2);
363}
364"#,
365 r#"
366//- /main.rs
367struct V(pub i32, pub i32);
368
369enum E {
370 V(V)
371}
372mod foo;
373
374//- /foo.rs
375use V;
376
377use crate::E;
378fn f() {
379 let e = E::V(V(9, 2));
380}
381"#,
382 )
383 }
384
385 #[test]
386 fn test_several_files_record() {
387 // FIXME: this should fix the usage as well!
388 check_assist(
389 extract_struct_from_enum_variant,
390 r#"
391//- /main.rs
392enum E {
393 <|>V { i: i32, j: i32 }
394}
395mod foo;
396
397//- /foo.rs
398use crate::E;
399fn f() {
400 let e = E::V { i: 9, j: 2 };
401}
402"#,
403 r#"
404struct V{ pub i: i32, pub j: i32 }
405
406enum E {
407 V(V)
408}
409mod foo;
410
411"#,
412 )
413 }
414
348 fn check_not_applicable(ra_fixture: &str) { 415 fn check_not_applicable(ra_fixture: &str) {
349 let fixture = 416 let fixture =
350 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); 417 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
diff --git a/crates/assists/src/handlers/reorder_fields.rs b/crates/assists/src/handlers/reorder_fields.rs
index 527f457a7..7c0f0f44e 100644
--- a/crates/assists/src/handlers/reorder_fields.rs
+++ b/crates/assists/src/handlers/reorder_fields.rs
@@ -47,9 +47,11 @@ fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
47 "Reorder record fields", 47 "Reorder record fields",
48 target, 48 target,
49 |edit| { 49 |edit| {
50 let mut rewriter = algo::SyntaxRewriter::default();
50 for (old, new) in fields.iter().zip(&sorted_fields) { 51 for (old, new) in fields.iter().zip(&sorted_fields) {
51 algo::diff(old, new).into_text_edit(edit.text_edit_builder()); 52 rewriter.replace(old, new);
52 } 53 }
54 edit.rewrite(rewriter);
53 }, 55 },
54 ) 56 )
55} 57}
diff --git a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
new file mode 100644
index 000000000..82625516c
--- /dev/null
+++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -0,0 +1,398 @@
1use ide_db::imports_locator;
2use itertools::Itertools;
3use syntax::{
4 ast::{self, make, AstNode},
5 Direction, SmolStr,
6 SyntaxKind::{IDENT, WHITESPACE},
7 TextSize,
8};
9
10use crate::{
11 assist_context::{AssistBuilder, AssistContext, Assists},
12 utils::{
13 add_trait_assoc_items_to_impl, filter_assoc_items, mod_path_to_ast, render_snippet, Cursor,
14 DefaultMethods,
15 },
16 AssistId, AssistKind,
17};
18
19// Assist: replace_derive_with_manual_impl
20//
21// Converts a `derive` impl into a manual one.
22//
23// ```
24// # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
25// #[derive(Deb<|>ug, Display)]
26// struct S;
27// ```
28// ->
29// ```
30// # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
31// #[derive(Display)]
32// struct S;
33//
34// impl Debug for S {
35// fn fmt(&self, f: &mut Formatter) -> Result<()> {
36// ${0:todo!()}
37// }
38// }
39// ```
40pub(crate) fn replace_derive_with_manual_impl(
41 acc: &mut Assists,
42 ctx: &AssistContext,
43) -> Option<()> {
44 let attr = ctx.find_node_at_offset::<ast::Attr>()?;
45
46 let attr_name = attr
47 .syntax()
48 .descendants_with_tokens()
49 .filter(|t| t.kind() == IDENT)
50 .find_map(syntax::NodeOrToken::into_token)
51 .filter(|t| t.text() == "derive")?
52 .text()
53 .clone();
54
55 let trait_token =
56 ctx.token_at_offset().find(|t| t.kind() == IDENT && *t.text() != attr_name)?;
57 let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text())));
58
59 let annotated_name = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?;
60 let insert_pos = annotated_name.syntax().parent()?.text_range().end();
61
62 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?;
63 let current_crate = current_module.krate();
64
65 let found_traits = imports_locator::find_imports(&ctx.sema, current_crate, trait_token.text())
66 .into_iter()
67 .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate {
68 either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_),
69 _ => None,
70 })
71 .flat_map(|trait_| {
72 current_module
73 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
74 .as_ref()
75 .map(mod_path_to_ast)
76 .zip(Some(trait_))
77 });
78
79 let mut no_traits_found = true;
80 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
81 add_assist(acc, ctx, &attr, &trait_path, Some(trait_), &annotated_name, insert_pos)?;
82 }
83 if no_traits_found {
84 add_assist(acc, ctx, &attr, &trait_path, None, &annotated_name, insert_pos)?;
85 }
86 Some(())
87}
88
89fn add_assist(
90 acc: &mut Assists,
91 ctx: &AssistContext,
92 attr: &ast::Attr,
93 trait_path: &ast::Path,
94 trait_: Option<hir::Trait>,
95 annotated_name: &ast::Name,
96 insert_pos: TextSize,
97) -> Option<()> {
98 let target = attr.syntax().text_range();
99 let input = attr.token_tree()?;
100 let label = format!("Convert to manual `impl {} for {}`", trait_path, annotated_name);
101 let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?;
102
103 acc.add(
104 AssistId("replace_derive_with_manual_impl", AssistKind::Refactor),
105 label,
106 target,
107 |builder| {
108 let impl_def_with_items =
109 impl_def_from_trait(&ctx.sema, annotated_name, trait_, trait_path);
110 update_attribute(builder, &input, &trait_name, &attr);
111 match (ctx.config.snippet_cap, impl_def_with_items) {
112 (None, _) => builder.insert(
113 insert_pos,
114 format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name),
115 ),
116 (Some(cap), None) => builder.insert_snippet(
117 cap,
118 insert_pos,
119 format!("\n\nimpl {} for {} {{\n $0\n}}", trait_path, annotated_name),
120 ),
121 (Some(cap), Some((impl_def, first_assoc_item))) => {
122 let mut cursor = Cursor::Before(first_assoc_item.syntax());
123 let placeholder;
124 if let ast::AssocItem::Fn(ref func) = first_assoc_item {
125 if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
126 {
127 if m.syntax().text() == "todo!()" {
128 placeholder = m;
129 cursor = Cursor::Replace(placeholder.syntax());
130 }
131 }
132 }
133
134 builder.insert_snippet(
135 cap,
136 insert_pos,
137 format!("\n\n{}", render_snippet(cap, impl_def.syntax(), cursor)),
138 )
139 }
140 };
141 },
142 )
143}
144
145fn impl_def_from_trait(
146 sema: &hir::Semantics<ide_db::RootDatabase>,
147 annotated_name: &ast::Name,
148 trait_: Option<hir::Trait>,
149 trait_path: &ast::Path,
150) -> Option<(ast::Impl, ast::AssocItem)> {
151 let trait_ = trait_?;
152 let target_scope = sema.scope(annotated_name.syntax());
153 let trait_items = filter_assoc_items(sema.db, &trait_.items(sema.db), DefaultMethods::No);
154 if trait_items.is_empty() {
155 return None;
156 }
157 let impl_def = make::impl_trait(
158 trait_path.clone(),
159 make::path_unqualified(make::path_segment(make::name_ref(annotated_name.text()))),
160 );
161 let (impl_def, first_assoc_item) =
162 add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope);
163 Some((impl_def, first_assoc_item))
164}
165
166fn update_attribute(
167 builder: &mut AssistBuilder,
168 input: &ast::TokenTree,
169 trait_name: &ast::NameRef,
170 attr: &ast::Attr,
171) {
172 let new_attr_input = input
173 .syntax()
174 .descendants_with_tokens()
175 .filter(|t| t.kind() == IDENT)
176 .filter_map(|t| t.into_token().map(|t| t.text().clone()))
177 .filter(|t| t != trait_name.text())
178 .collect::<Vec<SmolStr>>();
179 let has_more_derives = !new_attr_input.is_empty();
180
181 if has_more_derives {
182 let new_attr_input = format!("({})", new_attr_input.iter().format(", "));
183 builder.replace(input.syntax().text_range(), new_attr_input);
184 } else {
185 let attr_range = attr.syntax().text_range();
186 builder.delete(attr_range);
187
188 if let Some(line_break_range) = attr
189 .syntax()
190 .next_sibling_or_token()
191 .filter(|t| t.kind() == WHITESPACE)
192 .map(|t| t.text_range())
193 {
194 builder.delete(line_break_range);
195 }
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use crate::tests::{check_assist, check_assist_not_applicable};
202
203 use super::*;
204
205 #[test]
206 fn add_custom_impl_debug() {
207 check_assist(
208 replace_derive_with_manual_impl,
209 "
210mod fmt {
211 pub struct Error;
212 pub type Result = Result<(), Error>;
213 pub struct Formatter<'a>;
214 pub trait Debug {
215 fn fmt(&self, f: &mut Formatter<'_>) -> Result;
216 }
217}
218
219#[derive(Debu<|>g)]
220struct Foo {
221 bar: String,
222}
223",
224 "
225mod fmt {
226 pub struct Error;
227 pub type Result = Result<(), Error>;
228 pub struct Formatter<'a>;
229 pub trait Debug {
230 fn fmt(&self, f: &mut Formatter<'_>) -> Result;
231 }
232}
233
234struct Foo {
235 bar: String,
236}
237
238impl fmt::Debug for Foo {
239 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240 ${0:todo!()}
241 }
242}
243",
244 )
245 }
246 #[test]
247 fn add_custom_impl_all() {
248 check_assist(
249 replace_derive_with_manual_impl,
250 "
251mod foo {
252 pub trait Bar {
253 type Qux;
254 const Baz: usize = 42;
255 const Fez: usize;
256 fn foo();
257 fn bar() {}
258 }
259}
260
261#[derive(<|>Bar)]
262struct Foo {
263 bar: String,
264}
265",
266 "
267mod foo {
268 pub trait Bar {
269 type Qux;
270 const Baz: usize = 42;
271 const Fez: usize;
272 fn foo();
273 fn bar() {}
274 }
275}
276
277struct Foo {
278 bar: String,
279}
280
281impl foo::Bar for Foo {
282 $0type Qux;
283
284 const Baz: usize = 42;
285
286 const Fez: usize;
287
288 fn foo() {
289 todo!()
290 }
291}
292",
293 )
294 }
295 #[test]
296 fn add_custom_impl_for_unique_input() {
297 check_assist(
298 replace_derive_with_manual_impl,
299 "
300#[derive(Debu<|>g)]
301struct Foo {
302 bar: String,
303}
304 ",
305 "
306struct Foo {
307 bar: String,
308}
309
310impl Debug for Foo {
311 $0
312}
313 ",
314 )
315 }
316
317 #[test]
318 fn add_custom_impl_for_with_visibility_modifier() {
319 check_assist(
320 replace_derive_with_manual_impl,
321 "
322#[derive(Debug<|>)]
323pub struct Foo {
324 bar: String,
325}
326 ",
327 "
328pub struct Foo {
329 bar: String,
330}
331
332impl Debug for Foo {
333 $0
334}
335 ",
336 )
337 }
338
339 #[test]
340 fn add_custom_impl_when_multiple_inputs() {
341 check_assist(
342 replace_derive_with_manual_impl,
343 "
344#[derive(Display, Debug<|>, Serialize)]
345struct Foo {}
346 ",
347 "
348#[derive(Display, Serialize)]
349struct Foo {}
350
351impl Debug for Foo {
352 $0
353}
354 ",
355 )
356 }
357
358 #[test]
359 fn test_ignore_derive_macro_without_input() {
360 check_assist_not_applicable(
361 replace_derive_with_manual_impl,
362 "
363#[derive(<|>)]
364struct Foo {}
365 ",
366 )
367 }
368
369 #[test]
370 fn test_ignore_if_cursor_on_param() {
371 check_assist_not_applicable(
372 replace_derive_with_manual_impl,
373 "
374#[derive<|>(Debug)]
375struct Foo {}
376 ",
377 );
378
379 check_assist_not_applicable(
380 replace_derive_with_manual_impl,
381 "
382#[derive(Debug)<|>]
383struct Foo {}
384 ",
385 )
386 }
387
388 #[test]
389 fn test_ignore_if_not_derive() {
390 check_assist_not_applicable(
391 replace_derive_with_manual_impl,
392 "
393#[allow(non_camel_<|>case_types)]
394struct Foo {}
395 ",
396 )
397 }
398}
diff --git a/crates/assists/src/handlers/wrap_return_type_in_result.rs b/crates/assists/src/handlers/wrap_return_type_in_result.rs
new file mode 100644
index 000000000..59e5debb1
--- /dev/null
+++ b/crates/assists/src/handlers/wrap_return_type_in_result.rs
@@ -0,0 +1,1158 @@
1use std::iter;
2
3use syntax::{
4 ast::{self, make, BlockExpr, Expr, LoopBodyOwner},
5 match_ast, AstNode, SyntaxNode,
6};
7use test_utils::mark;
8
9use crate::{AssistContext, AssistId, AssistKind, Assists};
10
11// Assist: wrap_return_type_in_result
12//
13// Wrap the function's return type into Result.
14//
15// ```
16// fn foo() -> i32<|> { 42i32 }
17// ```
18// ->
19// ```
20// fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
21// ```
22pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
23 let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
24 let parent = ret_type.syntax().parent()?;
25 let block_expr = match_ast! {
26 match parent {
27 ast::Fn(func) => func.body()?,
28 ast::ClosureExpr(closure) => match closure.body()? {
29 Expr::BlockExpr(block) => block,
30 // closures require a block when a return type is specified
31 _ => return None,
32 },
33 _ => return None,
34 }
35 };
36
37 let type_ref = &ret_type.ty()?;
38 let ret_type_str = type_ref.syntax().text().to_string();
39 let first_part_ret_type = ret_type_str.splitn(2, '<').next();
40 if let Some(ret_type_first_part) = first_part_ret_type {
41 if ret_type_first_part.ends_with("Result") {
42 mark::hit!(wrap_return_type_in_result_simple_return_type_already_result);
43 return None;
44 }
45 }
46
47 acc.add(
48 AssistId("wrap_return_type_in_result", AssistKind::RefactorRewrite),
49 "Wrap return type in Result",
50 type_ref.syntax().text_range(),
51 |builder| {
52 let mut tail_return_expr_collector = TailReturnCollector::new();
53 tail_return_expr_collector.collect_jump_exprs(&block_expr, false);
54 tail_return_expr_collector.collect_tail_exprs(&block_expr);
55
56 for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap {
57 let ok_wrapped = make::expr_call(
58 make::expr_path(make::path_unqualified(make::path_segment(make::name_ref(
59 "Ok",
60 )))),
61 make::arg_list(iter::once(ret_expr_arg.clone())),
62 );
63 builder.replace_ast(ret_expr_arg, ok_wrapped);
64 }
65
66 match ctx.config.snippet_cap {
67 Some(cap) => {
68 let snippet = format!("Result<{}, ${{0:_}}>", type_ref);
69 builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet)
70 }
71 None => builder
72 .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)),
73 }
74 },
75 )
76}
77
78struct TailReturnCollector {
79 exprs_to_wrap: Vec<ast::Expr>,
80}
81
82impl TailReturnCollector {
83 fn new() -> Self {
84 Self { exprs_to_wrap: vec![] }
85 }
86 /// Collect all`return` expression
87 fn collect_jump_exprs(&mut self, block_expr: &BlockExpr, collect_break: bool) {
88 let statements = block_expr.statements();
89 for stmt in statements {
90 let expr = match &stmt {
91 ast::Stmt::ExprStmt(stmt) => stmt.expr(),
92 ast::Stmt::LetStmt(stmt) => stmt.initializer(),
93 ast::Stmt::Item(_) => continue,
94 };
95 if let Some(expr) = &expr {
96 self.handle_exprs(expr, collect_break);
97 }
98 }
99
100 // Browse tail expressions for each block
101 if let Some(expr) = block_expr.expr() {
102 if let Some(last_exprs) = get_tail_expr_from_block(&expr) {
103 for last_expr in last_exprs {
104 let last_expr = match last_expr {
105 NodeType::Node(expr) => expr,
106 NodeType::Leaf(expr) => expr.syntax().clone(),
107 };
108
109 if let Some(last_expr) = Expr::cast(last_expr.clone()) {
110 self.handle_exprs(&last_expr, collect_break);
111 } else if let Some(expr_stmt) = ast::Stmt::cast(last_expr) {
112 let expr_stmt = match &expr_stmt {
113 ast::Stmt::ExprStmt(stmt) => stmt.expr(),
114 ast::Stmt::LetStmt(stmt) => stmt.initializer(),
115 ast::Stmt::Item(_) => None,
116 };
117 if let Some(expr) = &expr_stmt {
118 self.handle_exprs(expr, collect_break);
119 }
120 }
121 }
122 }
123 }
124 }
125
126 fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) {
127 match expr {
128 Expr::BlockExpr(block_expr) => {
129 self.collect_jump_exprs(&block_expr, collect_break);
130 }
131 Expr::ReturnExpr(ret_expr) => {
132 if let Some(ret_expr_arg) = &ret_expr.expr() {
133 self.exprs_to_wrap.push(ret_expr_arg.clone());
134 }
135 }
136 Expr::BreakExpr(break_expr) if collect_break => {
137 if let Some(break_expr_arg) = &break_expr.expr() {
138 self.exprs_to_wrap.push(break_expr_arg.clone());
139 }
140 }
141 Expr::IfExpr(if_expr) => {
142 for block in if_expr.blocks() {
143 self.collect_jump_exprs(&block, collect_break);
144 }
145 }
146 Expr::LoopExpr(loop_expr) => {
147 if let Some(block_expr) = loop_expr.loop_body() {
148 self.collect_jump_exprs(&block_expr, collect_break);
149 }
150 }
151 Expr::ForExpr(for_expr) => {
152 if let Some(block_expr) = for_expr.loop_body() {
153 self.collect_jump_exprs(&block_expr, collect_break);
154 }
155 }
156 Expr::WhileExpr(while_expr) => {
157 if let Some(block_expr) = while_expr.loop_body() {
158 self.collect_jump_exprs(&block_expr, collect_break);
159 }
160 }
161 Expr::MatchExpr(match_expr) => {
162 if let Some(arm_list) = match_expr.match_arm_list() {
163 arm_list.arms().filter_map(|match_arm| match_arm.expr()).for_each(|expr| {
164 self.handle_exprs(&expr, collect_break);
165 });
166 }
167 }
168 _ => {}
169 }
170 }
171
172 fn collect_tail_exprs(&mut self, block: &BlockExpr) {
173 if let Some(expr) = block.expr() {
174 self.handle_exprs(&expr, true);
175 self.fetch_tail_exprs(&expr);
176 }
177 }
178
179 fn fetch_tail_exprs(&mut self, expr: &Expr) {
180 if let Some(exprs) = get_tail_expr_from_block(expr) {
181 for node_type in &exprs {
182 match node_type {
183 NodeType::Leaf(expr) => {
184 self.exprs_to_wrap.push(expr.clone());
185 }
186 NodeType::Node(expr) => {
187 if let Some(last_expr) = Expr::cast(expr.clone()) {
188 self.fetch_tail_exprs(&last_expr);
189 }
190 }
191 }
192 }
193 }
194 }
195}
196
197#[derive(Debug)]
198enum NodeType {
199 Leaf(ast::Expr),
200 Node(SyntaxNode),
201}
202
203/// Get a tail expression inside a block
204fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> {
205 match expr {
206 Expr::IfExpr(if_expr) => {
207 let mut nodes = vec![];
208 for block in if_expr.blocks() {
209 if let Some(block_expr) = block.expr() {
210 if let Some(tail_exprs) = get_tail_expr_from_block(&block_expr) {
211 nodes.extend(tail_exprs);
212 }
213 } else if let Some(last_expr) = block.syntax().last_child() {
214 nodes.push(NodeType::Node(last_expr));
215 } else {
216 nodes.push(NodeType::Node(block.syntax().clone()));
217 }
218 }
219 Some(nodes)
220 }
221 Expr::LoopExpr(loop_expr) => {
222 loop_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
223 }
224 Expr::ForExpr(for_expr) => {
225 for_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
226 }
227 Expr::WhileExpr(while_expr) => {
228 while_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
229 }
230 Expr::BlockExpr(block_expr) => {
231 block_expr.expr().map(|lc| vec![NodeType::Node(lc.syntax().clone())])
232 }
233 Expr::MatchExpr(match_expr) => {
234 let arm_list = match_expr.match_arm_list()?;
235 let arms: Vec<NodeType> = arm_list
236 .arms()
237 .filter_map(|match_arm| match_arm.expr())
238 .map(|expr| match expr {
239 Expr::ReturnExpr(ret_expr) => NodeType::Node(ret_expr.syntax().clone()),
240 Expr::BreakExpr(break_expr) => NodeType::Node(break_expr.syntax().clone()),
241 _ => match expr.syntax().last_child() {
242 Some(last_expr) => NodeType::Node(last_expr),
243 None => NodeType::Node(expr.syntax().clone()),
244 },
245 })
246 .collect();
247
248 Some(arms)
249 }
250 Expr::BreakExpr(expr) => expr.expr().map(|e| vec![NodeType::Leaf(e)]),
251 Expr::ReturnExpr(ret_expr) => Some(vec![NodeType::Node(ret_expr.syntax().clone())]),
252
253 Expr::CallExpr(_)
254 | Expr::Literal(_)
255 | Expr::TupleExpr(_)
256 | Expr::ArrayExpr(_)
257 | Expr::ParenExpr(_)
258 | Expr::PathExpr(_)
259 | Expr::RecordExpr(_)
260 | Expr::IndexExpr(_)
261 | Expr::MethodCallExpr(_)
262 | Expr::AwaitExpr(_)
263 | Expr::CastExpr(_)
264 | Expr::RefExpr(_)
265 | Expr::PrefixExpr(_)
266 | Expr::RangeExpr(_)
267 | Expr::BinExpr(_)
268 | Expr::MacroCall(_)
269 | Expr::BoxExpr(_) => Some(vec![NodeType::Leaf(expr.clone())]),
270 _ => None,
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use crate::tests::{check_assist, check_assist_not_applicable};
277
278 use super::*;
279
280 #[test]
281 fn wrap_return_type_in_result_simple() {
282 check_assist(
283 wrap_return_type_in_result,
284 r#"
285fn foo() -> i3<|>2 {
286 let test = "test";
287 return 42i32;
288}
289"#,
290 r#"
291fn foo() -> Result<i32, ${0:_}> {
292 let test = "test";
293 return Ok(42i32);
294}
295"#,
296 );
297 }
298
299 #[test]
300 fn wrap_return_type_in_result_simple_closure() {
301 check_assist(
302 wrap_return_type_in_result,
303 r#"
304fn foo() {
305 || -> i32<|> {
306 let test = "test";
307 return 42i32;
308 };
309}
310"#,
311 r#"
312fn foo() {
313 || -> Result<i32, ${0:_}> {
314 let test = "test";
315 return Ok(42i32);
316 };
317}
318"#,
319 );
320 }
321
322 #[test]
323 fn wrap_return_type_in_result_simple_return_type_bad_cursor() {
324 check_assist_not_applicable(
325 wrap_return_type_in_result,
326 r#"
327fn foo() -> i32 {
328 let test = "test";<|>
329 return 42i32;
330}
331"#,
332 );
333 }
334
335 #[test]
336 fn wrap_return_type_in_result_simple_return_type_bad_cursor_closure() {
337 check_assist_not_applicable(
338 wrap_return_type_in_result,
339 r#"
340fn foo() {
341 || -> i32 {
342 let test = "test";<|>
343 return 42i32;
344 };
345}
346"#,
347 );
348 }
349
350 #[test]
351 fn wrap_return_type_in_result_closure_non_block() {
352 check_assist_not_applicable(wrap_return_type_in_result, r#"fn foo() { || -> i<|>32 3; }"#);
353 }
354
355 #[test]
356 fn wrap_return_type_in_result_simple_return_type_already_result_std() {
357 check_assist_not_applicable(
358 wrap_return_type_in_result,
359 r#"
360fn foo() -> std::result::Result<i32<|>, String> {
361 let test = "test";
362 return 42i32;
363}
364"#,
365 );
366 }
367
368 #[test]
369 fn wrap_return_type_in_result_simple_return_type_already_result() {
370 mark::check!(wrap_return_type_in_result_simple_return_type_already_result);
371 check_assist_not_applicable(
372 wrap_return_type_in_result,
373 r#"
374fn foo() -> Result<i32<|>, String> {
375 let test = "test";
376 return 42i32;
377}
378"#,
379 );
380 }
381
382 #[test]
383 fn wrap_return_type_in_result_simple_return_type_already_result_closure() {
384 check_assist_not_applicable(
385 wrap_return_type_in_result,
386 r#"
387fn foo() {
388 || -> Result<i32<|>, String> {
389 let test = "test";
390 return 42i32;
391 };
392}
393"#,
394 );
395 }
396
397 #[test]
398 fn wrap_return_type_in_result_simple_with_cursor() {
399 check_assist(
400 wrap_return_type_in_result,
401 r#"
402fn foo() -> <|>i32 {
403 let test = "test";
404 return 42i32;
405}
406"#,
407 r#"
408fn foo() -> Result<i32, ${0:_}> {
409 let test = "test";
410 return Ok(42i32);
411}
412"#,
413 );
414 }
415
416 #[test]
417 fn wrap_return_type_in_result_simple_with_tail() {
418 check_assist(
419 wrap_return_type_in_result,
420 r#"
421fn foo() -><|> i32 {
422 let test = "test";
423 42i32
424}
425"#,
426 r#"
427fn foo() -> Result<i32, ${0:_}> {
428 let test = "test";
429 Ok(42i32)
430}
431"#,
432 );
433 }
434
435 #[test]
436 fn wrap_return_type_in_result_simple_with_tail_closure() {
437 check_assist(
438 wrap_return_type_in_result,
439 r#"
440fn foo() {
441 || -><|> i32 {
442 let test = "test";
443 42i32
444 };
445}
446"#,
447 r#"
448fn foo() {
449 || -> Result<i32, ${0:_}> {
450 let test = "test";
451 Ok(42i32)
452 };
453}
454"#,
455 );
456 }
457
458 #[test]
459 fn wrap_return_type_in_result_simple_with_tail_only() {
460 check_assist(
461 wrap_return_type_in_result,
462 r#"fn foo() -> i32<|> { 42i32 }"#,
463 r#"fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }"#,
464 );
465 }
466
467 #[test]
468 fn wrap_return_type_in_result_simple_with_tail_block_like() {
469 check_assist(
470 wrap_return_type_in_result,
471 r#"
472fn foo() -> i32<|> {
473 if true {
474 42i32
475 } else {
476 24i32
477 }
478}
479"#,
480 r#"
481fn foo() -> Result<i32, ${0:_}> {
482 if true {
483 Ok(42i32)
484 } else {
485 Ok(24i32)
486 }
487}
488"#,
489 );
490 }
491
492 #[test]
493 fn wrap_return_type_in_result_simple_without_block_closure() {
494 check_assist(
495 wrap_return_type_in_result,
496 r#"
497fn foo() {
498 || -> i32<|> {
499 if true {
500 42i32
501 } else {
502 24i32
503 }
504 };
505}
506"#,
507 r#"
508fn foo() {
509 || -> Result<i32, ${0:_}> {
510 if true {
511 Ok(42i32)
512 } else {
513 Ok(24i32)
514 }
515 };
516}
517"#,
518 );
519 }
520
521 #[test]
522 fn wrap_return_type_in_result_simple_with_nested_if() {
523 check_assist(
524 wrap_return_type_in_result,
525 r#"
526fn foo() -> i32<|> {
527 if true {
528 if false {
529 1
530 } else {
531 2
532 }
533 } else {
534 24i32
535 }
536}
537"#,
538 r#"
539fn foo() -> Result<i32, ${0:_}> {
540 if true {
541 if false {
542 Ok(1)
543 } else {
544 Ok(2)
545 }
546 } else {
547 Ok(24i32)
548 }
549}
550"#,
551 );
552 }
553
554 #[test]
555 fn wrap_return_type_in_result_simple_with_await() {
556 check_assist(
557 wrap_return_type_in_result,
558 r#"
559async fn foo() -> i<|>32 {
560 if true {
561 if false {
562 1.await
563 } else {
564 2.await
565 }
566 } else {
567 24i32.await
568 }
569}
570"#,
571 r#"
572async fn foo() -> Result<i32, ${0:_}> {
573 if true {
574 if false {
575 Ok(1.await)
576 } else {
577 Ok(2.await)
578 }
579 } else {
580 Ok(24i32.await)
581 }
582}
583"#,
584 );
585 }
586
587 #[test]
588 fn wrap_return_type_in_result_simple_with_array() {
589 check_assist(
590 wrap_return_type_in_result,
591 r#"fn foo() -> [i32;<|> 3] { [1, 2, 3] }"#,
592 r#"fn foo() -> Result<[i32; 3], ${0:_}> { Ok([1, 2, 3]) }"#,
593 );
594 }
595
596 #[test]
597 fn wrap_return_type_in_result_simple_with_cast() {
598 check_assist(
599 wrap_return_type_in_result,
600 r#"
601fn foo() -<|>> i32 {
602 if true {
603 if false {
604 1 as i32
605 } else {
606 2 as i32
607 }
608 } else {
609 24 as i32
610 }
611}
612"#,
613 r#"
614fn foo() -> Result<i32, ${0:_}> {
615 if true {
616 if false {
617 Ok(1 as i32)
618 } else {
619 Ok(2 as i32)
620 }
621 } else {
622 Ok(24 as i32)
623 }
624}
625"#,
626 );
627 }
628
629 #[test]
630 fn wrap_return_type_in_result_simple_with_tail_block_like_match() {
631 check_assist(
632 wrap_return_type_in_result,
633 r#"
634fn foo() -> i32<|> {
635 let my_var = 5;
636 match my_var {
637 5 => 42i32,
638 _ => 24i32,
639 }
640}
641"#,
642 r#"
643fn foo() -> Result<i32, ${0:_}> {
644 let my_var = 5;
645 match my_var {
646 5 => Ok(42i32),
647 _ => Ok(24i32),
648 }
649}
650"#,
651 );
652 }
653
654 #[test]
655 fn wrap_return_type_in_result_simple_with_loop_with_tail() {
656 check_assist(
657 wrap_return_type_in_result,
658 r#"
659fn foo() -> i32<|> {
660 let my_var = 5;
661 loop {
662 println!("test");
663 5
664 }
665 my_var
666}
667"#,
668 r#"
669fn foo() -> Result<i32, ${0:_}> {
670 let my_var = 5;
671 loop {
672 println!("test");
673 5
674 }
675 Ok(my_var)
676}
677"#,
678 );
679 }
680
681 #[test]
682 fn wrap_return_type_in_result_simple_with_loop_in_let_stmt() {
683 check_assist(
684 wrap_return_type_in_result,
685 r#"
686fn foo() -> i32<|> {
687 let my_var = let x = loop {
688 break 1;
689 };
690 my_var
691}
692"#,
693 r#"
694fn foo() -> Result<i32, ${0:_}> {
695 let my_var = let x = loop {
696 break 1;
697 };
698 Ok(my_var)
699}
700"#,
701 );
702 }
703
704 #[test]
705 fn wrap_return_type_in_result_simple_with_tail_block_like_match_return_expr() {
706 check_assist(
707 wrap_return_type_in_result,
708 r#"
709fn foo() -> i32<|> {
710 let my_var = 5;
711 let res = match my_var {
712 5 => 42i32,
713 _ => return 24i32,
714 };
715 res
716}
717"#,
718 r#"
719fn foo() -> Result<i32, ${0:_}> {
720 let my_var = 5;
721 let res = match my_var {
722 5 => 42i32,
723 _ => return Ok(24i32),
724 };
725 Ok(res)
726}
727"#,
728 );
729
730 check_assist(
731 wrap_return_type_in_result,
732 r#"
733fn foo() -> i32<|> {
734 let my_var = 5;
735 let res = if my_var == 5 {
736 42i32
737 } else {
738 return 24i32;
739 };
740 res
741}
742"#,
743 r#"
744fn foo() -> Result<i32, ${0:_}> {
745 let my_var = 5;
746 let res = if my_var == 5 {
747 42i32
748 } else {
749 return Ok(24i32);
750 };
751 Ok(res)
752}
753"#,
754 );
755 }
756
757 #[test]
758 fn wrap_return_type_in_result_simple_with_tail_block_like_match_deeper() {
759 check_assist(
760 wrap_return_type_in_result,
761 r#"
762fn foo() -> i32<|> {
763 let my_var = 5;
764 match my_var {
765 5 => {
766 if true {
767 42i32
768 } else {
769 25i32
770 }
771 },
772 _ => {
773 let test = "test";
774 if test == "test" {
775 return bar();
776 }
777 53i32
778 },
779 }
780}
781"#,
782 r#"
783fn foo() -> Result<i32, ${0:_}> {
784 let my_var = 5;
785 match my_var {
786 5 => {
787 if true {
788 Ok(42i32)
789 } else {
790 Ok(25i32)
791 }
792 },
793 _ => {
794 let test = "test";
795 if test == "test" {
796 return Ok(bar());
797 }
798 Ok(53i32)
799 },
800 }
801}
802"#,
803 );
804 }
805
806 #[test]
807 fn wrap_return_type_in_result_simple_with_tail_block_like_early_return() {
808 check_assist(
809 wrap_return_type_in_result,
810 r#"
811fn foo() -> i<|>32 {
812 let test = "test";
813 if test == "test" {
814 return 24i32;
815 }
816 53i32
817}
818"#,
819 r#"
820fn foo() -> Result<i32, ${0:_}> {
821 let test = "test";
822 if test == "test" {
823 return Ok(24i32);
824 }
825 Ok(53i32)
826}
827"#,
828 );
829 }
830
831 #[test]
832 fn wrap_return_type_in_result_simple_with_closure() {
833 check_assist(
834 wrap_return_type_in_result,
835 r#"
836fn foo(the_field: u32) -><|> u32 {
837 let true_closure = || { return true; };
838 if the_field < 5 {
839 let mut i = 0;
840 if true_closure() {
841 return 99;
842 } else {
843 return 0;
844 }
845 }
846 the_field
847}
848"#,
849 r#"
850fn foo(the_field: u32) -> Result<u32, ${0:_}> {
851 let true_closure = || { return true; };
852 if the_field < 5 {
853 let mut i = 0;
854 if true_closure() {
855 return Ok(99);
856 } else {
857 return Ok(0);
858 }
859 }
860 Ok(the_field)
861}
862"#,
863 );
864
865 check_assist(
866 wrap_return_type_in_result,
867 r#"
868 fn foo(the_field: u32) -> u32<|> {
869 let true_closure = || {
870 return true;
871 };
872 if the_field < 5 {
873 let mut i = 0;
874
875
876 if true_closure() {
877 return 99;
878 } else {
879 return 0;
880 }
881 }
882 let t = None;
883
884 t.unwrap_or_else(|| the_field)
885 }
886 "#,
887 r#"
888 fn foo(the_field: u32) -> Result<u32, ${0:_}> {
889 let true_closure = || {
890 return true;
891 };
892 if the_field < 5 {
893 let mut i = 0;
894
895
896 if true_closure() {
897 return Ok(99);
898 } else {
899 return Ok(0);
900 }
901 }
902 let t = None;
903
904 Ok(t.unwrap_or_else(|| the_field))
905 }
906 "#,
907 );
908 }
909
910 #[test]
911 fn wrap_return_type_in_result_simple_with_weird_forms() {
912 check_assist(
913 wrap_return_type_in_result,
914 r#"
915fn foo() -> i32<|> {
916 let test = "test";
917 if test == "test" {
918 return 24i32;
919 }
920 let mut i = 0;
921 loop {
922 if i == 1 {
923 break 55;
924 }
925 i += 1;
926 }
927}
928"#,
929 r#"
930fn foo() -> Result<i32, ${0:_}> {
931 let test = "test";
932 if test == "test" {
933 return Ok(24i32);
934 }
935 let mut i = 0;
936 loop {
937 if i == 1 {
938 break Ok(55);
939 }
940 i += 1;
941 }
942}
943"#,
944 );
945
946 check_assist(
947 wrap_return_type_in_result,
948 r#"
949fn foo() -> i32<|> {
950 let test = "test";
951 if test == "test" {
952 return 24i32;
953 }
954 let mut i = 0;
955 loop {
956 loop {
957 if i == 1 {
958 break 55;
959 }
960 i += 1;
961 }
962 }
963}
964"#,
965 r#"
966fn foo() -> Result<i32, ${0:_}> {
967 let test = "test";
968 if test == "test" {
969 return Ok(24i32);
970 }
971 let mut i = 0;
972 loop {
973 loop {
974 if i == 1 {
975 break Ok(55);
976 }
977 i += 1;
978 }
979 }
980}
981"#,
982 );
983
984 check_assist(
985 wrap_return_type_in_result,
986 r#"
987fn foo() -> i3<|>2 {
988 let test = "test";
989 let other = 5;
990 if test == "test" {
991 let res = match other {
992 5 => 43,
993 _ => return 56,
994 };
995 }
996 let mut i = 0;
997 loop {
998 loop {
999 if i == 1 {
1000 break 55;
1001 }
1002 i += 1;
1003 }
1004 }
1005}
1006"#,
1007 r#"
1008fn foo() -> Result<i32, ${0:_}> {
1009 let test = "test";
1010 let other = 5;
1011 if test == "test" {
1012 let res = match other {
1013 5 => 43,
1014 _ => return Ok(56),
1015 };
1016 }
1017 let mut i = 0;
1018 loop {
1019 loop {
1020 if i == 1 {
1021 break Ok(55);
1022 }
1023 i += 1;
1024 }
1025 }
1026}
1027"#,
1028 );
1029
1030 check_assist(
1031 wrap_return_type_in_result,
1032 r#"
1033fn foo(the_field: u32) -> u32<|> {
1034 if the_field < 5 {
1035 let mut i = 0;
1036 loop {
1037 if i > 5 {
1038 return 55u32;
1039 }
1040 i += 3;
1041 }
1042 match i {
1043 5 => return 99,
1044 _ => return 0,
1045 };
1046 }
1047 the_field
1048}
1049"#,
1050 r#"
1051fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1052 if the_field < 5 {
1053 let mut i = 0;
1054 loop {
1055 if i > 5 {
1056 return Ok(55u32);
1057 }
1058 i += 3;
1059 }
1060 match i {
1061 5 => return Ok(99),
1062 _ => return Ok(0),
1063 };
1064 }
1065 Ok(the_field)
1066}
1067"#,
1068 );
1069
1070 check_assist(
1071 wrap_return_type_in_result,
1072 r#"
1073fn foo(the_field: u32) -> u3<|>2 {
1074 if the_field < 5 {
1075 let mut i = 0;
1076 match i {
1077 5 => return 99,
1078 _ => return 0,
1079 }
1080 }
1081 the_field
1082}
1083"#,
1084 r#"
1085fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1086 if the_field < 5 {
1087 let mut i = 0;
1088 match i {
1089 5 => return Ok(99),
1090 _ => return Ok(0),
1091 }
1092 }
1093 Ok(the_field)
1094}
1095"#,
1096 );
1097
1098 check_assist(
1099 wrap_return_type_in_result,
1100 r#"
1101fn foo(the_field: u32) -> u32<|> {
1102 if the_field < 5 {
1103 let mut i = 0;
1104 if i == 5 {
1105 return 99
1106 } else {
1107 return 0
1108 }
1109 }
1110 the_field
1111}
1112"#,
1113 r#"
1114fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1115 if the_field < 5 {
1116 let mut i = 0;
1117 if i == 5 {
1118 return Ok(99)
1119 } else {
1120 return Ok(0)
1121 }
1122 }
1123 Ok(the_field)
1124}
1125"#,
1126 );
1127
1128 check_assist(
1129 wrap_return_type_in_result,
1130 r#"
1131fn foo(the_field: u32) -> <|>u32 {
1132 if the_field < 5 {
1133 let mut i = 0;
1134 if i == 5 {
1135 return 99;
1136 } else {
1137 return 0;
1138 }
1139 }
1140 the_field
1141}
1142"#,
1143 r#"
1144fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1145 if the_field < 5 {
1146 let mut i = 0;
1147 if i == 5 {
1148 return Ok(99);
1149 } else {
1150 return Ok(0);
1151 }
1152 }
1153 Ok(the_field)
1154}
1155"#,
1156 );
1157 }
1158}
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index af88b3437..e8d81b33d 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -120,13 +120,11 @@ mod handlers {
120 120
121 pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>; 121 pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>;
122 122
123 mod add_custom_impl;
124 mod add_explicit_type; 123 mod add_explicit_type;
125 mod add_missing_impl_members; 124 mod add_missing_impl_members;
126 mod add_turbo_fish; 125 mod add_turbo_fish;
127 mod apply_demorgan; 126 mod apply_demorgan;
128 mod auto_import; 127 mod auto_import;
129 mod change_return_type_to_result;
130 mod change_visibility; 128 mod change_visibility;
131 mod convert_integer_literal; 129 mod convert_integer_literal;
132 mod early_return; 130 mod early_return;
@@ -157,6 +155,7 @@ mod handlers {
157 mod remove_mut; 155 mod remove_mut;
158 mod remove_unused_param; 156 mod remove_unused_param;
159 mod reorder_fields; 157 mod reorder_fields;
158 mod replace_derive_with_manual_impl;
160 mod replace_if_let_with_match; 159 mod replace_if_let_with_match;
161 mod replace_impl_trait_with_generic; 160 mod replace_impl_trait_with_generic;
162 mod replace_let_with_if_let; 161 mod replace_let_with_if_let;
@@ -165,16 +164,15 @@ mod handlers {
165 mod replace_unwrap_with_match; 164 mod replace_unwrap_with_match;
166 mod split_import; 165 mod split_import;
167 mod unwrap_block; 166 mod unwrap_block;
167 mod wrap_return_type_in_result;
168 168
169 pub(crate) fn all() -> &'static [Handler] { 169 pub(crate) fn all() -> &'static [Handler] {
170 &[ 170 &[
171 // These are alphabetic for the foolish consistency 171 // These are alphabetic for the foolish consistency
172 add_custom_impl::add_custom_impl,
173 add_explicit_type::add_explicit_type, 172 add_explicit_type::add_explicit_type,
174 add_turbo_fish::add_turbo_fish, 173 add_turbo_fish::add_turbo_fish,
175 apply_demorgan::apply_demorgan, 174 apply_demorgan::apply_demorgan,
176 auto_import::auto_import, 175 auto_import::auto_import,
177 change_return_type_to_result::change_return_type_to_result,
178 change_visibility::change_visibility, 176 change_visibility::change_visibility,
179 convert_integer_literal::convert_integer_literal, 177 convert_integer_literal::convert_integer_literal,
180 early_return::convert_to_guarded_return, 178 early_return::convert_to_guarded_return,
@@ -208,6 +206,7 @@ mod handlers {
208 remove_mut::remove_mut, 206 remove_mut::remove_mut,
209 remove_unused_param::remove_unused_param, 207 remove_unused_param::remove_unused_param,
210 reorder_fields::reorder_fields, 208 reorder_fields::reorder_fields,
209 replace_derive_with_manual_impl::replace_derive_with_manual_impl,
211 replace_if_let_with_match::replace_if_let_with_match, 210 replace_if_let_with_match::replace_if_let_with_match,
212 replace_impl_trait_with_generic::replace_impl_trait_with_generic, 211 replace_impl_trait_with_generic::replace_impl_trait_with_generic,
213 replace_let_with_if_let::replace_let_with_if_let, 212 replace_let_with_if_let::replace_let_with_if_let,
@@ -215,6 +214,7 @@ mod handlers {
215 replace_unwrap_with_match::replace_unwrap_with_match, 214 replace_unwrap_with_match::replace_unwrap_with_match,
216 split_import::split_import, 215 split_import::split_import,
217 unwrap_block::unwrap_block, 216 unwrap_block::unwrap_block,
217 wrap_return_type_in_result::wrap_return_type_in_result,
218 // These are manually sorted for better priorities 218 // These are manually sorted for better priorities
219 add_missing_impl_members::add_missing_impl_members, 219 add_missing_impl_members::add_missing_impl_members,
220 add_missing_impl_members::add_missing_default_members, 220 add_missing_impl_members::add_missing_default_members,
diff --git a/crates/assists/src/tests.rs b/crates/assists/src/tests.rs
index 849d85e76..709a34d03 100644
--- a/crates/assists/src/tests.rs
+++ b/crates/assists/src/tests.rs
@@ -7,7 +7,7 @@ use syntax::TextRange;
7use test_utils::{assert_eq_text, extract_offset, extract_range}; 7use test_utils::{assert_eq_text, extract_offset, extract_range};
8 8
9use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists}; 9use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists};
10use stdx::trim_indent; 10use stdx::{format_to, trim_indent};
11 11
12pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { 12pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
13 RootDatabase::with_single_file(text) 13 RootDatabase::with_single_file(text)
@@ -98,11 +98,24 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
98 match (assist, expected) { 98 match (assist, expected) {
99 (Some(assist), ExpectedResult::After(after)) => { 99 (Some(assist), ExpectedResult::After(after)) => {
100 let mut source_change = assist.source_change; 100 let mut source_change = assist.source_change;
101 let change = source_change.source_file_edits.pop().unwrap(); 101 assert!(!source_change.source_file_edits.is_empty());
102 102 let skip_header = source_change.source_file_edits.len() == 1;
103 let mut actual = db.file_text(change.file_id).as_ref().to_owned(); 103 source_change.source_file_edits.sort_by_key(|it| it.file_id);
104 change.edit.apply(&mut actual); 104
105 assert_eq_text!(after, &actual); 105 let mut buf = String::new();
106 for source_file_edit in source_change.source_file_edits {
107 let mut text = db.file_text(source_file_edit.file_id).as_ref().to_owned();
108 source_file_edit.edit.apply(&mut text);
109 if !skip_header {
110 let sr = db.file_source_root(source_file_edit.file_id);
111 let sr = db.source_root(sr);
112 let path = sr.path_for_file(&source_file_edit.file_id).unwrap();
113 format_to!(buf, "//- {}\n", path)
114 }
115 buf.push_str(&text);
116 }
117
118 assert_eq_text!(after, &buf);
106 } 119 }
107 (Some(assist), ExpectedResult::Target(target)) => { 120 (Some(assist), ExpectedResult::Target(target)) => {
108 let range = assist.assist.target; 121 let range = assist.assist.target;
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index 168e1626a..dbf4f21aa 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -3,25 +3,6 @@
3use super::check_doc_test; 3use super::check_doc_test;
4 4
5#[test] 5#[test]
6fn doctest_add_custom_impl() {
7 check_doc_test(
8 "add_custom_impl",
9 r#####"
10#[derive(Deb<|>ug, Display)]
11struct S;
12"#####,
13 r#####"
14#[derive(Display)]
15struct S;
16
17impl Debug for S {
18 $0
19}
20"#####,
21 )
22}
23
24#[test]
25fn doctest_add_explicit_type() { 6fn doctest_add_explicit_type() {
26 check_doc_test( 7 check_doc_test(
27 "add_explicit_type", 8 "add_explicit_type",
@@ -178,19 +159,6 @@ pub mod std { pub mod collections { pub struct HashMap { } } }
178} 159}
179 160
180#[test] 161#[test]
181fn doctest_change_return_type_to_result() {
182 check_doc_test(
183 "change_return_type_to_result",
184 r#####"
185fn foo() -> i32<|> { 42i32 }
186"#####,
187 r#####"
188fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
189"#####,
190 )
191}
192
193#[test]
194fn doctest_change_visibility() { 162fn doctest_change_visibility() {
195 check_doc_test( 163 check_doc_test(
196 "change_visibility", 164 "change_visibility",
@@ -832,6 +800,29 @@ const test: Foo = Foo {foo: 1, bar: 0}
832} 800}
833 801
834#[test] 802#[test]
803fn doctest_replace_derive_with_manual_impl() {
804 check_doc_test(
805 "replace_derive_with_manual_impl",
806 r#####"
807trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
808#[derive(Deb<|>ug, Display)]
809struct S;
810"#####,
811 r#####"
812trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
813#[derive(Display)]
814struct S;
815
816impl Debug for S {
817 fn fmt(&self, f: &mut Formatter) -> Result<()> {
818 ${0:todo!()}
819 }
820}
821"#####,
822 )
823}
824
825#[test]
835fn doctest_replace_if_let_with_match() { 826fn doctest_replace_if_let_with_match() {
836 check_doc_test( 827 check_doc_test(
837 "replace_if_let_with_match", 828 "replace_if_let_with_match",
@@ -985,3 +976,16 @@ fn foo() {
985"#####, 976"#####,
986 ) 977 )
987} 978}
979
980#[test]
981fn doctest_wrap_return_type_in_result() {
982 check_doc_test(
983 "wrap_return_type_in_result",
984 r#####"
985fn foo() -> i32<|> { 42i32 }
986"#####,
987 r#####"
988fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
989"#####,
990 )
991}
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 56f925ee6..7071fe96b 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -4,17 +4,22 @@ pub(crate) mod import_assets;
4 4
5use std::ops; 5use std::ops;
6 6
7use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait}; 7use hir::{Crate, Enum, HasSource, Module, ScopeDef, Semantics, Trait};
8use ide_db::RootDatabase; 8use ide_db::RootDatabase;
9use itertools::Itertools; 9use itertools::Itertools;
10use syntax::{ 10use syntax::{
11 ast::{self, make, ArgListOwner}, 11 ast::edit::AstNodeEdit,
12 ast::NameOwner,
13 ast::{self, edit, make, ArgListOwner},
12 AstNode, Direction, 14 AstNode, Direction,
13 SyntaxKind::*, 15 SyntaxKind::*,
14 SyntaxNode, TextSize, T, 16 SyntaxNode, TextSize, T,
15}; 17};
16 18
17use crate::assist_config::SnippetCap; 19use crate::{
20 assist_config::SnippetCap,
21 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
22};
18 23
19pub use insert_use::MergeBehaviour; 24pub use insert_use::MergeBehaviour;
20pub(crate) use insert_use::{insert_use, ImportScope}; 25pub(crate) use insert_use::{insert_use, ImportScope};
@@ -77,6 +82,87 @@ pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> {
77 None 82 None
78} 83}
79 84
85#[derive(Copy, Clone, PartialEq)]
86pub enum DefaultMethods {
87 Only,
88 No,
89}
90
91pub fn filter_assoc_items(
92 db: &RootDatabase,
93 items: &[hir::AssocItem],
94 default_methods: DefaultMethods,
95) -> Vec<ast::AssocItem> {
96 fn has_def_name(item: &ast::AssocItem) -> bool {
97 match item {
98 ast::AssocItem::Fn(def) => def.name(),
99 ast::AssocItem::TypeAlias(def) => def.name(),
100 ast::AssocItem::Const(def) => def.name(),
101 ast::AssocItem::MacroCall(_) => None,
102 }
103 .is_some()
104 };
105
106 items
107 .iter()
108 .map(|i| match i {
109 hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(db).value),
110 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(db).value),
111 hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(db).value),
112 })
113 .filter(has_def_name)
114 .filter(|it| match it {
115 ast::AssocItem::Fn(def) => matches!(
116 (default_methods, def.body()),
117 (DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None)
118 ),
119 _ => default_methods == DefaultMethods::No,
120 })
121 .collect::<Vec<_>>()
122}
123
124pub fn add_trait_assoc_items_to_impl(
125 sema: &hir::Semantics<ide_db::RootDatabase>,
126 items: Vec<ast::AssocItem>,
127 trait_: hir::Trait,
128 impl_def: ast::Impl,
129 target_scope: hir::SemanticsScope,
130) -> (ast::Impl, ast::AssocItem) {
131 let impl_item_list = impl_def.assoc_item_list().unwrap_or_else(make::assoc_item_list);
132
133 let n_existing_items = impl_item_list.assoc_items().count();
134 let source_scope = sema.scope_for_def(trait_);
135 let ast_transform = QualifyPaths::new(&target_scope, &source_scope)
136 .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone()));
137
138 let items = items
139 .into_iter()
140 .map(|it| ast_transform::apply(&*ast_transform, it))
141 .map(|it| match it {
142 ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)),
143 ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()),
144 _ => it,
145 })
146 .map(|it| edit::remove_attrs_and_docs(&it));
147
148 let new_impl_item_list = impl_item_list.append_items(items);
149 let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list);
150 let first_new_item =
151 new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap();
152 return (new_impl_def, first_new_item);
153
154 fn add_body(fn_def: ast::Fn) -> ast::Fn {
155 match fn_def.body() {
156 Some(_) => fn_def,
157 None => {
158 let body =
159 make::block_expr(None, Some(make::expr_todo())).indent(edit::IndentLevel(1));
160 fn_def.with_body(body)
161 }
162 }
163 }
164}
165
80#[derive(Clone, Copy, Debug)] 166#[derive(Clone, Copy, Debug)]
81pub(crate) enum Cursor<'a> { 167pub(crate) enum Cursor<'a> {
82 Replace(&'a SyntaxNode), 168 Replace(&'a SyntaxNode),
diff --git a/crates/completion/src/completions/postfix.rs b/crates/completion/src/completions/postfix.rs
index 348f017bd..7fbda7a6b 100644
--- a/crates/completion/src/completions/postfix.rs
+++ b/crates/completion/src/completions/postfix.rs
@@ -184,6 +184,16 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
184 ctx, 184 ctx,
185 cap, 185 cap,
186 &dot_receiver, 186 &dot_receiver,
187 "some",
188 "Some(expr)",
189 &format!("Some({})", receiver_text),
190 )
191 .add_to(acc);
192
193 postfix_snippet(
194 ctx,
195 cap,
196 &dot_receiver,
187 "dbg", 197 "dbg",
188 "dbg!(expr)", 198 "dbg!(expr)",
189 &format!("dbg!({})", receiver_text), 199 &format!("dbg!({})", receiver_text),
@@ -291,6 +301,7 @@ fn main() {
291 sn ok Ok(expr) 301 sn ok Ok(expr)
292 sn ref &expr 302 sn ref &expr
293 sn refm &mut expr 303 sn refm &mut expr
304 sn some Some(expr)
294 sn while while expr {} 305 sn while while expr {}
295 "#]], 306 "#]],
296 ); 307 );
@@ -314,6 +325,7 @@ fn main() {
314 sn ok Ok(expr) 325 sn ok Ok(expr)
315 sn ref &expr 326 sn ref &expr
316 sn refm &mut expr 327 sn refm &mut expr
328 sn some Some(expr)
317 "#]], 329 "#]],
318 ) 330 )
319 } 331 }
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 05bafe9c8..1ed77b40b 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -6,7 +6,7 @@ pub(crate) mod tags;
6#[cfg(test)] 6#[cfg(test)]
7mod tests; 7mod tests;
8 8
9use hir::{Local, Name, Semantics, VariantDef}; 9use hir::{AsAssocItem, Local, Name, Semantics, VariantDef};
10use ide_db::{ 10use ide_db::{
11 defs::{Definition, NameClass, NameRefClass}, 11 defs::{Definition, NameClass, NameRefClass},
12 RootDatabase, 12 RootDatabase,
@@ -557,7 +557,9 @@ fn highlight_element(
557 h 557 h
558 } 558 }
559 } 559 }
560 T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] => HighlightTag::Operator.into(), 560 T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => {
561 HighlightTag::Operator.into()
562 }
561 T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => { 563 T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => {
562 HighlightTag::Macro.into() 564 HighlightTag::Macro.into()
563 } 565 }
@@ -744,6 +746,9 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
744 if func.is_unsafe(db) { 746 if func.is_unsafe(db) {
745 h |= HighlightModifier::Unsafe; 747 h |= HighlightModifier::Unsafe;
746 } 748 }
749 if func.as_assoc_item(db).is_some() && func.self_param(db).is_none() {
750 h |= HighlightModifier::Static;
751 }
747 return h; 752 return h;
748 } 753 }
749 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, 754 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct,
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index e8f78ad52..65e0671a5 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -65,6 +65,8 @@ pub enum HighlightModifier {
65 Consuming, 65 Consuming,
66 Unsafe, 66 Unsafe,
67 Callable, 67 Callable,
68 /// Used for associated functions
69 Static,
68} 70}
69 71
70impl HighlightTag { 72impl HighlightTag {
@@ -124,6 +126,7 @@ impl HighlightModifier {
124 HighlightModifier::Consuming, 126 HighlightModifier::Consuming,
125 HighlightModifier::Unsafe, 127 HighlightModifier::Unsafe,
126 HighlightModifier::Callable, 128 HighlightModifier::Callable,
129 HighlightModifier::Static,
127 ]; 130 ];
128 131
129 fn as_str(self) -> &'static str { 132 fn as_str(self) -> &'static str {
@@ -137,6 +140,7 @@ impl HighlightModifier {
137 HighlightModifier::Consuming => "consuming", 140 HighlightModifier::Consuming => "consuming",
138 HighlightModifier::Unsafe => "unsafe", 141 HighlightModifier::Unsafe => "unsafe",
139 HighlightModifier::Callable => "callable", 142 HighlightModifier::Callable => "callable",
143 HighlightModifier::Static => "static",
140 } 144 }
141 } 145 }
142 146
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
new file mode 100644
index 000000000..cd80d72b7
--- /dev/null
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
@@ -0,0 +1,56 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; }
8.documentation { color: #629755; }
9.injected { opacity: 0.65 ; }
10.struct, .enum { color: #7CB8BB; }
11.enum_variant { color: #BDE0F3; }
12.string_literal { color: #CC9393; }
13.field { color: #94BFF3; }
14.function { color: #93E0E3; }
15.function.unsafe { color: #BC8383; }
16.operator.unsafe { color: #BC8383; }
17.parameter { color: #94BFF3; }
18.text { color: #DCDCCC; }
19.type { color: #7CB8BB; }
20.builtin_type { color: #8CD0D3; }
21.type_param { color: #DFAF8F; }
22.attribute { color: #94BFF3; }
23.numeric_literal { color: #BFEBBF; }
24.bool_literal { color: #BFE6EB; }
25.macro { color: #94BFF3; }
26.module { color: #AFD8AF; }
27.value_param { color: #DCDCCC; }
28.variable { color: #DCDCCC; }
29.format_specifier { color: #CC696B; }
30.mutable { text-decoration: underline; }
31.escape_sequence { color: #94BFF3; }
32.keyword { color: #F0DFAF; font-weight: bold; }
33.keyword.unsafe { color: #BC8383; font-weight: bold; }
34.control { font-style: italic; }
35
36.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
37</style>
38<pre><code><span class="keyword">fn</span> <span class="function declaration">not_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
39
40<span class="keyword">struct</span> <span class="struct declaration">foo</span> <span class="punctuation">{</span><span class="punctuation">}</span>
41
42<span class="keyword">impl</span> <span class="struct">foo</span> <span class="punctuation">{</span>
43 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
44 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
45<span class="punctuation">}</span>
46
47<span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="punctuation">{</span>
48 <span class="keyword">fn</span> <span class="function declaration static">t_is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
49 <span class="keyword">fn</span> <span class="function declaration">t_is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
50<span class="punctuation">}</span>
51
52<span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="punctuation">{</span>
53 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
54 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
55<span class="punctuation">}</span>
56 </code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 6322d404f..6be88f856 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -53,7 +53,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
53 <span class="comment documentation">/// #</span><span class="generic injected"> </span><span class="attribute injected">#</span><span class="attribute injected">!</span><span class="attribute injected">[</span><span class="function attribute injected">allow</span><span class="punctuation injected">(</span><span class="attribute injected">unused_mut</span><span class="punctuation injected">)</span><span class="attribute injected">]</span> 53 <span class="comment documentation">/// #</span><span class="generic injected"> </span><span class="attribute injected">#</span><span class="attribute injected">!</span><span class="attribute injected">[</span><span class="function attribute injected">allow</span><span class="punctuation injected">(</span><span class="attribute injected">unused_mut</span><span class="punctuation injected">)</span><span class="attribute injected">]</span>
54 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="keyword injected">mut</span><span class="generic injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> 54 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="keyword injected">mut</span><span class="generic injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected">
55</span> <span class="comment documentation">/// ```</span> 55</span> <span class="comment documentation">/// ```</span>
56 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="punctuation">{</span> 56 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
57 <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">bar</span><span class="punctuation">:</span> <span class="bool_literal">true</span> <span class="punctuation">}</span> 57 <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">bar</span><span class="punctuation">:</span> <span class="bool_literal">true</span> <span class="punctuation">}</span>
58 <span class="punctuation">}</span> 58 <span class="punctuation">}</span>
59 59
@@ -67,9 +67,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
67 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span> 67 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span>
68 <span class="comment documentation">///</span> 68 <span class="comment documentation">///</span>
69 <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span> 69 <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span>
70 <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="punctuation injected">(</span><span class="generic injected">foo</span><span class="punctuation injected">.</span><span class="generic injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span> 70 <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="punctuation injected">(</span><span class="generic injected">foo</span><span class="operator injected">.</span><span class="generic injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span>
71 <span class="comment documentation">///</span> 71 <span class="comment documentation">///</span>
72 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">bar</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="variable injected">foo</span><span class="punctuation injected">.</span><span class="field injected">bar</span><span class="generic injected"> </span><span class="operator injected">||</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="punctuation injected">;</span> 72 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">bar</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="generic injected"> </span><span class="operator injected">||</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="punctuation injected">;</span>
73 <span class="comment documentation">///</span> 73 <span class="comment documentation">///</span>
74 <span class="comment documentation">/// </span><span class="comment injected">/* multi-line 74 <span class="comment documentation">/// </span><span class="comment injected">/* multi-line
75 </span><span class="comment documentation">/// </span><span class="comment injected"> comment */</span> 75 </span><span class="comment documentation">/// </span><span class="comment injected"> comment */</span>
@@ -81,7 +81,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
81 <span class="comment documentation">/// ```</span> 81 <span class="comment documentation">/// ```</span>
82 <span class="comment documentation">///</span> 82 <span class="comment documentation">///</span>
83 <span class="comment documentation">/// ```rust,no_run</span> 83 <span class="comment documentation">/// ```rust,no_run</span>
84 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">foobar</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">.</span><span class="function injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> 84 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">foobar</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="operator injected">.</span><span class="function injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected">
85</span> <span class="comment documentation">/// ```</span> 85</span> <span class="comment documentation">/// ```</span>
86 <span class="comment documentation">///</span> 86 <span class="comment documentation">///</span>
87 <span class="comment documentation">/// ```sh</span> 87 <span class="comment documentation">/// ```sh</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index 18addd00d..57c178916 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -40,7 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
40<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 40<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
41 <span class="function">fixture</span><span class="punctuation">(</span><span class="string_literal">r#"</span> 41 <span class="function">fixture</span><span class="punctuation">(</span><span class="string_literal">r#"</span>
42 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="punctuation">{</span> 42 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="punctuation">{</span>
43 <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 43 <span class="keyword">fn</span> <span class="function declaration static">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
44 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="punctuation">,</span> <span class="numeric_literal">4</span><span class="punctuation">)</span><span class="punctuation">;</span> 44 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="punctuation">,</span> <span class="numeric_literal">4</span><span class="punctuation">)</span><span class="punctuation">;</span>
45 <span class="punctuation">}</span> 45 <span class="punctuation">}</span>
46 <span class="punctuation">}</span><span class="string_literal">"#</span> 46 <span class="punctuation">}</span><span class="string_literal">"#</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 552fea668..4b6d6adc9 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -73,27 +73,27 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
73 <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span> 73 <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span>
74 <span class="comment">// unsafe fn and method calls</span> 74 <span class="comment">// unsafe fn and method calls</span>
75 <span class="function unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 75 <span class="function unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
76 <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="punctuation">.</span><span class="field unsafe">b</span><span class="punctuation">;</span> 76 <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="operator">.</span><span class="field unsafe">b</span><span class="punctuation">;</span>
77 <span class="keyword control">match</span> <span class="variable">u</span> <span class="punctuation">{</span> 77 <span class="keyword control">match</span> <span class="variable">u</span> <span class="punctuation">{</span>
78 <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span> 78 <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span>
79 <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">a</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span> 79 <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">a</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span>
80 <span class="punctuation">}</span> 80 <span class="punctuation">}</span>
81 <span class="struct">HasUnsafeFn</span><span class="punctuation">.</span><span class="function unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 81 <span class="struct">HasUnsafeFn</span><span class="operator">.</span><span class="function unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
82 82
83 <span class="comment">// unsafe deref</span> 83 <span class="comment">// unsafe deref</span>
84 <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="variable">x</span><span class="punctuation">;</span> 84 <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="variable">x</span><span class="punctuation">;</span>
85 85
86 <span class="comment">// unsafe access to a static mut</span> 86 <span class="comment">// unsafe access to a static mut</span>
87 <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="static mutable unsafe">global_mut</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">;</span> 87 <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="static mutable unsafe">global_mut</span><span class="operator">.</span><span class="field">a</span><span class="punctuation">;</span>
88 88
89 <span class="comment">// unsafe ref of packed fields</span> 89 <span class="comment">// unsafe ref of packed fields</span>
90 <span class="keyword">let</span> <span class="variable declaration">packed</span> <span class="operator">=</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span> 90 <span class="keyword">let</span> <span class="variable declaration">packed</span> <span class="operator">=</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span>
91 <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="operator unsafe">&</span><span class="variable">packed</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">;</span> 91 <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="operator unsafe">&</span><span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="punctuation">;</span>
92 <span class="keyword">let</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">;</span> 92 <span class="keyword">let</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="punctuation">;</span>
93 <span class="keyword">let</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="keyword unsafe">ref</span> <span class="field">a</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">;</span> 93 <span class="keyword">let</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="keyword unsafe">ref</span> <span class="field">a</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">;</span>
94 <span class="keyword">let</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">_a</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">;</span> 94 <span class="keyword">let</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">_a</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">;</span>
95 95
96 <span class="comment">// unsafe auto ref of packed field</span> 96 <span class="comment">// unsafe auto ref of packed field</span>
97 <span class="variable">packed</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">.</span><span class="function unsafe">calls_autoref</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 97 <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="operator">.</span><span class="function unsafe">calls_autoref</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
98 <span class="punctuation">}</span> 98 <span class="punctuation">}</span>
99<span class="punctuation">}</span></code></pre> \ No newline at end of file 99<span class="punctuation">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 5eb222ee2..6a10a9dcd 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -67,21 +67,21 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
67 67
68<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="punctuation">{</span> 68<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
69 <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> 69 <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span>
70 <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span> 70 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
71 <span class="punctuation">}</span> 71 <span class="punctuation">}</span>
72<span class="punctuation">}</span> 72<span class="punctuation">}</span>
73 73
74<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span> 74<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
75 <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">Foo</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> 75 <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">Foo</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span>
76 <span class="value_param">f</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="self_keyword mutable consuming">self</span><span class="punctuation">)</span> 76 <span class="value_param">f</span><span class="operator">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="self_keyword mutable consuming">self</span><span class="punctuation">)</span>
77 <span class="punctuation">}</span> 77 <span class="punctuation">}</span>
78 78
79 <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span> 79 <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span>
80 <span class="self_keyword mutable">self</span><span class="punctuation">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span> 80 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span>
81 <span class="punctuation">}</span> 81 <span class="punctuation">}</span>
82 82
83 <span class="keyword">fn</span> <span class="function declaration">quop</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> 83 <span class="keyword">fn</span> <span class="function declaration">quop</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span>
84 <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span> 84 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
85 <span class="punctuation">}</span> 85 <span class="punctuation">}</span>
86<span class="punctuation">}</span> 86<span class="punctuation">}</span>
87 87
@@ -92,15 +92,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
92 92
93<span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> 93<span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span>
94 <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">FooCopy</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="punctuation">{</span> 94 <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">FooCopy</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="punctuation">{</span>
95 <span class="value_param">f</span><span class="punctuation">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">)</span> 95 <span class="value_param">f</span><span class="operator">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">)</span>
96 <span class="punctuation">}</span> 96 <span class="punctuation">}</span>
97 97
98 <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span> 98 <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span>
99 <span class="self_keyword mutable">self</span><span class="punctuation">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span> 99 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span>
100 <span class="punctuation">}</span> 100 <span class="punctuation">}</span>
101 101
102 <span class="keyword">fn</span> <span class="function declaration">quop</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="punctuation">{</span> 102 <span class="keyword">fn</span> <span class="function declaration">quop</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="punctuation">{</span>
103 <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span> 103 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
104 <span class="punctuation">}</span> 104 <span class="punctuation">}</span>
105<span class="punctuation">}</span> 105<span class="punctuation">}</span>
106 106
@@ -152,10 +152,10 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
152 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> <span class="operator">=</span> <span class="unresolved_reference">Vec</span><span class="operator">::</span><span class="unresolved_reference">new</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 152 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> <span class="operator">=</span> <span class="unresolved_reference">Vec</span><span class="operator">::</span><span class="unresolved_reference">new</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
153 <span class="keyword control">if</span> <span class="bool_literal">true</span> <span class="punctuation">{</span> 153 <span class="keyword control">if</span> <span class="bool_literal">true</span> <span class="punctuation">{</span>
154 <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="numeric_literal">92</span><span class="punctuation">;</span> 154 <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="numeric_literal">92</span><span class="punctuation">;</span>
155 <span class="variable mutable">vec</span><span class="punctuation">.</span><span class="unresolved_reference">push</span><span class="punctuation">(</span><span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="numeric_literal">1</span> <span class="punctuation">}</span><span class="punctuation">)</span><span class="punctuation">;</span> 155 <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">push</span><span class="punctuation">(</span><span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="numeric_literal">1</span> <span class="punctuation">}</span><span class="punctuation">)</span><span class="punctuation">;</span>
156 <span class="punctuation">}</span> 156 <span class="punctuation">}</span>
157 <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span> 157 <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span>
158 <span class="variable mutable">vec</span><span class="punctuation">.</span><span class="unresolved_reference">set_len</span><span class="punctuation">(</span><span class="numeric_literal">0</span><span class="punctuation">)</span><span class="punctuation">;</span> 158 <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">set_len</span><span class="punctuation">(</span><span class="numeric_literal">0</span><span class="punctuation">)</span><span class="punctuation">;</span>
159 <span class="static mutable unsafe">STATIC_MUT</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="punctuation">;</span> 159 <span class="static mutable unsafe">STATIC_MUT</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="punctuation">;</span>
160 <span class="punctuation">}</span> 160 <span class="punctuation">}</span>
161 161
@@ -175,14 +175,14 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
175 175
176 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> 176 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span>
177 <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> 177 <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span>
178 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 178 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
179 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 179 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
180 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span> 180 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span>
181 181
182 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> <span class="field">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> 182 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> <span class="field">x</span> <span class="punctuation">}</span><span class="punctuation">;</span>
183 <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 183 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
184 <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 184 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
185 <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="variable mutable">copy</span><span class="punctuation">)</span><span class="punctuation">;</span> 185 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="variable mutable">copy</span><span class="punctuation">)</span><span class="punctuation">;</span>
186 186
187 <span class="keyword">let</span> <span class="variable declaration callable">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="punctuation">;</span> 187 <span class="keyword">let</span> <span class="variable declaration callable">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="punctuation">;</span>
188 <span class="keyword">let</span> <span class="variable declaration callable">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function">baz</span><span class="punctuation">;</span> 188 <span class="keyword">let</span> <span class="variable declaration callable">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function">baz</span><span class="punctuation">;</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
index 401e87a73..c7589605f 100644
--- a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
@@ -37,11 +37,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
37</style> 37</style>
38<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 38<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
39 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="punctuation">;</span> 39 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="punctuation">;</span>
40 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(76,47%,83%);">x</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="punctuation">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 40 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(76,47%,83%);">x</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
41 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(15,86%,51%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="punctuation">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 41 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(15,86%,51%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
42 42
43 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="punctuation">;</span> 43 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="punctuation">;</span>
44 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(90,74%,79%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span><span class="punctuation">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 44 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(90,74%,79%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
45<span class="punctuation">}</span> 45<span class="punctuation">}</span>
46 46
47<span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 47<span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 2b667b0d4..5c22e2fce 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -513,6 +513,34 @@ fn test_extern_crate() {
513 ); 513 );
514} 514}
515 515
516#[test]
517fn test_associated_function() {
518 check_highlighting(
519 r#"
520fn not_static() {}
521
522struct foo {}
523
524impl foo {
525 pub fn is_static() {}
526 pub fn is_not_static(&self) {}
527}
528
529trait t {
530 fn t_is_static() {}
531 fn t_is_not_static(&self) {}
532}
533
534impl t for foo {
535 pub fn is_static() {}
536 pub fn is_not_static(&self) {}
537}
538 "#,
539 expect_file!["./test_data/highlight_assoc_functions.html"],
540 false,
541 )
542}
543
516/// Highlights the code given by the `ra_fixture` argument, renders the 544/// Highlights the code given by the `ra_fixture` argument, renders the
517/// result as HTML, and compares it with the HTML file given as `snapshot`. 545/// result as HTML, and compares it with the HTML file given as `snapshot`.
518/// Note that the `snapshot` file is overwritten by the rendered HTML. 546/// Note that the `snapshot` file is overwritten by the rendered HTML.
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 92b7c7b68..f8ecd8e83 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -426,6 +426,7 @@ fn semantic_token_type_and_modifiers(
426 HighlightModifier::Consuming => semantic_tokens::CONSUMING, 426 HighlightModifier::Consuming => semantic_tokens::CONSUMING,
427 HighlightModifier::Unsafe => semantic_tokens::UNSAFE, 427 HighlightModifier::Unsafe => semantic_tokens::UNSAFE,
428 HighlightModifier::Callable => semantic_tokens::CALLABLE, 428 HighlightModifier::Callable => semantic_tokens::CALLABLE,
429 HighlightModifier::Static => lsp_types::SemanticTokenModifier::STATIC,
429 }; 430 };
430 mods |= modifier; 431 mods |= modifier;
431 } 432 }
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 7ac6076a4..320c430c9 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -349,7 +349,10 @@ pub struct SyntaxRewriter<'a> {
349 349
350impl fmt::Debug for SyntaxRewriter<'_> { 350impl fmt::Debug for SyntaxRewriter<'_> {
351 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 351 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352 f.debug_struct("SyntaxRewriter").field("replacements", &self.replacements).finish() 352 f.debug_struct("SyntaxRewriter")
353 .field("replacements", &self.replacements)
354 .field("insertions", &self.insertions)
355 .finish()
353 } 356 }
354} 357}
355 358
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index b1578820f..876659a2b 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -25,6 +25,10 @@ pub fn assoc_item_list() -> ast::AssocItemList {
25 ast_from_text("impl C for D {};") 25 ast_from_text("impl C for D {};")
26} 26}
27 27
28pub fn impl_trait(trait_: ast::Path, ty: ast::Path) -> ast::Impl {
29 ast_from_text(&format!("impl {} for {} {{}}", trait_, ty))
30}
31
28pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { 32pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
29 ast_from_text(&format!("use {};", name_ref)) 33 ast_from_text(&format!("use {};", name_ref))
30} 34}
diff --git a/editors/code/rust.tmGrammar.json b/editors/code/rust.tmGrammar.json
index 77595aa00..cd4775d27 100644
--- a/editors/code/rust.tmGrammar.json
+++ b/editors/code/rust.tmGrammar.json
@@ -50,7 +50,7 @@
50 { 50 {
51 "comment": "macro type metavariables", 51 "comment": "macro type metavariables",
52 "name": "meta.macro.metavariable.type.rust", 52 "name": "meta.macro.metavariable.type.rust",
53 "match": "(\\$)((crate)|([A-Z][A-Za-z0-9_]*))((:)(block|expr|ident|item|lifetime|literal|meta|pat|path|stmt|tt|ty|vis))?", 53 "match": "(\\$)((crate)|([A-Z][A-Za-z0-9_]*))((:)(block|expr|ident|item|lifetime|literal|meta|path?|stmt|tt|ty|vis))?",
54 "captures": { 54 "captures": {
55 "1": { 55 "1": {
56 "name": "keyword.operator.macro.dollar.rust" 56 "name": "keyword.operator.macro.dollar.rust"
@@ -77,7 +77,7 @@
77 { 77 {
78 "comment": "macro metavariables", 78 "comment": "macro metavariables",
79 "name": "meta.macro.metavariable.rust", 79 "name": "meta.macro.metavariable.rust",
80 "match": "(\\$)([a-z][A-Za-z0-9_]*)((:)(block|expr|ident|item|lifetime|literal|meta|pat|path|stmt|tt|ty|vis))?", 80 "match": "(\\$)([a-z][A-Za-z0-9_]*)((:)(block|expr|ident|item|lifetime|literal|meta|path?|stmt|tt|ty|vis))?",
81 "captures": { 81 "captures": {
82 "1": { 82 "1": {
83 "name": "keyword.operator.macro.dollar.rust" 83 "name": "keyword.operator.macro.dollar.rust"
@@ -167,7 +167,7 @@
167 "match": "(mod)\\s+((?:r#(?!crate|[Ss]elf|super))?[a-z][A-Za-z0-9_]*)", 167 "match": "(mod)\\s+((?:r#(?!crate|[Ss]elf|super))?[a-z][A-Za-z0-9_]*)",
168 "captures": { 168 "captures": {
169 "1": { 169 "1": {
170 "name": "keyword.control.rust" 170 "name": "storage.type.rust"
171 }, 171 },
172 "2": { 172 "2": {
173 "name": "entity.name.module.rust" 173 "name": "entity.name.module.rust"
@@ -180,7 +180,7 @@
180 "begin": "\\b(extern)\\s+(crate)", 180 "begin": "\\b(extern)\\s+(crate)",
181 "beginCaptures": { 181 "beginCaptures": {
182 "1": { 182 "1": {
183 "name": "keyword.control.rust" 183 "name": "storage.type.rust"
184 }, 184 },
185 "2": { 185 "2": {
186 "name": "keyword.other.crate.rust" 186 "name": "keyword.other.crate.rust"
@@ -213,7 +213,7 @@
213 "begin": "\\b(use)\\s", 213 "begin": "\\b(use)\\s",
214 "beginCaptures": { 214 "beginCaptures": {
215 "1": { 215 "1": {
216 "name": "keyword.control.rust" 216 "name": "keyword.other.rust"
217 } 217 }
218 }, 218 },
219 "end": ";", 219 "end": ";",
@@ -307,9 +307,14 @@
307 "block-comments": { 307 "block-comments": {
308 "patterns": [ 308 "patterns": [
309 { 309 {
310 "comment": "block comments", 310 "comment": "empty block comments",
311 "name": "comment.block.rust", 311 "name": "comment.block.rust",
312 "begin": "/\\*(?!\\*)", 312 "match": "/\\*\\*/"
313 },
314 {
315 "comment": "block documentation comments",
316 "name": "comment.block.documentation.rust",
317 "begin": "/\\*\\*",
313 "end": "\\*/", 318 "end": "\\*/",
314 "patterns": [ 319 "patterns": [
315 { 320 {
@@ -318,9 +323,9 @@
318 ] 323 ]
319 }, 324 },
320 { 325 {
321 "comment": "block documentation comments", 326 "comment": "block comments",
322 "name": "comment.block.documentation.rust", 327 "name": "comment.block.rust",
323 "begin": "/\\*\\*", 328 "begin": "/\\*(?!\\*)",
324 "end": "\\*/", 329 "end": "\\*/",
325 "patterns": [ 330 "patterns": [
326 { 331 {
@@ -342,7 +347,7 @@
342 "match": "\\b(const)\\s+([A-Z][A-Za-z0-9_]*)\\b", 347 "match": "\\b(const)\\s+([A-Z][A-Za-z0-9_]*)\\b",
343 "captures": { 348 "captures": {
344 "1": { 349 "1": {
345 "name": "keyword.control.rust" 350 "name": "storage.type.rust"
346 }, 351 },
347 "2": { 352 "2": {
348 "name": "constant.other.caps.rust" 353 "name": "constant.other.caps.rust"
@@ -450,7 +455,7 @@
450 "begin": "\\b(fn)\\s+((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)((\\()|(<))", 455 "begin": "\\b(fn)\\s+((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)((\\()|(<))",
451 "beginCaptures": { 456 "beginCaptures": {
452 "1": { 457 "1": {
453 "name": "keyword.control.fn.rust" 458 "name": "keyword.other.fn.rust"
454 }, 459 },
455 "2": { 460 "2": {
456 "name": "entity.name.function.rust" 461 "name": "entity.name.function.rust"
@@ -643,7 +648,7 @@
643 { 648 {
644 "comment": "control flow keywords", 649 "comment": "control flow keywords",
645 "name": "keyword.control.rust", 650 "name": "keyword.control.rust",
646 "match": "\\b(async|await|break|continue|do|else|for|if|loop|match|move|return|try|where|while|yield)\\b" 651 "match": "\\b(await|break|continue|do|else|for|if|loop|match|return|try|while|yield)\\b"
647 }, 652 },
648 { 653 {
649 "comment": "storage keywords", 654 "comment": "storage keywords",
@@ -658,7 +663,7 @@
658 { 663 {
659 "comment": "other keywords", 664 "comment": "other keywords",
660 "name": "keyword.other.rust", 665 "name": "keyword.other.rust",
661 "match": "\\b(as|become|box|dyn|final|impl|in|override|priv|pub|ref|typeof|union|unsafe|unsized|use|virtual)\\b" 666 "match": "\\b(as|async|become|box|dyn|move|final|impl|in|override|priv|pub|ref|typeof|union|unsafe|unsized|use|virtual|where)\\b"
662 }, 667 },
663 { 668 {
664 "comment": "fn", 669 "comment": "fn",
@@ -676,11 +681,6 @@
676 "match": "\\bmut\\b" 681 "match": "\\bmut\\b"
677 }, 682 },
678 { 683 {
679 "comment": "math operators",
680 "name": "keyword.operator.math.rust",
681 "match": "(([+%]|(\\*(?!\\w)))(?!=))|(-(?!>))|(/(?!/))"
682 },
683 {
684 "comment": "logical operators", 684 "comment": "logical operators",
685 "name": "keyword.operator.logical.rust", 685 "name": "keyword.operator.logical.rust",
686 "match": "(\\^|\\||\\|\\||&&|<<|>>|!)(?!=)" 686 "match": "(\\^|\\||\\|\\||&&|<<|>>|!)(?!=)"
@@ -693,7 +693,7 @@
693 { 693 {
694 "comment": "assignment operators", 694 "comment": "assignment operators",
695 "name": "keyword.operator.assignment.rust", 695 "name": "keyword.operator.assignment.rust",
696 "match": "(-=|\\*=|/=|%=|\\^=|&=|\\|=|<<=|>>=)" 696 "match": "(\\+=|-=|\\*=|/=|%=|\\^=|&=|\\|=|<<=|>>=)"
697 }, 697 },
698 { 698 {
699 "comment": "single equal", 699 "comment": "single equal",
@@ -706,6 +706,11 @@
706 "match": "(=(=)?(?!>)|!=|<=|(?<!=)>=)" 706 "match": "(=(=)?(?!>)|!=|<=|(?<!=)>=)"
707 }, 707 },
708 { 708 {
709 "comment": "math operators",
710 "name": "keyword.operator.math.rust",
711 "match": "(([+%]|(\\*(?!\\w)))(?!=))|(-(?!>))|(/(?!/))"
712 },
713 {
709 "comment": "less than, greater than (special case)", 714 "comment": "less than, greater than (special case)",
710 "match": "(?:\\b|(?:(\\))|(\\])|(\\})))[ \\t]+([<>])[ \\t]+(?:\\b|(?:(\\()|(\\[)|(\\{)))", 715 "match": "(?:\\b|(?:(\\))|(\\])|(\\})))[ \\t]+([<>])[ \\t]+(?:\\b|(?:(\\()|(\\[)|(\\{)))",
711 "captures": { 716 "captures": {
@@ -1127,7 +1132,7 @@
1127 { 1132 {
1128 "comment": "variables", 1133 "comment": "variables",
1129 "name": "variable.other.rust", 1134 "name": "variable.other.rust",
1130 "match": "\\b(?<!\\.)(?:r#(?!(crate|[Ss]elf|super)))?[a-z0-9_]+\\b" 1135 "match": "\\b(?<!(?<!\\.)\\.)(?:r#(?!(crate|[Ss]elf|super)))?[a-z0-9_]+\\b"
1131 } 1136 }
1132 ] 1137 ]
1133 } 1138 }
diff --git a/editors/code/src/snippets.ts b/editors/code/src/snippets.ts
index 258b49982..fee736e7d 100644
--- a/editors/code/src/snippets.ts
+++ b/editors/code/src/snippets.ts
@@ -3,16 +3,29 @@ import * as vscode from 'vscode';
3import { assert } from './util'; 3import { assert } from './util';
4 4
5export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) { 5export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) {
6 assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`); 6 if (edit.entries().length === 1) {
7 const [uri, edits] = edit.entries()[0]; 7 const [uri, edits] = edit.entries()[0];
8 const editor = await editorFromUri(uri);
9 if (editor) await applySnippetTextEdits(editor, edits);
10 return;
11 }
12 for (const [uri, edits] of edit.entries()) {
13 const editor = await editorFromUri(uri);
14 if (editor) await editor.edit((builder) => {
15 for (const indel of edits) {
16 assert(!parseSnippet(indel.newText), `bad ws edit: snippet received with multiple edits: ${JSON.stringify(edit)}`);
17 builder.replace(indel.range, indel.newText);
18 }
19 });
20 }
21}
8 22
23async function editorFromUri(uri: vscode.Uri): Promise<vscode.TextEditor | undefined> {
9 if (vscode.window.activeTextEditor?.document.uri !== uri) { 24 if (vscode.window.activeTextEditor?.document.uri !== uri) {
10 // `vscode.window.visibleTextEditors` only contains editors whose contents are being displayed 25 // `vscode.window.visibleTextEditors` only contains editors whose contents are being displayed
11 await vscode.window.showTextDocument(uri, {}); 26 await vscode.window.showTextDocument(uri, {});
12 } 27 }
13 const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString()); 28 return vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString());
14 if (!editor) return;
15 await applySnippetTextEdits(editor, edits);
16} 29}
17 30
18export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) { 31export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) {
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs
index 9de60c76c..99652e76b 100644
--- a/xtask/tests/tidy.rs
+++ b/xtask/tests/tidy.rs
@@ -214,8 +214,6 @@ fn check_todo(path: &Path, text: &str) {
214 // This file itself obviously needs to use todo (<- like this!). 214 // This file itself obviously needs to use todo (<- like this!).
215 "tests/cli.rs", 215 "tests/cli.rs",
216 // Some of our assists generate `todo!()`. 216 // Some of our assists generate `todo!()`.
217 "tests/generated.rs",
218 "handlers/add_missing_impl_members.rs",
219 "handlers/add_turbo_fish.rs", 217 "handlers/add_turbo_fish.rs",
220 "handlers/generate_function.rs", 218 "handlers/generate_function.rs",
221 // To support generating `todo!()` in assists, we have `expr_todo()` in 219 // To support generating `todo!()` in assists, we have `expr_todo()` in
@@ -228,6 +226,11 @@ fn check_todo(path: &Path, text: &str) {
228 return; 226 return;
229 } 227 }
230 if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") { 228 if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") {
229 // Generated by an assist
230 if text.contains("${0:todo!()}") {
231 return;
232 }
233
231 panic!( 234 panic!(
232 "\nTODO markers or todo! macros should not be committed to the master branch,\n\ 235 "\nTODO markers or todo! macros should not be committed to the master branch,\n\
233 use FIXME instead\n\ 236 use FIXME instead\n\