From f48664068210b92f4884ee8e6fe8504dabcd4d9a Mon Sep 17 00:00:00 2001 From: Daiki Ihara Date: Fri, 4 Dec 2020 00:05:39 +0900 Subject: [PATCH] Extract tests module to file in ide_db crate --- crates/ide_db/src/call_info.rs | 527 +-------------- crates/ide_db/src/call_info/tests.rs | 523 ++++++++++++++ crates/ide_db/src/helpers/insert_use.rs | 639 +----------------- crates/ide_db/src/helpers/insert_use/tests.rs | 620 +++++++++++++++++ crates/ide_db/src/line_index.rs | 131 +--- crates/ide_db/src/line_index/tests.rs | 128 ++++ crates/ide_db/src/traits.rs | 148 +--- crates/ide_db/src/traits/tests.rs | 144 ++++ 8 files changed, 1419 insertions(+), 1441 deletions(-) create mode 100644 crates/ide_db/src/call_info/tests.rs create mode 100644 crates/ide_db/src/helpers/insert_use/tests.rs create mode 100644 crates/ide_db/src/line_index/tests.rs create mode 100644 crates/ide_db/src/traits/tests.rs diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs index 83a602b9ad4..615fa7b0ee7 100644 --- a/crates/ide_db/src/call_info.rs +++ b/crates/ide_db/src/call_info.rs @@ -228,529 +228,4 @@ fn arg_list(&self) -> Option { } #[cfg(test)] -mod tests { - use crate::RootDatabase; - use base_db::{fixture::ChangeFixture, FilePosition}; - use expect_test::{expect, Expect}; - use test_utils::{mark, RangeOrOffset}; - - /// Creates analysis from a multi-file fixture, returns positions marked with <|>. - pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { - let change_fixture = ChangeFixture::parse(ra_fixture); - let mut database = RootDatabase::default(); - database.apply_change(change_fixture.change); - let (file_id, range_or_offset) = - change_fixture.file_position.expect("expected a marker (<|>)"); - let offset = match range_or_offset { - RangeOrOffset::Range(_) => panic!(), - RangeOrOffset::Offset(it) => it, - }; - (database, FilePosition { file_id, offset }) - } - - fn check(ra_fixture: &str, expect: Expect) { - let (db, position) = position(ra_fixture); - let call_info = crate::call_info::call_info(&db, position); - let actual = match call_info { - Some(call_info) => { - let docs = match &call_info.doc { - None => "".to_string(), - Some(docs) => format!("{}\n------\n", docs.as_str()), - }; - let params = call_info - .parameter_labels() - .enumerate() - .map(|(i, param)| { - if Some(i) == call_info.active_parameter { - format!("<{}>", param) - } else { - param.to_string() - } - }) - .collect::>() - .join(", "); - format!("{}{}\n({})\n", docs, call_info.signature, params) - } - None => String::new(), - }; - expect.assert_eq(&actual); - } - - #[test] - fn test_fn_signature_two_args() { - check( - r#" -fn foo(x: u32, y: u32) -> u32 {x + y} -fn bar() { foo(<|>3, ); } -"#, - expect![[r#" - fn foo(x: u32, y: u32) -> u32 - (, y: u32) - "#]], - ); - check( - r#" -fn foo(x: u32, y: u32) -> u32 {x + y} -fn bar() { foo(3<|>, ); } -"#, - expect![[r#" - fn foo(x: u32, y: u32) -> u32 - (, y: u32) - "#]], - ); - check( - r#" -fn foo(x: u32, y: u32) -> u32 {x + y} -fn bar() { foo(3,<|> ); } -"#, - expect![[r#" - fn foo(x: u32, y: u32) -> u32 - (x: u32, ) - "#]], - ); - check( - r#" -fn foo(x: u32, y: u32) -> u32 {x + y} -fn bar() { foo(3, <|>); } -"#, - expect![[r#" - fn foo(x: u32, y: u32) -> u32 - (x: u32, ) - "#]], - ); - } - - #[test] - fn test_fn_signature_two_args_empty() { - check( - r#" -fn foo(x: u32, y: u32) -> u32 {x + y} -fn bar() { foo(<|>); } -"#, - expect![[r#" - fn foo(x: u32, y: u32) -> u32 - (, y: u32) - "#]], - ); - } - - #[test] - fn test_fn_signature_two_args_first_generics() { - check( - r#" -fn foo(x: T, y: U) -> u32 - where T: Copy + Display, U: Debug -{ x + y } - -fn bar() { foo(<|>3, ); } -"#, - expect![[r#" - fn foo(x: i32, y: {unknown}) -> u32 - (, y: {unknown}) - "#]], - ); - } - - #[test] - fn test_fn_signature_no_params() { - check( - r#" -fn foo() -> T where T: Copy + Display {} -fn bar() { foo(<|>); } -"#, - expect![[r#" - fn foo() -> {unknown} - () - "#]], - ); - } - - #[test] - fn test_fn_signature_for_impl() { - check( - r#" -struct F; -impl F { pub fn new() { } } -fn bar() { - let _ : F = F::new(<|>); -} -"#, - expect![[r#" - fn new() - () - "#]], - ); - } - - #[test] - fn test_fn_signature_for_method_self() { - check( - r#" -struct S; -impl S { pub fn do_it(&self) {} } - -fn bar() { - let s: S = S; - s.do_it(<|>); -} -"#, - expect![[r#" - fn do_it(&self) - () - "#]], - ); - } - - #[test] - fn test_fn_signature_for_method_with_arg() { - check( - r#" -struct S; -impl S { - fn foo(&self, x: i32) {} -} - -fn main() { S.foo(<|>); } -"#, - expect![[r#" - fn foo(&self, x: i32) - () - "#]], - ); - } - - #[test] - fn test_fn_signature_for_method_with_arg_as_assoc_fn() { - check( - r#" -struct S; -impl S { - fn foo(&self, x: i32) {} -} - -fn main() { S::foo(<|>); } -"#, - expect![[r#" - fn foo(self: &S, x: i32) - (, x: i32) - "#]], - ); - } - - #[test] - fn test_fn_signature_with_docs_simple() { - check( - r#" -/// test -// non-doc-comment -fn foo(j: u32) -> u32 { - j -} - -fn bar() { - let _ = foo(<|>); -} -"#, - expect![[r#" - test - ------ - fn foo(j: u32) -> u32 - () - "#]], - ); - } - - #[test] - fn test_fn_signature_with_docs() { - check( - r#" -/// Adds one to the number given. -/// -/// # Examples -/// -/// ``` -/// let five = 5; -/// -/// assert_eq!(6, my_crate::add_one(5)); -/// ``` -pub fn add_one(x: i32) -> i32 { - x + 1 -} - -pub fn do() { - add_one(<|> -}"#, - expect![[r##" - Adds one to the number given. - - # Examples - - ``` - let five = 5; - - assert_eq!(6, my_crate::add_one(5)); - ``` - ------ - fn add_one(x: i32) -> i32 - () - "##]], - ); - } - - #[test] - fn test_fn_signature_with_docs_impl() { - check( - r#" -struct addr; -impl addr { - /// Adds one to the number given. - /// - /// # Examples - /// - /// ``` - /// let five = 5; - /// - /// assert_eq!(6, my_crate::add_one(5)); - /// ``` - pub fn add_one(x: i32) -> i32 { - x + 1 - } -} - -pub fn do_it() { - addr {}; - addr::add_one(<|>); -} -"#, - expect![[r##" - Adds one to the number given. - - # Examples - - ``` - let five = 5; - - assert_eq!(6, my_crate::add_one(5)); - ``` - ------ - fn add_one(x: i32) -> i32 - () - "##]], - ); - } - - #[test] - fn test_fn_signature_with_docs_from_actix() { - check( - r#" -struct WriteHandler; - -impl WriteHandler { - /// Method is called when writer emits error. - /// - /// If this method returns `ErrorAction::Continue` writer processing - /// continues otherwise stream processing stops. - fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running { - Running::Stop - } - - /// Method is called when writer finishes. - /// - /// By default this method stops actor's `Context`. - fn finished(&mut self, ctx: &mut Self::Context) { - ctx.stop() - } -} - -pub fn foo(mut r: WriteHandler<()>) { - r.finished(<|>); -} -"#, - expect![[r#" - Method is called when writer finishes. - - By default this method stops actor's `Context`. - ------ - fn finished(&mut self, ctx: &mut {unknown}) - () - "#]], - ); - } - - #[test] - fn call_info_bad_offset() { - mark::check!(call_info_bad_offset); - check( - r#" -fn foo(x: u32, y: u32) -> u32 {x + y} -fn bar() { foo <|> (3, ); } -"#, - expect![[""]], - ); - } - - #[test] - fn test_nested_method_in_lambda() { - check( - r#" -struct Foo; -impl Foo { fn bar(&self, _: u32) { } } - -fn bar(_: u32) { } - -fn main() { - let foo = Foo; - std::thread::spawn(move || foo.bar(<|>)); -} -"#, - expect![[r#" - fn bar(&self, _: u32) - (<_: u32>) - "#]], - ); - } - - #[test] - fn works_for_tuple_structs() { - check( - r#" -/// A cool tuple struct -struct S(u32, i32); -fn main() { - let s = S(0, <|>); -} -"#, - expect![[r#" - A cool tuple struct - ------ - struct S(u32, i32) - (u32, ) - "#]], - ); - } - - #[test] - fn generic_struct() { - check( - r#" -struct S(T); -fn main() { - let s = S(<|>); -} -"#, - expect![[r#" - struct S({unknown}) - (<{unknown}>) - "#]], - ); - } - - #[test] - fn works_for_enum_variants() { - check( - r#" -enum E { - /// A Variant - A(i32), - /// Another - B, - /// And C - C { a: i32, b: i32 } -} - -fn main() { - let a = E::A(<|>); -} -"#, - expect![[r#" - A Variant - ------ - enum E::A(i32) - () - "#]], - ); - } - - #[test] - fn cant_call_struct_record() { - check( - r#" -struct S { x: u32, y: i32 } -fn main() { - let s = S(<|>); -} -"#, - expect![[""]], - ); - } - - #[test] - fn cant_call_enum_record() { - check( - r#" -enum E { - /// A Variant - A(i32), - /// Another - B, - /// And C - C { a: i32, b: i32 } -} - -fn main() { - let a = E::C(<|>); -} -"#, - expect![[""]], - ); - } - - #[test] - fn fn_signature_for_call_in_macro() { - check( - r#" -macro_rules! id { ($($tt:tt)*) => { $($tt)* } } -fn foo() { } -id! { - fn bar() { foo(<|>); } -} -"#, - expect![[r#" - fn foo() - () - "#]], - ); - } - - #[test] - fn call_info_for_lambdas() { - check( - r#" -struct S; -fn foo(s: S) -> i32 { 92 } -fn main() { - (|s| foo(s))(<|>) -} - "#, - expect![[r#" - (S) -> i32 - () - "#]], - ) - } - - #[test] - fn call_info_for_fn_ptr() { - check( - r#" -fn main(f: fn(i32, f64) -> char) { - f(0, <|>) -} - "#, - expect![[r#" - (i32, f64) -> char - (i32, ) - "#]], - ) - } -} +mod tests; diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs new file mode 100644 index 00000000000..9335aeaa5ff --- /dev/null +++ b/crates/ide_db/src/call_info/tests.rs @@ -0,0 +1,523 @@ +use crate::RootDatabase; +use base_db::{fixture::ChangeFixture, FilePosition}; +use expect_test::{expect, Expect}; +use test_utils::{mark, RangeOrOffset}; + +/// Creates analysis from a multi-file fixture, returns positions marked with <|>. +pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { + let change_fixture = ChangeFixture::parse(ra_fixture); + let mut database = RootDatabase::default(); + database.apply_change(change_fixture.change); + let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)"); + let offset = match range_or_offset { + RangeOrOffset::Range(_) => panic!(), + RangeOrOffset::Offset(it) => it, + }; + (database, FilePosition { file_id, offset }) +} + +fn check(ra_fixture: &str, expect: Expect) { + let (db, position) = position(ra_fixture); + let call_info = crate::call_info::call_info(&db, position); + let actual = match call_info { + Some(call_info) => { + let docs = match &call_info.doc { + None => "".to_string(), + Some(docs) => format!("{}\n------\n", docs.as_str()), + }; + let params = call_info + .parameter_labels() + .enumerate() + .map(|(i, param)| { + if Some(i) == call_info.active_parameter { + format!("<{}>", param) + } else { + param.to_string() + } + }) + .collect::>() + .join(", "); + format!("{}{}\n({})\n", docs, call_info.signature, params) + } + None => String::new(), + }; + expect.assert_eq(&actual); +} + +#[test] +fn test_fn_signature_two_args() { + check( + r#" +fn foo(x: u32, y: u32) -> u32 {x + y} +fn bar() { foo(<|>3, ); } +"#, + expect![[r#" + fn foo(x: u32, y: u32) -> u32 + (, y: u32) + "#]], + ); + check( + r#" +fn foo(x: u32, y: u32) -> u32 {x + y} +fn bar() { foo(3<|>, ); } +"#, + expect![[r#" + fn foo(x: u32, y: u32) -> u32 + (, y: u32) + "#]], + ); + check( + r#" +fn foo(x: u32, y: u32) -> u32 {x + y} +fn bar() { foo(3,<|> ); } +"#, + expect![[r#" + fn foo(x: u32, y: u32) -> u32 + (x: u32, ) + "#]], + ); + check( + r#" +fn foo(x: u32, y: u32) -> u32 {x + y} +fn bar() { foo(3, <|>); } +"#, + expect![[r#" + fn foo(x: u32, y: u32) -> u32 + (x: u32, ) + "#]], + ); +} + +#[test] +fn test_fn_signature_two_args_empty() { + check( + r#" +fn foo(x: u32, y: u32) -> u32 {x + y} +fn bar() { foo(<|>); } +"#, + expect![[r#" + fn foo(x: u32, y: u32) -> u32 + (, y: u32) + "#]], + ); +} + +#[test] +fn test_fn_signature_two_args_first_generics() { + check( + r#" +fn foo(x: T, y: U) -> u32 + where T: Copy + Display, U: Debug +{ x + y } + +fn bar() { foo(<|>3, ); } +"#, + expect![[r#" + fn foo(x: i32, y: {unknown}) -> u32 + (, y: {unknown}) + "#]], + ); +} + +#[test] +fn test_fn_signature_no_params() { + check( + r#" +fn foo() -> T where T: Copy + Display {} +fn bar() { foo(<|>); } +"#, + expect![[r#" + fn foo() -> {unknown} + () + "#]], + ); +} + +#[test] +fn test_fn_signature_for_impl() { + check( + r#" +struct F; +impl F { pub fn new() { } } +fn bar() { + let _ : F = F::new(<|>); +} +"#, + expect![[r#" + fn new() + () + "#]], + ); +} + +#[test] +fn test_fn_signature_for_method_self() { + check( + r#" +struct S; +impl S { pub fn do_it(&self) {} } + +fn bar() { + let s: S = S; + s.do_it(<|>); +} +"#, + expect![[r#" + fn do_it(&self) + () + "#]], + ); +} + +#[test] +fn test_fn_signature_for_method_with_arg() { + check( + r#" +struct S; +impl S { + fn foo(&self, x: i32) {} +} + +fn main() { S.foo(<|>); } +"#, + expect![[r#" + fn foo(&self, x: i32) + () + "#]], + ); +} + +#[test] +fn test_fn_signature_for_method_with_arg_as_assoc_fn() { + check( + r#" +struct S; +impl S { + fn foo(&self, x: i32) {} +} + +fn main() { S::foo(<|>); } +"#, + expect![[r#" + fn foo(self: &S, x: i32) + (, x: i32) + "#]], + ); +} + +#[test] +fn test_fn_signature_with_docs_simple() { + check( + r#" +/// test +// non-doc-comment +fn foo(j: u32) -> u32 { + j +} + +fn bar() { + let _ = foo(<|>); +} +"#, + expect![[r#" + test + ------ + fn foo(j: u32) -> u32 + () + "#]], + ); +} + +#[test] +fn test_fn_signature_with_docs() { + check( + r#" +/// Adds one to the number given. +/// +/// # Examples +/// +/// ``` +/// let five = 5; +/// +/// assert_eq!(6, my_crate::add_one(5)); +/// ``` +pub fn add_one(x: i32) -> i32 { + x + 1 +} + +pub fn do() { + add_one(<|> +}"#, + expect![[r##" + Adds one to the number given. + + # Examples + + ``` + let five = 5; + + assert_eq!(6, my_crate::add_one(5)); + ``` + ------ + fn add_one(x: i32) -> i32 + () + "##]], + ); +} + +#[test] +fn test_fn_signature_with_docs_impl() { + check( + r#" +struct addr; +impl addr { + /// Adds one to the number given. + /// + /// # Examples + /// + /// ``` + /// let five = 5; + /// + /// assert_eq!(6, my_crate::add_one(5)); + /// ``` + pub fn add_one(x: i32) -> i32 { + x + 1 + } +} + +pub fn do_it() { + addr {}; + addr::add_one(<|>); +} +"#, + expect![[r##" + Adds one to the number given. + + # Examples + + ``` + let five = 5; + + assert_eq!(6, my_crate::add_one(5)); + ``` + ------ + fn add_one(x: i32) -> i32 + () + "##]], + ); +} + +#[test] +fn test_fn_signature_with_docs_from_actix() { + check( + r#" +struct WriteHandler; + +impl WriteHandler { + /// Method is called when writer emits error. + /// + /// If this method returns `ErrorAction::Continue` writer processing + /// continues otherwise stream processing stops. + fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running { + Running::Stop + } + + /// Method is called when writer finishes. + /// + /// By default this method stops actor's `Context`. + fn finished(&mut self, ctx: &mut Self::Context) { + ctx.stop() + } +} + +pub fn foo(mut r: WriteHandler<()>) { + r.finished(<|>); +} +"#, + expect![[r#" + Method is called when writer finishes. + + By default this method stops actor's `Context`. + ------ + fn finished(&mut self, ctx: &mut {unknown}) + () + "#]], + ); +} + +#[test] +fn call_info_bad_offset() { + mark::check!(call_info_bad_offset); + check( + r#" +fn foo(x: u32, y: u32) -> u32 {x + y} +fn bar() { foo <|> (3, ); } +"#, + expect![[""]], + ); +} + +#[test] +fn test_nested_method_in_lambda() { + check( + r#" +struct Foo; +impl Foo { fn bar(&self, _: u32) { } } + +fn bar(_: u32) { } + +fn main() { + let foo = Foo; + std::thread::spawn(move || foo.bar(<|>)); +} +"#, + expect![[r#" + fn bar(&self, _: u32) + (<_: u32>) + "#]], + ); +} + +#[test] +fn works_for_tuple_structs() { + check( + r#" +/// A cool tuple struct +struct S(u32, i32); +fn main() { + let s = S(0, <|>); +} +"#, + expect![[r#" + A cool tuple struct + ------ + struct S(u32, i32) + (u32, ) + "#]], + ); +} + +#[test] +fn generic_struct() { + check( + r#" +struct S(T); +fn main() { + let s = S(<|>); +} +"#, + expect![[r#" + struct S({unknown}) + (<{unknown}>) + "#]], + ); +} + +#[test] +fn works_for_enum_variants() { + check( + r#" +enum E { + /// A Variant + A(i32), + /// Another + B, + /// And C + C { a: i32, b: i32 } +} + +fn main() { + let a = E::A(<|>); +} +"#, + expect![[r#" + A Variant + ------ + enum E::A(i32) + () + "#]], + ); +} + +#[test] +fn cant_call_struct_record() { + check( + r#" +struct S { x: u32, y: i32 } +fn main() { + let s = S(<|>); +} +"#, + expect![[""]], + ); +} + +#[test] +fn cant_call_enum_record() { + check( + r#" +enum E { + /// A Variant + A(i32), + /// Another + B, + /// And C + C { a: i32, b: i32 } +} + +fn main() { + let a = E::C(<|>); +} +"#, + expect![[""]], + ); +} + +#[test] +fn fn_signature_for_call_in_macro() { + check( + r#" +macro_rules! id { ($($tt:tt)*) => { $($tt)* } } +fn foo() { } +id! { + fn bar() { foo(<|>); } +} +"#, + expect![[r#" + fn foo() + () + "#]], + ); +} + +#[test] +fn call_info_for_lambdas() { + check( + r#" +struct S; +fn foo(s: S) -> i32 { 92 } +fn main() { + (|s| foo(s))(<|>) +} + "#, + expect![[r#" + (S) -> i32 + () + "#]], + ) +} + +#[test] +fn call_info_for_fn_ptr() { + check( + r#" +fn main(f: fn(i32, f64) -> char) { + f(0, <|>) +} + "#, + expect![[r#" + (i32, f64) -> char + (i32, ) + "#]], + ) +} diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs index 08d246c167c..040843990d0 100644 --- a/crates/ide_db/src/helpers/insert_use.rs +++ b/crates/ide_db/src/helpers/insert_use.rs @@ -573,641 +573,4 @@ fn find_insert_position( } #[cfg(test)] -mod tests { - use super::*; - - use test_utils::assert_eq_text; - - #[test] - fn insert_existing() { - check_full("std::fs", "use std::fs;", "use std::fs;") - } - - #[test] - fn insert_start() { - check_none( - "std::bar::AA", - r" -use std::bar::B; -use std::bar::D; -use std::bar::F; -use std::bar::G;", - r" -use std::bar::AA; -use std::bar::B; -use std::bar::D; -use std::bar::F; -use std::bar::G;", - ) - } - - #[test] - fn insert_start_indent() { - mark::check!(insert_use_indent_after); - check_none( - "std::bar::AA", - r" - use std::bar::B; - use std::bar::D;", - r" - use std::bar::AA; - use std::bar::B; - use std::bar::D;", - ) - } - - #[test] - fn insert_middle() { - check_none( - "std::bar::EE", - r" -use std::bar::A; -use std::bar::D; -use std::bar::F; -use std::bar::G;", - r" -use std::bar::A; -use std::bar::D; -use std::bar::EE; -use std::bar::F; -use std::bar::G;", - ) - } - - #[test] - fn insert_middle_indent() { - check_none( - "std::bar::EE", - r" - use std::bar::A; - use std::bar::D; - use std::bar::F; - use std::bar::G;", - r" - use std::bar::A; - use std::bar::D; - use std::bar::EE; - use std::bar::F; - use std::bar::G;", - ) - } - - #[test] - fn insert_end() { - check_none( - "std::bar::ZZ", - r" -use std::bar::A; -use std::bar::D; -use std::bar::F; -use std::bar::G;", - r" -use std::bar::A; -use std::bar::D; -use std::bar::F; -use std::bar::G; -use std::bar::ZZ;", - ) - } - - #[test] - fn insert_end_indent() { - mark::check!(insert_use_indent_before); - check_none( - "std::bar::ZZ", - r" - use std::bar::A; - use std::bar::D; - use std::bar::F; - use std::bar::G;", - r" - use std::bar::A; - use std::bar::D; - use std::bar::F; - use std::bar::G; - use std::bar::ZZ;", - ) - } - - #[test] - fn insert_middle_nested() { - check_none( - "std::bar::EE", - r" -use std::bar::A; -use std::bar::{D, Z}; // example of weird imports due to user -use std::bar::F; -use std::bar::G;", - r" -use std::bar::A; -use std::bar::EE; -use std::bar::{D, Z}; // example of weird imports due to user -use std::bar::F; -use std::bar::G;", - ) - } - - #[test] - fn insert_middle_groups() { - check_none( - "foo::bar::GG", - r" - use std::bar::A; - use std::bar::D; - - use foo::bar::F; - use foo::bar::H;", - r" - use std::bar::A; - use std::bar::D; - - use foo::bar::F; - use foo::bar::GG; - use foo::bar::H;", - ) - } - - #[test] - fn insert_first_matching_group() { - check_none( - "foo::bar::GG", - r" - use foo::bar::A; - use foo::bar::D; - - use std; - - use foo::bar::F; - use foo::bar::H;", - r" - use foo::bar::A; - use foo::bar::D; - use foo::bar::GG; - - use std; - - use foo::bar::F; - use foo::bar::H;", - ) - } - - #[test] - fn insert_missing_group_std() { - check_none( - "std::fmt", - r" - use foo::bar::A; - use foo::bar::D;", - r" - use std::fmt; - - use foo::bar::A; - use foo::bar::D;", - ) - } - - #[test] - fn insert_missing_group_self() { - check_none( - "self::fmt", - r" -use foo::bar::A; -use foo::bar::D;", - r" -use foo::bar::A; -use foo::bar::D; - -use self::fmt;", - ) - } - - #[test] - fn insert_no_imports() { - check_full( - "foo::bar", - "fn main() {}", - r"use foo::bar; - -fn main() {}", - ) - } - - #[test] - fn insert_empty_file() { - // empty files will get two trailing newlines - // this is due to the test case insert_no_imports above - check_full( - "foo::bar", - "", - r"use foo::bar; - -", - ) - } - - #[test] - fn insert_empty_module() { - mark::check!(insert_use_no_indent_after); - check( - "foo::bar", - "mod x {}", - r"{ - use foo::bar; -}", - None, - true, - ) - } - - #[test] - fn insert_after_inner_attr() { - check_full( - "foo::bar", - r"#![allow(unused_imports)]", - r"#![allow(unused_imports)] - -use foo::bar;", - ) - } - - #[test] - fn insert_after_inner_attr2() { - check_full( - "foo::bar", - r"#![allow(unused_imports)] - -#![no_std] -fn main() {}", - r"#![allow(unused_imports)] - -#![no_std] - -use foo::bar; -fn main() {}", - ); - } - - #[test] - fn inserts_after_single_line_inner_comments() { - check_none( - "foo::bar::Baz", - "//! Single line inner comments do not allow any code before them.", - r#"//! Single line inner comments do not allow any code before them. - -use foo::bar::Baz;"#, - ); - } - - #[test] - fn inserts_after_multiline_inner_comments() { - check_none( - "foo::bar::Baz", - r#"/*! Multiline inner comments do not allow any code before them. */ - -/*! Still an inner comment, cannot place any code before. */ -fn main() {}"#, - r#"/*! Multiline inner comments do not allow any code before them. */ - -/*! Still an inner comment, cannot place any code before. */ - -use foo::bar::Baz; -fn main() {}"#, - ) - } - - #[test] - fn inserts_after_all_inner_items() { - check_none( - "foo::bar::Baz", - r#"#![allow(unused_imports)] -/*! Multiline line comment 2 */ - - -//! Single line comment 1 -#![no_std] -//! Single line comment 2 -fn main() {}"#, - r#"#![allow(unused_imports)] -/*! Multiline line comment 2 */ - - -//! Single line comment 1 -#![no_std] -//! Single line comment 2 - -use foo::bar::Baz; -fn main() {}"#, - ) - } - - #[test] - fn merge_groups() { - check_last("std::io", r"use std::fmt;", r"use std::{fmt, io};") - } - - #[test] - fn merge_groups_last() { - check_last( - "std::io", - r"use std::fmt::{Result, Display};", - r"use std::fmt::{Result, Display}; -use std::io;", - ) - } - - #[test] - fn merge_last_into_self() { - check_last("foo::bar::baz", r"use foo::bar;", r"use foo::bar::{self, baz};"); - } - - #[test] - fn merge_groups_full() { - check_full( - "std::io", - r"use std::fmt::{Result, Display};", - r"use std::{fmt::{Result, Display}, io};", - ) - } - - #[test] - fn merge_groups_long_full() { - check_full( - "std::foo::bar::Baz", - r"use std::foo::bar::Qux;", - r"use std::foo::bar::{Baz, Qux};", - ) - } - - #[test] - fn merge_groups_long_last() { - check_last( - "std::foo::bar::Baz", - r"use std::foo::bar::Qux;", - r"use std::foo::bar::{Baz, Qux};", - ) - } - - #[test] - fn merge_groups_long_full_list() { - check_full( - "std::foo::bar::Baz", - r"use std::foo::bar::{Qux, Quux};", - r"use std::foo::bar::{Baz, Quux, Qux};", - ) - } - - #[test] - fn merge_groups_long_last_list() { - check_last( - "std::foo::bar::Baz", - r"use std::foo::bar::{Qux, Quux};", - r"use std::foo::bar::{Baz, Quux, Qux};", - ) - } - - #[test] - fn merge_groups_long_full_nested() { - check_full( - "std::foo::bar::Baz", - r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", - r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};", - ) - } - - #[test] - fn merge_groups_long_last_nested() { - check_last( - "std::foo::bar::Baz", - r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", - r"use std::foo::bar::Baz; -use std::foo::bar::{Qux, quux::{Fez, Fizz}};", - ) - } - - #[test] - fn merge_groups_full_nested_deep() { - check_full( - "std::foo::bar::quux::Baz", - r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", - r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};", - ) - } - - #[test] - fn merge_groups_full_nested_long() { - check_full( - "std::foo::bar::Baz", - r"use std::{foo::bar::Qux};", - r"use std::{foo::bar::{Baz, Qux}};", - ); - } - - #[test] - fn merge_groups_last_nested_long() { - check_full( - "std::foo::bar::Baz", - r"use std::{foo::bar::Qux};", - r"use std::{foo::bar::{Baz, Qux}};", - ); - } - - #[test] - fn merge_groups_skip_pub() { - check_full( - "std::io", - r"pub use std::fmt::{Result, Display};", - r"pub use std::fmt::{Result, Display}; -use std::io;", - ) - } - - #[test] - fn merge_groups_skip_pub_crate() { - check_full( - "std::io", - r"pub(crate) use std::fmt::{Result, Display};", - r"pub(crate) use std::fmt::{Result, Display}; -use std::io;", - ) - } - - #[test] - #[ignore] // FIXME: Support this - fn split_out_merge() { - check_last( - "std::fmt::Result", - r"use std::{fmt, io};", - r"use std::fmt::{self, Result}; -use std::io;", - ) - } - - #[test] - fn merge_into_module_import() { - check_full( - "std::fmt::Result", - r"use std::{fmt, io};", - r"use std::{fmt::{self, Result}, io};", - ) - } - - #[test] - fn merge_groups_self() { - check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};") - } - - #[test] - fn merge_mod_into_glob() { - check_full( - "token::TokenKind", - r"use token::TokenKind::*;", - r"use token::TokenKind::{*, self};", - ) - // FIXME: have it emit `use token::TokenKind::{self, *}`? - } - - #[test] - fn merge_self_glob() { - check_full("self", r"use self::*;", r"use self::{*, self};") - // FIXME: have it emit `use {self, *}`? - } - - #[test] - fn merge_glob_nested() { - check_full( - "foo::bar::quux::Fez", - r"use foo::bar::{Baz, quux::*};", - r"use foo::bar::{Baz, quux::{self::*, Fez}};", - ) - } - - #[test] - fn merge_nested_considers_first_segments() { - check_full( - "hir_ty::display::write_bounds_like_dyn_trait", - r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};", - r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};", - ); - } - - #[test] - fn skip_merge_last_too_long() { - check_last( - "foo::bar", - r"use foo::bar::baz::Qux;", - r"use foo::bar; -use foo::bar::baz::Qux;", - ); - } - - #[test] - fn skip_merge_last_too_long2() { - check_last( - "foo::bar::baz::Qux", - r"use foo::bar;", - r"use foo::bar; -use foo::bar::baz::Qux;", - ); - } - - #[test] - fn insert_short_before_long() { - check_none( - "foo::bar", - r"use foo::bar::baz::Qux;", - r"use foo::bar; -use foo::bar::baz::Qux;", - ); - } - - #[test] - fn merge_last_fail() { - check_merge_only_fail( - r"use foo::bar::{baz::{Qux, Fez}};", - r"use foo::bar::{baaz::{Quux, Feez}};", - MergeBehaviour::Last, - ); - } - - #[test] - fn merge_last_fail1() { - check_merge_only_fail( - r"use foo::bar::{baz::{Qux, Fez}};", - r"use foo::bar::baaz::{Quux, Feez};", - MergeBehaviour::Last, - ); - } - - #[test] - fn merge_last_fail2() { - check_merge_only_fail( - r"use foo::bar::baz::{Qux, Fez};", - r"use foo::bar::{baaz::{Quux, Feez}};", - MergeBehaviour::Last, - ); - } - - #[test] - fn merge_last_fail3() { - check_merge_only_fail( - r"use foo::bar::baz::{Qux, Fez};", - r"use foo::bar::baaz::{Quux, Feez};", - MergeBehaviour::Last, - ); - } - - fn check( - path: &str, - ra_fixture_before: &str, - ra_fixture_after: &str, - mb: Option, - module: bool, - ) { - let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone(); - if module { - syntax = syntax.descendants().find_map(ast::Module::cast).unwrap().syntax().clone(); - } - let file = super::ImportScope::from(syntax).unwrap(); - let path = ast::SourceFile::parse(&format!("use {};", path)) - .tree() - .syntax() - .descendants() - .find_map(ast::Path::cast) - .unwrap(); - - let rewriter = insert_use(&file, path, mb); - let result = rewriter.rewrite(file.as_syntax_node()).to_string(); - assert_eq_text!(&result, ra_fixture_after); - } - - fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { - check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Full), false) - } - - fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { - check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Last), false) - } - - fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { - check(path, ra_fixture_before, ra_fixture_after, None, false) - } - - fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehaviour) { - let use0 = ast::SourceFile::parse(ra_fixture0) - .tree() - .syntax() - .descendants() - .find_map(ast::Use::cast) - .unwrap(); - - let use1 = ast::SourceFile::parse(ra_fixture1) - .tree() - .syntax() - .descendants() - .find_map(ast::Use::cast) - .unwrap(); - - let result = try_merge_imports(&use0, &use1, mb); - assert_eq!(result.map(|u| u.to_string()), None); - } -} +mod tests; diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs new file mode 100644 index 00000000000..86bfa5b41ff --- /dev/null +++ b/crates/ide_db/src/helpers/insert_use/tests.rs @@ -0,0 +1,620 @@ +use super::*; + +use test_utils::assert_eq_text; + +#[test] +fn insert_existing() { + check_full("std::fs", "use std::fs;", "use std::fs;") +} + +#[test] +fn insert_start() { + check_none( + "std::bar::AA", + r" +use std::bar::B; +use std::bar::D; +use std::bar::F; +use std::bar::G;", + r" +use std::bar::AA; +use std::bar::B; +use std::bar::D; +use std::bar::F; +use std::bar::G;", + ) +} + +#[test] +fn insert_start_indent() { + mark::check!(insert_use_indent_after); + check_none( + "std::bar::AA", + r" + use std::bar::B; + use std::bar::D;", + r" + use std::bar::AA; + use std::bar::B; + use std::bar::D;", + ) +} + +#[test] +fn insert_middle() { + check_none( + "std::bar::EE", + r" +use std::bar::A; +use std::bar::D; +use std::bar::F; +use std::bar::G;", + r" +use std::bar::A; +use std::bar::D; +use std::bar::EE; +use std::bar::F; +use std::bar::G;", + ) +} + +#[test] +fn insert_middle_indent() { + check_none( + "std::bar::EE", + r" + use std::bar::A; + use std::bar::D; + use std::bar::F; + use std::bar::G;", + r" + use std::bar::A; + use std::bar::D; + use std::bar::EE; + use std::bar::F; + use std::bar::G;", + ) +} + +#[test] +fn insert_end() { + check_none( + "std::bar::ZZ", + r" +use std::bar::A; +use std::bar::D; +use std::bar::F; +use std::bar::G;", + r" +use std::bar::A; +use std::bar::D; +use std::bar::F; +use std::bar::G; +use std::bar::ZZ;", + ) +} + +#[test] +fn insert_end_indent() { + mark::check!(insert_use_indent_before); + check_none( + "std::bar::ZZ", + r" + use std::bar::A; + use std::bar::D; + use std::bar::F; + use std::bar::G;", + r" + use std::bar::A; + use std::bar::D; + use std::bar::F; + use std::bar::G; + use std::bar::ZZ;", + ) +} + +#[test] +fn insert_middle_nested() { + check_none( + "std::bar::EE", + r" +use std::bar::A; +use std::bar::{D, Z}; // example of weird imports due to user +use std::bar::F; +use std::bar::G;", + r" +use std::bar::A; +use std::bar::EE; +use std::bar::{D, Z}; // example of weird imports due to user +use std::bar::F; +use std::bar::G;", + ) +} + +#[test] +fn insert_middle_groups() { + check_none( + "foo::bar::GG", + r" + use std::bar::A; + use std::bar::D; + + use foo::bar::F; + use foo::bar::H;", + r" + use std::bar::A; + use std::bar::D; + + use foo::bar::F; + use foo::bar::GG; + use foo::bar::H;", + ) +} + +#[test] +fn insert_first_matching_group() { + check_none( + "foo::bar::GG", + r" + use foo::bar::A; + use foo::bar::D; + + use std; + + use foo::bar::F; + use foo::bar::H;", + r" + use foo::bar::A; + use foo::bar::D; + use foo::bar::GG; + + use std; + + use foo::bar::F; + use foo::bar::H;", + ) +} + +#[test] +fn insert_missing_group_std() { + check_none( + "std::fmt", + r" + use foo::bar::A; + use foo::bar::D;", + r" + use std::fmt; + + use foo::bar::A; + use foo::bar::D;", + ) +} + +#[test] +fn insert_missing_group_self() { + check_none( + "self::fmt", + r" +use foo::bar::A; +use foo::bar::D;", + r" +use foo::bar::A; +use foo::bar::D; + +use self::fmt;", + ) +} + +#[test] +fn insert_no_imports() { + check_full( + "foo::bar", + "fn main() {}", + r"use foo::bar; + +fn main() {}", + ) +} + +#[test] +fn insert_empty_file() { + // empty files will get two trailing newlines + // this is due to the test case insert_no_imports above + check_full( + "foo::bar", + "", + r"use foo::bar; + +", + ) +} + +#[test] +fn insert_empty_module() { + mark::check!(insert_use_no_indent_after); + check( + "foo::bar", + "mod x {}", + r"{ + use foo::bar; +}", + None, + true, + ) +} + +#[test] +fn insert_after_inner_attr() { + check_full( + "foo::bar", + r"#![allow(unused_imports)]", + r"#![allow(unused_imports)] + +use foo::bar;", + ) +} + +#[test] +fn insert_after_inner_attr2() { + check_full( + "foo::bar", + r"#![allow(unused_imports)] + +#![no_std] +fn main() {}", + r"#![allow(unused_imports)] + +#![no_std] + +use foo::bar; +fn main() {}", + ); +} + +#[test] +fn inserts_after_single_line_inner_comments() { + check_none( + "foo::bar::Baz", + "//! Single line inner comments do not allow any code before them.", + r#"//! Single line inner comments do not allow any code before them. + +use foo::bar::Baz;"#, + ); +} + +#[test] +fn inserts_after_multiline_inner_comments() { + check_none( + "foo::bar::Baz", + r#"/*! Multiline inner comments do not allow any code before them. */ + +/*! Still an inner comment, cannot place any code before. */ +fn main() {}"#, + r#"/*! Multiline inner comments do not allow any code before them. */ + +/*! Still an inner comment, cannot place any code before. */ + +use foo::bar::Baz; +fn main() {}"#, + ) +} + +#[test] +fn inserts_after_all_inner_items() { + check_none( + "foo::bar::Baz", + r#"#![allow(unused_imports)] +/*! Multiline line comment 2 */ + + +//! Single line comment 1 +#![no_std] +//! Single line comment 2 +fn main() {}"#, + r#"#![allow(unused_imports)] +/*! Multiline line comment 2 */ + + +//! Single line comment 1 +#![no_std] +//! Single line comment 2 + +use foo::bar::Baz; +fn main() {}"#, + ) +} + +#[test] +fn merge_groups() { + check_last("std::io", r"use std::fmt;", r"use std::{fmt, io};") +} + +#[test] +fn merge_groups_last() { + check_last( + "std::io", + r"use std::fmt::{Result, Display};", + r"use std::fmt::{Result, Display}; +use std::io;", + ) +} + +#[test] +fn merge_last_into_self() { + check_last("foo::bar::baz", r"use foo::bar;", r"use foo::bar::{self, baz};"); +} + +#[test] +fn merge_groups_full() { + check_full( + "std::io", + r"use std::fmt::{Result, Display};", + r"use std::{fmt::{Result, Display}, io};", + ) +} + +#[test] +fn merge_groups_long_full() { + check_full("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Baz, Qux};") +} + +#[test] +fn merge_groups_long_last() { + check_last("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Baz, Qux};") +} + +#[test] +fn merge_groups_long_full_list() { + check_full( + "std::foo::bar::Baz", + r"use std::foo::bar::{Qux, Quux};", + r"use std::foo::bar::{Baz, Quux, Qux};", + ) +} + +#[test] +fn merge_groups_long_last_list() { + check_last( + "std::foo::bar::Baz", + r"use std::foo::bar::{Qux, Quux};", + r"use std::foo::bar::{Baz, Quux, Qux};", + ) +} + +#[test] +fn merge_groups_long_full_nested() { + check_full( + "std::foo::bar::Baz", + r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", + r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};", + ) +} + +#[test] +fn merge_groups_long_last_nested() { + check_last( + "std::foo::bar::Baz", + r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", + r"use std::foo::bar::Baz; +use std::foo::bar::{Qux, quux::{Fez, Fizz}};", + ) +} + +#[test] +fn merge_groups_full_nested_deep() { + check_full( + "std::foo::bar::quux::Baz", + r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", + r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};", + ) +} + +#[test] +fn merge_groups_full_nested_long() { + check_full( + "std::foo::bar::Baz", + r"use std::{foo::bar::Qux};", + r"use std::{foo::bar::{Baz, Qux}};", + ); +} + +#[test] +fn merge_groups_last_nested_long() { + check_full( + "std::foo::bar::Baz", + r"use std::{foo::bar::Qux};", + r"use std::{foo::bar::{Baz, Qux}};", + ); +} + +#[test] +fn merge_groups_skip_pub() { + check_full( + "std::io", + r"pub use std::fmt::{Result, Display};", + r"pub use std::fmt::{Result, Display}; +use std::io;", + ) +} + +#[test] +fn merge_groups_skip_pub_crate() { + check_full( + "std::io", + r"pub(crate) use std::fmt::{Result, Display};", + r"pub(crate) use std::fmt::{Result, Display}; +use std::io;", + ) +} + +#[test] +#[ignore] // FIXME: Support this +fn split_out_merge() { + check_last( + "std::fmt::Result", + r"use std::{fmt, io};", + r"use std::fmt::{self, Result}; +use std::io;", + ) +} + +#[test] +fn merge_into_module_import() { + check_full("std::fmt::Result", r"use std::{fmt, io};", r"use std::{fmt::{self, Result}, io};") +} + +#[test] +fn merge_groups_self() { + check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};") +} + +#[test] +fn merge_mod_into_glob() { + check_full("token::TokenKind", r"use token::TokenKind::*;", r"use token::TokenKind::{*, self};") + // FIXME: have it emit `use token::TokenKind::{self, *}`? +} + +#[test] +fn merge_self_glob() { + check_full("self", r"use self::*;", r"use self::{*, self};") + // FIXME: have it emit `use {self, *}`? +} + +#[test] +fn merge_glob_nested() { + check_full( + "foo::bar::quux::Fez", + r"use foo::bar::{Baz, quux::*};", + r"use foo::bar::{Baz, quux::{self::*, Fez}};", + ) +} + +#[test] +fn merge_nested_considers_first_segments() { + check_full( + "hir_ty::display::write_bounds_like_dyn_trait", + r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};", + r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};", + ); +} + +#[test] +fn skip_merge_last_too_long() { + check_last( + "foo::bar", + r"use foo::bar::baz::Qux;", + r"use foo::bar; +use foo::bar::baz::Qux;", + ); +} + +#[test] +fn skip_merge_last_too_long2() { + check_last( + "foo::bar::baz::Qux", + r"use foo::bar;", + r"use foo::bar; +use foo::bar::baz::Qux;", + ); +} + +#[test] +fn insert_short_before_long() { + check_none( + "foo::bar", + r"use foo::bar::baz::Qux;", + r"use foo::bar; +use foo::bar::baz::Qux;", + ); +} + +#[test] +fn merge_last_fail() { + check_merge_only_fail( + r"use foo::bar::{baz::{Qux, Fez}};", + r"use foo::bar::{baaz::{Quux, Feez}};", + MergeBehaviour::Last, + ); +} + +#[test] +fn merge_last_fail1() { + check_merge_only_fail( + r"use foo::bar::{baz::{Qux, Fez}};", + r"use foo::bar::baaz::{Quux, Feez};", + MergeBehaviour::Last, + ); +} + +#[test] +fn merge_last_fail2() { + check_merge_only_fail( + r"use foo::bar::baz::{Qux, Fez};", + r"use foo::bar::{baaz::{Quux, Feez}};", + MergeBehaviour::Last, + ); +} + +#[test] +fn merge_last_fail3() { + check_merge_only_fail( + r"use foo::bar::baz::{Qux, Fez};", + r"use foo::bar::baaz::{Quux, Feez};", + MergeBehaviour::Last, + ); +} + +fn check( + path: &str, + ra_fixture_before: &str, + ra_fixture_after: &str, + mb: Option, + module: bool, +) { + let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone(); + if module { + syntax = syntax.descendants().find_map(ast::Module::cast).unwrap().syntax().clone(); + } + let file = super::ImportScope::from(syntax).unwrap(); + let path = ast::SourceFile::parse(&format!("use {};", path)) + .tree() + .syntax() + .descendants() + .find_map(ast::Path::cast) + .unwrap(); + + let rewriter = insert_use(&file, path, mb); + let result = rewriter.rewrite(file.as_syntax_node()).to_string(); + assert_eq_text!(&result, ra_fixture_after); +} + +fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { + check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Full), false) +} + +fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { + check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Last), false) +} + +fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { + check(path, ra_fixture_before, ra_fixture_after, None, false) +} + +fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehaviour) { + let use0 = ast::SourceFile::parse(ra_fixture0) + .tree() + .syntax() + .descendants() + .find_map(ast::Use::cast) + .unwrap(); + + let use1 = ast::SourceFile::parse(ra_fixture1) + .tree() + .syntax() + .descendants() + .find_map(ast::Use::cast) + .unwrap(); + + let result = try_merge_imports(&use0, &use1, mb); + assert_eq!(result.map(|u| u.to_string()), None); +} diff --git a/crates/ide_db/src/line_index.rs b/crates/ide_db/src/line_index.rs index a381f7fb8d5..41226305eaa 100644 --- a/crates/ide_db/src/line_index.rs +++ b/crates/ide_db/src/line_index.rs @@ -149,133 +149,4 @@ fn utf16_to_utf8_col(&self, line: u32, mut col: u32) -> TextSize { } #[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_line_index() { - let text = "hello\nworld"; - let index = LineIndex::new(text); - assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 }); - assert_eq!(index.line_col(1.into()), LineCol { line: 0, col_utf16: 1 }); - assert_eq!(index.line_col(5.into()), LineCol { line: 0, col_utf16: 5 }); - assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 0 }); - assert_eq!(index.line_col(7.into()), LineCol { line: 1, col_utf16: 1 }); - assert_eq!(index.line_col(8.into()), LineCol { line: 1, col_utf16: 2 }); - assert_eq!(index.line_col(10.into()), LineCol { line: 1, col_utf16: 4 }); - assert_eq!(index.line_col(11.into()), LineCol { line: 1, col_utf16: 5 }); - assert_eq!(index.line_col(12.into()), LineCol { line: 1, col_utf16: 6 }); - - let text = "\nhello\nworld"; - let index = LineIndex::new(text); - assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 }); - assert_eq!(index.line_col(1.into()), LineCol { line: 1, col_utf16: 0 }); - assert_eq!(index.line_col(2.into()), LineCol { line: 1, col_utf16: 1 }); - assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 5 }); - assert_eq!(index.line_col(7.into()), LineCol { line: 2, col_utf16: 0 }); - } - - #[test] - fn test_char_len() { - assert_eq!('メ'.len_utf8(), 3); - assert_eq!('メ'.len_utf16(), 1); - } - - #[test] - fn test_empty_index() { - let col_index = LineIndex::new( - " -const C: char = 'x'; -", - ); - assert_eq!(col_index.utf16_lines.len(), 0); - } - - #[test] - fn test_single_char() { - let col_index = LineIndex::new( - " -const C: char = 'メ'; -", - ); - - assert_eq!(col_index.utf16_lines.len(), 1); - assert_eq!(col_index.utf16_lines[&1].len(), 1); - assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() }); - - // UTF-8 to UTF-16, no changes - assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15); - - // UTF-8 to UTF-16 - assert_eq!(col_index.utf8_to_utf16_col(1, 22.into()), 20); - - // UTF-16 to UTF-8, no changes - assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextSize::from(15)); - - // UTF-16 to UTF-8 - assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21)); - - let col_index = LineIndex::new("a𐐏b"); - assert_eq!(col_index.utf16_to_utf8_col(0, 3), TextSize::from(5)); - } - - #[test] - fn test_string() { - let col_index = LineIndex::new( - " -const C: char = \"メ メ\"; -", - ); - - assert_eq!(col_index.utf16_lines.len(), 1); - assert_eq!(col_index.utf16_lines[&1].len(), 2); - assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() }); - assert_eq!(col_index.utf16_lines[&1][1], Utf16Char { start: 21.into(), end: 24.into() }); - - // UTF-8 to UTF-16 - assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15); - - assert_eq!(col_index.utf8_to_utf16_col(1, 21.into()), 19); - assert_eq!(col_index.utf8_to_utf16_col(1, 25.into()), 21); - - assert!(col_index.utf8_to_utf16_col(2, 15.into()) == 15); - - // UTF-16 to UTF-8 - assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextSize::from(15)); - - // メ UTF-8: 0xE3 0x83 0xA1, UTF-16: 0x30E1 - assert_eq!(col_index.utf16_to_utf8_col(1, 17), TextSize::from(17)); // first メ at 17..20 - assert_eq!(col_index.utf16_to_utf8_col(1, 18), TextSize::from(20)); // space - assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21)); // second メ at 21..24 - - assert_eq!(col_index.utf16_to_utf8_col(2, 15), TextSize::from(15)); - } - - #[test] - fn test_splitlines() { - fn r(lo: u32, hi: u32) -> TextRange { - TextRange::new(lo.into(), hi.into()) - } - - let text = "a\nbb\nccc\n"; - let line_index = LineIndex::new(text); - - let actual = line_index.lines(r(0, 9)).collect::>(); - let expected = vec![r(0, 2), r(2, 5), r(5, 9)]; - assert_eq!(actual, expected); - - let text = ""; - let line_index = LineIndex::new(text); - - let actual = line_index.lines(r(0, 0)).collect::>(); - let expected = vec![]; - assert_eq!(actual, expected); - - let text = "\n"; - let line_index = LineIndex::new(text); - - let actual = line_index.lines(r(0, 1)).collect::>(); - let expected = vec![r(0, 1)]; - assert_eq!(actual, expected) - } -} +mod tests; diff --git a/crates/ide_db/src/line_index/tests.rs b/crates/ide_db/src/line_index/tests.rs new file mode 100644 index 00000000000..05f7484e8fe --- /dev/null +++ b/crates/ide_db/src/line_index/tests.rs @@ -0,0 +1,128 @@ +use super::*; + +#[test] +fn test_line_index() { + let text = "hello\nworld"; + let index = LineIndex::new(text); + assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 }); + assert_eq!(index.line_col(1.into()), LineCol { line: 0, col_utf16: 1 }); + assert_eq!(index.line_col(5.into()), LineCol { line: 0, col_utf16: 5 }); + assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 0 }); + assert_eq!(index.line_col(7.into()), LineCol { line: 1, col_utf16: 1 }); + assert_eq!(index.line_col(8.into()), LineCol { line: 1, col_utf16: 2 }); + assert_eq!(index.line_col(10.into()), LineCol { line: 1, col_utf16: 4 }); + assert_eq!(index.line_col(11.into()), LineCol { line: 1, col_utf16: 5 }); + assert_eq!(index.line_col(12.into()), LineCol { line: 1, col_utf16: 6 }); + + let text = "\nhello\nworld"; + let index = LineIndex::new(text); + assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 }); + assert_eq!(index.line_col(1.into()), LineCol { line: 1, col_utf16: 0 }); + assert_eq!(index.line_col(2.into()), LineCol { line: 1, col_utf16: 1 }); + assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 5 }); + assert_eq!(index.line_col(7.into()), LineCol { line: 2, col_utf16: 0 }); +} + +#[test] +fn test_char_len() { + assert_eq!('メ'.len_utf8(), 3); + assert_eq!('メ'.len_utf16(), 1); +} + +#[test] +fn test_empty_index() { + let col_index = LineIndex::new( + " +const C: char = 'x'; +", + ); + assert_eq!(col_index.utf16_lines.len(), 0); +} + +#[test] +fn test_single_char() { + let col_index = LineIndex::new( + " +const C: char = 'メ'; +", + ); + + assert_eq!(col_index.utf16_lines.len(), 1); + assert_eq!(col_index.utf16_lines[&1].len(), 1); + assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() }); + + // UTF-8 to UTF-16, no changes + assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15); + + // UTF-8 to UTF-16 + assert_eq!(col_index.utf8_to_utf16_col(1, 22.into()), 20); + + // UTF-16 to UTF-8, no changes + assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextSize::from(15)); + + // UTF-16 to UTF-8 + assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21)); + + let col_index = LineIndex::new("a𐐏b"); + assert_eq!(col_index.utf16_to_utf8_col(0, 3), TextSize::from(5)); +} + +#[test] +fn test_string() { + let col_index = LineIndex::new( + " +const C: char = \"メ メ\"; +", + ); + + assert_eq!(col_index.utf16_lines.len(), 1); + assert_eq!(col_index.utf16_lines[&1].len(), 2); + assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() }); + assert_eq!(col_index.utf16_lines[&1][1], Utf16Char { start: 21.into(), end: 24.into() }); + + // UTF-8 to UTF-16 + assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15); + + assert_eq!(col_index.utf8_to_utf16_col(1, 21.into()), 19); + assert_eq!(col_index.utf8_to_utf16_col(1, 25.into()), 21); + + assert!(col_index.utf8_to_utf16_col(2, 15.into()) == 15); + + // UTF-16 to UTF-8 + assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextSize::from(15)); + + // メ UTF-8: 0xE3 0x83 0xA1, UTF-16: 0x30E1 + assert_eq!(col_index.utf16_to_utf8_col(1, 17), TextSize::from(17)); // first メ at 17..20 + assert_eq!(col_index.utf16_to_utf8_col(1, 18), TextSize::from(20)); // space + assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21)); // second メ at 21..24 + + assert_eq!(col_index.utf16_to_utf8_col(2, 15), TextSize::from(15)); +} + +#[test] +fn test_splitlines() { + fn r(lo: u32, hi: u32) -> TextRange { + TextRange::new(lo.into(), hi.into()) + } + + let text = "a\nbb\nccc\n"; + let line_index = LineIndex::new(text); + + let actual = line_index.lines(r(0, 9)).collect::>(); + let expected = vec![r(0, 2), r(2, 5), r(5, 9)]; + assert_eq!(actual, expected); + + let text = ""; + let line_index = LineIndex::new(text); + + let actual = line_index.lines(r(0, 0)).collect::>(); + let expected = vec![]; + assert_eq!(actual, expected); + + let text = "\n"; + let line_index = LineIndex::new(text); + + let actual = line_index.lines(r(0, 1)).collect::>(); + let expected = vec![r(0, 1)]; + assert_eq!(actual, expected) +} diff --git a/crates/ide_db/src/traits.rs b/crates/ide_db/src/traits.rs index f57b6dd91ec..78a43f587ab 100644 --- a/crates/ide_db/src/traits.rs +++ b/crates/ide_db/src/traits.rs @@ -78,150 +78,4 @@ pub fn get_missing_assoc_items( } #[cfg(test)] -mod tests { - use crate::RootDatabase; - use base_db::{fixture::ChangeFixture, FilePosition}; - use expect_test::{expect, Expect}; - use hir::Semantics; - use syntax::ast::{self, AstNode}; - use test_utils::RangeOrOffset; - - /// Creates analysis from a multi-file fixture, returns positions marked with <|>. - pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { - let change_fixture = ChangeFixture::parse(ra_fixture); - let mut database = RootDatabase::default(); - database.apply_change(change_fixture.change); - let (file_id, range_or_offset) = - change_fixture.file_position.expect("expected a marker (<|>)"); - let offset = match range_or_offset { - RangeOrOffset::Range(_) => panic!(), - RangeOrOffset::Offset(it) => it, - }; - (database, FilePosition { file_id, offset }) - } - - fn check_trait(ra_fixture: &str, expect: Expect) { - let (db, position) = position(ra_fixture); - let sema = Semantics::new(&db); - let file = sema.parse(position.file_id); - let impl_block: ast::Impl = - sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap(); - let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block); - let actual = match trait_ { - Some(trait_) => trait_.name(&db).to_string(), - None => String::new(), - }; - expect.assert_eq(&actual); - } - - fn check_missing_assoc(ra_fixture: &str, expect: Expect) { - let (db, position) = position(ra_fixture); - let sema = Semantics::new(&db); - let file = sema.parse(position.file_id); - let impl_block: ast::Impl = - sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap(); - let items = crate::traits::get_missing_assoc_items(&sema, &impl_block); - let actual = items - .into_iter() - .map(|item| item.name(&db).unwrap().to_string()) - .collect::>() - .join("\n"); - expect.assert_eq(&actual); - } - - #[test] - fn resolve_trait() { - check_trait( - r#" -pub trait Foo { - fn bar(); -} -impl Foo for u8 { - <|> -} - "#, - expect![["Foo"]], - ); - check_trait( - r#" -pub trait Foo { - fn bar(); -} -impl Foo for u8 { - fn bar() { - fn baz() { - <|> - } - baz(); - } -} - "#, - expect![["Foo"]], - ); - check_trait( - r#" -pub trait Foo { - fn bar(); -} -pub struct Bar; -impl Bar { - <|> -} - "#, - expect![[""]], - ); - } - - #[test] - fn missing_assoc_items() { - check_missing_assoc( - r#" -pub trait Foo { - const FOO: u8; - fn bar(); -} -impl Foo for u8 { - <|> -}"#, - expect![[r#" - FOO - bar"#]], - ); - - check_missing_assoc( - r#" -pub trait Foo { - const FOO: u8; - fn bar(); -} -impl Foo for u8 { - const FOO: u8 = 10; - <|> -}"#, - expect![[r#" - bar"#]], - ); - - check_missing_assoc( - r#" -pub trait Foo { - const FOO: u8; - fn bar(); -} -impl Foo for u8 { - const FOO: u8 = 10; - fn bar() {<|>} -}"#, - expect![[r#""#]], - ); - - check_missing_assoc( - r#" -pub struct Foo; -impl Foo { - fn bar() {<|>} -}"#, - expect![[r#""#]], - ); - } -} +mod tests; diff --git a/crates/ide_db/src/traits/tests.rs b/crates/ide_db/src/traits/tests.rs new file mode 100644 index 00000000000..09c7ac3ec4a --- /dev/null +++ b/crates/ide_db/src/traits/tests.rs @@ -0,0 +1,144 @@ +use crate::RootDatabase; +use base_db::{fixture::ChangeFixture, FilePosition}; +use expect_test::{expect, Expect}; +use hir::Semantics; +use syntax::ast::{self, AstNode}; +use test_utils::RangeOrOffset; + +/// Creates analysis from a multi-file fixture, returns positions marked with <|>. +pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { + let change_fixture = ChangeFixture::parse(ra_fixture); + let mut database = RootDatabase::default(); + database.apply_change(change_fixture.change); + let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)"); + let offset = match range_or_offset { + RangeOrOffset::Range(_) => panic!(), + RangeOrOffset::Offset(it) => it, + }; + (database, FilePosition { file_id, offset }) +} + +fn check_trait(ra_fixture: &str, expect: Expect) { + let (db, position) = position(ra_fixture); + let sema = Semantics::new(&db); + let file = sema.parse(position.file_id); + let impl_block: ast::Impl = + sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap(); + let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block); + let actual = match trait_ { + Some(trait_) => trait_.name(&db).to_string(), + None => String::new(), + }; + expect.assert_eq(&actual); +} + +fn check_missing_assoc(ra_fixture: &str, expect: Expect) { + let (db, position) = position(ra_fixture); + let sema = Semantics::new(&db); + let file = sema.parse(position.file_id); + let impl_block: ast::Impl = + sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap(); + let items = crate::traits::get_missing_assoc_items(&sema, &impl_block); + let actual = items + .into_iter() + .map(|item| item.name(&db).unwrap().to_string()) + .collect::>() + .join("\n"); + expect.assert_eq(&actual); +} + +#[test] +fn resolve_trait() { + check_trait( + r#" +pub trait Foo { + fn bar(); +} +impl Foo for u8 { + <|> +} + "#, + expect![["Foo"]], + ); + check_trait( + r#" +pub trait Foo { + fn bar(); +} +impl Foo for u8 { + fn bar() { + fn baz() { + <|> + } + baz(); + } +} + "#, + expect![["Foo"]], + ); + check_trait( + r#" +pub trait Foo { + fn bar(); +} +pub struct Bar; +impl Bar { + <|> +} + "#, + expect![[""]], + ); +} + +#[test] +fn missing_assoc_items() { + check_missing_assoc( + r#" +pub trait Foo { + const FOO: u8; + fn bar(); +} +impl Foo for u8 { + <|> +}"#, + expect![[r#" + FOO + bar"#]], + ); + + check_missing_assoc( + r#" +pub trait Foo { + const FOO: u8; + fn bar(); +} +impl Foo for u8 { + const FOO: u8 = 10; + <|> +}"#, + expect![[r#" + bar"#]], + ); + + check_missing_assoc( + r#" +pub trait Foo { + const FOO: u8; + fn bar(); +} +impl Foo for u8 { + const FOO: u8 = 10; + fn bar() {<|>} +}"#, + expect![[r#""#]], + ); + + check_missing_assoc( + r#" +pub struct Foo; +impl Foo { + fn bar() {<|>} +}"#, + expect![[r#""#]], + ); +}