rust-analyzer/crates/ide/src/references.rs
Wilfred Hughes deda58e8f1 manual: Convert to mdbook
Split manual.adoc into markdown files, one for each chapter.

For the parts of the manual that are generated from source code doc
comments, update the comments to use markdown syntax and update the
code generators to write to `generated.md` files.

For the weekly release, stop copying the .adoc files to the
`rust-analyzer/rust-analyzer.github.io` at release time. Instead,
we'll sync the manual hourly from this repository.

See https://github.com/rust-analyzer/rust-analyzer.github.io/pull/226
for the sync. This PR should be merged first, and that PR needs to be
merged before the next weekly release.

This change is based on #15795, but rebased and updated. I've also
manually checked each page for markdown syntax issues and fixed any I
encountered.

Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
Co-authored-by: Josh Rotenberg <joshrotenberg@gmail.com>
2025-01-24 13:23:22 -08:00

2778 lines
54 KiB
Rust

//! This module implements a reference search.
//! First, the element at the cursor position must be either an `ast::Name`
//! or `ast::NameRef`. If it's an `ast::NameRef`, at the classification step we
//! try to resolve the direct tree parent of this element, otherwise we
//! already have a definition and just need to get its HIR together with
//! some information that is needed for further steps of searching.
//! After that, we collect files that might contain references and look
//! for text occurrences of the identifier. If there's an `ast::NameRef`
//! at the index that the match starts at and its tree parent is
//! resolved to the search element definition, we get a reference.
use hir::{PathResolution, Semantics};
use ide_db::{
defs::{Definition, NameClass, NameRefClass},
search::{ReferenceCategory, SearchScope, UsageSearchResult},
FileId, RootDatabase,
};
use itertools::Itertools;
use nohash_hasher::IntMap;
use span::Edition;
use syntax::{
ast::{self, HasName},
match_ast, AstNode,
SyntaxKind::*,
SyntaxNode, TextRange, TextSize, T,
};
use crate::{highlight_related, FilePosition, HighlightedRange, NavigationTarget, TryToNav};
#[derive(Debug, Clone)]
pub struct ReferenceSearchResult {
pub declaration: Option<Declaration>,
pub references: IntMap<FileId, Vec<(TextRange, ReferenceCategory)>>,
}
#[derive(Debug, Clone)]
pub struct Declaration {
pub nav: NavigationTarget,
pub is_mut: bool,
}
// Feature: Find All References
//
// Shows all references of the item at the cursor location
//
// | Editor | Shortcut |
// |---------|----------|
// | VS Code | <kbd>Shift+Alt+F12</kbd> |
//
// ![Find All References](https://user-images.githubusercontent.com/48062697/113020670-b7c34f00-917a-11eb-8003-370ac5f2b3cb.gif)
pub(crate) fn find_all_refs(
sema: &Semantics<'_, RootDatabase>,
position: FilePosition,
search_scope: Option<SearchScope>,
) -> Option<Vec<ReferenceSearchResult>> {
let _p = tracing::info_span!("find_all_refs").entered();
let syntax = sema.parse_guess_edition(position.file_id).syntax().clone();
let make_searcher = |literal_search: bool| {
move |def: Definition| {
let mut usages =
def.usages(sema).set_scope(search_scope.as_ref()).include_self_refs().all();
if literal_search {
retain_adt_literal_usages(&mut usages, def, sema);
}
let mut references: IntMap<FileId, Vec<(TextRange, ReferenceCategory)>> = usages
.into_iter()
.map(|(file_id, refs)| {
(
file_id.into(),
refs.into_iter()
.map(|file_ref| (file_ref.range, file_ref.category))
.unique()
.collect(),
)
})
.collect();
let declaration = match def {
Definition::Module(module) => {
Some(NavigationTarget::from_module_to_decl(sema.db, module))
}
def => def.try_to_nav(sema.db),
}
.map(|nav| {
let (nav, extra_ref) = match nav.def_site {
Some(call) => (call, Some(nav.call_site)),
None => (nav.call_site, None),
};
if let Some(extra_ref) = extra_ref {
references
.entry(extra_ref.file_id)
.or_default()
.push((extra_ref.focus_or_full_range(), ReferenceCategory::empty()));
}
Declaration {
is_mut: matches!(def, Definition::Local(l) if l.is_mut(sema.db)),
nav,
}
});
ReferenceSearchResult { declaration, references }
}
};
// Find references for control-flow keywords.
if let Some(res) = handle_control_flow_keywords(sema, position) {
return Some(vec![res]);
}
match name_for_constructor_search(&syntax, position) {
Some(name) => {
let def = match NameClass::classify(sema, &name)? {
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
NameClass::PatFieldShorthand { local_def: _, field_ref, adt_subst: _ } => {
Definition::Field(field_ref)
}
};
Some(vec![make_searcher(true)(def)])
}
None => {
let search = make_searcher(false);
Some(find_defs(sema, &syntax, position.offset)?.into_iter().map(search).collect())
}
}
}
pub(crate) fn find_defs<'a>(
sema: &'a Semantics<'_, RootDatabase>,
syntax: &SyntaxNode,
offset: TextSize,
) -> Option<impl IntoIterator<Item = Definition> + 'a> {
let token = syntax.token_at_offset(offset).find(|t| {
matches!(
t.kind(),
IDENT
| INT_NUMBER
| LIFETIME_IDENT
| STRING
| T![self]
| T![super]
| T![crate]
| T![Self]
)
})?;
if let Some((_, resolution)) = sema.check_for_format_args_template(token.clone(), offset) {
return resolution.map(Definition::from).map(|it| vec![it]);
}
Some(
sema.descend_into_macros_exact(token)
.into_iter()
.filter_map(|it| ast::NameLike::cast(it.parent()?))
.filter_map(move |name_like| {
let def = match name_like {
ast::NameLike::NameRef(name_ref) => {
match NameRefClass::classify(sema, &name_ref)? {
NameRefClass::Definition(def, _) => def,
NameRefClass::FieldShorthand {
local_ref,
field_ref: _,
adt_subst: _,
} => Definition::Local(local_ref),
NameRefClass::ExternCrateShorthand { decl, .. } => {
Definition::ExternCrateDecl(decl)
}
}
}
ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? {
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
NameClass::PatFieldShorthand { local_def, field_ref: _, adt_subst: _ } => {
Definition::Local(local_def)
}
},
ast::NameLike::Lifetime(lifetime) => {
NameRefClass::classify_lifetime(sema, &lifetime)
.and_then(|class| match class {
NameRefClass::Definition(it, _) => Some(it),
_ => None,
})
.or_else(|| {
NameClass::classify_lifetime(sema, &lifetime)
.and_then(NameClass::defined)
})?
}
};
Some(def)
})
.collect(),
)
}
/// Filter out all non-literal usages for adt-defs
fn retain_adt_literal_usages(
usages: &mut UsageSearchResult,
def: Definition,
sema: &Semantics<'_, RootDatabase>,
) {
let refs = usages.references.values_mut();
match def {
Definition::Adt(hir::Adt::Enum(enum_)) => {
refs.for_each(|it| {
it.retain(|reference| {
reference
.name
.as_name_ref()
.is_some_and(|name_ref| is_enum_lit_name_ref(sema, enum_, name_ref))
})
});
usages.references.retain(|_, it| !it.is_empty());
}
Definition::Adt(_) | Definition::Variant(_) => {
refs.for_each(|it| {
it.retain(|reference| reference.name.as_name_ref().is_some_and(is_lit_name_ref))
});
usages.references.retain(|_, it| !it.is_empty());
}
_ => {}
}
}
/// Returns `Some` if the cursor is at a position for an item to search for all its constructor/literal usages
fn name_for_constructor_search(syntax: &SyntaxNode, position: FilePosition) -> Option<ast::Name> {
let token = syntax.token_at_offset(position.offset).right_biased()?;
let token_parent = token.parent()?;
let kind = token.kind();
if kind == T![;] {
ast::Struct::cast(token_parent)
.filter(|struct_| struct_.field_list().is_none())
.and_then(|struct_| struct_.name())
} else if kind == T!['{'] {
match_ast! {
match token_parent {
ast::RecordFieldList(rfl) => match_ast! {
match (rfl.syntax().parent()?) {
ast::Variant(it) => it.name(),
ast::Struct(it) => it.name(),
ast::Union(it) => it.name(),
_ => None,
}
},
ast::VariantList(vl) => ast::Enum::cast(vl.syntax().parent()?)?.name(),
_ => None,
}
}
} else if kind == T!['('] {
let tfl = ast::TupleFieldList::cast(token_parent)?;
match_ast! {
match (tfl.syntax().parent()?) {
ast::Variant(it) => it.name(),
ast::Struct(it) => it.name(),
_ => None,
}
}
} else {
None
}
}
fn is_enum_lit_name_ref(
sema: &Semantics<'_, RootDatabase>,
enum_: hir::Enum,
name_ref: &ast::NameRef,
) -> bool {
let path_is_variant_of_enum = |path: ast::Path| {
matches!(
sema.resolve_path(&path),
Some(PathResolution::Def(hir::ModuleDef::Variant(variant)))
if variant.parent_enum(sema.db) == enum_
)
};
name_ref
.syntax()
.ancestors()
.find_map(|ancestor| {
match_ast! {
match ancestor {
ast::PathExpr(path_expr) => path_expr.path().map(path_is_variant_of_enum),
ast::RecordExpr(record_expr) => record_expr.path().map(path_is_variant_of_enum),
_ => None,
}
}
})
.unwrap_or(false)
}
fn path_ends_with(path: Option<ast::Path>, name_ref: &ast::NameRef) -> bool {
path.and_then(|path| path.segment())
.and_then(|segment| segment.name_ref())
.map_or(false, |segment| segment == *name_ref)
}
fn is_lit_name_ref(name_ref: &ast::NameRef) -> bool {
name_ref.syntax().ancestors().find_map(|ancestor| {
match_ast! {
match ancestor {
ast::PathExpr(path_expr) => Some(path_ends_with(path_expr.path(), name_ref)),
ast::RecordExpr(record_expr) => Some(path_ends_with(record_expr.path(), name_ref)),
_ => None,
}
}
}).unwrap_or(false)
}
fn handle_control_flow_keywords(
sema: &Semantics<'_, RootDatabase>,
FilePosition { file_id, offset }: FilePosition,
) -> Option<ReferenceSearchResult> {
let file = sema.parse_guess_edition(file_id);
let edition =
sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT);
let token = file.syntax().token_at_offset(offset).find(|t| t.kind().is_keyword(edition))?;
let references = match token.kind() {
T![fn] | T![return] | T![try] => highlight_related::highlight_exit_points(sema, token),
T![async] => highlight_related::highlight_yield_points(sema, token),
T![loop] | T![while] | T![break] | T![continue] => {
highlight_related::highlight_break_points(sema, token)
}
T![for] if token.parent().and_then(ast::ForExpr::cast).is_some() => {
highlight_related::highlight_break_points(sema, token)
}
_ => return None,
}
.into_iter()
.map(|(file_id, ranges)| {
let ranges = ranges
.into_iter()
.map(|HighlightedRange { range, category }| (range, category))
.collect();
(file_id.into(), ranges)
})
.collect();
Some(ReferenceSearchResult { declaration: None, references })
}
#[cfg(test)]
mod tests {
use expect_test::{expect, Expect};
use ide_db::FileId;
use span::EditionedFileId;
use stdx::format_to;
use crate::{fixture, SearchScope};
#[test]
fn exclude_tests() {
check(
r#"
fn test_func() {}
fn func() {
test_func$0();
}
#[test]
fn test() {
test_func();
}
"#,
expect![[r#"
test_func Function FileId(0) 0..17 3..12
FileId(0) 35..44
FileId(0) 75..84 test
"#]],
);
check(
r#"
fn test_func() {}
fn func() {
test_func$0();
}
#[::core::prelude::v1::test]
fn test() {
test_func();
}
"#,
expect![[r#"
test_func Function FileId(0) 0..17 3..12
FileId(0) 35..44
FileId(0) 96..105 test
"#]],
);
}
#[test]
fn test_access() {
check(
r#"
struct S { f$0: u32 }
#[test]
fn test() {
let mut x = S { f: 92 };
x.f = 92;
}
"#,
expect![[r#"
f Field FileId(0) 11..17 11..12
FileId(0) 61..62 read test
FileId(0) 76..77 write test
"#]],
);
}
#[test]
fn test_struct_literal_after_space() {
check(
r#"
struct Foo $0{
a: i32,
}
impl Foo {
fn f() -> i32 { 42 }
}
fn main() {
let f: Foo;
f = Foo {a: Foo::f()};
}
"#,
expect![[r#"
Foo Struct FileId(0) 0..26 7..10
FileId(0) 101..104
"#]],
);
}
#[test]
fn test_struct_literal_before_space() {
check(
r#"
struct Foo$0 {}
fn main() {
let f: Foo;
f = Foo {};
}
"#,
expect![[r#"
Foo Struct FileId(0) 0..13 7..10
FileId(0) 41..44
FileId(0) 54..57
"#]],
);
}
#[test]
fn test_struct_literal_with_generic_type() {
check(
r#"
struct Foo<T> $0{}
fn main() {
let f: Foo::<i32>;
f = Foo {};
}
"#,
expect![[r#"
Foo Struct FileId(0) 0..16 7..10
FileId(0) 64..67
"#]],
);
}
#[test]
fn test_struct_literal_for_tuple() {
check(
r#"
struct Foo$0(i32);
fn main() {
let f: Foo;
f = Foo(1);
}
"#,
expect![[r#"
Foo Struct FileId(0) 0..16 7..10
FileId(0) 54..57
"#]],
);
}
#[test]
fn test_struct_literal_for_union() {
check(
r#"
union Foo $0{
x: u32
}
fn main() {
let f: Foo;
f = Foo { x: 1 };
}
"#,
expect![[r#"
Foo Union FileId(0) 0..24 6..9
FileId(0) 62..65
"#]],
);
}
#[test]
fn test_enum_after_space() {
check(
r#"
enum Foo $0{
A,
B(),
C{},
}
fn main() {
let f: Foo;
f = Foo::A;
f = Foo::B();
f = Foo::C{};
}
"#,
expect![[r#"
Foo Enum FileId(0) 0..37 5..8
FileId(0) 74..77
FileId(0) 90..93
FileId(0) 108..111
"#]],
);
}
#[test]
fn test_variant_record_after_space() {
check(
r#"
enum Foo {
A $0{ n: i32 },
B,
}
fn main() {
let f: Foo;
f = Foo::B;
f = Foo::A { n: 92 };
}
"#,
expect![[r#"
A Variant FileId(0) 15..27 15..16
FileId(0) 95..96
"#]],
);
}
#[test]
fn test_variant_tuple_before_paren() {
check(
r#"
enum Foo {
A$0(i32),
B,
}
fn main() {
let f: Foo;
f = Foo::B;
f = Foo::A(92);
}
"#,
expect![[r#"
A Variant FileId(0) 15..21 15..16
FileId(0) 89..90
"#]],
);
}
#[test]
fn test_enum_before_space() {
check(
r#"
enum Foo$0 {
A,
B,
}
fn main() {
let f: Foo;
f = Foo::A;
}
"#,
expect![[r#"
Foo Enum FileId(0) 0..26 5..8
FileId(0) 50..53
FileId(0) 63..66
"#]],
);
}
#[test]
fn test_enum_with_generic_type() {
check(
r#"
enum Foo<T> $0{
A(T),
B,
}
fn main() {
let f: Foo<i8>;
f = Foo::A(1);
}
"#,
expect![[r#"
Foo Enum FileId(0) 0..32 5..8
FileId(0) 73..76
"#]],
);
}
#[test]
fn test_enum_for_tuple() {
check(
r#"
enum Foo$0{
A(i8),
B(i8),
}
fn main() {
let f: Foo;
f = Foo::A(1);
}
"#,
expect![[r#"
Foo Enum FileId(0) 0..33 5..8
FileId(0) 70..73
"#]],
);
}
#[test]
fn test_find_all_refs_for_local() {
check(
r#"
fn main() {
let mut i = 1;
let j = 1;
i = i$0 + j;
{
i = 0;
}
i = 5;
}"#,
expect![[r#"
i Local FileId(0) 20..25 24..25 write
FileId(0) 50..51 write
FileId(0) 54..55 read
FileId(0) 76..77 write
FileId(0) 94..95 write
"#]],
);
}
#[test]
fn search_filters_by_range() {
check(
r#"
fn foo() {
let spam$0 = 92;
spam + spam
}
fn bar() {
let spam = 92;
spam + spam
}
"#,
expect![[r#"
spam Local FileId(0) 19..23 19..23
FileId(0) 34..38 read
FileId(0) 41..45 read
"#]],
);
}
#[test]
fn test_find_all_refs_for_param_inside() {
check(
r#"
fn foo(i : u32) -> u32 { i$0 }
"#,
expect![[r#"
i ValueParam FileId(0) 7..8 7..8
FileId(0) 25..26 read
"#]],
);
}
#[test]
fn test_find_all_refs_for_fn_param() {
check(
r#"
fn foo(i$0 : u32) -> u32 { i }
"#,
expect![[r#"
i ValueParam FileId(0) 7..8 7..8
FileId(0) 25..26 read
"#]],
);
}
#[test]
fn test_find_all_refs_field_name() {
check(
r#"
//- /lib.rs
struct Foo {
pub spam$0: u32,
}
fn main(s: Foo) {
let f = s.spam;
}
"#,
expect![[r#"
spam Field FileId(0) 17..30 21..25
FileId(0) 67..71 read
"#]],
);
}
#[test]
fn test_find_all_refs_impl_item_name() {
check(
r#"
struct Foo;
impl Foo {
fn f$0(&self) { }
}
"#,
expect![[r#"
f Function FileId(0) 27..43 30..31
(no references)
"#]],
);
}
#[test]
fn test_find_all_refs_enum_var_name() {
check(
r#"
enum Foo {
A,
B$0,
C,
}
"#,
expect![[r#"
B Variant FileId(0) 22..23 22..23
(no references)
"#]],
);
}
#[test]
fn test_find_all_refs_enum_var_field() {
check(
r#"
enum Foo {
A,
B { field$0: u8 },
C,
}
"#,
expect![[r#"
field Field FileId(0) 26..35 26..31
(no references)
"#]],
);
}
#[test]
fn test_self() {
check(
r#"
struct S$0<T> {
t: PhantomData<T>,
}
impl<T> S<T> {
fn new() -> Self {
Self {
t: Default::default(),
}
}
}
"#,
expect![[r#"
S Struct FileId(0) 0..38 7..8
FileId(0) 48..49
FileId(0) 71..75
FileId(0) 86..90
"#]],
)
}
#[test]
fn test_self_inside_not_adt_impl() {
check(
r#"
pub trait TestTrait {
type Assoc;
fn stuff() -> Self;
}
impl TestTrait for () {
type Assoc$0 = u8;
fn stuff() -> Self {
let me: Self = ();
me
}
}
"#,
expect![[r#"
Assoc TypeAlias FileId(0) 92..108 97..102
FileId(0) 31..36
"#]],
)
}
#[test]
fn test_find_all_refs_two_modules() {
check(
r#"
//- /lib.rs
pub mod foo;
pub mod bar;
fn f() {
let i = foo::Foo { n: 5 };
}
//- /foo.rs
use crate::bar;
pub struct Foo {
pub n: u32,
}
fn f() {
let i = bar::Bar { n: 5 };
}
//- /bar.rs
use crate::foo;
pub struct Bar {
pub n: u32,
}
fn f() {
let i = foo::Foo$0 { n: 5 };
}
"#,
expect![[r#"
Foo Struct FileId(1) 17..51 28..31 foo
FileId(0) 53..56
FileId(2) 79..82
"#]],
);
}
#[test]
fn test_find_all_refs_decl_module() {
check(
r#"
//- /lib.rs
mod foo$0;
use foo::Foo;
fn f() {
let i = Foo { n: 5 };
}
//- /foo.rs
pub struct Foo {
pub n: u32,
}
"#,
expect![[r#"
foo Module FileId(0) 0..8 4..7
FileId(0) 14..17 import
"#]],
);
}
#[test]
fn test_find_all_refs_decl_module_on_self() {
check(
r#"
//- /lib.rs
mod foo;
//- /foo.rs
use self$0;
"#,
expect![[r#"
foo Module FileId(0) 0..8 4..7
FileId(1) 4..8 import
"#]],
);
}
#[test]
fn test_find_all_refs_decl_module_on_self_crate_root() {
check(
r#"
//- /lib.rs
use self$0;
"#,
expect![[r#"
Module FileId(0) 0..10
FileId(0) 4..8 import
"#]],
);
}
#[test]
fn test_find_all_refs_super_mod_vis() {
check(
r#"
//- /lib.rs
mod foo;
//- /foo.rs
mod some;
use some::Foo;
fn f() {
let i = Foo { n: 5 };
}
//- /foo/some.rs
pub(super) struct Foo$0 {
pub n: u32,
}
"#,
expect![[r#"
Foo Struct FileId(2) 0..41 18..21 some
FileId(1) 20..23 import
FileId(1) 47..50
"#]],
);
}
#[test]
fn test_find_all_refs_with_scope() {
let code = r#"
//- /lib.rs
mod foo;
mod bar;
pub fn quux$0() {}
//- /foo.rs
fn f() { super::quux(); }
//- /bar.rs
fn f() { super::quux(); }
"#;
check_with_scope(
code,
None,
expect![[r#"
quux Function FileId(0) 19..35 26..30
FileId(1) 16..20
FileId(2) 16..20
"#]],
);
check_with_scope(
code,
Some(SearchScope::single_file(EditionedFileId::current_edition(FileId::from_raw(2)))),
expect![[r#"
quux Function FileId(0) 19..35 26..30
FileId(2) 16..20
"#]],
);
}
#[test]
fn test_find_all_refs_macro_def() {
check(
r#"
#[macro_export]
macro_rules! m1$0 { () => (()) }
fn foo() {
m1();
m1();
}
"#,
expect![[r#"
m1 Macro FileId(0) 0..46 29..31
FileId(0) 63..65
FileId(0) 73..75
"#]],
);
}
#[test]
fn test_basic_highlight_read_write() {
check(
r#"
fn foo() {
let mut i$0 = 0;
i = i + 1;
}
"#,
expect![[r#"
i Local FileId(0) 19..24 23..24 write
FileId(0) 34..35 write
FileId(0) 38..39 read
"#]],
);
}
#[test]
fn test_basic_highlight_field_read_write() {
check(
r#"
struct S {
f: u32,
}
fn foo() {
let mut s = S{f: 0};
s.f$0 = 0;
}
"#,
expect![[r#"
f Field FileId(0) 15..21 15..16
FileId(0) 55..56 read
FileId(0) 68..69 write
"#]],
);
}
#[test]
fn test_basic_highlight_decl_no_write() {
check(
r#"
fn foo() {
let i$0;
i = 1;
}
"#,
expect![[r#"
i Local FileId(0) 19..20 19..20
FileId(0) 26..27 write
"#]],
);
}
#[test]
fn test_find_struct_function_refs_outside_module() {
check(
r#"
mod foo {
pub struct Foo;
impl Foo {
pub fn new$0() -> Foo { Foo }
}
}
fn main() {
let _f = foo::Foo::new();
}
"#,
expect![[r#"
new Function FileId(0) 54..81 61..64
FileId(0) 126..129
"#]],
);
}
#[test]
fn test_find_all_refs_nested_module() {
check(
r#"
//- /lib.rs
mod foo { mod bar; }
fn f$0() {}
//- /foo/bar.rs
use crate::f;
fn g() { f(); }
"#,
expect![[r#"
f Function FileId(0) 22..31 25..26
FileId(1) 11..12 import
FileId(1) 24..25
"#]],
);
}
#[test]
fn test_find_all_refs_struct_pat() {
check(
r#"
struct S {
field$0: u8,
}
fn f(s: S) {
match s {
S { field } => {}
}
}
"#,
expect![[r#"
field Field FileId(0) 15..24 15..20
FileId(0) 68..73 read
"#]],
);
}
#[test]
fn test_find_all_refs_enum_var_pat() {
check(
r#"
enum En {
Variant {
field$0: u8,
}
}
fn f(e: En) {
match e {
En::Variant { field } => {}
}
}
"#,
expect![[r#"
field Field FileId(0) 32..41 32..37
FileId(0) 102..107 read
"#]],
);
}
#[test]
fn test_find_all_refs_enum_var_privacy() {
check(
r#"
mod m {
pub enum En {
Variant {
field$0: u8,
}
}
}
fn f() -> m::En {
m::En::Variant { field: 0 }
}
"#,
expect![[r#"
field Field FileId(0) 56..65 56..61
FileId(0) 125..130 read
"#]],
);
}
#[test]
fn test_find_self_refs() {
check(
r#"
struct Foo { bar: i32 }
impl Foo {
fn foo(self) {
let x = self$0.bar;
if true {
let _ = match () {
() => self,
};
}
}
}
"#,
expect![[r#"
self SelfParam FileId(0) 47..51 47..51
FileId(0) 71..75 read
FileId(0) 152..156 read
"#]],
);
}
#[test]
fn test_find_self_refs_decl() {
check(
r#"
struct Foo { bar: i32 }
impl Foo {
fn foo(self$0) {
self;
}
}
"#,
expect![[r#"
self SelfParam FileId(0) 47..51 47..51
FileId(0) 63..67 read
"#]],
);
}
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
check_with_scope(ra_fixture, None, expect)
}
fn check_with_scope(
#[rust_analyzer::rust_fixture] ra_fixture: &str,
search_scope: Option<SearchScope>,
expect: Expect,
) {
let (analysis, pos) = fixture::position(ra_fixture);
let refs = analysis.find_all_refs(pos, search_scope).unwrap().unwrap();
let mut actual = String::new();
for mut refs in refs {
actual += "\n\n";
if let Some(decl) = refs.declaration {
format_to!(actual, "{}", decl.nav.debug_render());
if decl.is_mut {
format_to!(actual, " write",)
}
actual += "\n\n";
}
for (file_id, references) in &mut refs.references {
references.sort_by_key(|(range, _)| range.start());
for (range, category) in references {
format_to!(actual, "{:?} {:?}", file_id, range);
for (name, _flag) in category.iter_names() {
format_to!(actual, " {}", name.to_lowercase());
}
actual += "\n";
}
}
if refs.references.is_empty() {
actual += "(no references)\n";
}
}
expect.assert_eq(actual.trim_start())
}
#[test]
fn test_find_lifetimes_function() {
check(
r#"
trait Foo<'a> {}
impl<'a> Foo<'a> for &'a () {}
fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> {
fn bar<'a>(_: &'a ()) {}
x
}
"#,
expect![[r#"
'a LifetimeParam FileId(0) 55..57
FileId(0) 63..65
FileId(0) 71..73
FileId(0) 82..84
FileId(0) 95..97
FileId(0) 106..108
"#]],
);
}
#[test]
fn test_find_lifetimes_type_alias() {
check(
r#"
type Foo<'a, T> where T: 'a$0 = &'a T;
"#,
expect![[r#"
'a LifetimeParam FileId(0) 9..11
FileId(0) 25..27
FileId(0) 31..33
"#]],
);
}
#[test]
fn test_find_lifetimes_trait_impl() {
check(
r#"
trait Foo<'a> {
fn foo() -> &'a ();
}
impl<'a> Foo<'a> for &'a () {
fn foo() -> &'a$0 () {
unimplemented!()
}
}
"#,
expect![[r#"
'a LifetimeParam FileId(0) 47..49
FileId(0) 55..57
FileId(0) 64..66
FileId(0) 89..91
"#]],
);
}
#[test]
fn test_map_range_to_original() {
check(
r#"
macro_rules! foo {($i:ident) => {$i} }
fn main() {
let a$0 = "test";
foo!(a);
}
"#,
expect![[r#"
a Local FileId(0) 59..60 59..60
FileId(0) 80..81 read
"#]],
);
}
#[test]
fn test_map_range_to_original_ref() {
check(
r#"
macro_rules! foo {($i:ident) => {$i} }
fn main() {
let a = "test";
foo!(a$0);
}
"#,
expect![[r#"
a Local FileId(0) 59..60 59..60
FileId(0) 80..81 read
"#]],
);
}
#[test]
fn test_find_labels() {
check(
r#"
fn foo<'a>() -> &'a () {
'a: loop {
'b: loop {
continue 'a$0;
}
break 'a;
}
}
"#,
expect![[r#"
'a Label FileId(0) 29..32 29..31
FileId(0) 80..82
FileId(0) 108..110
"#]],
);
}
#[test]
fn test_find_const_param() {
check(
r#"
fn foo<const FOO$0: usize>() -> usize {
FOO
}
"#,
expect![[r#"
FOO ConstParam FileId(0) 7..23 13..16
FileId(0) 42..45
"#]],
);
}
#[test]
fn test_trait() {
check(
r#"
trait Foo$0 where Self: {}
impl Foo for () {}
"#,
expect![[r#"
Foo Trait FileId(0) 0..24 6..9
FileId(0) 31..34
"#]],
);
}
#[test]
fn test_trait_self() {
check(
r#"
trait Foo where Self$0 {
fn f() -> Self;
}
impl Foo for () {}
"#,
expect![[r#"
Self TypeParam FileId(0) 0..44 6..9
FileId(0) 16..20
FileId(0) 37..41
"#]],
);
}
#[test]
fn test_self_ty() {
check(
r#"
struct $0Foo;
impl Foo where Self: {
fn f() -> Self;
}
"#,
expect![[r#"
Foo Struct FileId(0) 0..11 7..10
FileId(0) 18..21
FileId(0) 28..32
FileId(0) 50..54
"#]],
);
check(
r#"
struct Foo;
impl Foo where Self: {
fn f() -> Self$0;
}
"#,
expect![[r#"
impl Impl FileId(0) 13..57 18..21
FileId(0) 18..21
FileId(0) 28..32
FileId(0) 50..54
"#]],
);
}
#[test]
fn test_self_variant_with_payload() {
check(
r#"
enum Foo { Bar() }
impl Foo {
fn foo(self) {
match self {
Self::Bar$0() => (),
}
}
}
"#,
expect![[r#"
Bar Variant FileId(0) 11..16 11..14
FileId(0) 89..92
"#]],
);
}
#[test]
fn test_trait_alias() {
check(
r#"
trait Foo {}
trait Bar$0 = Foo where Self: ;
fn foo<T: Bar>(_: impl Bar, _: &dyn Bar) {}
"#,
expect![[r#"
Bar TraitAlias FileId(0) 13..42 19..22
FileId(0) 53..56
FileId(0) 66..69
FileId(0) 79..82
"#]],
);
}
#[test]
fn test_trait_alias_self() {
check(
r#"
trait Foo = where Self$0: ;
"#,
expect![[r#"
Self TypeParam FileId(0) 0..25 6..9
FileId(0) 18..22
"#]],
);
}
#[test]
fn test_attr_differs_from_fn_with_same_name() {
check(
r#"
#[test]
fn test$0() {
test();
}
"#,
expect![[r#"
test Function FileId(0) 0..33 11..15
FileId(0) 24..28 test
"#]],
);
}
#[test]
fn test_const_in_pattern() {
check(
r#"
const A$0: i32 = 42;
fn main() {
match A {
A => (),
_ => (),
}
if let A = A {}
}
"#,
expect![[r#"
A Const FileId(0) 0..18 6..7
FileId(0) 42..43
FileId(0) 54..55
FileId(0) 97..98
FileId(0) 101..102
"#]],
);
}
#[test]
fn test_primitives() {
check(
r#"
fn foo(_: bool) -> bo$0ol { true }
"#,
expect![[r#"
FileId(0) 10..14
FileId(0) 19..23
"#]],
);
}
#[test]
fn test_transitive() {
check(
r#"
//- /level3.rs new_source_root:local crate:level3
pub struct Fo$0o;
//- /level2.rs new_source_root:local crate:level2 deps:level3
pub use level3::Foo;
//- /level1.rs new_source_root:local crate:level1 deps:level2
pub use level2::Foo;
//- /level0.rs new_source_root:local crate:level0 deps:level1
pub use level1::Foo;
"#,
expect![[r#"
Foo Struct FileId(0) 0..15 11..14
FileId(1) 16..19 import
FileId(2) 16..19 import
FileId(3) 16..19 import
"#]],
);
}
#[test]
fn test_decl_macro_references() {
check(
r#"
//- /lib.rs crate:lib
#[macro_use]
mod qux;
mod bar;
pub use self::foo;
//- /qux.rs
#[macro_export]
macro_rules! foo$0 {
() => {struct Foo;};
}
//- /bar.rs
foo!();
//- /other.rs crate:other deps:lib new_source_root:local
lib::foo!();
"#,
expect![[r#"
foo Macro FileId(1) 0..61 29..32
FileId(0) 46..49 import
FileId(2) 0..3
FileId(3) 5..8
"#]],
);
}
#[test]
fn macro_doesnt_reference_attribute_on_call() {
check(
r#"
macro_rules! m {
() => {};
}
#[proc_macro_test::attr_noop]
m$0!();
"#,
expect![[r#"
m Macro FileId(0) 0..32 13..14
FileId(0) 64..65
"#]],
);
}
#[test]
fn multi_def() {
check(
r#"
macro_rules! m {
($name:ident) => {
mod module {
pub fn $name() {}
}
pub fn $name() {}
}
}
m!(func$0);
fn f() {
func();
module::func();
}
"#,
expect![[r#"
func Function FileId(0) 137..146 140..144 module
FileId(0) 181..185
func Function FileId(0) 137..146 140..144
FileId(0) 161..165
"#]],
)
}
#[test]
fn attr_expanded() {
check(
r#"
//- proc_macros: identity
#[proc_macros::identity]
fn func$0() {
func();
}
"#,
expect![[r#"
func Function FileId(0) 25..50 28..32
FileId(0) 41..45
"#]],
)
}
#[test]
fn attr_assoc_item() {
check(
r#"
//- proc_macros: identity
trait Trait {
#[proc_macros::identity]
fn func() {
Self::func$0();
}
}
"#,
expect![[r#"
func Function FileId(0) 48..87 51..55 Trait
FileId(0) 74..78
"#]],
)
}
// FIXME: import is classified as function
#[test]
fn attr() {
check(
r#"
//- proc_macros: identity
use proc_macros::identity;
#[proc_macros::$0identity]
fn func() {}
"#,
expect![[r#"
identity Attribute FileId(1) 1..107 32..40
FileId(0) 43..51
"#]],
);
check(
r#"
#![crate_type="proc-macro"]
#[proc_macro_attribute]
fn func$0() {}
"#,
expect![[r#"
func Attribute FileId(0) 28..64 55..59
(no references)
"#]],
);
}
// FIXME: import is classified as function
#[test]
fn proc_macro() {
check(
r#"
//- proc_macros: mirror
use proc_macros::mirror;
mirror$0! {}
"#,
expect![[r#"
mirror ProcMacro FileId(1) 1..77 22..28
FileId(0) 26..32
"#]],
)
}
#[test]
fn derive() {
check(
r#"
//- proc_macros: derive_identity
//- minicore: derive
use proc_macros::DeriveIdentity;
#[derive(proc_macros::DeriveIdentity$0)]
struct Foo;
"#,
expect![[r#"
derive_identity Derive FileId(2) 1..107 45..60
FileId(0) 17..31 import
FileId(0) 56..70
"#]],
);
check(
r#"
#![crate_type="proc-macro"]
#[proc_macro_derive(Derive, attributes(x))]
pub fn deri$0ve(_stream: TokenStream) -> TokenStream {}
"#,
expect![[r#"
derive Derive FileId(0) 28..125 79..85
(no references)
"#]],
);
}
#[test]
fn assoc_items_trait_def() {
check(
r#"
trait Trait {
const CONST$0: usize;
}
impl Trait for () {
const CONST: usize = 0;
}
impl Trait for ((),) {
const CONST: usize = 0;
}
fn f<T: Trait>() {
let _ = <()>::CONST;
let _ = T::CONST;
}
"#,
expect![[r#"
CONST Const FileId(0) 18..37 24..29 Trait
FileId(0) 71..76
FileId(0) 125..130
FileId(0) 183..188
FileId(0) 206..211
"#]],
);
check(
r#"
trait Trait {
type TypeAlias$0;
}
impl Trait for () {
type TypeAlias = ();
}
impl Trait for ((),) {
type TypeAlias = ();
}
fn f<T: Trait>() {
let _: <() as Trait>::TypeAlias;
let _: T::TypeAlias;
}
"#,
expect![[r#"
TypeAlias TypeAlias FileId(0) 18..33 23..32 Trait
FileId(0) 66..75
FileId(0) 117..126
FileId(0) 181..190
FileId(0) 207..216
"#]],
);
check(
r#"
trait Trait {
fn function$0() {}
}
impl Trait for () {
fn function() {}
}
impl Trait for ((),) {
fn function() {}
}
fn f<T: Trait>() {
let _ = <()>::function;
let _ = T::function;
}
"#,
expect![[r#"
function Function FileId(0) 18..34 21..29 Trait
FileId(0) 65..73
FileId(0) 112..120
FileId(0) 166..174
FileId(0) 192..200
"#]],
);
}
#[test]
fn assoc_items_trait_impl_def() {
check(
r#"
trait Trait {
const CONST: usize;
}
impl Trait for () {
const CONST$0: usize = 0;
}
impl Trait for ((),) {
const CONST: usize = 0;
}
fn f<T: Trait>() {
let _ = <()>::CONST;
let _ = T::CONST;
}
"#,
expect![[r#"
CONST Const FileId(0) 65..88 71..76
FileId(0) 183..188
"#]],
);
check(
r#"
trait Trait {
type TypeAlias;
}
impl Trait for () {
type TypeAlias$0 = ();
}
impl Trait for ((),) {
type TypeAlias = ();
}
fn f<T: Trait>() {
let _: <() as Trait>::TypeAlias;
let _: T::TypeAlias;
}
"#,
expect![[r#"
TypeAlias TypeAlias FileId(0) 61..81 66..75
FileId(0) 23..32
FileId(0) 117..126
FileId(0) 181..190
FileId(0) 207..216
"#]],
);
check(
r#"
trait Trait {
fn function() {}
}
impl Trait for () {
fn function$0() {}
}
impl Trait for ((),) {
fn function() {}
}
fn f<T: Trait>() {
let _ = <()>::function;
let _ = T::function;
}
"#,
expect![[r#"
function Function FileId(0) 62..78 65..73
FileId(0) 166..174
"#]],
);
}
#[test]
fn assoc_items_ref() {
check(
r#"
trait Trait {
const CONST: usize;
}
impl Trait for () {
const CONST: usize = 0;
}
impl Trait for ((),) {
const CONST: usize = 0;
}
fn f<T: Trait>() {
let _ = <()>::CONST$0;
let _ = T::CONST;
}
"#,
expect![[r#"
CONST Const FileId(0) 65..88 71..76
FileId(0) 183..188
"#]],
);
check(
r#"
trait Trait {
type TypeAlias;
}
impl Trait for () {
type TypeAlias = ();
}
impl Trait for ((),) {
type TypeAlias = ();
}
fn f<T: Trait>() {
let _: <() as Trait>::TypeAlias$0;
let _: T::TypeAlias;
}
"#,
expect![[r#"
TypeAlias TypeAlias FileId(0) 18..33 23..32 Trait
FileId(0) 66..75
FileId(0) 117..126
FileId(0) 181..190
FileId(0) 207..216
"#]],
);
check(
r#"
trait Trait {
fn function() {}
}
impl Trait for () {
fn function() {}
}
impl Trait for ((),) {
fn function() {}
}
fn f<T: Trait>() {
let _ = <()>::function$0;
let _ = T::function;
}
"#,
expect![[r#"
function Function FileId(0) 62..78 65..73
FileId(0) 166..174
"#]],
);
}
#[test]
fn name_clashes() {
check(
r#"
trait Foo {
fn method$0(&self) -> u8;
}
struct Bar {
method: u8,
}
impl Foo for Bar {
fn method(&self) -> u8 {
self.method
}
}
fn method() {}
"#,
expect![[r#"
method Function FileId(0) 16..39 19..25 Foo
FileId(0) 101..107
"#]],
);
check(
r#"
trait Foo {
fn method(&self) -> u8;
}
struct Bar {
method$0: u8,
}
impl Foo for Bar {
fn method(&self) -> u8 {
self.method
}
}
fn method() {}
"#,
expect![[r#"
method Field FileId(0) 60..70 60..66
FileId(0) 136..142 read
"#]],
);
check(
r#"
trait Foo {
fn method(&self) -> u8;
}
struct Bar {
method: u8,
}
impl Foo for Bar {
fn method$0(&self) -> u8 {
self.method
}
}
fn method() {}
"#,
expect![[r#"
method Function FileId(0) 98..148 101..107
(no references)
"#]],
);
check(
r#"
trait Foo {
fn method(&self) -> u8;
}
struct Bar {
method: u8,
}
impl Foo for Bar {
fn method(&self) -> u8 {
self.method$0
}
}
fn method() {}
"#,
expect![[r#"
method Field FileId(0) 60..70 60..66
FileId(0) 136..142 read
"#]],
);
check(
r#"
trait Foo {
fn method(&self) -> u8;
}
struct Bar {
method: u8,
}
impl Foo for Bar {
fn method(&self) -> u8 {
self.method
}
}
fn method$0() {}
"#,
expect![[r#"
method Function FileId(0) 151..165 154..160
(no references)
"#]],
);
}
#[test]
fn raw_identifier() {
check(
r#"
fn r#fn$0() {}
fn main() { r#fn(); }
"#,
expect![[r#"
r#fn Function FileId(0) 0..12 3..7
FileId(0) 25..29
"#]],
);
}
#[test]
fn implicit_format_args() {
check(
r#"
//- minicore: fmt
fn test() {
let a = "foo";
format_args!("hello {a} {a$0} {}", a);
// ^
// ^
// ^
}
"#,
expect![[r#"
a Local FileId(0) 20..21 20..21
FileId(0) 56..57 read
FileId(0) 60..61 read
FileId(0) 68..69 read
"#]],
);
}
#[test]
fn goto_ref_fn_kw() {
check(
r#"
macro_rules! N {
($i:ident, $x:expr, $blk:expr) => {
for $i in 0..$x {
$blk
}
};
}
fn main() {
$0fn f() {
N!(i, 5, {
println!("{}", i);
return;
});
for i in 1..5 {
return;
}
(|| {
return;
})();
}
}
"#,
expect![[r#"
FileId(0) 136..138
FileId(0) 207..213
FileId(0) 264..270
"#]],
)
}
#[test]
fn goto_ref_exit_points() {
check(
r#"
fn$0 foo() -> u32 {
if true {
return 0;
}
0?;
0xDEAD_BEEF
}
"#,
expect![[r#"
FileId(0) 0..2
FileId(0) 40..46
FileId(0) 62..63
FileId(0) 69..80
"#]],
);
}
#[test]
fn test_ref_yield_points() {
check(
r#"
pub async$0 fn foo() {
let x = foo()
.await
.await;
|| { 0.await };
(async { 0.await }).await
}
"#,
expect![[r#"
FileId(0) 4..9
FileId(0) 48..53
FileId(0) 63..68
FileId(0) 114..119
"#]],
);
}
#[test]
fn goto_ref_for_kw() {
check(
r#"
fn main() {
$0for i in 1..5 {
break;
continue;
}
}
"#,
expect![[r#"
FileId(0) 16..19
FileId(0) 40..45
FileId(0) 55..63
"#]],
)
}
#[test]
fn goto_ref_on_break_kw() {
check(
r#"
fn main() {
for i in 1..5 {
$0break;
continue;
}
}
"#,
expect![[r#"
FileId(0) 16..19
FileId(0) 40..45
"#]],
)
}
#[test]
fn goto_ref_on_break_kw_for_block() {
check(
r#"
fn main() {
'a:{
$0break 'a;
}
}
"#,
expect![[r#"
FileId(0) 16..19
FileId(0) 29..37
"#]],
)
}
#[test]
fn goto_ref_on_break_with_label() {
check(
r#"
fn foo() {
'outer: loop {
break;
'inner: loop {
'innermost: loop {
}
$0break 'outer;
break;
}
break;
}
}
"#,
expect![[r#"
FileId(0) 15..27
FileId(0) 39..44
FileId(0) 127..139
FileId(0) 178..183
"#]],
);
}
#[test]
fn goto_ref_on_return_in_try() {
check(
r#"
fn main() {
fn f() {
try {
$0return;
}
return;
}
return;
}
"#,
expect![[r#"
FileId(0) 16..18
FileId(0) 51..57
FileId(0) 78..84
"#]],
)
}
#[test]
fn goto_ref_on_break_in_try() {
check(
r#"
fn main() {
for i in 1..100 {
let x: Result<(), ()> = try {
$0break;
};
}
}
"#,
expect![[r#"
FileId(0) 16..19
FileId(0) 84..89
"#]],
)
}
#[test]
fn goto_ref_on_return_in_async_block() {
check(
r#"
fn main() {
$0async {
return;
}
}
"#,
expect![[r#"
FileId(0) 16..21
FileId(0) 32..38
"#]],
)
}
#[test]
fn goto_ref_on_return_in_macro_call() {
check(
r#"
//- minicore:include
//- /lib.rs
macro_rules! M {
($blk:expr) => {
fn f() {
$blk
}
$blk
};
}
fn main() {
M!({
return$0;
});
f();
include!("a.rs")
}
//- /a.rs
{
return;
}
"#,
expect![[r#"
FileId(0) 46..48
FileId(0) 106..108
FileId(0) 122..149
FileId(0) 135..141
FileId(0) 165..181
FileId(1) 6..12
"#]],
)
}
// The following are tests for short_associated_function_fast_search() in crates/ide-db/src/search.rs, because find all references
// use `FindUsages` and I found it easy to test it here.
#[test]
fn goto_ref_on_short_associated_function() {
cov_mark::check!(short_associated_function_fast_search);
check(
r#"
struct Foo;
impl Foo {
fn new$0() {}
}
fn bar() {
Foo::new();
}
fn baz() {
Foo::new;
}
"#,
expect![[r#"
new Function FileId(0) 27..38 30..33
FileId(0) 62..65
FileId(0) 91..94
"#]],
);
}
#[test]
fn goto_ref_on_short_associated_function_with_aliases() {
cov_mark::check!(short_associated_function_fast_search);
cov_mark::check!(container_use_rename);
cov_mark::check!(container_type_alias);
check(
r#"
//- /lib.rs
mod a;
mod b;
struct Foo;
impl Foo {
fn new$0() {}
}
fn bar() {
b::c::Baz::new();
}
//- /a.rs
use crate::Foo as Bar;
fn baz() { Bar::new(); }
fn quux() { <super::b::Other as super::b::Trait>::Assoc::new(); }
//- /b.rs
pub(crate) mod c;
pub(crate) struct Other;
pub(crate) trait Trait {
type Assoc;
}
impl Trait for Other {
type Assoc = super::Foo;
}
//- /b/c.rs
type Itself<T> = T;
pub(in super::super) type Baz = Itself<crate::Foo>;
"#,
expect![[r#"
new Function FileId(0) 42..53 45..48
FileId(0) 83..86
FileId(1) 40..43
FileId(1) 106..109
"#]],
);
}
#[test]
fn goto_ref_on_short_associated_function_self_works() {
cov_mark::check!(short_associated_function_fast_search);
cov_mark::check!(self_type_alias);
check(
r#"
//- /lib.rs
mod module;
struct Foo;
impl Foo {
fn new$0() {}
fn bar() { Self::new(); }
}
trait Trait {
type Assoc;
fn baz();
}
impl Trait for Foo {
type Assoc = Self;
fn baz() { Self::new(); }
}
//- /module.rs
impl super::Foo {
fn quux() { Self::new(); }
}
fn foo() { <super::Foo as super::Trait>::Assoc::new(); }
"#,
expect![[r#"
new Function FileId(0) 40..51 43..46
FileId(0) 73..76
FileId(0) 195..198
FileId(1) 40..43
FileId(1) 99..102
"#]],
);
}
#[test]
fn goto_ref_on_short_associated_function_overlapping_self_ranges() {
check(
r#"
struct Foo;
impl Foo {
fn new$0() {}
fn bar() {
Self::new();
impl Foo {
fn baz() { Self::new(); }
}
}
}
"#,
expect![[r#"
new Function FileId(0) 27..38 30..33
FileId(0) 68..71
FileId(0) 123..126
"#]],
);
}
#[test]
fn goto_ref_on_short_associated_function_no_direct_self_but_path_contains_self() {
cov_mark::check!(short_associated_function_fast_search);
check(
r#"
struct Foo;
impl Foo {
fn new$0() {}
}
trait Trait {
type Assoc;
}
impl<A, B> Trait for (A, B) {
type Assoc = B;
}
impl Foo {
fn bar() {
<((), Foo) as Trait>::Assoc::new();
<((), Self) as Trait>::Assoc::new();
}
}
"#,
expect![[r#"
new Function FileId(0) 27..38 30..33
FileId(0) 188..191
FileId(0) 233..236
"#]],
);
}
// Checks that we can circumvent our fast path logic using complicated type level functions.
// This mainly exists as a documentation. I don't believe it is fixable.
// Usages search is not 100% accurate anyway; we miss macros.
#[test]
fn goto_ref_on_short_associated_function_complicated_type_magic_can_confuse_our_logic() {
cov_mark::check!(short_associated_function_fast_search);
cov_mark::check!(same_name_different_def_type_alias);
check(
r#"
struct Foo;
impl Foo {
fn new$0() {}
}
struct ChoiceA;
struct ChoiceB;
trait Choice {
type Choose<A, B>;
}
impl Choice for ChoiceA {
type Choose<A, B> = A;
}
impl Choice for ChoiceB {
type Choose<A, B> = B;
}
type Choose<A, C> = <C as Choice>::Choose<A, Foo>;
fn bar() {
Choose::<(), ChoiceB>::new();
}
"#,
expect![[r#"
new Function FileId(0) 27..38 30..33
(no references)
"#]],
);
}
#[test]
fn goto_ref_on_short_associated_function_same_path_mention_alias_and_self() {
cov_mark::check!(short_associated_function_fast_search);
check(
r#"
struct Foo;
impl Foo {
fn new$0() {}
}
type IgnoreFirst<A, B> = B;
impl Foo {
fn bar() {
<IgnoreFirst<Foo, Self>>::new();
}
}
"#,
expect![[r#"
new Function FileId(0) 27..38 30..33
FileId(0) 131..134
"#]],
);
}
#[test]
fn goto_ref_on_included_file() {
check(
r#"
//- minicore:include
//- /lib.rs
include!("foo.rs");
fn howdy() {
let _ = FOO;
}
//- /foo.rs
const FOO$0: i32 = 0;
"#,
expect![[r#"
FOO Const FileId(1) 0..19 6..9
FileId(0) 45..48
"#]],
);
}
}