File is large — showing lines 1–2,000 of 4,260.
1use std::{iter, mem::discriminant};23use crate::Analysis;4use crate::{5 FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult,6 doc_links::token_as_doc_comment,7 navigation_target::{self, ToNav},8};9use hir::{10 AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, ModuleDef, Semantics, sym,11};12use ide_db::ra_fixture::{RaFixtureConfig, UpmapFromRaFixture};13use ide_db::{14 RootDatabase, SymbolKind,15 base_db::{AnchoredPath, SourceDatabase},16 defs::{Definition, IdentClass},17 famous_defs::FamousDefs,18 helpers::pick_best_token,19 syntax_helpers::node_ext::find_loops,20};21use itertools::Itertools;22use span::FileId;23use syntax::{24 AstNode, AstToken, SyntaxKind::*, SyntaxNode, SyntaxToken, T, TextRange, ast, match_ast,25};2627#[derive(Debug)]28pub struct GotoDefinitionConfig<'a> {29 pub ra_fixture: RaFixtureConfig<'a>,30}3132// Feature: Go to Definition33//34// Navigates to the definition of an identifier.35//36// For outline modules, this will navigate to the source file of the module.37//38// | Editor | Shortcut |39// |---------|----------|40// | VS Code | <kbd>F12</kbd> |41//42// 43//44// #### Special Go to Definitions45//46// You can go to definition on operators and keywords as well. The behavior goes as follows:47//48// - On overloadable operators, this will take you to the `impl` of the operator's trait for this type, or to the trait if49// the impl cannot be determined.50// - For `?` on `Result` that goes through a non-trivial `From` (i.e. not the blanket `impl<T> From<T> for T`), it'll take51// you to the `From` impl.52// - On control flow keywords (loops, conditions, etc.) and `fn`, it'll take you to all exit points for this construct53// or its entrance, the opposite of the keyword you're at (e.g. on `fn` it'll take you to all exit points, and on `return`54// it'll take you to the `fn`).55// - It'll skip known blanket impls from the standard library where possible. For example, on a `try_into()` that comes56// from the blanket `impl<T: TryFrom<U>, U> TryInto<T> for U`, it'll take you to the `TryFrom` impl, and if it also57// comes from the blanket `impl<T: From<U>, U> TryFrom<U> for T`, it'll take you to the `From` impl.58pub(crate) fn goto_definition(59 db: &RootDatabase,60 FilePosition { file_id, offset }: FilePosition,61 config: &GotoDefinitionConfig<'_>,62) -> Option<RangeInfo<Vec<NavigationTarget>>> {63 let sema = &Semantics::new(db);64 let file = sema.parse_guess_edition(file_id).syntax().clone();65 let edition = sema.attach_first_edition(file_id).edition(db);66 let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {67 IDENT68 | INT_NUMBER69 | LIFETIME_IDENT70 | T![self]71 | T![super]72 | T![crate]73 | T![Self]74 | COMMENT75 | INNER_DOC_COMMENT76 | OUTER_DOC_COMMENT => 4,77 // index and prefix ops78 T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,79 kind if kind.is_keyword(edition) => 2,80 T!['('] | T![')'] => 2,81 kind if kind.is_trivia() => 0,82 _ => 1,83 })?;84 if let Some(doc_comment) = token_as_doc_comment(&original_token) {85 return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, link_range| {86 let nav = def.try_to_nav(sema)?;87 Some(RangeInfo::new(link_range, nav.collect()))88 });89 }9091 if let Some((range, _, _, resolution)) =92 sema.check_for_format_args_template(original_token.clone(), offset)93 {94 return Some(RangeInfo::new(95 range,96 match resolution {97 Some(res) => def_to_nav(sema, Definition::from(res)),98 None => vec![],99 },100 ));101 }102103 if let Some(navs) = handle_control_flow_keywords(sema, &original_token) {104 return Some(RangeInfo::new(original_token.text_range(), navs));105 }106107 let tokens = sema.descend_into_macros_no_opaque(original_token.clone(), false);108 let mut navs = Vec::new();109 for token in tokens {110 if let Some(n) = find_definition_for_known_blanket_dual_impls(sema, &token.value) {111 navs.extend(n);112 continue;113 }114115 if let Some(n) = find_definition_for_comparison_operators(sema, &token.value) {116 navs.extend(n);117 continue;118 }119120 let parent = token.value.parent()?;121122 if let Some(question_mark_conversion) = goto_question_mark_conversions(sema, &parent) {123 navs.extend(def_to_nav(sema, question_mark_conversion.into()));124 continue;125 }126127 if let Some(token) = ast::String::cast(token.value.clone())128 && let Some(original_token) = ast::String::cast(original_token.clone())129 && let Some((analysis, fixture_analysis)) =130 Analysis::from_ra_fixture(sema, original_token, &token, &config.ra_fixture)131 && let Some((virtual_file_id, file_offset)) = fixture_analysis.map_offset_down(offset)132 {133 return hir::attach_db_allow_change(&analysis.db, || {134 goto_definition(135 &analysis.db,136 FilePosition { file_id: virtual_file_id, offset: file_offset },137 config,138 )139 })140 .and_then(|navs| {141 navs.upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, file_id).ok()142 });143 }144145 let token_file_id = token.file_id;146 if let Some(token) = ast::String::cast(token.value.clone())147 && let Some(x) =148 try_lookup_include_path(sema, InFile::new(token_file_id, token), file_id)149 {150 navs.push(x);151 continue;152 }153154 if ast::TokenTree::can_cast(parent.kind())155 && let Some(x) = try_lookup_macro_def_in_macro_use(sema, token.value)156 {157 navs.push(x);158 continue;159 }160161 let Some(ident_class) = IdentClass::classify_node(sema, &parent) else { continue };162 navs.extend(ident_class.definitions().into_iter().flat_map(|(def, _)| {163 if let Definition::ExternCrateDecl(crate_def) = def {164 return crate_def165 .resolved_crate(db)166 .map(|it| it.root_module(db).to_nav(db))167 .into_iter()168 .flatten()169 .collect();170 }171 try_filter_trait_item_definition(sema, &def).unwrap_or_else(|| def_to_nav(sema, def))172 }));173 }174 let navs = navs.into_iter().unique().collect();175176 Some(RangeInfo::new(original_token.text_range(), navs))177}178179/// When the `?` operator is used on `Result`, go to the `From` impl if it exists as this provides more value.180fn goto_question_mark_conversions(181 sema: &Semantics<'_, RootDatabase>,182 node: &SyntaxNode,183) -> Option<hir::Function> {184 let node = ast::TryExpr::cast(node.clone())?;185 let try_expr_ty = sema.type_of_expr(&node.expr()?)?.adjusted();186187 let fd = FamousDefs(sema, try_expr_ty.krate(sema.db));188 let result_enum = fd.core_result_Result()?.into();189190 let (try_expr_ty_adt, try_expr_ty_args) = try_expr_ty.as_adt_with_args()?;191 if try_expr_ty_adt != result_enum {192 // FIXME: Support `Poll<Result>`.193 return None;194 }195 let original_err_ty = try_expr_ty_args.get(1)?.clone()?;196197 let returned_ty = sema.try_expr_returned_type(&node)?;198 let (returned_adt, returned_ty_args) = returned_ty.as_adt_with_args()?;199 if returned_adt != result_enum {200 return None;201 }202 let returned_err_ty = returned_ty_args.get(1)?.clone()?;203204 if returned_err_ty.could_unify_with_deeply(sema.db, &original_err_ty) {205 return None;206 }207208 let from_trait = fd.core_convert_From()?;209 let from_fn = from_trait.function(sema.db, sym::from)?;210 sema.resolve_trait_impl_method(211 returned_err_ty.clone(),212 from_trait,213 from_fn,214 [returned_err_ty, original_err_ty],215 )216}217218// If the token is into(), try_into(), search the definition of From, TryFrom.219fn find_definition_for_known_blanket_dual_impls(220 sema: &Semantics<'_, RootDatabase>,221 original_token: &SyntaxToken,222) -> Option<Vec<NavigationTarget>> {223 let method_call = ast::MethodCallExpr::cast(original_token.parent()?.parent()?)?;224 let callable = sema.resolve_method_call_as_callable(&method_call)?;225 let CallableKind::Function(f) = callable.kind() else { return None };226 let assoc = f.as_assoc_item(sema.db)?;227228 let return_type = callable.return_type();229 let fd = FamousDefs(sema, return_type.krate(sema.db));230231 let t = match assoc.container(sema.db) {232 hir::AssocItemContainer::Trait(t) => t,233 hir::AssocItemContainer::Impl(impl_)234 if impl_.self_ty(sema.db).is_str() && f.name(sema.db) == sym::parse =>235 {236 let t = fd.core_convert_FromStr()?;237 let t_f = t.function(sema.db, &sym::from_str)?;238 return sema239 .resolve_trait_impl_method(240 return_type.clone(),241 t,242 t_f,243 [return_type.type_arguments().next()?],244 )245 .map(|f| def_to_nav(sema, f.into()));246 }247 hir::AssocItemContainer::Impl(_) => return None,248 };249250 let fn_name = f.name(sema.db);251 let f = if fn_name == sym::into && fd.core_convert_Into() == Some(t) {252 let dual = fd.core_convert_From()?;253 let dual_f = dual.function(sema.db, &sym::from)?;254 sema.resolve_trait_impl_method(255 return_type.clone(),256 dual,257 dual_f,258 [return_type, callable.receiver_param(sema.db)?.1],259 )?260 } else if fn_name == sym::try_into && fd.core_convert_TryInto() == Some(t) {261 let dual = fd.core_convert_TryFrom()?;262 let dual_f = dual.function(sema.db, &sym::try_from)?;263 sema.resolve_trait_impl_method(264 return_type.clone(),265 dual,266 dual_f,267 // Extract the `T` from `Result<T, ..>`268 [return_type.type_arguments().next()?, callable.receiver_param(sema.db)?.1],269 )?270 } else if fn_name == sym::to_string && fd.alloc_string_ToString() == Some(t) {271 let dual = fd.core_fmt_Display()?;272 let dual_f = dual.function(sema.db, &sym::fmt)?;273 sema.resolve_trait_impl_method(274 return_type.clone(),275 dual,276 dual_f,277 [callable.receiver_param(sema.db)?.1.strip_reference()],278 )?279 } else {280 return None;281 };282 // Assert that we got a trait impl function, if we are back in a trait definition we didn't283 // succeed284 let _t = f.as_assoc_item(sema.db)?.implemented_trait(sema.db)?;285 let def = Definition::from(f);286 Some(def_to_nav(sema, def))287}288289// If the token is a comparison operator (!=, <, <=, >, >=) that resolves to a default trait method, navigate to the corresponding primary method (eq for ne, partial_cmp for the others).290fn find_definition_for_comparison_operators(291 sema: &Semantics<'_, RootDatabase>,292 original_token: &SyntaxToken,293) -> Option<Vec<NavigationTarget>> {294 let bin_expr = ast::BinExpr::cast(original_token.parent()?)?;295296 let f = sema.resolve_bin_expr(&bin_expr)?;297 let assoc = f.as_assoc_item(sema.db)?;298299 let lhs_type = sema.type_of_expr(&bin_expr.lhs()?)?.original;300 let rhs_type = sema.type_of_expr(&bin_expr.rhs()?)?.original;301302 let t = match assoc.container(sema.db) {303 hir::AssocItemContainer::Trait(t) => t,304 hir::AssocItemContainer::Impl(_) => return None, // Already implemented by the type305 };306307 let fn_name = f.name(sema.db);308 let fn_name_str = fn_name.as_str();309310 let trait_name = t.name(sema.db);311 let trait_name_str = trait_name.as_str();312313 let (target_fn_name, expected_trait) = match fn_name_str {314 "ne" => ("eq", "PartialEq"),315 "lt" | "le" | "gt" | "ge" => ("partial_cmp", "PartialOrd"),316 _ => return None,317 };318319 if trait_name_str != expected_trait {320 return None;321 }322323 let primary_f = t.items(sema.db).into_iter().find_map(|item| {324 if let hir::AssocItem::Function(func) = item325 && func.name(sema.db).as_str() == target_fn_name326 {327 return Some(func);328 }329 None330 })?;331332 // Chalk requires ALL trait substitutions, including `Self`!333 // We must pass [Self, Rhs]334 let resolved_f = sema.resolve_trait_impl_method(335 lhs_type.clone(),336 t,337 primary_f,338 [lhs_type.clone(), rhs_type.clone()],339 )?;340341 let def = Definition::from(resolved_f);342343 Some(def_to_nav(sema, def))344}345fn try_lookup_include_path(346 sema: &Semantics<'_, RootDatabase>,347 token: InFile<ast::String>,348 file_id: FileId,349) -> Option<NavigationTarget> {350 let file = token.file_id.macro_file()?;351352 // Check that we are in the eager argument expansion of an include macro353 // that is we are the string input of it354 if !iter::successors(Some(file), |file| file.parent(sema.db).macro_file())355 .any(|file| file.is_include_like_macro(sema.db) && file.eager_arg(sema.db).is_none())356 {357 return None;358 }359 let path = token.value.value().ok()?;360361 let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;362 let size = sema.db.file_text(file_id).text(sema.db).len().try_into().ok()?;363 Some(NavigationTarget {364 file_id,365 full_range: TextRange::new(0.into(), size),366 name: hir::Symbol::intern(&path),367 alias: None,368 focus_range: None,369 kind: None,370 container_name: None,371 description: None,372 })373}374375fn try_lookup_macro_def_in_macro_use(376 sema: &Semantics<'_, RootDatabase>,377 token: SyntaxToken,378) -> Option<NavigationTarget> {379 let extern_crate = token.parent()?.ancestors().find_map(ast::ExternCrate::cast)?;380 let extern_crate = sema.to_def(&extern_crate)?;381 let krate = extern_crate.resolved_crate(sema.db)?;382383 for mod_def in krate.root_module(sema.db).declarations(sema.db) {384 if let ModuleDef::Macro(mac) = mod_def385 && mac.name(sema.db).as_str() == token.text()386 && let Some(nav) = mac.try_to_nav(sema)387 {388 return Some(nav.call_site);389 }390 }391392 None393}394395/// finds the trait definition of an impl'd item, except function396/// e.g.397/// ```rust398/// trait A { type a; }399/// struct S;400/// impl A for S { type a = i32; } // <-- on this associate type, will get the location of a in the trait401/// ```402fn try_filter_trait_item_definition(403 sema: &Semantics<'_, RootDatabase>,404 def: &Definition<'_>,405) -> Option<Vec<NavigationTarget>> {406 let db = sema.db;407 let assoc = def.as_assoc_item(db)?;408 match assoc {409 AssocItem::Function(..) => None,410 AssocItem::Const(..) | AssocItem::TypeAlias(..) => {411 let trait_ = assoc.implemented_trait(db)?;412 let name = def.name(db)?;413 let discriminant_value = discriminant(&assoc);414 trait_415 .items(db)416 .iter()417 .filter(|itm| discriminant(*itm) == discriminant_value)418 .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(sema)).flatten())419 .map(|it| it.collect())420 }421 }422}423424fn handle_control_flow_keywords(425 sema: &Semantics<'_, RootDatabase>,426 token: &SyntaxToken,427) -> Option<Vec<NavigationTarget>> {428 match token.kind() {429 // For `fn` / `loop` / `while` / `for` / `async` / `match`, return the keyword it self,430 // so that VSCode will find the references when using `ctrl + click`431 T![fn] | T![async] | T![try] | T![return] => nav_for_exit_points(sema, token),432 T![loop] | T![while] | T![break] | T![continue] => nav_for_break_points(sema, token),433 T![for] if token.parent().and_then(ast::ForExpr::cast).is_some() => {434 nav_for_break_points(sema, token)435 }436 T![match] | T![=>] | T![if] => nav_for_branch_exit_points(sema, token),437 _ => None,438 }439}440441pub(crate) fn find_fn_or_blocks(442 sema: &Semantics<'_, RootDatabase>,443 token: &SyntaxToken,444) -> Vec<SyntaxNode> {445 let find_ancestors = |token: SyntaxToken| {446 let token_kind = token.kind();447448 for anc in sema.token_ancestors_with_macros(token) {449 let node = match_ast! {450 match anc {451 ast::Fn(fn_) => fn_.syntax().clone(),452 ast::ClosureExpr(c) => c.syntax().clone(),453 ast::BlockExpr(blk) => {454 match blk.modifier() {455 Some(ast::BlockModifier::Async(_)) => blk.syntax().clone(),456 Some(ast::BlockModifier::Try { .. }) if token_kind != T![return] => blk.syntax().clone(),457 _ => continue,458 }459 },460 _ => continue,461 }462 };463464 return Some(node);465 }466 None467 };468469 sema.descend_into_macros(token.clone()).into_iter().filter_map(find_ancestors).collect_vec()470}471472fn nav_for_exit_points(473 sema: &Semantics<'_, RootDatabase>,474 token: &SyntaxToken,475) -> Option<Vec<NavigationTarget>> {476 let db = sema.db;477 let token_kind = token.kind();478479 let navs = find_fn_or_blocks(sema, token)480 .into_iter()481 .filter_map(|node| {482 let file_id = sema.hir_file_for(&node);483484 match_ast! {485 match node {486 ast::Fn(fn_) => {487 let mut nav = sema.to_def(&fn_)?.try_to_nav(sema)?;488 // For async token, we navigate to itself, which triggers489 // VSCode to find the references490 let focus_token = if matches!(token_kind, T![async]) {491 fn_.async_token()?492 } else {493 fn_.fn_token()?494 };495496 let focus_frange = InFile::new(file_id, focus_token.text_range())497 .original_node_file_range_opt(db)498 .map(|(frange, _)| frange);499500 if let Some(FileRange { file_id, range }) = focus_frange {501 let contains_frange = |nav: &NavigationTarget| {502 nav.file_id == file_id.file_id(db) && nav.full_range.contains_range(range)503 };504505 if let Some(def_site) = nav.def_site.as_mut() {506 if contains_frange(def_site) {507 def_site.focus_range = Some(range);508 }509 } else if contains_frange(&nav.call_site) {510 nav.call_site.focus_range = Some(range);511 }512 }513514 Some(nav)515 },516 ast::ClosureExpr(c) => {517 let pipe_tok = c.param_list().and_then(|it| it.pipe_token())?.text_range();518 let closure_in_file = InFile::new(file_id, c.into());519 Some(expr_to_nav(db, closure_in_file, Some(pipe_tok)))520 },521 ast::BlockExpr(blk) => {522 match blk.modifier() {523 Some(ast::BlockModifier::Async(_)) => {524 let async_tok = blk.async_token()?.text_range();525 let blk_in_file = InFile::new(file_id, blk.into());526 Some(expr_to_nav(db, blk_in_file, Some(async_tok)))527 },528 Some(ast::BlockModifier::Try { .. }) if token_kind != T![return] => {529 let try_tok = blk.try_block_modifier()?.try_token()?.text_range();530 let blk_in_file = InFile::new(file_id, blk.into());531 Some(expr_to_nav(db, blk_in_file, Some(try_tok)))532 },533 _ => None,534 }535 },536 _ => None,537 }538 }539 })540 .flatten()541 .collect_vec();542543 Some(navs)544}545546pub(crate) fn find_branch_root(547 sema: &Semantics<'_, RootDatabase>,548 token: &SyntaxToken,549) -> Vec<SyntaxNode> {550 let find_nodes = |node_filter: fn(SyntaxNode) -> Option<SyntaxNode>| {551 sema.descend_into_macros(token.clone())552 .into_iter()553 .filter_map(|token| node_filter(token.parent()?))554 .collect_vec()555 };556557 match token.kind() {558 T![match] => find_nodes(|node| Some(ast::MatchExpr::cast(node)?.syntax().clone())),559 T![=>] => find_nodes(|node| Some(ast::MatchArm::cast(node)?.syntax().clone())),560 T![if] => find_nodes(|node| {561 let if_expr = ast::IfExpr::cast(node)?;562563 let root_if = iter::successors(Some(if_expr.clone()), |if_expr| {564 let parent_if = if_expr.syntax().parent().and_then(ast::IfExpr::cast)?;565 let ast::ElseBranch::IfExpr(else_branch) = parent_if.else_branch()? else {566 return None;567 };568569 (else_branch.syntax() == if_expr.syntax()).then_some(parent_if)570 })571 .last()?;572573 Some(root_if.syntax().clone())574 }),575 _ => vec![],576 }577}578579fn nav_for_branch_exit_points(580 sema: &Semantics<'_, RootDatabase>,581 token: &SyntaxToken,582) -> Option<Vec<NavigationTarget>> {583 let db = sema.db;584585 let navs = match token.kind() {586 T![match] => find_branch_root(sema, token)587 .into_iter()588 .filter_map(|node| {589 let file_id = sema.hir_file_for(&node);590 let match_expr = ast::MatchExpr::cast(node)?;591 let focus_range = match_expr.match_token()?.text_range();592 let match_expr_in_file = InFile::new(file_id, match_expr.into());593 Some(expr_to_nav(db, match_expr_in_file, Some(focus_range)))594 })595 .flatten()596 .collect_vec(),597598 T![=>] => find_branch_root(sema, token)599 .into_iter()600 .filter_map(|node| {601 let match_arm = ast::MatchArm::cast(node)?;602 let match_expr = sema603 .ancestors_with_macros(match_arm.syntax().clone())604 .find_map(ast::MatchExpr::cast)?;605 let file_id = sema.hir_file_for(match_expr.syntax());606 let focus_range = match_arm.fat_arrow_token()?.text_range();607 let match_expr_in_file = InFile::new(file_id, match_expr.into());608 Some(expr_to_nav(db, match_expr_in_file, Some(focus_range)))609 })610 .flatten()611 .collect_vec(),612613 T![if] => find_branch_root(sema, token)614 .into_iter()615 .filter_map(|node| {616 let file_id = sema.hir_file_for(&node);617 let if_expr = ast::IfExpr::cast(node)?;618 let focus_range = if_expr.if_token()?.text_range();619 let if_expr_in_file = InFile::new(file_id, if_expr.into());620 Some(expr_to_nav(db, if_expr_in_file, Some(focus_range)))621 })622 .flatten()623 .collect_vec(),624625 _ => return Some(Vec::new()),626 };627628 Some(navs)629}630631fn nav_for_break_points(632 sema: &Semantics<'_, RootDatabase>,633 token: &SyntaxToken,634) -> Option<Vec<NavigationTarget>> {635 let db = sema.db;636637 let navs = find_loops(sema, token)?638 .filter_map(|expr| {639 let file_id = sema.hir_file_for(expr.syntax());640 let expr_in_file = InFile::new(file_id, expr.clone());641 let focus_range = match expr {642 ast::Expr::LoopExpr(loop_) => loop_.loop_token()?.text_range(),643 ast::Expr::WhileExpr(while_) => while_.while_token()?.text_range(),644 ast::Expr::ForExpr(for_) => for_.for_token()?.text_range(),645 // We guarantee that the label exists646 ast::Expr::BlockExpr(blk) => blk.label().unwrap().syntax().text_range(),647 _ => return None,648 };649 let nav = expr_to_nav(db, expr_in_file, Some(focus_range));650 Some(nav)651 })652 .flatten()653 .collect_vec();654655 Some(navs)656}657658fn def_to_nav(sema: &Semantics<'_, RootDatabase>, def: Definition<'_>) -> Vec<NavigationTarget> {659 def.try_to_nav(sema).map(|it| it.collect()).unwrap_or_default()660}661662fn expr_to_nav(663 db: &RootDatabase,664 InFile { file_id, value }: InFile<ast::Expr>,665 focus_range: Option<TextRange>,666) -> UpmappingResult<NavigationTarget> {667 let kind = SymbolKind::Label;668669 let value_range = value.syntax().text_range();670 let navs = navigation_target::orig_range_with_focus_r(db, file_id, value_range, focus_range);671 navs.map(|(hir::FileRangeWrapper { file_id, range }, focus_range)| {672 NavigationTarget::from_syntax(673 file_id,674 hir::Symbol::intern("<expr>"),675 focus_range,676 range,677 kind,678 )679 })680}681682#[cfg(test)]683mod tests {684 use crate::{GotoDefinitionConfig, fixture};685 use ide_db::{FileRange, ra_fixture::RaFixtureConfig};686 use itertools::Itertools;687688 const TEST_CONFIG: GotoDefinitionConfig<'_> =689 GotoDefinitionConfig { ra_fixture: RaFixtureConfig::default() };690691 #[track_caller]692 fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {693 let (analysis, position, expected) = fixture::annotations(ra_fixture);694 let navs = analysis695 .goto_definition(position, &TEST_CONFIG)696 .unwrap()697 .expect("no definition found")698 .info;699700 let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());701 let navs = navs702 .into_iter()703 .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })704 .sorted_by_key(cmp)705 .collect::<Vec<_>>();706 let expected = expected707 .into_iter()708 .map(|(FileRange { file_id, range }, _)| FileRange { file_id, range })709 .sorted_by_key(cmp)710 .collect::<Vec<_>>();711712 assert_eq!(expected, navs);713 }714715 fn check_unresolved(#[rust_analyzer::rust_fixture] ra_fixture: &str) {716 let (analysis, position) = fixture::position(ra_fixture);717 let navs = analysis718 .goto_definition(position, &TEST_CONFIG)719 .unwrap()720 .expect("no definition found")721 .info;722723 assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}")724 }725726 #[track_caller]727 fn check_no_definition(#[rust_analyzer::rust_fixture] ra_fixture: &str) {728 let (analysis, position) = fixture::position(ra_fixture);729 let navs = analysis.goto_definition(position, &TEST_CONFIG).unwrap();730 assert!(navs.is_none(), "didn't expect this to resolve anywhere: {navs:?}");731 }732733 fn check_name(expected_name: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str) {734 let (analysis, position, _) = fixture::annotations(ra_fixture);735 let navs = analysis736 .goto_definition(position, &TEST_CONFIG)737 .unwrap()738 .expect("no definition found")739 .info;740 assert!(navs.len() < 2, "expected single navigation target but encountered {}", navs.len());741 let Some(target) = navs.into_iter().next() else {742 panic!("expected single navigation target but encountered none");743 };744 assert_eq!(target.name, hir::Symbol::intern(expected_name));745 }746747 #[test]748 fn goto_def_pat_range_to_inclusive() {749 check_name(750 "RangeToInclusive",751 r#"752//- minicore: range753fn f(ch: char) -> bool {754 match ch {755 ..$0='z' => true,756 _ => false757 }758}759"#,760 );761 }762763 #[test]764 fn goto_def_pat_range_to() {765 check_name(766 "RangeTo",767 r#"768//- minicore: range769fn f(ch: char) -> bool {770 match ch {771 .$0.'z' => true,772 _ => false773 }774}775"#,776 );777 }778779 #[test]780 fn goto_def_pat_range() {781 check_name(782 "Range",783 r#"784//- minicore: range785fn f(ch: char) -> bool {786 match ch {787 'a'.$0.'z' => true,788 _ => false789 }790}791"#,792 );793 }794795 #[test]796 fn goto_def_pat_range_inclusive() {797 check_name(798 "RangeInclusive",799 r#"800//- minicore: range801fn f(ch: char) -> bool {802 match ch {803 'a'..$0='z' => true,804 _ => false805 }806}807"#,808 );809 }810811 #[test]812 fn goto_def_pat_range_from() {813 check_name(814 "RangeFrom",815 r#"816//- minicore: range817fn f(ch: char) -> bool {818 match ch {819 'a'..$0 => true,820 _ => false821 }822}823"#,824 );825 }826827 #[test]828 fn goto_def_expr_range() {829 check_name(830 "Range",831 r#"832//- minicore: range833let x = 0.$0.1;834"#,835 );836 }837838 #[test]839 fn goto_def_expr_range_from() {840 check_name(841 "RangeFrom",842 r#"843//- minicore: range844fn f(arr: &[i32]) -> &[i32] {845 &arr[0.$0.]846}847"#,848 );849 }850851 #[test]852 fn goto_def_expr_range_inclusive() {853 check_name(854 "RangeInclusive",855 r#"856//- minicore: range857let x = 0.$0.=1;858"#,859 );860 }861862 #[test]863 fn goto_def_expr_range_full() {864 check_name(865 "RangeFull",866 r#"867//- minicore: range868fn f(arr: &[i32]) -> &[i32] {869 &arr[.$0.]870}871"#,872 );873 }874875 #[test]876 fn goto_def_expr_range_to() {877 check_name(878 "RangeTo",879 r#"880//- minicore: range881fn f(arr: &[i32]) -> &[i32] {882 &arr[.$0.10]883}884"#,885 );886 }887888 #[test]889 fn goto_def_expr_range_to_inclusive() {890 check_name(891 "RangeToInclusive",892 r#"893//- minicore: range894fn f(arr: &[i32]) -> &[i32] {895 &arr[.$0.=10]896}897"#,898 );899 }900901 #[test]902 fn goto_def_in_included_file() {903 check(904 r#"905//- minicore:include906//- /main.rs907908include!("a.rs");909910fn main() {911 foo();912}913914//- /a.rs915fn func_in_include() {916 //^^^^^^^^^^^^^^^917}918919fn foo() {920 func_in_include$0();921}922"#,923 );924 }925926 #[test]927 fn goto_def_in_included_file_nested() {928 check(929 r#"930//- minicore:include931//- /main.rs932933macro_rules! passthrough {934 ($($tt:tt)*) => { $($tt)* }935}936937passthrough!(include!("a.rs"));938939fn main() {940 foo();941}942943//- /a.rs944fn func_in_include() {945 //^^^^^^^^^^^^^^^946}947948fn foo() {949 func_in_include$0();950}951"#,952 );953 }954955 #[test]956 fn goto_def_in_included_file_inside_mod() {957 check(958 r#"959//- minicore:include960//- /main.rs961mod a {962 include!("b.rs");963}964//- /b.rs965fn func_in_include() {966 //^^^^^^^^^^^^^^^967}968fn foo() {969 func_in_include$0();970}971"#,972 );973974 check(975 r#"976//- minicore:include977//- /main.rs978mod a {979 include!("a.rs");980}981//- /a.rs982fn func_in_include() {983 //^^^^^^^^^^^^^^^984}985986fn foo() {987 func_in_include$0();988}989"#,990 );991 }992993 #[test]994 fn goto_def_if_items_same_name() {995 check(996 r#"997trait Trait {998 type A;999 const A: i32;1000 //^1001}10021003struct T;1004impl Trait for T {1005 type A = i32;1006 const A$0: i32 = -9;1007}"#,1008 );1009 }10101011 #[test]1012 fn goto_def_array_length_prefers_value_namespace() {1013 check(1014 r#"1015struct N;10161017trait Trait {}10181019impl<const N: usize> Trait for [N; N$0] {}1020 //^1021"#,1022 );1023 }10241025 #[test]1026 fn goto_def_in_mac_call_in_attr_invoc() {1027 check(1028 r#"1029//- proc_macros: identity1030pub struct Struct {1031 // ^^^^^^1032 field: i32,1033}10341035macro_rules! identity {1036 ($($tt:tt)*) => {$($tt)*};1037}10381039#[proc_macros::identity]1040fn function() {1041 identity!(Struct$0 { field: 0 });1042}10431044"#,1045 )1046 }10471048 #[test]1049 fn goto_def_for_extern_crate() {1050 check(1051 r#"1052//- /main.rs crate:main deps:std1053extern crate std$0;1054//- /std/lib.rs crate:std1055// empty1056//^file1057"#,1058 )1059 }10601061 #[test]1062 fn goto_def_for_renamed_extern_crate() {1063 check(1064 r#"1065//- /main.rs crate:main deps:std1066extern crate std as abc$0;1067//- /std/lib.rs crate:std1068// empty1069//^file1070"#,1071 )1072 }10731074 #[test]1075 fn goto_def_in_items() {1076 check(1077 r#"1078struct Foo;1079 //^^^1080enum E { X(Foo$0) }1081"#,1082 );1083 }10841085 #[test]1086 fn goto_def_at_start_of_item() {1087 check(1088 r#"1089struct Foo;1090 //^^^1091enum E { X($0Foo) }1092"#,1093 );1094 }10951096 #[test]1097 fn goto_definition_resolves_correct_name() {1098 check(1099 r#"1100//- /lib.rs1101use a::Foo;1102mod a;1103mod b;1104enum E { X(Foo$0) }11051106//- /a.rs1107pub struct Foo;1108 //^^^1109//- /b.rs1110pub struct Foo;1111"#,1112 );1113 }11141115 #[test]1116 fn goto_def_for_module_declaration() {1117 check(1118 r#"1119//- /lib.rs1120mod $0foo;11211122//- /foo.rs1123// empty1124//^file1125"#,1126 );11271128 check(1129 r#"1130//- /lib.rs1131mod $0foo;11321133//- /foo/mod.rs1134// empty1135//^file1136"#,1137 );1138 }11391140 #[test]1141 fn goto_def_for_macros() {1142 check(1143 r#"1144macro_rules! foo { () => { () } }1145 //^^^1146fn bar() {1147 $0foo!();1148}1149"#,1150 );1151 }11521153 #[test]1154 fn goto_def_for_macros_from_other_crates() {1155 check(1156 r#"1157//- /lib.rs crate:main deps:foo1158use foo::foo;1159fn bar() {1160 $0foo!();1161}11621163//- /foo/lib.rs crate:foo1164#[macro_export]1165macro_rules! foo { () => { () } }1166 //^^^1167"#,1168 );1169 }11701171 #[test]1172 fn goto_def_for_macros_in_use_tree() {1173 check(1174 r#"1175//- /lib.rs crate:main deps:foo1176use foo::foo$0;11771178//- /foo/lib.rs crate:foo1179#[macro_export]1180macro_rules! foo { () => { () } }1181 //^^^1182"#,1183 );1184 }11851186 #[test]1187 fn goto_def_for_macro_defined_fn_with_arg() {1188 check(1189 r#"1190//- /lib.rs1191macro_rules! define_fn {1192 ($name:ident) => (fn $name() {})1193}11941195define_fn!(foo);1196 //^^^11971198fn bar() {1199 $0foo();1200}1201"#,1202 );1203 }12041205 #[test]1206 fn goto_def_for_macro_defined_fn_no_arg() {1207 check(1208 r#"1209//- /lib.rs1210macro_rules! define_fn {1211 () => (fn foo() {})1212 //^^^1213}12141215 define_fn!();1216//^^^^^^^^^^1217fn bar() {1218 $0foo();1219}1220"#,1221 );1222 }12231224 #[test]1225 fn goto_definition_works_for_macro_inside_pattern() {1226 check(1227 r#"1228//- /lib.rs1229macro_rules! foo {() => {0}}1230 //^^^12311232fn bar() {1233 match (0,1) {1234 ($0foo!(), _) => {}1235 }1236}1237"#,1238 );1239 }12401241 #[test]1242 fn goto_definition_works_for_macro_inside_match_arm_lhs() {1243 check(1244 r#"1245//- /lib.rs1246macro_rules! foo {() => {0}}1247 //^^^1248fn bar() {1249 match 0 {1250 $0foo!() => {}1251 }1252}1253"#,1254 );1255 }12561257 #[test]1258 fn goto_definition_works_for_consts_inside_range_pattern() {1259 check(1260 r#"1261//- /lib.rs1262const A: u32 = 0;1263 //^12641265fn bar(v: u32) {1266 match v {1267 0..=$0A => {}1268 _ => {}1269 }1270}1271"#,1272 );1273 }12741275 #[test]1276 fn goto_def_for_use_alias() {1277 check(1278 r#"1279//- /lib.rs crate:main deps:foo1280use foo as bar$0;12811282//- /foo/lib.rs crate:foo1283// empty1284//^file1285"#,1286 );1287 }12881289 #[test]1290 fn goto_def_for_use_alias_foo_macro() {1291 check(1292 r#"1293//- /lib.rs crate:main deps:foo1294use foo::foo as bar$0;12951296//- /foo/lib.rs crate:foo1297#[macro_export]1298macro_rules! foo { () => { () } }1299 //^^^1300"#,1301 );1302 }13031304 #[test]1305 fn goto_def_for_methods() {1306 check(1307 r#"1308struct Foo;1309impl Foo {1310 fn frobnicate(&self) { }1311 //^^^^^^^^^^1312}13131314fn bar(foo: &Foo) {1315 foo.frobnicate$0();1316}1317"#,1318 );1319 }13201321 #[test]1322 fn goto_def_for_fields() {1323 check(1324 r#"1325struct Foo {1326 spam: u32,1327} //^^^^13281329fn bar(foo: &Foo) {1330 foo.spam$0;1331}1332"#,1333 );1334 }13351336 #[test]1337 fn goto_def_for_record_fields() {1338 check(1339 r#"1340//- /lib.rs1341struct Foo {1342 spam: u32,1343} //^^^^13441345fn bar() -> Foo {1346 Foo {1347 spam$0: 0,1348 }1349}1350"#,1351 );1352 }13531354 #[test]1355 fn goto_def_for_record_pat_fields() {1356 check(1357 r#"1358//- /lib.rs1359struct Foo {1360 spam: u32,1361} //^^^^13621363fn bar(foo: Foo) -> Foo {1364 let Foo { spam$0: _, } = foo1365}1366"#,1367 );1368 }13691370 #[test]1371 fn goto_def_for_record_fields_macros() {1372 check(1373 r"1374macro_rules! m { () => { 92 };}1375struct Foo { spam: u32 }1376 //^^^^13771378fn bar() -> Foo {1379 Foo { spam$0: m!() }1380}1381",1382 );1383 }13841385 #[test]1386 fn goto_for_tuple_fields() {1387 check(1388 r#"1389struct Foo(u32);1390 //^^^13911392fn bar() {1393 let foo = Foo(0);1394 foo.$00;1395}1396"#,1397 );1398 }13991400 #[test]1401 fn goto_def_for_ufcs_inherent_methods() {1402 check(1403 r#"1404struct Foo;1405impl Foo {1406 fn frobnicate() { }1407} //^^^^^^^^^^14081409fn bar(foo: &Foo) {1410 Foo::frobnicate$0();1411}1412"#,1413 );1414 }14151416 #[test]1417 fn goto_def_for_ufcs_trait_methods_through_traits() {1418 check(1419 r#"1420trait Foo {1421 fn frobnicate();1422} //^^^^^^^^^^14231424fn bar() {1425 Foo::frobnicate$0();1426}1427"#,1428 );1429 }14301431 #[test]1432 fn goto_def_for_ufcs_trait_methods_through_self() {1433 check(1434 r#"1435struct Foo;1436trait Trait {1437 fn frobnicate();1438} //^^^^^^^^^^1439impl Trait for Foo {}14401441fn bar() {1442 Foo::frobnicate$0();1443}1444"#,1445 );1446 }14471448 #[test]1449 fn goto_definition_on_self() {1450 check(1451 r#"1452struct Foo;1453impl Foo {1454 //^^^1455 pub fn new() -> Self {1456 Self$0 {}1457 }1458}1459"#,1460 );1461 check(1462 r#"1463struct Foo;1464impl Foo {1465 //^^^1466 pub fn new() -> Self$0 {1467 Self {}1468 }1469}1470"#,1471 );14721473 check(1474 r#"1475enum Foo { A }1476impl Foo {1477 //^^^1478 pub fn new() -> Self$0 {1479 Foo::A1480 }1481}1482"#,1483 );14841485 check(1486 r#"1487enum Foo { A }1488impl Foo {1489 //^^^1490 pub fn thing(a: &Self$0) {1491 }1492}1493"#,1494 );1495 }14961497 #[test]1498 fn goto_definition_on_self_in_trait_impl() {1499 check(1500 r#"1501struct Foo;1502trait Make {1503 fn new() -> Self;1504}1505impl Make for Foo {1506 //^^^1507 fn new() -> Self {1508 Self$0 {}1509 }1510}1511"#,1512 );15131514 check(1515 r#"1516struct Foo;1517trait Make {1518 fn new() -> Self;1519}1520impl Make for Foo {1521 //^^^1522 fn new() -> Self$0 {1523 Self {}1524 }1525}1526"#,1527 );1528 }15291530 #[test]1531 fn goto_def_when_used_on_definition_name_itself() {1532 check(1533 r#"1534struct Foo$0 { value: u32 }1535 //^^^1536 "#,1537 );15381539 check(1540 r#"1541struct Foo {1542 field$0: string,1543} //^^^^^1544"#,1545 );15461547 check(1548 r#"1549fn foo_test$0() { }1550 //^^^^^^^^1551"#,1552 );15531554 check(1555 r#"1556enum Foo$0 { Variant }1557 //^^^1558"#,1559 );15601561 check(1562 r#"1563enum Foo {1564 Variant1,1565 Variant2$0,1566 //^^^^^^^^1567 Variant3,1568}1569"#,1570 );15711572 check(1573 r#"1574static INNER$0: &str = "";1575 //^^^^^1576"#,1577 );15781579 check(1580 r#"1581const INNER$0: &str = "";1582 //^^^^^1583"#,1584 );15851586 check(1587 r#"1588type Thing$0 = Option<()>;1589 //^^^^^1590"#,1591 );15921593 check(1594 r#"1595trait Foo$0 { }1596 //^^^1597"#,1598 );15991600 check(1601 r#"1602trait Foo$0 = ;1603 //^^^1604"#,1605 );16061607 check(1608 r#"1609mod bar$0 { }1610 //^^^1611"#,1612 );1613 }16141615 #[test]1616 fn goto_from_macro() {1617 check(1618 r#"1619macro_rules! id {1620 ($($tt:tt)*) => { $($tt)* }1621}1622fn foo() {}1623 //^^^1624id! {1625 fn bar() {1626 fo$0o();1627 }1628}1629mod confuse_index { fn foo(); }1630"#,1631 );1632 }16331634 #[test]1635 fn goto_through_format() {1636 check(1637 r#"1638//- minicore: fmt1639#[macro_export]1640macro_rules! format {1641 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*)))1642}1643pub mod __export {1644 pub use core::format_args;1645 fn foo() {} // for index confusion1646}1647fn foo() -> i8 {}1648 //^^^1649fn test() {1650 format!("{}", fo$0o())1651}1652"#,1653 );1654 }16551656 #[test]1657 fn goto_through_included_file() {1658 check(1659 r#"1660//- /main.rs1661#[rustc_builtin_macro]1662macro_rules! include {}16631664include!("foo.rs");16651666fn f() {1667 foo$0();1668}16691670mod confuse_index {1671 pub fn foo() {}1672}16731674//- /foo.rs1675fn foo() {}1676 //^^^1677 "#,1678 );1679 }16801681 #[test]1682 fn goto_through_included_file_struct_with_doc_comment() {1683 check(1684 r#"1685//- /main.rs1686#[rustc_builtin_macro]1687macro_rules! include {}16881689include!("foo.rs");16901691fn f() {1692 let x = Foo$0;1693}16941695mod confuse_index {1696 pub struct Foo;1697}16981699//- /foo.rs1700/// This is a doc comment1701pub struct Foo;1702 //^^^1703 "#,1704 );1705 }17061707 #[test]1708 fn goto_for_type_param() {1709 check(1710 r#"1711struct Foo<T: Clone> { t: $0T }1712 //^1713"#,1714 );1715 }17161717 #[test]1718 fn goto_within_macro() {1719 check(1720 r#"1721macro_rules! id {1722 ($($tt:tt)*) => ($($tt)*)1723}17241725fn foo() {1726 let x = 1;1727 //^1728 id!({1729 let y = $0x;1730 let z = y;1731 });1732}1733"#,1734 );17351736 check(1737 r#"1738macro_rules! id {1739 ($($tt:tt)*) => ($($tt)*)1740}17411742fn foo() {1743 let x = 1;1744 id!({1745 let y = x;1746 //^1747 let z = $0y;1748 });1749}1750"#,1751 );1752 }17531754 #[test]1755 fn goto_def_in_local_fn() {1756 check(1757 r#"1758fn main() {1759 fn foo() {1760 let x = 92;1761 //^1762 $0x;1763 }1764}1765"#,1766 );1767 }17681769 #[test]1770 fn goto_def_in_local_macro() {1771 check(1772 r#"1773fn bar() {1774 macro_rules! foo { () => { () } }1775 //^^^1776 $0foo!();1777}1778"#,1779 );1780 }17811782 #[test]1783 fn goto_def_for_field_init_shorthand() {1784 check(1785 r#"1786struct Foo { x: i32 }1787 //^1788fn main() {1789 let x = 92;1790 //^1791 Foo { x$0 };1792}1793"#,1794 )1795 }17961797 #[test]1798 fn goto_def_for_enum_variant_field() {1799 check(1800 r#"1801enum Foo {1802 Bar { x: i32 }1803 //^1804}1805fn baz(foo: Foo) {1806 match foo {1807 Foo::Bar { x$0 } => x1808 //^1809 };1810}1811"#,1812 );1813 }18141815 #[test]1816 fn goto_def_for_enum_variant_self_pattern_const() {1817 check(1818 r#"1819enum Foo { Bar }1820 //^^^1821impl Foo {1822 fn baz(self) {1823 match self { Self::Bar$0 => {} }1824 }1825}1826"#,1827 );1828 }18291830 #[test]1831 fn goto_def_for_enum_variant_self_pattern_record() {1832 check(1833 r#"1834enum Foo { Bar { val: i32 } }1835 //^^^1836impl Foo {1837 fn baz(self) -> i32 {1838 match self { Self::Bar$0 { val } => {} }1839 }1840}1841"#,1842 );1843 }18441845 #[test]1846 fn goto_def_for_enum_variant_self_expr_const() {1847 check(1848 r#"1849enum Foo { Bar }1850 //^^^1851impl Foo {1852 fn baz(self) { Self::Bar$0; }1853}1854"#,1855 );1856 }18571858 #[test]1859 fn goto_def_for_enum_variant_self_expr_record() {1860 check(1861 r#"1862enum Foo { Bar { val: i32 } }1863 //^^^1864impl Foo {1865 fn baz(self) { Self::Bar$0 {val: 4}; }1866}1867"#,1868 );1869 }18701871 #[test]1872 fn goto_def_for_type_alias_generic_parameter() {1873 check(1874 r#"1875type Alias<T> = T$0;1876 //^1877"#,1878 )1879 }18801881 #[test]1882 fn goto_def_for_macro_container() {1883 check(1884 r#"1885//- /lib.rs crate:main deps:foo1886foo::module$0::mac!();18871888//- /foo/lib.rs crate:foo1889pub mod module {1890 //^^^^^^1891 #[macro_export]1892 macro_rules! _mac { () => { () } }1893 pub use crate::_mac as mac;1894}1895"#,1896 );1897 }18981899 #[test]1900 fn goto_def_for_assoc_ty_in_path() {1901 check(1902 r#"1903trait Iterator {1904 type Item;1905 //^^^^1906}19071908fn f() -> impl Iterator<Item$0 = u8> {}1909"#,1910 );1911 }19121913 #[test]1914 fn goto_def_for_super_assoc_ty_in_path() {1915 check(1916 r#"1917trait Super {1918 type Item;1919 //^^^^1920}19211922trait Sub: Super {}19231924fn f() -> impl Sub<Item$0 = u8> {}1925"#,1926 );1927 }19281929 #[test]1930 fn goto_def_for_module_declaration_in_path_if_types_and_values_same_name() {1931 check(1932 r#"1933mod bar {1934 pub struct Foo {}1935 //^^^1936 pub fn Foo() {}1937}19381939fn baz() {1940 let _foo_enum: bar::Foo$0 = bar::Foo {};1941}1942 "#,1943 )1944 }19451946 #[test]1947 fn unknown_assoc_ty() {1948 check_unresolved(1949 r#"1950trait Iterator { type Item; }1951fn f() -> impl Iterator<Invalid$0 = u8> {}1952"#,1953 )1954 }19551956 #[test]1957 fn goto_def_for_assoc_ty_in_path_multiple() {1958 check(1959 r#"1960trait Iterator {1961 type A;1962 //^1963 type B;1964}19651966fn f() -> impl Iterator<A$0 = u8, B = ()> {}1967"#,1968 );1969 check(1970 r#"1971trait Iterator {1972 type A;1973 type B;1974 //^1975}19761977fn f() -> impl Iterator<A = u8, B$0 = ()> {}1978"#,1979 );1980 }19811982 #[test]1983 fn goto_def_for_assoc_ty_ufcs() {1984 check(1985 r#"1986trait Iterator {1987 type Item;1988 //^^^^1989}19901991fn g() -> <() as Iterator<Item$0 = ()>>::Item {}1992"#,1993 );1994 }19951996 #[test]1997 fn goto_def_for_assoc_ty_ufcs_multiple() {1998 check(1999 r#"2000trait Iterator {
Code quality findings 45
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning
correctness
unwrap-usage
ast::Expr::BlockExpr(blk) => blk.label().unwrap().syntax().text_range(),
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning
correctness
unwrap-usage
.unwrap()
Warning: '.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
.expect("no definition found")
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning
correctness
unwrap-usage
.unwrap()
Warning: '.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
.expect("no definition found")
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning
correctness
unwrap-usage
let navs = analysis.goto_definition(position, &TEST_CONFIG).unwrap();
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
&arr[0.$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
&arr[.$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
&arr[.$0.10]
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
&arr[.$0.=10]
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
Struct[0]$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
foo[0]$0 = Bar;
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 _ = core::mem::offset_of!(Foo, fiel$0d);
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 _ = core::mem::offset_of!(Bar, 0.fiel$0d);
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 _ = core::mem::offset_of!(Bar, 0.Abc.0.fiel$0d);
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 _ = core::mem::offset_of!(Bar, 0.Ab$0c.0.field);
Performance Info: Frequent cloning, especially of Strings, Vecs, or other heap-allocated types inside loops, can be expensive. Consider using references/borrowing where possible.
info
performance
clone-in-loop
let tokens = sema.descend_into_macros_no_opaque(original_token.clone(), false);
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 (target_fn_name, expected_trait) = match fn_name_str {
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 token.kind() {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info
correctness
match-wildcard
match anc {
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
ast::Fn(fn_) => fn_.syntax().clone(),
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
ast::ClosureExpr(c) => c.syntax().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 blk.modifier() {
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
Some(ast::BlockModifier::Async(_)) => blk.syntax().clone(),
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
Some(ast::BlockModifier::Try { .. }) if token_kind != T![return] => blk.syntax().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
let focus_range = match expr {
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 ch {
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 ch {
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 ch {
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 ch {
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 ch {
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 v {
Info: Including file content directly into the binary at compile time. Ensure this is intended, as it increases binary size and doesn't reflect runtime file changes.
info
maintainability
include-in-binary
let str = include_str!("foo.txt$0");
Info: Including file content directly into the binary at compile time. Ensure this is intended, as it increases binary size and doesn't reflect runtime file changes.
info
maintainability
include-in-binary
let str = include_str!(concat!("foo", ".tx$0t"));
Info: Including file content directly into the binary at compile time. Ensure this is intended, as it increases binary size and doesn't reflect runtime file changes.
info
maintainability
include-in-binary
#[doc = include_str!("docs.md$0")]
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info
maintainability
println-macro
println!("{}", i);
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info
maintainability
println-macro
println!("{}", i);
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 0 {
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 0 {
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 0 {
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 42 {
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 Tag(Path) {
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 x {
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 x {
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 x {