mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 21:43:37 +00:00
Rewrite goto implementation tests
This commit is contained in:
parent
4484908a86
commit
34072d53b6
3 changed files with 119 additions and 104 deletions
|
@ -279,13 +279,17 @@ impl ToNav for hir::Module {
|
||||||
impl ToNav for hir::ImplDef {
|
impl ToNav for hir::ImplDef {
|
||||||
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
|
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
|
||||||
let src = self.source(db);
|
let src = self.source(db);
|
||||||
let frange = if let Some(item) = self.is_builtin_derive(db) {
|
let derive_attr = self.is_builtin_derive(db);
|
||||||
|
let frange = if let Some(item) = &derive_attr {
|
||||||
original_range(db, item.syntax())
|
original_range(db, item.syntax())
|
||||||
} else {
|
} else {
|
||||||
original_range(db, src.as_ref().map(|it| it.syntax()))
|
original_range(db, src.as_ref().map(|it| it.syntax()))
|
||||||
};
|
};
|
||||||
let focus_range =
|
let focus_range = if derive_attr.is_some() {
|
||||||
src.value.target_type().map(|ty| original_range(db, src.with_value(ty.syntax())).range);
|
None
|
||||||
|
} else {
|
||||||
|
src.value.target_type().map(|ty| original_range(db, src.with_value(ty.syntax())).range)
|
||||||
|
};
|
||||||
|
|
||||||
NavigationTarget::from_syntax(
|
NavigationTarget::from_syntax(
|
||||||
frange.file_id,
|
frange.file_id,
|
||||||
|
|
|
@ -74,135 +74,152 @@ fn impls_for_trait(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::mock_analysis::analysis_and_position;
|
use ra_db::FileRange;
|
||||||
|
|
||||||
fn check_goto(fixture: &str, expected: &[&str]) {
|
use crate::mock_analysis::{analysis_and_position, MockAnalysis};
|
||||||
let (analysis, pos) = analysis_and_position(fixture);
|
|
||||||
|
|
||||||
let mut navs = analysis.goto_implementation(pos).unwrap().unwrap().info;
|
fn check(ra_fixture: &str) {
|
||||||
assert_eq!(navs.len(), expected.len());
|
let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
|
||||||
navs.sort_by_key(|nav| (nav.file_id(), nav.full_range().start()));
|
let annotations = mock.annotations();
|
||||||
navs.into_iter().enumerate().for_each(|(i, nav)| nav.assert_match(expected[i]));
|
let analysis = mock.analysis();
|
||||||
|
|
||||||
|
let navs = analysis.goto_implementation(position).unwrap().unwrap().info;
|
||||||
|
|
||||||
|
let key = |frange: &FileRange| (frange.file_id, frange.range.start());
|
||||||
|
|
||||||
|
let mut expected = annotations
|
||||||
|
.into_iter()
|
||||||
|
.map(|(range, data)| {
|
||||||
|
assert!(data.is_empty());
|
||||||
|
range
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
expected.sort_by_key(key);
|
||||||
|
|
||||||
|
let mut actual = navs
|
||||||
|
.into_iter()
|
||||||
|
.map(|nav| FileRange { file_id: nav.file_id(), range: nav.range() })
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
actual.sort_by_key(key);
|
||||||
|
|
||||||
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn goto_implementation_works() {
|
fn goto_implementation_works() {
|
||||||
check_goto(
|
check(
|
||||||
"
|
r#"
|
||||||
//- /lib.rs
|
struct Foo<|>;
|
||||||
struct Foo<|>;
|
impl Foo {}
|
||||||
impl Foo {}
|
//^^^
|
||||||
",
|
"#,
|
||||||
&["impl IMPL_DEF FileId(1) 12..23"],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn goto_implementation_works_multiple_blocks() {
|
fn goto_implementation_works_multiple_blocks() {
|
||||||
check_goto(
|
check(
|
||||||
"
|
r#"
|
||||||
//- /lib.rs
|
struct Foo<|>;
|
||||||
struct Foo<|>;
|
impl Foo {}
|
||||||
impl Foo {}
|
//^^^
|
||||||
impl Foo {}
|
impl Foo {}
|
||||||
",
|
//^^^
|
||||||
&["impl IMPL_DEF FileId(1) 12..23", "impl IMPL_DEF FileId(1) 24..35"],
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn goto_implementation_works_multiple_mods() {
|
fn goto_implementation_works_multiple_mods() {
|
||||||
check_goto(
|
check(
|
||||||
"
|
r#"
|
||||||
//- /lib.rs
|
struct Foo<|>;
|
||||||
struct Foo<|>;
|
mod a {
|
||||||
mod a {
|
|
||||||
impl super::Foo {}
|
impl super::Foo {}
|
||||||
}
|
//^^^^^^^^^^
|
||||||
mod b {
|
}
|
||||||
|
mod b {
|
||||||
impl super::Foo {}
|
impl super::Foo {}
|
||||||
}
|
//^^^^^^^^^^
|
||||||
",
|
}
|
||||||
&["impl IMPL_DEF FileId(1) 24..42", "impl IMPL_DEF FileId(1) 57..75"],
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn goto_implementation_works_multiple_files() {
|
fn goto_implementation_works_multiple_files() {
|
||||||
check_goto(
|
check(
|
||||||
"
|
r#"
|
||||||
//- /lib.rs
|
//- /lib.rs
|
||||||
struct Foo<|>;
|
struct Foo<|>;
|
||||||
mod a;
|
mod a;
|
||||||
mod b;
|
mod b;
|
||||||
//- /a.rs
|
//- /a.rs
|
||||||
impl crate::Foo {}
|
impl crate::Foo {}
|
||||||
//- /b.rs
|
//^^^^^^^^^^
|
||||||
impl crate::Foo {}
|
//- /b.rs
|
||||||
",
|
impl crate::Foo {}
|
||||||
&["impl IMPL_DEF FileId(2) 0..18", "impl IMPL_DEF FileId(3) 0..18"],
|
//^^^^^^^^^^
|
||||||
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn goto_implementation_for_trait() {
|
fn goto_implementation_for_trait() {
|
||||||
check_goto(
|
check(
|
||||||
"
|
r#"
|
||||||
//- /lib.rs
|
trait T<|> {}
|
||||||
trait T<|> {}
|
struct Foo;
|
||||||
struct Foo;
|
impl T for Foo {}
|
||||||
impl T for Foo {}
|
//^^^
|
||||||
",
|
"#,
|
||||||
&["impl IMPL_DEF FileId(1) 23..40"],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn goto_implementation_for_trait_multiple_files() {
|
fn goto_implementation_for_trait_multiple_files() {
|
||||||
check_goto(
|
check(
|
||||||
"
|
r#"
|
||||||
//- /lib.rs
|
//- /lib.rs
|
||||||
trait T<|> {};
|
trait T<|> {};
|
||||||
struct Foo;
|
struct Foo;
|
||||||
mod a;
|
mod a;
|
||||||
mod b;
|
mod b;
|
||||||
//- /a.rs
|
//- /a.rs
|
||||||
impl crate::T for crate::Foo {}
|
impl crate::T for crate::Foo {}
|
||||||
//- /b.rs
|
//^^^^^^^^^^
|
||||||
impl crate::T for crate::Foo {}
|
//- /b.rs
|
||||||
",
|
impl crate::T for crate::Foo {}
|
||||||
&["impl IMPL_DEF FileId(2) 0..31", "impl IMPL_DEF FileId(3) 0..31"],
|
//^^^^^^^^^^
|
||||||
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn goto_implementation_all_impls() {
|
fn goto_implementation_all_impls() {
|
||||||
check_goto(
|
check(
|
||||||
"
|
r#"
|
||||||
//- /lib.rs
|
//- /lib.rs
|
||||||
trait T {}
|
trait T {}
|
||||||
struct Foo<|>;
|
struct Foo<|>;
|
||||||
impl Foo {}
|
impl Foo {}
|
||||||
impl T for Foo {}
|
//^^^
|
||||||
impl T for &Foo {}
|
impl T for Foo {}
|
||||||
",
|
//^^^
|
||||||
&[
|
impl T for &Foo {}
|
||||||
"impl IMPL_DEF FileId(1) 23..34",
|
//^^^^
|
||||||
"impl IMPL_DEF FileId(1) 35..52",
|
"#,
|
||||||
"impl IMPL_DEF FileId(1) 53..71",
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn goto_implementation_to_builtin_derive() {
|
fn goto_implementation_to_builtin_derive() {
|
||||||
check_goto(
|
check(
|
||||||
"
|
r#"
|
||||||
//- /lib.rs
|
|
||||||
#[derive(Copy)]
|
#[derive(Copy)]
|
||||||
struct Foo<|>;
|
//^^^^^^^^^^^^^^^
|
||||||
",
|
struct Foo<|>;
|
||||||
&["impl IMPL_DEF FileId(1) 0..15"],
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,6 @@ use test_utils::{
|
||||||
use crate::{
|
use crate::{
|
||||||
Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange,
|
Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange,
|
||||||
};
|
};
|
||||||
use ra_syntax::TextRange;
|
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
|
|
||||||
/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
|
/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
|
||||||
/// from a set of in-memory files.
|
/// from a set of in-memory files.
|
||||||
|
@ -81,27 +79,23 @@ impl MockAnalysis {
|
||||||
.expect("no file in this mock");
|
.expect("no file in this mock");
|
||||||
FileId(idx as u32 + 1)
|
FileId(idx as u32 + 1)
|
||||||
}
|
}
|
||||||
pub fn annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> {
|
pub fn annotations(&self) -> Vec<(FileRange, String)> {
|
||||||
self.files
|
self.files
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(idx, fixture)| {
|
.flat_map(|(idx, fixture)| {
|
||||||
let file_id = FileId(idx as u32 + 1);
|
let file_id = FileId(idx as u32 + 1);
|
||||||
let annotations = extract_annotations(&fixture.text);
|
let annotations = extract_annotations(&fixture.text);
|
||||||
if annotations.is_empty() {
|
annotations
|
||||||
return None;
|
.into_iter()
|
||||||
}
|
.map(move |(range, data)| (FileRange { file_id, range }, data))
|
||||||
Some((file_id, annotations))
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
pub fn annotation(&self) -> (FileRange, String) {
|
pub fn annotation(&self) -> (FileRange, String) {
|
||||||
let all = self.annotations();
|
let mut all = self.annotations();
|
||||||
assert_eq!(all.len(), 1);
|
assert_eq!(all.len(), 1);
|
||||||
let (file_id, mut for_file) = all.into_iter().next().unwrap();
|
all.pop().unwrap()
|
||||||
assert_eq!(for_file.len(), 1);
|
|
||||||
let (range, data) = for_file.pop().unwrap();
|
|
||||||
(FileRange { file_id, range}, data)
|
|
||||||
}
|
}
|
||||||
pub fn analysis_host(self) -> AnalysisHost {
|
pub fn analysis_host(self) -> AnalysisHost {
|
||||||
let mut host = AnalysisHost::default();
|
let mut host = AnalysisHost::default();
|
||||||
|
|
Loading…
Reference in a new issue