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, EXPR_STMT, MATCH_ARM, STMT_LIST},
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 matches!(parent.kind(), STMT_LIST | EXPR_STMT) => {
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
777 #[test]
778 fn let_tail_block() {
779 check_edit(
780 "let",
781 r#"
782fn main() {
783 baz.l$0
784}
785"#,
786 r#"
787fn main() {
788 let $0 = baz;
789}
790"#,
791 );
792
793 check(
794 r#"
795fn main() {
796 baz.l$0
797}
798"#,
799 expect![[r#"
800 sn box Box::new(expr)
801 sn call function(expr)
802 sn const const {}
803 sn dbg dbg!(expr)
804 sn dbgr dbg!(&expr)
805 sn deref *expr
806 sn if if expr {}
807 sn let let
808 sn letm let mut
809 sn match match expr {}
810 sn not !expr
811 sn ref &expr
812 sn refm &mut expr
813 sn return return expr
814 sn unsafe unsafe {}
815 sn while while expr {}
816 "#]],
817 );
818
819 check(
820 r#"
821fn main() {
822 &baz.l$0
823}
824"#,
825 expect![[r#"
826 sn box Box::new(expr)
827 sn call function(expr)
828 sn const const {}
829 sn dbg dbg!(expr)
830 sn dbgr dbg!(&expr)
831 sn deref *expr
832 sn if if expr {}
833 sn let let
834 sn letm let mut
835 sn match match expr {}
836 sn not !expr
837 sn ref &expr
838 sn refm &mut expr
839 sn return return expr
840 sn unsafe unsafe {}
841 sn while while expr {}
842 "#]],
843 );
844 }
845
846 #[test]
847 fn let_before_semicolon() {
848 check_edit(
849 "let",
850 r#"
851fn main() {
852 baz.l$0;
853}
854"#,
855 r#"
856fn main() {
857 let $0 = baz;
858}
859"#,
860 );
861 }
862
863 #[test]
864 fn option_iflet() {
865 check_edit(
866 "ifl",
867 r#"
868//- minicore: option
869fn main() {
870 let bar = Some(true);
871 bar.$0
872}
873"#,
874 r#"
875fn main() {
876 let bar = Some(true);
877 if let Some(${1:bar}) = bar {
878 $0
879}
880}
881"#,
882 );
883 }
884
885 #[test]
886 fn option_iflet_cond() {
887 check(
888 r#"
889//- minicore: option
890fn main() {
891 let bar = Some(true);
892 if bar.$0
893}
894"#,
895 expect![[r#"
896 me and(…) fn(self, Option<U>) -> Option<U>
897 me as_ref() const fn(&self) -> Option<&T>
898 me ok_or(…) const fn(self, E) -> Result<T, E>
899 me unwrap() const fn(self) -> T
900 me unwrap_or(…) fn(self, T) -> T
901 sn box Box::new(expr)
902 sn call function(expr)
903 sn const const {}
904 sn dbg dbg!(expr)
905 sn dbgr dbg!(&expr)
906 sn deref *expr
907 sn let let Some(_)
908 sn letm let Some(mut _)
909 sn ref &expr
910 sn refm &mut expr
911 sn return return expr
912 sn unsafe unsafe {}
913 "#]],
914 );
915 check_edit(
916 "let",
917 r#"
918//- minicore: option
919fn main() {
920 let bar = Some(true);
921 if bar.$0
922}
923"#,
924 r#"
925fn main() {
926 let bar = Some(true);
927 if let Some(${0:bar}) = bar
928}
929"#,
930 );
931 check_edit(
932 "let",
933 r#"
934//- minicore: option
935fn main() {
936 let bar = Some(true);
937 if true && bar.$0
938}
939"#,
940 r#"
941fn main() {
942 let bar = Some(true);
943 if true && let Some(${0:bar}) = bar
944}
945"#,
946 );
947 check_edit(
948 "let",
949 r#"
950//- minicore: option
951fn main() {
952 let bar = Some(true);
953 if true && true && bar.$0
954}
955"#,
956 r#"
957fn main() {
958 let bar = Some(true);
959 if true && true && let Some(${0:bar}) = bar
960}
961"#,
962 );
963 }
964
965 #[test]
966 fn iflet_fallback_cond() {
967 check_edit(
968 "let",
969 r#"
970fn main() {
971 let bar = 2;
972 if bar.$0
973}
974"#,
975 r#"
976fn main() {
977 let bar = 2;
978 if let $1 = bar
979}
980"#,
981 );
982 }
983
984 #[test]
985 fn match_arm_let_block() {
986 check(
987 r#"
988fn main() {
989 match 2 {
990 bar => bar.$0
991 }
992}
993"#,
994 expect![[r#"
995 sn box Box::new(expr)
996 sn call function(expr)
997 sn const const {}
998 sn dbg dbg!(expr)
999 sn dbgr dbg!(&expr)
1000 sn deref *expr
1001 sn let let
1002 sn letm let mut
1003 sn match match expr {}
1004 sn ref &expr
1005 sn refm &mut expr
1006 sn return return expr
1007 sn unsafe unsafe {}
1008 "#]],
1009 );
1010 check(
1011 r#"
1012fn main() {
1013 match 2 {
1014 bar => &bar.l$0
1015 }
1016}
1017"#,
1018 expect![[r#"
1019 sn box Box::new(expr)
1020 sn call function(expr)
1021 sn const const {}
1022 sn dbg dbg!(expr)
1023 sn dbgr dbg!(&expr)
1024 sn deref *expr
1025 sn let let
1026 sn letm let mut
1027 sn match match expr {}
1028 sn ref &expr
1029 sn refm &mut expr
1030 sn return return expr
1031 sn unsafe unsafe {}
1032 "#]],
1033 );
1034 check_edit(
1035 "let",
1036 r#"
1037fn main() {
1038 match 2 {
1039 bar => bar.$0
1040 }
1041}
1042"#,
1043 r#"
1044fn main() {
1045 match 2 {
1046 bar => {
1047 let $1 = bar;
1048 $0
1049}
1050 }
1051}
1052"#,
1053 );
1054 }
1055
1056 #[test]
1057 fn closure_let_block() {
1058 check_edit(
1059 "let",
1060 r#"
1061fn main() {
1062 let bar = 2;
1063 let f = || bar.$0;
1064}
1065"#,
1066 r#"
1067fn main() {
1068 let bar = 2;
1069 let f = || {
1070 let $1 = bar;
1071 $0
1072};
1073}
1074"#,
1075 );
1076 }
1077
1078 #[test]
1079 fn option_letelse() {
1080 check_edit(
1081 "lete",
1082 r#"
1083//- minicore: option
1084fn main() {
1085 let bar = Some(true);
1086 bar.$0
1087}
1088"#,
1089 r#"
1090fn main() {
1091 let bar = Some(true);
1092 let Some(${1:bar}) = bar else {
1093 $2
1094};$0
1095}
1096"#,
1097 );
1098
1099 check_edit(
1100 "lete",
1101 r#"
1102//- minicore: option
1103fn main() {
1104 let bar = Some(true);
1105 bar.$0
1106 other();
1107}
1108"#,
1109 r#"
1110fn main() {
1111 let bar = Some(true);
1112 let Some(${1:bar}) = bar else {
1113 $2
1114};$0
1115 other();
1116}
1117"#,
1118 );
1119 }
1120
1121 #[test]
1122 fn result_match() {
1123 check_edit(
1124 "match",
1125 r#"
1126//- minicore: result
1127fn main() {
1128 let bar = Ok(true);
1129 bar.$0
1130}
1131"#,
1132 r#"
1133fn main() {
1134 let bar = Ok(true);
1135 match bar {
1136 Ok(${1:_}) => {$2},
1137 Err(${3:_}) => {$0},
1138}
1139}
1140"#,
1141 );
1142 }
1143
1144 #[test]
1145 fn postfix_completion_works_for_ambiguous_float_literal() {
1146 check_edit("refm", r#"fn main() { 42.$0 }"#, r#"fn main() { &mut 42 }"#)
1147 }
1148
1149 #[test]
1150 fn works_in_simple_macro() {
1151 check_edit(
1152 "dbg",
1153 r#"
1154macro_rules! m { ($e:expr) => { $e } }
1155fn main() {
1156 let bar: u8 = 12;
1157 m!(bar.d$0)
1158}
1159"#,
1160 r#"
1161macro_rules! m { ($e:expr) => { $e } }
1162fn main() {
1163 let bar: u8 = 12;
1164 m!(dbg!(bar))
1165}
1166"#,
1167 );
1168 }
1169
1170 #[test]
1171 fn postfix_completion_for_references() {
1172 check_edit("dbg", r#"fn main() { &&42.$0 }"#, r#"fn main() { dbg!(&&42) }"#);
1173 check_edit("dbg", r#"fn main() { &&*"hello".$0 }"#, r#"fn main() { dbg!(&&*"hello") }"#);
1174 check_edit("refm", r#"fn main() { &&42.$0 }"#, r#"fn main() { &&&mut 42 }"#);
1175 check_edit(
1176 "ifl",
1177 r#"
1178//- minicore: option
1179fn main() {
1180 let bar = &Some(true);
1181 bar.$0
1182}
1183"#,
1184 r#"
1185fn main() {
1186 let bar = &Some(true);
1187 if let Some(${1:bar}) = bar {
1188 $0
1189}
1190}
1191"#,
1192 )
1193 }
1194
1195 #[test]
1196 fn postfix_completion_for_nots() {
1197 check_edit(
1198 "if",
1199 r#"
1200fn main() {
1201 let is_foo = true;
1202 !is_foo.$0
1203}
1204"#,
1205 r#"
1206fn main() {
1207 let is_foo = true;
1208 if !is_foo {
1209 $0
1210}
1211}
1212"#,
1213 )
1214 }
1215
1216 #[test]
1217 fn postfix_completion_if_else_in_value() {
1218 check_edit(
1219 "if",
1220 r#"
1221fn main() {
1222 let s = cond.is_some().$0;
1223}
1224"#,
1225 r#"
1226fn main() {
1227 let s = if cond.is_some() {
1228 $1
1229} else {
1230 $0
1231};
1232}
1233"#,
1234 );
1235
1236 check_edit(
1237 "ifl",
1238 r#"
1239//- minicore: option
1240fn main() {
1241 let cond = Some("x");
1242 let s = cond.$0;
1243}
1244"#,
1245 r#"
1246fn main() {
1247 let cond = Some("x");
1248 let s = if let Some(${1:cond}) = cond {
1249 $2
1250} else {
1251 $0
1252};
1253}
1254"#,
1255 );
1256
1257 check_edit(
1258 "if",
1259 r#"
1260fn main() {
1261 2 + true.$0;
1262}
1263"#,
1264 r#"
1265fn main() {
1266 2 + if true {
1267 $1
1268} else {
1269 $0
1270};
1271}
1272"#,
1273 );
1274 }
1275
1276 #[test]
1277 fn postfix_completion_for_unsafe() {
1278 postfix_completion_for_block("unsafe");
1279 }
1280
1281 #[test]
1282 fn postfix_completion_for_const() {
1283 postfix_completion_for_block("const");
1284 }
1285
1286 fn postfix_completion_for_block(kind: &str) {
1287 check_edit(kind, r#"fn main() { foo.$0 }"#, &format!("fn main() {{ {kind} {{ foo }} }}"));
1288 check_edit(
1289 kind,
1290 r#"fn main() { { foo }.$0 }"#,
1291 &format!("fn main() {{ {kind} {{ foo }} }}"),
1292 );
1293 check_edit(
1294 kind,
1295 r#"fn main() { if x { foo }.$0 }"#,
1296 &format!("fn main() {{ {kind} {{ if x {{ foo }} }} }}"),
1297 );
1298 check_edit(
1299 kind,
1300 r#"fn main() { loop { foo }.$0 }"#,
1301 &format!("fn main() {{ {kind} {{ loop {{ foo }} }} }}"),
1302 );
1303 check_edit(
1304 kind,
1305 r#"fn main() { if true {}.$0 }"#,
1306 &format!("fn main() {{ {kind} {{ if true {{}} }} }}"),
1307 );
1308 check_edit(
1309 kind,
1310 r#"fn main() { while true {}.$0 }"#,
1311 &format!("fn main() {{ {kind} {{ while true {{}} }} }}"),
1312 );
1313 check_edit(
1314 kind,
1315 r#"
1316//- minicore: iterator
1317fn main() { for i in 0..10 {}.$0 }"#,
1318 &format!("fn main() {{ {kind} {{ for i in 0..10 {{}} }} }}"),
1319 );
1320 check_edit(
1321 kind,
1322 r#"fn main() { let x = if true {1} else {2}.$0 }"#,
1323 &format!("fn main() {{ let x = {kind} {{ if true {{1}} else {{2}} }} }}"),
1324 );
1325
1326 if kind == "const" {
1327 check_edit(
1328 kind,
1329 r#"fn main() { unsafe {1}.$0 }"#,
1330 &format!("fn main() {{ {kind} {{ unsafe {{1}} }} }}"),
1331 );
1332 } else {
1333 check_edit(
1334 kind,
1335 r#"fn main() { const {1}.$0 }"#,
1336 &format!("fn main() {{ {kind} {{ const {{1}} }} }}"),
1337 );
1338 }
1339
1340 check_edit(
1342 kind,
1343 r#"fn main() { let x = true else {panic!()}.$0}"#,
1344 &format!("fn main() {{ let x = true else {{panic!()}}.{kind} $0}}"),
1345 );
1346 }
1347
1348 #[test]
1349 fn custom_postfix_completion() {
1350 let config = CompletionConfig {
1351 snippets: vec![
1352 Snippet::new(
1353 &[],
1354 &["break".into()],
1355 &["ControlFlow::Break(${receiver})".into()],
1356 "",
1357 &["core::ops::ControlFlow".into()],
1358 crate::SnippetScope::Expr,
1359 )
1360 .unwrap(),
1361 ],
1362 ..TEST_CONFIG
1363 };
1364
1365 check_edit_with_config(
1366 config.clone(),
1367 "break",
1368 r#"
1369//- minicore: try
1370fn main() { 42.$0 }
1371"#,
1372 r#"
1373use core::ops::ControlFlow;
1374
1375fn main() { ControlFlow::Break(42) }
1376"#,
1377 );
1378
1379 check_edit_with_config(
1385 config.clone(),
1386 "break",
1387 r#"
1388//- minicore: try
1389fn main() { '\\'.$0 }
1390"#,
1391 r#"
1392use core::ops::ControlFlow;
1393
1394fn main() { ControlFlow::Break('\\\\') }
1395"#,
1396 );
1397
1398 check_edit_with_config(
1399 config,
1400 "break",
1401 r#"
1402//- minicore: try
1403fn main() {
1404 match true {
1405 true => "${1:placeholder}",
1406 false => "\$",
1407 }.$0
1408}
1409"#,
1410 r#"
1411use core::ops::ControlFlow;
1412
1413fn main() {
1414 ControlFlow::Break(match true {
1415 true => "\${1:placeholder}",
1416 false => "\\\$",
1417})
1418}
1419"#,
1420 );
1421 }
1422
1423 #[test]
1424 fn postfix_completion_for_format_like_strings() {
1425 check_edit(
1426 "format",
1427 r#"fn main() { "{some_var:?}".$0 }"#,
1428 r#"fn main() { format!("{some_var:?}") }"#,
1429 );
1430 check_edit(
1431 "panic",
1432 r#"fn main() { "Panic with {a}".$0 }"#,
1433 r#"fn main() { panic!("Panic with {a}"); }"#,
1434 );
1435 check_edit(
1436 "println",
1437 r#"fn main() { "{ 2+2 } { SomeStruct { val: 1, other: 32 } :?}".$0 }"#,
1438 r#"fn main() { println!("{} {:?}", 2+2, SomeStruct { val: 1, other: 32 }); }"#,
1439 );
1440 check_edit(
1441 "loge",
1442 r#"fn main() { "{2+2}".$0 }"#,
1443 r#"fn main() { log::error!("{}", 2+2); }"#,
1444 );
1445 check_edit(
1446 "logt",
1447 r#"fn main() { "{2+2}".$0 }"#,
1448 r#"fn main() { log::trace!("{}", 2+2); }"#,
1449 );
1450 check_edit(
1451 "logd",
1452 r#"fn main() { "{2+2}".$0 }"#,
1453 r#"fn main() { log::debug!("{}", 2+2); }"#,
1454 );
1455 check_edit(
1456 "logi",
1457 r#"fn main() { "{2+2}".$0 }"#,
1458 r#"fn main() { log::info!("{}", 2+2); }"#,
1459 );
1460 check_edit(
1461 "logw",
1462 r#"fn main() { "{2+2}".$0 }"#,
1463 r#"fn main() { log::warn!("{}", 2+2); }"#,
1464 );
1465 check_edit(
1466 "loge",
1467 r#"fn main() { "{2+2}".$0 }"#,
1468 r#"fn main() { log::error!("{}", 2+2); }"#,
1469 );
1470 }
1471
1472 #[test]
1473 fn postfix_custom_snippets_completion_for_references() {
1474 let snippet = Snippet::new(
1477 &[],
1478 &["ok".into()],
1479 &["Ok(${receiver})".into()],
1480 "",
1481 &[],
1482 crate::SnippetScope::Expr,
1483 )
1484 .unwrap();
1485
1486 check_edit_with_config(
1487 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1488 "ok",
1489 r#"fn main() { &&42.o$0 }"#,
1490 r#"fn main() { Ok(&&42) }"#,
1491 );
1492
1493 check_edit_with_config(
1494 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1495 "ok",
1496 r#"fn main() { &&42.$0 }"#,
1497 r#"fn main() { Ok(&&42) }"#,
1498 );
1499
1500 check_edit_with_config(
1501 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1502 "ok",
1503 r#"fn main() { &raw mut 42.$0 }"#,
1504 r#"fn main() { Ok(&raw mut 42) }"#,
1505 );
1506
1507 check_edit_with_config(
1508 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1509 "ok",
1510 r#"fn main() { &raw const 42.$0 }"#,
1511 r#"fn main() { Ok(&raw const 42) }"#,
1512 );
1513
1514 check_edit_with_config(
1515 CompletionConfig { snippets: vec![snippet], ..TEST_CONFIG },
1516 "ok",
1517 r#"
1518struct A {
1519 a: i32,
1520}
1521
1522fn main() {
1523 let a = A {a :1};
1524 &a.a.$0
1525}
1526 "#,
1527 r#"
1528struct A {
1529 a: i32,
1530}
1531
1532fn main() {
1533 let a = A {a :1};
1534 Ok(&a.a)
1535}
1536 "#,
1537 );
1538 }
1539
1540 #[test]
1541 fn postfix_custom_snippets_completion_for_reference_expr() {
1542 let snippet = Snippet::new(
1544 &[],
1545 &["group".into()],
1546 &["(${receiver})".into()],
1547 "",
1548 &[],
1549 crate::SnippetScope::Expr,
1550 )
1551 .unwrap();
1552
1553 check_edit_with_config(
1554 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1555 "group",
1556 r#"fn main() { &[1, 2, 3].g$0 }"#,
1557 r#"fn main() { (&[1, 2, 3]) }"#,
1558 );
1559
1560 check_edit_with_config(
1561 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1562 "group",
1563 r#"fn main() { &&foo(a, b, 1+1).$0 }"#,
1564 r#"fn main() { (&&foo(a, b, 1+1)) }"#,
1565 );
1566
1567 check_edit_with_config(
1568 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1569 "group",
1570 r#"fn main() { &mut Foo { a: 1, b: 2, c: 3 }.$0 }"#,
1571 r#"fn main() { (&mut Foo { a: 1, b: 2, c: 3 }) }"#,
1572 );
1573
1574 check_edit_with_config(
1575 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1576 "group",
1577 r#"fn main() { &raw mut Foo::new().$0 }"#,
1578 r#"fn main() { (&raw mut Foo::new()) }"#,
1579 );
1580
1581 check_edit_with_config(
1582 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1583 "group",
1584 r#"fn main() { &raw const Foo::bar::SOME_CONST.$0 }"#,
1585 r#"fn main() { (&raw const Foo::bar::SOME_CONST) }"#,
1586 );
1587
1588 check_edit_with_config(
1589 CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
1590 "group",
1591 r#"macro_rules! id { ($($t:tt)*) => ($($t)*); }
1592fn main() { id!(&raw const Foo::bar::SOME_CONST.$0) }"#,
1593 r#"macro_rules! id { ($($t:tt)*) => ($($t)*); }
1594fn main() { id!((&raw const Foo::bar::SOME_CONST)) }"#,
1595 );
1596 }
1597
1598 #[test]
1599 fn no_postfix_completions_in_if_block_that_has_an_else() {
1600 check(
1601 r#"
1602fn test() {
1603 if true {}.$0 else {}
1604}
1605"#,
1606 expect![[r#""#]],
1607 );
1608 }
1609
1610 #[test]
1611 fn mut_ref_consuming() {
1612 check_edit(
1613 "call",
1614 r#"
1615fn main() {
1616 let mut x = &mut 2;
1617 &mut x.$0;
1618}
1619"#,
1620 r#"
1621fn main() {
1622 let mut x = &mut 2;
1623 ${1}(&mut x);
1624}
1625"#,
1626 );
1627 }
1628
1629 #[test]
1630 fn deref_consuming() {
1631 check_edit(
1632 "call",
1633 r#"
1634fn main() {
1635 let mut x = &mut 2;
1636 &mut *x.$0;
1637}
1638"#,
1639 r#"
1640fn main() {
1641 let mut x = &mut 2;
1642 ${1}(&mut *x);
1643}
1644"#,
1645 );
1646 }
1647
1648 #[test]
1649 fn inside_macro() {
1650 check_edit(
1651 "box",
1652 r#"
1653macro_rules! assert {
1654 ( $it:expr $(,)? ) => { $it };
1655}
1656
1657fn foo() {
1658 let a = true;
1659 assert!(if a == false { true } else { false }.$0);
1660}
1661 "#,
1662 r#"
1663macro_rules! assert {
1664 ( $it:expr $(,)? ) => { $it };
1665}
1666
1667fn foo() {
1668 let a = true;
1669 assert!(Box::new(if a == false { true } else { false }));
1670}
1671 "#,
1672 );
1673 }
1674
1675 #[test]
1676 fn snippet_dedent() {
1677 check_edit(
1678 "let",
1679 r#"
1680//- minicore: option
1681fn foo(x: Option<i32>, y: Option<i32>) {
1682 let _f = || {
1683 x
1684 .and(y)
1685 .map(|it| {
1686 it+2
1687 })
1688 .$0
1689 };
1690}
1691"#,
1692 r#"
1693fn foo(x: Option<i32>, y: Option<i32>) {
1694 let _f = || {
1695 let $0 = x
1696.and(y)
1697.map(|it| {
1698 it+2
1699});
1700 };
1701}
1702"#,
1703 );
1704 }
1705
1706 #[test]
1707 fn postfix_new() {
1708 check_edit(
1709 "new",
1710 r#"
1711struct OtherThing;
1712struct RefCell<T>(T);
1713impl<T> RefCell<T> {
1714 fn new(t: T) -> Self { RefCell(t) }
1715}
1716
1717fn main() {
1718 let other_thing = OtherThing;
1719 let thing: RefCell<OtherThing> = other_thing.$0;
1720}
1721"#,
1722 r#"
1723struct OtherThing;
1724struct RefCell<T>(T);
1725impl<T> RefCell<T> {
1726 fn new(t: T) -> Self { RefCell(t) }
1727}
1728
1729fn main() {
1730 let other_thing = OtherThing;
1731 let thing: RefCell<OtherThing> = RefCell::new(other_thing$0);
1732}
1733"#,
1734 );
1735
1736 check_edit(
1737 "new",
1738 r#"
1739mod foo {
1740 pub struct OtherThing;
1741 pub struct RefCell<T>(T);
1742 impl<T> RefCell<T> {
1743 pub fn new(t: T) -> Self { RefCell(t) }
1744 }
1745}
1746
1747fn main() {
1748 let thing: foo::RefCell<foo::OtherThing> = foo::OtherThing.$0;
1749}
1750"#,
1751 r#"
1752mod foo {
1753 pub struct OtherThing;
1754 pub struct RefCell<T>(T);
1755 impl<T> RefCell<T> {
1756 pub fn new(t: T) -> Self { RefCell(t) }
1757 }
1758}
1759
1760fn main() {
1761 let thing: foo::RefCell<foo::OtherThing> = foo::RefCell::new(foo::OtherThing$0);
1762}
1763"#,
1764 );
1765 }
1766}