src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs RUST 1,799 lines View on github.com → Search inside
1//! Postfix completions, like `Ok(10).ifl$0` => `if let Ok() = Ok(10) { $0 }`.23mod format_like;45use 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};2526use 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};3233pub(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    }4142    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_literal49                }50                DotAccessKind::Method => false,51            },52        ),53        _ => return,54    };55    let expr_ctx = &dot_access.ctx;56    let receiver_accessor = receiver_accessor(dot_receiver);5758    let receiver_text =59        get_receiver_text(&ctx.sema, dot_receiver, receiver_is_ambiguous_float_literal);6061    let cap = match ctx.config.snippet_cap {62        Some(it) => it,63        None => return,64    };6566    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        };7677    let cfg = ctx.config.find_path_config(ctx.is_nightly);7879    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    }9394    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);9798    // 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 compilation100101    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        };109110    if !ctx.config.snippets.is_empty() {111        add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text);112    }113114    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); // fixme117    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);120121    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_ty125            .iterate_assoc_items(ctx.db, |item| {126                if let hir::AssocItem::Function(func) = item127                    && func.name(ctx.db) == hir::sym::new128                    && !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                None136            })137            .is_some();138139        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();142143            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    }151152    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    }215216    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);259260                    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);266267                    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);281282                    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);288289                    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    }319320    if receiver_ty.is_bool() || receiver_ty.is_unknown() {321        postfix_snippet("not", "!expr", format!("!{receiver_text}")).add_to(acc, ctx.db);322    }323324    let block_should_be_wrapped = if let ast::Expr::BlockExpr(block) = dot_receiver {325        block.modifier().is_some() || !block.is_standalone()326    } else {327        true328    };329    {330        let (open_brace, close_brace) =331            if block_should_be_wrapped { ("{ ", " }") } else { ("", "") };332        // FIXME: Why add parentheses333        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);337338        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    }342343    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    }348349    postfix_snippet("return", "return expr", format!("return {receiver_text}{semi}"))350        .add_to(acc, ctx.db);351352    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}357358fn suggest_receiver_name(359    receiver: &ast::Expr,360    n: &str,361    sema: &Semantics<'_, RootDatabase>,362) -> SmolStr {363    let placeholder = |name| format_smolstr!("${{{n}:{name}}}");364365    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    }378379    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}385386fn 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]);402403    // The receiver texts should be interpreted as-is, as they are expected to be404    // normal Rust expressions.405    SnippetEdit::escape_snippet_bits(&mut text);406    return text;407408    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}414415fn receiver_accessor(receiver: &ast::Expr) -> ast::Expr {416    receiver417        .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}428429/// 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 string431///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();436437    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    }443444    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    }450451    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_element458                .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    }472473    (resulting_element, prefix)474}475476fn 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 token484        // mapping.485        never!();486        return None;487    }488    let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());489490    // Wrapping impl Fn in an option ruins lifetime inference for the parameters in a way that491    // can't be annotated for the closure, hence fix it by constructing it without the Option first492    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            item516        }517    }518    Some(build(ctx, cap, delete_range))519}520521fn 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    None546}547548pub(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}565566pub(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}581582#[cfg(test)]583mod tests {584    use expect_test::expect;585586    use crate::{587        CompletionConfig, Snippet,588        tests::{TEST_CONFIG, check, check_edit, check_edit_with_config},589    };590591    #[test]592    fn postfix_completion_works_for_trivial_path_expression() {593        check(594            r#"595fn main() {596    let bar = true;597    bar.$0598}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         *expr607                sn if       if expr {}608                sn let             let609                sn letm        let mut610                sn match match expr {}611                sn not           !expr612                sn ref           &expr613                sn refm      &mut expr614                sn return  return expr615                sn unsafe    unsafe {}616                sn while while expr {}617            "#]],618        );619    }620621    #[test]622    fn postfix_completion_works_for_function_calln() {623        check(624            r#"625fn foo(elt: bool) -> bool {626    !elt627}628629fn 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         *expr641                sn if       if expr {}642                sn match match expr {}643                sn not           !expr644                sn ref           &expr645                sn refm      &mut expr646                sn return  return expr647                sn unsafe    unsafe {}648                sn while while expr {}649            "#]],650        );651    }652653    #[test]654    fn postfix_completion_works_in_if_condition() {655        check(656            r#"657fn foo(cond: bool) {658    if cond.$0659}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         *expr668                sn let             let669                sn not           !expr670                sn ref           &expr671                sn refm      &mut expr672                sn return  return expr673                sn unsafe    unsafe {}674            "#]],675        );676    }677678    #[test]679    fn postfix_type_filtering() {680        check(681            r#"682fn main() {683    let bar: u8 = 12;684    bar.$0685}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         *expr694                sn let             let695                sn letm        let mut696                sn match match expr {}697                sn ref           &expr698                sn refm      &mut expr699                sn return  return expr700                sn unsafe    unsafe {}701            "#]],702        )703    }704705    #[test]706    fn let_middle_block() {707        check_edit(708            "let",709            r#"710fn main() {711    baz.l$0712    res713}714"#,715            r#"716fn main() {717    let $0 = baz;718    res719}720"#,721        );722723        check(724            r#"725fn main() {726    baz.l$0727    res728}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         *expr737                sn if       if expr {}738                sn let             let739                sn letm        let mut740                sn match match expr {}741                sn not           !expr742                sn ref           &expr743                sn refm      &mut expr744                sn return  return expr745                sn unsafe    unsafe {}746                sn while while expr {}747            "#]],748        );749        check(750            r#"751fn main() {752    &baz.l$0753    res754}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         *expr763                sn if       if expr {}764                sn let             let765                sn letm        let mut766                sn match match expr {}767                sn not           !expr768                sn ref           &expr769                sn refm      &mut expr770                sn return  return expr771                sn unsafe    unsafe {}772                sn while while expr {}773            "#]],774        );775776        check_edit(777            "let",778            r#"779fn foo() -> (i32, i32) {780    "a".let$0781    (2, 3).clone()782}783"#,784            r#"785fn foo() -> (i32, i32) {786    let $0 = "a";787    (2, 3).clone()788}789"#,790        );791792        check_edit(793            "let",794            r#"795fn foo() -> (i32, i32) {796    "a".l$0797    (2, 3).clone()798}799"#,800            r#"801fn foo() -> (i32, i32) {802    let $0 = "a";803    (2, 3).clone()804}805"#,806        );807    }808809    #[test]810    fn let_tail_block() {811        check_edit(812            "let",813            r#"814fn main() {815    baz.l$0816}817"#,818            r#"819fn main() {820    let $0 = baz;821}822"#,823        );824825        check(826            r#"827fn main() {828    baz.l$0829}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         *expr838                sn if       if expr {}839                sn let             let840                sn letm        let mut841                sn match match expr {}842                sn not           !expr843                sn ref           &expr844                sn refm      &mut expr845                sn return  return expr846                sn unsafe    unsafe {}847                sn while while expr {}848            "#]],849        );850851        check(852            r#"853fn main() {854    &baz.l$0855}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         *expr864                sn if       if expr {}865                sn let             let866                sn letm        let mut867                sn match match expr {}868                sn not           !expr869                sn ref           &expr870                sn refm      &mut expr871                sn return  return expr872                sn unsafe    unsafe {}873                sn while while expr {}874            "#]],875        );876    }877878    #[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    }894895    #[test]896    fn option_iflet() {897        check_edit(898            "ifl",899            r#"900//- minicore: option901fn main() {902    let bar = Some(true);903    bar.$0904}905"#,906            r#"907fn main() {908    let bar = Some(true);909    if let Some(${1:bar}) = bar {910    $0911}912}913"#,914        );915    }916917    #[test]918    fn option_iflet_cond() {919        check(920            r#"921//- minicore: option922fn main() {923    let bar = Some(true);924    if bar.$0925}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) -> T932                me unwrap_or(…)              fn(self, T) -> T933                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                                *expr939                sn let                            let Some(_)940                sn letm                       let Some(mut _)941                sn ref                                  &expr942                sn refm                             &mut expr943                sn return                         return expr944                sn unsafe                           unsafe {}945            "#]],946        );947        check_edit(948            "let",949            r#"950//- minicore: option951fn main() {952    let bar = Some(true);953    if bar.$0954}955"#,956            r#"957fn main() {958    let bar = Some(true);959    if let Some(${0:bar}) = bar960}961"#,962        );963        check_edit(964            "let",965            r#"966//- minicore: option967fn main() {968    let bar = Some(true);969    if true && bar.$0970}971"#,972            r#"973fn main() {974    let bar = Some(true);975    if true && let Some(${0:bar}) = bar976}977"#,978        );979        check_edit(980            "let",981            r#"982//- minicore: option983fn main() {984    let bar = Some(true);985    if true && true && bar.$0986}987"#,988            r#"989fn main() {990    let bar = Some(true);991    if true && true && let Some(${0:bar}) = bar992}993"#,994        );995    }996997    #[test]998    fn iflet_fallback_cond() {999        check_edit(1000            "let",1001            r#"1002fn main() {1003    let bar = 2;1004    if bar.$01005}1006"#,1007            r#"1008fn main() {1009    let bar = 2;1010    if let $1 = bar1011}1012"#,1013        );1014    }10151016    #[test]1017    fn match_arm_let_block() {1018        check(1019            r#"1020fn main() {1021    match 2 {1022        bar => bar.$01023    }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         *expr1033                sn let             let1034                sn letm        let mut1035                sn match match expr {}1036                sn ref           &expr1037                sn refm      &mut expr1038                sn return  return expr1039                sn unsafe    unsafe {}1040            "#]],1041        );1042        check(1043            r#"1044fn main() {1045    match 2 {1046        bar => &bar.l$01047    }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         *expr1057                sn let             let1058                sn letm        let mut1059                sn match match expr {}1060                sn ref           &expr1061                sn refm      &mut expr1062                sn return  return expr1063                sn unsafe    unsafe {}1064            "#]],1065        );1066        check_edit(1067            "let",1068            r#"1069fn main() {1070    match 2 {1071        bar => bar.$01072    }1073}1074"#,1075            r#"1076fn main() {1077    match 2 {1078        bar => {1079    let $1 = bar;1080    $01081}1082    }1083}1084"#,1085        );1086    }10871088    #[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    $01104};1105}1106"#,1107        );1108    }11091110    #[test]1111    fn option_letelse() {1112        check_edit(1113            "lete",1114            r#"1115//- minicore: option1116fn main() {1117    let bar = Some(true);1118    bar.$01119}1120"#,1121            r#"1122fn main() {1123    let bar = Some(true);1124    let Some(${1:bar}) = bar else {1125    $21126};$01127}1128"#,1129        );11301131        check_edit(1132            "lete",1133            r#"1134//- minicore: option1135fn main() {1136    let bar = Some(true);1137    bar.$01138    other();1139}1140"#,1141            r#"1142fn main() {1143    let bar = Some(true);1144    let Some(${1:bar}) = bar else {1145    $21146};$01147    other();1148}1149"#,1150        );1151    }11521153    #[test]1154    fn result_match() {1155        check_edit(1156            "match",1157            r#"1158//- minicore: result1159fn main() {1160    let bar = Ok(true);1161    bar.$01162}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    }11751176    #[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    }11801181    #[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    }12011202    #[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: option1211fn main() {1212    let bar = &Some(true);1213    bar.$01214}1215"#,1216            r#"1217fn main() {1218    let bar = &Some(true);1219    if let Some(${1:bar}) = bar {1220    $01221}1222}1223"#,1224        )1225    }12261227    #[test]1228    fn postfix_completion_for_nots() {1229        check_edit(1230            "if",1231            r#"1232fn main() {1233    let is_foo = true;1234    !is_foo.$01235}1236"#,1237            r#"1238fn main() {1239    let is_foo = true;1240    if !is_foo {1241    $01242}1243}1244"#,1245        )1246    }12471248    #[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    $11261} else {1262    $01263};1264}1265"#,1266        );12671268        check_edit(1269            "ifl",1270            r#"1271//- minicore: option1272fn 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    $21282} else {1283    $01284};1285}1286"#,1287        );12881289        check_edit(1290            "if",1291            r#"1292fn main() {1293    2 + true.$0;1294}1295"#,1296            r#"1297fn main() {1298    2 + if true {1299    $11300} else {1301    $01302};1303}1304"#,1305        );1306    }13071308    #[test]1309    fn postfix_completion_for_unsafe() {1310        postfix_completion_for_block("unsafe");1311    }13121313    #[test]1314    fn postfix_completion_for_const() {1315        postfix_completion_for_block("const");1316    }13171318    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: iterator1349fn 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        );13571358        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        }13711372        // completion will not be triggered1373        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    }13791380    #[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_CONFIG1395        };13961397        check_edit_with_config(1398            config.clone(),1399            "break",1400            r#"1401//- minicore: try1402fn main() { 42.$0 }1403"#,1404            r#"1405use core::ops::ControlFlow;14061407fn main() { ControlFlow::Break(42) }1408"#,1409        );14101411        // 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 than1415        // what users would see. Unescaping happens thereafter.1416        check_edit_with_config(1417            config.clone(),1418            "break",1419            r#"1420//- minicore: try1421fn main() { '\\'.$0 }1422"#,1423            r#"1424use core::ops::ControlFlow;14251426fn main() { ControlFlow::Break('\\\\') }1427"#,1428        );14291430        check_edit_with_config(1431            config,1432            "break",1433            r#"1434//- minicore: try1435fn main() {1436    match true {1437        true => "${1:placeholder}",1438        false => "\$",1439    }.$01440}1441"#,1442            r#"1443use core::ops::ControlFlow;14441445fn main() {1446    ControlFlow::Break(match true {1447    true => "\${1:placeholder}",1448    false => "\\\$",1449})1450}1451"#,1452        );1453    }14541455    #[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    }15031504    #[test]1505    fn postfix_custom_snippets_completion_for_references() {1506        // https://github.com/rust-lang/rust-analyzer/issues/792915071508        let snippet = Snippet::new(1509            &[],1510            &["ok".into()],1511            &["Ok(${receiver})".into()],1512            "",1513            &[],1514            crate::SnippetScope::Expr,1515        )1516        .unwrap();15171518        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        );15241525        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        );15311532        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        );15381539        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        );15451546        check_edit_with_config(1547            CompletionConfig { snippets: vec![snippet], ..TEST_CONFIG },1548            "ok",1549            r#"1550struct A {1551    a: i32,1552}15531554fn main() {1555    let a = A {a :1};1556    &a.a.$01557}1558            "#,1559            r#"1560struct A {1561    a: i32,1562}15631564fn main() {1565    let a = A {a :1};1566    Ok(&a.a)1567}1568            "#,1569        );1570    }15711572    #[test]1573    fn postfix_custom_snippets_completion_for_reference_expr() {1574        // https://github.com/rust-lang/rust-analyzer/issues/210351575        let snippet = Snippet::new(1576            &[],1577            &["group".into()],1578            &["(${receiver})".into()],1579            "",1580            &[],1581            crate::SnippetScope::Expr,1582        )1583        .unwrap();15841585        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        );15911592        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        );15981599        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        );16051606        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        );16121613        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        );16191620        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    }16291630    #[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    }16411642    #[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    }16601661    #[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    }16791680    #[test]1681    fn inside_macro() {1682        check_edit(1683            "box",1684            r#"1685macro_rules! assert {1686    ( $it:expr $(,)? ) => { $it };1687}16881689fn 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}16981699fn foo() {1700    let a = true;1701    assert!(Box::new(if a == false { true } else { false }));1702}1703        "#,1704        );1705    }17061707    #[test]1708    fn snippet_dedent() {1709        check_edit(1710            "let",1711            r#"1712//- minicore: option1713fn foo(x: Option<i32>, y: Option<i32>) {1714    let _f = || {1715        x1716            .and(y)1717            .map(|it| {1718                it+21719            })1720            .$01721    };1722}1723"#,1724            r#"1725fn foo(x: Option<i32>, y: Option<i32>) {1726    let _f = || {1727        let $0 = x1728.and(y)1729.map(|it| {1730    it+21731});1732    };1733}1734"#,1735        );1736    }17371738    #[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}17481749fn 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}17601761fn main() {1762    let other_thing = OtherThing;1763    let thing: RefCell<OtherThing> = RefCell::new(other_thing$0);1764}1765"#,1766        );17671768        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}17781779fn 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}17911792fn main() {1793    let thing: foo::RefCell<foo::OtherThing> = foo::RefCell::new(foo::OtherThing$0);1794}1795"#,1796        );1797    }1798}

