Rewrite goto implementation tests

This commit is contained in:
Aleksey Kladov 2020-06-30 13:20:16 +02:00
parent 4484908a86
commit 34072d53b6
3 changed files with 119 additions and 104 deletions

View file

@ -279,13 +279,17 @@ impl ToNav for hir::Module {
impl ToNav for hir::ImplDef {
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
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())
} else {
original_range(db, src.as_ref().map(|it| it.syntax()))
};
let focus_range =
src.value.target_type().map(|ty| original_range(db, src.with_value(ty.syntax())).range);
let focus_range = if derive_attr.is_some() {
None
} else {
src.value.target_type().map(|ty| original_range(db, src.with_value(ty.syntax())).range)
};
NavigationTarget::from_syntax(
frange.file_id,

View file

@ -74,135 +74,152 @@ fn impls_for_trait(
#[cfg(test)]
mod tests {
use crate::mock_analysis::analysis_and_position;
use ra_db::FileRange;
fn check_goto(fixture: &str, expected: &[&str]) {
let (analysis, pos) = analysis_and_position(fixture);
use crate::mock_analysis::{analysis_and_position, MockAnalysis};
let mut navs = analysis.goto_implementation(pos).unwrap().unwrap().info;
assert_eq!(navs.len(), expected.len());
navs.sort_by_key(|nav| (nav.file_id(), nav.full_range().start()));
navs.into_iter().enumerate().for_each(|(i, nav)| nav.assert_match(expected[i]));
fn check(ra_fixture: &str) {
let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
let annotations = mock.annotations();
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]
fn goto_implementation_works() {
check_goto(
"
//- /lib.rs
struct Foo<|>;
impl Foo {}
",
&["impl IMPL_DEF FileId(1) 12..23"],
check(
r#"
struct Foo<|>;
impl Foo {}
//^^^
"#,
);
}
#[test]
fn goto_implementation_works_multiple_blocks() {
check_goto(
"
//- /lib.rs
struct Foo<|>;
impl Foo {}
impl Foo {}
",
&["impl IMPL_DEF FileId(1) 12..23", "impl IMPL_DEF FileId(1) 24..35"],
check(
r#"
struct Foo<|>;
impl Foo {}
//^^^
impl Foo {}
//^^^
"#,
);
}
#[test]
fn goto_implementation_works_multiple_mods() {
check_goto(
"
//- /lib.rs
struct Foo<|>;
mod a {
impl super::Foo {}
}
mod b {
impl super::Foo {}
}
",
&["impl IMPL_DEF FileId(1) 24..42", "impl IMPL_DEF FileId(1) 57..75"],
check(
r#"
struct Foo<|>;
mod a {
impl super::Foo {}
//^^^^^^^^^^
}
mod b {
impl super::Foo {}
//^^^^^^^^^^
}
"#,
);
}
#[test]
fn goto_implementation_works_multiple_files() {
check_goto(
"
//- /lib.rs
struct Foo<|>;
mod a;
mod b;
//- /a.rs
impl crate::Foo {}
//- /b.rs
impl crate::Foo {}
",
&["impl IMPL_DEF FileId(2) 0..18", "impl IMPL_DEF FileId(3) 0..18"],
check(
r#"
//- /lib.rs
struct Foo<|>;
mod a;
mod b;
//- /a.rs
impl crate::Foo {}
//^^^^^^^^^^
//- /b.rs
impl crate::Foo {}
//^^^^^^^^^^
"#,
);
}
#[test]
fn goto_implementation_for_trait() {
check_goto(
"
//- /lib.rs
trait T<|> {}
struct Foo;
impl T for Foo {}
",
&["impl IMPL_DEF FileId(1) 23..40"],
check(
r#"
trait T<|> {}
struct Foo;
impl T for Foo {}
//^^^
"#,
);
}
#[test]
fn goto_implementation_for_trait_multiple_files() {
check_goto(
"
//- /lib.rs
trait T<|> {};
struct Foo;
mod a;
mod b;
//- /a.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"],
check(
r#"
//- /lib.rs
trait T<|> {};
struct Foo;
mod a;
mod b;
//- /a.rs
impl crate::T for crate::Foo {}
//^^^^^^^^^^
//- /b.rs
impl crate::T for crate::Foo {}
//^^^^^^^^^^
"#,
);
}
#[test]
fn goto_implementation_all_impls() {
check_goto(
"
//- /lib.rs
trait T {}
struct Foo<|>;
impl 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",
],
check(
r#"
//- /lib.rs
trait T {}
struct Foo<|>;
impl Foo {}
//^^^
impl T for Foo {}
//^^^
impl T for &Foo {}
//^^^^
"#,
);
}
#[test]
fn goto_implementation_to_builtin_derive() {
check_goto(
"
//- /lib.rs
#[derive(Copy)]
struct Foo<|>;
",
&["impl IMPL_DEF FileId(1) 0..15"],
check(
r#"
#[derive(Copy)]
//^^^^^^^^^^^^^^^
struct Foo<|>;
"#,
);
}
}

View file

@ -10,8 +10,6 @@ use test_utils::{
use crate::{
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
/// from a set of in-memory files.
@ -81,27 +79,23 @@ impl MockAnalysis {
.expect("no file in this mock");
FileId(idx as u32 + 1)
}
pub fn annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> {
pub fn annotations(&self) -> Vec<(FileRange, String)> {
self.files
.iter()
.enumerate()
.filter_map(|(idx, fixture)| {
.flat_map(|(idx, fixture)| {
let file_id = FileId(idx as u32 + 1);
let annotations = extract_annotations(&fixture.text);
if annotations.is_empty() {
return None;
}
Some((file_id, annotations))
annotations
.into_iter()
.map(move |(range, data)| (FileRange { file_id, range }, data))
})
.collect()
}
pub fn annotation(&self) -> (FileRange, String) {
let all = self.annotations();
let mut all = self.annotations();
assert_eq!(all.len(), 1);
let (file_id, mut for_file) = all.into_iter().next().unwrap();
assert_eq!(for_file.len(), 1);
let (range, data) = for_file.pop().unwrap();
(FileRange { file_id, range}, data)
all.pop().unwrap()
}
pub fn analysis_host(self) -> AnalysisHost {
let mut host = AnalysisHost::default();