Repository: rust-analyzer/rust-analyzer
Pull request #6710 (2020-12-03 15:06:29)

Extract tests module to file in ide_db crate
Helps with #6522 

- pass `cargo test`


Patch:
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs
index 83a602b9ad..615fa7b0ee 100644
--- a/crates/ide_db/src/call_info.rs
+++ b/crates/ide_db/src/call_info.rs
@@ -228,529 +228,4 @@ impl FnCallNode {
 }
 
 #[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::<Vec<_>>()
-                    .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
-                (<x: 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>, 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, <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, <y: 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
-                (<x: u32>, y: u32)
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_two_args_first_generics() {
-        check(
-            r#"
-fn foo<T, U: Copy + Display>(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
-                (<x: i32>, y: {unknown})
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_no_params() {
-        check(
-            r#"
-fn foo<T>() -> 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)
-                (<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)
-                (<self: &S>, 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
-                (<j: 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
-                (<x: 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
-                (<x: i32>)
-            "##]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_with_docs_from_actix() {
-        check(
-            r#"
-struct WriteHandler<E>;
-
-impl<E> WriteHandler<E> {
-    /// 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})
-                (<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, <i32>)
-            "#]],
-        );
-    }
-
-    #[test]
-    fn generic_struct() {
-        check(
-            r#"
-struct S<T>(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)
-                (<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
-                (<S>)
-            "#]],
-        )
-    }
-
-    #[test]
-    fn call_info_for_fn_ptr() {
-        check(
-            r#"
-fn main(f: fn(i32, f64) -> char) {
-    f(0, <|>)
-}
-        "#,
-            expect![[r#"
-                (i32, f64) -> char
-                (i32, <f64>)
-            "#]],
-        )
-    }
-}
+mod tests;
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index 08d246c167..040843990d 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<MergeBehaviour>,
-        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/line_index.rs b/crates/ide_db/src/line_index.rs
index a381f7fb8d..41226305ea 100644
--- a/crates/ide_db/src/line_index.rs
+++ b/crates/ide_db/src/line_index.rs
@@ -149,133 +149,4 @@ impl LineIndex {
 }
 
 #[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::<Vec<_>>();
-        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::<Vec<_>>();
-        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::<Vec<_>>();
-        let expected = vec![r(0, 1)];
-        assert_eq!(actual, expected)
-    }
-}
+mod tests;
diff --git a/crates/ide_db/src/traits.rs b/crates/ide_db/src/traits.rs
index f57b6dd91e..78a43f587a 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::<Vec<_>>()
-            .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;


Test patch:
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 0000000000..9335aeaa5f
--- /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::<Vec<_>>()
+                .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
+                (<x: 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>, 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, <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, <y: 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
+                (<x: u32>, y: u32)
+            "#]],
+    );
+}
+
+#[test]
+fn test_fn_signature_two_args_first_generics() {
+    check(
+        r#"
+fn foo<T, U: Copy + Display>(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
+                (<x: i32>, y: {unknown})
+            "#]],
+    );
+}
+
+#[test]
+fn test_fn_signature_no_params() {
+    check(
+        r#"
+fn foo<T>() -> 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)
+                (<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)
+                (<self: &S>, 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
+                (<j: 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
+                (<x: 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
+                (<x: i32>)
+            "##]],
+    );
+}
+
+#[test]
+fn test_fn_signature_with_docs_from_actix() {
+    check(
+        r#"
+struct WriteHandler<E>;
+
+impl<E> WriteHandler<E> {
+    /// 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})
+                (<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, <i32>)
+            "#]],
+    );
+}
+
+#[test]
+fn generic_struct() {
+    check(
+        r#"
+struct S<T>(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)
+                (<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
+                (<S>)
+            "#]],
+    )
+}
+
+#[test]
+fn call_info_for_fn_ptr() {
+    check(
+        r#"
+fn main(f: fn(i32, f64) -> char) {
+    f(0, <|>)
+}
+        "#,
+        expect![[r#"
+                (i32, f64) -> char
+                (i32, <f64>)
+            "#]],
+    )
+}
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 0000000000..86bfa5b41f
--- /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<MergeBehaviour>,
+    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/tests.rs b/crates/ide_db/src/line_index/tests.rs
new file mode 100644
index 0000000000..05f7484e8f
--- /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::<Vec<_>>();
+    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::<Vec<_>>();
+    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::<Vec<_>>();
+    let expected = vec![r(0, 1)];
+    assert_eq!(actual, expected)
+}
diff --git a/crates/ide_db/src/traits/tests.rs b/crates/ide_db/src/traits/tests.rs
new file mode 100644
index 0000000000..09c7ac3ec4
--- /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::<Vec<_>>()
+        .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#""#]],
+    );
+}