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.rs | 210 |
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(); +} +"#, + ) + } } |