src/tools/rust-analyzer/crates/ide/src/goto_definition.rs RUST 4,201 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 4,201.
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// ![Go to Definition](https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif)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.

Get this view in your editor

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