1use 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}