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// 43pub(crate) fn goto_definition(44 db: &RootDatabase,45 FilePosition { file_id, offset }: FilePosition,46 config: &GotoDefinitionConfig<'_>,47) -> Option<RangeInfo<Vec<NavigationTarget>>> {48 let sema = &Semantics::new(db);49 let file = sema.parse_guess_edition(file_id).syntax().clone();50 let edition = sema.attach_first_edition(file_id).edition(db);51 let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {52 IDENT53 | INT_NUMBER54 | LIFETIME_IDENT55 | T![self]56 | T![super]57 | T![crate]58 | T![Self]59 | COMMENT => 4,60 // index and prefix ops61 T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,62 kind if kind.is_keyword(edition) => 2,63 T!['('] | T![')'] => 2,64 kind if kind.is_trivia() => 0,65 _ => 1,66 })?;67 if let Some(doc_comment) = token_as_doc_comment(&original_token) {68 return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, link_range| {69 let nav = def.try_to_nav(sema)?;70 Some(RangeInfo::new(link_range, nav.collect()))71 });72 }7374 if let Some((range, _, _, resolution)) =75 sema.check_for_format_args_template(original_token.clone(), offset)76 {77 return Some(RangeInfo::new(78 range,79 match resolution {80 Some(res) => def_to_nav(sema, Definition::from(res)),81 None => vec![],82 },83 ));84 }8586 if let Some(navs) = handle_control_flow_keywords(sema, &original_token) {87 return Some(RangeInfo::new(original_token.text_range(), navs));88 }8990 let tokens = sema.descend_into_macros_no_opaque(original_token.clone(), false);91 let mut navs = Vec::new();92 for token in tokens {93 if let Some(n) = find_definition_for_known_blanket_dual_impls(sema, &token.value) {94 navs.extend(n);95 continue;96 }9798 if let Some(n) = find_definition_for_comparison_operators(sema, &token.value) {99 navs.extend(n);100 continue;101 }102103 let parent = token.value.parent()?;104105 if let Some(question_mark_conversion) = goto_question_mark_conversions(sema, &parent) {106 navs.extend(def_to_nav(sema, question_mark_conversion.into()));107 continue;108 }109110 if let Some(token) = ast::String::cast(token.value.clone())111 && let Some(original_token) = ast::String::cast(original_token.clone())112 && let Some((analysis, fixture_analysis)) =113 Analysis::from_ra_fixture(sema, original_token, &token, &config.ra_fixture)114 && let Some((virtual_file_id, file_offset)) = fixture_analysis.map_offset_down(offset)115 {116 return hir::attach_db_allow_change(&analysis.db, || {117 goto_definition(118 &analysis.db,119 FilePosition { file_id: virtual_file_id, offset: file_offset },120 config,121 )122 })123 .and_then(|navs| {124 navs.upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, file_id).ok()125 });126 }127128 let token_file_id = token.file_id;129 if let Some(token) = ast::String::cast(token.value.clone())130 && let Some(x) =131 try_lookup_include_path(sema, InFile::new(token_file_id, token), file_id)132 {133 navs.push(x);134 continue;135 }136137 if ast::TokenTree::can_cast(parent.kind())138 && let Some(x) = try_lookup_macro_def_in_macro_use(sema, token.value)139 {140 navs.push(x);141 continue;142 }143144 let Some(ident_class) = IdentClass::classify_node(sema, &parent) else { continue };145 navs.extend(ident_class.definitions().into_iter().flat_map(|(def, _)| {146 if let Definition::ExternCrateDecl(crate_def) = def {147 return crate_def148 .resolved_crate(db)149 .map(|it| it.root_module(db).to_nav(db))150 .into_iter()151 .flatten()152 .collect();153 }154 try_filter_trait_item_definition(sema, &def).unwrap_or_else(|| def_to_nav(sema, def))155 }));156 }157 let navs = navs.into_iter().unique().collect();158159 Some(RangeInfo::new(original_token.text_range(), navs))160}161162/// When the `?` operator is used on `Result`, go to the `From` impl if it exists as this provides more value.163fn goto_question_mark_conversions(164 sema: &Semantics<'_, RootDatabase>,165 node: &SyntaxNode,166) -> Option<hir::Function> {167 let node = ast::TryExpr::cast(node.clone())?;168 let try_expr_ty = sema.type_of_expr(&node.expr()?)?.adjusted();169170 let fd = FamousDefs(sema, try_expr_ty.krate(sema.db));171 let result_enum = fd.core_result_Result()?.into();172173 let (try_expr_ty_adt, try_expr_ty_args) = try_expr_ty.as_adt_with_args()?;174 if try_expr_ty_adt != result_enum {175 // FIXME: Support `Poll<Result>`.176 return None;177 }178 let original_err_ty = try_expr_ty_args.get(1)?.clone()?;179180 let returned_ty = sema.try_expr_returned_type(&node)?;181 let (returned_adt, returned_ty_args) = returned_ty.as_adt_with_args()?;182 if returned_adt != result_enum {183 return None;184 }185 let returned_err_ty = returned_ty_args.get(1)?.clone()?;186187 if returned_err_ty.could_unify_with_deeply(sema.db, &original_err_ty) {188 return None;189 }190191 let from_trait = fd.core_convert_From()?;192 let from_fn = from_trait.function(sema.db, sym::from)?;193 sema.resolve_trait_impl_method(194 returned_err_ty.clone(),195 from_trait,196 from_fn,197 [returned_err_ty, original_err_ty],198 )199}200201// If the token is into(), try_into(), search the definition of From, TryFrom.202fn find_definition_for_known_blanket_dual_impls(203 sema: &Semantics<'_, RootDatabase>,204 original_token: &SyntaxToken,205) -> Option<Vec<NavigationTarget>> {206 let method_call = ast::MethodCallExpr::cast(original_token.parent()?.parent()?)?;207 let callable = sema.resolve_method_call_as_callable(&method_call)?;208 let CallableKind::Function(f) = callable.kind() else { return None };209 let assoc = f.as_assoc_item(sema.db)?;210211 let return_type = callable.return_type();212 let fd = FamousDefs(sema, return_type.krate(sema.db));213214 let t = match assoc.container(sema.db) {215 hir::AssocItemContainer::Trait(t) => t,216 hir::AssocItemContainer::Impl(impl_)217 if impl_.self_ty(sema.db).is_str() && f.name(sema.db) == sym::parse =>218 {219 let t = fd.core_convert_FromStr()?;220 let t_f = t.function(sema.db, &sym::from_str)?;221 return sema222 .resolve_trait_impl_method(223 return_type.clone(),224 t,225 t_f,226 [return_type.type_arguments().next()?],227 )228 .map(|f| def_to_nav(sema, f.into()));229 }230 hir::AssocItemContainer::Impl(_) => return None,231 };232233 let fn_name = f.name(sema.db);234 let f = if fn_name == sym::into && fd.core_convert_Into() == Some(t) {235 let dual = fd.core_convert_From()?;236 let dual_f = dual.function(sema.db, &sym::from)?;237 sema.resolve_trait_impl_method(238 return_type.clone(),239 dual,240 dual_f,241 [return_type, callable.receiver_param(sema.db)?.1],242 )?243 } else if fn_name == sym::try_into && fd.core_convert_TryInto() == Some(t) {244 let dual = fd.core_convert_TryFrom()?;245 let dual_f = dual.function(sema.db, &sym::try_from)?;246 sema.resolve_trait_impl_method(247 return_type.clone(),248 dual,249 dual_f,250 // Extract the `T` from `Result<T, ..>`251 [return_type.type_arguments().next()?, callable.receiver_param(sema.db)?.1],252 )?253 } else if fn_name == sym::to_string && fd.alloc_string_ToString() == Some(t) {254 let dual = fd.core_fmt_Display()?;255 let dual_f = dual.function(sema.db, &sym::fmt)?;256 sema.resolve_trait_impl_method(257 return_type.clone(),258 dual,259 dual_f,260 [callable.receiver_param(sema.db)?.1.strip_reference()],261 )?262 } else {263 return None;264 };265 // Assert that we got a trait impl function, if we are back in a trait definition we didn't266 // succeed267 let _t = f.as_assoc_item(sema.db)?.implemented_trait(sema.db)?;268 let def = Definition::from(f);269 Some(def_to_nav(sema, def))270}271272// 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).273fn find_definition_for_comparison_operators(274 sema: &Semantics<'_, RootDatabase>,275 original_token: &SyntaxToken,276) -> Option<Vec<NavigationTarget>> {277 let bin_expr = ast::BinExpr::cast(original_token.parent()?)?;278279 let f = sema.resolve_bin_expr(&bin_expr)?;280 let assoc = f.as_assoc_item(sema.db)?;281282 let lhs_type = sema.type_of_expr(&bin_expr.lhs()?)?.original;283 let rhs_type = sema.type_of_expr(&bin_expr.rhs()?)?.original;284285 let t = match assoc.container(sema.db) {286 hir::AssocItemContainer::Trait(t) => t,287 hir::AssocItemContainer::Impl(_) => return None, // Already implemented by the type288 };289290 let fn_name = f.name(sema.db);291 let fn_name_str = fn_name.as_str();292293 let trait_name = t.name(sema.db);294 let trait_name_str = trait_name.as_str();295296 let (target_fn_name, expected_trait) = match fn_name_str {297 "ne" => ("eq", "PartialEq"),298 "lt" | "le" | "gt" | "ge" => ("partial_cmp", "PartialOrd"),299 _ => return None,300 };301302 if trait_name_str != expected_trait {303 return None;304 }305306 let primary_f = t.items(sema.db).into_iter().find_map(|item| {307 if let hir::AssocItem::Function(func) = item308 && func.name(sema.db).as_str() == target_fn_name309 {310 return Some(func);311 }312 None313 })?;314315 // Chalk requires ALL trait substitutions, including `Self`!316 // We must pass [Self, Rhs]317 let resolved_f = sema.resolve_trait_impl_method(318 lhs_type.clone(),319 t,320 primary_f,321 [lhs_type.clone(), rhs_type.clone()],322 )?;323324 let def = Definition::from(resolved_f);325326 Some(def_to_nav(sema, def))327}328fn try_lookup_include_path(329 sema: &Semantics<'_, RootDatabase>,330 token: InFile<ast::String>,331 file_id: FileId,332) -> Option<NavigationTarget> {333 let file = token.file_id.macro_file()?;334335 // Check that we are in the eager argument expansion of an include macro336 // that is we are the string input of it337 if !iter::successors(Some(file), |file| file.parent(sema.db).macro_file())338 .any(|file| file.is_include_like_macro(sema.db) && file.eager_arg(sema.db).is_none())339 {340 return None;341 }342 let path = token.value.value().ok()?;343344 let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;345 let size = sema.db.file_text(file_id).text(sema.db).len().try_into().ok()?;346 Some(NavigationTarget {347 file_id,348 full_range: TextRange::new(0.into(), size),349 name: hir::Symbol::intern(&path),350 alias: None,351 focus_range: None,352 kind: None,353 container_name: None,354 description: None,355 docs: None,356 })357}358359fn try_lookup_macro_def_in_macro_use(360 sema: &Semantics<'_, RootDatabase>,361 token: SyntaxToken,362) -> Option<NavigationTarget> {363 let extern_crate = token.parent()?.ancestors().find_map(ast::ExternCrate::cast)?;364 let extern_crate = sema.to_def(&extern_crate)?;365 let krate = extern_crate.resolved_crate(sema.db)?;366367 for mod_def in krate.root_module(sema.db).declarations(sema.db) {368 if let ModuleDef::Macro(mac) = mod_def369 && mac.name(sema.db).as_str() == token.text()370 && let Some(nav) = mac.try_to_nav(sema)371 {372 return Some(nav.call_site);373 }374 }375376 None377}378379/// finds the trait definition of an impl'd item, except function380/// e.g.381/// ```rust382/// trait A { type a; }383/// struct S;384/// impl A for S { type a = i32; } // <-- on this associate type, will get the location of a in the trait385/// ```386fn try_filter_trait_item_definition(387 sema: &Semantics<'_, RootDatabase>,388 def: &Definition,389) -> Option<Vec<NavigationTarget>> {390 let db = sema.db;391 let assoc = def.as_assoc_item(db)?;392 match assoc {393 AssocItem::Function(..) => None,394 AssocItem::Const(..) | AssocItem::TypeAlias(..) => {395 let trait_ = assoc.implemented_trait(db)?;396 let name = def.name(db)?;397 let discriminant_value = discriminant(&assoc);398 trait_399 .items(db)400 .iter()401 .filter(|itm| discriminant(*itm) == discriminant_value)402 .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(sema)).flatten())403 .map(|it| it.collect())404 }405 }406}407408fn handle_control_flow_keywords(409 sema: &Semantics<'_, RootDatabase>,410 token: &SyntaxToken,411) -> Option<Vec<NavigationTarget>> {412 match token.kind() {413 // For `fn` / `loop` / `while` / `for` / `async` / `match`, return the keyword it self,414 // so that VSCode will find the references when using `ctrl + click`415 T![fn] | T![async] | T![try] | T![return] => nav_for_exit_points(sema, token),416 T![loop] | T![while] | T![break] | T![continue] => nav_for_break_points(sema, token),417 T![for] if token.parent().and_then(ast::ForExpr::cast).is_some() => {418 nav_for_break_points(sema, token)419 }420 T![match] | T![=>] | T![if] => nav_for_branch_exit_points(sema, token),421 _ => None,422 }423}424425pub(crate) fn find_fn_or_blocks(426 sema: &Semantics<'_, RootDatabase>,427 token: &SyntaxToken,428) -> Vec<SyntaxNode> {429 let find_ancestors = |token: SyntaxToken| {430 let token_kind = token.kind();431432 for anc in sema.token_ancestors_with_macros(token) {433 let node = match_ast! {434 match anc {435 ast::Fn(fn_) => fn_.syntax().clone(),436 ast::ClosureExpr(c) => c.syntax().clone(),437 ast::BlockExpr(blk) => {438 match blk.modifier() {439 Some(ast::BlockModifier::Async(_)) => blk.syntax().clone(),440 Some(ast::BlockModifier::Try { .. }) if token_kind != T![return] => blk.syntax().clone(),441 _ => continue,442 }443 },444 _ => continue,445 }446 };447448 return Some(node);449 }450 None451 };452453 sema.descend_into_macros(token.clone()).into_iter().filter_map(find_ancestors).collect_vec()454}455456fn nav_for_exit_points(457 sema: &Semantics<'_, RootDatabase>,458 token: &SyntaxToken,459) -> Option<Vec<NavigationTarget>> {460 let db = sema.db;461 let token_kind = token.kind();462463 let navs = find_fn_or_blocks(sema, token)464 .into_iter()465 .filter_map(|node| {466 let file_id = sema.hir_file_for(&node);467468 match_ast! {469 match node {470 ast::Fn(fn_) => {471 let mut nav = sema.to_def(&fn_)?.try_to_nav(sema)?;472 // For async token, we navigate to itself, which triggers473 // VSCode to find the references474 let focus_token = if matches!(token_kind, T![async]) {475 fn_.async_token()?476 } else {477 fn_.fn_token()?478 };479480 let focus_frange = InFile::new(file_id, focus_token.text_range())481 .original_node_file_range_opt(db)482 .map(|(frange, _)| frange);483484 if let Some(FileRange { file_id, range }) = focus_frange {485 let contains_frange = |nav: &NavigationTarget| {486 nav.file_id == file_id.file_id(db) && nav.full_range.contains_range(range)487 };488489 if let Some(def_site) = nav.def_site.as_mut() {490 if contains_frange(def_site) {491 def_site.focus_range = Some(range);492 }493 } else if contains_frange(&nav.call_site) {494 nav.call_site.focus_range = Some(range);495 }496 }497498 Some(nav)499 },500 ast::ClosureExpr(c) => {501 let pipe_tok = c.param_list().and_then(|it| it.pipe_token())?.text_range();502 let closure_in_file = InFile::new(file_id, c.into());503 Some(expr_to_nav(db, closure_in_file, Some(pipe_tok)))504 },505 ast::BlockExpr(blk) => {506 match blk.modifier() {507 Some(ast::BlockModifier::Async(_)) => {508 let async_tok = blk.async_token()?.text_range();509 let blk_in_file = InFile::new(file_id, blk.into());510 Some(expr_to_nav(db, blk_in_file, Some(async_tok)))511 },512 Some(ast::BlockModifier::Try { .. }) if token_kind != T![return] => {513 let try_tok = blk.try_block_modifier()?.try_token()?.text_range();514 let blk_in_file = InFile::new(file_id, blk.into());515 Some(expr_to_nav(db, blk_in_file, Some(try_tok)))516 },517 _ => None,518 }519 },520 _ => None,521 }522 }523 })524 .flatten()525 .collect_vec();526527 Some(navs)528}529530pub(crate) fn find_branch_root(531 sema: &Semantics<'_, RootDatabase>,532 token: &SyntaxToken,533) -> Vec<SyntaxNode> {534 let find_nodes = |node_filter: fn(SyntaxNode) -> Option<SyntaxNode>| {535 sema.descend_into_macros(token.clone())536 .into_iter()537 .filter_map(|token| node_filter(token.parent()?))538 .collect_vec()539 };540541 match token.kind() {542 T![match] => find_nodes(|node| Some(ast::MatchExpr::cast(node)?.syntax().clone())),543 T![=>] => find_nodes(|node| Some(ast::MatchArm::cast(node)?.syntax().clone())),544 T![if] => find_nodes(|node| {545 let if_expr = ast::IfExpr::cast(node)?;546547 let root_if = iter::successors(Some(if_expr.clone()), |if_expr| {548 let parent_if = if_expr.syntax().parent().and_then(ast::IfExpr::cast)?;549 let ast::ElseBranch::IfExpr(else_branch) = parent_if.else_branch()? else {550 return None;551 };552553 (else_branch.syntax() == if_expr.syntax()).then_some(parent_if)554 })555 .last()?;556557 Some(root_if.syntax().clone())558 }),559 _ => vec![],560 }561}562563fn nav_for_branch_exit_points(564 sema: &Semantics<'_, RootDatabase>,565 token: &SyntaxToken,566) -> Option<Vec<NavigationTarget>> {567 let db = sema.db;568569 let navs = match token.kind() {570 T![match] => find_branch_root(sema, token)571 .into_iter()572 .filter_map(|node| {573 let file_id = sema.hir_file_for(&node);574 let match_expr = ast::MatchExpr::cast(node)?;575 let focus_range = match_expr.match_token()?.text_range();576 let match_expr_in_file = InFile::new(file_id, match_expr.into());577 Some(expr_to_nav(db, match_expr_in_file, Some(focus_range)))578 })579 .flatten()580 .collect_vec(),581582 T![=>] => find_branch_root(sema, token)583 .into_iter()584 .filter_map(|node| {585 let match_arm = ast::MatchArm::cast(node)?;586 let match_expr = sema587 .ancestors_with_macros(match_arm.syntax().clone())588 .find_map(ast::MatchExpr::cast)?;589 let file_id = sema.hir_file_for(match_expr.syntax());590 let focus_range = match_arm.fat_arrow_token()?.text_range();591 let match_expr_in_file = InFile::new(file_id, match_expr.into());592 Some(expr_to_nav(db, match_expr_in_file, Some(focus_range)))593 })594 .flatten()595 .collect_vec(),596597 T![if] => find_branch_root(sema, token)598 .into_iter()599 .filter_map(|node| {600 let file_id = sema.hir_file_for(&node);601 let if_expr = ast::IfExpr::cast(node)?;602 let focus_range = if_expr.if_token()?.text_range();603 let if_expr_in_file = InFile::new(file_id, if_expr.into());604 Some(expr_to_nav(db, if_expr_in_file, Some(focus_range)))605 })606 .flatten()607 .collect_vec(),608609 _ => return Some(Vec::new()),610 };611612 Some(navs)613}614615fn nav_for_break_points(616 sema: &Semantics<'_, RootDatabase>,617 token: &SyntaxToken,618) -> Option<Vec<NavigationTarget>> {619 let db = sema.db;620621 let navs = find_loops(sema, token)?622 .filter_map(|expr| {623 let file_id = sema.hir_file_for(expr.syntax());624 let expr_in_file = InFile::new(file_id, expr.clone());625 let focus_range = match expr {626 ast::Expr::LoopExpr(loop_) => loop_.loop_token()?.text_range(),627 ast::Expr::WhileExpr(while_) => while_.while_token()?.text_range(),628 ast::Expr::ForExpr(for_) => for_.for_token()?.text_range(),629 // We guarantee that the label exists630 ast::Expr::BlockExpr(blk) => blk.label().unwrap().syntax().text_range(),631 _ => return None,632 };633 let nav = expr_to_nav(db, expr_in_file, Some(focus_range));634 Some(nav)635 })636 .flatten()637 .collect_vec();638639 Some(navs)640}641642fn def_to_nav(sema: &Semantics<'_, RootDatabase>, def: Definition) -> Vec<NavigationTarget> {643 def.try_to_nav(sema).map(|it| it.collect()).unwrap_or_default()644}645646fn expr_to_nav(647 db: &RootDatabase,648 InFile { file_id, value }: InFile<ast::Expr>,649 focus_range: Option<TextRange>,650) -> UpmappingResult<NavigationTarget> {651 let kind = SymbolKind::Label;652653 let value_range = value.syntax().text_range();654 let navs = navigation_target::orig_range_with_focus_r(db, file_id, value_range, focus_range);655 navs.map(|(hir::FileRangeWrapper { file_id, range }, focus_range)| {656 NavigationTarget::from_syntax(657 file_id,658 hir::Symbol::intern("<expr>"),659 focus_range,660 range,661 kind,662 )663 })664}665666#[cfg(test)]667mod tests {668 use crate::{GotoDefinitionConfig, fixture};669 use ide_db::{FileRange, ra_fixture::RaFixtureConfig};670 use itertools::Itertools;671672 const TEST_CONFIG: GotoDefinitionConfig<'_> =673 GotoDefinitionConfig { ra_fixture: RaFixtureConfig::default() };674675 #[track_caller]676 fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {677 let (analysis, position, expected) = fixture::annotations(ra_fixture);678 let navs = analysis679 .goto_definition(position, &TEST_CONFIG)680 .unwrap()681 .expect("no definition found")682 .info;683684 let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());685 let navs = navs686 .into_iter()687 .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })688 .sorted_by_key(cmp)689 .collect::<Vec<_>>();690 let expected = expected691 .into_iter()692 .map(|(FileRange { file_id, range }, _)| FileRange { file_id, range })693 .sorted_by_key(cmp)694 .collect::<Vec<_>>();695696 assert_eq!(expected, navs);697 }698699 fn check_unresolved(#[rust_analyzer::rust_fixture] ra_fixture: &str) {700 let (analysis, position) = fixture::position(ra_fixture);701 let navs = analysis702 .goto_definition(position, &TEST_CONFIG)703 .unwrap()704 .expect("no definition found")705 .info;706707 assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}")708 }709710 fn check_name(expected_name: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str) {711 let (analysis, position, _) = fixture::annotations(ra_fixture);712 let navs = analysis713 .goto_definition(position, &TEST_CONFIG)714 .unwrap()715 .expect("no definition found")716 .info;717 assert!(navs.len() < 2, "expected single navigation target but encountered {}", navs.len());718 let Some(target) = navs.into_iter().next() else {719 panic!("expected single navigation target but encountered none");720 };721 assert_eq!(target.name, hir::Symbol::intern(expected_name));722 }723724 #[test]725 fn goto_def_pat_range_to_inclusive() {726 check_name(727 "RangeToInclusive",728 r#"729//- minicore: range730fn f(ch: char) -> bool {731 match ch {732 ..$0='z' => true,733 _ => false734 }735}736"#,737 );738 }739740 #[test]741 fn goto_def_pat_range_to() {742 check_name(743 "RangeTo",744 r#"745//- minicore: range746fn f(ch: char) -> bool {747 match ch {748 .$0.'z' => true,749 _ => false750 }751}752"#,753 );754 }755756 #[test]757 fn goto_def_pat_range() {758 check_name(759 "Range",760 r#"761//- minicore: range762fn f(ch: char) -> bool {763 match ch {764 'a'.$0.'z' => true,765 _ => false766 }767}768"#,769 );770 }771772 #[test]773 fn goto_def_pat_range_inclusive() {774 check_name(775 "RangeInclusive",776 r#"777//- minicore: range778fn f(ch: char) -> bool {779 match ch {780 'a'..$0='z' => true,781 _ => false782 }783}784"#,785 );786 }787788 #[test]789 fn goto_def_pat_range_from() {790 check_name(791 "RangeFrom",792 r#"793//- minicore: range794fn f(ch: char) -> bool {795 match ch {796 'a'..$0 => true,797 _ => false798 }799}800"#,801 );802 }803804 #[test]805 fn goto_def_expr_range() {806 check_name(807 "Range",808 r#"809//- minicore: range810let x = 0.$0.1;811"#,812 );813 }814815 #[test]816 fn goto_def_expr_range_from() {817 check_name(818 "RangeFrom",819 r#"820//- minicore: range821fn f(arr: &[i32]) -> &[i32] {822 &arr[0.$0.]823}824"#,825 );826 }827828 #[test]829 fn goto_def_expr_range_inclusive() {830 check_name(831 "RangeInclusive",832 r#"833//- minicore: range834let x = 0.$0.=1;835"#,836 );837 }838839 #[test]840 fn goto_def_expr_range_full() {841 check_name(842 "RangeFull",843 r#"844//- minicore: range845fn f(arr: &[i32]) -> &[i32] {846 &arr[.$0.]847}848"#,849 );850 }851852 #[test]853 fn goto_def_expr_range_to() {854 check_name(855 "RangeTo",856 r#"857//- minicore: range858fn f(arr: &[i32]) -> &[i32] {859 &arr[.$0.10]860}861"#,862 );863 }864865 #[test]866 fn goto_def_expr_range_to_inclusive() {867 check_name(868 "RangeToInclusive",869 r#"870//- minicore: range871fn f(arr: &[i32]) -> &[i32] {872 &arr[.$0.=10]873}874"#,875 );876 }877878 #[test]879 fn goto_def_in_included_file() {880 check(881 r#"882//- minicore:include883//- /main.rs884885include!("a.rs");886887fn main() {888 foo();889}890891//- /a.rs892fn func_in_include() {893 //^^^^^^^^^^^^^^^894}895896fn foo() {897 func_in_include$0();898}899"#,900 );901 }902903 #[test]904 fn goto_def_in_included_file_nested() {905 check(906 r#"907//- minicore:include908//- /main.rs909910macro_rules! passthrough {911 ($($tt:tt)*) => { $($tt)* }912}913914passthrough!(include!("a.rs"));915916fn main() {917 foo();918}919920//- /a.rs921fn func_in_include() {922 //^^^^^^^^^^^^^^^923}924925fn foo() {926 func_in_include$0();927}928"#,929 );930 }931932 #[test]933 fn goto_def_in_included_file_inside_mod() {934 check(935 r#"936//- minicore:include937//- /main.rs938mod a {939 include!("b.rs");940}941//- /b.rs942fn func_in_include() {943 //^^^^^^^^^^^^^^^944}945fn foo() {946 func_in_include$0();947}948"#,949 );950951 check(952 r#"953//- minicore:include954//- /main.rs955mod a {956 include!("a.rs");957}958//- /a.rs959fn func_in_include() {960 //^^^^^^^^^^^^^^^961}962963fn foo() {964 func_in_include$0();965}966"#,967 );968 }969970 #[test]971 fn goto_def_if_items_same_name() {972 check(973 r#"974trait Trait {975 type A;976 const A: i32;977 //^978}979980struct T;981impl Trait for T {982 type A = i32;983 const A$0: i32 = -9;984}"#,985 );986 }987 #[test]988 fn goto_def_in_mac_call_in_attr_invoc() {989 check(990 r#"991//- proc_macros: identity992pub struct Struct {993 // ^^^^^^994 field: i32,995}996997macro_rules! identity {998 ($($tt:tt)*) => {$($tt)*};999}10001001#[proc_macros::identity]1002fn function() {1003 identity!(Struct$0 { field: 0 });1004}10051006"#,1007 )1008 }10091010 #[test]1011 fn goto_def_for_extern_crate() {1012 check(1013 r#"1014//- /main.rs crate:main deps:std1015extern crate std$0;1016//- /std/lib.rs crate:std1017// empty1018//^file1019"#,1020 )1021 }10221023 #[test]1024 fn goto_def_for_renamed_extern_crate() {1025 check(1026 r#"1027//- /main.rs crate:main deps:std1028extern crate std as abc$0;1029//- /std/lib.rs crate:std1030// empty1031//^file1032"#,1033 )1034 }10351036 #[test]1037 fn goto_def_in_items() {1038 check(1039 r#"1040struct Foo;1041 //^^^1042enum E { X(Foo$0) }1043"#,1044 );1045 }10461047 #[test]1048 fn goto_def_at_start_of_item() {1049 check(1050 r#"1051struct Foo;1052 //^^^1053enum E { X($0Foo) }1054"#,1055 );1056 }10571058 #[test]1059 fn goto_definition_resolves_correct_name() {1060 check(1061 r#"1062//- /lib.rs1063use a::Foo;1064mod a;1065mod b;1066enum E { X(Foo$0) }10671068//- /a.rs1069pub struct Foo;1070 //^^^1071//- /b.rs1072pub struct Foo;1073"#,1074 );1075 }10761077 #[test]1078 fn goto_def_for_module_declaration() {1079 check(1080 r#"1081//- /lib.rs1082mod $0foo;10831084//- /foo.rs1085// empty1086//^file1087"#,1088 );10891090 check(1091 r#"1092//- /lib.rs1093mod $0foo;10941095//- /foo/mod.rs1096// empty1097//^file1098"#,1099 );1100 }11011102 #[test]1103 fn goto_def_for_macros() {1104 check(1105 r#"1106macro_rules! foo { () => { () } }1107 //^^^1108fn bar() {1109 $0foo!();1110}1111"#,1112 );1113 }11141115 #[test]1116 fn goto_def_for_macros_from_other_crates() {1117 check(1118 r#"1119//- /lib.rs crate:main deps:foo1120use foo::foo;1121fn bar() {1122 $0foo!();1123}11241125//- /foo/lib.rs crate:foo1126#[macro_export]1127macro_rules! foo { () => { () } }1128 //^^^1129"#,1130 );1131 }11321133 #[test]1134 fn goto_def_for_macros_in_use_tree() {1135 check(1136 r#"1137//- /lib.rs crate:main deps:foo1138use foo::foo$0;11391140//- /foo/lib.rs crate:foo1141#[macro_export]1142macro_rules! foo { () => { () } }1143 //^^^1144"#,1145 );1146 }11471148 #[test]1149 fn goto_def_for_macro_defined_fn_with_arg() {1150 check(1151 r#"1152//- /lib.rs1153macro_rules! define_fn {1154 ($name:ident) => (fn $name() {})1155}11561157define_fn!(foo);1158 //^^^11591160fn bar() {1161 $0foo();1162}1163"#,1164 );1165 }11661167 #[test]1168 fn goto_def_for_macro_defined_fn_no_arg() {1169 check(1170 r#"1171//- /lib.rs1172macro_rules! define_fn {1173 () => (fn foo() {})1174 //^^^1175}11761177 define_fn!();1178//^^^^^^^^^^1179fn bar() {1180 $0foo();1181}1182"#,1183 );1184 }11851186 #[test]1187 fn goto_definition_works_for_macro_inside_pattern() {1188 check(1189 r#"1190//- /lib.rs1191macro_rules! foo {() => {0}}1192 //^^^11931194fn bar() {1195 match (0,1) {1196 ($0foo!(), _) => {}1197 }1198}1199"#,1200 );1201 }12021203 #[test]1204 fn goto_definition_works_for_macro_inside_match_arm_lhs() {1205 check(1206 r#"1207//- /lib.rs1208macro_rules! foo {() => {0}}1209 //^^^1210fn bar() {1211 match 0 {1212 $0foo!() => {}1213 }1214}1215"#,1216 );1217 }12181219 #[test]1220 fn goto_definition_works_for_consts_inside_range_pattern() {1221 check(1222 r#"1223//- /lib.rs1224const A: u32 = 0;1225 //^12261227fn bar(v: u32) {1228 match v {1229 0..=$0A => {}1230 _ => {}1231 }1232}1233"#,1234 );1235 }12361237 #[test]1238 fn goto_def_for_use_alias() {1239 check(1240 r#"1241//- /lib.rs crate:main deps:foo1242use foo as bar$0;12431244//- /foo/lib.rs crate:foo1245// empty1246//^file1247"#,1248 );1249 }12501251 #[test]1252 fn goto_def_for_use_alias_foo_macro() {1253 check(1254 r#"1255//- /lib.rs crate:main deps:foo1256use foo::foo as bar$0;12571258//- /foo/lib.rs crate:foo1259#[macro_export]1260macro_rules! foo { () => { () } }1261 //^^^1262"#,1263 );1264 }12651266 #[test]1267 fn goto_def_for_methods() {1268 check(1269 r#"1270struct Foo;1271impl Foo {1272 fn frobnicate(&self) { }1273 //^^^^^^^^^^1274}12751276fn bar(foo: &Foo) {1277 foo.frobnicate$0();1278}1279"#,1280 );1281 }12821283 #[test]1284 fn goto_def_for_fields() {1285 check(1286 r#"1287struct Foo {1288 spam: u32,1289} //^^^^12901291fn bar(foo: &Foo) {1292 foo.spam$0;1293}1294"#,1295 );1296 }12971298 #[test]1299 fn goto_def_for_record_fields() {1300 check(1301 r#"1302//- /lib.rs1303struct Foo {1304 spam: u32,1305} //^^^^13061307fn bar() -> Foo {1308 Foo {1309 spam$0: 0,1310 }1311}1312"#,1313 );1314 }13151316 #[test]1317 fn goto_def_for_record_pat_fields() {1318 check(1319 r#"1320//- /lib.rs1321struct Foo {1322 spam: u32,1323} //^^^^13241325fn bar(foo: Foo) -> Foo {1326 let Foo { spam$0: _, } = foo1327}1328"#,1329 );1330 }13311332 #[test]1333 fn goto_def_for_record_fields_macros() {1334 check(1335 r"1336macro_rules! m { () => { 92 };}1337struct Foo { spam: u32 }1338 //^^^^13391340fn bar() -> Foo {1341 Foo { spam$0: m!() }1342}1343",1344 );1345 }13461347 #[test]1348 fn goto_for_tuple_fields() {1349 check(1350 r#"1351struct Foo(u32);1352 //^^^13531354fn bar() {1355 let foo = Foo(0);1356 foo.$00;1357}1358"#,1359 );1360 }13611362 #[test]1363 fn goto_def_for_ufcs_inherent_methods() {1364 check(1365 r#"1366struct Foo;1367impl Foo {1368 fn frobnicate() { }1369} //^^^^^^^^^^13701371fn bar(foo: &Foo) {1372 Foo::frobnicate$0();1373}1374"#,1375 );1376 }13771378 #[test]1379 fn goto_def_for_ufcs_trait_methods_through_traits() {1380 check(1381 r#"1382trait Foo {1383 fn frobnicate();1384} //^^^^^^^^^^13851386fn bar() {1387 Foo::frobnicate$0();1388}1389"#,1390 );1391 }13921393 #[test]1394 fn goto_def_for_ufcs_trait_methods_through_self() {1395 check(1396 r#"1397struct Foo;1398trait Trait {1399 fn frobnicate();1400} //^^^^^^^^^^1401impl Trait for Foo {}14021403fn bar() {1404 Foo::frobnicate$0();1405}1406"#,1407 );1408 }14091410 #[test]1411 fn goto_definition_on_self() {1412 check(1413 r#"1414struct Foo;1415impl Foo {1416 //^^^1417 pub fn new() -> Self {1418 Self$0 {}1419 }1420}1421"#,1422 );1423 check(1424 r#"1425struct Foo;1426impl Foo {1427 //^^^1428 pub fn new() -> Self$0 {1429 Self {}1430 }1431}1432"#,1433 );14341435 check(1436 r#"1437enum Foo { A }1438impl Foo {1439 //^^^1440 pub fn new() -> Self$0 {1441 Foo::A1442 }1443}1444"#,1445 );14461447 check(1448 r#"1449enum Foo { A }1450impl Foo {1451 //^^^1452 pub fn thing(a: &Self$0) {1453 }1454}1455"#,1456 );1457 }14581459 #[test]1460 fn goto_definition_on_self_in_trait_impl() {1461 check(1462 r#"1463struct Foo;1464trait Make {1465 fn new() -> Self;1466}1467impl Make for Foo {1468 //^^^1469 fn new() -> Self {1470 Self$0 {}1471 }1472}1473"#,1474 );14751476 check(1477 r#"1478struct Foo;1479trait Make {1480 fn new() -> Self;1481}1482impl Make for Foo {1483 //^^^1484 fn new() -> Self$0 {1485 Self {}1486 }1487}1488"#,1489 );1490 }14911492 #[test]1493 fn goto_def_when_used_on_definition_name_itself() {1494 check(1495 r#"1496struct Foo$0 { value: u32 }1497 //^^^1498 "#,1499 );15001501 check(1502 r#"1503struct Foo {1504 field$0: string,1505} //^^^^^1506"#,1507 );15081509 check(1510 r#"1511fn foo_test$0() { }1512 //^^^^^^^^1513"#,1514 );15151516 check(1517 r#"1518enum Foo$0 { Variant }1519 //^^^1520"#,1521 );15221523 check(1524 r#"1525enum Foo {1526 Variant1,1527 Variant2$0,1528 //^^^^^^^^1529 Variant3,1530}1531"#,1532 );15331534 check(1535 r#"1536static INNER$0: &str = "";1537 //^^^^^1538"#,1539 );15401541 check(1542 r#"1543const INNER$0: &str = "";1544 //^^^^^1545"#,1546 );15471548 check(1549 r#"1550type Thing$0 = Option<()>;1551 //^^^^^1552"#,1553 );15541555 check(1556 r#"1557trait Foo$0 { }1558 //^^^1559"#,1560 );15611562 check(1563 r#"1564trait Foo$0 = ;1565 //^^^1566"#,1567 );15681569 check(1570 r#"1571mod bar$0 { }1572 //^^^1573"#,1574 );1575 }15761577 #[test]1578 fn goto_from_macro() {1579 check(1580 r#"1581macro_rules! id {1582 ($($tt:tt)*) => { $($tt)* }1583}1584fn foo() {}1585 //^^^1586id! {1587 fn bar() {1588 fo$0o();1589 }1590}1591mod confuse_index { fn foo(); }1592"#,1593 );1594 }15951596 #[test]1597 fn goto_through_format() {1598 check(1599 r#"1600//- minicore: fmt1601#[macro_export]1602macro_rules! format {1603 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*)))1604}1605pub mod __export {1606 pub use core::format_args;1607 fn foo() {} // for index confusion1608}1609fn foo() -> i8 {}1610 //^^^1611fn test() {1612 format!("{}", fo$0o())1613}1614"#,1615 );1616 }16171618 #[test]1619 fn goto_through_included_file() {1620 check(1621 r#"1622//- /main.rs1623#[rustc_builtin_macro]1624macro_rules! include {}16251626include!("foo.rs");16271628fn f() {1629 foo$0();1630}16311632mod confuse_index {1633 pub fn foo() {}1634}16351636//- /foo.rs1637fn foo() {}1638 //^^^1639 "#,1640 );1641 }16421643 #[test]1644 fn goto_through_included_file_struct_with_doc_comment() {1645 check(1646 r#"1647//- /main.rs1648#[rustc_builtin_macro]1649macro_rules! include {}16501651include!("foo.rs");16521653fn f() {1654 let x = Foo$0;1655}16561657mod confuse_index {1658 pub struct Foo;1659}16601661//- /foo.rs1662/// This is a doc comment1663pub struct Foo;1664 //^^^1665 "#,1666 );1667 }16681669 #[test]1670 fn goto_for_type_param() {1671 check(1672 r#"1673struct Foo<T: Clone> { t: $0T }1674 //^1675"#,1676 );1677 }16781679 #[test]1680 fn goto_within_macro() {1681 check(1682 r#"1683macro_rules! id {1684 ($($tt:tt)*) => ($($tt)*)1685}16861687fn foo() {1688 let x = 1;1689 //^1690 id!({1691 let y = $0x;1692 let z = y;1693 });1694}1695"#,1696 );16971698 check(1699 r#"1700macro_rules! id {1701 ($($tt:tt)*) => ($($tt)*)1702}17031704fn foo() {1705 let x = 1;1706 id!({1707 let y = x;1708 //^1709 let z = $0y;1710 });1711}1712"#,1713 );1714 }17151716 #[test]1717 fn goto_def_in_local_fn() {1718 check(1719 r#"1720fn main() {1721 fn foo() {1722 let x = 92;1723 //^1724 $0x;1725 }1726}1727"#,1728 );1729 }17301731 #[test]1732 fn goto_def_in_local_macro() {1733 check(1734 r#"1735fn bar() {1736 macro_rules! foo { () => { () } }1737 //^^^1738 $0foo!();1739}1740"#,1741 );1742 }17431744 #[test]1745 fn goto_def_for_field_init_shorthand() {1746 check(1747 r#"1748struct Foo { x: i32 }1749 //^1750fn main() {1751 let x = 92;1752 //^1753 Foo { x$0 };1754}1755"#,1756 )1757 }17581759 #[test]1760 fn goto_def_for_enum_variant_field() {1761 check(1762 r#"1763enum Foo {1764 Bar { x: i32 }1765 //^1766}1767fn baz(foo: Foo) {1768 match foo {1769 Foo::Bar { x$0 } => x1770 //^1771 };1772}1773"#,1774 );1775 }17761777 #[test]1778 fn goto_def_for_enum_variant_self_pattern_const() {1779 check(1780 r#"1781enum Foo { Bar }1782 //^^^1783impl Foo {1784 fn baz(self) {1785 match self { Self::Bar$0 => {} }1786 }1787}1788"#,1789 );1790 }17911792 #[test]1793 fn goto_def_for_enum_variant_self_pattern_record() {1794 check(1795 r#"1796enum Foo { Bar { val: i32 } }1797 //^^^1798impl Foo {1799 fn baz(self) -> i32 {1800 match self { Self::Bar$0 { val } => {} }1801 }1802}1803"#,1804 );1805 }18061807 #[test]1808 fn goto_def_for_enum_variant_self_expr_const() {1809 check(1810 r#"1811enum Foo { Bar }1812 //^^^1813impl Foo {1814 fn baz(self) { Self::Bar$0; }1815}1816"#,1817 );1818 }18191820 #[test]1821 fn goto_def_for_enum_variant_self_expr_record() {1822 check(1823 r#"1824enum Foo { Bar { val: i32 } }1825 //^^^1826impl Foo {1827 fn baz(self) { Self::Bar$0 {val: 4}; }1828}1829"#,1830 );1831 }18321833 #[test]1834 fn goto_def_for_type_alias_generic_parameter() {1835 check(1836 r#"1837type Alias<T> = T$0;1838 //^1839"#,1840 )1841 }18421843 #[test]1844 fn goto_def_for_macro_container() {1845 check(1846 r#"1847//- /lib.rs crate:main deps:foo1848foo::module$0::mac!();18491850//- /foo/lib.rs crate:foo1851pub mod module {1852 //^^^^^^1853 #[macro_export]1854 macro_rules! _mac { () => { () } }1855 pub use crate::_mac as mac;1856}1857"#,1858 );1859 }18601861 #[test]1862 fn goto_def_for_assoc_ty_in_path() {1863 check(1864 r#"1865trait Iterator {1866 type Item;1867 //^^^^1868}18691870fn f() -> impl Iterator<Item$0 = u8> {}1871"#,1872 );1873 }18741875 #[test]1876 fn goto_def_for_super_assoc_ty_in_path() {1877 check(1878 r#"1879trait Super {1880 type Item;1881 //^^^^1882}18831884trait Sub: Super {}18851886fn f() -> impl Sub<Item$0 = u8> {}1887"#,1888 );1889 }18901891 #[test]1892 fn goto_def_for_module_declaration_in_path_if_types_and_values_same_name() {1893 check(1894 r#"1895mod bar {1896 pub struct Foo {}1897 //^^^1898 pub fn Foo() {}1899}19001901fn baz() {1902 let _foo_enum: bar::Foo$0 = bar::Foo {};1903}1904 "#,1905 )1906 }19071908 #[test]1909 fn unknown_assoc_ty() {1910 check_unresolved(1911 r#"1912trait Iterator { type Item; }1913fn f() -> impl Iterator<Invalid$0 = u8> {}1914"#,1915 )1916 }19171918 #[test]1919 fn goto_def_for_assoc_ty_in_path_multiple() {1920 check(1921 r#"1922trait Iterator {1923 type A;1924 //^1925 type B;1926}19271928fn f() -> impl Iterator<A$0 = u8, B = ()> {}1929"#,1930 );1931 check(1932 r#"1933trait Iterator {1934 type A;1935 type B;1936 //^1937}19381939fn f() -> impl Iterator<A = u8, B$0 = ()> {}1940"#,1941 );1942 }19431944 #[test]1945 fn goto_def_for_assoc_ty_ufcs() {1946 check(1947 r#"1948trait Iterator {1949 type Item;1950 //^^^^1951}19521953fn g() -> <() as Iterator<Item$0 = ()>>::Item {}1954"#,1955 );1956 }19571958 #[test]1959 fn goto_def_for_assoc_ty_ufcs_multiple() {1960 check(1961 r#"1962trait Iterator {1963 type A;1964 //^1965 type B;1966}19671968fn g() -> <() as Iterator<A$0 = (), B = u8>>::B {}1969"#,1970 );1971 check(1972 r#"1973trait Iterator {1974 type A;1975 type B;1976 //^1977}19781979fn g() -> <() as Iterator<A = (), B$0 = u8>>::A {}1980"#,1981 );1982 }19831984 #[test]1985 fn goto_self_param_ty_specified() {1986 check(1987 r#"1988struct Foo {}19891990impl Foo {1991 fn bar(self: &Foo) {1992 //^^^^1993 let foo = sel$0f;1994 }1995}"#,1996 )1997 }19981999 #[test]2000 fn goto_self_param_on_decl() {
Findings
✓ No findings reported for this file.