Skip to content

Commit

Permalink
GotoDefinition on a Range or InclusiveRange operator will link to the…
Browse files Browse the repository at this point in the history
… struct definition
  • Loading branch information
duncpro committed Oct 21, 2024
1 parent 87f4dad commit e46ea16
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 4 deletions.
17 changes: 16 additions & 1 deletion src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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")
}
Expand Down Expand Up @@ -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),
Expand Down
55 changes: 52 additions & 3 deletions src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ 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},
helpers::pick_best_token,
RootDatabase, SymbolKind,
};
use itertools::Itertools;

use ide_db::famous_defs::FamousDefs;
use span::{Edition, FileId};
use syntax::{
ast::{self, HasLoopBody},
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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(
Expand Down

0 comments on commit e46ea16

Please sign in to comment.