src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs RUST 1,565 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    syntax_helpers::suggest_name::NameGenerator,12    text_edit::TextEdit,13    ty_filter::TryEnum,14};15use itertools::Itertools;16use stdx::never;17use syntax::{18    SmolStr,19    SyntaxKind::{CLOSURE_EXPR, EXPR_STMT, MATCH_ARM, STMT_LIST},20    T, TextRange, TextSize, ToSmolStr,21    ast::{self, AstNode, AstToken},22    format_smolstr, match_ast,23};2425use crate::{26    CompletionItem, CompletionItemKind, CompletionRelevance, Completions, SnippetScope,27    completions::postfix::format_like::add_format_like_completions,28    context::{BreakableKind, CompletionContext, DotAccess, DotAccessKind},29    item::{Builder, CompletionRelevancePostfixMatch},30};3132pub(crate) fn complete_postfix(33    acc: &mut Completions,34    ctx: &CompletionContext<'_>,35    dot_access: &DotAccess<'_>,36) {37    if !ctx.config.enable_postfix_completions {38        return;39    }4041    let (dot_receiver, receiver_ty, receiver_is_ambiguous_float_literal) = match dot_access {42        DotAccess { receiver_ty: Some(ty), receiver: Some(it), kind, .. } => (43            it,44            &ty.original,45            match *kind {46                DotAccessKind::Field { receiver_is_ambiguous_float_literal } => {47                    receiver_is_ambiguous_float_literal48                }49                DotAccessKind::Method => false,50            },51        ),52        _ => return,53    };54    let expr_ctx = &dot_access.ctx;55    let receiver_accessor = receiver_accessor(dot_receiver);5657    let receiver_text =58        get_receiver_text(&ctx.sema, dot_receiver, receiver_is_ambiguous_float_literal);5960    let cap = match ctx.config.snippet_cap {61        Some(it) => it,62        None => return,63    };6465    let postfix_snippet = match build_postfix_snippet_builder(ctx, cap, dot_receiver) {66        Some(it) => it,67        None => return,68    };69    let semi =70        if expr_ctx.in_block_expr && ctx.token.next_token().is_none_or(|it| it.kind() != T![;]) {71            ";"72        } else {73            ""74        };7576    let cfg = ctx.config.find_path_config(ctx.is_nightly);7778    if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop()79        && receiver_ty.impls_trait(ctx.db, drop_trait, &[])80        && let Some(drop_fn) = ctx.famous_defs().core_mem_drop()81        && let Some(path) = ctx.module.find_path(ctx.db, ItemInNs::Values(drop_fn.into()), cfg)82    {83        cov_mark::hit!(postfix_drop_completion);84        let mut item = postfix_snippet(85            "drop",86            "fn drop(&mut self)",87            &format!("{path}($0{receiver_text})", path = path.display(ctx.db, ctx.edition)),88        );89        item.set_documentation(drop_fn.docs(ctx.db));90        item.add_to(acc, ctx.db);91    }9293    postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc, ctx.db);94    postfix_snippet("refm", "&mut expr", &format!("&mut {receiver_text}")).add_to(acc, ctx.db);95    postfix_snippet("deref", "*expr", &format!("*{receiver_text}")).add_to(acc, ctx.db);9697    // The rest of the postfix completions create an expression that moves an argument,98    // so it's better to consider references now to avoid breaking the compilation99100    let (dot_receiver_including_refs, prefix) = include_references(&receiver_accessor);101    let mut receiver_text = receiver_text;102    receiver_text.insert_str(0, &prefix);103    let postfix_snippet =104        match build_postfix_snippet_builder(ctx, cap, &dot_receiver_including_refs) {105            Some(it) => it,106            None => return,107        };108109    if !ctx.config.snippets.is_empty() {110        add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text);111    }112113    postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})"))114        .add_to(acc, ctx.db);115    postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc, ctx.db); // fixme116    postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc, ctx.db);117    postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})"))118        .add_to(acc, ctx.db);119120    let try_enum = TryEnum::from_ty(&ctx.sema, receiver_ty);121    let is_in_cond = is_in_condition(&dot_receiver_including_refs);122    if let Some(parent) = dot_receiver_including_refs.syntax().parent() {123        let placeholder = suggest_receiver_name(dot_receiver, "0", &ctx.sema);124        match &try_enum {125            Some(try_enum) if is_in_cond => match try_enum {126                TryEnum::Result => {127                    postfix_snippet(128                        "let",129                        "let Ok(_)",130                        &format!("let Ok({placeholder}) = {receiver_text}"),131                    )132                    .add_to(acc, ctx.db);133                    postfix_snippet(134                        "letm",135                        "let Ok(mut _)",136                        &format!("let Ok(mut {placeholder}) = {receiver_text}"),137                    )138                    .add_to(acc, ctx.db);139                }140                TryEnum::Option => {141                    postfix_snippet(142                        "let",143                        "let Some(_)",144                        &format!("let Some({placeholder}) = {receiver_text}"),145                    )146                    .add_to(acc, ctx.db);147                    postfix_snippet(148                        "letm",149                        "let Some(mut _)",150                        &format!("let Some(mut {placeholder}) = {receiver_text}"),151                    )152                    .add_to(acc, ctx.db);153                }154            },155            _ if is_in_cond => {156                postfix_snippet("let", "let", &format!("let $1 = {receiver_text}"))157                    .add_to(acc, ctx.db);158            }159            _ if matches!(parent.kind(), STMT_LIST | EXPR_STMT) => {160                postfix_snippet("let", "let", &format!("let $0 = {receiver_text}{semi}"))161                    .add_to(acc, ctx.db);162                postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text}{semi}"))163                    .add_to(acc, ctx.db);164            }165            _ if matches!(parent.kind(), MATCH_ARM | CLOSURE_EXPR) => {166                postfix_snippet(167                    "let",168                    "let",169                    &format!("{{\n    let $1 = {receiver_text};\n    $0\n}}"),170                )171                .add_to(acc, ctx.db);172                postfix_snippet(173                    "letm",174                    "let mut",175                    &format!("{{\n    let mut $1 = {receiver_text};\n    $0\n}}"),176                )177                .add_to(acc, ctx.db);178            }179            _ => (),180        }181    }182183    if !is_in_cond {184        match try_enum {185            Some(try_enum) => match try_enum {186                TryEnum::Result => {187                    postfix_snippet(188                    "match",189                    "match expr {}",190                    &format!("match {receiver_text} {{\n    Ok(${{1:_}}) => {{$2}},\n    Err(${{3:_}}) => {{$0}},\n}}"),191                )192                .add_to(acc, ctx.db);193                }194                TryEnum::Option => {195                    postfix_snippet(196                    "match",197                    "match expr {}",198                    &format!(199                        "match {receiver_text} {{\n    Some(${{1:_}}) => {{$2}},\n    None => {{$0}},\n}}"200                    ),201                )202                .add_to(acc, ctx.db);203                }204            },205            None => {206                postfix_snippet(207                    "match",208                    "match expr {}",209                    &format!("match {receiver_text} {{\n    ${{1:_}} => {{$0}},\n}}"),210                )211                .add_to(acc, ctx.db);212            }213        }214        if let Some(try_enum) = &try_enum {215            let placeholder = suggest_receiver_name(dot_receiver, "1", &ctx.sema);216            match try_enum {217                TryEnum::Result => {218                    postfix_snippet(219                        "ifl",220                        "if let Ok {}",221                        &format!("if let Ok({placeholder}) = {receiver_text} {{\n    $0\n}}"),222                    )223                    .add_to(acc, ctx.db);224225                    postfix_snippet(226                        "lete",227                        "let Ok else {}",228                        &format!(229                            "let Ok({placeholder}) = {receiver_text} else {{\n    $2\n}};\n$0"230                        ),231                    )232                    .add_to(acc, ctx.db);233234                    postfix_snippet(235                        "while",236                        "while let Ok {}",237                        &format!("while let Ok({placeholder}) = {receiver_text} {{\n    $0\n}}"),238                    )239                    .add_to(acc, ctx.db);240                }241                TryEnum::Option => {242                    postfix_snippet(243                        "ifl",244                        "if let Some {}",245                        &format!("if let Some({placeholder}) = {receiver_text} {{\n    $0\n}}"),246                    )247                    .add_to(acc, ctx.db);248249                    postfix_snippet(250                        "lete",251                        "let Some else {}",252                        &format!(253                            "let Some({placeholder}) = {receiver_text} else {{\n    $2\n}};\n$0"254                        ),255                    )256                    .add_to(acc, ctx.db);257258                    postfix_snippet(259                        "while",260                        "while let Some {}",261                        &format!("while let Some({placeholder}) = {receiver_text} {{\n    $0\n}}"),262                    )263                    .add_to(acc, ctx.db);264                }265            }266        } else if receiver_ty.is_bool() || receiver_ty.is_unknown() {267            postfix_snippet("if", "if expr {}", &format!("if {receiver_text} {{\n    $0\n}}"))268                .add_to(acc, ctx.db);269            postfix_snippet(270                "while",271                "while expr {}",272                &format!("while {receiver_text} {{\n    $0\n}}"),273            )274            .add_to(acc, ctx.db);275        } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator()276            && receiver_ty.impls_trait(ctx.db, trait_, &[])277        {278            postfix_snippet(279                "for",280                "for ele in expr {}",281                &format!("for ele in {receiver_text} {{\n    $0\n}}"),282            )283            .add_to(acc, ctx.db);284        }285    }286287    if receiver_ty.is_bool() || receiver_ty.is_unknown() {288        postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db);289    }290291    let block_should_be_wrapped = if let ast::Expr::BlockExpr(block) = dot_receiver {292        block.modifier().is_some() || !block.is_standalone()293    } else {294        true295    };296    {297        let (open_brace, close_brace) =298            if block_should_be_wrapped { ("{ ", " }") } else { ("", "") };299        // FIXME: Why add parentheses300        let (open_paren, close_paren) = if is_in_cond { ("(", ")") } else { ("", "") };301        let unsafe_completion_string =302            format!("{open_paren}unsafe {open_brace}{receiver_text}{close_brace}{close_paren}");303        postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc, ctx.db);304305        let const_completion_string =306            format!("{open_paren}const {open_brace}{receiver_text}{close_brace}{close_paren}");307        postfix_snippet("const", "const {}", &const_completion_string).add_to(acc, ctx.db);308    }309310    if let ast::Expr::Literal(literal) = dot_receiver.clone()311        && let Some(literal_text) = ast::String::cast(literal.token())312    {313        add_format_like_completions(acc, ctx, dot_receiver, cap, &literal_text, semi);314    }315316    postfix_snippet("return", "return expr", &format!("return {receiver_text}{semi}"))317        .add_to(acc, ctx.db);318319    if let Some(BreakableKind::Block | BreakableKind::Loop) = expr_ctx.in_breakable {320        postfix_snippet("break", "break expr", &format!("break {receiver_text}{semi}"))321            .add_to(acc, ctx.db);322    }323}324325fn suggest_receiver_name(326    receiver: &ast::Expr,327    n: &str,328    sema: &Semantics<'_, RootDatabase>,329) -> SmolStr {330    let placeholder = |name| format_smolstr!("${{{n}:{name}}}");331332    match receiver {333        ast::Expr::PathExpr(path) => {334            if let Some(name) = path.path().and_then(|it| it.as_single_name_ref()) {335                return placeholder(name.text().as_str());336            }337        }338        ast::Expr::RefExpr(it) => {339            if let Some(receiver) = it.expr() {340                return suggest_receiver_name(&receiver, n, sema);341            }342        }343        _ => {}344    }345346    let name = NameGenerator::new_with_names([].into_iter()).try_for_variable(receiver, sema);347    match name {348        Some(name) => placeholder(&name),349        None => format_smolstr!("${n}"),350    }351}352353fn get_receiver_text(354    sema: &Semantics<'_, RootDatabase>,355    receiver: &ast::Expr,356    receiver_is_ambiguous_float_literal: bool,357) -> String {358    // Do not just call `receiver.to_string()`, as that will mess up whitespaces inside macros.359    let Some(mut range) = sema.original_range_opt(receiver.syntax()) else {360        return receiver.to_string();361    };362    if receiver_is_ambiguous_float_literal {363        range.range = TextRange::at(range.range.start(), range.range.len() - TextSize::of('.'))364    }365    let file_text = sema.db.file_text(range.file_id.file_id(sema.db));366    let text = file_text.text(sema.db);367    let indent_spaces = indent_of_tail_line(&text[TextRange::up_to(range.range.start())]);368    let mut text = stdx::dedent_by(indent_spaces, &text[range.range]);369370    // The receiver texts should be interpreted as-is, as they are expected to be371    // normal Rust expressions.372    escape_snippet_bits(&mut text);373    return text;374375    fn indent_of_tail_line(text: &str) -> usize {376        let tail_line = text.rsplit_once('\n').map_or(text, |(_, s)| s);377        let trimmed = tail_line.trim_start_matches(' ');378        tail_line.len() - trimmed.len()379    }380}381382/// Escapes `\` and `$` so that they don't get interpreted as snippet-specific constructs.383///384/// Note that we don't need to escape the other characters that can be escaped,385/// because they wouldn't be treated as snippet-specific constructs without '$'.386fn escape_snippet_bits(text: &mut String) {387    stdx::replace(text, '\\', "\\\\");388    stdx::replace(text, '$', "\\$");389}390391fn receiver_accessor(receiver: &ast::Expr) -> ast::Expr {392    receiver393        .syntax()394        .parent()395        .and_then(ast::Expr::cast)396        .filter(|it| {397            matches!(398                it,399                ast::Expr::FieldExpr(_) | ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_)400            )401        })402        .unwrap_or_else(|| receiver.clone())403}404405/// Given an `initial_element`, tries to expand it to include deref(s), not(s), and then references.406/// Returns the expanded expressions, and the added prefix as a string407///408/// For example, if called with the `42` in `&&mut *42`, would return `(&&mut *42, "&&mut *")`.409fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) {410    let mut resulting_element = initial_element.clone();411    let mut prefix = String::new();412413    while let Some(parent) = resulting_element.syntax().parent().and_then(ast::PrefixExpr::cast)414        && parent.op_kind() == Some(ast::UnaryOp::Deref)415    {416        resulting_element = ast::Expr::from(parent);417        prefix.insert(0, '*');418    }419420    while let Some(parent) = resulting_element.syntax().parent().and_then(ast::PrefixExpr::cast)421        && parent.op_kind() == Some(ast::UnaryOp::Not)422    {423        resulting_element = ast::Expr::from(parent);424        prefix.insert(0, '!');425    }426427    while let Some(parent_ref_element) =428        resulting_element.syntax().parent().and_then(ast::RefExpr::cast)429    {430        let last_child_or_token = parent_ref_element.syntax().last_child_or_token();431        prefix.insert_str(432            0,433            parent_ref_element434                .syntax()435                .children_with_tokens()436                .filter(|it| Some(it) != last_child_or_token.as_ref())437                .format("")438                .to_smolstr()439                .as_str(),440        );441        resulting_element = ast::Expr::from(parent_ref_element);442    }443444    (resulting_element, prefix)445}446447fn build_postfix_snippet_builder<'ctx>(448    ctx: &'ctx CompletionContext<'_>,449    cap: SnippetCap,450    receiver: &'ctx ast::Expr,451) -> Option<impl Fn(&str, &str, &str) -> Builder + 'ctx> {452    let receiver_range = ctx.sema.original_range_opt(receiver.syntax())?.range;453    if ctx.source_range().end() < receiver_range.start() {454        // This shouldn't happen, yet it does. I assume this might be due to an incorrect token455        // mapping.456        never!();457        return None;458    }459    let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());460461    // Wrapping impl Fn in an option ruins lifetime inference for the parameters in a way that462    // can't be annotated for the closure, hence fix it by constructing it without the Option first463    fn build<'ctx>(464        ctx: &'ctx CompletionContext<'_>,465        cap: SnippetCap,466        delete_range: TextRange,467    ) -> impl Fn(&str, &str, &str) -> Builder + 'ctx {468        move |label, detail, snippet| {469            let edit = TextEdit::replace(delete_range, snippet.to_owned());470            let mut item = CompletionItem::new(471                CompletionItemKind::Snippet,472                ctx.source_range(),473                label,474                ctx.edition,475            );476            item.detail(detail).snippet_edit(cap, edit);477            let postfix_match = if ctx.original_token.text() == label {478                cov_mark::hit!(postfix_exact_match_is_high_priority);479                Some(CompletionRelevancePostfixMatch::Exact)480            } else {481                cov_mark::hit!(postfix_inexact_match_is_low_priority);482                Some(CompletionRelevancePostfixMatch::NonExact)483            };484            let relevance = CompletionRelevance { postfix_match, ..Default::default() };485            item.set_relevance(relevance);486            item487        }488    }489    Some(build(ctx, cap, delete_range))490}491492fn add_custom_postfix_completions(493    acc: &mut Completions,494    ctx: &CompletionContext<'_>,495    postfix_snippet: impl Fn(&str, &str, &str) -> Builder,496    receiver_text: &str,497) -> Option<()> {498    ImportScope::find_insert_use_container(&ctx.token.parent()?, &ctx.sema)?;499    ctx.config.postfix_snippets().filter(|(_, snip)| snip.scope == SnippetScope::Expr).for_each(500        |(trigger, snippet)| {501            let imports = match snippet.imports(ctx) {502                Some(imports) => imports,503                None => return,504            };505            let body = snippet.postfix_snippet(receiver_text);506            let mut builder =507                postfix_snippet(trigger, snippet.description.as_deref().unwrap_or_default(), &body);508            builder.documentation(Documentation::new_owned(format!("```rust\n{body}\n```")));509            for import in imports.into_iter() {510                builder.add_import(import);511            }512            builder.add_to(acc, ctx.db);513        },514    );515    None516}517518pub(crate) fn is_in_condition(it: &ast::Expr) -> bool {519    it.syntax()520        .parent()521        .and_then(|parent| {522            Some(match_ast! { match parent {523                ast::IfExpr(expr) => expr.condition()? == *it,524                ast::WhileExpr(expr) => expr.condition()? == *it,525                ast::MatchGuard(guard) => guard.condition()? == *it,526                ast::BinExpr(bin_expr) => (bin_expr.op_token()?.kind() == T![&&])527                    .then(|| is_in_condition(&bin_expr.into()))?,528                ast::Expr(expr) => (expr.syntax().text_range().start() == it.syntax().text_range().start())529                    .then(|| is_in_condition(&expr))?,530                _ => return None,531            } })532        })533        .unwrap_or(false)534}535536#[cfg(test)]537mod tests {538    use expect_test::expect;539540    use crate::{541        CompletionConfig, Snippet,542        tests::{TEST_CONFIG, check, check_edit, check_edit_with_config},543    };544545    #[test]546    fn postfix_completion_works_for_trivial_path_expression() {547        check(548            r#"549fn main() {550    let bar = true;551    bar.$0552}553"#,554            expect![[r#"555                sn box  Box::new(expr)556                sn call function(expr)557                sn const      const {}558                sn dbg      dbg!(expr)559                sn dbgr    dbg!(&expr)560                sn deref         *expr561                sn if       if expr {}562                sn let             let563                sn letm        let mut564                sn match match expr {}565                sn not           !expr566                sn ref           &expr567                sn refm      &mut expr568                sn return  return expr569                sn unsafe    unsafe {}570                sn while while expr {}571            "#]],572        );573    }574575    #[test]576    fn postfix_completion_works_for_function_calln() {577        check(578            r#"579fn foo(elt: bool) -> bool {580    !elt581}582583fn main() {584    let bar = true;585    foo(bar.$0)586}587"#,588            expect![[r#"589                sn box  Box::new(expr)590                sn call function(expr)591                sn const      const {}592                sn dbg      dbg!(expr)593                sn dbgr    dbg!(&expr)594                sn deref         *expr595                sn if       if expr {}596                sn match match expr {}597                sn not           !expr598                sn ref           &expr599                sn refm      &mut expr600                sn return  return expr601                sn unsafe    unsafe {}602                sn while while expr {}603            "#]],604        );605    }606607    #[test]608    fn postfix_completion_works_in_if_condition() {609        check(610            r#"611fn foo(cond: bool) {612    if cond.$0613}614"#,615            expect![[r#"616                sn box  Box::new(expr)617                sn call function(expr)618                sn const      const {}619                sn dbg      dbg!(expr)620                sn dbgr    dbg!(&expr)621                sn deref         *expr622                sn let             let623                sn not           !expr624                sn ref           &expr625                sn refm      &mut expr626                sn return  return expr627                sn unsafe    unsafe {}628            "#]],629        );630    }631632    #[test]633    fn postfix_type_filtering() {634        check(635            r#"636fn main() {637    let bar: u8 = 12;638    bar.$0639}640"#,641            expect![[r#"642                sn box  Box::new(expr)643                sn call function(expr)644                sn const      const {}645                sn dbg      dbg!(expr)646                sn dbgr    dbg!(&expr)647                sn deref         *expr648                sn let             let649                sn letm        let mut650                sn match match expr {}651                sn ref           &expr652                sn refm      &mut expr653                sn return  return expr654                sn unsafe    unsafe {}655            "#]],656        )657    }658659    #[test]660    fn let_middle_block() {661        check_edit(662            "let",663            r#"664fn main() {665    baz.l$0666    res667}668"#,669            r#"670fn main() {671    let $0 = baz;672    res673}674"#,675        );676677        check(678            r#"679fn main() {680    baz.l$0681    res682}683"#,684            expect![[r#"685                sn box  Box::new(expr)686                sn call function(expr)687                sn const      const {}688                sn dbg      dbg!(expr)689                sn dbgr    dbg!(&expr)690                sn deref         *expr691                sn if       if expr {}692                sn let             let693                sn letm        let mut694                sn match match expr {}695                sn not           !expr696                sn ref           &expr697                sn refm      &mut expr698                sn return  return expr699                sn unsafe    unsafe {}700                sn while while expr {}701            "#]],702        );703        check(704            r#"705fn main() {706    &baz.l$0707    res708}709"#,710            expect![[r#"711                sn box  Box::new(expr)712                sn call function(expr)713                sn const      const {}714                sn dbg      dbg!(expr)715                sn dbgr    dbg!(&expr)716                sn deref         *expr717                sn if       if expr {}718                sn let             let719                sn letm        let mut720                sn match match expr {}721                sn not           !expr722                sn ref           &expr723                sn refm      &mut expr724                sn return  return expr725                sn unsafe    unsafe {}726                sn while while expr {}727            "#]],728        );729    }730731    #[test]732    fn let_tail_block() {733        check_edit(734            "let",735            r#"736fn main() {737    baz.l$0738}739"#,740            r#"741fn main() {742    let $0 = baz;743}744"#,745        );746747        check(748            r#"749fn main() {750    baz.l$0751}752"#,753            expect![[r#"754                sn box  Box::new(expr)755                sn call function(expr)756                sn const      const {}757                sn dbg      dbg!(expr)758                sn dbgr    dbg!(&expr)759                sn deref         *expr760                sn if       if expr {}761                sn let             let762                sn letm        let mut763                sn match match expr {}764                sn not           !expr765                sn ref           &expr766                sn refm      &mut expr767                sn return  return expr768                sn unsafe    unsafe {}769                sn while while expr {}770            "#]],771        );772773        check(774            r#"775fn main() {776    &baz.l$0777}778"#,779            expect![[r#"780                sn box  Box::new(expr)781                sn call function(expr)782                sn const      const {}783                sn dbg      dbg!(expr)784                sn dbgr    dbg!(&expr)785                sn deref         *expr786                sn if       if expr {}787                sn let             let788                sn letm        let mut789                sn match match expr {}790                sn not           !expr791                sn ref           &expr792                sn refm      &mut expr793                sn return  return expr794                sn unsafe    unsafe {}795                sn while while expr {}796            "#]],797        );798    }799800    #[test]801    fn let_before_semicolon() {802        check_edit(803            "let",804            r#"805fn main() {806    baz.l$0;807}808"#,809            r#"810fn main() {811    let $0 = baz;812}813"#,814        );815    }816817    #[test]818    fn option_iflet() {819        check_edit(820            "ifl",821            r#"822//- minicore: option823fn main() {824    let bar = Some(true);825    bar.$0826}827"#,828            r#"829fn main() {830    let bar = Some(true);831    if let Some(${1:bar}) = bar {832    $0833}834}835"#,836        );837    }838839    #[test]840    fn option_iflet_cond() {841        check(842            r#"843//- minicore: option844fn main() {845    let bar = Some(true);846    if bar.$0847}848"#,849            expect![[r#"850                me and(…)    fn(self, Option<U>) -> Option<U>851                me as_ref()     const fn(&self) -> Option<&T>852                me ok_or(…) const fn(self, E) -> Result<T, E>853                me unwrap()               const fn(self) -> T854                me unwrap_or(…)              fn(self, T) -> T855                sn box                         Box::new(expr)856                sn call                        function(expr)857                sn const                             const {}858                sn dbg                             dbg!(expr)859                sn dbgr                           dbg!(&expr)860                sn deref                                *expr861                sn let                            let Some(_)862                sn letm                       let Some(mut _)863                sn ref                                  &expr864                sn refm                             &mut expr865                sn return                         return expr866                sn unsafe                           unsafe {}867            "#]],868        );869        check_edit(870            "let",871            r#"872//- minicore: option873fn main() {874    let bar = Some(true);875    if bar.$0876}877"#,878            r#"879fn main() {880    let bar = Some(true);881    if let Some(${0:bar}) = bar882}883"#,884        );885        check_edit(886            "let",887            r#"888//- minicore: option889fn main() {890    let bar = Some(true);891    if true && bar.$0892}893"#,894            r#"895fn main() {896    let bar = Some(true);897    if true && let Some(${0:bar}) = bar898}899"#,900        );901        check_edit(902            "let",903            r#"904//- minicore: option905fn main() {906    let bar = Some(true);907    if true && true && bar.$0908}909"#,910            r#"911fn main() {912    let bar = Some(true);913    if true && true && let Some(${0:bar}) = bar914}915"#,916        );917    }918919    #[test]920    fn iflet_fallback_cond() {921        check_edit(922            "let",923            r#"924fn main() {925    let bar = 2;926    if bar.$0927}928"#,929            r#"930fn main() {931    let bar = 2;932    if let $1 = bar933}934"#,935        );936    }937938    #[test]939    fn match_arm_let_block() {940        check(941            r#"942fn main() {943    match 2 {944        bar => bar.$0945    }946}947"#,948            expect![[r#"949                sn box  Box::new(expr)950                sn call function(expr)951                sn const      const {}952                sn dbg      dbg!(expr)953                sn dbgr    dbg!(&expr)954                sn deref         *expr955                sn let             let956                sn letm        let mut957                sn match match expr {}958                sn ref           &expr959                sn refm      &mut expr960                sn return  return expr961                sn unsafe    unsafe {}962            "#]],963        );964        check(965            r#"966fn main() {967    match 2 {968        bar => &bar.l$0969    }970}971"#,972            expect![[r#"973                sn box  Box::new(expr)974                sn call function(expr)975                sn const      const {}976                sn dbg      dbg!(expr)977                sn dbgr    dbg!(&expr)978                sn deref         *expr979                sn let             let980                sn letm        let mut981                sn match match expr {}982                sn ref           &expr983                sn refm      &mut expr984                sn return  return expr985                sn unsafe    unsafe {}986            "#]],987        );988        check_edit(989            "let",990            r#"991fn main() {992    match 2 {993        bar => bar.$0994    }995}996"#,997            r#"998fn main() {999    match 2 {1000        bar => {1001    let $1 = bar;1002    $01003}1004    }1005}1006"#,1007        );1008    }10091010    #[test]1011    fn closure_let_block() {1012        check_edit(1013            "let",1014            r#"1015fn main() {1016    let bar = 2;1017    let f = || bar.$0;1018}1019"#,1020            r#"1021fn main() {1022    let bar = 2;1023    let f = || {1024    let $1 = bar;1025    $01026};1027}1028"#,1029        );1030    }10311032    #[test]1033    fn option_letelse() {1034        check_edit(1035            "lete",1036            r#"1037//- minicore: option1038fn main() {1039    let bar = Some(true);1040    bar.$01041}1042"#,1043            r#"1044fn main() {1045    let bar = Some(true);1046    let Some(${1:bar}) = bar else {1047    $21048};1049$01050}1051"#,1052        );1053    }10541055    #[test]1056    fn result_match() {1057        check_edit(1058            "match",1059            r#"1060//- minicore: result1061fn main() {1062    let bar = Ok(true);1063    bar.$01064}1065"#,1066            r#"1067fn main() {1068    let bar = Ok(true);1069    match bar {1070    Ok(${1:_}) => {$2},1071    Err(${3:_}) => {$0},1072}1073}1074"#,1075        );1076    }10771078    #[test]1079    fn postfix_completion_works_for_ambiguous_float_literal() {1080        check_edit("refm", r#"fn main() { 42.$0 }"#, r#"fn main() { &mut 42 }"#)1081    }10821083    #[test]1084    fn works_in_simple_macro() {1085        check_edit(1086            "dbg",1087            r#"1088macro_rules! m { ($e:expr) => { $e } }1089fn main() {1090    let bar: u8 = 12;1091    m!(bar.d$0)1092}1093"#,1094            r#"1095macro_rules! m { ($e:expr) => { $e } }1096fn main() {1097    let bar: u8 = 12;1098    m!(dbg!(bar))1099}1100"#,1101        );1102    }11031104    #[test]1105    fn postfix_completion_for_references() {1106        check_edit("dbg", r#"fn main() { &&42.$0 }"#, r#"fn main() { dbg!(&&42) }"#);1107        check_edit("dbg", r#"fn main() { &&*"hello".$0 }"#, r#"fn main() { dbg!(&&*"hello") }"#);1108        check_edit("refm", r#"fn main() { &&42.$0 }"#, r#"fn main() { &&&mut 42 }"#);1109        check_edit(1110            "ifl",1111            r#"1112//- minicore: option1113fn main() {1114    let bar = &Some(true);1115    bar.$01116}1117"#,1118            r#"1119fn main() {1120    let bar = &Some(true);1121    if let Some(${1:bar}) = bar {1122    $01123}1124}1125"#,1126        )1127    }11281129    #[test]1130    fn postfix_completion_for_nots() {1131        check_edit(1132            "if",1133            r#"1134fn main() {1135    let is_foo = true;1136    !is_foo.$01137}1138"#,1139            r#"1140fn main() {1141    let is_foo = true;1142    if !is_foo {1143    $01144}1145}1146"#,1147        )1148    }11491150    #[test]1151    fn postfix_completion_for_unsafe() {1152        postfix_completion_for_block("unsafe");1153    }11541155    #[test]1156    fn postfix_completion_for_const() {1157        postfix_completion_for_block("const");1158    }11591160    fn postfix_completion_for_block(kind: &str) {1161        check_edit(kind, r#"fn main() { foo.$0 }"#, &format!("fn main() {{ {kind} {{ foo }} }}"));1162        check_edit(1163            kind,1164            r#"fn main() { { foo }.$0 }"#,1165            &format!("fn main() {{ {kind} {{ foo }} }}"),1166        );1167        check_edit(1168            kind,1169            r#"fn main() { if x { foo }.$0 }"#,1170            &format!("fn main() {{ {kind} {{ if x {{ foo }} }} }}"),1171        );1172        check_edit(1173            kind,1174            r#"fn main() { loop { foo }.$0 }"#,1175            &format!("fn main() {{ {kind} {{ loop {{ foo }} }} }}"),1176        );1177        check_edit(1178            kind,1179            r#"fn main() { if true {}.$0 }"#,1180            &format!("fn main() {{ {kind} {{ if true {{}} }} }}"),1181        );1182        check_edit(1183            kind,1184            r#"fn main() { while true {}.$0 }"#,1185            &format!("fn main() {{ {kind} {{ while true {{}} }} }}"),1186        );1187        check_edit(1188            kind,1189            r#"fn main() { for i in 0..10 {}.$0 }"#,1190            &format!("fn main() {{ {kind} {{ for i in 0..10 {{}} }} }}"),1191        );1192        check_edit(1193            kind,1194            r#"fn main() { let x = if true {1} else {2}.$0 }"#,1195            &format!("fn main() {{ let x = {kind} {{ if true {{1}} else {{2}} }} }}"),1196        );11971198        if kind == "const" {1199            check_edit(1200                kind,1201                r#"fn main() { unsafe {1}.$0 }"#,1202                &format!("fn main() {{ {kind} {{ unsafe {{1}} }} }}"),1203            );1204        } else {1205            check_edit(1206                kind,1207                r#"fn main() { const {1}.$0 }"#,1208                &format!("fn main() {{ {kind} {{ const {{1}} }} }}"),1209            );1210        }12111212        // completion will not be triggered1213        check_edit(1214            kind,1215            r#"fn main() { let x = true else {panic!()}.$0}"#,1216            &format!("fn main() {{ let x = true else {{panic!()}}.{kind} $0}}"),1217        );1218    }12191220    #[test]1221    fn custom_postfix_completion() {1222        let config = CompletionConfig {1223            snippets: vec![1224                Snippet::new(1225                    &[],1226                    &["break".into()],1227                    &["ControlFlow::Break(${receiver})".into()],1228                    "",1229                    &["core::ops::ControlFlow".into()],1230                    crate::SnippetScope::Expr,1231                )1232                .unwrap(),1233            ],1234            ..TEST_CONFIG1235        };12361237        check_edit_with_config(1238            config.clone(),1239            "break",1240            r#"1241//- minicore: try1242fn main() { 42.$0 }1243"#,1244            r#"1245use core::ops::ControlFlow;12461247fn main() { ControlFlow::Break(42) }1248"#,1249        );12501251        // The receiver texts should be escaped, see comments in `get_receiver_text()`1252        // for detail.1253        //1254        // Note that the last argument is what *lsp clients would see* rather than1255        // what users would see. Unescaping happens thereafter.1256        check_edit_with_config(1257            config.clone(),1258            "break",1259            r#"1260//- minicore: try1261fn main() { '\\'.$0 }1262"#,1263            r#"1264use core::ops::ControlFlow;12651266fn main() { ControlFlow::Break('\\\\') }1267"#,1268        );12691270        check_edit_with_config(1271            config,1272            "break",1273            r#"1274//- minicore: try1275fn main() {1276    match true {1277        true => "${1:placeholder}",1278        false => "\$",1279    }.$01280}1281"#,1282            r#"1283use core::ops::ControlFlow;12841285fn main() {1286    ControlFlow::Break(match true {1287    true => "\${1:placeholder}",1288    false => "\\\$",1289})1290}1291"#,1292        );1293    }12941295    #[test]1296    fn postfix_completion_for_format_like_strings() {1297        check_edit(1298            "format",1299            r#"fn main() { "{some_var:?}".$0 }"#,1300            r#"fn main() { format!("{some_var:?}") }"#,1301        );1302        check_edit(1303            "panic",1304            r#"fn main() { "Panic with {a}".$0 }"#,1305            r#"fn main() { panic!("Panic with {a}"); }"#,1306        );1307        check_edit(1308            "println",1309            r#"fn main() { "{ 2+2 } { SomeStruct { val: 1, other: 32 } :?}".$0 }"#,1310            r#"fn main() { println!("{} {:?}", 2+2, SomeStruct { val: 1, other: 32 }); }"#,1311        );1312        check_edit(1313            "loge",1314            r#"fn main() { "{2+2}".$0 }"#,1315            r#"fn main() { log::error!("{}", 2+2); }"#,1316        );1317        check_edit(1318            "logt",1319            r#"fn main() { "{2+2}".$0 }"#,1320            r#"fn main() { log::trace!("{}", 2+2); }"#,1321        );1322        check_edit(1323            "logd",1324            r#"fn main() { "{2+2}".$0 }"#,1325            r#"fn main() { log::debug!("{}", 2+2); }"#,1326        );1327        check_edit(1328            "logi",1329            r#"fn main() { "{2+2}".$0 }"#,1330            r#"fn main() { log::info!("{}", 2+2); }"#,1331        );1332        check_edit(1333            "logw",1334            r#"fn main() { "{2+2}".$0 }"#,1335            r#"fn main() { log::warn!("{}", 2+2); }"#,1336        );1337        check_edit(1338            "loge",1339            r#"fn main() { "{2+2}".$0 }"#,1340            r#"fn main() { log::error!("{}", 2+2); }"#,1341        );1342    }13431344    #[test]1345    fn postfix_custom_snippets_completion_for_references() {1346        // https://github.com/rust-lang/rust-analyzer/issues/792913471348        let snippet = Snippet::new(1349            &[],1350            &["ok".into()],1351            &["Ok(${receiver})".into()],1352            "",1353            &[],1354            crate::SnippetScope::Expr,1355        )1356        .unwrap();13571358        check_edit_with_config(1359            CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },1360            "ok",1361            r#"fn main() { &&42.o$0 }"#,1362            r#"fn main() { Ok(&&42) }"#,1363        );13641365        check_edit_with_config(1366            CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },1367            "ok",1368            r#"fn main() { &&42.$0 }"#,1369            r#"fn main() { Ok(&&42) }"#,1370        );13711372        check_edit_with_config(1373            CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },1374            "ok",1375            r#"fn main() { &raw mut 42.$0 }"#,1376            r#"fn main() { Ok(&raw mut 42) }"#,1377        );13781379        check_edit_with_config(1380            CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },1381            "ok",1382            r#"fn main() { &raw const 42.$0 }"#,1383            r#"fn main() { Ok(&raw const 42) }"#,1384        );13851386        check_edit_with_config(1387            CompletionConfig { snippets: vec![snippet], ..TEST_CONFIG },1388            "ok",1389            r#"1390struct A {1391    a: i32,1392}13931394fn main() {1395    let a = A {a :1};1396    &a.a.$01397}1398            "#,1399            r#"1400struct A {1401    a: i32,1402}14031404fn main() {1405    let a = A {a :1};1406    Ok(&a.a)1407}1408            "#,1409        );1410    }14111412    #[test]1413    fn postfix_custom_snippets_completion_for_reference_expr() {1414        // https://github.com/rust-lang/rust-analyzer/issues/210351415        let snippet = Snippet::new(1416            &[],1417            &["group".into()],1418            &["(${receiver})".into()],1419            "",1420            &[],1421            crate::SnippetScope::Expr,1422        )1423        .unwrap();14241425        check_edit_with_config(1426            CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },1427            "group",1428            r#"fn main() { &[1, 2, 3].g$0 }"#,1429            r#"fn main() { (&[1, 2, 3]) }"#,1430        );14311432        check_edit_with_config(1433            CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },1434            "group",1435            r#"fn main() { &&foo(a, b, 1+1).$0 }"#,1436            r#"fn main() { (&&foo(a, b, 1+1)) }"#,1437        );14381439        check_edit_with_config(1440            CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },1441            "group",1442            r#"fn main() { &mut Foo { a: 1, b: 2, c: 3 }.$0 }"#,1443            r#"fn main() { (&mut Foo { a: 1, b: 2, c: 3 }) }"#,1444        );14451446        check_edit_with_config(1447            CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },1448            "group",1449            r#"fn main() { &raw mut Foo::new().$0 }"#,1450            r#"fn main() { (&raw mut Foo::new()) }"#,1451        );14521453        check_edit_with_config(1454            CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },1455            "group",1456            r#"fn main() { &raw const Foo::bar::SOME_CONST.$0 }"#,1457            r#"fn main() { (&raw const Foo::bar::SOME_CONST) }"#,1458        );1459    }14601461    #[test]1462    fn no_postfix_completions_in_if_block_that_has_an_else() {1463        check(1464            r#"1465fn test() {1466    if true {}.$0 else {}1467}1468"#,1469            expect![[r#""#]],1470        );1471    }14721473    #[test]1474    fn mut_ref_consuming() {1475        check_edit(1476            "call",1477            r#"1478fn main() {1479    let mut x = &mut 2;1480    &mut x.$0;1481}1482"#,1483            r#"1484fn main() {1485    let mut x = &mut 2;1486    ${1}(&mut x);1487}1488"#,1489        );1490    }14911492    #[test]1493    fn deref_consuming() {1494        check_edit(1495            "call",1496            r#"1497fn main() {1498    let mut x = &mut 2;1499    &mut *x.$0;1500}1501"#,1502            r#"1503fn main() {1504    let mut x = &mut 2;1505    ${1}(&mut *x);1506}1507"#,1508        );1509    }15101511    #[test]1512    fn inside_macro() {1513        check_edit(1514            "box",1515            r#"1516macro_rules! assert {1517    ( $it:expr $(,)? ) => { $it };1518}15191520fn foo() {1521    let a = true;1522    assert!(if a == false { true } else { false }.$0);1523}1524        "#,1525            r#"1526macro_rules! assert {1527    ( $it:expr $(,)? ) => { $it };1528}15291530fn foo() {1531    let a = true;1532    assert!(Box::new(if a == false { true } else { false }));1533}1534        "#,1535        );1536    }15371538    #[test]1539    fn snippet_dedent() {1540        check_edit(1541            "let",1542            r#"1543//- minicore: option1544fn foo(x: Option<i32>, y: Option<i32>) {1545    let _f = || {1546        x1547            .and(y)1548            .map(|it| it+2)1549            .$01550    };1551}1552"#,1553            r#"1554fn foo(x: Option<i32>, y: Option<i32>) {1555    let _f = || {1556        let $0 = x1557    .and(y)1558    .map(|it| it+2);1559    };1560}1561"#,1562        );1563    }1564}

Code quality findings 48

Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
format!("{open_paren}unsafe {open_brace}{receiver_text}{close_brace}{close_paren}");
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc, ctx.db);
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
sn unsafe unsafe {}
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
sn unsafe unsafe {}
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
sn unsafe unsafe {}
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
sn unsafe unsafe {}
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
sn unsafe unsafe {}
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
sn unsafe unsafe {}
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
sn unsafe unsafe {}
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
sn unsafe unsafe {}
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
sn unsafe unsafe {}
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
sn unsafe unsafe {}
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
sn unsafe unsafe {}
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
r#"fn main() { unsafe {1}.$0 }"#,
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
&format!("fn main() {{ {kind} {{ unsafe {{1}} }} }}"),
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.start())]);
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]);
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 {
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc, ctx.db); // fixme
Info: `dbg!` macro is intended for temporary debugging. Remove before committing production code. Use proper logging instead.
info maintainability dbg-macro
postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc, ctx.db);
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)
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info maintainability println-macro
r#"fn main() { println!("{} {:?}", 2+2, SomeStruct { val: 1, other: 32 }); }"#,

Get this view in your editor

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