Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
format_to!(res.signature, "unsafe ");
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#"
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.