ide_assists/handlers/
term_search.rs

1//! Term search assist
2use hir::term_search::{TermSearchConfig, TermSearchCtx};
3use ide_db::{
4    assists::{AssistId, GroupLabel},
5    famous_defs::FamousDefs,
6};
7
8use itertools::Itertools;
9use syntax::{AstNode, ast};
10
11use crate::assist_context::{AssistContext, Assists};
12
13pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
14    let unexpanded = ctx.find_node_at_offset::<ast::MacroCall>()?;
15    let syntax = unexpanded.syntax();
16    let goal_range = syntax.text_range();
17
18    let parent = syntax.parent()?;
19    let scope = ctx.sema.scope(&parent)?;
20
21    let macro_call = ctx.sema.resolve_macro_call(&unexpanded)?;
22
23    let famous_defs = FamousDefs(&ctx.sema, scope.krate());
24    let std_todo = famous_defs.core_macros_todo()?;
25    let std_unimplemented = famous_defs.core_macros_unimplemented()?;
26
27    if macro_call != std_todo && macro_call != std_unimplemented {
28        return None;
29    }
30
31    let target_ty = ctx.sema.type_of_expr(&ast::Expr::cast(parent.clone())?)?.adjusted();
32
33    let term_search_ctx = TermSearchCtx {
34        sema: &ctx.sema,
35        scope: &scope,
36        goal: target_ty,
37        config: TermSearchConfig {
38            fuel: ctx.config.term_search_fuel,
39            enable_borrowcheck: ctx.config.term_search_borrowck,
40            ..Default::default()
41        },
42    };
43    let paths = hir::term_search::term_search(&term_search_ctx);
44
45    if paths.is_empty() {
46        return None;
47    }
48
49    let mut formatter = |_: &hir::Type<'_>| String::from("todo!()");
50
51    let edition = scope.krate().edition(ctx.db());
52    let paths = paths
53        .into_iter()
54        .filter_map(|path| {
55            path.gen_source_code(
56                &scope,
57                &mut formatter,
58                ctx.config.find_path_config(ctx.sema.is_nightly(scope.module().krate(ctx.sema.db))),
59                scope.krate().to_display_target(ctx.db()),
60            )
61            .ok()
62        })
63        .unique();
64
65    let macro_name = macro_call.name(ctx.sema.db);
66    let macro_name = macro_name.display(ctx.sema.db, edition);
67
68    for code in paths {
69        acc.add_group(
70            &GroupLabel(String::from("Term search")),
71            AssistId::generate("term_search"),
72            format!("Replace {macro_name}!() with {code}"),
73            goal_range,
74            |builder| {
75                builder.replace(goal_range, code);
76            },
77        );
78    }
79
80    Some(())
81}
82
83#[cfg(test)]
84mod tests {
85    use crate::tests::{check_assist, check_assist_not_applicable};
86
87    use super::*;
88
89    #[test]
90    fn test_complete_local() {
91        check_assist(
92            term_search,
93            r#"//- minicore: todo, unimplemented
94fn f() { let a: u128 = 1; let b: u128 = todo$0!() }"#,
95            r#"fn f() { let a: u128 = 1; let b: u128 = a }"#,
96        )
97    }
98
99    #[test]
100    fn test_complete_todo_with_msg() {
101        check_assist(
102            term_search,
103            r#"//- minicore: todo, unimplemented
104fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#,
105            r#"fn f() { let a: u128 = 1; let b: u128 = a }"#,
106        )
107    }
108
109    #[test]
110    fn test_complete_unimplemented_with_msg() {
111        check_assist(
112            term_search,
113            r#"//- minicore: todo, unimplemented
114fn f() { let a: u128 = 1; let b: u128 = unimplemented$0!("asd") }"#,
115            r#"fn f() { let a: u128 = 1; let b: u128 = a }"#,
116        )
117    }
118
119    #[test]
120    fn test_complete_unimplemented() {
121        check_assist(
122            term_search,
123            r#"//- minicore: todo, unimplemented
124fn f() { let a: u128 = 1; let b: u128 = unimplemented$0!() }"#,
125            r#"fn f() { let a: u128 = 1; let b: u128 = a }"#,
126        )
127    }
128
129    #[test]
130    fn test_complete_struct_field() {
131        check_assist(
132            term_search,
133            r#"//- minicore: todo, unimplemented
134struct A { pub x: i32, y: bool }
135fn f() { let a = A { x: 1, y: true }; let b: i32 = todo$0!(); }"#,
136            r#"struct A { pub x: i32, y: bool }
137fn f() { let a = A { x: 1, y: true }; let b: i32 = a.x; }"#,
138        )
139    }
140
141    #[test]
142    fn test_enum_with_generics() {
143        check_assist(
144            term_search,
145            r#"//- minicore: todo, unimplemented, option
146fn f() { let a: i32 = 1; let b: Option<i32> = todo$0!(); }"#,
147            r#"fn f() { let a: i32 = 1; let b: Option<i32> = None; }"#,
148        )
149    }
150
151    #[test]
152    fn test_enum_with_generics2() {
153        check_assist(
154            term_search,
155            r#"//- minicore: todo, unimplemented
156enum Option<T> { None, Some(T) }
157fn f() { let a: i32 = 1; let b: Option<i32> = todo$0!(); }"#,
158            r#"enum Option<T> { None, Some(T) }
159fn f() { let a: i32 = 1; let b: Option<i32> = Option::None; }"#,
160        )
161    }
162
163    #[test]
164    fn test_enum_with_generics3() {
165        check_assist(
166            term_search,
167            r#"//- minicore: todo, unimplemented
168enum Option<T> { None, Some(T) }
169fn f() { let a: Option<i32> = Option::None; let b: Option<Option<i32>> = todo$0!(); }"#,
170            r#"enum Option<T> { None, Some(T) }
171fn f() { let a: Option<i32> = Option::None; let b: Option<Option<i32>> = Option::None; }"#,
172        )
173    }
174
175    #[test]
176    fn test_enum_with_generics4() {
177        check_assist(
178            term_search,
179            r#"//- minicore: todo, unimplemented
180enum Foo<T = i32> { Foo(T) }
181fn f() { let a = 0; let b: Foo = todo$0!(); }"#,
182            r#"enum Foo<T = i32> { Foo(T) }
183fn f() { let a = 0; let b: Foo = Foo::Foo(a); }"#,
184        );
185
186        check_assist(
187            term_search,
188            r#"//- minicore: todo, unimplemented
189enum Foo<T = i32> { Foo(T) }
190fn f() { let a: Foo<u32> = Foo::Foo(0); let b: Foo<u32> = todo$0!(); }"#,
191            r#"enum Foo<T = i32> { Foo(T) }
192fn f() { let a: Foo<u32> = Foo::Foo(0); let b: Foo<u32> = a; }"#,
193        )
194    }
195
196    #[test]
197    fn test_newtype() {
198        check_assist(
199            term_search,
200            r#"//- minicore: todo, unimplemented
201struct Foo(i32);
202fn f() { let a: i32 = 1; let b: Foo = todo$0!(); }"#,
203            r#"struct Foo(i32);
204fn f() { let a: i32 = 1; let b: Foo = Foo(a); }"#,
205        )
206    }
207
208    #[test]
209    fn test_shadowing() {
210        check_assist(
211            term_search,
212            r#"//- minicore: todo, unimplemented
213fn f() { let a: i32 = 1; let b: i32 = 2; let a: u32 = 0; let c: i32 = todo$0!(); }"#,
214            r#"fn f() { let a: i32 = 1; let b: i32 = 2; let a: u32 = 0; let c: i32 = b; }"#,
215        )
216    }
217
218    #[test]
219    fn test_famous_bool() {
220        check_assist(
221            term_search,
222            r#"//- minicore: todo, unimplemented
223fn f() { let a: bool = todo$0!(); }"#,
224            r#"fn f() { let a: bool = true; }"#,
225        )
226    }
227
228    #[test]
229    fn test_fn_with_reference_types() {
230        check_assist(
231            term_search,
232            r#"//- minicore: todo, unimplemented
233fn f(a: &i32) -> f32 { a as f32 }
234fn g() { let a = 1; let b: f32 = todo$0!(); }"#,
235            r#"fn f(a: &i32) -> f32 { a as f32 }
236fn g() { let a = 1; let b: f32 = f(&a); }"#,
237        )
238    }
239
240    #[test]
241    fn test_fn_with_reference_types2() {
242        check_assist(
243            term_search,
244            r#"//- minicore: todo, unimplemented
245fn f(a: &i32) -> f32 { a as f32 }
246fn g() { let a = &1; let b: f32 = todo$0!(); }"#,
247            r#"fn f(a: &i32) -> f32 { a as f32 }
248fn g() { let a = &1; let b: f32 = f(a); }"#,
249        )
250    }
251
252    #[test]
253    fn test_fn_with_reference_types3() {
254        check_assist_not_applicable(
255            term_search,
256            r#"//- minicore: todo, unimplemented
257            fn f(a: &i32) -> f32 { a as f32 }
258            fn g() { let a = &mut 1; let b: f32 = todo$0!(); }"#,
259        )
260    }
261
262    #[test]
263    fn test_tuple_simple() {
264        check_assist(
265            term_search,
266            r#"//- minicore: todo, unimplemented
267fn f() { let a = 1; let b = 0.0; let c: (i32, f64) = todo$0!(); }"#,
268            r#"fn f() { let a = 1; let b = 0.0; let c: (i32, f64) = (a, b); }"#,
269        )
270    }
271
272    #[test]
273    fn test_tuple_nested() {
274        check_assist(
275            term_search,
276            r#"//- minicore: todo, unimplemented
277fn f() { let a = 1; let b = 0.0; let c: (i32, (i32, f64)) = todo$0!(); }"#,
278            r#"fn f() { let a = 1; let b = 0.0; let c: (i32, (i32, f64)) = (a, (a, b)); }"#,
279        )
280    }
281
282    #[test]
283    fn test_tuple_struct_with_generics() {
284        check_assist(
285            term_search,
286            r#"//- minicore: todo, unimplemented
287struct Foo<T>(T);
288fn f() { let a = 1; let b: Foo<i32> = todo$0!(); }"#,
289            r#"struct Foo<T>(T);
290fn f() { let a = 1; let b: Foo<i32> = Foo(a); }"#,
291        )
292    }
293
294    #[test]
295    fn test_struct_assoc_item() {
296        check_assist(
297            term_search,
298            r#"//- minicore: todo, unimplemented
299struct Foo;
300impl Foo { const FOO: i32 = 0; }
301fn f() { let a: i32 = todo$0!(); }"#,
302            r#"struct Foo;
303impl Foo { const FOO: i32 = 0; }
304fn f() { let a: i32 = Foo::FOO; }"#,
305        )
306    }
307
308    #[test]
309    fn test_trait_assoc_item() {
310        check_assist(
311            term_search,
312            r#"//- minicore: todo, unimplemented
313struct Foo;
314trait Bar { const BAR: i32; }
315impl Bar for Foo { const BAR: i32 = 0; }
316fn f() { let a: i32 = todo$0!(); }"#,
317            r#"struct Foo;
318trait Bar { const BAR: i32; }
319impl Bar for Foo { const BAR: i32 = 0; }
320fn f() { let a: i32 = Foo::BAR; }"#,
321        )
322    }
323}