Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide/src/goto_definition.rs')
-rw-r--r--crates/ide/src/goto_definition.rs210
1 files changed, 164 insertions, 46 deletions
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 2dcb13d9e7..0ee9795af5 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,5 +1,6 @@
use std::{iter, mem::discriminant};
+use crate::Analysis;
use crate::{
FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult,
doc_links::token_as_doc_comment,
@@ -8,6 +9,7 @@ use crate::{
use hir::{
AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, ModuleDef, Semantics, sym,
};
+use ide_db::{MiniCore, ra_fixture::UpmapFromRaFixture};
use ide_db::{
RootDatabase, SymbolKind,
base_db::{AnchoredPath, SourceDatabase},
@@ -25,6 +27,11 @@ use syntax::{
match_ast,
};
+#[derive(Debug)]
+pub struct GotoDefinitionConfig<'a> {
+ pub minicore: MiniCore<'a>,
+}
+
// Feature: Go to Definition
//
// Navigates to the definition of an identifier.
@@ -39,6 +46,7 @@ use syntax::{
pub(crate) fn goto_definition(
db: &RootDatabase,
FilePosition { file_id, offset }: FilePosition,
+ config: &GotoDefinitionConfig<'_>,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
let sema = &Semantics::new(db);
let file = sema.parse_guess_edition(file_id).syntax().clone();
@@ -83,52 +91,64 @@ pub(crate) fn goto_definition(
return Some(RangeInfo::new(original_token.text_range(), navs));
}
- let navs = sema
- .descend_into_macros_no_opaque(original_token.clone(), false)
- .into_iter()
- .filter_map(|token| {
- if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &token.value) {
- return Some(navs);
- }
+ let tokens = sema.descend_into_macros_no_opaque(original_token.clone(), false);
+ let mut navs = Vec::new();
+ for token in tokens {
+ if let Some(n) = find_definition_for_known_blanket_dual_impls(sema, &token.value) {
+ navs.extend(n);
+ continue;
+ }
- let parent = token.value.parent()?;
+ if let Some(token) = ast::String::cast(token.value.clone())
+ && let Some(original_token) = ast::String::cast(original_token.clone())
+ && let Some((analysis, fixture_analysis)) =
+ Analysis::from_ra_fixture(sema, original_token, &token, config.minicore)
+ && let Some((virtual_file_id, file_offset)) = fixture_analysis.map_offset_down(offset)
+ {
+ return hir::attach_db_allow_change(&analysis.db, || {
+ goto_definition(
+ &analysis.db,
+ FilePosition { file_id: virtual_file_id, offset: file_offset },
+ config,
+ )
+ })
+ .and_then(|navs| {
+ navs.upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, file_id).ok()
+ });
+ }
- let token_file_id = token.file_id;
- if let Some(token) = ast::String::cast(token.value.clone())
- && let Some(x) =
- try_lookup_include_path(sema, InFile::new(token_file_id, token), file_id)
- {
- return Some(vec![x]);
- }
+ let parent = token.value.parent()?;
- if ast::TokenTree::can_cast(parent.kind())
- && let Some(x) = try_lookup_macro_def_in_macro_use(sema, token.value)
- {
- return Some(vec![x]);
- }
+ let token_file_id = token.file_id;
+ if let Some(token) = ast::String::cast(token.value.clone())
+ && let Some(x) =
+ try_lookup_include_path(sema, InFile::new(token_file_id, token), file_id)
+ {
+ navs.push(x);
+ continue;
+ }
- Some(
- IdentClass::classify_node(sema, &parent)?
- .definitions()
+ if ast::TokenTree::can_cast(parent.kind())
+ && let Some(x) = try_lookup_macro_def_in_macro_use(sema, token.value)
+ {
+ navs.push(x);
+ continue;
+ }
+
+ let Some(ident_class) = IdentClass::classify_node(sema, &parent) else { continue };
+ navs.extend(ident_class.definitions().into_iter().flat_map(|(def, _)| {
+ if let Definition::ExternCrateDecl(crate_def) = def {
+ return crate_def
+ .resolved_crate(db)
+ .map(|it| it.root_module().to_nav(sema.db))
.into_iter()
- .flat_map(|(def, _)| {
- if let Definition::ExternCrateDecl(crate_def) = def {
- return crate_def
- .resolved_crate(db)
- .map(|it| it.root_module().to_nav(sema.db))
- .into_iter()
- .flatten()
- .collect();
- }
- try_filter_trait_item_definition(sema, &def)
- .unwrap_or_else(|| def_to_nav(sema, def))
- })
- .collect(),
- )
- })
- .flatten()
- .unique()
- .collect::<Vec<NavigationTarget>>();
+ .flatten()
+ .collect();
+ }
+ try_filter_trait_item_definition(sema, &def).unwrap_or_else(|| def_to_nav(sema, def))
+ }));
+ }
+ let navs = navs.into_iter().unique().collect();
Some(RangeInfo::new(original_token.text_range(), navs))
}
@@ -584,15 +604,22 @@ fn expr_to_nav(
#[cfg(test)]
mod tests {
- use crate::fixture;
- use ide_db::FileRange;
+ use crate::{GotoDefinitionConfig, fixture};
+ use ide_db::{FileRange, MiniCore};
use itertools::Itertools;
use syntax::SmolStr;
+ const TEST_CONFIG: GotoDefinitionConfig<'_> =
+ GotoDefinitionConfig { minicore: MiniCore::default() };
+
#[track_caller]
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, position, expected) = fixture::annotations(ra_fixture);
- let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
+ let navs = analysis
+ .goto_definition(position, &TEST_CONFIG)
+ .unwrap()
+ .expect("no definition found")
+ .info;
let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());
let navs = navs
@@ -611,14 +638,22 @@ mod tests {
fn check_unresolved(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, position) = fixture::position(ra_fixture);
- let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
+ let navs = analysis
+ .goto_definition(position, &TEST_CONFIG)
+ .unwrap()
+ .expect("no definition found")
+ .info;
assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}")
}
fn check_name(expected_name: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, position, _) = fixture::annotations(ra_fixture);
- let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
+ let navs = analysis
+ .goto_definition(position, &TEST_CONFIG)
+ .unwrap()
+ .expect("no definition found")
+ .info;
assert!(navs.len() < 2, "expected single navigation target but encountered {}", navs.len());
let Some(target) = navs.into_iter().next() else {
panic!("expected single navigation target but encountered none");
@@ -3961,4 +3996,87 @@ mod prim_str {}
"#,
);
}
+
+ #[test]
+ fn ra_fixture() {
+ check(
+ r##"
+fn fixture(#[rust_analyzer::rust_fixture] ra_fixture: &str) {}
+
+fn foo() {
+ fixture(r#"
+fn foo() {}
+// ^^^
+fn bar() {
+ f$0oo();
+}
+ "#)
+}
+ "##,
+ );
+ }
+
+ #[test]
+ fn regression_20038() {
+ check(
+ r#"
+//- minicore: clone, fn
+struct Map<Fut, F>(Fut, F);
+
+struct InspectFn<F>(F);
+
+trait FnOnce1<A> {
+ type Output;
+}
+
+trait Future1 {
+ type Output;
+}
+
+trait FusedFuture1: Future1 {
+ fn is_terminated(&self) -> bool;
+ //^^^^^^^^^^^^^
+}
+
+impl<T, A, R> FnOnce1<A> for T
+where
+ T: FnOnce(A) -> R,
+{
+ type Output = R;
+}
+
+impl<F, A> FnOnce1<A> for InspectFn<F>
+where
+ F: for<'a> FnOnce1<&'a A, Output = ()>,
+{
+ type Output = A;
+}
+
+impl<Fut, F, T> Future1 for Map<Fut, F>
+where
+ Fut: Future1,
+ F: FnOnce1<Fut::Output, Output = T>,
+{
+ type Output = T;
+}
+
+impl<Fut, F, T> FusedFuture1 for Map<Fut, F>
+where
+ Fut: Future1,
+ F: FnOnce1<Fut::Output, Output = T>,
+{
+ fn is_terminated(&self) -> bool {
+ false
+ }
+}
+
+fn overflows<Fut, F>(inner: &Map<Fut, InspectFn<F>>)
+where
+ Map<Fut, InspectFn<F>>: FusedFuture1
+{
+ let _x = inner.is_terminated$0();
+}
+"#,
+ )
+ }
}