src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs RUST 827 lines View on github.com → Search inside
1//! Implementation of "param name" inlay hints:2//! ```no_run3//! fn max(x: i32, y: i32) -> i32 { x + y }4//! _ = max(/*x*/4, /*y*/4);5//! ```67use std::iter::zip;89use either::Either;10use hir::{EditionedFileId, Semantics, name};11use ide_db::{RootDatabase, famous_defs::FamousDefs};1213use stdx::to_lower_snake_case;14use syntax::T;15use syntax::ast::{self, AstNode, HasArgList, HasName, UnaryOp};1617use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};1819pub(super) fn hints(20    acc: &mut Vec<InlayHint>,21    FamousDefs(sema, krate): &FamousDefs<'_, '_>,22    config: &InlayHintsConfig<'_>,23    file_id: EditionedFileId,24    expr: ast::Expr,25) -> Option<()> {26    if !config.parameter_hints {27        return None;28    }2930    let (callable, arg_list) = get_callable(sema, &expr)?;31    let unary_function = callable.n_params() == 1;32    let function_name = match callable.kind() {33        hir::CallableKind::Function(function) => Some(function.name(sema.db)),34        _ => None,35    };36    let function_name = function_name.as_ref().map(|it| it.as_str());37    let hints = callable38        .params()39        .into_iter()40        .zip(arg_list.args_maybe_empty())41        .filter_map(|(p, arg)| {42            let arg = arg?;43            // Only annotate hints for expressions that exist in the original file44            let range = sema.original_range_opt(arg.syntax())?;45            if range.file_id != file_id {46                return None;47            }48            let param_name = p.name(sema.db)?;49            Some((p, param_name, arg, range))50        })51        .filter(|(_, param_name, arg, _)| {52            !should_hide_param_name_hint(53                sema,54                unary_function,55                function_name,56                param_name.as_str(),57                arg,58            )59        })60        .map(|(param, param_name, _, hir::FileRange { range, .. })| {61            let colon = if config.render_colons { ":" } else { "" };62            let label = InlayHintLabel::simple(63                format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))),64                None,65                config.lazy_location_opt(|| {66                    let source = sema.source(param)?;67                    let name_syntax = match source.value.as_ref() {68                        Either::Left(pat) => pat.name(),69                        Either::Right(param) => match param.pat()? {70                            ast::Pat::IdentPat(it) => it.name(),71                            _ => None,72                        },73                    }?;74                    sema.original_range_opt(name_syntax.syntax()).map(|frange| ide_db::FileRange {75                        file_id: frange.file_id.file_id(sema.db),76                        range: frange.range,77                    })78                }),79            );80            InlayHint {81                range,82                kind: InlayKind::Parameter,83                label,84                text_edit: None,85                position: InlayHintPosition::Before,86                pad_left: false,87                pad_right: true,88                resolve_parent: Some(expr.syntax().text_range()),89            }90        });9192    acc.extend(hints);9394    // Show hint for the next expected (missing) argument if enabled95    if config.parameter_hints_for_missing_arguments {96        let provided_args_count = arg_list.args().count();97        let params = callable.params();98        let total_params = params.len();99100        if provided_args_count < total_params101            && let Some(next_param) = params.get(provided_args_count)102            && let Some(param_name) = next_param.name(sema.db)103        {104            // Apply heuristics to hide obvious parameter hints105            if should_hide_missing_param_hint(unary_function, function_name, param_name.as_str()) {106                return Some(());107            }108109            // Determine the position for the hint110            if let Some(hint_range) = missing_arg_hint_position(&arg_list) {111                let colon = if config.render_colons { ":" } else { "" };112                let label = InlayHintLabel::simple(113                    format!("{}{}", param_name.display(sema.db, krate.edition(sema.db)), colon),114                    None,115                    config.lazy_location_opt(|| {116                        let source = sema.source(next_param.clone())?;117                        let name_syntax = match source.value.as_ref() {118                            Either::Left(pat) => pat.name(),119                            Either::Right(param) => match param.pat()? {120                                ast::Pat::IdentPat(it) => it.name(),121                                _ => None,122                            },123                        }?;124                        sema.original_range_opt(name_syntax.syntax()).map(|frange| {125                            ide_db::FileRange {126                                file_id: frange.file_id.file_id(sema.db),127                                range: frange.range,128                            }129                        })130                    }),131                );132                acc.push(InlayHint {133                    range: hint_range,134                    kind: InlayKind::Parameter,135                    label,136                    text_edit: None,137                    position: InlayHintPosition::Before,138                    pad_left: true,139                    pad_right: false,140                    resolve_parent: Some(expr.syntax().text_range()),141                });142            }143        }144    }145146    Some(())147}148149/// Determines the position where the hint for a missing argument should be placed.150/// Returns the range of the token where the hint should appear.151fn missing_arg_hint_position(arg_list: &ast::ArgList) -> Option<syntax::TextRange> {152    // Always place the hint on the closing paren, so it appears before `)`.153    // This way `foo()` becomes `foo(a)` visually with the hint.154    arg_list155        .syntax()156        .children_with_tokens()157        .filter_map(|it| it.into_token())158        .find(|t| t.kind() == T![')'])159        .map(|t| t.text_range())160}161162fn get_callable<'db>(163    sema: &Semantics<'db, RootDatabase>,164    expr: &ast::Expr,165) -> Option<(hir::Callable<'db>, ast::ArgList)> {166    match expr {167        ast::Expr::CallExpr(expr) => {168            let descended = sema.descend_node_into_attributes(expr.clone()).pop();169            let expr = descended.as_ref().unwrap_or(expr);170            sema.type_of_expr(&expr.expr()?)?.original.as_callable(sema.db).zip(expr.arg_list())171        }172        ast::Expr::MethodCallExpr(expr) => {173            let descended = sema.descend_node_into_attributes(expr.clone()).pop();174            let expr = descended.as_ref().unwrap_or(expr);175            sema.resolve_method_call_as_callable(expr).zip(expr.arg_list())176        }177        _ => None,178    }179}180181const INSIGNIFICANT_METHOD_NAMES: &[&str] = &["clone", "as_ref", "into"];182const INSIGNIFICANT_PARAMETER_NAMES: &[&str] =183    &["predicate", "value", "pat", "rhs", "other", "msg", "op"];184185fn should_hide_param_name_hint(186    sema: &Semantics<'_, RootDatabase>,187    unary_function: bool,188    function_name: Option<&str>,189    param_name: &str,190    argument: &ast::Expr,191) -> bool {192    // These are to be tested in the `parameter_hint_heuristics` test193    // hide when:194    // - the parameter name is a suffix of the function's name195    // - the argument is a qualified constructing or call expression where the qualifier is an ADT196    // - exact argument<->parameter match(ignoring leading and trailing underscore) or197    //   parameter is a prefix/suffix of argument with _ splitting it off198    // - param starts with `ra_fixture`199    // - param is a well known name in a unary function200    // - param is generated name201202    let param_name = param_name.trim_matches('_');203    if param_name.is_empty() {204        return true;205    }206207    if param_name.starts_with("ra_fixture") || name::is_generated(param_name) {208        return true;209    }210211    if unary_function {212        if let Some(function_name) = function_name213            && is_param_name_suffix_of_fn_name(param_name, function_name)214        {215            return true;216        }217        if is_obvious_param(param_name) {218            return true;219        }220    }221222    is_argument_expr_similar_to_param_name(sema, argument, param_name)223}224225/// Determines whether to hide the parameter hint for a missing argument.226/// This is a simplified version of `should_hide_param_name_hint` that doesn't227/// require an actual argument expression.228fn should_hide_missing_param_hint(229    unary_function: bool,230    function_name: Option<&str>,231    param_name: &str,232) -> bool {233    let param_name = param_name.trim_matches('_');234    if param_name.is_empty() {235        return true;236    }237238    if param_name.starts_with("ra_fixture") {239        return true;240    }241242    if unary_function {243        if let Some(function_name) = function_name244            && is_param_name_suffix_of_fn_name(param_name, function_name)245        {246            return true;247        }248        if is_obvious_param(param_name) {249            return true;250        }251    }252253    false254}255256/// Hide the parameter name of a unary function if it is a `_` - prefixed suffix of the function's name, or equal.257///258/// `fn strip_suffix(suffix)` will be hidden.259/// `fn stripsuffix(suffix)` will not be hidden.260fn is_param_name_suffix_of_fn_name(param_name: &str, fn_name: &str) -> bool {261    fn_name == param_name262        || fn_name263            .len()264            .checked_sub(param_name.len())265            .and_then(|at| fn_name.is_char_boundary(at).then(|| fn_name.split_at(at)))266            .is_some_and(|(prefix, suffix)| {267                suffix.eq_ignore_ascii_case(param_name) && prefix.ends_with('_')268            })269}270271fn is_argument_expr_similar_to_param_name(272    sema: &Semantics<'_, RootDatabase>,273    argument: &ast::Expr,274    param_name: &str,275) -> bool {276    match get_segment_representation(argument) {277        Some(Either::Left(argument)) => is_argument_similar_to_param_name(&argument, param_name),278        Some(Either::Right(path)) => {279            path.segment()280                .and_then(|it| it.name_ref())281                .is_some_and(|name_ref| name_ref.text().eq_ignore_ascii_case(param_name))282                || is_adt_constructor_similar_to_param_name(sema, &path, param_name)283        }284        None => false,285    }286}287288/// Check whether param_name and argument are the same or289/// whether param_name is a prefix/suffix of argument(split at `_`).290pub(super) fn is_argument_similar_to_param_name(291    argument: &[ast::NameRef],292    param_name: &str,293) -> bool {294    debug_assert!(!argument.is_empty());295    debug_assert!(!param_name.is_empty());296    let param_name = param_name.split('_');297    let argument = argument.iter().flat_map(|it| it.text_non_mutable().split('_'));298299    let prefix_match = zip(argument.clone(), param_name.clone())300        .all(|(arg, param)| arg.eq_ignore_ascii_case(param));301    let postfix_match = || {302        zip(argument.rev(), param_name.rev()).all(|(arg, param)| arg.eq_ignore_ascii_case(param))303    };304    prefix_match || postfix_match()305}306307pub(super) fn get_segment_representation(308    expr: &ast::Expr,309) -> Option<Either<Vec<ast::NameRef>, ast::Path>> {310    match expr {311        ast::Expr::MethodCallExpr(method_call_expr) => {312            let receiver =313                method_call_expr.receiver().and_then(|expr| get_segment_representation(&expr));314            let name_ref = method_call_expr.name_ref()?;315            if INSIGNIFICANT_METHOD_NAMES.contains(&name_ref.text().as_str()) {316                return receiver;317            }318            Some(Either::Left(match receiver {319                Some(Either::Left(mut left)) => {320                    left.push(name_ref);321                    left322                }323                Some(Either::Right(_)) | None => vec![name_ref],324            }))325        }326        ast::Expr::FieldExpr(field_expr) => {327            let expr = field_expr.expr().and_then(|expr| get_segment_representation(&expr));328            let name_ref = field_expr.name_ref()?;329            let res = match expr {330                Some(Either::Left(mut left)) => {331                    left.push(name_ref);332                    left333                }334                Some(Either::Right(_)) | None => vec![name_ref],335            };336            Some(Either::Left(res))337        }338        // paths339        ast::Expr::MacroExpr(macro_expr) => macro_expr.macro_call()?.path().map(Either::Right),340        ast::Expr::RecordExpr(record_expr) => record_expr.path().map(Either::Right),341        ast::Expr::PathExpr(path_expr) => {342            let path = path_expr.path()?;343            // single segment paths are likely locals344            Some(match path.as_single_name_ref() {345                None => Either::Right(path),346                Some(name_ref) => Either::Left(vec![name_ref]),347            })348        }349        ast::Expr::PrefixExpr(prefix_expr) if prefix_expr.op_kind() == Some(UnaryOp::Not) => None,350        // recurse351        ast::Expr::PrefixExpr(prefix_expr) => get_segment_representation(&prefix_expr.expr()?),352        ast::Expr::RefExpr(ref_expr) => get_segment_representation(&ref_expr.expr()?),353        ast::Expr::CastExpr(cast_expr) => get_segment_representation(&cast_expr.expr()?),354        ast::Expr::CallExpr(call_expr) => get_segment_representation(&call_expr.expr()?),355        ast::Expr::AwaitExpr(await_expr) => get_segment_representation(&await_expr.expr()?),356        ast::Expr::IndexExpr(index_expr) => get_segment_representation(&index_expr.base()?),357        ast::Expr::ParenExpr(paren_expr) => get_segment_representation(&paren_expr.expr()?),358        ast::Expr::TryExpr(try_expr) => get_segment_representation(&try_expr.expr()?),359        // ast::Expr::ClosureExpr(closure_expr) => todo!(),360        _ => None,361    }362}363364fn is_obvious_param(param_name: &str) -> bool {365    // avoid displaying hints for common functions like map, filter, etc.366    // or other obvious words used in std367    param_name.len() == 1 || INSIGNIFICANT_PARAMETER_NAMES.contains(&param_name)368}369370fn is_adt_constructor_similar_to_param_name(371    sema: &Semantics<'_, RootDatabase>,372    path: &ast::Path,373    param_name: &str,374) -> bool {375    (|| match sema.resolve_path(path)? {376        hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => {377            Some(to_lower_snake_case(&path.segment()?.name_ref()?.text()) == param_name)378        }379        hir::PathResolution::Def(hir::ModuleDef::Function(_) | hir::ModuleDef::EnumVariant(_)) => {380            if to_lower_snake_case(&path.segment()?.name_ref()?.text()) == param_name {381                return Some(true);382            }383            let qual = path.qualifier()?;384            match sema.resolve_path(&qual)? {385                hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => {386                    Some(to_lower_snake_case(&qual.segment()?.name_ref()?.text()) == param_name)387                }388                _ => None,389            }390        }391        _ => None,392    })()393    .unwrap_or(false)394}395396#[cfg(test)]397mod tests {398    use crate::{399        InlayHintsConfig,400        inlay_hints::tests::{DISABLED_CONFIG, check_with_config},401    };402403    #[track_caller]404    fn check_params(#[rust_analyzer::rust_fixture] ra_fixture: &str) {405        check_with_config(406            InlayHintsConfig { parameter_hints: true, ..DISABLED_CONFIG },407            ra_fixture,408        );409    }410411    #[test]412    fn param_hints_only() {413        check_params(414            r#"415fn foo(a: i32, b: i32) -> i32 { a + b }416fn main() {417    let _x = foo(418        4,419      //^ a420        4,421      //^ b422    );423}"#,424        );425    }426427    #[test]428    fn param_hints_on_closure() {429        check_params(430            r#"431//- minicore: fn432fn main() {433    let clo = |a: u8, b: u8| a + b;434    clo(435        1,436      //^ a437        2,438      //^ b439    );440}441            "#,442        );443    }444445    #[test]446    fn param_name_similar_to_fn_name_still_hints() {447        check_params(448            r#"449fn max(x: i32, y: i32) -> i32 { x + y }450fn main() {451    let _x = max(452        4,453      //^ x454        4,455      //^ y456    );457}"#,458        );459    }460461    #[test]462    fn param_name_similar_to_fn_name() {463        check_params(464            r#"465fn param_with_underscore(with_underscore: i32) -> i32 { with_underscore }466fn main() {467    let _x = param_with_underscore(468        4,469    );470}"#,471        );472        check_params(473            r#"474fn param_with_underscore(underscore: i32) -> i32 { underscore }475fn main() {476    let _x = param_with_underscore(477        4,478    );479}"#,480        );481    }482483    #[test]484    fn param_name_same_as_fn_name() {485        check_params(486            r#"487fn foo(foo: i32) -> i32 { foo }488fn main() {489    let _x = foo(490        4,491    );492}"#,493        );494    }495496    #[test]497    fn never_hide_param_when_multiple_params() {498        check_params(499            r#"500fn foo(foo: i32, bar: i32) -> i32 { bar + baz }501fn main() {502    let _x = foo(503        4,504      //^ foo505        8,506      //^ bar507    );508}"#,509        );510    }511512    #[test]513    fn param_hints_look_through_as_ref_and_clone() {514        check_params(515            r#"516fn foo(bar: i32, baz: f32) {}517518fn main() {519    let bar = 3;520    let baz = &"baz";521    let fez = 1.0;522    foo(bar.clone(), bar.clone());523                   //^^^^^^^^^^^ baz524    foo(bar.as_ref(), bar.as_ref());525                    //^^^^^^^^^^^^ baz526}527"#,528        );529    }530531    #[test]532    fn self_param_hints() {533        check_params(534            r#"535struct Foo;536537impl Foo {538    fn foo(self: Self) {}539    fn bar(self: &Self) {}540}541542fn main() {543    Foo::foo(Foo);544           //^^^ self545    Foo::bar(&Foo);546           //^^^^ self547}548"#,549        )550    }551552    #[test]553    fn param_name_hints_show_for_literals() {554        check_params(555            r#"pub fn test(a: i32, b: i32) -> [i32; 2] { [a, b] }556fn main() {557    test(558        0xa_b,559      //^^^^^ a560        0xa_b,561      //^^^^^ b562    );563}"#,564        )565    }566567    #[test]568    fn param_name_hints_show_after_empty_arg() {569        check_params(570            r#"pub fn test(a: i32, b: i32, c: i32) {}571fn main() {572    test(, 2,);573         //^ b574    test(, , 3);575           //^ c576}"#,577        )578    }579580    #[test]581    fn function_call_parameter_hint() {582        check_params(583            r#"584//- minicore: option585struct FileId {}586struct SmolStr {}587588struct TextRange {}589struct SyntaxKind {}590struct NavigationTarget {}591592struct Test {}593594impl Test {595    fn method(&self, mut param: i32) -> i32 { param * 2 }596597    fn from_syntax(598        file_id: FileId,599        name: SmolStr,600        focus_range: Option<TextRange>,601        full_range: TextRange,602        kind: SyntaxKind,603        docs: Option<String>,604    ) -> NavigationTarget {605        NavigationTarget {}606    }607}608609fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 {610    foo + bar611}612async fn test_async(foo: i32, _: i32) {}613614fn main() {615    let not_literal = 1;616    let _: i32 = test_func(1,    2,      "hello", 3,  not_literal);617                         //^ foo ^ bar   ^^^^^^^ msg  ^^^^^^^^^^^ last618    let t: Test = Test {};619    t.method(123);620           //^^^ param621    Test::method(&t,      3456);622               //^^ self  ^^^^ param623    Test::from_syntax(624        FileId {},625        "impl".into(),626      //^^^^^^^^^^^^^ name627        None,628      //^^^^ focus_range629        TextRange {},630      //^^^^^^^^^^^^ full_range631        SyntaxKind {},632      //^^^^^^^^^^^^^ kind633        None,634      //^^^^ docs635    );636    test_async(1, 2)637             //^ foo638}"#,639        );640    }641642    #[test]643    fn parameter_hint_heuristics() {644        check_params(645            r#"646fn check(ra_fixture_thing: &str) {}647648fn map(f: i32) {}649fn filter(predicate: i32) {}650651fn strip_suffix(suffix: &str) {}652fn stripsuffix(suffix: &str) {}653fn same(same: u32) {}654fn same2(_same2: u32) {}655656fn enum_matches_param_name(completion_kind: CompletionKind) {}657658fn foo(param: u32) {}659fn bar(param_eter: u32) {}660fn baz(a_d_e: u32) {}661fn far(loop_: u32) {}662fn faz(r#loop: u32) {}663664enum CompletionKind {665    Keyword,666}667668fn non_ident_pat((a, b): (u32, u32)) {}669670fn main() {671    const PARAM: u32 = 0;672    foo(PARAM);673    foo(!PARAM);674     // ^^^^^^ param675    check("");676677    map(0);678    filter(0);679680    strip_suffix("");681    stripsuffix("");682              //^^ suffix683    same(0);684    same2(0);685686    enum_matches_param_name(CompletionKind::Keyword);687688    let param = 0;689    foo(param);690    foo(param as _);691    let param_end = 0;692    foo(param_end);693    let start_param = 0;694    foo(start_param);695    let param2 = 0;696    foo(param2);697      //^^^^^^ param698699    macro_rules! param {700        () => {};701    };702    foo(param!());703704    let param_eter = 0;705    bar(param_eter);706    let param_eter_end = 0;707    bar(param_eter_end);708    let start_param_eter = 0;709    bar(start_param_eter);710    let param_eter2 = 0;711    bar(param_eter2);712      //^^^^^^^^^^^ param_eter713    let loop_level = 0;714    far(loop_level);715    faz(loop_level);716717    non_ident_pat((0, 0));718719    baz(a.d.e);720    baz(a.dc.e);721     // ^^^^^^ a_d_e722    baz(ac.d.e);723     // ^^^^^^ a_d_e724    baz(a.d.ec);725     // ^^^^^^ a_d_e726}"#,727        );728    }729730    #[track_caller]731    fn check_missing_params(#[rust_analyzer::rust_fixture] ra_fixture: &str) {732        check_with_config(733            InlayHintsConfig {734                parameter_hints: true,735                parameter_hints_for_missing_arguments: true,736                ..DISABLED_CONFIG737            },738            ra_fixture,739        );740    }741742    #[test]743    fn missing_param_hint_empty_call() {744        // When calling foo() with no args, show hint for first param on the closing paren745        check_missing_params(746            r#"747fn foo(a: i32, b: i32) -> i32 { a + b }748fn main() {749    foo();750      //^ a751}"#,752        );753    }754755    #[test]756    fn missing_param_hint_after_first_arg() {757        // foo(1,) - show hint for 'a' on '1', and 'b' on the trailing comma758        check_missing_params(759            r#"760fn foo(a: i32, b: i32) -> i32 { a + b }761fn main() {762    foo(1,);763      //^ a764        //^ b765}"#,766        );767    }768769    #[test]770    fn missing_param_hint_partial_args() {771        // foo(1, 2,) - show hints for a, b on args, and c on trailing comma772        check_missing_params(773            r#"774fn foo(a: i32, b: i32, c: i32) -> i32 { a + b + c }775fn main() {776    foo(1, 2,);777      //^ a778         //^ b779           //^ c780}"#,781        );782    }783784    #[test]785    fn missing_param_hint_method_call() {786        // S.foo(1,) - show hint for 'a' on '1', and 'b' on trailing comma787        check_missing_params(788            r#"789struct S;790impl S {791    fn foo(&self, a: i32, b: i32) -> i32 { a + b }792}793fn main() {794    S.foo(1,);795        //^ a796          //^ b797}"#,798        );799    }800801    #[test]802    fn missing_param_hint_no_hint_when_complete() {803        // When all args provided, no missing hint - just regular param hints804        check_missing_params(805            r#"806fn foo(a: i32, b: i32) -> i32 { a + b }807fn main() {808    foo(1, 2);809      //^ a810         //^ b811}"#,812        );813    }814815    #[test]816    fn missing_param_hint_respects_heuristics() {817        // The hint should be hidden if it matches heuristics (e.g., single param unary fn with same name)818        check_missing_params(819            r#"820fn foo(foo: i32) -> i32 { foo }821fn main() {822    foo();823}"#,824        );825    }826}

Code quality findings 7

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
let function_name = match callable.kind() {
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
let name_syntax = match source.value.as_ref() {
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
Either::Right(param) => match param.pat()? {
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
let name_syntax = match source.value.as_ref() {
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
Either::Right(param) => match param.pat()? {
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
// ast::Expr::ClosureExpr(closure_expr) => todo!(),
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 sema.resolve_path(&qual)? {

Get this view in your editor

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