Repository: rust-analyzer/rust-analyzer
Pull request #4662 (2020-05-30 21:59:52)

minor


bors r+
🤖


Patch:
diff --git a/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs b/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
deleted file mode 100644
index 999aec421b..0000000000
--- a/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
+++ /dev/null
@@ -1,303 +0,0 @@
-use crate::{assist_context::AssistBuilder, AssistContext, AssistId, Assists};
-use ast::{NameOwner, ParamList, TypeAscriptionOwner, TypeParamList, TypeRef};
-use ra_syntax::{ast, ast::TypeParamsOwner, AstNode, SyntaxKind, TextRange, TextSize};
-use rustc_hash::FxHashSet;
-
-static ASSIST_NAME: &str = "change_lifetime_anon_to_named";
-static ASSIST_LABEL: &str = "Give anonymous lifetime a name";
-
-// Assist: change_lifetime_anon_to_named
-//
-// Change an anonymous lifetime to a named lifetime.
-//
-// ```
-// impl Cursor<'_<|>> {
-//     fn node(self) -> &SyntaxNode {
-//         match self {
-//             Cursor::Replace(node) | Cursor::Before(node) => node,
-//         }
-//     }
-// }
-// ```
-// ->
-// ```
-// impl<'a> Cursor<'a> {
-//     fn node(self) -> &SyntaxNode {
-//         match self {
-//             Cursor::Replace(node) | Cursor::Before(node) => node,
-//         }
-//     }
-// }
-// ```
-// FIXME: How can we handle renaming any one of multiple anonymous lifetimes?
-// FIXME: should also add support for the case fun(f: &Foo) -> &<|>Foo
-pub(crate) fn change_lifetime_anon_to_named(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
-    let lifetime_token = ctx
-        .find_token_at_offset(SyntaxKind::LIFETIME)
-        .filter(|lifetime| lifetime.text() == "'_")?;
-    if let Some(fn_def) = lifetime_token.ancestors().find_map(ast::FnDef::cast) {
-        generate_fn_def_assist(acc, &fn_def, lifetime_token.text_range())
-    } else if let Some(impl_def) = lifetime_token.ancestors().find_map(ast::ImplDef::cast) {
-        // only allow naming the last anonymous lifetime
-        lifetime_token.next_token().filter(|tok| tok.kind() == SyntaxKind::R_ANGLE)?;
-        generate_impl_def_assist(acc, &impl_def, lifetime_token.text_range())
-    } else {
-        None
-    }
-}
-
-/// Generate the assist for the fn def case
-fn generate_fn_def_assist(
-    acc: &mut Assists,
-    fn_def: &ast::FnDef,
-    lifetime_loc: TextRange,
-) -> Option<()> {
-    let param_list: ParamList = fn_def.param_list()?;
-    let new_lifetime_param = generate_unique_lifetime_param_name(&fn_def.type_param_list())?;
-    let end_of_fn_ident = fn_def.name()?.ident_token()?.text_range().end();
-    let self_param =
-        // use the self if it's a reference and has no explicit lifetime
-        param_list.self_param().filter(|p| p.lifetime_token().is_none() && p.amp_token().is_some());
-    // compute the location which implicitly has the same lifetime as the anonymous lifetime
-    let loc_needing_lifetime = if let Some(self_param) = self_param {
-        // if we have a self reference, use that
-        Some(self_param.self_token()?.text_range().start())
-    } else {
-        // otherwise, if there's a single reference parameter without a named liftime, use that
-        let fn_params_without_lifetime: Vec<_> = param_list
-            .params()
-            .filter_map(|param| match param.ascribed_type() {
-                Some(TypeRef::ReferenceType(ascribed_type))
-                    if ascribed_type.lifetime_token() == None =>
-                {
-                    Some(ascribed_type.amp_token()?.text_range().end())
-                }
-                _ => None,
-            })
-            .collect();
-        match fn_params_without_lifetime.len() {
-            1 => Some(fn_params_without_lifetime.into_iter().nth(0)?),
-            0 => None,
-            // multiple unnnamed is invalid. assist is not applicable
-            _ => return None,
-        }
-    };
-    acc.add(AssistId(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |builder| {
-        add_lifetime_param(fn_def, builder, end_of_fn_ident, new_lifetime_param);
-        builder.replace(lifetime_loc, format!("'{}", new_lifetime_param));
-        loc_needing_lifetime.map(|loc| builder.insert(loc, format!("'{} ", new_lifetime_param)));
-    })
-}
-
-/// Generate the assist for the impl def case
-fn generate_impl_def_assist(
-    acc: &mut Assists,
-    impl_def: &ast::ImplDef,
-    lifetime_loc: TextRange,
-) -> Option<()> {
-    let new_lifetime_param = generate_unique_lifetime_param_name(&impl_def.type_param_list())?;
-    let end_of_impl_kw = impl_def.impl_token()?.text_range().end();
-    acc.add(AssistId(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |builder| {
-        add_lifetime_param(impl_def, builder, end_of_impl_kw, new_lifetime_param);
-        builder.replace(lifetime_loc, format!("'{}", new_lifetime_param));
-    })
-}
-
-/// Given a type parameter list, generate a unique lifetime parameter name
-/// which is not in the list
-fn generate_unique_lifetime_param_name(
-    existing_type_param_list: &Option<TypeParamList>,
-) -> Option<char> {
-    match existing_type_param_list {
-        Some(type_params) => {
-            let used_lifetime_params: FxHashSet<_> = type_params
-                .lifetime_params()
-                .map(|p| p.syntax().text().to_string()[1..].to_owned())
-                .collect();
-            (b'a'..=b'z').map(char::from).find(|c| !used_lifetime_params.contains(&c.to_string()))
-        }
-        None => Some('a'),
-    }
-}
-
-/// Add the lifetime param to `builder`. If there are type parameters in `type_params_owner`, add it to the end. Otherwise
-/// add new type params brackets with the lifetime parameter at `new_type_params_loc`.
-fn add_lifetime_param<TypeParamsOwner: ast::TypeParamsOwner>(
-    type_params_owner: &TypeParamsOwner,
-    builder: &mut AssistBuilder,
-    new_type_params_loc: TextSize,
-    new_lifetime_param: char,
-) {
-    match type_params_owner.type_param_list() {
-        // add the new lifetime parameter to an existing type param list
-        Some(type_params) => {
-            builder.insert(
-                (u32::from(type_params.syntax().text_range().end()) - 1).into(),
-                format!(", '{}", new_lifetime_param),
-            );
-        }
-        // create a new type param list containing only the new lifetime parameter
-        None => {
-            builder.insert(new_type_params_loc, format!("<'{}>", new_lifetime_param));
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::tests::{check_assist, check_assist_not_applicable};
-
-    #[test]
-    fn test_example_case() {
-        check_assist(
-            change_lifetime_anon_to_named,
-            r#"impl Cursor<'_<|>> {
-                fn node(self) -> &SyntaxNode {
-                    match self {
-                        Cursor::Replace(node) | Cursor::Before(node) => node,
-                    }
-                }
-            }"#,
-            r#"impl<'a> Cursor<'a> {
-                fn node(self) -> &SyntaxNode {
-                    match self {
-                        Cursor::Replace(node) | Cursor::Before(node) => node,
-                    }
-                }
-            }"#,
-        );
-    }
-
-    #[test]
-    fn test_example_case_simplified() {
-        check_assist(
-            change_lifetime_anon_to_named,
-            r#"impl Cursor<'_<|>> {"#,
-            r#"impl<'a> Cursor<'a> {"#,
-        );
-    }
-
-    #[test]
-    fn test_example_case_cursor_after_tick() {
-        check_assist(
-            change_lifetime_anon_to_named,
-            r#"impl Cursor<'<|>_> {"#,
-            r#"impl<'a> Cursor<'a> {"#,
-        );
-    }
-
-    #[test]
-    fn test_example_case_cursor_before_tick() {
-        check_assist(
-            change_lifetime_anon_to_named,
-            r#"impl Cursor<<|>'_> {"#,
-            r#"impl<'a> Cursor<'a> {"#,
-        );
-    }
-
-    #[test]
-    fn test_not_applicable_cursor_position() {
-        check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<'_><|> {"#);
-        check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<|><'_> {"#);
-    }
-
-    #[test]
-    fn test_not_applicable_lifetime_already_name() {
-        check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<'a<|>> {"#);
-        check_assist_not_applicable(
-            change_lifetime_anon_to_named,
-            r#"fn my_fun<'a>() -> X<'a<|>>"#,
-        );
-    }
-
-    #[test]
-    fn test_with_type_parameter() {
-        check_assist(
-            change_lifetime_anon_to_named,
-            r#"impl<T> Cursor<T, '_<|>>"#,
-            r#"impl<T, 'a> Cursor<T, 'a>"#,
-        );
-    }
-
-    #[test]
-    fn test_with_existing_lifetime_name_conflict() {
-        check_assist(
-            change_lifetime_anon_to_named,
-            r#"impl<'a, 'b> Cursor<'a, 'b, '_<|>>"#,
-            r#"impl<'a, 'b, 'c> Cursor<'a, 'b, 'c>"#,
-        );
-    }
-
-    #[test]
-    fn test_function_return_value_anon_lifetime_param() {
-        check_assist(
-            change_lifetime_anon_to_named,
-            r#"fn my_fun() -> X<'_<|>>"#,
-            r#"fn my_fun<'a>() -> X<'a>"#,
-        );
-    }
-
-    #[test]
-    fn test_function_return_value_anon_reference_lifetime() {
-        check_assist(
-            change_lifetime_anon_to_named,
-            r#"fn my_fun() -> &'_<|> X"#,
-            r#"fn my_fun<'a>() -> &'a X"#,
-        );
-    }
-
-    #[test]
-    fn test_function_param_anon_lifetime() {
-        check_assist(
-            change_lifetime_anon_to_named,
-            r#"fn my_fun(x: X<'_<|>>)"#,
-            r#"fn my_fun<'a>(x: X<'a>)"#,
-        );
-    }
-
-    #[test]
-    fn test_function_add_lifetime_to_params() {
-        check_assist(
-            change_lifetime_anon_to_named,
-            r#"fn my_fun(f: &Foo) -> X<'_<|>>"#,
-            r#"fn my_fun<'a>(f: &'a Foo) -> X<'a>"#,
-        );
-    }
-
-    #[test]
-    fn test_function_add_lifetime_to_params_in_presence_of_other_lifetime() {
-        check_assist(
-            change_lifetime_anon_to_named,
-            r#"fn my_fun<'other>(f: &Foo, b: &'other Bar) -> X<'_<|>>"#,
-            r#"fn my_fun<'other, 'a>(f: &'a Foo, b: &'other Bar) -> X<'a>"#,
-        );
-    }
-
-    #[test]
-    fn test_function_not_applicable_without_self_and_multiple_unnamed_param_lifetimes() {
-        // this is not permitted under lifetime elision rules
-        check_assist_not_applicable(
-            change_lifetime_anon_to_named,
-            r#"fn my_fun(f: &Foo, b: &Bar) -> X<'_<|>>"#,
-        );
-    }
-
-    #[test]
-    fn test_function_add_lifetime_to_self_ref_param() {
-        check_assist(
-            change_lifetime_anon_to_named,
-            r#"fn my_fun<'other>(&self, f: &Foo, b: &'other Bar) -> X<'_<|>>"#,
-            r#"fn my_fun<'other, 'a>(&'a self, f: &Foo, b: &'other Bar) -> X<'a>"#,
-        );
-    }
-
-    #[test]
-    fn test_function_add_lifetime_to_param_with_non_ref_self() {
-        check_assist(
-            change_lifetime_anon_to_named,
-            r#"fn my_fun<'other>(self, f: &Foo, b: &'other Bar) -> X<'_<|>>"#,
-            r#"fn my_fun<'other, 'a>(self, f: &'a Foo, b: &'other Bar) -> X<'a>"#,
-        );
-    }
-}
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 3f8f7ffbfc..464bc03dde 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -112,7 +112,6 @@ mod handlers {
     mod add_turbo_fish;
     mod apply_demorgan;
     mod auto_import;
-    mod change_lifetime_anon_to_named;
     mod change_return_type_to_result;
     mod change_visibility;
     mod early_return;
@@ -152,7 +151,6 @@ mod handlers {
             add_turbo_fish::add_turbo_fish,
             apply_demorgan::apply_demorgan,
             auto_import::auto_import,
-            change_lifetime_anon_to_named::change_lifetime_anon_to_named,
             change_return_type_to_result::change_return_type_to_result,
             change_visibility::change_visibility,
             early_return::convert_to_guarded_return,
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index 78084cb573..54bab3476c 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -140,13 +140,13 @@ impl<'a> InferenceContext<'a> {
 
                 let mut sig_tys = Vec::new();
 
-                // collect explicitly written argument types
-                for arg_type in arg_types.iter() {
-                    let arg_ty = if let Some(type_ref) = arg_type {
+                for (arg_pat, arg_type) in args.iter().zip(arg_types.iter()) {
+                    let expected = if let Some(type_ref) = arg_type {
                         self.make_ty(type_ref)
                     } else {
-                        self.table.new_type_var()
+                        Ty::Unknown
                     };
+                    let arg_ty = self.infer_pat(*arg_pat, &expected, BindingMode::default());
                     sig_tys.push(arg_ty);
                 }
 
@@ -158,7 +158,7 @@ impl<'a> InferenceContext<'a> {
                 sig_tys.push(ret_ty.clone());
                 let sig_ty = Ty::apply(
                     TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1 },
-                    Substs(sig_tys.clone().into()),
+                    Substs(sig_tys.into()),
                 );
                 let closure_ty =
                     Ty::apply_one(TypeCtor::Closure { def: self.owner, expr: tgt_expr }, sig_ty);
@@ -168,12 +168,6 @@ impl<'a> InferenceContext<'a> {
                 // infer the body.
                 self.coerce(&closure_ty, &expected.ty);
 
-                // Now go through the argument patterns
-                for (arg_pat, arg_ty) in args.iter().zip(sig_tys) {
-                    let resolved = self.resolve_ty_as_possible(arg_ty);
-                    self.infer_pat(*arg_pat, &resolved, BindingMode::default());
-                }
-
                 let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
                 let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
 
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index cd6464b405..8a995d779b 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -391,7 +391,6 @@ fn highlight_element(
         INT_NUMBER | FLOAT_NUMBER => HighlightTag::NumericLiteral.into(),
         BYTE => HighlightTag::ByteLiteral.into(),
         CHAR => HighlightTag::CharLiteral.into(),
-        QUESTION => Highlight::new(HighlightTag::Operator) | HighlightModifier::ControlFlow,
         LIFETIME => {
             let h = Highlight::new(HighlightTag::Lifetime);
             match element.parent().map(|it| it.kind()) {
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index 1514531de2..46c718c91e 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -42,7 +42,6 @@ pub enum HighlightTag {
     Local,
     UnresolvedReference,
     FormatSpecifier,
-    Operator,
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -90,7 +89,6 @@ impl HighlightTag {
             HighlightTag::Local => "variable",
             HighlightTag::UnresolvedReference => "unresolved_reference",
             HighlightTag::FormatSpecifier => "format_specifier",
-            HighlightTag::Operator => "operator",
         }
     }
 }
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 2fbbb4e632..8e8e7033db 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -317,7 +317,6 @@ fn semantic_token_type_and_modifiers(
         HighlightTag::Keyword => lsp_types::SemanticTokenType::KEYWORD,
         HighlightTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE,
         HighlightTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER,
-        HighlightTag::Operator => lsp_types::SemanticTokenType::OPERATOR,
     };
 
     for modifier in highlight.modifiers.iter() {
diff --git a/docs/user/assists.md b/docs/user/assists.md
index a1058ecde9..4ad7ea59d2 100644
--- a/docs/user/assists.md
+++ b/docs/user/assists.md
@@ -259,30 +259,6 @@ fn main() {
 }
 ```
 
-## `change_lifetime_anon_to_named`
-
-Change an anonymous lifetime to a named lifetime.
-
-```rust
-// BEFORE
-impl Cursor<'_┃> {
-    fn node(self) -> &SyntaxNode {
-        match self {
-            Cursor::Replace(node) | Cursor::Before(node) => node,
-        }
-    }
-}
-
-// AFTER
-impl<'a> Cursor<'a> {
-    fn node(self) -> &SyntaxNode {
-        match self {
-            Cursor::Replace(node) | Cursor::Before(node) => node,
-        }
-    }
-}
-```
-
 ## `change_return_type_to_result`
 
 Change the function's return type to Result.
diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs
index 4bd6b5f0ca..20dcde8206 100644
--- a/xtask/src/codegen/gen_assists_docs.rs
+++ b/xtask/src/codegen/gen_assists_docs.rs
@@ -8,7 +8,7 @@ use crate::{
 };
 
 pub fn generate_assists_docs(mode: Mode) -> Result<()> {
-    let assists = collect_assists()?;
+    let assists = Assist::collect()?;
     generate_tests(&assists, mode)?;
     generate_docs(&assists, mode)?;
     Ok(())
@@ -22,81 +22,61 @@ struct Assist {
     after: String,
 }
 
-fn hide_hash_comments(text: &str) -> String {
-    text.split('\n') // want final newline
-        .filter(|&it| !(it.starts_with("# ") || it == "#"))
-        .map(|it| format!("{}\n", it))
-        .collect()
-}
-
-fn reveal_hash_comments(text: &str) -> String {
-    text.split('\n') // want final newline
-        .map(|it| {
-            if it.starts_with("# ") {
-                &it[2..]
-            } else if it == "#" {
-                ""
-            } else {
-                it
-            }
-        })
-        .map(|it| format!("{}\n", it))
-        .collect()
-}
-
-fn collect_assists() -> Result<Vec<Assist>> {
-    let mut res = Vec::new();
-    for path in rust_files(&project_root().join(codegen::ASSISTS_DIR)) {
-        collect_file(&mut res, path.as_path())?;
-    }
-    res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id));
-    return Ok(res);
-
-    fn collect_file(acc: &mut Vec<Assist>, path: &Path) -> Result<()> {
-        let text = fs::read_to_string(path)?;
-        let comment_blocks = extract_comment_blocks_with_empty_lines(&text);
-
-        for block in comment_blocks {
-            // FIXME: doesn't support blank lines yet, need to tweak
-            // `extract_comment_blocks` for that.
-            let mut lines = block.iter();
-            let first_line = lines.next().unwrap();
-            if !first_line.starts_with("Assist: ") {
-                continue;
-            }
-            let id = first_line["Assist: ".len()..].to_string();
-            assert!(
-                id.chars().all(|it| it.is_ascii_lowercase() || it == '_'),
-                "invalid assist id: {:?}",
-                id
-            );
-
-            let doc = take_until(lines.by_ref(), "```").trim().to_string();
-            assert!(
-                doc.chars().next().unwrap().is_ascii_uppercase() && doc.ends_with('.'),
-                "\n\n{}: assist docs should be proper sentences, with capitalization and a full stop at the end.\n\n{}\n\n",
-                id, doc,
-            );
-
-            let before = take_until(lines.by_ref(), "```");
-
-            assert_eq!(lines.next().unwrap().as_str(), "->");
-            assert_eq!(lines.next().unwrap().as_str(), "```");
-            let after = take_until(lines.by_ref(), "```");
-            acc.push(Assist { id, doc, before, after })
+impl Assist {
+    fn collect() -> Result<Vec<Assist>> {
+        let mut res = Vec::new();
+        for path in rust_files(&project_root().join(codegen::ASSISTS_DIR)) {
+            collect_file(&mut res, path.as_path())?;
         }
+        res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id));
+        return Ok(res);
+
+        fn collect_file(acc: &mut Vec<Assist>, path: &Path) -> Result<()> {
+            let text = fs::read_to_string(path)?;
+            let comment_blocks = extract_comment_blocks_with_empty_lines(&text);
+
+            for block in comment_blocks {
+                // FIXME: doesn't support blank lines yet, need to tweak
+                // `extract_comment_blocks` for that.
+                let mut lines = block.iter();
+                let first_line = lines.next().unwrap();
+                if !first_line.starts_with("Assist: ") {
+                    continue;
+                }
+                let id = first_line["Assist: ".len()..].to_string();
+                assert!(
+                    id.chars().all(|it| it.is_ascii_lowercase() || it == '_'),
+                    "invalid assist id: {:?}",
+                    id
+                );
+
+                let doc = take_until(lines.by_ref(), "```").trim().to_string();
+                assert!(
+                    doc.chars().next().unwrap().is_ascii_uppercase() && doc.ends_with('.'),
+                    "\n\n{}: assist docs should be proper sentences, with capitalization and a full stop at the end.\n\n{}\n\n",
+                    id, doc,
+                );
+
+                let before = take_until(lines.by_ref(), "```");
+
+                assert_eq!(lines.next().unwrap().as_str(), "->");
+                assert_eq!(lines.next().unwrap().as_str(), "```");
+                let after = take_until(lines.by_ref(), "```");
+                acc.push(Assist { id, doc, before, after })
+            }
 
-        fn take_until<'a>(lines: impl Iterator<Item = &'a String>, marker: &str) -> String {
-            let mut buf = Vec::new();
-            for line in lines {
-                if line == marker {
-                    break;
+            fn take_until<'a>(lines: impl Iterator<Item = &'a String>, marker: &str) -> String {
+                let mut buf = Vec::new();
+                for line in lines {
+                    if line == marker {
+                        break;
+                    }
+                    buf.push(line.clone());
                 }
-                buf.push(line.clone());
+                buf.join("\n")
             }
-            buf.join("\n")
+            Ok(())
         }
-        Ok(())
     }
 }
 
@@ -157,3 +137,25 @@ fn generate_docs(assists: &[Assist], mode: Mode) -> Result<()> {
 
     codegen::update(&project_root().join(codegen::ASSISTS_DOCS), &buf, mode)
 }
+
+fn hide_hash_comments(text: &str) -> String {
+    text.split('\n') // want final newline
+        .filter(|&it| !(it.starts_with("# ") || it == "#"))
+        .map(|it| format!("{}\n", it))
+        .collect()
+}
+
+fn reveal_hash_comments(text: &str) -> String {
+    text.split('\n') // want final newline
+        .map(|it| {
+            if it.starts_with("# ") {
+                &it[2..]
+            } else if it == "#" {
+                ""
+            } else {
+                it
+            }
+        })
+        .map(|it| format!("{}\n", it))
+        .collect()
+}


Test patch:
diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs
index abffbf97cf..250e56a696 100644
--- a/crates/ra_assists/src/tests/generated.rs
+++ b/crates/ra_assists/src/tests/generated.rs
@@ -268,31 +268,6 @@ pub mod std { pub mod collections { pub struct HashMap { } } }
     )
 }
 
-#[test]
-fn doctest_change_lifetime_anon_to_named() {
-    check_doc_test(
-        "change_lifetime_anon_to_named",
-        r#####"
-impl Cursor<'_<|>> {
-    fn node(self) -> &SyntaxNode {
-        match self {
-            Cursor::Replace(node) | Cursor::Before(node) => node,
-        }
-    }
-}
-"#####,
-        r#####"
-impl<'a> Cursor<'a> {
-    fn node(self) -> &SyntaxNode {
-        match self {
-            Cursor::Replace(node) | Cursor::Before(node) => node,
-        }
-    }
-}
-"#####,
-    )
-}
-
 #[test]
 fn doctest_change_return_type_to_result() {
     check_doc_test(
diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs
index fe62587c0d..0c5f972a2c 100644
--- a/crates/ra_hir_ty/src/tests/patterns.rs
+++ b/crates/ra_hir_ty/src/tests/patterns.rs
@@ -520,53 +520,3 @@ fn main() {
         105..107 '()': ()
     ")
 }
-
-#[test]
-fn match_ergonomics_in_closure_params() {
-    assert_snapshot!(
-        infer(r#"
-#[lang = "fn_once"]
-trait FnOnce<Args> {
-    type Output;
-}
-
-fn foo<T, U, F: FnOnce(T) -> U>(t: T, f: F) -> U { loop {} }
-
-fn test() {
-    foo(&(1, "a"), |&(x, y)| x); // normal, no match ergonomics
-    foo(&(1, "a"), |(x, y)| x);
-}
-"#),
-        @r###"
-    94..95 't': T
-    100..101 'f': F
-    111..122 '{ loop {} }': U
-    113..120 'loop {}': !
-    118..120 '{}': ()
-    134..233 '{     ... x); }': ()
-    140..143 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32
-    140..167 'foo(&(...y)| x)': i32
-    144..153 '&(1, "a")': &(i32, &str)
-    145..153 '(1, "a")': (i32, &str)
-    146..147 '1': i32
-    149..152 '"a"': &str
-    155..166 '|&(x, y)| x': |&(i32, &str)| -> i32
-    156..163 '&(x, y)': &(i32, &str)
-    157..163 '(x, y)': (i32, &str)
-    158..159 'x': i32
-    161..162 'y': &str
-    165..166 'x': i32
-    204..207 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32
-    204..230 'foo(&(...y)| x)': &i32
-    208..217 '&(1, "a")': &(i32, &str)
-    209..217 '(1, "a")': (i32, &str)
-    210..211 '1': i32
-    213..216 '"a"': &str
-    219..229 '|(x, y)| x': |&(i32, &str)| -> &i32
-    220..226 '(x, y)': (i32, &str)
-    221..222 'x': &i32
-    224..225 'y': &&str
-    228..229 'x': &i32
-    "###
-    );
-}