File is large — showing lines 1–2,000 of 3,380.
1//! This module implements a reference search.2//! First, the element at the cursor position must be either an `ast::Name`3//! or `ast::NameRef`. If it's an `ast::NameRef`, at the classification step we4//! try to resolve the direct tree parent of this element, otherwise we5//! already have a definition and just need to get its HIR together with6//! some information that is needed for further steps of searching.7//! After that, we collect files that might contain references and look8//! for text occurrences of the identifier. If there's an `ast::NameRef`9//! at the index that the match starts at and its tree parent is10//! resolved to the search element definition, we get a reference.11//!12//! Special handling for constructors/initializations:13//! When searching for references to a struct/enum/variant, if the cursor is positioned on:14//! - `{` after a struct/enum/variant definition15//! - `(` for tuple structs/variants16//! - `;` for unit structs17//! - The type name in a struct/enum/variant definition18//! Then only constructor/initialization usages will be shown, filtering out other references.1920use hir::{PathResolution, Semantics};21use ide_db::{22 FileId, RootDatabase,23 base_db::SourceDatabase,24 defs::{Definition, NameClass, NameRefClass},25 helpers::pick_best_token,26 ra_fixture::{RaFixtureConfig, UpmapFromRaFixture},27 search::{ReferenceCategory, SearchScope, UsageSearchResult},28};29use itertools::Itertools;30use macros::UpmapFromRaFixture;31use nohash_hasher::IntMap;32use syntax::AstToken;33use syntax::{34 AstNode,35 SyntaxKind::*,36 SyntaxNode, T, TextRange, TextSize,37 ast::{self, HasName},38 match_ast,39};4041use crate::{42 Analysis, FilePosition, HighlightedRange, NavigationTarget, TryToNav,43 doc_links::token_as_doc_comment, highlight_related,44};4546/// Result of a reference search operation.47#[derive(Debug, Clone, UpmapFromRaFixture)]48pub struct ReferenceSearchResult {49 /// Information about the declaration site of the searched item.50 /// For ADTs (structs/enums), this points to the type definition.51 /// May be None for primitives or items without clear declaration sites.52 pub declaration: Option<Declaration>,53 /// All references found, grouped by file.54 /// For ADTs when searching from a constructor position (e.g. on '{', '(', ';'),55 /// this only includes constructor/initialization usages.56 /// The map key is the file ID, and the value is a vector of (range, category) pairs.57 /// - range: The text range of the reference in the file58 /// - category: Metadata about how the reference is used (read/write/etc)59 pub references: IntMap<FileId, Vec<(TextRange, ReferenceCategory)>>,60}6162/// Information about the declaration site of a searched item.63#[derive(Debug, Clone, UpmapFromRaFixture)]64pub struct Declaration {65 /// Navigation information to jump to the declaration66 pub nav: NavigationTarget,67 /// Whether the declared item is mutable (relevant for variables)68 pub is_mut: bool,69}7071// Feature: Find All References72//73// Shows all references of the item at the cursor location. This includes:74// - Direct references to variables, functions, types, etc.75// - Constructor/initialization references when cursor is on struct/enum definition tokens76// - References in patterns and type contexts77// - References through dereferencing and borrowing78// - References in macro expansions79//80// Special handling for constructors:81// - When the cursor is on `{`, `(`, or `;` in a struct/enum definition82// These cases will show only constructor/initialization usages of the type83// (for example, `S { .. }`, `S(..)`, or `S`) instead of every type reference.84//85// | Editor | Shortcut |86// |---------|----------|87// | VS Code | <kbd>Shift+Alt+F12</kbd> |88//89// 9091#[derive(Debug)]92pub struct FindAllRefsConfig<'a> {93 pub search_scope: Option<SearchScope>,94 pub ra_fixture: RaFixtureConfig<'a>,95 pub exclude_imports: bool,96 pub exclude_tests: bool,97}9899/// Find all references to the item at the given position.100///101/// # Arguments102/// * `sema` - Semantic analysis context103/// * `position` - Position in the file where to look for the item104/// * `search_scope` - Optional scope to limit the search (e.g. current crate only)105///106/// # Returns107/// Returns `None` if no valid item is found at the position.108/// Otherwise returns a vector of `ReferenceSearchResult`, usually with one element.109/// Multiple results can occur in case of ambiguity or when searching for trait items.110///111/// # Special cases112/// - Control flow keywords (break, continue, etc): Shows all related jump points113/// - Constructor search: When on struct/enum definition tokens (`{`, `(`, `;`), shows only initialization sites114/// - Format string arguments: Shows template parameter usages115/// - Lifetime parameters: Shows lifetime constraint usages116///117/// # Constructor search118/// When the cursor is on specific tokens in a struct/enum definition:119/// - `{` after struct/enum/variant: Shows record literal initializations120/// - `(` after tuple struct/variant: Shows tuple literal initializations121/// - `;` after unit struct: Shows unit literal initializations122/// - Type name in definition: Shows all initialization usages123/// In these cases, other kinds of references (like type references) are filtered out.124pub(crate) fn find_all_refs<'db>(125 sema: &Semantics<'db, RootDatabase>,126 position: FilePosition,127 config: &FindAllRefsConfig<'_>,128) -> Option<Vec<ReferenceSearchResult>> {129 let _p = tracing::info_span!("find_all_refs").entered();130 let syntax = sema.parse_guess_edition(position.file_id).syntax().clone();131 let exclude_library_refs = !is_library_file(sema.db, position.file_id);132 let make_searcher = |literal_search: bool| {133 move |def: Definition<'db>| {134 let mut included_categories = ReferenceCategory::all();135 if config.exclude_imports {136 included_categories.remove(ReferenceCategory::IMPORT);137 }138 if config.exclude_tests {139 included_categories.remove(ReferenceCategory::TEST);140 }141 let mut usages = def142 .usages(sema)143 .set_scope(config.search_scope.as_ref())144 .set_included_categories(included_categories)145 .set_exclude_library_files(exclude_library_refs)146 .include_self_refs()147 .all();148 if literal_search {149 retain_adt_literal_usages(&mut usages, def, sema);150 }151152 let mut references: IntMap<FileId, Vec<(TextRange, ReferenceCategory)>> = usages153 .into_iter()154 .map(|(file_id, refs)| {155 (156 file_id.file_id(sema.db),157 refs.into_iter()158 .map(|file_ref| (file_ref.range, file_ref.category))159 .unique()160 .collect(),161 )162 })163 .collect();164 let declaration = match def {165 Definition::Module(module) => {166 Some(NavigationTarget::from_module_to_decl(sema.db, module))167 }168 def => def.try_to_nav(sema),169 }170 .map(|nav| {171 let (nav, extra_ref) = match nav.def_site {172 Some(call) => (call, Some(nav.call_site)),173 None => (nav.call_site, None),174 };175 if let Some(extra_ref) = extra_ref {176 references177 .entry(extra_ref.file_id)178 .or_default()179 .push((extra_ref.focus_or_full_range(), ReferenceCategory::empty()));180 }181 Declaration {182 is_mut: matches!(def, Definition::Local(l) if l.is_mut(sema.db)),183 nav,184 }185 });186 ReferenceSearchResult { declaration, references }187 }188 };189190 // Find references for control-flow keywords.191 if let Some(res) = handle_control_flow_keywords(sema, position) {192 return Some(vec![res]);193 }194195 if let Some(token) = syntax.token_at_offset(position.offset).left_biased()196 && let Some(token) = ast::String::cast(token.clone())197 && let Some((analysis, fixture_analysis)) =198 Analysis::from_ra_fixture(sema, token.clone(), &token, &config.ra_fixture)199 && let Some((virtual_file_id, file_offset)) =200 fixture_analysis.map_offset_down(position.offset)201 {202 return analysis203 .find_all_refs(FilePosition { file_id: virtual_file_id, offset: file_offset }, config)204 .ok()??205 .upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, position.file_id)206 .ok();207 }208209 match name_for_constructor_search(&syntax, position) {210 Some(name) => {211 let def = match NameClass::classify(sema, &name)? {212 NameClass::Definition(it) | NameClass::ConstReference(it) => it,213 NameClass::PatFieldShorthand { local_def: _, field_ref, adt_subst: _ } => {214 Definition::Field(field_ref)215 }216 };217 Some(vec![make_searcher(true)(def)])218 }219 None => {220 let search = make_searcher(false);221 Some(find_defs(sema, &syntax, position.offset)?.into_iter().map(search).collect())222 }223 }224}225226fn is_library_file(db: &RootDatabase, file_id: FileId) -> bool {227 let source_root = db.file_source_root(file_id).source_root_id(db);228 db.source_root(source_root).source_root(db).is_library229}230231pub(crate) fn find_defs<'db>(232 sema: &Semantics<'db, RootDatabase>,233 syntax: &SyntaxNode,234 offset: TextSize,235) -> Option<Vec<Definition<'db>>> {236 if let Some(token) = syntax.token_at_offset(offset).left_biased()237 && let Some(doc_comment) = token_as_doc_comment(&token)238 {239 return doc_comment240 .get_definition_with_descend_at(sema, offset, |def, _, _| Some(vec![def]));241 }242243 let token = syntax.token_at_offset(offset).find(|t| {244 matches!(245 t.kind(),246 IDENT247 | INT_NUMBER248 | LIFETIME_IDENT249 | STRING250 | T![self]251 | T![super]252 | T![crate]253 | T![Self]254 )255 })?;256257 if let Some((.., resolution)) = sema.check_for_format_args_template(token.clone(), offset) {258 return resolution.map(Definition::from).map(|it| vec![it]);259 }260261 Some(262 sema.descend_into_macros_exact(token)263 .into_iter()264 .filter_map(|it| ast::NameLike::cast(it.parent()?))265 .filter_map(move |name_like| {266 let def = match name_like {267 ast::NameLike::NameRef(name_ref) => {268 match NameRefClass::classify(sema, &name_ref)? {269 NameRefClass::Definition(def, _) => def,270 NameRefClass::FieldShorthand {271 local_ref,272 field_ref: _,273 adt_subst: _,274 } => Definition::Local(local_ref),275 NameRefClass::ExternCrateShorthand { decl, .. } => {276 Definition::ExternCrateDecl(decl)277 }278 }279 }280 ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? {281 NameClass::Definition(it) | NameClass::ConstReference(it) => it,282 NameClass::PatFieldShorthand { local_def, field_ref: _, adt_subst: _ } => {283 Definition::Local(local_def)284 }285 },286 ast::NameLike::Lifetime(lifetime) => {287 NameRefClass::classify_lifetime(sema, &lifetime)288 .and_then(|class| match class {289 NameRefClass::Definition(it, _) => Some(it),290 _ => None,291 })292 .or_else(|| {293 NameClass::classify_lifetime(sema, &lifetime)294 .and_then(NameClass::defined)295 })?296 }297 };298 Some(def)299 })300 .collect(),301 )302}303304/// Filter out all non-literal usages for adt-defs305fn retain_adt_literal_usages(306 usages: &mut UsageSearchResult,307 def: Definition<'_>,308 sema: &Semantics<'_, RootDatabase>,309) {310 let refs = usages.references.values_mut();311 match def {312 Definition::Adt(hir::Adt::Enum(enum_)) => {313 refs.for_each(|it| {314 it.retain(|reference| {315 reference316 .name317 .as_name_ref()318 .is_some_and(|name_ref| is_enum_lit_name_ref(sema, enum_, name_ref))319 })320 });321 usages.references.retain(|_, it| !it.is_empty());322 }323 Definition::Adt(_) | Definition::EnumVariant(_) => {324 refs.for_each(|it| {325 it.retain(|reference| reference.name.as_name_ref().is_some_and(is_lit_name_ref))326 });327 usages.references.retain(|_, it| !it.is_empty());328 }329 _ => {}330 }331}332333/// Returns `Some` if the cursor is at a position where we should search for constructor/initialization usages.334/// This is used to implement the special constructor search behavior when the cursor is on specific tokens335/// in a struct/enum/variant definition.336///337/// # Returns338/// - `Some(name)` if the cursor is on:339/// - `{` after a struct/enum/variant definition340/// - `(` for tuple structs/variants341/// - `;` for unit structs342/// - The type name in a struct/enum/variant definition343/// - `None` otherwise344///345/// The returned name is the name of the type whose constructor usages should be searched for.346fn name_for_constructor_search(syntax: &SyntaxNode, position: FilePosition) -> Option<ast::Name> {347 let token = syntax.token_at_offset(position.offset).right_biased()?;348 let token_parent = token.parent()?;349 let kind = token.kind();350 if kind == T![;] {351 ast::Struct::cast(token_parent)352 .filter(|struct_| struct_.field_list().is_none())353 .and_then(|struct_| struct_.name())354 } else if kind == T!['{'] {355 match_ast! {356 match token_parent {357 ast::RecordFieldList(rfl) => match_ast! {358 match (rfl.syntax().parent()?) {359 ast::Variant(it) => it.name(),360 ast::Struct(it) => it.name(),361 ast::Union(it) => it.name(),362 _ => None,363 }364 },365 ast::VariantList(vl) => ast::Enum::cast(vl.syntax().parent()?)?.name(),366 _ => None,367 }368 }369 } else if kind == T!['('] {370 let tfl = ast::TupleFieldList::cast(token_parent)?;371 match_ast! {372 match (tfl.syntax().parent()?) {373 ast::Variant(it) => it.name(),374 ast::Struct(it) => it.name(),375 _ => None,376 }377 }378 } else {379 None380 }381}382383/// Checks if a name reference is part of an enum variant literal expression.384/// Used to filter references when searching for enum variant constructors.385///386/// # Arguments387/// * `sema` - Semantic analysis context388/// * `enum_` - The enum type to check against389/// * `name_ref` - The name reference to check390///391/// # Returns392/// `true` if the name reference is used as part of constructing a variant of the given enum.393fn is_enum_lit_name_ref(394 sema: &Semantics<'_, RootDatabase>,395 enum_: hir::Enum,396 name_ref: &ast::NameRef,397) -> bool {398 let path_is_variant_of_enum = |path: ast::Path| {399 matches!(400 sema.resolve_path(&path),401 Some(PathResolution::Def(hir::ModuleDef::EnumVariant(variant)))402 if variant.parent_enum(sema.db) == enum_403 )404 };405 name_ref406 .syntax()407 .ancestors()408 .find_map(|ancestor| {409 match_ast! {410 match ancestor {411 ast::PathExpr(path_expr) => path_expr.path().map(path_is_variant_of_enum),412 ast::RecordExpr(record_expr) => record_expr.path().map(path_is_variant_of_enum),413 _ => None,414 }415 }416 })417 .unwrap_or(false)418}419420/// Checks if a path ends with the given name reference.421/// Helper function for checking constructor usage patterns.422fn path_ends_with(path: Option<ast::Path>, name_ref: &ast::NameRef) -> bool {423 path.and_then(|path| path.segment())424 .and_then(|segment| segment.name_ref())425 .map_or(false, |segment| segment == *name_ref)426}427428/// Checks if a name reference is used in a literal (constructor) context.429/// Used to filter references when searching for struct/variant constructors.430///431/// # Returns432/// `true` if the name reference is used as part of a struct/variant literal expression.433fn is_lit_name_ref(name_ref: &ast::NameRef) -> bool {434 name_ref.syntax().ancestors().find_map(|ancestor| {435 match_ast! {436 match ancestor {437 ast::PathExpr(path_expr) => Some(path_ends_with(path_expr.path(), name_ref)),438 ast::RecordExpr(record_expr) => Some(path_ends_with(record_expr.path(), name_ref)),439 _ => None,440 }441 }442 }).unwrap_or(false)443}444445fn handle_control_flow_keywords(446 sema: &Semantics<'_, RootDatabase>,447 FilePosition { file_id, offset }: FilePosition,448) -> Option<ReferenceSearchResult> {449 let file = sema.parse_guess_edition(file_id);450 let edition = sema.attach_first_edition(file_id).edition(sema.db);451 let token = pick_best_token(file.syntax().token_at_offset(offset), |kind| match kind {452 _ if kind.is_keyword(edition) => 4,453 T![=>] => 3,454 _ => 1,455 })?;456457 let references = match token.kind() {458 T![fn] | T![return] | T![try] => highlight_related::highlight_exit_points(sema, token),459 T![async] => highlight_related::highlight_yield_points(sema, token),460 T![loop] | T![while] | T![break] | T![continue] => {461 highlight_related::highlight_break_points(sema, token)462 }463 T![for] if token.parent().and_then(ast::ForExpr::cast).is_some() => {464 highlight_related::highlight_break_points(sema, token)465 }466 T![if] | T![=>] | T![match] => highlight_related::highlight_branch_exit_points(sema, token),467 _ => return None,468 }469 .into_iter()470 .map(|(file_id, ranges)| {471 let ranges = ranges472 .into_iter()473 .map(|HighlightedRange { range, category }| (range, category))474 .collect();475 (file_id.file_id(sema.db), ranges)476 })477 .collect();478479 Some(ReferenceSearchResult { declaration: None, references })480}481482#[cfg(test)]483mod tests {484 use expect_test::{Expect, expect};485 use hir::EditionedFileId;486 use ide_db::{FileId, RootDatabase, ra_fixture::RaFixtureConfig};487 use stdx::format_to;488489 use crate::{SearchScope, fixture, references::FindAllRefsConfig};490491 #[test]492 fn exclude_tests() {493 check_with_filters(494 r#"495fn test_func() {}496497fn func() {498 test_func$0();499}500501#[test]502fn test() {503 test_func();504}505"#,506 false,507 false,508 expect![[r#"509 test_func Function FileId(0) 0..17 3..12510511 FileId(0) 35..44512 FileId(0) 75..84 test513 "#]],514 );515516 check_with_filters(517 r#"518fn test_func() {}519520fn func() {521 test_func$0();522}523524#[::core::prelude::v1::test]525fn test() {526 test_func();527}528"#,529 false,530 false,531 expect![[r#"532 test_func Function FileId(0) 0..17 3..12533534 FileId(0) 35..44535 FileId(0) 96..105 test536 "#]],537 );538539 check_with_filters(540 r#"541fn test_func() {}542543fn func() {544 test_func$0();545}546547#[test]548fn test() {549 test_func();550}551"#,552 false,553 true,554 expect![[r#"555 test_func Function FileId(0) 0..17 3..12556557 FileId(0) 35..44558 "#]],559 );560 }561562 #[test]563 fn exclude_library_refs_filtering() {564 // exclude refs in 3rd party lib565 check_with_filters(566 r#"567//- /main.rs crate:main deps:dep568use dep::foo;569570fn main() {571 foo$0();572}573574//- /dep/lib.rs crate:dep new_source_root:library575pub fn foo() {}576577pub fn also_calls_foo() {578 foo();579}580"#,581 false,582 false,583 // FIXME: The ranges here are volatile when minicore changes, that's not good.584 expect![[r#"585 foo Function FileId(1) 0..15 7..10586587 FileId(0) 9..12 import588 FileId(0) 31..34589 "#]],590 );591592 // exclude refs in stdlib593 check_with_filters(594 r#"595//- minicore: option596fn main() {597 let _ = core::option::Option::Some$0(0);598}599"#,600 false,601 false,602 expect![[r#"603 Some Variant FileId(1) 6735..6767 6760..6764604605 FileId(0) 46..50606 "#]],607 );608609 // keep refs in local lib610 check_with_filters(611 r#"612//- /main.rs crate:main deps:dep613use dep::foo;614615fn main() {616 foo$0();617}618619//- /dep/lib.rs crate:dep620pub fn foo() {}621622pub fn also_calls_foo() {623 foo();624}625"#,626 false,627 false,628 expect![[r#"629 foo Function FileId(1) 0..15 7..10630631 FileId(0) 9..12 import632 FileId(0) 31..34633 FileId(1) 47..50634 "#]],635 );636 }637638 #[test]639 fn find_refs_from_library_source_keeps_library_refs() {640 check_with_filters(641 r#"642//- /main.rs crate:main deps:dep643use dep::foo;644645fn main() {646 foo();647}648649//- /dep/lib.rs crate:dep new_source_root:library650pub fn foo$0() {}651652pub fn also_calls_foo() {653 foo();654}655"#,656 false,657 false,658 expect![[r#"659 foo Function FileId(1) 0..15 7..10660661 FileId(0) 9..12 import662 FileId(0) 31..34663 FileId(1) 47..50664 "#]],665 );666 }667668 #[test]669 fn exclude_tests_macro_refs() {670 check(671 r#"672macro_rules! my_macro {673 ($e:expr) => { $e };674}675676fn foo$0() -> i32 { 42 }677678fn bar() {679 foo();680}681682#[test]683fn t2() {684 my_macro!(foo());685}686"#,687 expect![[r#"688 foo Function FileId(0) 52..74 55..58689690 FileId(0) 91..94691 FileId(0) 133..136 test692 "#]],693 );694 }695 #[test]696 fn test_struct_literal_after_space() {697 check(698 r#"699struct Foo $0{700 a: i32,701}702impl Foo {703 fn f() -> i32 { 42 }704}705fn main() {706 let f: Foo;707 f = Foo {a: Foo::f()};708}709"#,710 expect![[r#"711 Foo Struct FileId(0) 0..26 7..10712713 FileId(0) 101..104714 "#]],715 );716 }717718 #[test]719 fn test_struct_literal_before_space() {720 check(721 r#"722struct Foo$0 {}723 fn main() {724 let f: Foo;725 f = Foo {};726}727"#,728 expect![[r#"729 Foo Struct FileId(0) 0..13 7..10730731 FileId(0) 41..44732 FileId(0) 54..57733 "#]],734 );735 }736737 #[test]738 fn test_struct_literal_with_generic_type() {739 check(740 r#"741struct Foo<T> $0{}742 fn main() {743 let f: Foo::<i32>;744 f = Foo {};745}746"#,747 expect![[r#"748 Foo Struct FileId(0) 0..16 7..10749750 FileId(0) 64..67751 "#]],752 );753 }754755 #[test]756 fn test_struct_literal_for_tuple() {757 check(758 r#"759struct Foo$0(i32);760761fn main() {762 let f: Foo;763 f = Foo(1);764}765"#,766 expect![[r#"767 Foo Struct FileId(0) 0..16 7..10768769 FileId(0) 54..57770 "#]],771 );772 }773774 #[test]775 fn test_struct_literal_for_union() {776 check(777 r#"778union Foo $0{779 x: u32780}781782fn main() {783 let f: Foo;784 f = Foo { x: 1 };785}786"#,787 expect![[r#"788 Foo Union FileId(0) 0..24 6..9789790 FileId(0) 62..65791 "#]],792 );793 }794795 #[test]796 fn test_enum_after_space() {797 check(798 r#"799enum Foo $0{800 A,801 B(),802 C{},803}804fn main() {805 let f: Foo;806 f = Foo::A;807 f = Foo::B();808 f = Foo::C{};809}810"#,811 expect![[r#"812 Foo Enum FileId(0) 0..37 5..8813814 FileId(0) 74..77815 FileId(0) 90..93816 FileId(0) 108..111817 "#]],818 );819 }820821 #[test]822 fn test_variant_record_after_space() {823 check(824 r#"825enum Foo {826 A $0{ n: i32 },827 B,828}829fn main() {830 let f: Foo;831 f = Foo::B;832 f = Foo::A { n: 92 };833}834"#,835 expect![[r#"836 A Variant FileId(0) 15..27 15..16837838 FileId(0) 95..96839 "#]],840 );841 }842843 #[test]844 fn test_variant_tuple_before_paren() {845 check(846 r#"847enum Foo {848 A$0(i32),849 B,850}851fn main() {852 let f: Foo;853 f = Foo::B;854 f = Foo::A(92);855}856"#,857 expect![[r#"858 A Variant FileId(0) 15..21 15..16859860 FileId(0) 89..90861 "#]],862 );863 }864865 #[test]866 fn test_enum_before_space() {867 check(868 r#"869enum Foo$0 {870 A,871 B,872}873fn main() {874 let f: Foo;875 f = Foo::A;876}877"#,878 expect![[r#"879 Foo Enum FileId(0) 0..26 5..8880881 FileId(0) 50..53882 FileId(0) 63..66883 "#]],884 );885 }886887 #[test]888 fn test_enum_with_generic_type() {889 check(890 r#"891enum Foo<T> $0{892 A(T),893 B,894}895fn main() {896 let f: Foo<i8>;897 f = Foo::A(1);898}899"#,900 expect![[r#"901 Foo Enum FileId(0) 0..32 5..8902903 FileId(0) 73..76904 "#]],905 );906 }907908 #[test]909 fn test_enum_for_tuple() {910 check(911 r#"912enum Foo$0{913 A(i8),914 B(i8),915}916fn main() {917 let f: Foo;918 f = Foo::A(1);919}920"#,921 expect![[r#"922 Foo Enum FileId(0) 0..33 5..8923924 FileId(0) 70..73925 "#]],926 );927 }928929 #[test]930 fn test_find_all_refs_for_local() {931 check(932 r#"933fn main() {934 let mut i = 1;935 let j = 1;936 i = i$0 + j;937938 {939 i = 0;940 }941942 i = 5;943}"#,944 expect![[r#"945 i Local FileId(0) 20..25 24..25 write946947 FileId(0) 50..51 write948 FileId(0) 54..55 read949 FileId(0) 76..77 write950 FileId(0) 94..95 write951 "#]],952 );953 }954955 #[test]956 fn test_find_all_refs_in_comments() {957 check(958 r#"959struct Foo;960961/// $0[`Foo`] is just above962struct Bar;963"#,964 expect![[r#"965 Foo Struct FileId(0) 0..11 7..10966967 (no references)968 "#]],969 );970 }971972 #[test]973 fn search_filters_by_range() {974 check(975 r#"976fn foo() {977 let spam$0 = 92;978 spam + spam979}980fn bar() {981 let spam = 92;982 spam + spam983}984"#,985 expect![[r#"986 spam Local FileId(0) 19..23 19..23987988 FileId(0) 34..38 read989 FileId(0) 41..45 read990 "#]],991 );992 }993994 #[test]995 fn test_find_all_refs_for_param_inside() {996 check(997 r#"998fn foo(i : u32) -> u32 { i$0 }999"#,1000 expect![[r#"1001 i ValueParam FileId(0) 7..8 7..810021003 FileId(0) 25..26 read1004 "#]],1005 );1006 }10071008 #[test]1009 fn test_find_all_refs_for_fn_param() {1010 check(1011 r#"1012fn foo(i$0 : u32) -> u32 { i }1013"#,1014 expect![[r#"1015 i ValueParam FileId(0) 7..8 7..810161017 FileId(0) 25..26 read1018 "#]],1019 );1020 }10211022 #[test]1023 fn test_find_all_refs_field_name() {1024 check(1025 r#"1026//- /lib.rs1027struct Foo {1028 pub spam$0: u32,1029}10301031fn main(s: Foo) {1032 let f = s.spam;1033}1034"#,1035 expect![[r#"1036 spam Field FileId(0) 17..30 21..2510371038 FileId(0) 67..71 read1039 "#]],1040 );1041 }10421043 #[test]1044 fn test_find_all_refs_impl_item_name() {1045 check(1046 r#"1047struct Foo;1048impl Foo {1049 fn f$0(&self) { }1050}1051"#,1052 expect![[r#"1053 f Function FileId(0) 27..43 30..3110541055 (no references)1056 "#]],1057 );1058 }10591060 #[test]1061 fn test_find_all_refs_enum_var_name() {1062 check(1063 r#"1064enum Foo {1065 A,1066 B$0,1067 C,1068}1069"#,1070 expect![[r#"1071 B Variant FileId(0) 22..23 22..2310721073 (no references)1074 "#]],1075 );1076 }10771078 #[test]1079 fn test_find_all_refs_enum_var_field() {1080 check(1081 r#"1082enum Foo {1083 A,1084 B { field$0: u8 },1085 C,1086}1087"#,1088 expect![[r#"1089 field Field FileId(0) 26..35 26..3110901091 (no references)1092 "#]],1093 );1094 }10951096 #[test]1097 fn test_self() {1098 check(1099 r#"1100struct S$0<T> {1101 t: PhantomData<T>,1102}11031104impl<T> S<T> {1105 fn new() -> Self {1106 Self {1107 t: Default::default(),1108 }1109 }1110}1111"#,1112 expect![[r#"1113 S Struct FileId(0) 0..38 7..811141115 FileId(0) 48..491116 FileId(0) 71..751117 FileId(0) 86..901118 "#]],1119 )1120 }11211122 #[test]1123 fn test_self_inside_not_adt_impl() {1124 check(1125 r#"1126pub trait TestTrait {1127 type Assoc;1128 fn stuff() -> Self;1129}1130impl TestTrait for () {1131 type Assoc$0 = u8;1132 fn stuff() -> Self {1133 let me: Self = ();1134 me1135 }1136}1137"#,1138 expect![[r#"1139 Assoc TypeAlias FileId(0) 92..108 97..10211401141 FileId(0) 31..361142 "#]],1143 )1144 }11451146 #[test]1147 fn test_find_all_refs_two_modules() {1148 check(1149 r#"1150//- /lib.rs1151pub mod foo;1152pub mod bar;11531154fn f() {1155 let i = foo::Foo { n: 5 };1156}11571158//- /foo.rs1159use crate::bar;11601161pub struct Foo {1162 pub n: u32,1163}11641165fn f() {1166 let i = bar::Bar { n: 5 };1167}11681169//- /bar.rs1170use crate::foo;11711172pub struct Bar {1173 pub n: u32,1174}11751176fn f() {1177 let i = foo::Foo$0 { n: 5 };1178}1179"#,1180 expect![[r#"1181 Foo Struct FileId(1) 17..51 28..31 foo11821183 FileId(0) 53..561184 FileId(2) 79..821185 "#]],1186 );1187 }11881189 #[test]1190 fn test_find_all_refs_decl_module() {1191 check(1192 r#"1193//- /lib.rs1194mod foo$0;11951196use foo::Foo;11971198fn f() {1199 let i = Foo { n: 5 };1200}12011202//- /foo.rs1203pub struct Foo {1204 pub n: u32,1205}1206"#,1207 expect![[r#"1208 foo Module FileId(0) 0..8 4..712091210 FileId(0) 14..17 import1211 "#]],1212 );1213 }12141215 #[test]1216 fn test_find_all_refs_decl_module_on_self() {1217 check(1218 r#"1219//- /lib.rs1220mod foo;12211222//- /foo.rs1223use self$0;1224"#,1225 expect![[r#"1226 foo Module FileId(0) 0..8 4..712271228 FileId(1) 4..8 import1229 "#]],1230 );1231 }12321233 #[test]1234 fn test_find_all_refs_decl_module_on_self_crate_root() {1235 check(1236 r#"1237//- /lib.rs1238use self$0;1239"#,1240 expect![[r#"1241 _ CrateRoot FileId(0) 0..1012421243 FileId(0) 4..8 import1244 "#]],1245 );1246 }12471248 #[test]1249 fn test_find_all_refs_super_mod_vis() {1250 check(1251 r#"1252//- /lib.rs1253mod foo;12541255//- /foo.rs1256mod some;1257use some::Foo;12581259fn f() {1260 let i = Foo { n: 5 };1261}12621263//- /foo/some.rs1264pub(super) struct Foo$0 {1265 pub n: u32,1266}1267"#,1268 expect![[r#"1269 Foo Struct FileId(2) 0..41 18..21 some12701271 FileId(1) 20..23 import1272 FileId(1) 47..501273 "#]],1274 );1275 }12761277 #[test]1278 fn test_find_all_refs_with_scope() {1279 let code = r#"1280 //- /lib.rs1281 mod foo;1282 mod bar;12831284 pub fn quux$0() {}12851286 //- /foo.rs1287 fn f() { super::quux(); }12881289 //- /bar.rs1290 fn f() { super::quux(); }1291 "#;12921293 check_with_scope(1294 code,1295 None,1296 expect![[r#"1297 quux Function FileId(0) 19..35 26..3012981299 FileId(1) 16..201300 FileId(2) 16..201301 "#]],1302 );13031304 check_with_scope(1305 code,1306 Some(&mut |db| {1307 SearchScope::single_file(EditionedFileId::current_edition(db, FileId::from_raw(2)))1308 }),1309 expect![[r#"1310 quux Function FileId(0) 19..35 26..3013111312 FileId(2) 16..201313 "#]],1314 );1315 }13161317 #[test]1318 fn test_find_all_refs_macro_def() {1319 check(1320 r#"1321#[macro_export]1322macro_rules! m1$0 { () => (()) }13231324fn foo() {1325 m1();1326 m1();1327}1328"#,1329 expect![[r#"1330 m1 Macro FileId(0) 0..46 29..3113311332 FileId(0) 63..651333 FileId(0) 73..751334 "#]],1335 );1336 }13371338 #[test]1339 fn test_basic_highlight_read_write() {1340 check(1341 r#"1342fn foo() {1343 let mut i$0 = 0;1344 i = i + 1;1345}1346"#,1347 expect![[r#"1348 i Local FileId(0) 19..24 23..24 write13491350 FileId(0) 34..35 write1351 FileId(0) 38..39 read1352 "#]],1353 );1354 }13551356 #[test]1357 fn test_basic_highlight_field_read_write() {1358 check(1359 r#"1360struct S {1361 f: u32,1362}13631364fn foo() {1365 let mut s = S{f: 0};1366 s.f$0 = 0;1367}1368"#,1369 expect![[r#"1370 f Field FileId(0) 15..21 15..1613711372 FileId(0) 55..56 read1373 FileId(0) 68..69 write1374 "#]],1375 );1376 }13771378 #[test]1379 fn test_basic_highlight_decl_no_write() {1380 check(1381 r#"1382fn foo() {1383 let i$0;1384 i = 1;1385}1386"#,1387 expect![[r#"1388 i Local FileId(0) 19..20 19..2013891390 FileId(0) 26..27 write1391 "#]],1392 );1393 }13941395 #[test]1396 fn test_find_struct_function_refs_outside_module() {1397 check(1398 r#"1399mod foo {1400 pub struct Foo;14011402 impl Foo {1403 pub fn new$0() -> Foo { Foo }1404 }1405}14061407fn main() {1408 let _f = foo::Foo::new();1409}1410"#,1411 expect![[r#"1412 new Function FileId(0) 54..81 61..6414131414 FileId(0) 126..1291415 "#]],1416 );1417 }14181419 #[test]1420 fn test_find_all_refs_nested_module() {1421 check(1422 r#"1423//- /lib.rs1424mod foo { mod bar; }14251426fn f$0() {}14271428//- /foo/bar.rs1429use crate::f;14301431fn g() { f(); }1432"#,1433 expect![[r#"1434 f Function FileId(0) 22..31 25..2614351436 FileId(1) 11..12 import1437 FileId(1) 24..251438 "#]],1439 );1440 }14411442 #[test]1443 fn test_find_all_refs_struct_pat() {1444 check(1445 r#"1446struct S {1447 field$0: u8,1448}14491450fn f(s: S) {1451 match s {1452 S { field } => {}1453 }1454}1455"#,1456 expect![[r#"1457 field Field FileId(0) 15..24 15..2014581459 FileId(0) 68..73 read1460 "#]],1461 );1462 }14631464 #[test]1465 fn test_find_all_refs_enum_var_pat() {1466 check(1467 r#"1468enum En {1469 Variant {1470 field$0: u8,1471 }1472}14731474fn f(e: En) {1475 match e {1476 En::Variant { field } => {}1477 }1478}1479"#,1480 expect![[r#"1481 field Field FileId(0) 32..41 32..3714821483 FileId(0) 102..107 read1484 "#]],1485 );1486 }14871488 #[test]1489 fn test_find_all_refs_enum_var_privacy() {1490 check(1491 r#"1492mod m {1493 pub enum En {1494 Variant {1495 field$0: u8,1496 }1497 }1498}14991500fn f() -> m::En {1501 m::En::Variant { field: 0 }1502}1503"#,1504 expect![[r#"1505 field Field FileId(0) 56..65 56..6115061507 FileId(0) 125..130 read1508 "#]],1509 );1510 }15111512 #[test]1513 fn test_find_self_refs() {1514 check(1515 r#"1516struct Foo { bar: i32 }15171518impl Foo {1519 fn foo(self) {1520 let x = self$0.bar;1521 if true {1522 let _ = match () {1523 () => self,1524 };1525 }1526 }1527}1528"#,1529 expect![[r#"1530 self SelfParam FileId(0) 47..51 47..5115311532 FileId(0) 71..75 read1533 FileId(0) 152..156 read1534 "#]],1535 );1536 }15371538 #[test]1539 fn test_find_self_refs_decl() {1540 check(1541 r#"1542struct Foo { bar: i32 }15431544impl Foo {1545 fn foo(self$0) {1546 self;1547 }1548}1549"#,1550 expect![[r#"1551 self SelfParam FileId(0) 47..51 47..5115521553 FileId(0) 63..67 read1554 "#]],1555 );1556 }15571558 #[test]1559 fn test_highlight_if_branches() {1560 check(1561 r#"1562fn main() {1563 let x = if$0 true {1564 11565 } else if false {1566 21567 } else {1568 31569 };15701571 println!("x: {}", x);1572}1573"#,1574 expect![[r#"1575 FileId(0) 24..261576 FileId(0) 42..431577 FileId(0) 55..571578 FileId(0) 74..751579 FileId(0) 97..981580 "#]],1581 );1582 }15831584 #[test]1585 fn test_highlight_match_branches() {1586 check(1587 r#"1588fn main() {1589 $0match Some(42) {1590 Some(x) if x > 0 => println!("positive"),1591 Some(0) => println!("zero"),1592 Some(_) => println!("negative"),1593 None => println!("none"),1594 };1595}1596"#,1597 expect![[r#"1598 FileId(0) 16..211599 FileId(0) 61..811600 FileId(0) 102..1181601 FileId(0) 139..1591602 FileId(0) 177..1931603 "#]],1604 );1605 }16061607 #[test]1608 fn test_highlight_match_arm_arrow() {1609 check(1610 r#"1611fn main() {1612 match Some(42) {1613 Some(x) if x > 0 $0=> println!("positive"),1614 Some(0) => println!("zero"),1615 Some(_) => println!("negative"),1616 None => println!("none"),1617 }1618}1619"#,1620 expect![[r#"1621 FileId(0) 58..601622 FileId(0) 61..811623 "#]],1624 );1625 }16261627 #[test]1628 fn test_highlight_nested_branches() {1629 check(1630 r#"1631fn main() {1632 let x = $0if true {1633 if false {1634 11635 } else {1636 match Some(42) {1637 Some(_) => 2,1638 None => 3,1639 }1640 }1641 } else {1642 41643 };16441645 println!("x: {}", x);1646}1647"#,1648 expect![[r#"1649 FileId(0) 24..261650 FileId(0) 65..661651 FileId(0) 140..1411652 FileId(0) 167..1681653 FileId(0) 215..2161654 "#]],1655 );1656 }16571658 #[test]1659 fn test_highlight_match_with_complex_guards() {1660 check(1661 r#"1662fn main() {1663 let x = $0match (x, y) {1664 (a, b) if a > b && a % 2 == 0 => 1,1665 (a, b) if a < b || b % 2 == 1 => 2,1666 (a, _) if a > 40 => 3,1667 _ => 4,1668 };16691670 println!("x: {}", x);1671}1672"#,1673 expect![[r#"1674 FileId(0) 24..291675 FileId(0) 80..811676 FileId(0) 124..1251677 FileId(0) 155..1561678 FileId(0) 171..1721679 "#]],1680 );1681 }16821683 #[test]1684 fn test_highlight_mixed_if_match_expressions() {1685 check(1686 r#"1687fn main() {1688 let x = $0if let Some(x) = Some(42) {1689 11690 } else if let None = None {1691 21692 } else {1693 match 42 {1694 0 => 3,1695 _ => 4,1696 }1697 };1698}1699"#,1700 expect![[r#"1701 FileId(0) 24..261702 FileId(0) 60..611703 FileId(0) 73..751704 FileId(0) 102..1031705 FileId(0) 153..1541706 FileId(0) 173..1741707 "#]],1708 );1709 }17101711 fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {1712 check_with_filters(ra_fixture, false, false, expect)1713 }17141715 fn check_with_filters(1716 #[rust_analyzer::rust_fixture] ra_fixture: &str,1717 exclude_imports: bool,1718 exclude_tests: bool,1719 expect: Expect,1720 ) {1721 check_with_scope_and_filters(ra_fixture, None, exclude_imports, exclude_tests, expect)1722 }17231724 fn check_with_scope(1725 #[rust_analyzer::rust_fixture] ra_fixture: &str,1726 search_scope: Option<&mut dyn FnMut(&RootDatabase) -> SearchScope>,1727 expect: Expect,1728 ) {1729 check_with_scope_and_filters(ra_fixture, search_scope, false, false, expect)1730 }17311732 fn check_with_scope_and_filters(1733 #[rust_analyzer::rust_fixture] ra_fixture: &str,1734 search_scope: Option<&mut dyn FnMut(&RootDatabase) -> SearchScope>,1735 exclude_imports: bool,1736 exclude_tests: bool,1737 expect: Expect,1738 ) {1739 let (analysis, pos) = fixture::position(ra_fixture);1740 let config = FindAllRefsConfig {1741 search_scope: search_scope.map(|it| it(&analysis.db)),1742 ra_fixture: RaFixtureConfig::default(),1743 exclude_imports,1744 exclude_tests,1745 };1746 let refs = analysis.find_all_refs(pos, &config).unwrap().unwrap();17471748 let mut actual = String::new();1749 for mut refs in refs {1750 actual += "\n\n";17511752 if let Some(decl) = refs.declaration {1753 format_to!(actual, "{}", decl.nav.debug_render());1754 if decl.is_mut {1755 format_to!(actual, " write",)1756 }1757 actual += "\n\n";1758 }17591760 for (file_id, references) in &mut refs.references {1761 references.sort_by_key(|(range, _)| range.start());1762 for (range, category) in references {1763 format_to!(actual, "{:?} {:?}", file_id, range);1764 for (name, _flag) in category.iter_names() {1765 format_to!(actual, " {}", name.to_lowercase());1766 }1767 actual += "\n";1768 }1769 }17701771 if refs.references.is_empty() {1772 actual += "(no references)\n";1773 }1774 }1775 expect.assert_eq(actual.trim_start())1776 }17771778 #[test]1779 fn test_find_lifetimes_function() {1780 check(1781 r#"1782trait Foo<'a> {}1783impl<'a> Foo<'a> for &'a () {}1784fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> {1785 fn bar<'a>(_: &'a ()) {}1786 x1787}1788"#,1789 expect![[r#"1790 'a LifetimeParam FileId(0) 55..5717911792 FileId(0) 63..651793 FileId(0) 71..731794 FileId(0) 82..841795 FileId(0) 95..971796 FileId(0) 106..1081797 "#]],1798 );1799 }18001801 #[test]1802 fn test_find_lifetimes_type_alias() {1803 check(1804 r#"1805type Foo<'a, T> where T: 'a$0 = &'a T;1806"#,1807 expect![[r#"1808 'a LifetimeParam FileId(0) 9..1118091810 FileId(0) 25..271811 FileId(0) 31..331812 "#]],1813 );1814 }18151816 #[test]1817 fn test_find_lifetimes_trait_impl() {1818 check(1819 r#"1820trait Foo<'a> {1821 fn foo() -> &'a ();1822}1823impl<'a> Foo<'a> for &'a () {1824 fn foo() -> &'a$0 () {1825 unimplemented!()1826 }1827}1828"#,1829 expect![[r#"1830 'a LifetimeParam FileId(0) 47..4918311832 FileId(0) 55..571833 FileId(0) 64..661834 FileId(0) 89..911835 "#]],1836 );1837 }18381839 #[test]1840 fn test_map_range_to_original() {1841 check(1842 r#"1843macro_rules! foo {($i:ident) => {$i} }1844fn main() {1845 let a$0 = "test";1846 foo!(a);1847}1848"#,1849 expect![[r#"1850 a Local FileId(0) 59..60 59..6018511852 FileId(0) 80..81 read1853 "#]],1854 );1855 }18561857 #[test]1858 fn test_map_range_to_original_ref() {1859 check(1860 r#"1861macro_rules! foo {($i:ident) => {$i} }1862fn main() {1863 let a = "test";1864 foo!(a$0);1865}1866"#,1867 expect![[r#"1868 a Local FileId(0) 59..60 59..6018691870 FileId(0) 80..81 read1871 "#]],1872 );1873 }18741875 #[test]1876 fn test_find_labels() {1877 check(1878 r#"1879fn foo<'a>() -> &'a () {1880 'a: loop {1881 'b: loop {1882 continue 'a$0;1883 }1884 break 'a;1885 }1886}1887"#,1888 expect![[r#"1889 'a Label FileId(0) 29..32 29..3118901891 FileId(0) 80..821892 FileId(0) 108..1101893 "#]],1894 );1895 }18961897 #[test]1898 fn test_find_const_param() {1899 check(1900 r#"1901fn foo<const FOO$0: usize>() -> usize {1902 FOO1903}1904"#,1905 expect![[r#"1906 FOO ConstParam FileId(0) 7..23 13..1619071908 FileId(0) 42..451909 "#]],1910 );1911 }19121913 #[test]1914 fn test_trait() {1915 check(1916 r#"1917trait Foo$0 where Self: {}19181919impl Foo for () {}1920"#,1921 expect![[r#"1922 Foo Trait FileId(0) 0..24 6..919231924 FileId(0) 31..341925 "#]],1926 );1927 }19281929 #[test]1930 fn test_trait_self() {1931 check(1932 r#"1933trait Foo where Self$0 {1934 fn f() -> Self;1935}19361937impl Foo for () {}1938"#,1939 expect![[r#"1940 Self TypeParam FileId(0) 0..44 6..919411942 FileId(0) 16..201943 FileId(0) 37..411944 "#]],1945 );1946 }19471948 #[test]1949 fn test_self_ty() {1950 check(1951 r#"1952 struct $0Foo;19531954 impl Foo where Self: {1955 fn f() -> Self;1956 }1957 "#,1958 expect![[r#"1959 Foo Struct FileId(0) 0..11 7..1019601961 FileId(0) 18..211962 FileId(0) 28..321963 FileId(0) 50..541964 "#]],1965 );1966 check(1967 r#"1968struct Foo;19691970impl Foo where Self: {1971 fn f() -> Self$0;1972}1973"#,1974 expect![[r#"1975 impl Impl FileId(0) 13..57 18..2119761977 FileId(0) 18..211978 FileId(0) 28..321979 FileId(0) 50..541980 "#]],1981 );1982 }1983 #[test]1984 fn test_self_variant_with_payload() {1985 check(1986 r#"1987enum Foo { Bar() }19881989impl Foo {1990 fn foo(self) {1991 match self {1992 Self::Bar$0() => (),1993 }1994 }1995}19961997"#,1998 expect![[r#"1999 Bar Variant FileId(0) 11..16 11..14