1mod format_like;
4
5use base_db::SourceDatabase;
6use hir::{ItemInNs, Semantics};
7use ide_db::{
8 RootDatabase, SnippetCap,
9 documentation::{Documentation, HasDocs},
10 imports::insert_use::ImportScope,
11 source_change::SnippetEdit,
12 syntax_helpers::suggest_name::NameGenerator,
13 text_edit::TextEdit,
14 ty_filter::TryEnum,
15};
16use itertools::{Either, Itertools};
17use stdx::never;
18use syntax::{
19 SmolStr,
20 SyntaxKind::{CLOSURE_EXPR, MATCH_ARM},
21 T, TextRange, TextSize, ToSmolStr,
22 ast::{self, AstNode, AstToken},
23 format_smolstr, match_ast,
24};
25
26use crate::{
27 CompletionItem, CompletionItemKind, CompletionRelevance, Completions, SnippetScope,
28 completions::postfix::format_like::add_format_like_completions,
29 context::{BreakableKind, CompletionContext, DotAccess, DotAccessKind},
30 item::{Builder, CompletionRelevancePostfixMatch},
31};
32
33pub(crate) fn complete_postfix(
34 acc: &mut Completions,
35 ctx: &CompletionContext<'_, '_>,
36 dot_access: &DotAccess<'_>,
37) {
38 if !ctx.config.enable_postfix_completions {
39 return;
40 }
41
42 let (dot_receiver, receiver_ty, receiver_is_ambiguous_float_literal) = match dot_access {
43 DotAccess { receiver_ty: Some(ty), receiver: Some(it), kind, .. } => (
44 it,
45 &ty.original,
46 match *kind {
47 DotAccessKind::Field { receiver_is_ambiguous_float_literal } => {
48 receiver_is_ambiguous_float_literal
49 }
50 DotAccessKind::Method => false,
51 },
52 ),
53 _ => return,
54 };
55 let expr_ctx = &dot_access.ctx;
56 let receiver_accessor = receiver_accessor(dot_receiver);
57
58 let receiver_text =
59 get_receiver_text(&ctx.sema, dot_receiver, receiver_is_ambiguous_float_literal);
60
61 let cap = match ctx.config.snippet_cap {
62 Some(it) => it,
63 None => return,
64 };
65
66 let postfix_snippet = match build_postfix_snippet_builder(ctx, cap, dot_receiver) {
67 Some(it) => it,
68 None => return,
69 };
70 let semi =
71 if expr_ctx.in_block_expr && ctx.token.next_token().is_none_or(|it| it.kind() != T![;]) {
72 ";"
73 } else {
74 ""
75 };
76
77 let cfg = ctx.config.find_path_config(ctx.is_nightly);
78
79 if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop()
80 && receiver_ty.impls_trait(ctx.db, drop_trait, &[])
81 && let Some(drop_fn) = ctx.famous_defs().core_mem_drop()
82 && let Some(path) = ctx.module.find_path(ctx.db, ItemInNs::Values(drop_fn.into()), cfg)
83 {
84 cov_mark::hit!(postfix_drop_completion);
85 let mut item = postfix_snippet(
86 "drop",
87 "fn drop(&mut self)",
88 format!("{path}($0{receiver_text})", path = path.display(ctx.db, ctx.edition)),
89 );
90 item.set_documentation(drop_fn.docs(ctx.db));
91 item.add_to(acc, ctx.db);
92 }
93
94 postfix_snippet("ref", "&expr", format!("&{receiver_text}")).add_to(acc, ctx.db);
95 postfix_snippet("refm", "&mut expr", format!("&mut {receiver_text}")).add_to(acc, ctx.db);
96 postfix_snippet("deref", "*expr", format!("*{receiver_text}")).add_to(acc, ctx.db);
97
98 let (dot_receiver_including_refs, prefix) = include_references(&receiver_accessor);
102 let mut receiver_text = receiver_text;
103 receiver_text.insert_str(0, &prefix);
104 let postfix_snippet =
105 match build_postfix_snippet_builder(ctx, cap, &dot_receiver_including_refs) {
106 Some(it) => it,
107 None => return,
108 };
109
110 if !ctx.config.snippets.is_empty() {
111 add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text);
112 }
113
114 postfix_snippet("box", "Box::new(expr)", format!("Box::new({receiver_text})"))
115 .add_to(acc, ctx.db);
116 postfix_snippet("dbg", "dbg!(expr)", format!("dbg!({receiver_text})")).add_to(acc, ctx.db); postfix_snippet("dbgr", "dbg!(&expr)", format!("dbg!(&{receiver_text})")).add_to(acc, ctx.db);
118 postfix_snippet("call", "function(expr)", format!("${{1}}({receiver_text})"))
119 .add_to(acc, ctx.db);
120
121 if let Some(expected_ty) = ctx.expected_type.as_ref()
122 && let Some(adt) = expected_ty.as_adt()
123 {
124 let is_valid_new = expected_ty
125 .iterate_assoc_items(ctx.db, |item| {
126 if let hir::AssocItem::Function(func) = item
127 && func.name(ctx.db) == hir::sym::new
128 && !func.has_self_param(ctx.db)
129 {
130 let params = func.params_without_self(ctx.db);
131 if params.len() == 1 {
132 return Some(());
133 }
134 }
135 None
136 })
137 .is_some();
138
139 let adt = hir::ModuleDef::from(adt);
140 if is_valid_new && let Some(path) = ctx.module.find_path(ctx.db, adt, cfg) {
141 let ty_name = path.display(ctx.db, ctx.display_target.edition).to_smolstr();
142
143 postfix_snippet(
144 "new",
145 &format_smolstr!("{}::new(expr)", ty_name),
146 format!("{}::new({}$0)", ty_name, receiver_text),
147 )
148 .add_to(acc, ctx.db);
149 }
150 }
151
152 let try_enum = TryEnum::from_ty(&ctx.sema, receiver_ty);
153 let is_in_cond = is_in_condition(&dot_receiver_including_refs);
154 let is_in_value = is_in_value(&dot_receiver_including_refs);
155 if let Some(parent) = dot_receiver_including_refs.syntax().parent() {
156 let placeholder = suggest_receiver_name(dot_receiver, "0", &ctx.sema);
157 match &try_enum {
158 Some(try_enum) if is_in_cond => match try_enum {
159 TryEnum::Result => {
160 postfix_snippet(
161 "let",
162 "let Ok(_)",
163 format!("let Ok({placeholder}) = {receiver_text}"),
164 )
165 .add_to(acc, ctx.db);
166 postfix_snippet(
167 "letm",
168 "let Ok(mut _)",
169 format!("let Ok(mut {placeholder}) = {receiver_text}"),
170 )
171 .add_to(acc, ctx.db);
172 }
173 TryEnum::Option => {
174 postfix_snippet(
175 "let",
176 "let Some(_)",
177 format!("let Some({placeholder}) = {receiver_text}"),
178 )
179 .add_to(acc, ctx.db);
180 postfix_snippet(
181 "letm",
182 "let Some(mut _)",
183 format!("let Some(mut {placeholder}) = {receiver_text}"),
184 )
185 .add_to(acc, ctx.db);
186 }
187 },
188 _ if is_in_cond => {
189 postfix_snippet("let", "let", format!("let $1 = {receiver_text}"))
190 .add_to(acc, ctx.db);
191 }
192 _ if expr_ctx.in_block_expr => {
193 postfix_snippet("let", "let", format!("let $0 = {receiver_text}{semi}"))
194 .add_to(acc, ctx.db);
195 postfix_snippet("letm", "let mut", format!("let mut $0 = {receiver_text}{semi}"))
196 .add_to(acc, ctx.db);
197 }
198 _ if matches!(parent.kind(), MATCH_ARM | CLOSURE_EXPR) => {
199 postfix_snippet(
200 "let",
201 "let",
202 format!("{{\n let $1 = {receiver_text};\n $0\n}}"),
203 )
204 .add_to(acc, ctx.db);
205 postfix_snippet(
206 "letm",
207 "let mut",
208 format!("{{\n let mut $1 = {receiver_text};\n $0\n}}"),
209 )
210 .add_to(acc, ctx.db);
211 }
212 _ => (),
213 }
214 }
215
216 if !is_in_cond {
217 match try_enum {
218 Some(try_enum) => match try_enum {
219 TryEnum::Result => {
220 postfix_snippet(
221 "match",
222 "match expr {}",
223 format!("match {receiver_text} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}"),
224 )
225 .add_to(acc, ctx.db);
226 }
227 TryEnum::Option => {
228 postfix_snippet(
229 "match",
230 "match expr {}",
231 format!(
232 "match {receiver_text} {{\n Some(${{1:_}}) => {{$2}},\n None => {{$0}},\n}}"
233 ),
234 )
235 .add_to(acc, ctx.db);
236 }
237 },
238 None => {
239 postfix_snippet(
240 "match",
241 "match expr {}",
242 format!("match {receiver_text} {{\n ${{1:_}} => {{$0}},\n}}"),
243 )
244 .add_to(acc, ctx.db);
245 }
246 }
247 if let Some(try_enum) = &try_enum {
248 let placeholder = suggest_receiver_name(dot_receiver, "1", &ctx.sema);
249 let if_then_snip =
250 if is_in_value { "{\n $2\n} else {\n $0\n}" } else { "{\n $0\n}" };
251 match try_enum {
252 TryEnum::Result => {
253 postfix_snippet(
254 "ifl",
255 "if let Ok {}",
256 format!("if let Ok({placeholder}) = {receiver_text} {if_then_snip}"),
257 )
258 .add_to(acc, ctx.db);
259
260 postfix_snippet(
261 "lete",
262 "let Ok else {}",
263 format!("let Ok({placeholder}) = {receiver_text} else {{\n $2\n}};$0"),
264 )
265 .add_to(acc, ctx.db);
266
267 postfix_snippet(
268 "while",
269 "while let Ok {}",
270 format!("while let Ok({placeholder}) = {receiver_text} {{\n $0\n}}"),
271 )
272 .add_to(acc, ctx.db);
273 }
274 TryEnum::Option => {
275 postfix_snippet(
276 "ifl",
277 "if let Some {}",
278 format!("if let Some({placeholder}) = {receiver_text} {if_then_snip}"),
279 )
280 .add_to(acc, ctx.db);
281
282 postfix_snippet(
283 "lete",
284 "let Some else {}",
285 format!("let Some({placeholder}) = {receiver_text} else {{\n $2\n}};$0"),
286 )
287 .add_to(acc, ctx.db);
288
289 postfix_snippet(
290 "while",
291 "while let Some {}",
292 format!("while let Some({placeholder}) = {receiver_text} {{\n $0\n}}"),
293 )
294 .add_to(acc, ctx.db);
295 }
296 }
297 } else if receiver_ty.is_bool() || receiver_ty.is_unknown() {
298 let if_then_snip =
299 if is_in_value { "{\n $1\n} else {\n $0\n}" } else { "{\n $0\n}" };
300 postfix_snippet("if", "if expr {}", format!("if {receiver_text} {if_then_snip}"))
301 .add_to(acc, ctx.db);
302 postfix_snippet(
303 "while",
304 "while expr {}",
305 format!("while {receiver_text} {{\n $0\n}}"),
306 )
307 .add_to(acc, ctx.db);
308 } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator()
309 && receiver_ty.impls_trait(ctx.db, trait_, &[])
310 {
311 postfix_snippet(
312 "for",
313 "for ele in expr {}",
314 format!("for ele in {receiver_text} {{\n $0\n}}"),
315 )
316 .add_to(acc, ctx.db);
317 }
318 }
319
320 if receiver_ty.is_bool() || receiver_ty.is_unknown() {
321 postfix_snippet("not", "!expr", format!("!{receiver_text}")).add_to(acc, ctx.db);
322 }
323
324 let block_should_be_wrapped = if let ast::Expr::BlockExpr(block) = dot_receiver {
325 block.modifier().is_some() || !block.is_standalone()
326 } else {
327 true
328 };
329 {
330 let (open_brace, close_brace) =
331 if block_should_be_wrapped { ("{ ", " }") } else { ("", "") };
332 let (open_paren, close_paren) = if is_in_cond { ("(", ")") } else { ("", "") };
334 let unsafe_completion_string =
335 format!("{open_paren}unsafe {open_brace}{receiver_text}{close_brace}{close_paren}");
336 postfix_snippet("unsafe", "unsafe {}", unsafe_completion_string).add_to(acc, ctx.db);
337
338 let const_completion_string =
339 format!("{open_paren}const {open_brace}{receiver_text}{close_brace}{close_paren}");
340 postfix_snippet("const", "const {}", const_completion_string).add_to(acc, ctx.db);
341 }
342
343 if let ast::Expr::Literal(literal) = dot_receiver.clone()
344 && let Some(literal_text) = ast::String::cast(literal.token())
345 {
346 add_format_like_completions(acc, ctx, dot_receiver, cap, &literal_text, semi);
347 }
348
349 postfix_snippet("return", "return expr", format!("return {receiver_text}{semi}"))
350 .add_to(acc, ctx.db);
351
352 if let Some(BreakableKind::Block | BreakableKind::Loop) = expr_ctx.in_breakable {
353 postfix_snippet("break", "break expr", format!("break {receiver_text}{semi}"))
354 .add_to(acc, ctx.db);
355 }
356}
357
358fn suggest_receiver_name(
359 receiver: &ast::Expr,
360 n: &str,
361 sema: &Semantics<'_, RootDatabase>,
362) -> SmolStr {
363 let placeholder = |name| format_smolstr!("${{{n}:{name}}}");
364
365 match receiver {
366 ast::Expr::PathExpr(path) => {
367 if let Some(name) = path.path().and_then(|it| it.as_single_name_ref()) {
368 return placeholder(name.text());
369 }
370 }
371 ast::Expr::RefExpr(it) => {
372 if let Some(receiver) = it.expr() {
373 return suggest_receiver_name(&receiver, n, sema);
374 }
375 }
376 _ => {}
377 }
378
379 let name = NameGenerator::new_with_names([].into_iter()).try_for_variable(receiver, sema);
380 match name {
381 Some(name) => placeholder(&name),
382 None => format_smolstr!("${n}"),
383 }
384}
385
386fn get_receiver_text(
387 sema: &Semantics<'_, RootDatabase>,
388 receiver: &ast::Expr,
389 receiver_is_ambiguous_float_literal: bool,
390) -> String {
391 let Some(mut range) = sema.original_range_opt(receiver.syntax()) else {
393 return receiver.to_string();
394 };
395 if receiver_is_ambiguous_float_literal {
396 range.range = TextRange::at(range.range.start(), range.range.len() - TextSize::of('.'))
397 }
398 let file_text = sema.db.file_text(range.file_id.file_id(sema.db));
399 let text = file_text.text(sema.db);
400 let indent_spaces = indent_of_tail_line(&text[TextRange::up_to(range.range.end())]);
401 let mut text = stdx::dedent_by(indent_spaces, &text[range.range]);
402
403 SnippetEdit::escape_snippet_bits(&mut text);
406 return text;
407
408 fn indent_of_tail_line(text: &str) -> usize {
409 let tail_line = text.rsplit_once('\n').map_or(text, |(_, s)| s);
410 let trimmed = tail_line.trim_start_matches(' ');
411 tail_line.len() - trimmed.len()
412 }
413}
414
415fn receiver_accessor(receiver: &ast::Expr) -> ast::Expr {
416 receiver
417 .syntax()
418 .parent()
419 .and_then(ast::Expr::cast)
420 .filter(|it| {
421 matches!(
422 it,
423 ast::Expr::FieldExpr(_) | ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_)
424 )
425 })
426 .unwrap_or_else(|| receiver.clone())
427}
428
429fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) {
434 let mut resulting_element = initial_element.clone();
435 let mut prefix = String::new();
436
437 while let Some(parent) = resulting_element.syntax().parent().and_then(ast::PrefixExpr::cast)
438 && parent.op_kind() == Some(ast::UnaryOp::Deref)
439 {
440 resulting_element = ast::Expr::from(parent);
441 prefix.insert(0, '*');
442 }
443
444 while let Some(parent) = resulting_element.syntax().parent().and_then(ast::PrefixExpr::cast)
445 && parent.op_kind() == Some(ast::UnaryOp::Not)
446 {
447 resulting_element = ast::Expr::from(parent);
448 prefix.insert(0, '!');
449 }
450
451 while let Some(parent_ref_element) =
452 resulting_element.syntax().parent().and_then(ast::RefExpr::cast)
453 {
454 let last_child_or_token = parent_ref_element.syntax().last_child_or_token();
455 prefix.insert_str(
456 0,
457 parent_ref_element
458 .syntax()
459 .children_with_tokens()
460 .filter(|it| Some(it) != last_child_or_token.as_ref())
461 .flat_map(|it| {
462 let has_ws = it.next_sibling_or_token().is_some_and(|it| it.kind().is_trivia());
463 let need_ws = !has_ws && it.kind().is_any_identifier();
464 itertools::chain([Either::Left(it)], need_ws.then_some(Either::Right(" ")))
465 })
466 .format("")
467 .to_smolstr()
468 .as_str(),
469 );
470 resulting_element = ast::Expr::from(parent_ref_element);
471 }
472
473 (resulting_element, prefix)
474}
475
476fn build_postfix_snippet_builder<'ctx>(
477 ctx: &'ctx CompletionContext<'_, '_>,
478 cap: SnippetCap,
479 receiver: &'ctx ast::Expr,
480) -> Option<impl Fn(&str, &str, String) -> Builder + 'ctx> {
481 let receiver_range = ctx.sema.original_range_opt(receiver.syntax())?.range;
482 if ctx.source_range().end() < receiver_range.start() {
483 never!();
486 return None;
487 }
488 let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());
489
490 fn build<'ctx>(
493 ctx: &'ctx CompletionContext<'_, '_>,
494 cap: SnippetCap,
495 delete_range: TextRange,
496 ) -> impl Fn(&str, &str, String) -> Builder + 'ctx {
497 move |label, detail, snippet| {
498 let edit = TextEdit::replace(delete_range, snippet);
499 let mut item = CompletionItem::new(
500 CompletionItemKind::Snippet,
501 ctx.source_range(),
502 label,
503 ctx.edition,
504 );
505 item.detail(detail).snippet_edit(cap, edit);
506 let postfix_match = if ctx.original_token.text() == label {
507 cov_mark::hit!(postfix_exact_match_is_high_priority);
508 Some(CompletionRelevancePostfixMatch::Exact)
509 } else {
510 cov_mark::hit!(postfix_inexact_match_is_low_priority);
511 Some(CompletionRelevancePostfixMatch::NonExact)
512 };
513 let relevance = CompletionRelevance { postfix_match, ..Default::default() };
514 item.set_relevance(relevance);
515 item
516 }
517 }
518 Some(build(ctx, cap, delete_range))
519}
520
521fn add_custom_postfix_completions(
522 acc: &mut Completions,
523 ctx: &CompletionContext<'_, '_>,
524 postfix_snippet: impl Fn(&str, &str, String) -> Builder,
525 receiver_text: &str,
526) -> Option<()> {
527 ImportScope::find_insert_use_container(&ctx.token.parent()?, &ctx.sema)?;
528 ctx.config.postfix_snippets().filter(|(_, snip)| snip.scope == SnippetScope::Expr).for_each(
529 |(trigger, snippet)| {
530 let imports = match snippet.imports(ctx) {
531 Some(imports) => imports,
532 None => return,
533 };
534 let body = snippet.postfix_snippet(receiver_text);
535 let document = Documentation::new_owned(format!("```rust\n{body}\n```"));
536 let mut builder =
537 postfix_snippet(trigger, snippet.description.as_deref().unwrap_or_default(), body);
538 builder.documentation(document);
539 for import in imports.into_iter() {
540 builder.add_import(import);
541 }
542 builder.add_to(acc, ctx.db);
543 },
544 );
545 None
546}
547
548pub(crate) fn is_in_condition(it: &ast::Expr) -> bool {
549 it.syntax()
550 .parent()
551 .and_then(|parent| {
552 Some(match_ast! { match parent {
553 ast::IfExpr(expr) => expr.condition()? == *it,
554 ast::WhileExpr(expr) => expr.condition()? == *it,
555 ast::MatchGuard(guard) => guard.condition()? == *it,
556 ast::BinExpr(bin_expr) => (bin_expr.op_token()?.kind() == T![&&])
557 .then(|| is_in_condition(&bin_expr.into()))?,
558 ast::Expr(expr) => (expr.syntax().text_range().start() == it.syntax().text_range().start())
559 .then(|| is_in_condition(&expr))?,
560 _ => return None,
561 } })
562 })
563 .unwrap_or(false)
564}
565
566pub(crate) fn is_in_value(it: &ast::Expr) -> bool {
567 let Some(node) = it.syntax().parent() else { return false };
568 let kind = node.kind();
569 ast::LetStmt::can_cast(kind)
570 || ast::ArgList::can_cast(kind)
571 || ast::ArrayExpr::can_cast(kind)
572 || ast::ParenExpr::can_cast(kind)
573 || ast::BreakExpr::can_cast(kind)
574 || ast::ReturnExpr::can_cast(kind)
575 || ast::PrefixExpr::can_cast(kind)
576 || ast::FormatArgsArg::can_cast(kind)
577 || ast::RecordExprField::can_cast(kind)
578 || ast::BinExpr::cast(node.clone()).is_some_and(|expr| expr.rhs().as_ref() == Some(it))
579 || ast::IndexExpr::cast(node).is_some_and(|expr| expr.index().as_ref() == Some(it))
580}
581
582#[cfg(test)]
583mod tests {
584 use expect_test::expect;
585
586 use crate::{
587 CompletionConfig, Snippet,
588 tests::{TEST_CONFIG, check, check_edit, check_edit_with_config},
589 };
590
591 #[test]
592 fn postfix_completion_works_for_trivial_path_expression() {
593 check(
594 r#"
595fn main() {
596 let bar = true;
597 bar.$0
598}
599"#,
600 expect![[r#"
601 sn box Box::new(expr)
602 sn call function(expr)
603 sn const const {}
604 sn dbg dbg!(expr)
605 sn dbgr dbg!(&expr)
606 sn deref *expr
607 sn if if expr {}
608 sn let let
609 sn letm let mut
610 sn match match expr {}
611 sn not !expr
612 sn ref &expr
613 sn refm &mut expr
614 sn return return expr
615 sn unsafe unsafe {}
616 sn while while expr {}
617 "#]],
618 );
619 }
620
621 #[test]
622 fn postfix_completion_works_for_function_calln() {
623 check(
624 r#"
625fn foo(elt: bool) -> bool {
626 !elt
627}
628
629fn main() {
630 let bar = true;
631 foo(bar.$0)
632}
633"#,
634 expect![[r#"
635 sn box Box::new(expr)
636 sn call function(expr)
637 sn const const {}
638 sn dbg dbg!(expr)
639 sn dbgr dbg!(&expr)
640 sn deref *expr
641 sn if if expr {}
642 sn match match expr {}
643 sn not !expr
644 sn ref &expr
645 sn refm &mut expr
646 sn return return expr
647 sn unsafe unsafe {}
648 sn while while expr {}
649 "#]],
650 );
651 }
652
653 #[test]
654 fn postfix_completion_works_in_if_condition() {
655 check(
656 r#"
657fn foo(cond: bool) {
658 if cond.$0
659}
660"#,
661 expect![[r#"
662 sn box Box::new(expr)
663 sn call function(expr)
664 sn const const {}
665 sn dbg dbg!(expr)
666 sn dbgr dbg!(&expr)
667 sn deref *expr
668 sn let let
669 sn not !expr
670 sn ref &expr
671 sn refm &mut expr
672 sn return return expr
673 sn unsafe unsafe {}
674 "#]],
675 );
676 }
677
678 #[test]
679 fn postfix_type_filtering() {
680 check(
681 r#"
682fn main() {
683 let bar: u8 = 12;
684 bar.$0
685}
686"#,
687 expect![[r#"
688 sn box Box::new(expr)
689 sn call function(expr)
690 sn const const {}
691 sn dbg dbg!(expr)
692 sn dbgr dbg!(&expr)
693 sn deref *expr
694 sn let let
695 sn letm let mut
696 sn match match expr {}
697 sn ref &expr
698 sn refm &mut expr
699 sn return return expr
700 sn unsafe unsafe {}
701 "#]],
702 )
703 }
704
705 #[test]
706 fn let_middle_block() {
707 check_edit(
708 "let",
709 r#"
710fn main() {
711 baz.l$0
712 res
713}
714"#,
715 r#"
716fn main() {
717 let $0 = baz;
718 res
719}
720"#,
721 );
722
723 check(
724 r#"
725fn main() {
726 baz.l$0
727 res
728}
729"#,
730 expect![[r#"
731 sn box Box::new(expr)
732 sn call function(expr)
733 sn const const {}
734 sn dbg dbg!(expr)
735 sn dbgr dbg!(&expr)
736 sn deref *expr
737 sn if if expr {}
738 sn let let
739 sn letm let mut
740 sn match match expr {}
741 sn not !expr
742 sn ref &expr
743 sn refm &mut expr
744 sn return return expr
745 sn unsafe unsafe {}
746 sn while while expr {}
747 "#]],
748 );
749 check(
750 r#"
751fn main() {
752 &baz.l$0
753 res
754}
755"#,
756 expect![[r#"
757 sn box Box::new(expr)
758 sn call function(expr)
759 sn const const {}
760 sn dbg dbg!(expr)
761 sn dbgr dbg!(&expr)
762 sn deref *expr
763 sn if if expr {}
764 sn let let
765 sn letm let mut
766 sn match match expr {}
767 sn not !expr
768 sn ref &expr
769 sn refm &mut expr
770 sn return return expr
771 sn unsafe unsafe {}
772 sn while while expr {}
773 "#]],
774 );
775
776 check_edit(
777 "let",
778 r#"
779fn foo() -> (i32, i32) {
780 "a".let$0
781 (2, 3).clone()
782}
783"#,
784 r#"
785fn foo() -> (i32, i32) {
786 let $0 = "a";
787 (2, 3).clone()
788}
789"#,
790 );
791
792 check_edit(
793 "let",
794 r#"
795fn foo() -> (i32, i32) {
796 "a".l$0
797 (2, 3).clone()
798}
799"#,
800 r#"
801fn foo() -> (i32, i32) {
802 let $0 = "a";
803 (2, 3).clone()
804}
805"#,
806 );
807 }
808
809 #[test]
810 fn let_tail_block() {
811 check_edit(
812 "let",
813 r#"
814fn main() {
815 baz.l$0
816}
817"#,
818 r#"
819fn main() {
820 let $0 = baz;
821}
822"#,
823 );
824
825 check(
826 r#"
827fn main() {
828 baz.l$0
829}
830"#,
831 expect![[r#"
832 sn box Box::new(expr)
833 sn call function(expr)
834 sn const const {}
835 sn dbg dbg!(expr)
836 sn dbgr dbg!(&expr)
837 sn deref *expr
838 sn if if expr {}
839 sn let let
840 sn letm let mut
841 sn match match expr {}
842 sn not !expr
843 sn ref &expr
844 sn refm &mut expr
845 sn return return expr
846 sn unsafe unsafe {}
847 sn while while expr {}
848 "#]],
849 );
850
851 check(
852 r#"
853fn main() {
854 &baz.l$0
855}
856"#,
857 expect![[r#"
858 sn box Box::new(expr)
859 sn call function(expr)
860 sn const const {}
861 sn dbg dbg!(expr)
862 sn dbgr dbg!(&expr)
863 sn deref *expr
864 sn if if expr {}
865 sn let let
866 sn letm let mut
867 sn match match expr {}
868 sn not !expr
869 sn ref &expr
870 sn refm &mut expr
871 sn return return expr
872 sn unsafe unsafe {}
873 sn while while expr {}
874 "#]],
875 );
876 }
877
878 #[test]
879 fn let_before_semicolon() {
880 check_edit(
881 "let",
882 r#"
883fn main() {
884 baz.l$0;
885}
886"#,
887 r#"
888fn main() {
889 let $0 = baz;
890}
891"#,
892 );
893 }
894
895 #[test]
896 fn option_iflet() {
897 check_edit(
898 "ifl",
899 r#"
900//- minicore: option
901fn main() {
902 let bar = Some(true);
903 bar.$0
904}
905"#,
906 r#"
907fn main() {
908 let bar = Some(true);
909 if let Some(${1:bar}) = bar {
910 $0
911}
912}
913"#,
914 );
915 }
916
917 #[test]
918 fn option_iflet_cond() {
919 check(
920 r#"
921//- minicore: option
922fn main() {
923 let bar = Some(true);
924 if bar.$0
925}
926"#,
927 expect![[r#"
928 me and(…) fn(self, Option<U>) -> Option<U>
929 me as_ref() const fn(&self) -> Option<&T>
930 me ok_or(…) const fn(self, E) -> Result<T, E>
931 me unwrap() const fn(self) -> T
932 me unwrap_or(…) fn(self, T) -> T
933 sn box Box::new(expr)
934 sn call function(expr)
935 sn const const {}
936 sn dbg dbg!(expr)
937 sn dbgr dbg!(&expr)
938 sn deref *expr
939 sn let let Some(_)
940 sn letm let Some(mut _)
941 sn ref &expr
942 sn refm &mut expr
943 sn return return expr
944 sn unsafe unsafe {}
945 "#]],
946 );
947 check_edit(
948 "let",
949 r#"
950//- minicore: option
951fn main() {
952 let bar = Some(true);
953 if bar.$0
954}
955"#,
956 r#"
957fn main() {
958 let bar = Some(true);
959 if let Some(${0:bar}) = bar
960}
961"#,
962 );
963 check_edit(
964 "let",
965 r#"
966//- minicore: option
967fn main() {
968 let bar = Some(true);
969 if true && bar.$0
970}
971"#,
972 r#"
973fn main() {
974 let bar = Some(true);
975 if true && let Some(${0:bar}) = bar
976}
977"#,
978 );
979 check_edit(
980 "let",
981 r#"
982//- minicore: option
983fn main() {
984 let bar = Some(true);
985 if true && true && bar.$0
986}
987"#,
988 r#"
989fn main() {
990 let bar = Some(true);
991 if true && true && let Some(${0:bar}) = bar
992}
993"#,
994 );
995 }
996
997 #[test]
998 fn iflet_fallback_cond() {
999 check_edit(
1000 "let",
1001 r#"
1002fn main() {
1003 let bar = 2;
1004 if bar.$0
1005}
1006"#,
1007 r#"
1008fn main() {
1009 let bar = 2;
1010 if let $1 = bar
1011}
1012"#,
1013 );
1014 }
1015
1016 #[test]
1017 fn match_arm_let_block() {
1018 check(
1019 r#"
1020fn main() {
1021 match 2 {
1022 bar => bar.$0
1023 }
1024}
1025"#,
1026 expect![[r#"
1027 sn box Box::new(expr)
1028 sn call function(expr)
1029 sn const const {}
1030 sn dbg dbg!(expr)
1031 sn dbgr dbg!(&expr)
1032 sn deref *expr
1033 sn let let
1034 sn letm let mut
1035 sn match match expr {}
1036 sn ref &expr
1037 sn refm &mut expr
1038 sn return return expr
1039 sn unsafe unsafe {}
1040 "#]],
1041 );
1042 check(
1043 r#"
1044fn main() {
1045 match 2 {
1046 bar => &bar.l$0
1047 }
1048}
1049"#,
1050 expect![[r#"
1051 sn box Box::new(expr)
1052 sn call function(expr)
1053 sn const const {}
1054 sn dbg dbg!(expr)
1055 sn dbgr dbg!(&expr)
1056 sn deref *expr
1057 sn let let
1058 sn letm let mut
1059 sn match match expr {}
1060 sn ref &expr
1061 sn refm &mut expr
1062 sn return return expr
1063 sn unsafe unsafe {}
1064 "#]],
1065 );
1066 check_edit(
1067 "let",
1068 r#"
1069fn main() {
1070 match 2 {
1071 bar => bar.$0
1072 }
1073}
1074"#,
1075 r#"
1076fn main() {
1077 match 2 {
1078 bar => {
1079 let $1 = bar;
1080 $0
1081}
1082 }
1083}
1084"#,
1085 );
1086 }
1087
1088 #[test]
1089 fn closure_let_block() {
1090 check_edit(
1091 "let",
1092 r#"
1093fn main() {
1094 let bar = 2;
1095 let f = || bar.$0;
1096}
1097"#,
1098 r#"
1099fn main() {
1100 let bar = 2;
1101 let f = || {
1102 let $1 = bar;
1103 $0
1104};
1105}
1106"#,
1107 );
1108 }
1109
1110 #[test]
1111 fn option_letelse() {
1112 check_edit(
1113 "lete",
1114 r#"
1115//- minicore: option
1116fn main() {
1117 let bar = Some(true);
1118 bar.$0
1119}
1120"#,
1121 r#"
1122fn main() {
1123 let bar = Some(true);
1124 let Some(${1:bar}) = bar else {
1125 $2
1126};$0
1127}
1128"#,
1129 );
1130
1131 check_edit(
1132 "lete",
1133 r#"
1134//- minicore: option
1135fn main() {
1136 let bar = Some(true);
1137 bar.$0
1138 other();
1139}
1140"#,
1141 r#"
1142fn main() {
1143 let bar = Some(true);
1144 let Some(${1:bar}) = bar else {
1145 $2
1146};$0
1147 other();
1148}
1149"#,
1150 );
1151 }
1152
1153 #[test]
1154 fn result_match() {
1155 check_edit(
1156 "match",
1157 r#"
1158//- minicore: result
1159fn main() {
1160 let bar = Ok(true);
1161 bar.$0
1162}
1163"#,
1164 r#"
1165fn main() {
1166 let bar = Ok(true);
1167 match bar {
1168 Ok(${1:_}) => {$2},
1169 Err(${3:_}) => {$0},
1170}
1171}
1172"#,
1173 );
1174 }
1175
1176 #[test]
1177 fn postfix_completion_works_for_ambiguous_float_literal() {
1178 check_edit("refm", r#"fn main() { 42.$0 }"#, r#"fn main() { &mut 42 }"#)
1179 }
1180
1181 #[test]
1182 fn works_in_simple_macro() {
1183 check_edit(
1184 "dbg",
1185 r#"
1186macro_rules! m { ($e:expr) => { $e } }
1187fn main() {
1188 let bar: u8 = 12;
1189 m!(bar.d$0)
1190}
1191"#,
1192 r#"
1193macro_rules! m { ($e:expr) => { $e } }
1194fn main() {
1195 let bar: u8 = 12;
1196 m!(dbg!(bar))
1197}
1198"#,
1199 );
1200 }
1201
1202 #[test]
1203 fn postfix_completion_for_references() {
1204 check_edit("dbg", r#"fn main() { &&42.$0 }"#, r#"fn main() { dbg!(&&42) }"#);
1205 check_edit("dbg", r#"fn main() { &&*"hello".$0 }"#, r#"fn main() { dbg!(&&*"hello") }"#);
1206 check_edit("refm", r#"fn main() { &&42.$0 }"#, r#"fn main() { &&&mut 42 }"#);
1207 check_edit(
1208 "ifl",
1209 r#"
1210//- minicore: option
1211fn main() {
1212 let bar = &Some(true);
1213 bar.$0
1214}
1215"#,
1216 r#"
1217fn main() {
1218 let bar = &Some(true);
1219 if let Some(${1:bar}) = bar {
1220 $0
1221}
1222}
1223"#,
1224 )
1225 }
1226
1227 #[test]
1228 fn postfix_completion_for_nots() {
1229 check_edit(
1230 "if",
1231 r#"
1232fn main() {
1233 let is_foo = true;
1234 !is_foo.$0
1235}
1236"#,
1237 r#"
1238fn main() {
1239 let is_foo = true;
1240 if !is_foo {
1241 $0
1242}
1243}
1244"#,
1245 )
1246 }
1247
1248 #[test]
1249 fn postfix_completion_if_else_in_value() {
1250 check_edit(
1251 "if",
1252 r#"
1253fn main() {
1254 let s = cond.is_some().$0;
1255}
1256"#,
1257 r#"
1258fn main() {
1259 let s = if cond.is_some() {
1260 $1
1261} else {
1262 $0
1263};
1264}
1265"#,
1266 );
1267
1268 check_edit(
1269 "ifl",
1270 r#"
1271//- minicore: option
1272fn main() {
1273 let cond = Some("x");
1274 let s = cond.$0;
1275}
1276"#,
1277 r#"
1278fn main() {
1279 let cond = Some("x");
1280 let s = if let Some(${1:cond}) = cond {
1281 $2
1282} else {
1283 $0
1284};
1285}
1286"#,
1287 );
1288
1289 check_edit(
1290 "if",
1291 r#"
1292fn main() {
1293 2 + true.$0;
1294}
1295"#,
1296 r#"
1297fn main() {
1298 2 + if true {
1299 $1
1300} else {
1301 $0
1302};
1303}
1304"#,
1305 );
1306 }
1307
1308 #[test]
1309 fn postfix_completion_for_unsafe() {
1310 postfix_completion_for_block("unsafe");
1311 }
1312
1313 #[test]
1314 fn postfix_completion_for_const() {
1315 postfix_completion_for_block("const");
1316 }
1317
1318 fn postfix_completion_for_block(kind: &str) {
1319 check_edit(kind, r#"fn main() { foo.$0 }"#, &format!("fn main() {{ {kind} {{ foo }} }}"));
1320 check_edit(
1321 kind,
1322 r#"fn main() { { foo }.$0 }"#,
1323 &format!("fn main() {{ {kind} {{ foo }} }}"),
1324 );
1325 check_edit(
1326 kind,
1327 r#"fn main() { if x { foo }.$0 }"#,
1328 &format!("fn main() {{ {kind} {{ if x {{ foo }} }} }}"),
1329 );
1330 check_edit(
1331 kind,
1332 r#"fn main() { loop { foo }.$0 }"#,
1333 &format!("fn main() {{ {kind} {{ loop {{ foo }} }} }}"),
1334 );
1335 check_edit(
1336 kind,
1337 r#"fn main() { if true {}.$0 }"#,
1338 &format!("fn main() {{ {kind} {{ if true {{}} }} }}"),
1339 );
1340 check_edit(
1341 kind,
1342 r#"fn main() { while true {}.$0 }"#,
1343 &format!("fn main() {{ {kind} {{ while true {{}} }} }}"),
1344 );
1345 check_edit(
1346 kind,
1347 r#"
1348//- minicore: iterator
1349fn main() { for i in 0..10 {}.$0 }"#,
1350 &format!("fn main() {{ {kind} {{ for i in 0..10 {{}} }} }}"),
1351 );
1352 check_edit(
1353 kind,
1354 r#"fn main() { let x = if true {1} else {2}.$0 }"#,
1355 &format!("fn main() {{ let x = {kind} {{ if true {{1}} else {{2}} }} }}"),
1356 );
1357
1358 if kind == "const" {
1359 check_edit(
1360 kind,
1361 r#"fn main() { unsafe {1}.$0 }"#,
1362 &format!("fn main() {{ {kind} {{ unsafe {{1}} }} }}"),
1363 );
1364 } else {
1365 check_edit(
1366 kind,
1367 r#"fn main() { const {1}.$0 }"#,
1368 &format!("fn main() {{ {kind} {{ const {{1}} }} }}"),
1369 );
1370 }
1371
1372 check_edit(
1374 kind,
1375 r#"fn main() { let x = true else {panic!()}.$0}"#,
1376 &format!("fn main() {{ let x = true else {{panic!()}}.{kind} $0}}"),
1377 );
1378 }
1379
1380 #[test]
1381 fn custom_postfix_completion() {
1382 let config = CompletionConfig {
1383 snippets: vec![
1384 Snippet::new(
1385 &[],
1386 &["break".into()],
1387 &["ControlFlow::Break(${receiver})".into()],
1388 "",
1389 &["core::ops::ControlFlow".into()],
1390 crate::SnippetScope::Expr,
1391 )
1392 .unwrap(),
1393 ],
1394 ..TEST_CONFIG
1395 };
1396
1397 check_edit_with_config(
1398 config.clone(),
1399 "break",
1400 r#"
1401//- minicore: try
1402fn main() { 42.$0 }
1403"#,
1404 r#"
1405use core::ops::ControlFlow;
1406
1407fn main() { ControlFlow::Break(42) }
1408"#,
1409 );
1410
1411 check_edit_with_config(
1417 config.clone(),
1418 "break",
1419 r#"
1420//- minicore: try
1421fn main() { '\\'.$0 }
1422"#,
1423 r#"
1424use core::ops::ControlFlow;
1425
1426fn main() { ControlFlow::Break('\\\\') }
1427"#,
1428 );
1429
1430 check_edit_with_config(
1431 config,
1432 "break",
1433 r#"
1434//- minicore: try
1435fn main() {
1436 match true {
1437 true => "${1:placeholder}",
1438 false => "\$",
1439 }.$0
1440}
1441"#,
1442 r#"
1443use core::ops::ControlFlow;
1444
1445fn main() {
1446 ControlFlow::Break(match true {
1447 true => "\${1:placeholder}",
1448 false => "\\\$",
1449})
1450}
1451"#,
1452 );
1453 }
1454
1455 #[test]
1456 fn postfix_completion_for_format_like_strings() {
1457 check_edit(
1458 "format",
1459 r#"fn main() { "{some_var:?}".$0 }"#,
1460 r#"fn main() { format!("{some_var:?}") }"#,
1461 );
1462 check_edit(
1463 "panic",
1464 r#"fn main() { "Panic with {a}".$0 }"#,
1465 r#"fn main() { panic!("Panic with {a}"); }"#,
1466 );
1467 check_edit(
1468 "println",
1469 r#"fn main() { "{ 2+2 } { SomeStruct { val: 1, other: 32 } :?}".$0 }"#,
1470 r#"fn main() { println!("{} {:?}", 2+2, SomeStruct { val: 1, other: 32 }); }"#,
1471 );
1472 check_edit(
1473 "loge",
1474 r#"fn main() { "{2+2}".$0 }"#,
1475 r#"fn main() { log::error!("{}", 2+2); }"#,
1476 );
1477 check_edit(
1478 "logt",
1479 r#"fn main() { "{2+2}".$0 }"#,
1480 r#"fn main() { log::trace!("{}", 2+2); }"#,
1481 );
1482 check_edit(
1483 "logd",
1484 r#"fn main() { "{2+2}".$0 }"#,
1485 r#"fn main() { log::debug!("{}", 2+2); }"#,
1486 );
1487 check_edit(
1488 "logi",
1489 r#"fn main() { "{2+2}".$0 }"#,
1490 r#"fn main() { log::info!("{}", 2+2); }"#,
1491 );
1492 check_edit(
1493 "logw",
1494 r#"fn main() { "{2+2}".$0 }"#,
1495 r#"fn main() { log::warn!("{}", 2+2); }"#,
1496 );
1497 check_edit(
1498 "loge",
1499 r#"fn main() { "{2+2}".$0 }"#,
1500 r#"fn main() { log::error!("{}", 2+2); }"#,
1501 );
1502 }
1503
1504 #[test]
1505 fn postfix_custom_snippets_completion_for_references() {
1506 let snippet = Snippet::new(
1509 &[],
1510 &["ok".into()],
1511 &["Ok(${receiver})".into()],
1512 "",
1513 &[],
1514 crate::SnippetScope::Expr,
1515 )
1516 .unwrap();
1517
1518 check_edit_with_config(
1519 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1520 "ok",
1521 r#"fn main() { &&42.o$0 }"#,
1522 r#"fn main() { Ok(&&42) }"#,
1523 );
1524
1525 check_edit_with_config(
1526 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1527 "ok",
1528 r#"fn main() { &&42.$0 }"#,
1529 r#"fn main() { Ok(&&42) }"#,
1530 );
1531
1532 check_edit_with_config(
1533 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1534 "ok",
1535 r#"fn main() { &raw mut 42.$0 }"#,
1536 r#"fn main() { Ok(&raw mut 42) }"#,
1537 );
1538
1539 check_edit_with_config(
1540 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1541 "ok",
1542 r#"fn main() { &raw const 42.$0 }"#,
1543 r#"fn main() { Ok(&raw const 42) }"#,
1544 );
1545
1546 check_edit_with_config(
1547 CompletionConfig { snippets: vec![snippet], ..TEST_CONFIG },
1548 "ok",
1549 r#"
1550struct A {
1551 a: i32,
1552}
1553
1554fn main() {
1555 let a = A {a :1};
1556 &a.a.$0
1557}
1558 "#,
1559 r#"
1560struct A {
1561 a: i32,
1562}
1563
1564fn main() {
1565 let a = A {a :1};
1566 Ok(&a.a)
1567}
1568 "#,
1569 );
1570 }
1571
1572 #[test]
1573 fn postfix_custom_snippets_completion_for_reference_expr() {
1574 let snippet = Snippet::new(
1576 &[],
1577 &["group".into()],
1578 &["(${receiver})".into()],
1579 "",
1580 &[],
1581 crate::SnippetScope::Expr,
1582 )
1583 .unwrap();
1584
1585 check_edit_with_config(
1586 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1587 "group",
1588 r#"fn main() { &[1, 2, 3].g$0 }"#,
1589 r#"fn main() { (&[1, 2, 3]) }"#,
1590 );
1591
1592 check_edit_with_config(
1593 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1594 "group",
1595 r#"fn main() { &&foo(a, b, 1+1).$0 }"#,
1596 r#"fn main() { (&&foo(a, b, 1+1)) }"#,
1597 );
1598
1599 check_edit_with_config(
1600 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1601 "group",
1602 r#"fn main() { &mut Foo { a: 1, b: 2, c: 3 }.$0 }"#,
1603 r#"fn main() { (&mut Foo { a: 1, b: 2, c: 3 }) }"#,
1604 );
1605
1606 check_edit_with_config(
1607 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1608 "group",
1609 r#"fn main() { &raw mut Foo::new().$0 }"#,
1610 r#"fn main() { (&raw mut Foo::new()) }"#,
1611 );
1612
1613 check_edit_with_config(
1614 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1615 "group",
1616 r#"fn main() { &raw const Foo::bar::SOME_CONST.$0 }"#,
1617 r#"fn main() { (&raw const Foo::bar::SOME_CONST) }"#,
1618 );
1619
1620 check_edit_with_config(
1621 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1622 "group",
1623 r#"macro_rules! id { ($($t:tt)*) => ($($t)*); }
1624fn main() { id!(&raw const Foo::bar::SOME_CONST.$0) }"#,
1625 r#"macro_rules! id { ($($t:tt)*) => ($($t)*); }
1626fn main() { id!((&raw const Foo::bar::SOME_CONST)) }"#,
1627 );
1628 }
1629
1630 #[test]
1631 fn no_postfix_completions_in_if_block_that_has_an_else() {
1632 check(
1633 r#"
1634fn test() {
1635 if true {}.$0 else {}
1636}
1637"#,
1638 expect![[r#""#]],
1639 );
1640 }
1641
1642 #[test]
1643 fn mut_ref_consuming() {
1644 check_edit(
1645 "call",
1646 r#"
1647fn main() {
1648 let mut x = &mut 2;
1649 &mut x.$0;
1650}
1651"#,
1652 r#"
1653fn main() {
1654 let mut x = &mut 2;
1655 ${1}(&mut x);
1656}
1657"#,
1658 );
1659 }
1660
1661 #[test]
1662 fn deref_consuming() {
1663 check_edit(
1664 "call",
1665 r#"
1666fn main() {
1667 let mut x = &mut 2;
1668 &mut *x.$0;
1669}
1670"#,
1671 r#"
1672fn main() {
1673 let mut x = &mut 2;
1674 ${1}(&mut *x);
1675}
1676"#,
1677 );
1678 }
1679
1680 #[test]
1681 fn inside_macro() {
1682 check_edit(
1683 "box",
1684 r#"
1685macro_rules! assert {
1686 ( $it:expr $(,)? ) => { $it };
1687}
1688
1689fn foo() {
1690 let a = true;
1691 assert!(if a == false { true } else { false }.$0);
1692}
1693 "#,
1694 r#"
1695macro_rules! assert {
1696 ( $it:expr $(,)? ) => { $it };
1697}
1698
1699fn foo() {
1700 let a = true;
1701 assert!(Box::new(if a == false { true } else { false }));
1702}
1703 "#,
1704 );
1705 }
1706
1707 #[test]
1708 fn snippet_dedent() {
1709 check_edit(
1710 "let",
1711 r#"
1712//- minicore: option
1713fn foo(x: Option<i32>, y: Option<i32>) {
1714 let _f = || {
1715 x
1716 .and(y)
1717 .map(|it| {
1718 it+2
1719 })
1720 .$0
1721 };
1722}
1723"#,
1724 r#"
1725fn foo(x: Option<i32>, y: Option<i32>) {
1726 let _f = || {
1727 let $0 = x
1728.and(y)
1729.map(|it| {
1730 it+2
1731});
1732 };
1733}
1734"#,
1735 );
1736 }
1737
1738 #[test]
1739 fn postfix_new() {
1740 check_edit(
1741 "new",
1742 r#"
1743struct OtherThing;
1744struct RefCell<T>(T);
1745impl<T> RefCell<T> {
1746 fn new(t: T) -> Self { RefCell(t) }
1747}
1748
1749fn main() {
1750 let other_thing = OtherThing;
1751 let thing: RefCell<OtherThing> = other_thing.$0;
1752}
1753"#,
1754 r#"
1755struct OtherThing;
1756struct RefCell<T>(T);
1757impl<T> RefCell<T> {
1758 fn new(t: T) -> Self { RefCell(t) }
1759}
1760
1761fn main() {
1762 let other_thing = OtherThing;
1763 let thing: RefCell<OtherThing> = RefCell::new(other_thing$0);
1764}
1765"#,
1766 );
1767
1768 check_edit(
1769 "new",
1770 r#"
1771mod foo {
1772 pub struct OtherThing;
1773 pub struct RefCell<T>(T);
1774 impl<T> RefCell<T> {
1775 pub fn new(t: T) -> Self { RefCell(t) }
1776 }
1777}
1778
1779fn main() {
1780 let thing: foo::RefCell<foo::OtherThing> = foo::OtherThing.$0;
1781}
1782"#,
1783 r#"
1784mod foo {
1785 pub struct OtherThing;
1786 pub struct RefCell<T>(T);
1787 impl<T> RefCell<T> {
1788 pub fn new(t: T) -> Self { RefCell(t) }
1789 }
1790}
1791
1792fn main() {
1793 let thing: foo::RefCell<foo::OtherThing> = foo::RefCell::new(foo::OtherThing$0);
1794}
1795"#,
1796 );
1797 }
1798}