Repository: rust-analyzer/rust-analyzer
Pull request #11951 (2022-04-10 10:43:14)

minor: Simplify
bors r+

bors[bot]: Build failed:
  * [Rust (ubuntu-latest)](https://github.com/rust-analyzer/rust-analyzer/runs/5960679536?check_suite_focus=true)

Patch:
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs
index e6d94f1d0d..9fcaa4b06d 100644
--- a/crates/base_db/src/input.rs
+++ b/crates/base_db/src/input.rs
@@ -135,6 +135,33 @@ pub enum LangCrateOrigin {
     Other,
 }
 
+impl From<&str> for LangCrateOrigin {
+    fn from(s: &str) -> Self {
+        match s {
+            "alloc" => LangCrateOrigin::Alloc,
+            "core" => LangCrateOrigin::Core,
+            "proc-macro" => LangCrateOrigin::ProcMacro,
+            "std" => LangCrateOrigin::Std,
+            "test" => LangCrateOrigin::Test,
+            _ => LangCrateOrigin::Other,
+        }
+    }
+}
+
+impl fmt::Display for LangCrateOrigin {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let text = match self {
+            LangCrateOrigin::Alloc => "alloc",
+            LangCrateOrigin::Core => "core",
+            LangCrateOrigin::ProcMacro => "proc_macro",
+            LangCrateOrigin::Std => "std",
+            LangCrateOrigin::Test => "test",
+            LangCrateOrigin::Other => "other",
+        };
+        f.write_str(text)
+    }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct CrateDisplayName {
     // The name we use to display various paths (with `_`).
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index a98cd214c3..a1e170634b 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -12,6 +12,7 @@ use url::Url;
 
 use hir::{db::HirDatabase, Adt, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasAttrs};
 use ide_db::{
+    base_db::{CrateOrigin, LangCrateOrigin, SourceDatabase},
     defs::{Definition, NameClass, NameRefClass},
     helpers::pick_best_token,
     RootDatabase,
@@ -293,7 +294,7 @@ fn get_doc_link(db: &RootDatabase, def: Definition) -> Option<String> {
     let (target, file, frag) = filename_and_frag_for_def(db, def)?;
 
     let krate = crate_of_def(db, target)?;
-    let mut url = get_doc_base_url(db, &krate)?;
+    let mut url = get_doc_base_url(db, krate)?;
 
     if let Some(path) = mod_path_of_def(db, target) {
         url = url.join(&path).ok()?;
@@ -315,7 +316,7 @@ fn rewrite_intra_doc_link(
 
     let resolved = resolve_doc_path_for_def(db, def, link, ns)?;
     let krate = crate_of_def(db, resolved)?;
-    let mut url = get_doc_base_url(db, &krate)?;
+    let mut url = get_doc_base_url(db, krate)?;
 
     let (_, file, frag) = filename_and_frag_for_def(db, resolved)?;
     if let Some(path) = mod_path_of_def(db, resolved) {
@@ -335,7 +336,7 @@ fn rewrite_url_link(db: &RootDatabase, def: Definition, target: &str) -> Option<
     }
 
     let krate = crate_of_def(db, def)?;
-    let mut url = get_doc_base_url(db, &krate)?;
+    let mut url = get_doc_base_url(db, krate)?;
     let (def, file, frag) = filename_and_frag_for_def(db, def)?;
 
     if let Some(path) = mod_path_of_def(db, def) {
@@ -406,13 +407,20 @@ fn map_links<'e>(
 /// https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next
 /// ^^^^^^^^^^^^^^^^^^^^^^^^^^
 /// ```
-fn get_doc_base_url(db: &RootDatabase, krate: &Crate) -> Option<Url> {
+fn get_doc_base_url(db: &RootDatabase, krate: Crate) -> Option<Url> {
     let display_name = krate.display_name(db)?;
-    let base = match &**display_name.crate_name() {
+
+    let base = match db.crate_graph()[krate.into()].origin {
         // std and co do not specify `html_root_url` any longer so we gotta handwrite this ourself.
         // FIXME: Use the toolchains channel instead of nightly
-        name @ ("core" | "std" | "alloc" | "proc_macro" | "test") => {
-            format!("https://doc.rust-lang.org/nightly/{}", name)
+        CrateOrigin::Lang(
+            origin @ (LangCrateOrigin::Alloc
+            | LangCrateOrigin::Core
+            | LangCrateOrigin::ProcMacro
+            | LangCrateOrigin::Std
+            | LangCrateOrigin::Test),
+        ) => {
+            format!("https://doc.rust-lang.org/nightly/{origin}")
         }
         _ => {
             krate.get_html_root_url(db).or_else(|| {
diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs
index 0f75abe55c..6bab9fa1eb 100644
--- a/crates/ide/src/moniker.rs
+++ b/crates/ide/src/moniker.rs
@@ -153,17 +153,12 @@ pub(crate) fn def_to_moniker(
                 CrateOrigin::CratesIo { repo } => (repo?, krate.version(db)?),
                 CrateOrigin::Lang(lang) => (
                     "https://github.com/rust-lang/rust/".to_string(),
-                    format!(
-                        "https://github.com/rust-lang/rust/library/{}",
-                        match lang {
-                            LangCrateOrigin::Alloc => "alloc",
-                            LangCrateOrigin::Core => "core",
-                            LangCrateOrigin::ProcMacro => "proc_macro",
-                            LangCrateOrigin::Std => "std",
-                            LangCrateOrigin::Test => "test",
-                            LangCrateOrigin::Other => "",
+                    match lang {
+                        LangCrateOrigin::Other => {
+                            "https://github.com/rust-lang/rust/library/".into()
                         }
-                    ),
+                        lang => format!("https://github.com/rust-lang/rust/library/{lang}",),
+                    },
                 ),
             };
             PackageInformation { name, repo, version }
diff --git a/crates/ide_assists/src/handlers/extract_module.rs b/crates/ide_assists/src/handlers/extract_module.rs
index b5ed5699c9..57ce34ceeb 100644
--- a/crates/ide_assists/src/handlers/extract_module.rs
+++ b/crates/ide_assists/src/handlers/extract_module.rs
@@ -1,7 +1,4 @@
-use std::{
-    collections::{HashMap, HashSet},
-    iter,
-};
+use std::collections::{HashMap, HashSet};
 
 use hir::{HasSource, ModuleSource};
 use ide_db::{
@@ -84,36 +81,39 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext) -> Option<(
     }
 
     let mut module = extract_target(&node, ctx.selection_trimmed())?;
-    if module.body_items.is_empty() {
+    if module.body_items.len() == 0 {
         return None;
     }
 
     let old_item_indent = module.body_items[0].indent_level();
 
+    //This takes place in three steps:
+    //
+    //- Firstly, we will update the references(usages) e.g. converting a
+    //  function call bar() to modname::bar(), and similarly for other items
+    //
+    //- Secondly, changing the visibility of each item inside the newly selected module
+    //  i.e. making a fn a() {} to pub(crate) fn a() {}
+    //
+    //- Thirdly, resolving all the imports this includes removing paths from imports
+    //  outside the module, shifting/cloning them inside new module, or shifting the imports, or making
+    //  new import statemnts
+
+    //We are getting item usages and record_fields together, record_fields
+    //for change_visibility and usages for first point mentioned above in the process
+    let (usages_to_be_processed, record_fields) = module.get_usages_and_record_fields(ctx);
+
+    let import_paths_to_be_removed = module.resolve_imports(curr_parent_module, ctx);
+    module.body_items = module.change_visibility(record_fields)?;
+    if module.body_items.len() == 0 {
+        return None;
+    }
+
     acc.add(
         AssistId("extract_module", AssistKind::RefactorExtract),
         "Extract Module",
         module.text_range,
         |builder| {
-            //This takes place in three steps:
-            //
-            //- Firstly, we will update the references(usages) e.g. converting a
-            //  function call bar() to modname::bar(), and similarly for other items
-            //
-            //- Secondly, changing the visibility of each item inside the newly selected module
-            //  i.e. making a fn a() {} to pub(crate) fn a() {}
-            //
-            //- Thirdly, resolving all the imports this includes removing paths from imports
-            //  outside the module, shifting/cloning them inside new module, or shifting the imports, or making
-            //  new import statemnts
-
-            //We are getting item usages and record_fields together, record_fields
-            //for change_visibility and usages for first point mentioned above in the process
-            let (usages_to_be_processed, record_fields) = module.get_usages_and_record_fields(ctx);
-
-            let import_paths_to_be_removed = module.resolve_imports(curr_parent_module, ctx);
-            module.change_visibility(record_fields);
-
             let mut body_items: Vec<String> = Vec::new();
             let mut items_to_be_processed: Vec<ast::Item> = module.body_items.clone();
             let mut new_item_indent = old_item_indent + 1;
@@ -149,11 +149,17 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext) -> Option<(
                     body = impl_body_def;
 
                     // Add the import for enum/struct corresponding to given impl block
-                    module.make_use_stmt_of_node_with_super(self_ty.syntax());
-                    for item in module.use_items {
-                        let mut indented_item = String::new();
-                        format_to!(indented_item, "{}{}", old_item_indent + 1, item.to_string());
-                        body = format!("{}\n\n{}", indented_item, body);
+                    if let Some(_) = module.make_use_stmt_of_node_with_super(self_ty.syntax()) {
+                        for item in module.use_items {
+                            let mut indented_item = String::new();
+                            format_to!(
+                                indented_item,
+                                "{}{}",
+                                old_item_indent + 1,
+                                item.to_string()
+                            );
+                            body = format!("{}\n\n{}", indented_item, body);
+                        }
                     }
                 }
             }
@@ -210,25 +216,37 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext) -> Option<(
 #[derive(Debug)]
 struct Module {
     text_range: TextRange,
-    name: &'static str,
-    /// All items except use items.
-    body_items: Vec<ast::Item>,
-    /// Use items are kept separately as they help when the selection is inside an impl block,
-    /// we can directly take these items and keep them outside generated impl block inside
-    /// generated module.
-    use_items: Vec<ast::Item>,
+    name: String,
+    body_items: Vec<ast::Item>, // All items except use items
+    use_items: Vec<ast::Item>, // Use items are kept separately as they help when the selection is inside an impl block, we can directly take these items and keep them outside generated impl block inside generated module
 }
 
 fn extract_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Module> {
-    let selected_nodes = node
+    let mut use_items = vec![];
+
+    let mut body_items: Vec<ast::Item> = node
         .children()
-        .filter(|node| selection_range.contains_range(node.text_range()))
-        .chain(iter::once(node.clone()));
-    let (use_items, body_items) = selected_nodes
-        .filter_map(ast::Item::cast)
-        .partition(|item| matches!(item, ast::Item::Use(..)));
+        .filter_map(|child| {
+            if selection_range.contains_range(child.text_range()) {
+                let child_kind = child.kind();
+                if let Some(item) = ast::Item::cast(child) {
+                    if ast::Use::can_cast(child_kind) {
+                        use_items.push(item);
+                    } else {
+                        return Some(item);
+                    }
+                }
+                return None;
+            }
+            None
+        })
+        .collect();
 
-    Some(Module { text_range: selection_range, name: "modname", body_items, use_items })
+    if let Some(node_item) = ast::Item::cast(node.clone()) {
+        body_items.push(node_item);
+    }
+
+    Some(Module { text_range: selection_range, name: "modname".to_string(), body_items, use_items })
 }
 
 impl Module {
@@ -242,7 +260,7 @@ impl Module {
         //Here impl is not included as each item inside impl will be tied to the parent of
         //implementing block(a struct, enum, etc), if the parent is in selected module, it will
         //get updated by ADT section given below or if it is not, then we dont need to do any operation
-        for item in &self.body_items {
+        self.body_items.iter().cloned().for_each(|item| {
             match_ast! {
                 match (item.syntax()) {
                     ast::Adt(it) => {
@@ -311,7 +329,7 @@ impl Module {
                     _ => (),
                 }
             }
-        }
+        });
 
         (refs, adt_fields)
     }
@@ -320,15 +338,34 @@ impl Module {
         &self,
         ctx: &AssistContext,
         node_def: Definition,
-        refs_in_files: &mut HashMap<FileId, Vec<(TextRange, String)>>,
+        refs: &mut HashMap<FileId, Vec<(TextRange, String)>>,
     ) {
         for (file_id, references) in node_def.usages(&ctx.sema).all() {
-            let source_file = ctx.sema.parse(file_id);
-            let usages_in_file = references
-                .into_iter()
-                .filter_map(|usage| self.get_usage_to_be_processed(&source_file, usage));
-            refs_in_files.entry(file_id).or_default().extend(usages_in_file);
+            if let Some(file_refs) = refs.get_mut(&file_id) {
+                let mut usages = self.expand_ref_to_usages(references, ctx, file_id);
+                file_refs.append(&mut usages);
+            } else {
+                refs.insert(file_id, self.expand_ref_to_usages(references, ctx, file_id));
+            }
+        }
+    }
+
+    fn expand_ref_to_usages(
+        &self,
+        refs: Vec<FileReference>,
+        ctx: &AssistContext,
+        file_id: FileId,
+    ) -> Vec<(TextRange, String)> {
+        let source_file = ctx.sema.parse(file_id);
+
+        let mut usages_to_be_processed_for_file = Vec::new();
+        for usage in refs {
+            if let Some(x) = self.get_usage_to_be_processed(&source_file, usage) {
+                usages_to_be_processed_for_file.push(x);
+            }
         }
+
+        usages_to_be_processed_for_file
     }
 
     fn get_usage_to_be_processed(
@@ -336,7 +373,9 @@ impl Module {
         source_file: &SourceFile,
         FileReference { range, name, .. }: FileReference,
     ) -> Option<(TextRange, String)> {
-        let path: ast::Path = find_node_at_range(source_file.syntax(), range)?;
+        let path: Option<ast::Path> = find_node_at_range(source_file.syntax(), range);
+
+        let path = path?;
 
         for desc in path.syntax().descendants() {
             if desc.to_string() == name.syntax().to_string()
@@ -354,34 +393,42 @@ impl Module {
         None
     }
 
-    fn change_visibility(&mut self, record_fields: Vec<SyntaxNode>) {
-        let (mut replacements, record_field_parents, impls) =
-            get_replacements_for_visibilty_change(&mut self.body_items, false);
+    fn change_visibility(&self, record_fields: Vec<SyntaxNode>) -> Option<Vec<ast::Item>> {
+        let (body_items, mut replacements, record_field_parents, impls) =
+            get_replacements_for_visibilty_change(self.body_items.clone(), false);
 
-        let mut impl_items: Vec<ast::Item> = impls
-            .into_iter()
-            .flat_map(|impl_| impl_.syntax().descendants())
-            .filter_map(ast::Item::cast)
-            .collect();
+        let mut impl_items = Vec::new();
+        for impl_ in impls {
+            let mut this_impl_items = Vec::new();
+            for node in impl_.syntax().descendants() {
+                if let Some(item) = ast::Item::cast(node) {
+                    this_impl_items.push(item);
+                }
+            }
 
-        let (mut impl_item_replacements, _, _) =
-            get_replacements_for_visibilty_change(&mut impl_items, true);
+            impl_items.append(&mut this_impl_items);
+        }
+
+        let (_, mut impl_item_replacements, _, _) =
+            get_replacements_for_visibilty_change(impl_items, true);
 
         replacements.append(&mut impl_item_replacements);
 
-        for (_, field_owner) in record_field_parents {
-            for desc in field_owner.descendants().filter_map(ast::RecordField::cast) {
+        record_field_parents.into_iter().for_each(|x| {
+            x.1.descendants().filter_map(ast::RecordField::cast).for_each(|desc| {
                 let is_record_field_present =
                     record_fields.clone().into_iter().any(|x| x.to_string() == desc.to_string());
                 if is_record_field_present {
                     replacements.push((desc.visibility(), desc.syntax().clone()));
                 }
-            }
-        }
+            });
+        });
 
-        for (vis, syntax) in replacements {
+        replacements.into_iter().for_each(|(vis, syntax)| {
             add_change_vis(vis, syntax.first_child_or_token());
-        }
+        });
+
+        Some(body_items)
     }
 
     fn resolve_imports(
@@ -392,8 +439,8 @@ impl Module {
         let mut import_paths_to_be_removed: Vec<TextRange> = vec![];
         let mut node_set: HashSet<String> = HashSet::new();
 
-        for item in self.body_items.clone() {
-            for x in item.syntax().descendants() {
+        self.body_items.clone().into_iter().for_each(|item| {
+            item.syntax().descendants().for_each(|x| {
                 if let Some(name) = ast::Name::cast(x.clone()) {
                     if let Some(name_classify) = NameClass::classify(&ctx.sema, &name) {
                         //Necessary to avoid two same names going through
@@ -445,8 +492,8 @@ impl Module {
                         }
                     }
                 }
-            }
-        }
+            });
+        });
 
         import_paths_to_be_removed
     }
@@ -467,8 +514,8 @@ impl Module {
 
         let mut exists_inside_sel = false;
         let mut exists_outside_sel = false;
-        for (_, refs) in usage_res.iter() {
-            let mut non_use_nodes_itr = refs.iter().filter_map(|x| {
+        usage_res.clone().into_iter().for_each(|x| {
+            let mut non_use_nodes_itr = (&x.1).iter().filter_map(|x| {
                 if find_node_at_range::<ast::Use>(file.syntax(), x.range).is_none() {
                     let path_opt = find_node_at_range::<ast::Path>(file.syntax(), x.range);
                     return path_opt;
@@ -486,7 +533,7 @@ impl Module {
             if non_use_nodes_itr.any(|x| selection_range.contains_range(x.syntax().text_range())) {
                 exists_inside_sel = true;
             }
-        }
+        });
 
         let source_exists_outside_sel_in_same_mod = does_source_exists_outside_sel_in_same_mod(
             def,
@@ -496,14 +543,18 @@ impl Module {
             curr_file_id,
         );
 
-        let use_stmt_opt: Option<ast::Use> = usage_res.into_iter().find_map(|(file_id, refs)| {
+        let use_stmt_opt: Option<ast::Use> = usage_res.into_iter().find_map(|x| {
+            let file_id = x.0;
+            let mut use_opt: Option<ast::Use> = None;
             if file_id == curr_file_id {
-                refs.into_iter()
-                    .rev()
-                    .find_map(|fref| find_node_at_range(file.syntax(), fref.range))
-            } else {
-                None
+                (&x.1).iter().for_each(|x| {
+                    let node_opt: Option<ast::Use> = find_node_at_range(file.syntax(), x.range);
+                    if let Some(node) = node_opt {
+                        use_opt = Some(node);
+                    }
+                });
             }
+            use_opt
         });
 
         let mut use_tree_str_opt: Option<Vec<ast::Path>> = None;
@@ -565,14 +616,15 @@ impl Module {
 
             let use_ =
                 make::use_(None, make::use_tree(make::join_paths(use_tree_str), None, None, false));
-            let item = ast::Item::from(use_);
-            self.use_items.insert(0, item);
+            if let Some(item) = ast::Item::cast(use_.syntax().clone()) {
+                self.use_items.insert(0, item);
+            }
         }
 
         import_path_to_be_removed
     }
 
-    fn make_use_stmt_of_node_with_super(&mut self, node_syntax: &SyntaxNode) -> ast::Item {
+    fn make_use_stmt_of_node_with_super(&mut self, node_syntax: &SyntaxNode) -> Option<ast::Item> {
         let super_path = make::ext::ident_path("super");
         let node_path = make::ext::ident_path(&node_syntax.to_string());
         let use_ = make::use_(
@@ -580,9 +632,12 @@ impl Module {
             make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false),
         );
 
-        let item = ast::Item::from(use_);
-        self.use_items.insert(0, item.clone());
-        item
+        if let Some(item) = ast::Item::cast(use_.syntax().clone()) {
+            self.use_items.insert(0, item.clone());
+            return Some(item);
+        }
+
+        None
     }
 
     fn process_use_stmt_for_import_resolve(
@@ -768,9 +823,10 @@ fn does_source_exists_outside_sel_in_same_mod(
 }
 
 fn get_replacements_for_visibilty_change(
-    items: &mut [ast::Item],
+    items: Vec<ast::Item>,
     is_clone_for_updated: bool,
 ) -> (
+    Vec<ast::Item>,
     Vec<(Option<ast::Visibility>, SyntaxNode)>,
     Vec<(Option<ast::Visibility>, SyntaxNode)>,
     Vec<ast::Impl>,
@@ -778,11 +834,14 @@ fn get_replacements_for_visibilty_change(
     let mut replacements = Vec::new();
     let mut record_field_parents = Vec::new();
     let mut impls = Vec::new();
+    let mut body_items = Vec::new();
 
-    for item in items {
+    items.into_iter().for_each(|item| {
+        let mut item = item;
         if !is_clone_for_updated {
-            *item = item.clone_for_update();
+            item = item.clone_for_update();
         }
+        body_items.push(item.clone());
         //Use stmts are ignored
         match item {
             ast::Item::Const(it) => replacements.push((it.visibility(), it.syntax().clone())),
@@ -790,7 +849,7 @@ fn get_replacements_for_visibilty_change(
             ast::Item::ExternCrate(it) => replacements.push((it.visibility(), it.syntax().clone())),
             ast::Item::Fn(it) => replacements.push((it.visibility(), it.syntax().clone())),
             //Associated item's visibility should not be changed
-            ast::Item::Impl(it) if it.for_token().is_none() => impls.push(it.clone()),
+            ast::Item::Impl(it) if it.for_token().is_none() => impls.push(it),
             ast::Item::MacroDef(it) => replacements.push((it.visibility(), it.syntax().clone())),
             ast::Item::Module(it) => replacements.push((it.visibility(), it.syntax().clone())),
             ast::Item::Static(it) => replacements.push((it.visibility(), it.syntax().clone())),
@@ -806,9 +865,9 @@ fn get_replacements_for_visibilty_change(
             }
             _ => (),
         }
-    }
+    });
 
-    (replacements, record_field_parents, impls)
+    (body_items, replacements, record_field_parents, impls)
 }
 
 fn get_use_tree_paths_from_path(
@@ -831,13 +890,23 @@ fn get_use_tree_paths_from_path(
     Some(use_tree_str)
 }
 
-fn add_change_vis(vis: Option<ast::Visibility>, node_or_token_opt: Option<syntax::SyntaxElement>) {
-    if vis.is_none() {
+fn add_change_vis(
+    vis: Option<ast::Visibility>,
+    node_or_token_opt: Option<syntax::SyntaxElement>,
+) -> Option<()> {
+    if let None = vis {
         if let Some(node_or_token) = node_or_token_opt {
             let pub_crate_vis = make::visibility_pub_crate().clone_for_update();
-            ted::insert(ted::Position::before(node_or_token), pub_crate_vis.syntax());
+            if let Some(node) = node_or_token.as_node() {
+                ted::insert(ted::Position::before(node), pub_crate_vis.syntax());
+            }
+            if let Some(token) = node_or_token.as_token() {
+                ted::insert(ted::Position::before(token), pub_crate_vis.syntax());
+            }
         }
     }
+
+    Some(())
 }
 
 fn compare_hir_and_ast_module(
diff --git a/crates/ide_db/src/famous_defs.rs b/crates/ide_db/src/famous_defs.rs
index 58d99ff9df..f17fd0faa5 100644
--- a/crates/ide_db/src/famous_defs.rs
+++ b/crates/ide_db/src/famous_defs.rs
@@ -158,13 +158,9 @@ impl FamousDefs<'_, '_> {
         let mut path = path.split(':');
         let trait_ = path.next_back()?;
         let lang_crate = path.next()?;
-        let lang_crate = match lang_crate {
-            "core" => LangCrateOrigin::Core,
-            "alloc" => LangCrateOrigin::Alloc,
-            "test" => LangCrateOrigin::Test,
-            "proc_macro" => LangCrateOrigin::ProcMacro,
-            "std" => LangCrateOrigin::Std,
-            _ => return None,
+        let lang_crate = match LangCrateOrigin::from(lang_crate) {
+            LangCrateOrigin::Other => return None,
+            lang_crate => lang_crate,
         };
         let std_crate = self.find_lang_crate(lang_crate)?;
         let mut module = std_crate.root_module(db);
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index 10e5d897e0..d137a02ee8 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -71,8 +71,16 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar
     let done = match p.current() {
         T!['('] => tuple_expr(p),
         T!['['] => array_expr(p),
+        T![|] => closure_expr(p),
+        T![static] | T![async] | T![move] if la == T![|] => closure_expr(p),
+        T![static] | T![async] if la == T![move] && p.nth(2) == T![|] => closure_expr(p),
+        T![static] if la == T![async] && p.nth(2) == T![|] => closure_expr(p),
+        T![static] if la == T![async] && p.nth(2) == T![move] && p.nth(3) == T![|] => {
+            closure_expr(p)
+        }
         T![if] => if_expr(p),
         T![let] => let_expr(p),
+
         T![_] => {
             // test destructuring_assignment_wildcard_pat
             // fn foo() {
@@ -83,16 +91,12 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar
             p.bump(T![_]);
             m.complete(p, UNDERSCORE_EXPR)
         }
+
         T![loop] => loop_expr(p, None),
         T![box] => box_expr(p, None),
+        T![for] => for_expr(p, None),
         T![while] => while_expr(p, None),
         T![try] => try_block_expr(p, None),
-        T![match] => match_expr(p),
-        T![return] => return_expr(p),
-        T![yield] => yield_expr(p),
-        T![continue] => continue_expr(p),
-        T![break] => break_expr(p, r),
-
         LIFETIME_IDENT if la == T![:] => {
             let m = p.start();
             label(p);
@@ -117,21 +121,27 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar
                 }
             }
         }
-        // test effect_blocks
+        T![async] if la == T!['{'] || (la == T![move] && p.nth(2) == T!['{']) => {
+            let m = p.start();
+            p.bump(T![async]);
+            p.eat(T![move]);
+            stmt_list(p);
+            m.complete(p, BLOCK_EXPR)
+        }
+        T![match] => match_expr(p),
+        // test unsafe_block
         // fn f() { unsafe { } }
-        // fn f() { const { } }
-        // fn f() { async { } }
-        // fn f() { async move { } }
-        T![const] | T![unsafe] | T![async] if la == T!['{'] => {
+        T![unsafe] if la == T!['{'] => {
             let m = p.start();
-            p.bump_any();
+            p.bump(T![unsafe]);
             stmt_list(p);
             m.complete(p, BLOCK_EXPR)
         }
-        T![async] if la == T![move] && p.nth(2) == T!['{'] => {
+        // test const_block
+        // fn f() { const { } }
+        T![const] if la == T!['{'] => {
             let m = p.start();
-            p.bump(T![async]);
-            p.eat(T![move]);
+            p.bump(T![const]);
             stmt_list(p);
             m.complete(p, BLOCK_EXPR)
         }
@@ -146,11 +156,10 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar
             stmt_list(p);
             m.complete(p, BLOCK_EXPR)
         }
-
-        T![static] | T![async] | T![move] | T![|] => closure_expr(p),
-        T![for] if la == T![<] => closure_expr(p),
-        T![for] => for_expr(p, None),
-
+        T![return] => return_expr(p),
+        T![yield] => yield_expr(p),
+        T![continue] => continue_expr(p),
+        T![break] => break_expr(p, r),
         _ => {
             p.err_recover("expected expression", EXPR_RECOVERY_SET);
             return None;
@@ -245,30 +254,25 @@ fn array_expr(p: &mut Parser) -> CompletedMarker {
 //     static move || {};
 //     static async || {};
 //     static async move || {};
-//     for<'a> || {};
-//     for<'a> move || {};
 // }
 fn closure_expr(p: &mut Parser) -> CompletedMarker {
-    assert!(match p.current() {
-        T![static] | T![async] | T![move] | T![|] => true,
-        T![for] => p.nth(1) == T![<],
-        _ => false,
-    });
-
+    assert!(
+        p.at(T![|])
+            || (p.at(T![move]) && p.nth(1) == T![|])
+            || (p.at(T![async]) && p.nth(1) == T![|])
+            || (p.at(T![async]) && p.nth(1) == T![move] && p.nth(2) == T![|])
+            || (p.at(T![static]) && p.nth(1) == T![|])
+            || (p.at(T![static]) && p.nth(1) == T![move] && p.nth(2) == T![|])
+            || (p.at(T![static]) && p.nth(1) == T![async] && p.nth(2) == T![|])
+            || (p.at(T![static])
+                && p.nth(1) == T![async]
+                && p.nth(2) == T![move]
+                && p.nth(3) == T![|])
+    );
     let m = p.start();
-
-    if p.at(T![for]) {
-        types::for_binder(p);
-    }
-
     p.eat(T![static]);
     p.eat(T![async]);
     p.eat(T![move]);
-
-    if !p.at(T![|]) {
-        p.error("expected `|`");
-        return m.complete(p, CLOSURE_EXPR);
-    }
     params::param_list_closure(p);
     if opt_ret_type(p) {
         // test lambda_ret_block
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 509219f0b6..e33ae60862 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -908,14 +908,7 @@ fn sysroot_to_crate_graph(
                 env,
                 proc_macro,
                 false,
-                CrateOrigin::Lang(match &*sysroot[krate].name {
-                    "alloc" => LangCrateOrigin::Alloc,
-                    "core" => LangCrateOrigin::Core,
-                    "proc_macro" => LangCrateOrigin::ProcMacro,
-                    "std" => LangCrateOrigin::Std,
-                    "test" => LangCrateOrigin::Test,
-                    _ => LangCrateOrigin::Other,
-                }),
+                CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)),
             );
             Some((krate, crate_id))
         })
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 19e642968c..4e8d4b478b 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -449,7 +449,7 @@ FieldExpr =
   Attr* Expr '.' NameRef
 
 ClosureExpr =
-  Attr* ('for' GenericParamList)? 'static'? 'async'? 'move'?  ParamList RetType?
+  Attr* 'static'? 'async'? 'move'?  ParamList RetType?
   body:Expr
 
 IfExpr =
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 92d793e0fd..f51282fff4 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -837,8 +837,6 @@ pub struct ClosureExpr {
 }
 impl ast::HasAttrs for ClosureExpr {}
 impl ClosureExpr {
-    pub fn for_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![for]) }
-    pub fn generic_param_list(&self) -> Option<GenericParamList> { support::child(&self.syntax) }
     pub fn static_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![static]) }
     pub fn async_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![async]) }
     pub fn move_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![move]) }
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index 84dc655248..27d5d96159 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -239,7 +239,7 @@ Emacs support is maintained as part of the https://github.com/emacs-lsp/lsp-mode
 Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
 Not needed if the extension can install/update it on its own, coc-rust-analyzer is one example.
 
-There are several LSP client implementations for vim or neovim:
+The are several LSP client implementations for vim or neovim:
 
 ==== coc-rust-analyzer
 


Test patch:
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 67dc9884ed..57e049c10d 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -4531,7 +4531,7 @@ foo_macro!(
 
             ---
 
-            Doc comment for [`Foo`](https://doc.rust-lang.org/nightly/test/struct.Foo.html)
+            Doc comment for [`Foo`](https://docs.rs/test/*/test/struct.Foo.html)
         "#]],
     );
 }
@@ -4556,7 +4556,7 @@ pub struct Foo;
 
             ---
 
-            Doc comment for [`Foo`](https://doc.rust-lang.org/nightly/test/struct.Foo.html)
+            Doc comment for [`Foo`](https://docs.rs/test/*/test/struct.Foo.html)
         "#]],
     );
 }
diff --git a/crates/parser/test_data/parser/err/0024_many_type_parens.rast b/crates/parser/test_data/parser/err/0024_many_type_parens.rast
index d374f86610..82e6a11249 100644
--- a/crates/parser/test_data/parser/err/0024_many_type_parens.rast
+++ b/crates/parser/test_data/parser/err/0024_many_type_parens.rast
@@ -182,44 +182,45 @@ SOURCE_FILE
         WHITESPACE " "
         TUPLE_EXPR
           L_PAREN "("
-          CLOSURE_EXPR
+          FOR_EXPR
             FOR_KW "for"
-            GENERIC_PARAM_LIST
-              L_ANGLE "<"
-              LIFETIME_PARAM
-                LIFETIME
-                  LIFETIME_IDENT "'a"
-              R_ANGLE ">"
-          WHITESPACE " "
-          BIN_EXPR
+            PATH_PAT
+              PATH
+                PATH_SEGMENT
+                  L_ANGLE "<"
+                  ERROR
+                    LIFETIME_IDENT "'a"
+                  R_ANGLE ">"
+            WHITESPACE " "
             BIN_EXPR
               BIN_EXPR
                 BIN_EXPR
+                  BIN_EXPR
+                    PATH_EXPR
+                      PATH
+                        PATH_SEGMENT
+                          NAME_REF
+                            IDENT "Trait"
+                    L_ANGLE "<"
+                    ERROR
+                      LIFETIME_IDENT "'a"
+                  R_ANGLE ">"
+                  ERROR
+                    R_PAREN ")"
+                WHITESPACE " "
+                PLUS "+"
+                WHITESPACE " "
+                PAREN_EXPR
+                  L_PAREN "("
                   PATH_EXPR
                     PATH
                       PATH_SEGMENT
                         NAME_REF
-                          IDENT "Trait"
-                  L_ANGLE "<"
-                  ERROR
-                    LIFETIME_IDENT "'a"
-                R_ANGLE ">"
-                ERROR
+                          IDENT "Copy"
                   R_PAREN ")"
-              WHITESPACE " "
-              PLUS "+"
-              WHITESPACE " "
-              PAREN_EXPR
-                L_PAREN "("
-                PATH_EXPR
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "Copy"
-                R_PAREN ")"
-            R_ANGLE ">"
-            ERROR
-              SEMICOLON ";"
+              R_ANGLE ">"
+              ERROR
+                SEMICOLON ";"
           WHITESPACE "\n    "
           LET_EXPR
             LET_KW "let"
@@ -239,48 +240,49 @@ SOURCE_FILE
               L_ANGLE "<"
               TUPLE_EXPR
                 L_PAREN "("
-                CLOSURE_EXPR
+                FOR_EXPR
                   FOR_KW "for"
-                  GENERIC_PARAM_LIST
-                    L_ANGLE "<"
-                    LIFETIME_PARAM
-                      LIFETIME
-                        LIFETIME_IDENT "'a"
-                    R_ANGLE ">"
-                WHITESPACE " "
-                BIN_EXPR
+                  PATH_PAT
+                    PATH
+                      PATH_SEGMENT
+                        L_ANGLE "<"
+                        ERROR
+                          LIFETIME_IDENT "'a"
+                        R_ANGLE ">"
+                  WHITESPACE " "
                   BIN_EXPR
                     BIN_EXPR
                       BIN_EXPR
+                        BIN_EXPR
+                          PATH_EXPR
+                            PATH
+                              PATH_SEGMENT
+                                NAME_REF
+                                  IDENT "Trait"
+                          L_ANGLE "<"
+                          ERROR
+                            LIFETIME_IDENT "'a"
+                        R_ANGLE ">"
+                        ERROR
+                          R_PAREN ")"
+                      WHITESPACE " "
+                      PLUS "+"
+                      WHITESPACE " "
+                      PAREN_EXPR
+                        L_PAREN "("
                         PATH_EXPR
                           PATH
                             PATH_SEGMENT
                               NAME_REF
-                                IDENT "Trait"
-                        L_ANGLE "<"
-                        ERROR
-                          LIFETIME_IDENT "'a"
-                      R_ANGLE ">"
-                      ERROR
+                                IDENT "Copy"
                         R_PAREN ")"
                     WHITESPACE " "
                     PLUS "+"
                     WHITESPACE " "
                     PAREN_EXPR
                       L_PAREN "("
-                      PATH_EXPR
-                        PATH
-                          PATH_SEGMENT
-                            NAME_REF
-                              IDENT "Copy"
-                      R_PAREN ")"
-                  WHITESPACE " "
-                  PLUS "+"
-                  WHITESPACE " "
-                  PAREN_EXPR
-                    L_PAREN "("
-                    ERROR
-                      QUESTION "?"
+                      ERROR
+                        QUESTION "?"
                 PATH_EXPR
                   PATH
                     PATH_SEGMENT
@@ -305,21 +307,23 @@ error 141: expected SEMICOLON
 error 146: expected SEMICOLON
 error 146: expected expression
 error 148: expected expression
-error 158: expected `|`
-error 158: expected COMMA
+error 155: expected type
+error 158: expected IN_KW
 error 165: expected expression
 error 168: expected expression
 error 179: expected expression
+error 180: expected a block
 error 180: expected COMMA
 error 190: expected EQ
 error 190: expected expression
 error 191: expected COMMA
-error 204: expected `|`
-error 204: expected COMMA
+error 201: expected type
+error 204: expected IN_KW
 error 211: expected expression
 error 214: expected expression
 error 228: expected expression
 error 229: expected R_PAREN
+error 229: expected a block
 error 229: expected COMMA
 error 236: expected expression
 error 237: expected COMMA
diff --git a/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.rast b/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.rast
index c25ad8430d..bc54b01869 100644
--- a/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.rast
+++ b/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.rast
@@ -199,48 +199,6 @@ SOURCE_FILE
                 L_CURLY "{"
                 R_CURLY "}"
           SEMICOLON ";"
-        WHITESPACE "\n    "
-        EXPR_STMT
-          CLOSURE_EXPR
-            FOR_KW "for"
-            GENERIC_PARAM_LIST
-              L_ANGLE "<"
-              LIFETIME_PARAM
-                LIFETIME
-                  LIFETIME_IDENT "'a"
-              R_ANGLE ">"
-            WHITESPACE " "
-            PARAM_LIST
-              PIPE "|"
-              PIPE "|"
-            WHITESPACE " "
-            BLOCK_EXPR
-              STMT_LIST
-                L_CURLY "{"
-                R_CURLY "}"
-          SEMICOLON ";"
-        WHITESPACE "\n    "
-        EXPR_STMT
-          CLOSURE_EXPR
-            FOR_KW "for"
-            GENERIC_PARAM_LIST
-              L_ANGLE "<"
-              LIFETIME_PARAM
-                LIFETIME
-                  LIFETIME_IDENT "'a"
-              R_ANGLE ">"
-            WHITESPACE " "
-            MOVE_KW "move"
-            WHITESPACE " "
-            PARAM_LIST
-              PIPE "|"
-              PIPE "|"
-            WHITESPACE " "
-            BLOCK_EXPR
-              STMT_LIST
-                L_CURLY "{"
-                R_CURLY "}"
-          SEMICOLON ";"
         WHITESPACE "\n"
         R_CURLY "}"
   WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.rs b/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.rs
index 75516d2584..d01f921664 100644
--- a/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.rs
+++ b/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.rs
@@ -10,6 +10,4 @@ fn foo() {
     static move || {};
     static async || {};
     static async move || {};
-    for<'a> || {};
-    for<'a> move || {};
 }
diff --git a/crates/parser/test_data/parser/inline/ok/0157_const_block.rast b/crates/parser/test_data/parser/inline/ok/0157_const_block.rast
new file mode 100644
index 0000000000..d2025b895d
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/0157_const_block.rast
@@ -0,0 +1,24 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "f"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE " "
+        BLOCK_EXPR
+          CONST_KW "const"
+          WHITESPACE " "
+          STMT_LIST
+            L_CURLY "{"
+            WHITESPACE " "
+            R_CURLY "}"
+        WHITESPACE " "
+        R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0157_const_block.rs b/crates/parser/test_data/parser/inline/ok/0157_const_block.rs
new file mode 100644
index 0000000000..a2e3565a32
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/0157_const_block.rs
@@ -0,0 +1,1 @@
+fn f() { const { } }
diff --git a/crates/parser/test_data/parser/inline/ok/0162_unsafe_block.rast b/crates/parser/test_data/parser/inline/ok/0162_unsafe_block.rast
new file mode 100644
index 0000000000..2149676ded
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/0162_unsafe_block.rast
@@ -0,0 +1,24 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "f"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE " "
+        BLOCK_EXPR
+          UNSAFE_KW "unsafe"
+          WHITESPACE " "
+          STMT_LIST
+            L_CURLY "{"
+            WHITESPACE " "
+            R_CURLY "}"
+        WHITESPACE " "
+        R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0162_unsafe_block.rs b/crates/parser/test_data/parser/inline/ok/0162_unsafe_block.rs
new file mode 100644
index 0000000000..dfb9b2a1c1
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/0162_unsafe_block.rs
@@ -0,0 +1,1 @@
+fn f() { unsafe { } }
diff --git a/crates/parser/test_data/parser/inline/ok/0199_effect_blocks.rast b/crates/parser/test_data/parser/inline/ok/0199_effect_blocks.rast
deleted file mode 100644
index f14080c90e..0000000000
--- a/crates/parser/test_data/parser/inline/ok/0199_effect_blocks.rast
+++ /dev/null
@@ -1,95 +0,0 @@
-SOURCE_FILE
-  FN
-    FN_KW "fn"
-    WHITESPACE " "
-    NAME
-      IDENT "f"
-    PARAM_LIST
-      L_PAREN "("
-      R_PAREN ")"
-    WHITESPACE " "
-    BLOCK_EXPR
-      STMT_LIST
-        L_CURLY "{"
-        WHITESPACE " "
-        BLOCK_EXPR
-          UNSAFE_KW "unsafe"
-          WHITESPACE " "
-          STMT_LIST
-            L_CURLY "{"
-            WHITESPACE " "
-            R_CURLY "}"
-        WHITESPACE " "
-        R_CURLY "}"
-  WHITESPACE "\n"
-  FN
-    FN_KW "fn"
-    WHITESPACE " "
-    NAME
-      IDENT "f"
-    PARAM_LIST
-      L_PAREN "("
-      R_PAREN ")"
-    WHITESPACE " "
-    BLOCK_EXPR
-      STMT_LIST
-        L_CURLY "{"
-        WHITESPACE " "
-        BLOCK_EXPR
-          CONST_KW "const"
-          WHITESPACE " "
-          STMT_LIST
-            L_CURLY "{"
-            WHITESPACE " "
-            R_CURLY "}"
-        WHITESPACE " "
-        R_CURLY "}"
-  WHITESPACE "\n"
-  FN
-    FN_KW "fn"
-    WHITESPACE " "
-    NAME
-      IDENT "f"
-    PARAM_LIST
-      L_PAREN "("
-      R_PAREN ")"
-    WHITESPACE " "
-    BLOCK_EXPR
-      STMT_LIST
-        L_CURLY "{"
-        WHITESPACE " "
-        BLOCK_EXPR
-          ASYNC_KW "async"
-          WHITESPACE " "
-          STMT_LIST
-            L_CURLY "{"
-            WHITESPACE " "
-            R_CURLY "}"
-        WHITESPACE " "
-        R_CURLY "}"
-  WHITESPACE "\n"
-  FN
-    FN_KW "fn"
-    WHITESPACE " "
-    NAME
-      IDENT "f"
-    PARAM_LIST
-      L_PAREN "("
-      R_PAREN ")"
-    WHITESPACE " "
-    BLOCK_EXPR
-      STMT_LIST
-        L_CURLY "{"
-        WHITESPACE " "
-        BLOCK_EXPR
-          ASYNC_KW "async"
-          WHITESPACE " "
-          MOVE_KW "move"
-          WHITESPACE " "
-          STMT_LIST
-            L_CURLY "{"
-            WHITESPACE " "
-            R_CURLY "}"
-        WHITESPACE " "
-        R_CURLY "}"
-  WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0199_effect_blocks.rs b/crates/parser/test_data/parser/inline/ok/0199_effect_blocks.rs
deleted file mode 100644
index c57d24b2f7..0000000000
--- a/crates/parser/test_data/parser/inline/ok/0199_effect_blocks.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-fn f() { unsafe { } }
-fn f() { const { } }
-fn f() { async { } }
-fn f() { async move { } }
diff --git a/crates/project_model/src/tests.rs b/crates/project_model/src/tests.rs
index 351e820ada..3d089b61b5 100644
--- a/crates/project_model/src/tests.rs
+++ b/crates/project_model/src/tests.rs
@@ -1368,7 +1368,7 @@ fn rust_project_hello_world_project_model() {
                         ],
                         proc_macro: [],
                         origin: Lang(
-                            ProcMacro,
+                            Other,
                         ),
                         is_proc_macro: false,
                     },