Skip to main content

ide_completion/completions/
postfix.rs

1//! Postfix completions, like `Ok(10).ifl$0` => `if let Ok() = Ok(10) { $0 }`.
2
3mod 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    // The rest of the postfix completions create an expression that moves an argument,
99    // so it's better to consider references now to avoid breaking the compilation
100
101    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); // fixme
117    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        // FIXME: Why add parentheses
333        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    // Do not just call `receiver.to_string()`, as that will mess up whitespaces inside macros.
392    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    // The receiver texts should be interpreted as-is, as they are expected to be
404    // normal Rust expressions.
405    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
429/// Given an `initial_element`, tries to expand it to include deref(s), not(s), and then references.
430/// Returns the expanded expressions, and the added prefix as a string
431///
432/// For example, if called with the `42` in `&&mut *42`, would return `(&&mut *42, "&&mut *")`.
433fn 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        // This shouldn't happen, yet it does. I assume this might be due to an incorrect token
484        // mapping.
485        never!();
486        return None;
487    }
488    let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());
489
490    // Wrapping impl Fn in an option ruins lifetime inference for the parameters in a way that
491    // can't be annotated for the closure, hence fix it by constructing it without the Option first
492    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        // completion will not be triggered
1373        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        // The receiver texts should be escaped, see comments in `get_receiver_text()`
1412        // for detail.
1413        //
1414        // Note that the last argument is what *lsp clients would see* rather than
1415        // what users would see. Unescaping happens thereafter.
1416        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        // https://github.com/rust-lang/rust-analyzer/issues/7929
1507
1508        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        // https://github.com/rust-lang/rust-analyzer/issues/21035
1575        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}