Repository: rust-analyzer/rust-analyzer
Pull request #482 (2019-01-10 15:32:17)

fix code duplication


Patch:
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index b8246a7d1e..1b6b72c98e 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -17,8 +17,6 @@ macro_rules! ctry {
 pub mod db;
 #[cfg(test)]
 mod mock;
-#[macro_use]
-mod marks;
 mod query_definitions;
 mod path;
 pub mod source_binder;
diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs
deleted file mode 100644
index 05430b975c..0000000000
--- a/crates/ra_hir/src/marks.rs
+++ /dev/null
@@ -1,82 +0,0 @@
-//! This module implements manually tracked test coverage, which useful for
-//! quickly finding a test responsible for testing a particular bit of code.
-//!
-//! See https://matklad.github.io/2018/06/18/a-trick-for-test-maintenance.html
-//! for details, but the TL;DR is that you write your test as
-//!
-//! ```no-run
-//! #[test]
-//! fn test_foo() {
-//!     covers!(test_foo);
-//! }
-//! ```
-//!
-//! and in the code under test you write
-//!
-//! ```no-run
-//! fn foo() {
-//!     if some_condition() {
-//!         tested_by!(test_foo);
-//!     }
-//! }
-//! ```
-//!
-//! This module then checks that executing the test indeed covers the specified
-//! function. This is useful if you come back to the `foo` function ten years
-//! later and wonder where the test are: now you can grep for `test_foo`.
-
-#[macro_export]
-macro_rules! tested_by {
-    ($ident:ident) => {
-        #[cfg(test)]
-        {
-            crate::marks::marks::$ident.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
-        }
-    };
-}
-
-#[macro_export]
-macro_rules! covers {
-    ($ident:ident) => {
-        let _checker = crate::marks::marks::MarkChecker::new(&crate::marks::marks::$ident);
-    };
-}
-
-#[cfg(test)]
-pub(crate) mod marks {
-    use std::sync::atomic::{AtomicUsize, Ordering};
-
-    pub(crate) struct MarkChecker {
-        mark: &'static AtomicUsize,
-        value_on_entry: usize,
-    }
-
-    impl MarkChecker {
-        pub(crate) fn new(mark: &'static AtomicUsize) -> MarkChecker {
-            let value_on_entry = mark.load(Ordering::SeqCst);
-            MarkChecker {
-                mark,
-                value_on_entry,
-            }
-        }
-    }
-
-    impl Drop for MarkChecker {
-        fn drop(&mut self) {
-            if std::thread::panicking() {
-                return;
-            }
-            let value_on_exit = self.mark.load(Ordering::SeqCst);
-            assert!(value_on_exit > self.value_on_entry, "mark was not hit")
-        }
-    }
-
-    macro_rules! mark {
-        ($ident:ident) => {
-            #[allow(bad_style)]
-            pub(crate) static $ident: AtomicUsize = AtomicUsize::new(0);
-        };
-    }
-
-    mark!(name_res_works_for_broken_modules);
-}
diff --git a/crates/ra_hir/src/module_tree.rs b/crates/ra_hir/src/module_tree.rs
index 50383c6d8c..d2c92f150c 100644
--- a/crates/ra_hir/src/module_tree.rs
+++ b/crates/ra_hir/src/module_tree.rs
@@ -14,7 +14,7 @@ use ra_arena::{Arena, RawId, impl_arena_id};
 use crate::{Name, AsName, HirDatabase, SourceItemId, HirFileId, Problem, SourceFileItems, ModuleSource};
 
 impl ModuleSource {
-    pub(crate) fn from_source_item_id(
+    pub fn from_source_item_id(
         db: &impl HirDatabase,
         source_item_id: SourceItemId,
     ) -> ModuleSource {
@@ -217,10 +217,6 @@ fn modules(root: &impl ast::ModuleItemOwner) -> impl Iterator<Item = (Name, &ast
         })
         .filter_map(|module| {
             let name = module.name()?.as_name();
-            if !module.has_semi() && module.item_list().is_none() {
-                tested_by!(name_res_works_for_broken_modules);
-                return None;
-            }
             Some((name, module))
         })
 }
diff --git a/crates/ra_ide_api_light/src/assists/replace_if_let_with_match.rs b/crates/ra_ide_api_light/src/assists/replace_if_let_with_match.rs
index 30c3714801..d64c34d54a 100644
--- a/crates/ra_ide_api_light/src/assists/replace_if_let_with_match.rs
+++ b/crates/ra_ide_api_light/src/assists/replace_if_let_with_match.rs
@@ -1,9 +1,9 @@
-use ra_syntax::{
-    AstNode, SyntaxKind::{L_CURLY, R_CURLY, WHITESPACE},
-    ast,
-};
+use ra_syntax::{AstNode, ast};
 
-use crate::assists::{AssistCtx, Assist};
+use crate::{
+    assists::{AssistCtx, Assist},
+    formatting::extract_trivial_expression,
+};
 
 pub fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> {
     let if_expr: &ast::IfExpr = ctx.node_at_offset()?;
@@ -39,26 +39,12 @@ fn build_match_expr(
 }
 
 fn format_arm(block: &ast::Block) -> String {
-    match extract_expression(block) {
+    match extract_trivial_expression(block) {
         None => block.syntax().text().to_string(),
         Some(e) => format!("{},", e.syntax().text()),
     }
 }
 
-fn extract_expression(block: &ast::Block) -> Option<&ast::Expr> {
-    let expr = block.expr()?;
-    let non_trivial_children = block.syntax().children().filter(|it| {
-        !(it == &expr.syntax()
-            || it.kind() == L_CURLY
-            || it.kind() == R_CURLY
-            || it.kind() == WHITESPACE)
-    });
-    if non_trivial_children.count() > 0 {
-        return None;
-    }
-    Some(expr)
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/crates/ra_ide_api_light/src/formatting.rs b/crates/ra_ide_api_light/src/formatting.rs
new file mode 100644
index 0000000000..1f37692099
--- /dev/null
+++ b/crates/ra_ide_api_light/src/formatting.rs
@@ -0,0 +1,42 @@
+use ra_syntax::{
+    ast, AstNode,
+    SyntaxNode, SyntaxKind::*,
+};
+
+pub(crate) fn extract_trivial_expression(block: &ast::Block) -> Option<&ast::Expr> {
+    let expr = block.expr()?;
+    if expr.syntax().text().contains('\n') {
+        return None;
+    }
+    let non_trivial_children = block.syntax().children().filter(|it| match it.kind() {
+        WHITESPACE | L_CURLY | R_CURLY => false,
+        _ => it != &expr.syntax(),
+    });
+    if non_trivial_children.count() > 0 {
+        return None;
+    }
+    Some(expr)
+}
+
+pub(crate) fn compute_ws(left: &SyntaxNode, right: &SyntaxNode) -> &'static str {
+    match left.kind() {
+        L_PAREN | L_BRACK => return "",
+        L_CURLY => {
+            if let USE_TREE = right.kind() {
+                return "";
+            }
+        }
+        _ => (),
+    }
+    match right.kind() {
+        R_PAREN | R_BRACK => return "",
+        R_CURLY => {
+            if let USE_TREE = left.kind() {
+                return "";
+            }
+        }
+        DOT => return "",
+        _ => (),
+    }
+    " "
+}
diff --git a/crates/ra_ide_api_light/src/join_lines.rs b/crates/ra_ide_api_light/src/join_lines.rs
new file mode 100644
index 0000000000..ab7c5b4b5e
--- /dev/null
+++ b/crates/ra_ide_api_light/src/join_lines.rs
@@ -0,0 +1,451 @@
+use itertools::Itertools;
+use ra_syntax::{
+    SourceFile, TextRange, TextUnit, AstNode, SyntaxNode,
+    SyntaxKind::{self, WHITESPACE, COMMA, R_CURLY, R_PAREN, R_BRACK},
+    algo::find_covering_node,
+    ast,
+};
+
+use crate::{
+    LocalEdit, TextEditBuilder,
+    formatting::{compute_ws, extract_trivial_expression},
+};
+
+pub fn join_lines(file: &SourceFile, range: TextRange) -> LocalEdit {
+    let range = if range.is_empty() {
+        let syntax = file.syntax();
+        let text = syntax.text().slice(range.start()..);
+        let pos = match text.find('\n') {
+            None => {
+                return LocalEdit {
+                    label: "join lines".to_string(),
+                    edit: TextEditBuilder::default().finish(),
+                    cursor_position: None,
+                };
+            }
+            Some(pos) => pos,
+        };
+        TextRange::offset_len(range.start() + pos, TextUnit::of_char('\n'))
+    } else {
+        range
+    };
+
+    let node = find_covering_node(file.syntax(), range);
+    let mut edit = TextEditBuilder::default();
+    for node in node.descendants() {
+        let text = match node.leaf_text() {
+            Some(text) => text,
+            None => continue,
+        };
+        let range = match range.intersection(&node.range()) {
+            Some(range) => range,
+            None => continue,
+        } - node.range().start();
+        for (pos, _) in text[range].bytes().enumerate().filter(|&(_, b)| b == b'\n') {
+            let pos: TextUnit = (pos as u32).into();
+            let off = node.range().start() + range.start() + pos;
+            if !edit.invalidates_offset(off) {
+                remove_newline(&mut edit, node, text.as_str(), off);
+            }
+        }
+    }
+
+    LocalEdit {
+        label: "join lines".to_string(),
+        edit: edit.finish(),
+        cursor_position: None,
+    }
+}
+
+fn remove_newline(
+    edit: &mut TextEditBuilder,
+    node: &SyntaxNode,
+    node_text: &str,
+    offset: TextUnit,
+) {
+    if node.kind() != WHITESPACE || node_text.bytes().filter(|&b| b == b'\n').count() != 1 {
+        // The node is either the first or the last in the file
+        let suff = &node_text[TextRange::from_to(
+            offset - node.range().start() + TextUnit::of_char('\n'),
+            TextUnit::of_str(node_text),
+        )];
+        let spaces = suff.bytes().take_while(|&b| b == b' ').count();
+
+        edit.replace(
+            TextRange::offset_len(offset, ((spaces + 1) as u32).into()),
+            " ".to_string(),
+        );
+        return;
+    }
+
+    // Special case that turns something like:
+    //
+    // ```
+    // my_function({<|>
+    //    <some-expr>
+    // })
+    // ```
+    //
+    // into `my_function(<some-expr>)`
+    if join_single_expr_block(edit, node).is_some() {
+        return;
+    }
+    // ditto for
+    //
+    // ```
+    // use foo::{<|>
+    //    bar
+    // };
+    // ```
+    if join_single_use_tree(edit, node).is_some() {
+        return;
+    }
+
+    // The node is between two other nodes
+    let prev = node.prev_sibling().unwrap();
+    let next = node.next_sibling().unwrap();
+    if is_trailing_comma(prev.kind(), next.kind()) {
+        // Removes: trailing comma, newline (incl. surrounding whitespace)
+        edit.delete(TextRange::from_to(prev.range().start(), node.range().end()));
+    } else if prev.kind() == COMMA && next.kind() == R_CURLY {
+        // Removes: comma, newline (incl. surrounding whitespace)
+        let space = if let Some(left) = prev.prev_sibling() {
+            compute_ws(left, next)
+        } else {
+            " "
+        };
+        edit.replace(
+            TextRange::from_to(prev.range().start(), node.range().end()),
+            space.to_string(),
+        );
+    } else if let (Some(_), Some(next)) = (ast::Comment::cast(prev), ast::Comment::cast(next)) {
+        // Removes: newline (incl. surrounding whitespace), start of the next comment
+        edit.delete(TextRange::from_to(
+            node.range().start(),
+            next.syntax().range().start() + TextUnit::of_str(next.prefix()),
+        ));
+    } else {
+        // Remove newline but add a computed amount of whitespace characters
+        edit.replace(node.range(), compute_ws(prev, next).to_string());
+    }
+}
+
+fn join_single_expr_block(edit: &mut TextEditBuilder, node: &SyntaxNode) -> Option<()> {
+    let block = ast::Block::cast(node.parent()?)?;
+    let block_expr = ast::BlockExpr::cast(block.syntax().parent()?)?;
+    let expr = extract_trivial_expression(block)?;
+    edit.replace(
+        block_expr.syntax().range(),
+        expr.syntax().text().to_string(),
+    );
+    Some(())
+}
+
+fn join_single_use_tree(edit: &mut TextEditBuilder, node: &SyntaxNode) -> Option<()> {
+    let use_tree_list = ast::UseTreeList::cast(node.parent()?)?;
+    let (tree,) = use_tree_list.use_trees().collect_tuple()?;
+    edit.replace(
+        use_tree_list.syntax().range(),
+        tree.syntax().text().to_string(),
+    );
+    Some(())
+}
+
+fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool {
+    match (left, right) {
+        (COMMA, R_PAREN) | (COMMA, R_BRACK) => true,
+        _ => false,
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::test_utils::{assert_eq_text, check_action, extract_range};
+
+    use super::*;
+
+    fn check_join_lines(before: &str, after: &str) {
+        check_action(before, after, |file, offset| {
+            let range = TextRange::offset_len(offset, 0.into());
+            let res = join_lines(file, range);
+            Some(res)
+        })
+    }
+
+    #[test]
+    fn test_join_lines_comma() {
+        check_join_lines(
+            r"
+fn foo() {
+    <|>foo(1,
+    )
+}
+",
+            r"
+fn foo() {
+    <|>foo(1)
+}
+",
+        );
+    }
+
+    #[test]
+    fn test_join_lines_lambda_block() {
+        check_join_lines(
+            r"
+pub fn reparse(&self, edit: &AtomTextEdit) -> File {
+    <|>self.incremental_reparse(edit).unwrap_or_else(|| {
+        self.full_reparse(edit)
+    })
+}
+",
+            r"
+pub fn reparse(&self, edit: &AtomTextEdit) -> File {
+    <|>self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit))
+}
+",
+        );
+    }
+
+    #[test]
+    fn test_join_lines_block() {
+        check_join_lines(
+            r"
+fn foo() {
+    foo(<|>{
+        92
+    })
+}",
+            r"
+fn foo() {
+    foo(<|>92)
+}",
+        );
+    }
+
+    #[test]
+    fn test_join_lines_use_items_left() {
+        // No space after the '{'
+        check_join_lines(
+            r"
+<|>use ra_syntax::{
+    TextUnit, TextRange,
+};",
+            r"
+<|>use ra_syntax::{TextUnit, TextRange,
+};",
+        );
+    }
+
+    #[test]
+    fn test_join_lines_use_items_right() {
+        // No space after the '}'
+        check_join_lines(
+            r"
+use ra_syntax::{
+<|>    TextUnit, TextRange
+};",
+            r"
+use ra_syntax::{
+<|>    TextUnit, TextRange};",
+        );
+    }
+
+    #[test]
+    fn test_join_lines_use_items_right_comma() {
+        // No space after the '}'
+        check_join_lines(
+            r"
+use ra_syntax::{
+<|>    TextUnit, TextRange,
+};",
+            r"
+use ra_syntax::{
+<|>    TextUnit, TextRange};",
+        );
+    }
+
+    #[test]
+    fn test_join_lines_use_tree() {
+        check_join_lines(
+            r"
+use ra_syntax::{
+    algo::<|>{
+        find_leaf_at_offset,
+    },
+    ast,
+};",
+            r"
+use ra_syntax::{
+    algo::<|>find_leaf_at_offset,
+    ast,
+};",
+        );
+    }
+
+    #[test]
+    fn test_join_lines_normal_comments() {
+        check_join_lines(
+            r"
+fn foo() {
+    // Hello<|>
+    // world!
+}
+",
+            r"
+fn foo() {
+    // Hello<|> world!
+}
+",
+        );
+    }
+
+    #[test]
+    fn test_join_lines_doc_comments() {
+        check_join_lines(
+            r"
+fn foo() {
+    /// Hello<|>
+    /// world!
+}
+",
+            r"
+fn foo() {
+    /// Hello<|> world!
+}
+",
+        );
+    }
+
+    #[test]
+    fn test_join_lines_mod_comments() {
+        check_join_lines(
+            r"
+fn foo() {
+    //! Hello<|>
+    //! world!
+}
+",
+            r"
+fn foo() {
+    //! Hello<|> world!
+}
+",
+        );
+    }
+
+    #[test]
+    fn test_join_lines_multiline_comments_1() {
+        check_join_lines(
+            r"
+fn foo() {
+    // Hello<|>
+    /* world! */
+}
+",
+            r"
+fn foo() {
+    // Hello<|> world! */
+}
+",
+        );
+    }
+
+    #[test]
+    fn test_join_lines_multiline_comments_2() {
+        check_join_lines(
+            r"
+fn foo() {
+    // The<|>
+    /* quick
+    brown
+    fox! */
+}
+",
+            r"
+fn foo() {
+    // The<|> quick
+    brown
+    fox! */
+}
+",
+        );
+    }
+
+    fn check_join_lines_sel(before: &str, after: &str) {
+        let (sel, before) = extract_range(before);
+        let file = SourceFile::parse(&before);
+        let result = join_lines(&file, sel);
+        let actual = result.edit.apply(&before);
+        assert_eq_text!(after, &actual);
+    }
+
+    #[test]
+    fn test_join_lines_selection_fn_args() {
+        check_join_lines_sel(
+            r"
+fn foo() {
+    <|>foo(1,
+        2,
+        3,
+    <|>)
+}
+    ",
+            r"
+fn foo() {
+    foo(1, 2, 3)
+}
+    ",
+        );
+    }
+
+    #[test]
+    fn test_join_lines_selection_struct() {
+        check_join_lines_sel(
+            r"
+struct Foo <|>{
+    f: u32,
+}<|>
+    ",
+            r"
+struct Foo { f: u32 }
+    ",
+        );
+    }
+
+    #[test]
+    fn test_join_lines_selection_dot_chain() {
+        check_join_lines_sel(
+            r"
+fn foo() {
+    join(<|>type_params.type_params()
+            .filter_map(|it| it.name())
+            .map(|it| it.text())<|>)
+}",
+            r"
+fn foo() {
+    join(type_params.type_params().filter_map(|it| it.name()).map(|it| it.text()))
+}",
+        );
+    }
+
+    #[test]
+    fn test_join_lines_selection_lambda_block_body() {
+        check_join_lines_sel(
+            r"
+pub fn handle_find_matching_brace() {
+    params.offsets
+        .map(|offset| <|>{
+            world.analysis().matching_brace(&file, offset).unwrap_or(offset)
+        }<|>)
+        .collect();
+}",
+            r"
+pub fn handle_find_matching_brace() {
+    params.offsets
+        .map(|offset| world.analysis().matching_brace(&file, offset).unwrap_or(offset))
+        .collect();
+}",
+        );
+    }
+}
diff --git a/crates/ra_ide_api_light/src/lib.rs b/crates/ra_ide_api_light/src/lib.rs
index 40638eda8e..bc9bee752e 100644
--- a/crates/ra_ide_api_light/src/lib.rs
+++ b/crates/ra_ide_api_light/src/lib.rs
@@ -11,8 +11,10 @@ mod line_index_utils;
 mod structure;
 #[cfg(test)]
 mod test_utils;
+mod join_lines;
 mod typing;
 mod diagnostics;
+pub(crate) mod formatting;
 
 pub use self::{
     assists::LocalEdit,
@@ -21,8 +23,10 @@ pub use self::{
     line_index::{LineCol, LineIndex},
     line_index_utils::translate_offset_with_edit,
     structure::{file_structure, StructureNode},
-    typing::{join_lines, on_enter, on_dot_typed, on_eq_typed},
-    diagnostics::diagnostics
+    diagnostics::diagnostics,
+    join_lines::join_lines,
+    typing::{on_enter, on_dot_typed, on_eq_typed},
+
 };
 use ra_text_edit::TextEditBuilder;
 use ra_syntax::{
diff --git a/crates/ra_ide_api_light/src/typing.rs b/crates/ra_ide_api_light/src/typing.rs
index d8177f245e..5726209cce 100644
--- a/crates/ra_ide_api_light/src/typing.rs
+++ b/crates/ra_ide_api_light/src/typing.rs
@@ -1,62 +1,12 @@
-use std::mem;
-
-use itertools::Itertools;
 use ra_syntax::{
-    algo::{find_node_at_offset, find_covering_node, find_leaf_at_offset, LeafAtOffset},
+    algo::{find_node_at_offset, find_leaf_at_offset, LeafAtOffset},
     ast,
-    AstNode, Direction, SourceFile, SyntaxKind,
-    SyntaxKind::*,
-    SyntaxNode, TextRange, TextUnit,
+    AstNode, Direction, SourceFile, SyntaxKind::*,
+    SyntaxNode, TextUnit,
 };
 
 use crate::{LocalEdit, TextEditBuilder};
 
-pub fn join_lines(file: &SourceFile, range: TextRange) -> LocalEdit {
-    let range = if range.is_empty() {
-        let syntax = file.syntax();
-        let text = syntax.text().slice(range.start()..);
-        let pos = match text.find('\n') {
-            None => {
-                return LocalEdit {
-                    label: "join lines".to_string(),
-                    edit: TextEditBuilder::default().finish(),
-                    cursor_position: None,
-                };
-            }
-            Some(pos) => pos,
-        };
-        TextRange::offset_len(range.start() + pos, TextUnit::of_char('\n'))
-    } else {
-        range
-    };
-
-    let node = find_covering_node(file.syntax(), range);
-    let mut edit = TextEditBuilder::default();
-    for node in node.descendants() {
-        let text = match node.leaf_text() {
-            Some(text) => text,
-            None => continue,
-        };
-        let range = match range.intersection(&node.range()) {
-            Some(range) => range,
-            None => continue,
-        } - node.range().start();
-        for (pos, _) in text[range].bytes().enumerate().filter(|&(_, b)| b == b'\n') {
-            let pos: TextUnit = (pos as u32).into();
-            let off = node.range().start() + range.start() + pos;
-            if !edit.invalidates_offset(off) {
-                remove_newline(&mut edit, node, text.as_str(), off);
-            }
-        }
-    }
-
-    LocalEdit {
-        label: "join lines".to_string(),
-        edit: edit.finish(),
-        cursor_position: None,
-    }
-}
-
 pub fn on_enter(file: &SourceFile, offset: TextUnit) -> Option<LocalEdit> {
     let comment = find_leaf_at_offset(file.syntax(), offset)
         .left_biased()
@@ -184,441 +134,11 @@ fn last_line_indent_in_whitespace(ws: &str) -> &str {
     ws.split('\n').last().unwrap_or("")
 }
 
-fn remove_newline(
-    edit: &mut TextEditBuilder,
-    node: &SyntaxNode,
-    node_text: &str,
-    offset: TextUnit,
-) {
-    if node.kind() != WHITESPACE || node_text.bytes().filter(|&b| b == b'\n').count() != 1 {
-        // The node is either the first or the last in the file
-        let suff = &node_text[TextRange::from_to(
-            offset - node.range().start() + TextUnit::of_char('\n'),
-            TextUnit::of_str(node_text),
-        )];
-        let spaces = suff.bytes().take_while(|&b| b == b' ').count();
-
-        edit.replace(
-            TextRange::offset_len(offset, ((spaces + 1) as u32).into()),
-            " ".to_string(),
-        );
-        return;
-    }
-
-    // Special case that turns something like:
-    //
-    // ```
-    // my_function({<|>
-    //    <some-expr>
-    // })
-    // ```
-    //
-    // into `my_function(<some-expr>)`
-    if join_single_expr_block(edit, node).is_some() {
-        return;
-    }
-    // ditto for
-    //
-    // ```
-    // use foo::{<|>
-    //    bar
-    // };
-    // ```
-    if join_single_use_tree(edit, node).is_some() {
-        return;
-    }
-
-    // The node is between two other nodes
-    let prev = node.prev_sibling().unwrap();
-    let next = node.next_sibling().unwrap();
-    if is_trailing_comma(prev.kind(), next.kind()) {
-        // Removes: trailing comma, newline (incl. surrounding whitespace)
-        edit.delete(TextRange::from_to(prev.range().start(), node.range().end()));
-    } else if prev.kind() == COMMA && next.kind() == R_CURLY {
-        // Removes: comma, newline (incl. surrounding whitespace)
-        let space = if let Some(left) = prev.prev_sibling() {
-            compute_ws(left, next)
-        } else {
-            " "
-        };
-        edit.replace(
-            TextRange::from_to(prev.range().start(), node.range().end()),
-            space.to_string(),
-        );
-    } else if let (Some(_), Some(next)) = (ast::Comment::cast(prev), ast::Comment::cast(next)) {
-        // Removes: newline (incl. surrounding whitespace), start of the next comment
-        edit.delete(TextRange::from_to(
-            node.range().start(),
-            next.syntax().range().start() + TextUnit::of_str(next.prefix()),
-        ));
-    } else {
-        // Remove newline but add a computed amount of whitespace characters
-        edit.replace(node.range(), compute_ws(prev, next).to_string());
-    }
-}
-
-fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool {
-    match (left, right) {
-        (COMMA, R_PAREN) | (COMMA, R_BRACK) => true,
-        _ => false,
-    }
-}
-
-fn join_single_expr_block(edit: &mut TextEditBuilder, node: &SyntaxNode) -> Option<()> {
-    let block = ast::Block::cast(node.parent()?)?;
-    let block_expr = ast::BlockExpr::cast(block.syntax().parent()?)?;
-    let expr = single_expr(block)?;
-    edit.replace(
-        block_expr.syntax().range(),
-        expr.syntax().text().to_string(),
-    );
-    Some(())
-}
-
-fn single_expr(block: &ast::Block) -> Option<&ast::Expr> {
-    let mut res = None;
-    for child in block.syntax().children() {
-        if let Some(expr) = ast::Expr::cast(child) {
-            if expr.syntax().text().contains('\n') {
-                return None;
-            }
-            if mem::replace(&mut res, Some(expr)).is_some() {
-                return None;
-            }
-        } else {
-            match child.kind() {
-                WHITESPACE | L_CURLY | R_CURLY => (),
-                _ => return None,
-            }
-        }
-    }
-    res
-}
-
-fn join_single_use_tree(edit: &mut TextEditBuilder, node: &SyntaxNode) -> Option<()> {
-    let use_tree_list = ast::UseTreeList::cast(node.parent()?)?;
-    let (tree,) = use_tree_list.use_trees().collect_tuple()?;
-    edit.replace(
-        use_tree_list.syntax().range(),
-        tree.syntax().text().to_string(),
-    );
-    Some(())
-}
-
-fn compute_ws(left: &SyntaxNode, right: &SyntaxNode) -> &'static str {
-    match left.kind() {
-        L_PAREN | L_BRACK => return "",
-        L_CURLY => {
-            if let USE_TREE = right.kind() {
-                return "";
-            }
-        }
-        _ => (),
-    }
-    match right.kind() {
-        R_PAREN | R_BRACK => return "",
-        R_CURLY => {
-            if let USE_TREE = left.kind() {
-                return "";
-            }
-        }
-        DOT => return "",
-        _ => (),
-    }
-    " "
-}
-
 #[cfg(test)]
 mod tests {
-    use super::*;
-    use crate::test_utils::{
-        add_cursor, assert_eq_text, check_action, extract_offset, extract_range,
-};
-
-    fn check_join_lines(before: &str, after: &str) {
-        check_action(before, after, |file, offset| {
-            let range = TextRange::offset_len(offset, 0.into());
-            let res = join_lines(file, range);
-            Some(res)
-        })
-    }
+    use crate::test_utils::{add_cursor, assert_eq_text, extract_offset};
 
-    #[test]
-    fn test_join_lines_comma() {
-        check_join_lines(
-            r"
-fn foo() {
-    <|>foo(1,
-    )
-}
-",
-            r"
-fn foo() {
-    <|>foo(1)
-}
-",
-        );
-    }
-
-    #[test]
-    fn test_join_lines_lambda_block() {
-        check_join_lines(
-            r"
-pub fn reparse(&self, edit: &AtomTextEdit) -> File {
-    <|>self.incremental_reparse(edit).unwrap_or_else(|| {
-        self.full_reparse(edit)
-    })
-}
-",
-            r"
-pub fn reparse(&self, edit: &AtomTextEdit) -> File {
-    <|>self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit))
-}
-",
-        );
-    }
-
-    #[test]
-    fn test_join_lines_block() {
-        check_join_lines(
-            r"
-fn foo() {
-    foo(<|>{
-        92
-    })
-}",
-            r"
-fn foo() {
-    foo(<|>92)
-}",
-        );
-    }
-
-    #[test]
-    fn test_join_lines_use_items_left() {
-        // No space after the '{'
-        check_join_lines(
-            r"
-<|>use ra_syntax::{
-    TextUnit, TextRange,
-};",
-            r"
-<|>use ra_syntax::{TextUnit, TextRange,
-};",
-        );
-    }
-
-    #[test]
-    fn test_join_lines_use_items_right() {
-        // No space after the '}'
-        check_join_lines(
-            r"
-use ra_syntax::{
-<|>    TextUnit, TextRange
-};",
-            r"
-use ra_syntax::{
-<|>    TextUnit, TextRange};",
-        );
-    }
-
-    #[test]
-    fn test_join_lines_use_items_right_comma() {
-        // No space after the '}'
-        check_join_lines(
-            r"
-use ra_syntax::{
-<|>    TextUnit, TextRange,
-};",
-            r"
-use ra_syntax::{
-<|>    TextUnit, TextRange};",
-        );
-    }
-
-    #[test]
-    fn test_join_lines_use_tree() {
-        check_join_lines(
-            r"
-use ra_syntax::{
-    algo::<|>{
-        find_leaf_at_offset,
-    },
-    ast,
-};",
-            r"
-use ra_syntax::{
-    algo::<|>find_leaf_at_offset,
-    ast,
-};",
-        );
-    }
-
-    #[test]
-    fn test_join_lines_normal_comments() {
-        check_join_lines(
-            r"
-fn foo() {
-    // Hello<|>
-    // world!
-}
-",
-            r"
-fn foo() {
-    // Hello<|> world!
-}
-",
-        );
-    }
-
-    #[test]
-    fn test_join_lines_doc_comments() {
-        check_join_lines(
-            r"
-fn foo() {
-    /// Hello<|>
-    /// world!
-}
-",
-            r"
-fn foo() {
-    /// Hello<|> world!
-}
-",
-        );
-    }
-
-    #[test]
-    fn test_join_lines_mod_comments() {
-        check_join_lines(
-            r"
-fn foo() {
-    //! Hello<|>
-    //! world!
-}
-",
-            r"
-fn foo() {
-    //! Hello<|> world!
-}
-",
-        );
-    }
-
-    #[test]
-    fn test_join_lines_multiline_comments_1() {
-        check_join_lines(
-            r"
-fn foo() {
-    // Hello<|>
-    /* world! */
-}
-",
-            r"
-fn foo() {
-    // Hello<|> world! */
-}
-",
-        );
-    }
-
-    #[test]
-    fn test_join_lines_multiline_comments_2() {
-        check_join_lines(
-            r"
-fn foo() {
-    // The<|>
-    /* quick
-    brown
-    fox! */
-}
-",
-            r"
-fn foo() {
-    // The<|> quick
-    brown
-    fox! */
-}
-",
-        );
-    }
-
-    fn check_join_lines_sel(before: &str, after: &str) {
-        let (sel, before) = extract_range(before);
-        let file = SourceFile::parse(&before);
-        let result = join_lines(&file, sel);
-        let actual = result.edit.apply(&before);
-        assert_eq_text!(after, &actual);
-    }
-
-    #[test]
-    fn test_join_lines_selection_fn_args() {
-        check_join_lines_sel(
-            r"
-fn foo() {
-    <|>foo(1,
-        2,
-        3,
-    <|>)
-}
-    ",
-            r"
-fn foo() {
-    foo(1, 2, 3)
-}
-    ",
-        );
-    }
-
-    #[test]
-    fn test_join_lines_selection_struct() {
-        check_join_lines_sel(
-            r"
-struct Foo <|>{
-    f: u32,
-}<|>
-    ",
-            r"
-struct Foo { f: u32 }
-    ",
-        );
-    }
-
-    #[test]
-    fn test_join_lines_selection_dot_chain() {
-        check_join_lines_sel(
-            r"
-fn foo() {
-    join(<|>type_params.type_params()
-            .filter_map(|it| it.name())
-            .map(|it| it.text())<|>)
-}",
-            r"
-fn foo() {
-    join(type_params.type_params().filter_map(|it| it.name()).map(|it| it.text()))
-}",
-        );
-    }
-
-    #[test]
-    fn test_join_lines_selection_lambda_block_body() {
-        check_join_lines_sel(
-            r"
-pub fn handle_find_matching_brace() {
-    params.offsets
-        .map(|offset| <|>{
-            world.analysis().matching_brace(&file, offset).unwrap_or(offset)
-        }<|>)
-        .collect();
-}",
-            r"
-pub fn handle_find_matching_brace() {
-    params.offsets
-        .map(|offset| world.analysis().matching_brace(&file, offset).unwrap_or(offset))
-        .collect();
-}",
-        );
-    }
+    use super::*;
 
     #[test]
     fn test_on_eq_typed() {


Test patch:
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs
index ba9fcb3d1d..17de54b442 100644
--- a/crates/ra_hir/src/nameres/tests.rs
+++ b/crates/ra_hir/src/nameres/tests.rs
@@ -136,35 +136,6 @@ fn re_exports() {
     );
 }
 
-#[test]
-fn name_res_works_for_broken_modules() {
-    covers!(name_res_works_for_broken_modules);
-    let (item_map, module_id) = item_map(
-        "
-        //- /lib.rs
-        mod foo // no `;`, no body
-
-        use self::foo::Baz;
-        <|>
-
-        //- /foo/mod.rs
-        pub mod bar;
-
-        pub use self::bar::Baz;
-
-        //- /foo/bar.rs
-        pub struct Baz;
-    ",
-    );
-    check_module_item_map(
-        &item_map,
-        module_id,
-        "
-            Baz: _
-        ",
-    );
-}
-
 #[test]
 fn item_map_contains_items_from_expansions() {
     let (item_map, module_id) = item_map(