src/tools/rust-analyzer/crates/ide/src/signature_help.rs RUST 2,746 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 2,746.
1//! This module provides primitives for showing type and function parameter information when editing2//! a call or use-site.34use std::collections::BTreeSet;56use either::Either;7use hir::{8    AssocItem, DisplayTarget, GenericDef, GenericParam, HirDisplay, ModuleDef, PathResolution,9    Semantics, Trait,10};11use ide_db::{12    FilePosition, FxIndexMap,13    active_parameter::{callable_for_arg_list, generic_def_for_node},14    documentation::{Documentation, HasDocs},15};16use itertools::Itertools;17use span::Edition;18use stdx::format_to;19use syntax::{20    AstNode, Direction, NodeOrToken, SyntaxElementChildren, SyntaxNode, SyntaxToken, T, TextRange,21    TextSize, ToSmolStr, algo,22    ast::{self, AstChildren},23    match_ast,24};2526use crate::RootDatabase;2728/// Contains information about an item signature as seen from a use site.29///30/// This includes the "active parameter", which is the parameter whose value is currently being31/// edited.32#[derive(Debug)]33pub struct SignatureHelp {34    pub doc: Option<Documentation<'static>>,35    pub signature: String,36    pub active_parameter: Option<usize>,37    parameters: Vec<TextRange>,38}3940impl SignatureHelp {41    pub fn parameter_labels(&self) -> impl Iterator<Item = &str> + '_ {42        self.parameters.iter().map(move |&it| &self.signature[it])43    }4445    pub fn parameter_ranges(&self) -> &[TextRange] {46        &self.parameters47    }4849    fn push_call_param(&mut self, param: &str) {50        self.push_param("(", param);51    }5253    fn push_generic_param(&mut self, param: &str) {54        self.push_param("<", param);55    }5657    fn push_record_field(&mut self, param: &str) {58        self.push_param("{ ", param);59    }6061    fn push_param(&mut self, opening_delim: &str, param: &str) {62        if !self.signature.ends_with(opening_delim) {63            self.signature.push_str(", ");64        }65        let start = TextSize::of(&self.signature);66        self.signature.push_str(param);67        let end = TextSize::of(&self.signature);68        self.parameters.push(TextRange::new(start, end))69    }70}7172/// Computes parameter information for the given position.73pub(crate) fn signature_help(74    db: &RootDatabase,75    FilePosition { file_id, offset }: FilePosition,76) -> Option<SignatureHelp> {77    let sema = Semantics::new(db);78    let file = sema.parse_guess_edition(file_id);79    let file = file.syntax();80    let token = file81        .token_at_offset(offset)82        .left_biased()83        // if the cursor is sandwiched between two space tokens and the call is unclosed84        // this prevents us from leaving the CallExpression85        .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?;86    let token = sema.descend_into_macros_single_exact(token);87    let edition = sema.attach_first_edition(file_id).edition(db);88    let display_target = sema.first_crate(file_id)?.to_display_target(db);8990    for node in token.parent_ancestors() {91        match_ast! {92            match node {93                ast::ArgList(arg_list) => {94                    let cursor_outside = arg_list.r_paren_token().as_ref() == Some(&token);95                    if cursor_outside {96                        continue;97                    }98                    return signature_help_for_call(&sema, arg_list, token, edition, display_target);99                },100                ast::GenericArgList(garg_list) => {101                    let cursor_outside = garg_list.r_angle_token().as_ref() == Some(&token);102                    if cursor_outside {103                        continue;104                    }105                    return signature_help_for_generics(&sema, garg_list, token, edition, display_target);106                },107                ast::RecordExpr(record) => {108                    let cursor_outside = record.record_expr_field_list().and_then(|list| list.r_curly_token()).as_ref() == Some(&token);109                    if cursor_outside {110                        continue;111                    }112                    return signature_help_for_record_lit(&sema, record, token, edition, display_target);113                },114                ast::RecordPat(record) => {115                    let cursor_outside = record.record_pat_field_list().and_then(|list| list.r_curly_token()).as_ref() == Some(&token);116                    if cursor_outside {117                        continue;118                    }119                    return signature_help_for_record_pat(&sema, record, token, edition, display_target);120                },121                ast::TupleStructPat(tuple_pat) => {122                    let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token);123                    if cursor_outside {124                        continue;125                    }126                    return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token, edition, display_target);127                },128                ast::TuplePat(tuple_pat) => {129                    let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token);130                    if cursor_outside {131                        continue;132                    }133                    return signature_help_for_tuple_pat(&sema, tuple_pat, token, display_target);134                },135                ast::TupleExpr(tuple_expr) => {136                    let cursor_outside = tuple_expr.r_paren_token().as_ref() == Some(&token);137                    if cursor_outside {138                        continue;139                    }140                    return signature_help_for_tuple_expr(&sema, tuple_expr, token, display_target);141                },142                _ => (),143            }144        }145146        // Stop at multi-line expressions, since the signature of the outer call is not very147        // helpful inside them.148        if let Some(expr) = ast::Expr::cast(node.clone())149            && !matches!(expr, ast::Expr::RecordExpr(..))150            && expr.syntax().text().contains_char('\n')151        {152            break;153        }154    }155156    None157}158159fn signature_help_for_call(160    sema: &Semantics<'_, RootDatabase>,161    arg_list: ast::ArgList,162    token: SyntaxToken,163    edition: Edition,164    display_target: DisplayTarget,165) -> Option<SignatureHelp> {166    let (callable, active_parameter) =167        callable_for_arg_list(sema, arg_list, token.text_range().start())?;168169    let mut res =170        SignatureHelp { doc: None, signature: String::new(), parameters: vec![], active_parameter };171172    let db = sema.db;173    let mut fn_params = None;174    match callable.kind() {175        hir::CallableKind::Function(func) => {176            res.doc = func.docs(db).map(Documentation::into_owned);177            if func.is_const(db) {178                format_to!(res.signature, "const ");179            }180            if func.is_async(db) {181                format_to!(res.signature, "async ");182            }183            if func.is_unsafe(db) {184                format_to!(res.signature, "unsafe ");185            }186            format_to!(res.signature, "fn {}", func.name(db).display(db, edition));187188            let generic_params = GenericDef::Function(func)189                .params(db)190                .iter()191                .filter(|param| match param {192                    GenericParam::TypeParam(type_param) => !type_param.is_implicit(db),193                    GenericParam::ConstParam(_) | GenericParam::LifetimeParam(_) => true,194                })195                .map(|param| param.display(db, display_target))196                .join(", ");197            if !generic_params.is_empty() {198                format_to!(res.signature, "<{}>", generic_params);199            }200201            fn_params = Some(match callable.receiver_param(db) {202                Some(_self) => func.params_without_self(db),203                None => func.assoc_fn_params(db),204            });205        }206        hir::CallableKind::TupleStruct(strukt) => {207            res.doc = strukt.docs(db).map(Documentation::into_owned);208            format_to!(res.signature, "struct {}", strukt.name(db).display(db, edition));209210            let generic_params = GenericDef::Adt(strukt.into())211                .params(db)212                .iter()213                .map(|param| param.display(db, display_target))214                .join(", ");215            if !generic_params.is_empty() {216                format_to!(res.signature, "<{}>", generic_params);217            }218        }219        hir::CallableKind::TupleEnumVariant(variant) => {220            res.doc = variant.docs(db).map(Documentation::into_owned);221            format_to!(222                res.signature,223                "enum {}",224                variant.parent_enum(db).name(db).display(db, edition),225            );226227            let generic_params = GenericDef::Adt(variant.parent_enum(db).into())228                .params(db)229                .iter()230                .map(|param| param.display(db, display_target))231                .join(", ");232            if !generic_params.is_empty() {233                format_to!(res.signature, "<{}>", generic_params);234            }235236            format_to!(res.signature, "::{}", variant.name(db).display(db, edition))237        }238        hir::CallableKind::Closure(closure) => {239            let fn_trait = closure.fn_trait(db);240            format_to!(res.signature, "impl {fn_trait}")241        }242        hir::CallableKind::FnPtr => format_to!(res.signature, "fn"),243        hir::CallableKind::FnImpl(fn_trait) => match callable.ty().as_adt() {244            // FIXME: Render docs of the concrete trait impl function245            Some(adt) => format_to!(246                res.signature,247                "<{} as {fn_trait}>::{}",248                adt.name(db).display(db, edition),249                fn_trait.function_name()250            ),251            None => format_to!(res.signature, "impl {fn_trait}"),252        },253    }254255    res.signature.push('(');256    {257        if let Some((self_param, _)) = callable.receiver_param(db) {258            format_to!(res.signature, "{}", self_param.display(db, display_target))259        }260        let mut buf = String::new();261        for (idx, p) in callable.params().into_iter().enumerate() {262            buf.clear();263            if let Some(param) = sema.source(p.clone()) {264                match param.value {265                    Either::Right(param) => match param.pat() {266                        Some(pat) => format_to!(buf, "{}: ", pat),267                        None => format_to!(buf, "?: "),268                    },269                    Either::Left(_) => format_to!(buf, "self: "),270                }271            }272            // APITs (argument position `impl Trait`s) are inferred as {unknown} as the user is273            // in the middle of entering call arguments.274            // In that case, fall back to render definitions of the respective parameters.275            // This is overly conservative: we do not substitute known type vars276            // (see FIXME in tests::impl_trait) and falling back on any unknowns.277            match (p.ty().contains_unknown(), fn_params.as_deref()) {278                (true, Some(fn_params)) => {279                    format_to!(buf, "{}", fn_params[idx].ty().display(db, display_target))280                }281                _ => format_to!(buf, "{}", p.ty().display(db, display_target)),282            }283            res.push_call_param(&buf);284        }285    }286    res.signature.push(')');287288    let mut render = |ret_type: hir::Type<'_>| {289        if !ret_type.is_unit() {290            format_to!(res.signature, " -> {}", ret_type.display(db, display_target));291        }292    };293    match callable.kind() {294        hir::CallableKind::Function(func) => render(func.async_ret_type(db).unwrap_or_else(|| {295            if callable.return_type().contains_unknown() {296                func.ret_type(db)297            } else {298                callable.return_type()299            }300        })),301        hir::CallableKind::Closure(_) | hir::CallableKind::FnPtr | hir::CallableKind::FnImpl(_) => {302            render(callable.return_type())303        }304        hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}305    }306    Some(res)307}308309fn signature_help_for_generics(310    sema: &Semantics<'_, RootDatabase>,311    arg_list: ast::GenericArgList,312    token: SyntaxToken,313    edition: Edition,314    display_target: DisplayTarget,315) -> Option<SignatureHelp> {316    let (generics_def, mut active_parameter, first_arg_is_non_lifetime, variant) =317        generic_def_for_node(sema, &arg_list, &token)?;318    let mut res = SignatureHelp {319        doc: None,320        signature: String::new(),321        parameters: vec![],322        active_parameter: None,323    };324325    let db = sema.db;326    match generics_def {327        hir::GenericDef::Function(it) => {328            res.doc = it.docs(db).map(Documentation::into_owned);329            format_to!(res.signature, "fn {}", it.name(db).display(db, edition));330        }331        hir::GenericDef::Adt(hir::Adt::Enum(it)) => {332            res.doc = it.docs(db).map(Documentation::into_owned);333            format_to!(res.signature, "enum {}", it.name(db).display(db, edition));334            if let Some(variant) = variant {335                // In paths, generics of an enum can be specified *after* one of its variants.336                // eg. `None::<u8>`337                // We'll use the signature of the enum, but include the docs of the variant.338                res.doc = variant.docs(db).map(Documentation::into_owned);339            }340        }341        hir::GenericDef::Adt(hir::Adt::Struct(it)) => {342            res.doc = it.docs(db).map(Documentation::into_owned);343            format_to!(res.signature, "struct {}", it.name(db).display(db, edition));344        }345        hir::GenericDef::Adt(hir::Adt::Union(it)) => {346            res.doc = it.docs(db).map(Documentation::into_owned);347            format_to!(res.signature, "union {}", it.name(db).display(db, edition));348        }349        hir::GenericDef::Trait(it) => {350            res.doc = it.docs(db).map(Documentation::into_owned);351            format_to!(res.signature, "trait {}", it.name(db).display(db, edition));352        }353        hir::GenericDef::TypeAlias(it) => {354            res.doc = it.docs(db).map(Documentation::into_owned);355            format_to!(res.signature, "type {}", it.name(db).display(db, edition));356        }357        // These don't have generic args that can be specified358        hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) | hir::GenericDef::Static(_) => {359            return None;360        }361    }362363    let params = generics_def.params(sema.db);364    let num_lifetime_params =365        params.iter().take_while(|param| matches!(param, GenericParam::LifetimeParam(_))).count();366    if first_arg_is_non_lifetime {367        // Lifetime parameters were omitted.368        active_parameter += num_lifetime_params;369    }370    res.active_parameter = Some(active_parameter);371372    res.signature.push('<');373    let mut buf = String::new();374    for param in params {375        if let hir::GenericParam::TypeParam(ty) = param376            && ty.is_implicit(db)377        {378            continue;379        }380381        buf.clear();382        format_to!(buf, "{}", param.display(db, display_target));383        match param {384            GenericParam::TypeParam(param) => {385                if let Some(ty) = param.default(db) {386                    format_to!(buf, " = {}", ty.display(db, display_target));387                }388            }389            GenericParam::ConstParam(param) => {390                if let Some(expr) = param.default(db, display_target) {391                    format_to!(buf, " = {}", expr);392                }393            }394            _ => {}395        }396        res.push_generic_param(&buf);397    }398    if let hir::GenericDef::Trait(tr) = generics_def {399        add_assoc_type_bindings(db, &mut res, tr, arg_list, edition);400    }401    res.signature.push('>');402403    Some(res)404}405406fn add_assoc_type_bindings(407    db: &RootDatabase,408    res: &mut SignatureHelp,409    tr: Trait,410    args: ast::GenericArgList,411    edition: Edition,412) {413    if args.syntax().ancestors().find_map(ast::TypeBound::cast).is_none() {414        // Assoc type bindings are only valid in type bound position.415        return;416    }417418    let present_bindings = args419        .generic_args()420        .filter_map(|arg| match arg {421            ast::GenericArg::AssocTypeArg(arg) => arg.name_ref().map(|n| n.to_string()),422            _ => None,423        })424        .collect::<BTreeSet<_>>();425426    let mut buf = String::new();427    for binding in &present_bindings {428        buf.clear();429        format_to!(buf, "{} = …", binding);430        res.push_generic_param(&buf);431    }432433    for item in tr.items_with_supertraits(db) {434        if let AssocItem::TypeAlias(ty) = item {435            let name = ty.name(db).display_no_db(edition).to_smolstr();436            if !present_bindings.contains(&*name) {437                buf.clear();438                format_to!(buf, "{} = …", name);439                res.push_generic_param(&buf);440            }441        }442    }443}444445fn signature_help_for_record_lit(446    sema: &Semantics<'_, RootDatabase>,447    record: ast::RecordExpr,448    token: SyntaxToken,449    edition: Edition,450    display_target: DisplayTarget,451) -> Option<SignatureHelp> {452    signature_help_for_record_(453        sema,454        record.record_expr_field_list()?.syntax().children_with_tokens(),455        &record.path()?,456        record457            .record_expr_field_list()?458            .fields()459            .filter_map(|field| sema.resolve_record_field(&field))460            .map(|(field, _, ty)| (field, ty)),461        token,462        edition,463        display_target,464    )465}466467fn signature_help_for_record_pat(468    sema: &Semantics<'_, RootDatabase>,469    record: ast::RecordPat,470    token: SyntaxToken,471    edition: Edition,472    display_target: DisplayTarget,473) -> Option<SignatureHelp> {474    signature_help_for_record_(475        sema,476        record.record_pat_field_list()?.syntax().children_with_tokens(),477        &record.path()?,478        record479            .record_pat_field_list()?480            .fields()481            .filter_map(|field| sema.resolve_record_pat_field(&field)),482        token,483        edition,484        display_target,485    )486}487488fn signature_help_for_tuple_struct_pat(489    sema: &Semantics<'_, RootDatabase>,490    pat: ast::TupleStructPat,491    token: SyntaxToken,492    edition: Edition,493    display_target: DisplayTarget,494) -> Option<SignatureHelp> {495    let path = pat.path()?;496    let path_res = sema.resolve_path(&path)?;497    let mut res = SignatureHelp {498        doc: None,499        signature: String::new(),500        parameters: vec![],501        active_parameter: None,502    };503    let db = sema.db;504505    let fields: Vec<_> = if let PathResolution::Def(ModuleDef::EnumVariant(variant)) = path_res {506        let en = variant.parent_enum(db);507508        res.doc = en.docs(db).map(Documentation::into_owned);509        format_to!(510            res.signature,511            "enum {}::{} (",512            en.name(db).display(db, edition),513            variant.name(db).display(db, edition)514        );515        variant.fields(db)516    } else {517        let adt = match path_res {518            PathResolution::SelfType(imp) => imp.self_ty(db).as_adt()?,519            PathResolution::Def(ModuleDef::Adt(adt)) => adt,520            _ => return None,521        };522523        match adt {524            hir::Adt::Struct(it) => {525                res.doc = it.docs(db).map(Documentation::into_owned);526                format_to!(res.signature, "struct {} (", it.name(db).display(db, edition));527                it.fields(db)528            }529            _ => return None,530        }531    };532    Some(signature_help_for_tuple_pat_ish(533        db,534        res,535        pat.syntax(),536        token,537        pat.fields(),538        fields.into_iter().map(|it| it.ty(db)),539        display_target,540    ))541}542543fn signature_help_for_tuple_pat(544    sema: &Semantics<'_, RootDatabase>,545    pat: ast::TuplePat,546    token: SyntaxToken,547    display_target: DisplayTarget,548) -> Option<SignatureHelp> {549    let db = sema.db;550    let field_pats = pat.fields();551    let pat = pat.into();552    let ty = sema.type_of_pat(&pat)?;553    let fields = ty.original.tuple_fields(db);554555    Some(signature_help_for_tuple_pat_ish(556        db,557        SignatureHelp {558            doc: None,559            signature: String::from('('),560            parameters: vec![],561            active_parameter: None,562        },563        pat.syntax(),564        token,565        field_pats,566        fields.into_iter(),567        display_target,568    ))569}570571fn signature_help_for_tuple_expr(572    sema: &Semantics<'_, RootDatabase>,573    expr: ast::TupleExpr,574    token: SyntaxToken,575    display_target: DisplayTarget,576) -> Option<SignatureHelp> {577    let active_parameter = Some(578        expr.syntax()579            .children_with_tokens()580            .filter_map(NodeOrToken::into_token)581            .filter(|t| t.kind() == T![,])582            .take_while(|t| t.text_range().start() <= token.text_range().start())583            .count(),584    );585586    let db = sema.db;587    let mut res = SignatureHelp {588        doc: None,589        signature: String::from('('),590        parameters: vec![],591        active_parameter,592    };593    let expr = sema.type_of_expr(&expr.into())?;594    let fields = expr.original.tuple_fields(db);595    let mut buf = String::new();596    for ty in fields {597        format_to!(buf, "{}", ty.display_truncated(db, Some(20), display_target));598        res.push_call_param(&buf);599        buf.clear();600    }601    res.signature.push(')');602    Some(res)603}604605fn signature_help_for_record_<'db>(606    sema: &Semantics<'db, RootDatabase>,607    field_list_children: SyntaxElementChildren,608    path: &ast::Path,609    fields2: impl Iterator<Item = (hir::Field, hir::Type<'db>)>,610    token: SyntaxToken,611    edition: Edition,612    display_target: DisplayTarget,613) -> Option<SignatureHelp> {614    let active_parameter = field_list_children615        .filter_map(NodeOrToken::into_token)616        .filter(|t| t.kind() == T![,])617        .take_while(|t| t.text_range().start() <= token.text_range().start())618        .count();619620    let mut res = SignatureHelp {621        doc: None,622        signature: String::new(),623        parameters: vec![],624        active_parameter: Some(active_parameter),625    };626627    let fields;628629    let db = sema.db;630    let path_res = sema.resolve_path(path)?;631    if let PathResolution::Def(ModuleDef::EnumVariant(variant)) = path_res {632        fields = variant.fields(db);633        let en = variant.parent_enum(db);634635        res.doc = en.docs(db).map(Documentation::into_owned);636        format_to!(637            res.signature,638            "enum {}::{} {{ ",639            en.name(db).display(db, edition),640            variant.name(db).display(db, edition)641        );642    } else {643        let adt = match path_res {644            PathResolution::SelfType(imp) => imp.self_ty(db).as_adt()?,645            PathResolution::Def(ModuleDef::Adt(adt)) => adt,646            _ => return None,647        };648649        match adt {650            hir::Adt::Struct(it) => {651                fields = it.fields(db);652                res.doc = it.docs(db).map(Documentation::into_owned);653                format_to!(res.signature, "struct {} {{ ", it.name(db).display(db, edition));654            }655            hir::Adt::Union(it) => {656                fields = it.fields(db);657                res.doc = it.docs(db).map(Documentation::into_owned);658                format_to!(res.signature, "union {} {{ ", it.name(db).display(db, edition));659            }660            _ => return None,661        }662    }663664    let mut fields =665        fields.into_iter().map(|field| (field.name(db), Some(field))).collect::<FxIndexMap<_, _>>();666    let mut buf = String::new();667    for (field, ty) in fields2 {668        let name = field.name(db);669        format_to!(670            buf,671            "{}: {}",672            name.display(db, edition),673            ty.display_truncated(db, Some(20), display_target)674        );675        res.push_record_field(&buf);676        buf.clear();677678        if let Some(field) = fields.get_mut(&name) {679            *field = None;680        }681    }682    for (name, field) in fields {683        let Some(field) = field else { continue };684        format_to!(685            buf,686            "{}: {}",687            name.display(db, edition),688            field.ty(db).display_truncated(db, Some(20), display_target)689        );690        res.push_record_field(&buf);691        buf.clear();692    }693    res.signature.push_str(" }");694    Some(res)695}696697fn signature_help_for_tuple_pat_ish<'db>(698    db: &'db RootDatabase,699    mut res: SignatureHelp,700    pat: &SyntaxNode,701    token: SyntaxToken,702    mut field_pats: AstChildren<ast::Pat>,703    fields: impl ExactSizeIterator<Item = hir::Type<'db>>,704    display_target: DisplayTarget,705) -> SignatureHelp {706    let rest_pat = field_pats.find(|it| matches!(it, ast::Pat::RestPat(_)));707    let is_left_of_rest_pat =708        rest_pat.is_none_or(|it| token.text_range().start() < it.syntax().text_range().end());709710    let commas = pat711        .children_with_tokens()712        .filter_map(NodeOrToken::into_token)713        .filter(|t| t.kind() == T![,]);714715    res.active_parameter = {716        Some(if is_left_of_rest_pat {717            commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count()718        } else {719            let n_commas = commas720                .collect::<Vec<_>>()721                .into_iter()722                .rev()723                .take_while(|t| t.text_range().start() > token.text_range().start())724                .count();725            fields.len().saturating_sub(1).saturating_sub(n_commas)726        })727    };728729    let mut buf = String::new();730    for ty in fields {731        format_to!(buf, "{}", ty.display_truncated(db, Some(20), display_target));732        res.push_call_param(&buf);733        buf.clear();734    }735    res.signature.push(')');736    res737}738#[cfg(test)]739mod tests {740741    use expect_test::{Expect, expect};742    use ide_db::FilePosition;743    use stdx::format_to;744    use test_fixture::ChangeFixture;745746    use crate::RootDatabase;747748    /// Creates analysis from a multi-file fixture, returns positions marked with $0.749    pub(crate) fn position(750        #[rust_analyzer::rust_fixture] ra_fixture: &str,751    ) -> (RootDatabase, FilePosition) {752        let mut database = RootDatabase::default();753        let change_fixture = ChangeFixture::parse(ra_fixture);754        database.apply_change(change_fixture.change);755        let (file_id, range_or_offset) =756            change_fixture.file_position.expect("expected a marker ($0)");757        let offset = range_or_offset.expect_offset();758        let position = FilePosition { file_id: file_id.file_id(), offset };759        (database, position)760    }761762    #[track_caller]763    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {764        let (db, position) = position(ra_fixture);765        let sig_help = hir::attach_db(&db, || crate::signature_help::signature_help(&db, position));766        let actual = match sig_help {767            Some(sig_help) => {768                let mut rendered = String::new();769                if let Some(docs) = &sig_help.doc {770                    format_to!(rendered, "{}\n------\n", docs.as_str());771                }772                format_to!(rendered, "{}\n", sig_help.signature);773                let mut offset = 0;774                for (i, range) in sig_help.parameter_ranges().iter().enumerate() {775                    let is_active = sig_help.active_parameter == Some(i);776777                    let start = u32::from(range.start());778                    let gap = start.checked_sub(offset).unwrap_or_else(|| {779                        panic!("parameter ranges out of order: {:?}", sig_help.parameter_ranges())780                    });781                    rendered.extend(std::iter::repeat_n(' ', gap as usize));782                    let param_text = &sig_help.signature[*range];783                    let width = param_text.chars().count(); // …784                    let marker = if is_active { '^' } else { '-' };785                    rendered.extend(std::iter::repeat_n(marker, width));786                    offset += gap + u32::from(range.len());787                }788                if !sig_help.parameter_ranges().is_empty() {789                    format_to!(rendered, "\n");790                }791                rendered792            }793            None => String::new(),794        };795        expect.assert_eq(&actual);796    }797798    #[test]799    fn test_fn_signature_two_args() {800        check(801            r#"802//- minicore: sized, fn803fn foo(x: u32, y: u32) -> u32 {x + y}804fn bar() { foo($03, ); }805"#,806            expect![[r#"807                fn foo(x: u32, y: u32) -> u32808                       ^^^^^^  ------809            "#]],810        );811        check(812            r#"813//- minicore: sized, fn814fn foo(x: u32, y: u32) -> u32 {x + y}815fn bar() { foo(3$0, ); }816"#,817            expect![[r#"818                fn foo(x: u32, y: u32) -> u32819                       ^^^^^^  ------820            "#]],821        );822        check(823            r#"824//- minicore: sized, fn825fn foo(x: u32, y: u32) -> u32 {x + y}826fn bar() { foo(3,$0 ); }827"#,828            expect![[r#"829                fn foo(x: u32, y: u32) -> u32830                       ------  ^^^^^^831            "#]],832        );833        check(834            r#"835//- minicore: sized, fn836fn foo(x: u32, y: u32) -> u32 {x + y}837fn bar() { foo(3, $0); }838"#,839            expect![[r#"840                fn foo(x: u32, y: u32) -> u32841                       ------  ^^^^^^842            "#]],843        );844    }845846    #[test]847    fn test_fn_signature_two_args_empty() {848        check(849            r#"850//- minicore: sized, fn851fn foo(x: u32, y: u32) -> u32 {x + y}852fn bar() { foo($0); }853"#,854            expect![[r#"855                fn foo(x: u32, y: u32) -> u32856                       ^^^^^^  ------857            "#]],858        );859    }860861    #[test]862    fn test_fn_signature_two_args_first_generics() {863        check(864            r#"865//- minicore: sized, fn866fn foo<T, U: Copy + Display>(x: T, y: U) -> u32867    where T: Copy + Display, U: Debug868{ x + y }869870fn bar() { foo($03, ); }871"#,872            expect![[r#"873                fn foo<T, U>(x: i32, y: U) -> u32874                             ^^^^^^  ----875            "#]],876        );877    }878879    #[test]880    fn test_fn_signature_no_params() {881        check(882            r#"883//- minicore: sized, fn884fn foo<T>() -> T where T: Copy + Display {}885fn bar() { foo($0); }886"#,887            expect![[r#"888                fn foo<T>() -> T889            "#]],890        );891    }892893    #[test]894    fn test_fn_signature_for_impl() {895        check(896            r#"897//- minicore: sized, fn898struct F;899impl F { pub fn new() { } }900fn bar() {901    let _ : F = F::new($0);902}903"#,904            expect![[r#"905                fn new()906            "#]],907        );908    }909910    #[test]911    fn test_fn_signature_for_method_self() {912        check(913            r#"914//- minicore: sized, fn915struct S;916impl S { pub fn do_it(&self) {} }917918fn bar() {919    let s: S = S;920    s.do_it($0);921}922"#,923            expect![[r#"924                fn do_it(&self)925            "#]],926        );927    }928929    #[test]930    fn test_fn_signature_for_method_with_arg() {931        check(932            r#"933//- minicore: sized, fn934struct S;935impl S {936    fn foo(&self, x: i32) {}937}938939fn main() { S.foo($0); }940"#,941            expect![[r#"942                fn foo(&self, x: i32)943                              ^^^^^^944            "#]],945        );946    }947948    #[test]949    fn test_fn_signature_for_generic_method() {950        check(951            r#"952//- minicore: sized, fn953struct S<T>(T);954impl<T> S<T> {955    fn foo(&self, x: T) {}956}957958fn main() { S(1u32).foo($0); }959"#,960            expect![[r#"961                fn foo(&self, x: u32)962                              ^^^^^^963            "#]],964        );965    }966967    #[test]968    fn test_fn_signature_for_method_with_arg_as_assoc_fn() {969        check(970            r#"971//- minicore: sized, fn972struct S;973impl S {974    fn foo(&self, x: i32) {}975}976977fn main() { S::foo($0); }978"#,979            expect![[r#"980                fn foo(self: &S, x: i32)981                       ^^^^^^^^  ------982            "#]],983        );984    }985986    #[test]987    fn test_fn_signature_with_docs_simple() {988        check(989            r#"990//- minicore: sized, fn991/// test992// non-doc-comment993fn foo(j: u32) -> u32 {994    j995}996997fn bar() {998    let _ = foo($0);999}1000"#,1001            expect![[r#"1002                test1003                ------1004                fn foo(j: u32) -> u321005                       ^^^^^^1006            "#]],1007        );1008    }10091010    #[test]1011    fn test_fn_signature_with_docs() {1012        check(1013            r#"1014//- minicore: sized, fn1015/// Adds one to the number given.1016///1017/// # Examples1018///1019/// ```1020/// let five = 5;1021///1022/// assert_eq!(6, my_crate::add_one(5));1023/// ```1024pub fn add_one(x: i32) -> i32 {1025    x + 11026}10271028pub fn r#do() {1029    add_one($01030}"#,1031            expect![[r##"1032                Adds one to the number given.10331034                # Examples10351036                ```1037                let five = 5;10381039                assert_eq!(6, my_crate::add_one(5));1040                ```1041                ------1042                fn add_one(x: i32) -> i321043                           ^^^^^^1044            "##]],1045        );1046    }10471048    #[test]1049    fn test_fn_signature_with_docs_impl() {1050        check(1051            r#"1052//- minicore: sized, fn1053struct addr;1054impl addr {1055    /// Adds one to the number given.1056    ///1057    /// # Examples1058    ///1059    /// ```1060    /// let five = 5;1061    ///1062    /// assert_eq!(6, my_crate::add_one(5));1063    /// ```1064    pub fn add_one(x: i32) -> i32 {1065        x + 11066    }1067}10681069pub fn do_it() {1070    addr {};1071    addr::add_one($0);1072}1073"#,1074            expect![[r##"1075                Adds one to the number given.10761077                # Examples10781079                ```1080                let five = 5;10811082                assert_eq!(6, my_crate::add_one(5));1083                ```1084                ------1085                fn add_one(x: i32) -> i321086                           ^^^^^^1087            "##]],1088        );1089    }10901091    #[test]1092    fn test_fn_signature_with_docs_from_actix() {1093        check(1094            r#"1095//- minicore: sized, fn1096trait Actor {1097    /// Actor execution context type1098    type Context;1099}1100trait WriteHandler<E>1101where1102    Self: Actor1103{1104    /// Method is called when writer finishes.1105    ///1106    /// By default this method stops actor's `Context`.1107    fn finished(&mut self, ctx: &mut Self::Context) {}1108}11091110fn foo(mut r: impl WriteHandler<()>) {1111    r.finished($0);1112}1113"#,1114            expect![[r#"1115                Method is called when writer finishes.11161117                By default this method stops actor's `Context`.1118                ------1119                fn finished(&mut self, ctx: &mut <impl WriteHandler<()> as Actor>::Context)1120                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^1121            "#]],1122        );1123    }11241125    #[test]1126    fn call_info_bad_offset() {1127        check(1128            r#"1129//- minicore: sized, fn1130fn foo(x: u32, y: u32) -> u32 {x + y}1131fn bar() { foo $0 (3, ); }1132"#,1133            expect![[""]],1134        );1135    }11361137    #[test]1138    fn outside_of_arg_list() {1139        check(1140            r#"1141//- minicore: sized, fn1142fn foo(a: u8) {}1143fn f() {1144    foo(123)$01145}1146"#,1147            expect![[]],1148        );1149        check(1150            r#"1151//- minicore: sized, fn1152fn foo<T>(a: u8) {}1153fn f() {1154    foo::<u32>$0()1155}1156"#,1157            expect![[]],1158        );1159        check(1160            r#"1161//- minicore: sized, fn1162fn foo(a: u8) -> u8 {a}1163fn bar(a: u8) -> u8 {a}1164fn f() {1165    foo(bar(123)$0)1166}1167"#,1168            expect![[r#"1169                fn foo(a: u8) -> u81170                       ^^^^^1171            "#]],1172        );1173        check(1174            r#"1175//- minicore: sized, fn1176struct Vec<T>(T);1177struct Vec2<T>(T);1178fn f() {1179    let _: Vec2<Vec<u8>$0>1180}1181"#,1182            expect![[r#"1183                struct Vec2<T>1184                            ^1185            "#]],1186        );1187    }11881189    #[test]1190    fn test_nested_method_in_lambda() {1191        check(1192            r#"1193//- minicore: sized, fn1194struct Foo;1195impl Foo { fn bar(&self, _: u32) { } }11961197fn bar(_: u32) { }11981199fn main() {1200    let foo = Foo;1201    std::thread::spawn(move || foo.bar($0));1202}1203"#,1204            expect![[r#"1205                fn bar(&self, _: u32)1206                              ^^^^^^1207            "#]],1208        );1209    }12101211    #[test]1212    fn works_for_tuple_structs() {1213        check(1214            r#"1215//- minicore: sized, fn1216/// A cool tuple struct1217struct S(u32, i32);1218fn main() {1219    let s = S(0, $0);1220}1221"#,1222            expect![[r#"1223                A cool tuple struct1224                ------1225                struct S(u32, i32)1226                         ---  ^^^1227            "#]],1228        );1229    }12301231    #[test]1232    fn tuple_struct_pat() {1233        check(1234            r#"1235//- minicore: sized, fn1236/// A cool tuple struct1237struct S(u32, i32);1238fn main() {1239    let S(0, $0);1240}1241"#,1242            expect![[r#"1243                A cool tuple struct1244                ------1245                struct S (u32, i32)1246                          ---  ^^^1247            "#]],1248        );1249    }12501251    #[test]1252    fn tuple_struct_pat_rest() {1253        check(1254            r#"1255//- minicore: sized, fn1256/// A cool tuple struct1257struct S(u32, i32, f32, u16);1258fn main() {1259    let S(0, .., $0);1260}1261"#,1262            expect![[r#"1263                A cool tuple struct1264                ------1265                struct S (u32, i32, f32, u16)1266                          ---  ---  ---  ^^^1267            "#]],1268        );1269        check(1270            r#"1271//- minicore: sized, fn1272/// A cool tuple struct1273struct S(u32, i32, f32, u16, u8);1274fn main() {1275    let S(0, .., $0, 0);1276}1277"#,1278            expect![[r#"1279                A cool tuple struct1280                ------1281                struct S (u32, i32, f32, u16, u8)1282                          ---  ---  ---  ^^^  --1283            "#]],1284        );1285        check(1286            r#"1287//- minicore: sized, fn1288/// A cool tuple struct1289struct S(u32, i32, f32, u16);1290fn main() {1291    let S($0, .., 1);1292}1293"#,1294            expect![[r#"1295                A cool tuple struct1296                ------1297                struct S (u32, i32, f32, u16)1298                          ^^^  ---  ---  ---1299            "#]],1300        );1301        check(1302            r#"1303//- minicore: sized, fn1304/// A cool tuple struct1305struct S(u32, i32, f32, u16, u8);1306fn main() {1307    let S(1, .., 1, $0, 2);1308}1309"#,1310            expect![[r#"1311                A cool tuple struct1312                ------1313                struct S (u32, i32, f32, u16, u8)1314                          ---  ---  ---  ^^^  --1315            "#]],1316        );1317        check(1318            r#"1319//- minicore: sized, fn1320/// A cool tuple struct1321struct S(u32, i32, f32, u16);1322fn main() {1323    let S(1, $0.., 1);1324}1325"#,1326            expect![[r#"1327                A cool tuple struct1328                ------1329                struct S (u32, i32, f32, u16)1330                          ---  ^^^  ---  ---1331            "#]],1332        );1333        check(1334            r#"1335//- minicore: sized, fn1336/// A cool tuple struct1337struct S(u32, i32, f32, u16);1338fn main() {1339    let S(1, ..$0, 1);1340}1341"#,1342            expect![[r#"1343                A cool tuple struct1344                ------1345                struct S (u32, i32, f32, u16)1346                          ---  ^^^  ---  ---1347            "#]],1348        );1349    }13501351    #[test]1352    fn generic_struct() {1353        check(1354            r#"1355//- minicore: sized, fn1356struct S<T>(T);1357fn main() {1358    let s = S($0);1359}1360"#,1361            expect![[r#"1362                struct S<T>({unknown})1363                            ^^^^^^^^^1364            "#]],1365        );1366    }13671368    #[test]1369    fn works_for_enum_variants() {1370        check(1371            r#"1372//- minicore: sized, fn1373enum E {1374    /// A Variant1375    A(i32),1376    /// Another1377    B,1378    /// And C1379    C { a: i32, b: i32 }1380}13811382fn main() {1383    let a = E::A($0);1384}1385"#,1386            expect![[r#"1387                A Variant1388                ------1389                enum E::A(i32)1390                          ^^^1391            "#]],1392        );1393    }13941395    #[test]1396    fn cant_call_struct_record() {1397        check(1398            r#"1399//- minicore: sized, fn1400struct S { x: u32, y: i32 }1401fn main() {1402    let s = S($0);1403}1404"#,1405            expect![[""]],1406        );1407    }14081409    #[test]1410    fn cant_call_enum_record() {1411        check(1412            r#"1413//- minicore: sized, fn1414enum E {1415    /// A Variant1416    A(i32),1417    /// Another1418    B,1419    /// And C1420    C { a: i32, b: i32 }1421}14221423fn main() {1424    let a = E::C($0);1425}1426"#,1427            expect![[""]],1428        );1429    }14301431    #[test]1432    fn fn_signature_for_call_in_macro() {1433        check(1434            r#"1435//- minicore: sized, fn1436macro_rules! id { ($($tt:tt)*) => { $($tt)* } }1437fn foo() { }1438id! {1439    fn bar() { foo($0); }1440}1441"#,1442            expect![[r#"1443                fn foo()1444            "#]],1445        );1446    }14471448    #[test]1449    fn fn_signature_for_method_call_defined_in_macro() {1450        check(1451            r#"1452//- minicore: sized, fn1453macro_rules! id { ($($tt:tt)*) => { $($tt)* } }1454struct S;1455id! {1456    impl S {1457        fn foo<'a>(&'a mut self) {}1458    }1459}1460fn test() { S.foo($0); }1461"#,1462            expect![[r#"1463                fn foo<'a>(&'a mut self)1464            "#]],1465        );1466    }14671468    #[test]1469    fn call_info_for_lambdas() {1470        check(1471            r#"1472//- minicore: sized, fn1473struct S;1474fn foo(s: S) -> i32 { 92 }1475fn main() {1476    let _move = S;1477    (|s| {{_move}; foo(s)})($0)1478}1479        "#,1480            expect![[r#"1481                impl FnOnce(s: S) -> i321482                            ^^^^1483            "#]],1484        );1485        check(1486            r#"1487//- minicore: sized, fn1488struct S;1489fn foo(s: S) -> i32 { 92 }1490fn main() {1491    (|s| foo(s))($0)1492}1493        "#,1494            expect![[r#"1495                impl Fn(s: S) -> i321496                        ^^^^1497            "#]],1498        );1499        check(1500            r#"1501//- minicore: sized, fn1502struct S;1503fn foo(s: S) -> i32 { 92 }1504fn main() {1505    let mut mutate = 0;1506    (|s| { mutate = 1; foo(s) })($0)1507}1508        "#,1509            expect![[r#"1510                impl FnMut(s: S) -> i321511                           ^^^^1512            "#]],1513        );1514    }15151516    #[test]1517    fn call_info_for_fn_def_over_reference() {1518        check(1519            r#"1520//- minicore: sized, fn1521struct S;1522fn foo(s: S) -> i32 { 92 }1523fn main() {1524    let bar = &&&&&foo;1525    bar($0);1526}1527        "#,1528            expect![[r#"1529                fn foo(s: S) -> i321530                       ^^^^1531            "#]],1532        )1533    }15341535    #[test]1536    fn call_info_for_fn_ptr() {1537        check(1538            r#"1539//- minicore: sized, fn1540fn main(f: fn(i32, f64) -> char) {1541    f(0, $0)1542}1543        "#,1544            expect![[r#"1545                fn(i32, f64) -> char1546                   ---  ^^^1547            "#]],1548        )1549    }15501551    #[test]1552    fn call_info_for_fn_impl() {1553        check(1554            r#"1555//- minicore: sized, fn1556struct S;1557impl core::ops::FnOnce<(i32, f64)> for S {1558    type Output = char;1559}1560impl core::ops::FnMut<(i32, f64)> for S {}1561impl core::ops::Fn<(i32, f64)> for S {}1562fn main() {1563    S($0);1564}1565        "#,1566            expect![[r#"1567                <S as Fn>::call(i32, f64) -> char1568                                ^^^  ---1569            "#]],1570        );1571        check(1572            r#"1573//- minicore: sized, fn1574struct S;1575impl core::ops::FnOnce<(i32, f64)> for S {1576    type Output = char;1577}1578impl core::ops::FnMut<(i32, f64)> for S {}1579impl core::ops::Fn<(i32, f64)> for S {}1580fn main() {1581    S(1, $0);1582}1583        "#,1584            expect![[r#"1585                <S as Fn>::call(i32, f64) -> char1586                                ---  ^^^1587            "#]],1588        );1589        check(1590            r#"1591//- minicore: sized, fn1592struct S;1593impl core::ops::FnOnce<(i32, f64)> for S {1594    type Output = char;1595}1596impl core::ops::FnOnce<(char, char)> for S {1597    type Output = f64;1598}1599fn main() {1600    S($0);1601}1602        "#,1603            expect![""],1604        );1605        check(1606            r#"1607//- minicore: sized, fn1608struct S;1609impl core::ops::FnOnce<(i32, f64)> for S {1610    type Output = char;1611}1612impl core::ops::FnOnce<(char, char)> for S {1613    type Output = f64;1614}1615fn main() {1616    // FIXME: The ide layer loses the calling info here so we get an ambiguous trait solve result1617    S(0i32, $0);1618}1619        "#,1620            expect![""],1621        );1622    }16231624    #[test]1625    fn call_info_for_unclosed_call() {1626        check(1627            r#"1628//- minicore: sized, fn1629fn foo(foo: u32, bar: u32) {}1630fn main() {1631    foo($01632}"#,1633            expect![[r#"1634                fn foo(foo: u32, bar: u32)1635                       ^^^^^^^^  --------1636            "#]],1637        );1638        // check with surrounding space1639        check(1640            r#"1641//- minicore: sized, fn1642fn foo(foo: u32, bar: u32) {}1643fn main() {1644    foo( $01645}"#,1646            expect![[r#"1647                fn foo(foo: u32, bar: u32)1648                       ^^^^^^^^  --------1649            "#]],1650        )1651    }16521653    #[test]1654    fn test_multiline_argument() {1655        check(1656            r#"1657//- minicore: sized, fn1658fn callee(a: u8, b: u8) {}1659fn main() {1660    callee(match 0 {1661        0 => 1,$01662    })1663}"#,1664            expect![[r#""#]],1665        );1666        check(1667            r#"1668//- minicore: sized, fn1669fn callee(a: u8, b: u8) {}1670fn main() {1671    callee(match 0 {1672        0 => 1,1673    },$0)1674}"#,1675            expect![[r#"1676                fn callee(a: u8, b: u8)1677                          -----  ^^^^^1678            "#]],1679        );1680        check(1681            r#"1682//- minicore: sized, fn1683fn callee(a: u8, b: u8) {}1684fn main() {1685    callee($0match 0 {1686        0 => 1,1687    })1688}"#,1689            expect![[r#"1690                fn callee(a: u8, b: u8)1691                          ^^^^^  -----1692            "#]],1693        );1694    }16951696    #[test]1697    fn test_generics_simple() {1698        check(1699            r#"1700//- minicore: sized, fn1701/// Option docs.1702enum Option<T> {1703    Some(T),1704    None,1705}17061707fn f() {1708    let opt: Option<$01709}1710        "#,1711            expect![[r#"1712                Option docs.1713                ------1714                enum Option<T>1715                            ^1716            "#]],1717        );1718    }17191720    #[test]1721    fn test_generics_on_variant() {1722        check(1723            r#"1724//- minicore: sized, fn1725/// Option docs.1726enum Option<T> {1727    /// Some docs.1728    Some(T),1729    /// None docs.1730    None,1731}17321733use Option::*;17341735fn f() {1736    None::<$01737}1738        "#,1739            expect![[r#"1740                None docs.1741                ------1742                enum Option<T>1743                            ^1744            "#]],1745        );1746    }17471748    #[test]1749    fn test_lots_of_generics() {1750        check(1751            r#"1752//- minicore: sized, fn1753trait Tr<T> {}17541755struct S<T>(T);17561757impl<T> S<T> {1758    fn f<G, H>(g: G, h: impl Tr<G>) where G: Tr<()> {}1759}17601761fn f() {1762    S::<u8>::f::<(), $01763}1764        "#,1765            expect![[r#"1766                fn f<G: Tr<()>, H>1767                     ---------  ^1768            "#]],1769        );1770    }17711772    #[test]1773    fn test_generics_in_trait_ufcs() {1774        check(1775            r#"1776//- minicore: sized, fn1777trait Tr {1778    fn f<T: Tr, U>() {}1779}17801781struct S;17821783impl Tr for S {}17841785fn f() {1786    <S as Tr>::f::<$01787}1788        "#,1789            expect![[r#"1790                fn f<T: Tr, U>1791                     ^^^^^  -1792            "#]],1793        );1794    }17951796    #[test]1797    fn test_generics_in_method_call() {1798        check(1799            r#"1800//- minicore: sized, fn1801struct S;18021803impl S {1804    fn f<T>(&self) {}1805}18061807fn f() {1808    S.f::<$01809}1810        "#,1811            expect![[r#"1812                fn f<T>1813                     ^1814            "#]],1815        );1816    }18171818    #[test]1819    fn test_generic_param_in_method_call() {1820        check(1821            r#"1822//- minicore: sized, fn1823struct Foo;1824impl Foo {1825    fn test<V>(&mut self, val: V) {}1826}1827fn sup() {1828    Foo.test($0)1829}1830"#,1831            expect![[r#"1832                fn test<V>(&mut self, val: V)1833                                      ^^^^^^1834            "#]],1835        );1836    }18371838    #[test]1839    fn test_generic_kinds() {1840        check(1841            r#"1842//- minicore: sized, fn1843fn callee<'a, const A: u8, T, const C: u8>() {}18441845fn f() {1846    callee::<'static, $01847}1848        "#,1849            expect![[r#"1850                fn callee<'a, const A: u8, T, const C: u8>1851                          --  ^^^^^^^^^^^  -  -----------1852            "#]],1853        );1854        check(1855            r#"1856//- minicore: sized, fn1857fn callee<'a, const A: u8, T, const C: u8>() {}18581859fn f() {1860    callee::<NON_LIFETIME$01861}1862        "#,1863            expect![[r#"1864                fn callee<'a, const A: u8, T, const C: u8>1865                          --  ^^^^^^^^^^^  -  -----------1866            "#]],1867        );1868    }18691870    #[test]1871    fn test_trait_assoc_types() {1872        check(1873            r#"1874//- minicore: sized, fn1875trait Trait<'a, T> {1876    type Assoc;1877}1878fn f() -> impl Trait<(), $01879            "#,1880            expect![[r#"1881                trait Trait<'a, T, Assoc = …>1882                            --  -  ^^^^^^^^^1883            "#]],1884        );1885        check(1886            r#"1887//- minicore: sized, fn1888trait Iterator {1889    type Item;1890}1891fn f() -> impl Iterator<$01892            "#,1893            expect![[r#"1894                trait Iterator<Item = …>1895                               ^^^^^^^^1896            "#]],1897        );1898        check(1899            r#"1900//- minicore: sized, fn1901trait Iterator {1902    type Item;1903}1904fn f() -> impl Iterator<Item = $01905            "#,1906            expect![[r#"1907                trait Iterator<Item = …>1908                               ^^^^^^^^1909            "#]],1910        );1911        check(1912            r#"1913//- minicore: sized, fn1914trait Tr {1915    type A;1916    type B;1917}1918fn f() -> impl Tr<$01919            "#,1920            expect![[r#"1921                trait Tr<A = …, B = …>1922                         ^^^^^  -----1923            "#]],1924        );1925        check(1926            r#"1927//- minicore: sized, fn1928trait Tr {1929    type A;1930    type B;1931}1932fn f() -> impl Tr<B$01933            "#,1934            expect![[r#"1935                trait Tr<A = …, B = …>1936                         ^^^^^  -----1937            "#]],1938        );1939        check(1940            r#"1941//- minicore: sized, fn1942trait Tr {1943    type A;1944    type B;1945}1946fn f() -> impl Tr<B = $01947            "#,1948            expect![[r#"1949                trait Tr<B = …, A = …>1950                         ^^^^^  -----1951            "#]],1952        );1953        check(1954            r#"1955//- minicore: sized, fn1956trait Tr {1957    type A;1958    type B;1959}1960fn f() -> impl Tr<B = (), $01961            "#,1962            expect![[r#"1963                trait Tr<B = …, A = …>1964                         -----  ^^^^^1965            "#]],1966        );1967    }19681969    #[test]1970    fn test_supertrait_assoc() {1971        check(1972            r#"1973//- minicore: sized, fn1974trait Super {1975    type SuperTy;1976}1977trait Sub: Super + Super {1978    type SubTy;1979}1980fn f() -> impl Sub<$01981            "#,1982            expect![[r#"1983                trait Sub<SubTy = …, SuperTy = …>1984                          ^^^^^^^^^  -----------1985            "#]],1986        );1987    }19881989    #[test]1990    fn no_assoc_types_outside_type_bounds() {1991        check(1992            r#"1993//- minicore: sized, fn1994trait Tr<T> {1995    type Assoc;1996}19971998impl Tr<$01999        "#,2000            expect![[r#"

