]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
Rollup merge of #101189 - daxpedda:ready-into-inner, r=joshtriplett
[rust.git] / src / tools / rust-analyzer / crates / ide-assists / src / utils.rs
1 //! Assorted functions shared by several assists.
2
3 use std::ops;
4
5 use itertools::Itertools;
6
7 pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
8 use hir::{db::HirDatabase, HirDisplay, Semantics};
9 use ide_db::{famous_defs::FamousDefs, path_transform::PathTransform, RootDatabase, SnippetCap};
10 use stdx::format_to;
11 use syntax::{
12     ast::{
13         self,
14         edit::{self, AstNodeEdit},
15         edit_in_place::{AttrsOwnerEdit, Removable},
16         make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
17     },
18     ted, AstNode, AstToken, Direction, SmolStr, SourceFile,
19     SyntaxKind::*,
20     SyntaxNode, TextRange, TextSize, T,
21 };
22
23 use crate::assist_context::{AssistContext, SourceChangeBuilder};
24
25 pub(crate) mod suggest_name;
26 mod gen_trait_fn_body;
27
28 pub(crate) fn unwrap_trivial_block(block_expr: ast::BlockExpr) -> ast::Expr {
29     extract_trivial_expression(&block_expr)
30         .filter(|expr| !expr.syntax().text().contains_char('\n'))
31         .unwrap_or_else(|| block_expr.into())
32 }
33
34 pub fn extract_trivial_expression(block_expr: &ast::BlockExpr) -> Option<ast::Expr> {
35     if block_expr.modifier().is_some() {
36         return None;
37     }
38     let stmt_list = block_expr.stmt_list()?;
39     let has_anything_else = |thing: &SyntaxNode| -> bool {
40         let mut non_trivial_children =
41             stmt_list.syntax().children_with_tokens().filter(|it| match it.kind() {
42                 WHITESPACE | T!['{'] | T!['}'] => false,
43                 _ => it.as_node() != Some(thing),
44             });
45         non_trivial_children.next().is_some()
46     };
47
48     if let Some(expr) = stmt_list.tail_expr() {
49         if has_anything_else(expr.syntax()) {
50             return None;
51         }
52         return Some(expr);
53     }
54     // Unwrap `{ continue; }`
55     let stmt = stmt_list.statements().next()?;
56     if let ast::Stmt::ExprStmt(expr_stmt) = stmt {
57         if has_anything_else(expr_stmt.syntax()) {
58             return None;
59         }
60         let expr = expr_stmt.expr()?;
61         if matches!(expr.syntax().kind(), CONTINUE_EXPR | BREAK_EXPR | RETURN_EXPR) {
62             return Some(expr);
63         }
64     }
65     None
66 }
67
68 /// This is a method with a heuristics to support test methods annotated with custom test annotations, such as
69 /// `#[test_case(...)]`, `#[tokio::test]` and similar.
70 /// Also a regular `#[test]` annotation is supported.
71 ///
72 /// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test,
73 /// but it's better than not to have the runnables for the tests at all.
74 pub fn test_related_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> {
75     fn_def.attrs().find_map(|attr| {
76         let path = attr.path()?;
77         let text = path.syntax().text().to_string();
78         if text.starts_with("test") || text.ends_with("test") {
79             Some(attr)
80         } else {
81             None
82         }
83     })
84 }
85
86 #[derive(Copy, Clone, PartialEq)]
87 pub enum DefaultMethods {
88     Only,
89     No,
90 }
91
92 pub fn filter_assoc_items(
93     sema: &Semantics<'_, RootDatabase>,
94     items: &[hir::AssocItem],
95     default_methods: DefaultMethods,
96 ) -> Vec<ast::AssocItem> {
97     fn has_def_name(item: &ast::AssocItem) -> bool {
98         match item {
99             ast::AssocItem::Fn(def) => def.name(),
100             ast::AssocItem::TypeAlias(def) => def.name(),
101             ast::AssocItem::Const(def) => def.name(),
102             ast::AssocItem::MacroCall(_) => None,
103         }
104         .is_some()
105     }
106
107     items
108         .iter()
109         // Note: This throws away items with no source.
110         .filter_map(|&i| {
111             let item = match i {
112                 hir::AssocItem::Function(i) => ast::AssocItem::Fn(sema.source(i)?.value),
113                 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(sema.source(i)?.value),
114                 hir::AssocItem::Const(i) => ast::AssocItem::Const(sema.source(i)?.value),
115             };
116             Some(item)
117         })
118         .filter(has_def_name)
119         .filter(|it| match it {
120             ast::AssocItem::Fn(def) => matches!(
121                 (default_methods, def.body()),
122                 (DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None)
123             ),
124             _ => default_methods == DefaultMethods::No,
125         })
126         .collect::<Vec<_>>()
127 }
128
129 pub fn add_trait_assoc_items_to_impl(
130     sema: &Semantics<'_, RootDatabase>,
131     items: Vec<ast::AssocItem>,
132     trait_: hir::Trait,
133     impl_: ast::Impl,
134     target_scope: hir::SemanticsScope<'_>,
135 ) -> (ast::Impl, ast::AssocItem) {
136     let source_scope = sema.scope_for_def(trait_);
137
138     let transform = PathTransform::trait_impl(&target_scope, &source_scope, trait_, impl_.clone());
139
140     let items = items.into_iter().map(|assoc_item| {
141         transform.apply(assoc_item.syntax());
142         assoc_item.remove_attrs_and_docs();
143         assoc_item
144     });
145
146     let res = impl_.clone_for_update();
147
148     let assoc_item_list = res.get_or_create_assoc_item_list();
149     let mut first_item = None;
150     for item in items {
151         first_item.get_or_insert_with(|| item.clone());
152         match &item {
153             ast::AssocItem::Fn(fn_) if fn_.body().is_none() => {
154                 let body = make::block_expr(None, Some(make::ext::expr_todo()))
155                     .indent(edit::IndentLevel(1));
156                 ted::replace(fn_.get_or_create_body().syntax(), body.clone_for_update().syntax())
157             }
158             ast::AssocItem::TypeAlias(type_alias) => {
159                 if let Some(type_bound_list) = type_alias.type_bound_list() {
160                     type_bound_list.remove()
161                 }
162             }
163             _ => {}
164         }
165
166         assoc_item_list.add_item(item)
167     }
168
169     (res, first_item.unwrap())
170 }
171
172 #[derive(Clone, Copy, Debug)]
173 pub(crate) enum Cursor<'a> {
174     Replace(&'a SyntaxNode),
175     Before(&'a SyntaxNode),
176 }
177
178 impl<'a> Cursor<'a> {
179     fn node(self) -> &'a SyntaxNode {
180         match self {
181             Cursor::Replace(node) | Cursor::Before(node) => node,
182         }
183     }
184 }
185
186 pub(crate) fn render_snippet(_cap: SnippetCap, node: &SyntaxNode, cursor: Cursor<'_>) -> String {
187     assert!(cursor.node().ancestors().any(|it| it == *node));
188     let range = cursor.node().text_range() - node.text_range().start();
189     let range: ops::Range<usize> = range.into();
190
191     let mut placeholder = cursor.node().to_string();
192     escape(&mut placeholder);
193     let tab_stop = match cursor {
194         Cursor::Replace(placeholder) => format!("${{0:{}}}", placeholder),
195         Cursor::Before(placeholder) => format!("$0{}", placeholder),
196     };
197
198     let mut buf = node.to_string();
199     buf.replace_range(range, &tab_stop);
200     return buf;
201
202     fn escape(buf: &mut String) {
203         stdx::replace(buf, '{', r"\{");
204         stdx::replace(buf, '}', r"\}");
205         stdx::replace(buf, '$', r"\$");
206     }
207 }
208
209 pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize {
210     node.children_with_tokens()
211         .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR))
212         .map(|it| it.text_range().start())
213         .unwrap_or_else(|| node.text_range().start())
214 }
215
216 pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr {
217     invert_special_case(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr))
218 }
219
220 fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
221     match expr {
222         ast::Expr::BinExpr(bin) => {
223             let bin = bin.clone_for_update();
224             let op_token = bin.op_token()?;
225             let rev_token = match op_token.kind() {
226                 T![==] => T![!=],
227                 T![!=] => T![==],
228                 T![<] => T![>=],
229                 T![<=] => T![>],
230                 T![>] => T![<=],
231                 T![>=] => T![<],
232                 // Parenthesize other expressions before prefixing `!`
233                 _ => return Some(make::expr_prefix(T![!], make::expr_paren(expr.clone()))),
234             };
235             ted::replace(op_token, make::token(rev_token));
236             Some(bin.into())
237         }
238         ast::Expr::MethodCallExpr(mce) => {
239             let receiver = mce.receiver()?;
240             let method = mce.name_ref()?;
241             let arg_list = mce.arg_list()?;
242
243             let method = match method.text().as_str() {
244                 "is_some" => "is_none",
245                 "is_none" => "is_some",
246                 "is_ok" => "is_err",
247                 "is_err" => "is_ok",
248                 _ => return None,
249             };
250             Some(make::expr_method_call(receiver, make::name_ref(method), arg_list))
251         }
252         ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::UnaryOp::Not => match pe.expr()? {
253             ast::Expr::ParenExpr(parexpr) => parexpr.expr(),
254             _ => pe.expr(),
255         },
256         ast::Expr::Literal(lit) => match lit.kind() {
257             ast::LiteralKind::Bool(b) => match b {
258                 true => Some(ast::Expr::Literal(make::expr_literal("false"))),
259                 false => Some(ast::Expr::Literal(make::expr_literal("true"))),
260             },
261             _ => None,
262         },
263         _ => None,
264     }
265 }
266
267 pub(crate) fn next_prev() -> impl Iterator<Item = Direction> {
268     [Direction::Next, Direction::Prev].into_iter()
269 }
270
271 pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool {
272     let first_node_text = |pat: &ast::Pat| pat.syntax().first_child().map(|node| node.text());
273
274     let pat_head = match pat {
275         ast::Pat::IdentPat(bind_pat) => match bind_pat.pat() {
276             Some(p) => first_node_text(&p),
277             None => return pat.syntax().text() == var.syntax().text(),
278         },
279         pat => first_node_text(pat),
280     };
281
282     let var_head = first_node_text(var);
283
284     pat_head == var_head
285 }
286
287 pub(crate) fn does_nested_pattern(pat: &ast::Pat) -> bool {
288     let depth = calc_depth(pat, 0);
289
290     if 1 < depth {
291         return true;
292     }
293     false
294 }
295
296 fn calc_depth(pat: &ast::Pat, depth: usize) -> usize {
297     match pat {
298         ast::Pat::IdentPat(_)
299         | ast::Pat::BoxPat(_)
300         | ast::Pat::RestPat(_)
301         | ast::Pat::LiteralPat(_)
302         | ast::Pat::MacroPat(_)
303         | ast::Pat::OrPat(_)
304         | ast::Pat::ParenPat(_)
305         | ast::Pat::PathPat(_)
306         | ast::Pat::WildcardPat(_)
307         | ast::Pat::RangePat(_)
308         | ast::Pat::RecordPat(_)
309         | ast::Pat::RefPat(_)
310         | ast::Pat::SlicePat(_)
311         | ast::Pat::TuplePat(_)
312         | ast::Pat::ConstBlockPat(_) => depth,
313
314         // FIXME: Other patterns may also be nested. Currently it simply supports only `TupleStructPat`
315         ast::Pat::TupleStructPat(pat) => {
316             let mut max_depth = depth;
317             for p in pat.fields() {
318                 let d = calc_depth(&p, depth + 1);
319                 if d > max_depth {
320                     max_depth = d
321                 }
322             }
323             max_depth
324         }
325     }
326 }
327
328 // Uses a syntax-driven approach to find any impl blocks for the struct that
329 // exist within the module/file
330 //
331 // Returns `None` if we've found an existing fn
332 //
333 // FIXME: change the new fn checking to a more semantic approach when that's more
334 // viable (e.g. we process proc macros, etc)
335 // FIXME: this partially overlaps with `find_impl_block_*`
336 pub(crate) fn find_struct_impl(
337     ctx: &AssistContext<'_>,
338     adt: &ast::Adt,
339     name: &str,
340 ) -> Option<Option<ast::Impl>> {
341     let db = ctx.db();
342     let module = adt.syntax().parent()?;
343
344     let struct_def = ctx.sema.to_def(adt)?;
345
346     let block = module.descendants().filter_map(ast::Impl::cast).find_map(|impl_blk| {
347         let blk = ctx.sema.to_def(&impl_blk)?;
348
349         // FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}`
350         // (we currently use the wrong type parameter)
351         // also we wouldn't want to use e.g. `impl S<u32>`
352
353         let same_ty = match blk.self_ty(db).as_adt() {
354             Some(def) => def == struct_def,
355             None => false,
356         };
357         let not_trait_impl = blk.trait_(db).is_none();
358
359         if !(same_ty && not_trait_impl) {
360             None
361         } else {
362             Some(impl_blk)
363         }
364     });
365
366     if let Some(ref impl_blk) = block {
367         if has_fn(impl_blk, name) {
368             return None;
369         }
370     }
371
372     Some(block)
373 }
374
375 fn has_fn(imp: &ast::Impl, rhs_name: &str) -> bool {
376     if let Some(il) = imp.assoc_item_list() {
377         for item in il.assoc_items() {
378             if let ast::AssocItem::Fn(f) = item {
379                 if let Some(name) = f.name() {
380                     if name.text().eq_ignore_ascii_case(rhs_name) {
381                         return true;
382                     }
383                 }
384             }
385         }
386     }
387
388     false
389 }
390
391 /// Find the start of the `impl` block for the given `ast::Impl`.
392 //
393 // FIXME: this partially overlaps with `find_struct_impl`
394 pub(crate) fn find_impl_block_start(impl_def: ast::Impl, buf: &mut String) -> Option<TextSize> {
395     buf.push('\n');
396     let start = impl_def.assoc_item_list().and_then(|it| it.l_curly_token())?.text_range().end();
397     Some(start)
398 }
399
400 /// Find the end of the `impl` block for the given `ast::Impl`.
401 //
402 // FIXME: this partially overlaps with `find_struct_impl`
403 pub(crate) fn find_impl_block_end(impl_def: ast::Impl, buf: &mut String) -> Option<TextSize> {
404     buf.push('\n');
405     let end = impl_def
406         .assoc_item_list()
407         .and_then(|it| it.r_curly_token())?
408         .prev_sibling_or_token()?
409         .text_range()
410         .end();
411     Some(end)
412 }
413
414 // Generates the surrounding `impl Type { <code> }` including type and lifetime
415 // parameters
416 pub(crate) fn generate_impl_text(adt: &ast::Adt, code: &str) -> String {
417     generate_impl_text_inner(adt, None, code)
418 }
419
420 // Generates the surrounding `impl <trait> for Type { <code> }` including type
421 // and lifetime parameters
422 pub(crate) fn generate_trait_impl_text(adt: &ast::Adt, trait_text: &str, code: &str) -> String {
423     generate_impl_text_inner(adt, Some(trait_text), code)
424 }
425
426 fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str) -> String {
427     let generic_params = adt.generic_param_list();
428     let mut buf = String::with_capacity(code.len());
429     buf.push_str("\n\n");
430     adt.attrs()
431         .filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false))
432         .for_each(|attr| buf.push_str(format!("{}\n", attr).as_str()));
433     buf.push_str("impl");
434     if let Some(generic_params) = &generic_params {
435         let lifetimes = generic_params.lifetime_params().map(|lt| format!("{}", lt.syntax()));
436         let toc_params = generic_params.type_or_const_params().map(|toc_param| {
437             let type_param = match toc_param {
438                 ast::TypeOrConstParam::Type(x) => x,
439                 ast::TypeOrConstParam::Const(x) => return x.syntax().to_string(),
440             };
441             let mut buf = String::new();
442             if let Some(it) = type_param.name() {
443                 format_to!(buf, "{}", it.syntax());
444             }
445             if let Some(it) = type_param.colon_token() {
446                 format_to!(buf, "{} ", it);
447             }
448             if let Some(it) = type_param.type_bound_list() {
449                 format_to!(buf, "{}", it.syntax());
450             }
451             buf
452         });
453         let generics = lifetimes.chain(toc_params).format(", ");
454         format_to!(buf, "<{}>", generics);
455     }
456     buf.push(' ');
457     if let Some(trait_text) = trait_text {
458         buf.push_str(trait_text);
459         buf.push_str(" for ");
460     }
461     buf.push_str(&adt.name().unwrap().text());
462     if let Some(generic_params) = generic_params {
463         let lifetime_params = generic_params
464             .lifetime_params()
465             .filter_map(|it| it.lifetime())
466             .map(|it| SmolStr::from(it.text()));
467         let toc_params = generic_params
468             .type_or_const_params()
469             .filter_map(|it| it.name())
470             .map(|it| SmolStr::from(it.text()));
471         format_to!(buf, "<{}>", lifetime_params.chain(toc_params).format(", "))
472     }
473
474     match adt.where_clause() {
475         Some(where_clause) => {
476             format_to!(buf, "\n{}\n{{\n{}\n}}", where_clause, code);
477         }
478         None => {
479             format_to!(buf, " {{\n{}\n}}", code);
480         }
481     }
482
483     buf
484 }
485
486 pub(crate) fn add_method_to_adt(
487     builder: &mut SourceChangeBuilder,
488     adt: &ast::Adt,
489     impl_def: Option<ast::Impl>,
490     method: &str,
491 ) {
492     let mut buf = String::with_capacity(method.len() + 2);
493     if impl_def.is_some() {
494         buf.push('\n');
495     }
496     buf.push_str(method);
497
498     let start_offset = impl_def
499         .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
500         .unwrap_or_else(|| {
501             buf = generate_impl_text(adt, &buf);
502             adt.syntax().text_range().end()
503         });
504
505     builder.insert(start_offset, buf);
506 }
507
508 #[derive(Debug)]
509 pub(crate) struct ReferenceConversion {
510     conversion: ReferenceConversionType,
511     ty: hir::Type,
512 }
513
514 #[derive(Debug)]
515 enum ReferenceConversionType {
516     // reference can be stripped if the type is Copy
517     Copy,
518     // &String -> &str
519     AsRefStr,
520     // &Vec<T> -> &[T]
521     AsRefSlice,
522     // &Box<T> -> &T
523     Dereferenced,
524     // &Option<T> -> Option<&T>
525     Option,
526     // &Result<T, E> -> Result<&T, &E>
527     Result,
528 }
529
530 impl ReferenceConversion {
531     pub(crate) fn convert_type(&self, db: &dyn HirDatabase) -> String {
532         match self.conversion {
533             ReferenceConversionType::Copy => self.ty.display(db).to_string(),
534             ReferenceConversionType::AsRefStr => "&str".to_string(),
535             ReferenceConversionType::AsRefSlice => {
536                 let type_argument_name =
537                     self.ty.type_arguments().next().unwrap().display(db).to_string();
538                 format!("&[{}]", type_argument_name)
539             }
540             ReferenceConversionType::Dereferenced => {
541                 let type_argument_name =
542                     self.ty.type_arguments().next().unwrap().display(db).to_string();
543                 format!("&{}", type_argument_name)
544             }
545             ReferenceConversionType::Option => {
546                 let type_argument_name =
547                     self.ty.type_arguments().next().unwrap().display(db).to_string();
548                 format!("Option<&{}>", type_argument_name)
549             }
550             ReferenceConversionType::Result => {
551                 let mut type_arguments = self.ty.type_arguments();
552                 let first_type_argument_name =
553                     type_arguments.next().unwrap().display(db).to_string();
554                 let second_type_argument_name =
555                     type_arguments.next().unwrap().display(db).to_string();
556                 format!("Result<&{}, &{}>", first_type_argument_name, second_type_argument_name)
557             }
558         }
559     }
560
561     pub(crate) fn getter(&self, field_name: String) -> String {
562         match self.conversion {
563             ReferenceConversionType::Copy => format!("self.{}", field_name),
564             ReferenceConversionType::AsRefStr
565             | ReferenceConversionType::AsRefSlice
566             | ReferenceConversionType::Dereferenced
567             | ReferenceConversionType::Option
568             | ReferenceConversionType::Result => format!("self.{}.as_ref()", field_name),
569         }
570     }
571 }
572
573 // FIXME: It should return a new hir::Type, but currently constructing new types is too cumbersome
574 //        and all users of this function operate on string type names, so they can do the conversion
575 //        itself themselves.
576 pub(crate) fn convert_reference_type(
577     ty: hir::Type,
578     db: &RootDatabase,
579     famous_defs: &FamousDefs<'_, '_>,
580 ) -> Option<ReferenceConversion> {
581     handle_copy(&ty, db)
582         .or_else(|| handle_as_ref_str(&ty, db, famous_defs))
583         .or_else(|| handle_as_ref_slice(&ty, db, famous_defs))
584         .or_else(|| handle_dereferenced(&ty, db, famous_defs))
585         .or_else(|| handle_option_as_ref(&ty, db, famous_defs))
586         .or_else(|| handle_result_as_ref(&ty, db, famous_defs))
587         .map(|conversion| ReferenceConversion { ty, conversion })
588 }
589
590 fn handle_copy(ty: &hir::Type, db: &dyn HirDatabase) -> Option<ReferenceConversionType> {
591     ty.is_copy(db).then(|| ReferenceConversionType::Copy)
592 }
593
594 fn handle_as_ref_str(
595     ty: &hir::Type,
596     db: &dyn HirDatabase,
597     famous_defs: &FamousDefs<'_, '_>,
598 ) -> Option<ReferenceConversionType> {
599     let str_type = hir::BuiltinType::str().ty(db);
600
601     ty.impls_trait(db, famous_defs.core_convert_AsRef()?, &[str_type])
602         .then(|| ReferenceConversionType::AsRefStr)
603 }
604
605 fn handle_as_ref_slice(
606     ty: &hir::Type,
607     db: &dyn HirDatabase,
608     famous_defs: &FamousDefs<'_, '_>,
609 ) -> Option<ReferenceConversionType> {
610     let type_argument = ty.type_arguments().next()?;
611     let slice_type = hir::Type::new_slice(type_argument);
612
613     ty.impls_trait(db, famous_defs.core_convert_AsRef()?, &[slice_type])
614         .then(|| ReferenceConversionType::AsRefSlice)
615 }
616
617 fn handle_dereferenced(
618     ty: &hir::Type,
619     db: &dyn HirDatabase,
620     famous_defs: &FamousDefs<'_, '_>,
621 ) -> Option<ReferenceConversionType> {
622     let type_argument = ty.type_arguments().next()?;
623
624     ty.impls_trait(db, famous_defs.core_convert_AsRef()?, &[type_argument])
625         .then(|| ReferenceConversionType::Dereferenced)
626 }
627
628 fn handle_option_as_ref(
629     ty: &hir::Type,
630     db: &dyn HirDatabase,
631     famous_defs: &FamousDefs<'_, '_>,
632 ) -> Option<ReferenceConversionType> {
633     if ty.as_adt() == famous_defs.core_option_Option()?.ty(db).as_adt() {
634         Some(ReferenceConversionType::Option)
635     } else {
636         None
637     }
638 }
639
640 fn handle_result_as_ref(
641     ty: &hir::Type,
642     db: &dyn HirDatabase,
643     famous_defs: &FamousDefs<'_, '_>,
644 ) -> Option<ReferenceConversionType> {
645     if ty.as_adt() == famous_defs.core_result_Result()?.ty(db).as_adt() {
646         Some(ReferenceConversionType::Result)
647     } else {
648         None
649     }
650 }
651
652 pub(crate) fn get_methods(items: &ast::AssocItemList) -> Vec<ast::Fn> {
653     items
654         .assoc_items()
655         .flat_map(|i| match i {
656             ast::AssocItem::Fn(f) => Some(f),
657             _ => None,
658         })
659         .filter(|f| f.name().is_some())
660         .collect()
661 }
662
663 /// Trim(remove leading and trailing whitespace) `initial_range` in `source_file`, return the trimmed range.
664 pub(crate) fn trimmed_text_range(source_file: &SourceFile, initial_range: TextRange) -> TextRange {
665     let mut trimmed_range = initial_range;
666     while source_file
667         .syntax()
668         .token_at_offset(trimmed_range.start())
669         .find_map(Whitespace::cast)
670         .is_some()
671         && trimmed_range.start() < trimmed_range.end()
672     {
673         let start = trimmed_range.start() + TextSize::from(1);
674         trimmed_range = TextRange::new(start, trimmed_range.end());
675     }
676     while source_file
677         .syntax()
678         .token_at_offset(trimmed_range.end())
679         .find_map(Whitespace::cast)
680         .is_some()
681         && trimmed_range.start() < trimmed_range.end()
682     {
683         let end = trimmed_range.end() - TextSize::from(1);
684         trimmed_range = TextRange::new(trimmed_range.start(), end);
685     }
686     trimmed_range
687 }
688
689 /// Convert a list of function params to a list of arguments that can be passed
690 /// into a function call.
691 pub(crate) fn convert_param_list_to_arg_list(list: ast::ParamList) -> ast::ArgList {
692     let mut args = vec![];
693     for param in list.params() {
694         if let Some(ast::Pat::IdentPat(pat)) = param.pat() {
695             if let Some(name) = pat.name() {
696                 let name = name.to_string();
697                 let expr = make::expr_path(make::ext::ident_path(&name));
698                 args.push(expr);
699             }
700         }
701     }
702     make::arg_list(args)
703 }