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