Code quality findings 41

Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let indent_spaces = indent_of_tail_line(&text[TextRange::up_to(range.range.end())]);
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let mut text = stdx::dedent_by(indent_spaces, &text[range.range]);
Use of 'unsafe' bypasses Rust's safety guarantees; keep it minimal in scope and justify it (FFI, a measured optimisation) with a SAFETY comment
warning safety unsafe-block
sn unsafe unsafe {}
Use of 'unsafe' bypasses Rust's safety guarantees; keep it minimal in scope and justify it (FFI, a measured optimisation) with a SAFETY comment
warning safety unsafe-block
sn unsafe unsafe {}
Use of 'unsafe' bypasses Rust's safety guarantees; keep it minimal in scope and justify it (FFI, a measured optimisation) with a SAFETY comment
warning safety unsafe-block
sn unsafe unsafe {}
Use of 'unsafe' bypasses Rust's safety guarantees; keep it minimal in scope and justify it (FFI, a measured optimisation) with a SAFETY comment
warning safety unsafe-block
sn unsafe unsafe {}
Use of 'unsafe' bypasses Rust's safety guarantees; keep it minimal in scope and justify it (FFI, a measured optimisation) with a SAFETY comment
warning safety unsafe-block
sn unsafe unsafe {}
Use of 'unsafe' bypasses Rust's safety guarantees; keep it minimal in scope and justify it (FFI, a measured optimisation) with a SAFETY comment
warning safety unsafe-block
sn unsafe unsafe {}
Use of 'unsafe' bypasses Rust's safety guarantees; keep it minimal in scope and justify it (FFI, a measured optimisation) with a SAFETY comment
warning safety unsafe-block
sn unsafe unsafe {}
Use of 'unsafe' bypasses Rust's safety guarantees; keep it minimal in scope and justify it (FFI, a measured optimisation) with a SAFETY comment
warning safety unsafe-block
sn unsafe unsafe {}
Use of 'unsafe' bypasses Rust's safety guarantees; keep it minimal in scope and justify it (FFI, a measured optimisation) with a SAFETY comment
warning safety unsafe-block
sn unsafe unsafe {}
Use of 'unsafe' bypasses Rust's safety guarantees; keep it minimal in scope and justify it (FFI, a measured optimisation) with a SAFETY comment
warning safety unsafe-block
sn unsafe unsafe {}
Use of 'unsafe' bypasses Rust's safety guarantees; keep it minimal in scope and justify it (FFI, a measured optimisation) with a SAFETY comment
warning safety unsafe-block
sn unsafe unsafe {}
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
.unwrap(),
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
.unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
.unwrap();
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match *kind {
Performance Info: Frequent cloning, especially of Strings, Vecs, or other heap-allocated types inside loops, can be expensive. Consider using references/borrowing where possible.
info performance clone-in-loop
let mut resulting_element = initial_element.clone();
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
Some(match_ast! { match parent {
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbg dbg!(expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbgr dbg!(&expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbg dbg!(expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbgr dbg!(&expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbg dbg!(expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbgr dbg!(&expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbg dbg!(expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbgr dbg!(&expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbg dbg!(expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbgr dbg!(&expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbg dbg!(expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbgr dbg!(&expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbg dbg!(expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbgr dbg!(&expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbg dbg!(expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbgr dbg!(&expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbg dbg!(expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbgr dbg!(&expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbg dbg!(expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbgr dbg!(&expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbg dbg!(expr)
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
sn dbgr dbg!(&expr)

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.

Generated by searchcode.com · powered by code_get_file + code_get_findings