Code quality findings 30

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_to!(res.signature, "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
pub unsafe fn foo(x: u32, y: u32) -> u32 { x + y }
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
unsafe { foo($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
unsafe fn foo(x: u32, y: u32) -> u32
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
pub const unsafe fn foo(x: u32, y: u32) -> u32 { x + y }
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
unsafe { foo($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
const unsafe fn foo(x: u32, y: u32) -> u32
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
pub async unsafe fn foo(x: u32, y: u32) -> u32 { x + y }
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
unsafe { foo($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
async unsafe fn foo(x: u32, y: u32) -> u32
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
self.parameters.iter().map(move |&it| &self.signature[it])
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
format_to!(buf, "{}", fn_params[idx].ty().display(db, display_target))
Warning: '.expect()' will panic with a custom message on None/Err. While better than unwrap() for debugging, prefer non-panicking error handling in production code (match, if let, ?).
warning correctness expect-usage
change_fixture.file_position.expect("expected a marker ($0)");
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 param_text = &sig_help.signature[*range];
Warning: Ignoring a Result or Option using 'let _ =' can hide errors or unexpected None values. Ensure the value is handled appropriately (match, if let, ?, expect) unless intentionally discarded with justification.
warning correctness discarded-result
let _ = foo($0);
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
res.signature.push('(');
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
if let Some(param) = sema.source(p.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
match (p.ty().contains_unknown(), fn_params.as_deref()) {
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
res.signature.push('<');
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
.filter_map(|arg| match arg {
Performance Info: Calling .to_string() (especially on &str) allocates a new String. If done repeatedly in loops, consider alternatives like working with &str or using crates like `itoa`/`ryu` for number-to-string conversion.
info performance to-string-in-loop
ast::GenericArg::AssocTypeArg(arg) => arg.name_ref().map(|n| n.to_string()),
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 adt = match path_res {
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 adt {
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
res.signature.push(')');
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 adt = match path_res {
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 adt {
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
res.signature.push(')');
Info: Raw thread spawned using std::thread::spawn. Ensure the JoinHandle is managed (usually by calling .join()) to wait for completion and handle potential panics, unless intentionally detached.
info correctness thread-spawn-unjoined
std::thread::spawn(move || foo.bar($0));
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use Option::*;
Info: Raw thread spawned using std::thread::spawn. Ensure the JoinHandle is managed (usually by calling .join()) to wait for completion and handle potential panics, unless intentionally detached.
info correctness thread-spawn-unjoined
std::thread::spawn(move || { bar(A:$0) } );

Get this view in your editor

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