about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDuncan Proctor <duncpro@icloud.com>2024-10-22 03:11:23 -0400
committerDuncan Proctor <duncpro@icloud.com>2024-10-22 03:11:23 -0400
commit88b9b9d5dab1ecd3038149d80c8e75fe3302084a (patch)
tree57db10c3d81e3ce8662d6b781aaa62346f0d788c
parent8e69377c46bec8a77c9c56eb7d52ecd6f6b091f6 (diff)
downloadrust-88b9b9d5dab1ecd3038149d80c8e75fe3302084a.tar.gz
rust-88b9b9d5dab1ecd3038149d80c8e75fe3302084a.zip
goto definition on RangeFrom, RangeFull, RangeTo, and RangeToInclusive links to respective struct
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_definition.rs87
2 files changed, 65 insertions, 36 deletions
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index 5e05fad8c39..e95041b923f 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -353,9 +353,17 @@ impl SourceAnalyzer {
         db: &dyn HirDatabase,
         range_expr: &ast::RangeExpr,
     ) -> Option<StructId> {
-        let path = match range_expr.op_kind()? {
-            RangeOp::Exclusive => path![core::ops::Range],
-            RangeOp::Inclusive => path![core::ops::RangeInclusive],
+        let path: ModPath = match (range_expr.op_kind()?, range_expr.start(), range_expr.end()) {
+            (RangeOp::Exclusive, None, None) => path![core::ops::RangeFull],
+            (RangeOp::Exclusive, None, Some(_)) => path![core::ops::RangeTo],
+            (RangeOp::Exclusive, Some(_), None) => path![core::ops::RangeFrom],
+            (RangeOp::Exclusive, Some(_), Some(_)) => path![core::ops::Range],
+            (RangeOp::Inclusive, None, Some(_)) => path![core::ops::RangeToInclusive],
+            (RangeOp::Inclusive, Some(_), Some(_)) => path![core::ops::RangeInclusive],
+
+            // [E0586] inclusive ranges must be bounded at the end
+            (RangeOp::Inclusive, None, None) => return None,
+            (RangeOp::Inclusive, Some(_), None) => return None
         };
         self.resolver.resolve_known_struct(db.upcast(), &path)
     }
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 4c965fd2d86..c532e259355 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
@@ -449,55 +449,76 @@ mod tests {
         assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}")
     }
 
+    fn check_name(expected_name: &str, ra_fixture: &str) {
+        let (analysis, position, _) = fixture::annotations(ra_fixture);
+        let navs = analysis.goto_definition(position).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"); 
+        };
+        assert_eq!(target.name, SmolStr::new_inline(expected_name));
+    }
+
     #[test]
-    fn goto_def_range_inclusive_0() {
-        let ra_fixture = r#"
+    fn goto_def_range() {
+        check_name("Range", r#"
 //- minicore: range
-fn f(a: usize, b: usize) {
-    for _ in a.$0.=b {
-
+let x = 0.$0.1;
+"#
+        );
     }
+
+    #[test]
+    fn goto_def_range_from() {
+        check_name("RangeFrom", r#"
+//- minicore: range
+fn f(arr: &[i32]) -> &[i32] {
+    &arr[0.$0.]
 }
-"#;
-        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_inclusive_1() {
-        let ra_fixture = r#"
+    fn goto_def_range_inclusive() {
+        check_name("RangeInclusive", r#"
 //- minicore: range
-fn f(a: usize, b: usize) {
-    for _ in a..$0=b {
-
+let x = 0.$0.=1;
+"#
+        );
     }
+
+    #[test]
+    fn goto_def_range_full() {
+        check_name("RangeFull", r#"
+//- minicore: range
+fn f(arr: &[i32]) -> &[i32] {
+    &arr[.$0.]
 }
-"#;
-        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() {
-        let ra_fixture = r#"
+    fn goto_def_range_to() {
+        check_name("RangeTo", r#"
 //- minicore: range
-fn f(a: usize, b: usize) {
-    for _ in a.$0.b {
-
+fn f(arr: &[i32]) -> &[i32] {
+    &arr[.$0.10]
+}
+"#
+        );
     }
+
+    #[test]
+    fn goto_def_range_to_inclusive() {
+        check_name("RangeToInclusive", r#"
+//- minicore: range
+fn f(arr: &[i32]) -> &[i32] {
+    &arr[.$0.=10]
 }
-"#;
-        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]