about summary refs log tree commit diff
diff options
context:
space:
mode:
authorduncanproctor <duncpro@icloud.com>2024-10-21 11:29:05 -0400
committerduncanproctor <duncpro@icloud.com>2024-10-21 11:29:05 -0400
commite46ea16fd83d751c487e994c0ab6a86c43659538 (patch)
tree4ff86ef94a31f5e5aa73b8f975c6585341b6ee7f
parent87f4dad8dcab8b038e8e1a460c939accceb316fd (diff)
downloadrust-e46ea16fd83d751c487e994c0ab6a86c43659538.tar.gz
rust-e46ea16fd83d751c487e994c0ab6a86c43659538.zip
GotoDefinition on a Range or InclusiveRange operator will link to the struct definition
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs17
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_definition.rs55
2 files changed, 68 insertions, 4 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
index ba6e50abf65..9b4273ab103 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
@@ -1,7 +1,7 @@
 //! See [`FamousDefs`].
 
 use base_db::{CrateOrigin, LangCrateOrigin, SourceDatabase};
-use hir::{Crate, Enum, Function, Macro, Module, ScopeDef, Semantics, Trait};
+use hir::{Crate, Enum, Function, Macro, Module, ScopeDef, Semantics, Struct, Trait};
 
 use crate::RootDatabase;
 
@@ -102,6 +102,14 @@ impl FamousDefs<'_, '_> {
         self.find_trait("core:ops:Drop")
     }
 
+    pub fn core_ops_Range(&self) -> Option<Struct> {
+        self.find_struct("core:ops:Range")
+    }
+
+    pub fn core_ops_RangeInclusive(&self) -> Option<Struct> {
+        self.find_struct("core:ops:RangeInclusive")
+    }
+
     pub fn core_marker_Copy(&self) -> Option<Trait> {
         self.find_trait("core:marker:Copy")
     }
@@ -137,6 +145,13 @@ impl FamousDefs<'_, '_> {
         .flatten()
     }
 
+    fn find_struct(&self, path: &str) -> Option<Struct> {
+        match self.find_def(path)? {
+            hir::ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(it))) => Some(it),
+            _ => None,
+        }
+    }
+
     fn find_trait(&self, path: &str) -> Option<Trait> {
         match self.find_def(path)? {
             hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
index c61b2ba84f2..3ac7cc823ef 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
@@ -5,7 +5,7 @@ use crate::{
     navigation_target::{self, ToNav},
     FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult,
 };
-use hir::{AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics};
+use hir::{Adt, AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics};
 use ide_db::{
     base_db::{AnchoredPath, FileLoader, SourceDatabase},
     defs::{Definition, IdentClass},
@@ -13,7 +13,7 @@ use ide_db::{
     RootDatabase, SymbolKind,
 };
 use itertools::Itertools;
-
+use ide_db::famous_defs::FamousDefs;
 use span::{Edition, FileId};
 use syntax::{
     ast::{self, HasLoopBody},
@@ -41,6 +41,22 @@ pub(crate) fn goto_definition(
 ) -> Option<RangeInfo<Vec<NavigationTarget>>> {
     let sema = &Semantics::new(db);
     let file = sema.parse_guess_edition(file_id).syntax().clone();
+
+    if let syntax::TokenAtOffset::Single(tok) = file.token_at_offset(offset) {
+        if let Some(module) = sema.file_to_module_def(file_id) {
+            let famous_defs = FamousDefs(sema, module.krate());
+            let maybe_famous_struct = match tok.kind() {
+                T![..]  => famous_defs.core_ops_Range(),
+                T![..=] => famous_defs.core_ops_RangeInclusive(),
+                _ => None
+            };
+            if let Some(fstruct) = maybe_famous_struct {
+                let target = def_to_nav(db, Definition::Adt(Adt::Struct(fstruct)));
+                return Some(RangeInfo::new(tok.text_range(), target));
+            }
+        }
+    }
+
     let edition =
         sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT);
     let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
@@ -420,7 +436,7 @@ fn expr_to_nav(
 mod tests {
     use ide_db::FileRange;
     use itertools::Itertools;
-
+    use syntax::SmolStr;
     use crate::fixture;
 
     #[track_caller]
@@ -450,6 +466,39 @@ mod tests {
         assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}")
     }
 
+
+    #[test]
+    fn goto_def_range_inclusive() {
+        let ra_fixture = r#"
+//- minicore: range
+fn f(a: usize, b: usize) {
+    for _ in a..$0=b {
+
+    }
+}
+"#;
+        let (analysis, position, _) = fixture::annotations(ra_fixture);
+        let mut navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
+        let Some(target) = navs.pop() else { panic!("no target found") };
+        assert_eq!(target.name, SmolStr::new_inline("RangeInclusive"));
+    }
+
+    #[test]
+    fn goto_def_range_half_open() {
+        let ra_fixture = r#"
+//- minicore: range
+fn f(a: usize, b: usize) {
+    for _ in a.$0.b {
+
+    }
+}
+"#;
+        let (analysis, position, _) = fixture::annotations(ra_fixture);
+        let mut navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
+        let Some(target) = navs.pop() else { panic!("no target found") };
+        assert_eq!(target.name, SmolStr::new_inline("Range"));
+    }
+
     #[test]
     fn goto_def_in_included_file() {
         check(