aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src/utils.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists/src/utils.rs')
-rw-r--r--crates/assists/src/utils.rs125
1 files changed, 119 insertions, 6 deletions
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 56f925ee6..66c0cdd5f 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -4,20 +4,25 @@ 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::AttrsOwner,
13 ast::NameOwner,
14 ast::{self, edit, make, ArgListOwner},
12 AstNode, Direction, 15 AstNode, Direction,
13 SyntaxKind::*, 16 SyntaxKind::*,
14 SyntaxNode, TextSize, T, 17 SyntaxNode, TextSize, T,
15}; 18};
16 19
17use crate::assist_config::SnippetCap; 20use crate::{
21 assist_config::SnippetCap,
22 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
23};
18 24
19pub use insert_use::MergeBehaviour; 25pub use insert_use::{insert_use, ImportScope, MergeBehaviour};
20pub(crate) use insert_use::{insert_use, ImportScope};
21 26
22pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { 27pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
23 let mut segments = Vec::new(); 28 let mut segments = Vec::new();
@@ -77,6 +82,104 @@ pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> {
77 None 82 None
78} 83}
79 84
85/// This is a method with a heuristics to support test methods annotated with custom test annotations, such as
86/// `#[test_case(...)]`, `#[tokio::test]` and similar.
87/// Also a regular `#[test]` annotation is supported.
88///
89/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test,
90/// but it's better than not to have the runnables for the tests at all.
91pub fn test_related_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> {
92 fn_def.attrs().find_map(|attr| {
93 let path = attr.path()?;
94 if path.syntax().text().to_string().contains("test") {
95 Some(attr)
96 } else {
97 None
98 }
99 })
100}
101
102#[derive(Copy, Clone, PartialEq)]
103pub enum DefaultMethods {
104 Only,
105 No,
106}
107
108pub fn filter_assoc_items(
109 db: &RootDatabase,
110 items: &[hir::AssocItem],
111 default_methods: DefaultMethods,
112) -> Vec<ast::AssocItem> {
113 fn has_def_name(item: &ast::AssocItem) -> bool {
114 match item {
115 ast::AssocItem::Fn(def) => def.name(),
116 ast::AssocItem::TypeAlias(def) => def.name(),
117 ast::AssocItem::Const(def) => def.name(),
118 ast::AssocItem::MacroCall(_) => None,
119 }
120 .is_some()
121 };
122
123 items
124 .iter()
125 .map(|i| match i {
126 hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(db).value),
127 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(db).value),
128 hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(db).value),
129 })
130 .filter(has_def_name)
131 .filter(|it| match it {
132 ast::AssocItem::Fn(def) => matches!(
133 (default_methods, def.body()),
134 (DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None)
135 ),
136 _ => default_methods == DefaultMethods::No,
137 })
138 .collect::<Vec<_>>()
139}
140
141pub fn add_trait_assoc_items_to_impl(
142 sema: &hir::Semantics<ide_db::RootDatabase>,
143 items: Vec<ast::AssocItem>,
144 trait_: hir::Trait,
145 impl_def: ast::Impl,
146 target_scope: hir::SemanticsScope,
147) -> (ast::Impl, ast::AssocItem) {
148 let impl_item_list = impl_def.assoc_item_list().unwrap_or_else(make::assoc_item_list);
149
150 let n_existing_items = impl_item_list.assoc_items().count();
151 let source_scope = sema.scope_for_def(trait_);
152 let ast_transform = QualifyPaths::new(&target_scope, &source_scope)
153 .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone()));
154
155 let items = items
156 .into_iter()
157 .map(|it| ast_transform::apply(&*ast_transform, it))
158 .map(|it| match it {
159 ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)),
160 ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()),
161 _ => it,
162 })
163 .map(|it| edit::remove_attrs_and_docs(&it));
164
165 let new_impl_item_list = impl_item_list.append_items(items);
166 let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list);
167 let first_new_item =
168 new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap();
169 return (new_impl_def, first_new_item);
170
171 fn add_body(fn_def: ast::Fn) -> ast::Fn {
172 match fn_def.body() {
173 Some(_) => fn_def,
174 None => {
175 let body =
176 make::block_expr(None, Some(make::expr_todo())).indent(edit::IndentLevel(1));
177 fn_def.with_body(body)
178 }
179 }
180 }
181}
182
80#[derive(Clone, Copy, Debug)] 183#[derive(Clone, Copy, Debug)]
81pub(crate) enum Cursor<'a> { 184pub(crate) enum Cursor<'a> {
82 Replace(&'a SyntaxNode), 185 Replace(&'a SyntaxNode),
@@ -171,6 +274,12 @@ pub mod convert {
171 } 274 }
172} 275}
173 276
277pub mod default {
278 pub trait Default {
279 fn default() -> Self;
280 }
281}
282
174pub mod iter { 283pub mod iter {
175 pub use self::traits::{collect::IntoIterator, iterator::Iterator}; 284 pub use self::traits::{collect::IntoIterator, iterator::Iterator};
176 mod traits { 285 mod traits {
@@ -241,7 +350,7 @@ pub mod option {
241} 350}
242 351
243pub mod prelude { 352pub mod prelude {
244 pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}}; 353 pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default};
245} 354}
246#[prelude_import] 355#[prelude_import]
247pub use prelude::*; 356pub use prelude::*;
@@ -259,6 +368,10 @@ pub use prelude::*;
259 self.find_enum("core:option:Option") 368 self.find_enum("core:option:Option")
260 } 369 }
261 370
371 pub fn core_default_Default(&self) -> Option<Trait> {
372 self.find_trait("core:default:Default")
373 }
374
262 pub fn core_iter_Iterator(&self) -> Option<Trait> { 375 pub fn core_iter_Iterator(&self) -> Option<Trait> {
263 self.find_trait("core:iter:traits:iterator:Iterator") 376 self.find_trait("core:iter:traits:iterator:Iterator")
264 } 377 }