Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide/src/references.rs')
-rw-r--r--crates/ide/src/references.rs249
1 files changed, 246 insertions, 3 deletions
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 64b82b31c7..64d717f88d 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -9,7 +9,7 @@
//! 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::{DescendPreference, PathResolution, Semantics};
+use hir::{PathResolution, Semantics};
use ide_db::{
defs::{Definition, NameClass, NameRefClass},
search::{ReferenceCategory, SearchScope, UsageSearchResult},
@@ -17,6 +17,7 @@ use ide_db::{
};
use itertools::Itertools;
use nohash_hasher::IntMap;
+use span::Edition;
use syntax::{
ast::{self, HasName},
match_ast, AstNode,
@@ -148,7 +149,7 @@ pub(crate) fn find_defs<'a>(
}
Some(
- sema.descend_into_macros(DescendPreference::SameText, token)
+ sema.descend_into_macros_exact(token)
.into_iter()
.filter_map(|it| ast::NameLike::cast(it.parent()?))
.filter_map(move |name_like| {
@@ -305,7 +306,9 @@ fn handle_control_flow_keywords(
FilePosition { file_id, offset }: FilePosition,
) -> Option<ReferenceSearchResult> {
let file = sema.parse_guess_edition(file_id);
- let token = file.syntax().token_at_offset(offset).find(|t| t.kind().is_keyword())?;
+ 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),
@@ -2507,4 +2510,244 @@ fn main() {
"#]],
)
}
+
+ // 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
+ "#]],
+ );
+ }
}