src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs RUST 831 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().split('_'));298    let argument = argument.map(|it| it.strip_prefix("r#").unwrap_or(it));299300    let prefix_match = zip(argument.clone(), param_name.clone())301        .all(|(arg, param)| arg.eq_ignore_ascii_case(param));302    let postfix_match = || {303        zip(argument.rev(), param_name.rev()).all(|(arg, param)| arg.eq_ignore_ascii_case(param))304    };305    prefix_match || postfix_match()306}307308pub(super) fn get_segment_representation(309    expr: &ast::Expr,310) -> Option<Either<Vec<ast::NameRef>, ast::Path>> {311    match expr {312        ast::Expr::MethodCallExpr(method_call_expr) => {313            let receiver =314                method_call_expr.receiver().and_then(|expr| get_segment_representation(&expr));315            let name_ref = method_call_expr.name_ref()?;316            if INSIGNIFICANT_METHOD_NAMES.contains(&name_ref.text()) {317                return receiver;318            }319            Some(Either::Left(match receiver {320                Some(Either::Left(mut left)) => {321                    left.push(name_ref);322                    left323                }324                Some(Either::Right(_)) | None => vec![name_ref],325            }))326        }327        ast::Expr::FieldExpr(field_expr) => {328            let expr = field_expr.expr().and_then(|expr| get_segment_representation(&expr));329            let name_ref = field_expr.name_ref()?;330            let res = match expr {331                Some(Either::Left(mut left)) => {332                    left.push(name_ref);333                    left334                }335                Some(Either::Right(_)) | None => vec![name_ref],336            };337            Some(Either::Left(res))338        }339        // paths340        ast::Expr::MacroExpr(macro_expr) => macro_expr.macro_call()?.path().map(Either::Right),341        ast::Expr::RecordExpr(record_expr) => record_expr.path().map(Either::Right),342        ast::Expr::PathExpr(path_expr) => {343            let path = path_expr.path()?;344            // single segment paths are likely locals345            Some(match path.as_single_name_ref() {346                None => Either::Right(path),347                Some(name_ref) => Either::Left(vec![name_ref]),348            })349        }350        ast::Expr::PrefixExpr(prefix_expr) if prefix_expr.op_kind() == Some(UnaryOp::Not) => None,351        // recurse352        ast::Expr::PrefixExpr(prefix_expr) => get_segment_representation(&prefix_expr.expr()?),353        ast::Expr::RefExpr(ref_expr) => get_segment_representation(&ref_expr.expr()?),354        ast::Expr::CastExpr(cast_expr) => get_segment_representation(&cast_expr.expr()?),355        ast::Expr::CallExpr(call_expr) => get_segment_representation(&call_expr.expr()?),356        ast::Expr::AwaitExpr(await_expr) => get_segment_representation(&await_expr.expr()?),357        ast::Expr::IndexExpr(index_expr) => get_segment_representation(&index_expr.base()?),358        ast::Expr::ParenExpr(paren_expr) => get_segment_representation(&paren_expr.expr()?),359        ast::Expr::TryExpr(try_expr) => get_segment_representation(&try_expr.expr()?),360        // ast::Expr::ClosureExpr(closure_expr) => todo!(),361        _ => None,362    }363}364365fn is_obvious_param(param_name: &str) -> bool {366    // avoid displaying hints for common functions like map, filter, etc.367    // or other obvious words used in std368    param_name.len() == 1 || INSIGNIFICANT_PARAMETER_NAMES.contains(&param_name)369}370371fn is_adt_constructor_similar_to_param_name(372    sema: &Semantics<'_, RootDatabase>,373    path: &ast::Path,374    param_name: &str,375) -> bool {376    (|| match sema.resolve_path(path)? {377        hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => {378            Some(to_lower_snake_case(path.segment()?.name_ref()?.text()) == param_name)379        }380        hir::PathResolution::Def(hir::ModuleDef::Function(_) | hir::ModuleDef::EnumVariant(_)) => {381            if to_lower_snake_case(path.segment()?.name_ref()?.text()) == param_name {382                return Some(true);383            }384            let qual = path.qualifier()?;385            match sema.resolve_path(&qual)? {386                hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => {387                    Some(to_lower_snake_case(qual.segment()?.name_ref()?.text()) == param_name)388                }389                _ => None,390            }391        }392        _ => None,393    })()394    .unwrap_or(false)395}396397#[cfg(test)]398mod tests {399    use crate::{400        InlayHintsConfig,401        inlay_hints::tests::{DISABLED_CONFIG, check_with_config},402    };403404    #[track_caller]405    fn check_params(#[rust_analyzer::rust_fixture] ra_fixture: &str) {406        check_with_config(407            InlayHintsConfig { parameter_hints: true, ..DISABLED_CONFIG },408            ra_fixture,409        );410    }411412    #[test]413    fn param_hints_only() {414        check_params(415            r#"416fn foo(a: i32, b: i32) -> i32 { a + b }417fn main() {418    let _x = foo(419        4,420      //^ a421        4,422      //^ b423    );424}"#,425        );426    }427428    #[test]429    fn param_hints_on_closure() {430        check_params(431            r#"432//- minicore: fn433fn main() {434    let clo = |a: u8, b: u8| a + b;435    clo(436        1,437      //^ a438        2,439      //^ b440    );441}442            "#,443        );444    }445446    #[test]447    fn param_name_similar_to_fn_name_still_hints() {448        check_params(449            r#"450fn max(x: i32, y: i32) -> i32 { x + y }451fn main() {452    let _x = max(453        4,454      //^ x455        4,456      //^ y457    );458}"#,459        );460    }461462    #[test]463    fn param_name_similar_to_fn_name() {464        check_params(465            r#"466fn param_with_underscore(with_underscore: i32) -> i32 { with_underscore }467fn main() {468    let _x = param_with_underscore(469        4,470    );471}"#,472        );473        check_params(474            r#"475fn param_with_underscore(underscore: i32) -> i32 { underscore }476fn main() {477    let _x = param_with_underscore(478        4,479    );480}"#,481        );482    }483484    #[test]485    fn param_name_same_as_fn_name() {486        check_params(487            r#"488fn foo(foo: i32) -> i32 { foo }489fn main() {490    let _x = foo(491        4,492    );493}"#,494        );495    }496497    #[test]498    fn never_hide_param_when_multiple_params() {499        check_params(500            r#"501fn foo(foo: i32, bar: i32) -> i32 { bar + baz }502fn main() {503    let _x = foo(504        4,505      //^ foo506        8,507      //^ bar508    );509}"#,510        );511    }512513    #[test]514    fn param_hints_look_through_as_ref_and_clone() {515        check_params(516            r#"517fn foo(bar: i32, baz: f32) {}518519fn main() {520    let bar = 3;521    let baz = &"baz";522    let fez = 1.0;523    foo(bar.clone(), bar.clone());524                   //^^^^^^^^^^^ baz525    foo(bar.as_ref(), bar.as_ref());526                    //^^^^^^^^^^^^ baz527}528"#,529        );530    }531532    #[test]533    fn self_param_hints() {534        check_params(535            r#"536struct Foo;537538impl Foo {539    fn foo(self: Self) {}540    fn bar(self: &Self) {}541}542543fn main() {544    Foo::foo(Foo);545           //^^^ self546    Foo::bar(&Foo);547           //^^^^ self548}549"#,550        )551    }552553    #[test]554    fn param_name_hints_show_for_literals() {555        check_params(556            r#"pub fn test(a: i32, b: i32) -> [i32; 2] { [a, b] }557fn main() {558    test(559        0xa_b,560      //^^^^^ a561        0xa_b,562      //^^^^^ b563    );564}"#,565        )566    }567568    #[test]569    fn param_name_hints_show_after_empty_arg() {570        check_params(571            r#"pub fn test(a: i32, b: i32, c: i32) {}572fn main() {573    test(, 2,);574         //^ b575    test(, , 3);576           //^ c577}"#,578        )579    }580581    #[test]582    fn function_call_parameter_hint() {583        check_params(584            r#"585//- minicore: option586struct FileId {}587struct SmolStr {}588589struct TextRange {}590struct SyntaxKind {}591struct NavigationTarget {}592593struct Test {}594595impl Test {596    fn method(&self, mut param: i32) -> i32 { param * 2 }597598    fn from_syntax(599        file_id: FileId,600        name: SmolStr,601        focus_range: Option<TextRange>,602        full_range: TextRange,603        kind: SyntaxKind,604        docs: Option<String>,605    ) -> NavigationTarget {606        NavigationTarget {}607    }608}609610fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 {611    foo + bar612}613async fn test_async(foo: i32, _: i32) {}614615fn main() {616    let not_literal = 1;617    let _: i32 = test_func(1,    2,      "hello", 3,  not_literal);618                         //^ foo ^ bar   ^^^^^^^ msg  ^^^^^^^^^^^ last619    let t: Test = Test {};620    t.method(123);621           //^^^ param622    Test::method(&t,      3456);623               //^^ self  ^^^^ param624    Test::from_syntax(625        FileId {},626        "impl".into(),627      //^^^^^^^^^^^^^ name628        None,629      //^^^^ focus_range630        TextRange {},631      //^^^^^^^^^^^^ full_range632        SyntaxKind {},633      //^^^^^^^^^^^^^ kind634        None,635      //^^^^ docs636    );637    test_async(1, 2)638             //^ foo639}"#,640        );641    }642643    #[test]644    fn parameter_hint_heuristics() {645        check_params(646            r#"647fn check(ra_fixture_thing: &str) {}648649fn map(f: i32) {}650fn filter(predicate: i32) {}651652fn strip_suffix(suffix: &str) {}653fn stripsuffix(suffix: &str) {}654fn same(same: u32) {}655fn same2(_same2: u32) {}656657fn enum_matches_param_name(completion_kind: CompletionKind) {}658659fn foo(param: u32) {}660fn bar(param_eter: u32) {}661fn baz(a_d_e: u32) {}662fn far(loop_: u32) {}663fn faz(r#loop: u32) {}664665enum CompletionKind {666    Keyword,667}668669fn non_ident_pat((a, b): (u32, u32)) {}670671fn main() {672    const PARAM: u32 = 0;673    foo(PARAM);674    foo(!PARAM);675     // ^^^^^^ param676    check("");677678    map(0);679    filter(0);680681    strip_suffix("");682    stripsuffix("");683              //^^ suffix684    same(0);685    same2(0);686687    enum_matches_param_name(CompletionKind::Keyword);688689    let param = 0;690    foo(param);691    foo(param as _);692    let param_end = 0;693    foo(param_end);694    let start_param = 0;695    foo(start_param);696    let param2 = 0;697    foo(param2);698      //^^^^^^ param699700    macro_rules! param {701        () => {};702    };703    foo(param!());704705    let param_eter = 0;706    bar(param_eter);707    let param_eter_end = 0;708    bar(param_eter_end);709    let start_param_eter = 0;710    bar(start_param_eter);711    let param_eter2 = 0;712    bar(param_eter2);713      //^^^^^^^^^^^ param_eter714    let r#loop = true;715    let loop_level = 0;716    far(loop_level);717    faz(loop_level);718    far(r#loop);719    faz(r#loop);720721    non_ident_pat((0, 0));722723    baz(a.d.e);724    baz(a.dc.e);725     // ^^^^^^ a_d_e726    baz(ac.d.e);727     // ^^^^^^ a_d_e728    baz(a.d.ec);729     // ^^^^^^ a_d_e730}"#,731        );732    }733734    #[track_caller]735    fn check_missing_params(#[rust_analyzer::rust_fixture] ra_fixture: &str) {736        check_with_config(737            InlayHintsConfig {738                parameter_hints: true,739                parameter_hints_for_missing_arguments: true,740                ..DISABLED_CONFIG741            },742            ra_fixture,743        );744    }745746    #[test]747    fn missing_param_hint_empty_call() {748        // When calling foo() with no args, show hint for first param on the closing paren749        check_missing_params(750            r#"751fn foo(a: i32, b: i32) -> i32 { a + b }752fn main() {753    foo();754      //^ a755}"#,756        );757    }758759    #[test]760    fn missing_param_hint_after_first_arg() {761        // foo(1,) - show hint for 'a' on '1', and 'b' on the trailing comma762        check_missing_params(763            r#"764fn foo(a: i32, b: i32) -> i32 { a + b }765fn main() {766    foo(1,);767      //^ a768        //^ b769}"#,770        );771    }772773    #[test]774    fn missing_param_hint_partial_args() {775        // foo(1, 2,) - show hints for a, b on args, and c on trailing comma776        check_missing_params(777            r#"778fn foo(a: i32, b: i32, c: i32) -> i32 { a + b + c }779fn main() {780    foo(1, 2,);781      //^ a782         //^ b783           //^ c784}"#,785        );786    }787788    #[test]789    fn missing_param_hint_method_call() {790        // S.foo(1,) - show hint for 'a' on '1', and 'b' on trailing comma791        check_missing_params(792            r#"793struct S;794impl S {795    fn foo(&self, a: i32, b: i32) -> i32 { a + b }796}797fn main() {798    S.foo(1,);799        //^ a800          //^ b801}"#,802        );803    }804805    #[test]806    fn missing_param_hint_no_hint_when_complete() {807        // When all args provided, no missing hint - just regular param hints808        check_missing_params(809            r#"810fn foo(a: i32, b: i32) -> i32 { a + b }811fn main() {812    foo(1, 2);813      //^ a814         //^ b815}"#,816        );817    }818819    #[test]820    fn missing_param_hint_respects_heuristics() {821        // The hint should be hidden if it matches heuristics (e.g., single param unary fn with same name)822        check_missing_params(823            r#"824fn foo(foo: i32) -> i32 { foo }825fn main() {826    foo();827}"#,828        );829    }830}

Code quality findings 6

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()? {
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.

Generated by searchcode.com · powered by code_get_file + code_get_findings