Skip to content

Commit e46ea16

Browse files
committed
GotoDefinition on a Range or InclusiveRange operator will link to the struct definition
1 parent 87f4dad commit e46ea16

File tree

2 files changed

+68
-4
lines changed

2 files changed

+68
-4
lines changed

src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! See [`FamousDefs`].
22
33
use base_db::{CrateOrigin, LangCrateOrigin, SourceDatabase};
4-
use hir::{Crate, Enum, Function, Macro, Module, ScopeDef, Semantics, Trait};
4+
use hir::{Crate, Enum, Function, Macro, Module, ScopeDef, Semantics, Struct, Trait};
55

66
use crate::RootDatabase;
77

@@ -102,6 +102,14 @@ impl FamousDefs<'_, '_> {
102102
self.find_trait("core:ops:Drop")
103103
}
104104

105+
pub fn core_ops_Range(&self) -> Option<Struct> {
106+
self.find_struct("core:ops:Range")
107+
}
108+
109+
pub fn core_ops_RangeInclusive(&self) -> Option<Struct> {
110+
self.find_struct("core:ops:RangeInclusive")
111+
}
112+
105113
pub fn core_marker_Copy(&self) -> Option<Trait> {
106114
self.find_trait("core:marker:Copy")
107115
}
@@ -137,6 +145,13 @@ impl FamousDefs<'_, '_> {
137145
.flatten()
138146
}
139147

148+
fn find_struct(&self, path: &str) -> Option<Struct> {
149+
match self.find_def(path)? {
150+
hir::ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(it))) => Some(it),
151+
_ => None,
152+
}
153+
}
154+
140155
fn find_trait(&self, path: &str) -> Option<Trait> {
141156
match self.find_def(path)? {
142157
hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),

src/tools/rust-analyzer/crates/ide/src/goto_definition.rs

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ use crate::{
55
navigation_target::{self, ToNav},
66
FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult,
77
};
8-
use hir::{AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics};
8+
use hir::{Adt, AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics};
99
use ide_db::{
1010
base_db::{AnchoredPath, FileLoader, SourceDatabase},
1111
defs::{Definition, IdentClass},
1212
helpers::pick_best_token,
1313
RootDatabase, SymbolKind,
1414
};
1515
use itertools::Itertools;
16-
16+
use ide_db::famous_defs::FamousDefs;
1717
use span::{Edition, FileId};
1818
use syntax::{
1919
ast::{self, HasLoopBody},
@@ -41,6 +41,22 @@ pub(crate) fn goto_definition(
4141
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
4242
let sema = &Semantics::new(db);
4343
let file = sema.parse_guess_edition(file_id).syntax().clone();
44+
45+
if let syntax::TokenAtOffset::Single(tok) = file.token_at_offset(offset) {
46+
if let Some(module) = sema.file_to_module_def(file_id) {
47+
let famous_defs = FamousDefs(sema, module.krate());
48+
let maybe_famous_struct = match tok.kind() {
49+
T![..] => famous_defs.core_ops_Range(),
50+
T![..=] => famous_defs.core_ops_RangeInclusive(),
51+
_ => None
52+
};
53+
if let Some(fstruct) = maybe_famous_struct {
54+
let target = def_to_nav(db, Definition::Adt(Adt::Struct(fstruct)));
55+
return Some(RangeInfo::new(tok.text_range(), target));
56+
}
57+
}
58+
}
59+
4460
let edition =
4561
sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT);
4662
let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
@@ -420,7 +436,7 @@ fn expr_to_nav(
420436
mod tests {
421437
use ide_db::FileRange;
422438
use itertools::Itertools;
423-
439+
use syntax::SmolStr;
424440
use crate::fixture;
425441

426442
#[track_caller]
@@ -450,6 +466,39 @@ mod tests {
450466
assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}")
451467
}
452468

469+
470+
#[test]
471+
fn goto_def_range_inclusive() {
472+
let ra_fixture = r#"
473+
//- minicore: range
474+
fn f(a: usize, b: usize) {
475+
for _ in a..$0=b {
476+
477+
}
478+
}
479+
"#;
480+
let (analysis, position, _) = fixture::annotations(ra_fixture);
481+
let mut navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
482+
let Some(target) = navs.pop() else { panic!("no target found") };
483+
assert_eq!(target.name, SmolStr::new_inline("RangeInclusive"));
484+
}
485+
486+
#[test]
487+
fn goto_def_range_half_open() {
488+
let ra_fixture = r#"
489+
//- minicore: range
490+
fn f(a: usize, b: usize) {
491+
for _ in a.$0.b {
492+
493+
}
494+
}
495+
"#;
496+
let (analysis, position, _) = fixture::annotations(ra_fixture);
497+
let mut navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
498+
let Some(target) = navs.pop() else { panic!("no target found") };
499+
assert_eq!(target.name, SmolStr::new_inline("Range"));
500+
}
501+
453502
#[test]
454503
fn goto_def_in_included_file() {
455504
check(

0 commit comments

Comments
 (0)