Repository: rust-analyzer/rust-analyzer
Pull request #11734 (2022-03-17 06:54:13)

internal: Bump `xshell` and fix `dist`
bors r+


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index fab0edfe30..416ab4e3e6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2072,18 +2072,18 @@ checksum = "da260301476ad19a4733a0e930db8227a48ea04561e235a5102978145ec69fcc"
 
 [[package]]
 name = "xshell"
-version = "0.2.0"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3332cab90be2998a2aacb6494db45344bd16dfcc43ff36c42255018c6bcc96be"
+checksum = "4884417669886d3abff14feec797179526ade713f212e54ec08b19bc6bdc86aa"
 dependencies = [
  "xshell-macros",
 ]
 
 [[package]]
 name = "xshell-macros"
-version = "0.2.0"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f47e54cffa76000b7641328ab3bb1e146f93a1690ab86c5909c656f49d91019c"
+checksum = "37d92065701c3611323f96eac5475b995421fc7eb2bcba1336cdd80b9b2fb68f"
 
 [[package]]
 name = "xtask"
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index d04bd87b7b..322a9b820a 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -16,7 +16,6 @@ pub struct InlayHintsConfig {
     pub type_hints: bool,
     pub parameter_hints: bool,
     pub chaining_hints: bool,
-    pub closure_return_type_hints: bool,
     pub hide_named_constructor_hints: bool,
     pub max_length: Option<usize>,
 }
@@ -25,7 +24,6 @@ pub struct InlayHintsConfig {
 pub enum InlayKind {
     TypeHint,
     ParameterHint,
-    ClosureReturnTypeHint,
     ChainingHint,
 }
 
@@ -69,86 +67,55 @@ pub(crate) fn inlay_hints(
     let file = sema.parse(file_id);
     let file = file.syntax();
 
-    let mut acc = Vec::new();
-
-    let hints = |node| hints(&mut acc, &sema, config, node);
-    match range_limit {
-        Some(FileRange { range, .. }) => match file.covering_element(range) {
-            NodeOrToken::Token(_) => return acc,
-            NodeOrToken::Node(n) => n
-                .descendants()
-                .filter(|descendant| range.contains_range(descendant.text_range()))
-                .for_each(hints),
-        },
-        None => file.descendants().for_each(hints),
-    };
+    let mut hints = Vec::new();
+
+    if let Some(range_limit) = range_limit {
+        let range_limit = range_limit.range;
+        match file.covering_element(range_limit) {
+            NodeOrToken::Token(_) => return hints,
+            NodeOrToken::Node(n) => {
+                for node in n
+                    .descendants()
+                    .filter(|descendant| range_limit.contains_range(descendant.text_range()))
+                {
+                    get_hints(&mut hints, &sema, config, node);
+                }
+            }
+        }
+    } else {
+        for node in file.descendants() {
+            get_hints(&mut hints, &sema, config, node);
+        }
+    }
 
-    acc
+    hints
 }
 
-fn hints(
+fn get_hints(
     hints: &mut Vec<InlayHint>,
     sema: &Semantics<RootDatabase>,
     config: &InlayHintsConfig,
     node: SyntaxNode,
 ) {
-    let krate = sema.scope(&node).module().map(|it| it.krate());
-    let famous_defs = FamousDefs(sema, krate);
     if let Some(expr) = ast::Expr::cast(node.clone()) {
-        chaining_hints(hints, sema, &famous_defs, config, &expr);
+        get_chaining_hints(hints, sema, config, &expr);
         match expr {
             ast::Expr::CallExpr(it) => {
-                param_name_hints(hints, sema, config, ast::Expr::from(it));
+                get_param_name_hints(hints, sema, config, ast::Expr::from(it));
             }
             ast::Expr::MethodCallExpr(it) => {
-                param_name_hints(hints, sema, config, ast::Expr::from(it));
-            }
-            ast::Expr::ClosureExpr(it) => {
-                closure_ret_hints(hints, sema, &famous_defs, config, it);
+                get_param_name_hints(hints, sema, config, ast::Expr::from(it));
             }
             _ => (),
         }
     } else if let Some(it) = ast::IdentPat::cast(node) {
-        bind_pat_hints(hints, sema, config, &it);
-    }
-}
-
-fn closure_ret_hints(
-    acc: &mut Vec<InlayHint>,
-    sema: &Semantics<RootDatabase>,
-    famous_defs: &FamousDefs,
-    config: &InlayHintsConfig,
-    closure: ast::ClosureExpr,
-) -> Option<()> {
-    if !config.closure_return_type_hints {
-        return None;
-    }
-
-    let closure = sema.descend_node_into_attributes(closure.clone()).pop()?;
-
-    let param_list = match closure.body() {
-        Some(ast::Expr::BlockExpr(_)) => closure.param_list()?,
-        _ => return None,
-    };
-    let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure))?.adjusted();
-    let callable = ty.as_callable(sema.db)?;
-    let ty = callable.return_type();
-    if ty.is_unit() {
-        return None;
+        get_bind_pat_hints(hints, sema, config, &it);
     }
-    acc.push(InlayHint {
-        range: param_list.syntax().text_range(),
-        kind: InlayKind::ClosureReturnTypeHint,
-        label: hint_iterator(sema, &famous_defs, config, &ty)
-            .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string().into()),
-    });
-    Some(())
 }
 
-fn chaining_hints(
+fn get_chaining_hints(
     acc: &mut Vec<InlayHint>,
     sema: &Semantics<RootDatabase>,
-    famous_defs: &FamousDefs,
     config: &InlayHintsConfig,
     expr: &ast::Expr,
 ) -> Option<()> {
@@ -162,6 +129,8 @@ fn chaining_hints(
 
     let descended = sema.descend_node_into_attributes(expr.clone()).pop();
     let desc_expr = descended.as_ref().unwrap_or(expr);
+    let krate = sema.scope(desc_expr.syntax()).module().map(|it| it.krate());
+    let famous_defs = FamousDefs(sema, krate);
 
     let mut tokens = expr
         .syntax()
@@ -205,7 +174,7 @@ fn chaining_hints(
     Some(())
 }
 
-fn param_name_hints(
+fn get_param_name_hints(
     acc: &mut Vec<InlayHint>,
     sema: &Semantics<RootDatabase>,
     config: &InlayHintsConfig,
@@ -245,7 +214,7 @@ fn param_name_hints(
     Some(())
 }
 
-fn bind_pat_hints(
+fn get_bind_pat_hints(
     acc: &mut Vec<InlayHint>,
     sema: &Semantics<RootDatabase>,
     config: &InlayHintsConfig,
@@ -605,21 +574,13 @@ mod tests {
 
     use crate::{fixture, inlay_hints::InlayHintsConfig};
 
-    const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig {
-        render_colons: false,
-        type_hints: false,
-        parameter_hints: false,
-        chaining_hints: false,
-        hide_named_constructor_hints: false,
-        closure_return_type_hints: false,
-        max_length: None,
-    };
     const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
+        render_colons: true,
         type_hints: true,
         parameter_hints: true,
         chaining_hints: true,
-        closure_return_type_hints: true,
-        ..DISABLED_CONFIG
+        hide_named_constructor_hints: false,
+        max_length: None,
     };
 
     #[track_caller]
@@ -630,19 +591,46 @@ mod tests {
     #[track_caller]
     fn check_params(ra_fixture: &str) {
         check_with_config(
-            InlayHintsConfig { parameter_hints: true, ..DISABLED_CONFIG },
+            InlayHintsConfig {
+                render_colons: true,
+                parameter_hints: true,
+                type_hints: false,
+                chaining_hints: false,
+                hide_named_constructor_hints: false,
+                max_length: None,
+            },
             ra_fixture,
         );
     }
 
     #[track_caller]
     fn check_types(ra_fixture: &str) {
-        check_with_config(InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, ra_fixture);
+        check_with_config(
+            InlayHintsConfig {
+                render_colons: true,
+                parameter_hints: false,
+                type_hints: true,
+                chaining_hints: false,
+                hide_named_constructor_hints: false,
+                max_length: None,
+            },
+            ra_fixture,
+        );
     }
 
     #[track_caller]
     fn check_chains(ra_fixture: &str) {
-        check_with_config(InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, ra_fixture);
+        check_with_config(
+            InlayHintsConfig {
+                render_colons: true,
+                parameter_hints: false,
+                type_hints: false,
+                chaining_hints: true,
+                hide_named_constructor_hints: false,
+                max_length: None,
+            },
+            ra_fixture,
+        );
     }
 
     #[track_caller]
@@ -665,7 +653,14 @@ mod tests {
     #[test]
     fn hints_disabled() {
         check_with_config(
-            InlayHintsConfig { render_colons: true, ..DISABLED_CONFIG },
+            InlayHintsConfig {
+                render_colons: true,
+                type_hints: false,
+                parameter_hints: false,
+                chaining_hints: false,
+                hide_named_constructor_hints: false,
+                max_length: None,
+            },
             r#"
 fn foo(a: i32, b: i32) -> i32 { a + b }
 fn main() {
@@ -1114,7 +1109,14 @@ fn main() {
         let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
         let inlay_hints = analysis
             .inlay_hints(
-                &InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
+                &InlayHintsConfig {
+                    render_colons: true,
+                    parameter_hints: false,
+                    type_hints: true,
+                    chaining_hints: false,
+                    hide_named_constructor_hints: false,
+                    max_length: None,
+                },
                 file_id,
                 Some(FileRange {
                     file_id,
@@ -1423,7 +1425,7 @@ fn main() {
                 parameter_hints: true,
                 chaining_hints: true,
                 hide_named_constructor_hints: true,
-                ..DISABLED_CONFIG
+                max_length: None,
             },
             r#"
 //- minicore: try, option
@@ -1551,14 +1553,13 @@ fn fallible() -> ControlFlow<()> {
 fn main() {
     let mut start = 0;
           //^^^^^ i32
-    (0..2).for_each(|increment      | { start += increment; });
+    (0..2).for_each(|increment| { start += increment; });
                    //^^^^^^^^^ i32
 
     let multiply =
       //^^^^^^^^ |i32, i32| -> i32
       | a,     b| a * b
       //^ i32  ^ i32
-
     ;
 
     let _: i32 = multiply(1, 2);
@@ -1567,8 +1568,6 @@ fn main() {
 
     let return_42 = || 42;
       //^^^^^^^^^ || -> i32
-      || { 42 };
-    //^^ i32
 }"#,
         );
     }
@@ -1598,7 +1597,14 @@ fn main() {
     #[test]
     fn chaining_hints_ignore_comments() {
         check_expect(
-            InlayHintsConfig { type_hints: false, chaining_hints: true, ..DISABLED_CONFIG },
+            InlayHintsConfig {
+                render_colons: true,
+                parameter_hints: false,
+                type_hints: false,
+                chaining_hints: true,
+                hide_named_constructor_hints: false,
+                max_length: None,
+            },
             r#"
 struct A(B);
 impl A { fn into_b(self) -> B { self.0 } }
@@ -1649,7 +1655,14 @@ fn main() {
     #[test]
     fn struct_access_chaining_hints() {
         check_expect(
-            InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
+            InlayHintsConfig {
+                render_colons: true,
+                parameter_hints: false,
+                type_hints: false,
+                chaining_hints: true,
+                hide_named_constructor_hints: false,
+                max_length: None,
+            },
             r#"
 struct A { pub b: B }
 struct B { pub c: C }
@@ -1688,7 +1701,14 @@ fn main() {
     #[test]
     fn generic_chaining_hints() {
         check_expect(
-            InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
+            InlayHintsConfig {
+                render_colons: true,
+                parameter_hints: false,
+                type_hints: false,
+                chaining_hints: true,
+                hide_named_constructor_hints: false,
+                max_length: None,
+            },
             r#"
 struct A<T>(T);
 struct B<T>(T);
@@ -1728,7 +1748,14 @@ fn main() {
     #[test]
     fn shorten_iterator_chaining_hints() {
         check_expect(
-            InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
+            InlayHintsConfig {
+                render_colons: true,
+                parameter_hints: false,
+                type_hints: false,
+                chaining_hints: true,
+                hide_named_constructor_hints: false,
+                max_length: None,
+            },
             r#"
 //- minicore: iterators
 use core::iter;
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index a06793cf51..ef3487545c 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -109,7 +109,6 @@ impl StaticIndex<'_> {
                     type_hints: true,
                     parameter_hints: true,
                     chaining_hints: true,
-                    closure_return_type_hints: true,
                     hide_named_constructor_hints: false,
                     max_length: Some(25),
                 },
diff --git a/crates/ide_assists/src/handlers/convert_to_guarded_return.rs b/crates/ide_assists/src/handlers/convert_to_guarded_return.rs
index 445567b7f9..a7c43413d2 100644
--- a/crates/ide_assists/src/handlers/convert_to_guarded_return.rs
+++ b/crates/ide_assists/src/handlers/convert_to_guarded_return.rs
@@ -97,7 +97,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
     let parent_container = parent_block.syntax().parent()?;
 
     let early_expression: ast::Expr = match parent_container.kind() {
-        WHILE_EXPR | LOOP_EXPR => make::expr_continue(None),
+        WHILE_EXPR | LOOP_EXPR => make::expr_continue(),
         FN => make::expr_return(None),
         _ => return None,
     };
diff --git a/crates/ide_assists/src/handlers/convert_while_to_loop.rs b/crates/ide_assists/src/handlers/convert_while_to_loop.rs
index 739097067d..345d0c2063 100644
--- a/crates/ide_assists/src/handlers/convert_while_to_loop.rs
+++ b/crates/ide_assists/src/handlers/convert_while_to_loop.rs
@@ -53,7 +53,7 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext) -> O
             let while_indent_level = IndentLevel::from_node(while_expr.syntax());
 
             let break_block =
-                make::block_expr(once(make::expr_stmt(make::expr_break(None, None)).into()), None)
+                make::block_expr(once(make::expr_stmt(make::expr_break(None)).into()), None)
                     .indent(while_indent_level);
             let block_expr = if is_pattern_cond(while_cond.clone()) {
                 let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into()));
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index 8352774be4..e48a1cd9ef 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -287,10 +287,10 @@ enum FlowKind {
     Try {
         kind: TryKind,
     },
-    /// Break with label and value (`break 'label $expr;`)
-    Break(Option<ast::Lifetime>, Option<ast::Expr>),
-    /// Continue with label (`continue 'label;`)
-    Continue(Option<ast::Lifetime>),
+    /// Break with value (`break $expr;`)
+    Break(Option<ast::Expr>),
+    /// Continue
+    Continue,
 }
 
 #[derive(Debug, Clone)]
@@ -433,21 +433,21 @@ impl FlowKind {
     fn make_result_handler(&self, expr: Option<ast::Expr>) -> ast::Expr {
         match self {
             FlowKind::Return(_) => make::expr_return(expr),
-            FlowKind::Break(label, _) => make::expr_break(label.clone(), expr),
+            FlowKind::Break(_) => make::expr_break(expr),
             FlowKind::Try { .. } => {
                 stdx::never!("cannot have result handler with try");
                 expr.unwrap_or_else(|| make::expr_return(None))
             }
-            FlowKind::Continue(label) => {
+            FlowKind::Continue => {
                 stdx::always!(expr.is_none(), "continue with value is not possible");
-                make::expr_continue(label.clone())
+                make::expr_continue()
             }
         }
     }
 
     fn expr_ty(&self, ctx: &AssistContext) -> Option<hir::Type> {
         match self {
-            FlowKind::Return(Some(expr)) | FlowKind::Break(_, Some(expr)) => {
+            FlowKind::Return(Some(expr)) | FlowKind::Break(Some(expr)) => {
                 ctx.sema.type_of_expr(expr).map(TypeInfo::adjusted)
             }
             FlowKind::Try { .. } => {
@@ -839,8 +839,8 @@ impl FunctionBody {
                 cov_mark::hit!(external_control_flow_break_and_continue);
                 return None;
             }
-            (None, None, Some(b), None) => Some(FlowKind::Break(b.lifetime(), b.expr())),
-            (None, None, None, Some(c)) => Some(FlowKind::Continue(c.lifetime())),
+            (None, None, Some(b), None) => Some(FlowKind::Break(b.expr())),
+            (None, None, None, Some(_)) => Some(FlowKind::Continue),
             (None, None, None, None) => None,
         };
 
@@ -1185,20 +1185,20 @@ impl FlowHandler {
                 let action = flow_kind.clone();
                 if *ret_ty == FunType::Unit {
                     match flow_kind {
-                        FlowKind::Return(None)
-                        | FlowKind::Break(_, None)
-                        | FlowKind::Continue(_) => FlowHandler::If { action },
-                        FlowKind::Return(_) | FlowKind::Break(_, _) => {
+                        FlowKind::Return(None) | FlowKind::Break(None) | FlowKind::Continue => {
+                            FlowHandler::If { action }
+                        }
+                        FlowKind::Return(_) | FlowKind::Break(_) => {
                             FlowHandler::IfOption { action }
                         }
                         FlowKind::Try { kind } => FlowHandler::Try { kind: kind.clone() },
                     }
                 } else {
                     match flow_kind {
-                        FlowKind::Return(None)
-                        | FlowKind::Break(_, None)
-                        | FlowKind::Continue(_) => FlowHandler::MatchOption { none: action },
-                        FlowKind::Return(_) | FlowKind::Break(_, _) => {
+                        FlowKind::Return(None) | FlowKind::Break(None) | FlowKind::Continue => {
+                            FlowHandler::MatchOption { none: action }
+                        }
+                        FlowKind::Return(_) | FlowKind::Break(_) => {
                             FlowHandler::MatchResult { err: action }
                         }
                         FlowKind::Try { kind } => FlowHandler::Try { kind: kind.clone() },
@@ -3404,76 +3404,6 @@ fn $0fun_name(n: i32) -> ControlFlow<()> {
         );
     }
 
-    #[test]
-    fn break_loop_nested_labeled() {
-        check_assist(
-            extract_function,
-            r#"
-//- minicore: try
-fn foo() {
-    'bar: loop {
-        loop {
-            $0break 'bar;$0
-        }
-    }
-}
-"#,
-            r#"
-use core::ops::ControlFlow;
-
-fn foo() {
-    'bar: loop {
-        loop {
-            if let ControlFlow::Break(_) = fun_name() {
-                break 'bar;
-            }
-        }
-    }
-}
-
-fn $0fun_name() -> ControlFlow<()> {
-    return ControlFlow::Break(());
-    ControlFlow::Continue(())
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn continue_loop_nested_labeled() {
-        check_assist(
-            extract_function,
-            r#"
-//- minicore: try
-fn foo() {
-    'bar: loop {
-        loop {
-            $0continue 'bar;$0
-        }
-    }
-}
-"#,
-            r#"
-use core::ops::ControlFlow;
-
-fn foo() {
-    'bar: loop {
-        loop {
-            if let ControlFlow::Break(_) = fun_name() {
-                continue 'bar;
-            }
-        }
-    }
-}
-
-fn $0fun_name() -> ControlFlow<()> {
-    return ControlFlow::Break(());
-    ControlFlow::Continue(())
-}
-"#,
-        );
-    }
-
     #[test]
     fn return_from_nested_loop() {
         check_assist(
@@ -3678,46 +3608,6 @@ fn $0fun_name() -> Option<i32> {
         );
     }
 
-    #[test]
-    fn break_with_value_and_label() {
-        check_assist(
-            extract_function,
-            r#"
-fn foo() -> i32 {
-    'bar: loop {
-        let n = 1;
-        $0let k = 1;
-        if k == 42 {
-            break 'bar 4;
-        }
-        let m = k + 1;$0
-        let h = 1;
-    }
-}
-"#,
-            r#"
-fn foo() -> i32 {
-    'bar: loop {
-        let n = 1;
-        if let Some(value) = fun_name() {
-            break 'bar value;
-        }
-        let h = 1;
-    }
-}
-
-fn $0fun_name() -> Option<i32> {
-    let k = 1;
-    if k == 42 {
-        return Some(4);
-    }
-    let m = k + 1;
-    None
-}
-"#,
-        );
-    }
-
     #[test]
     fn break_with_value_and_return() {
         check_assist(
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index 867e6bcf48..91e6b84294 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -29,11 +29,11 @@ use crate::{
     item::Builder,
     render::{
         const_::render_const,
+        enum_variant::render_variant,
         function::{render_fn, render_method},
-        literal::{render_struct_literal, render_variant_lit},
-        macro_::render_macro,
         pattern::{render_struct_pat, render_variant_pat},
-        render_field, render_resolution, render_resolution_simple, render_tuple_field,
+        render_field, render_resolution, render_tuple_field,
+        struct_literal::render_struct_literal,
         type_alias::{render_type_alias, render_type_alias_with_eq},
         union_literal::render_union_literal,
         RenderContext,
@@ -124,37 +124,7 @@ impl Completions {
             cov_mark::hit!(qualified_path_doc_hidden);
             return;
         }
-        self.add(render_resolution(RenderContext::new(ctx), local_name, resolution));
-    }
-
-    pub(crate) fn add_resolution_simple(
-        &mut self,
-        ctx: &CompletionContext,
-        local_name: hir::Name,
-        resolution: hir::ScopeDef,
-    ) {
-        if ctx.is_scope_def_hidden(resolution) {
-            return;
-        }
-        self.add(render_resolution_simple(RenderContext::new(ctx), local_name, resolution));
-    }
-
-    pub(crate) fn add_macro(
-        &mut self,
-        ctx: &CompletionContext,
-        mac: hir::Macro,
-        local_name: hir::Name,
-    ) {
-        let is_private_editable = match ctx.is_visible(&mac) {
-            Visible::Yes => false,
-            Visible::Editable => true,
-            Visible::No => return,
-        };
-        self.add(render_macro(
-            RenderContext::new(ctx).private_editable(is_private_editable),
-            local_name,
-            mac,
-        ));
+        self.add(render_resolution(RenderContext::new(ctx, false), local_name, resolution));
     }
 
     pub(crate) fn add_function(
@@ -168,11 +138,7 @@ impl Completions {
             Visible::Editable => true,
             Visible::No => return,
         };
-        self.add(render_fn(
-            RenderContext::new(ctx).private_editable(is_private_editable),
-            local_name,
-            func,
-        ));
+        self.add(render_fn(RenderContext::new(ctx, is_private_editable), None, local_name, func));
     }
 
     pub(crate) fn add_method(
@@ -188,7 +154,8 @@ impl Completions {
             Visible::No => return,
         };
         self.add(render_method(
-            RenderContext::new(ctx).private_editable(is_private_editable),
+            RenderContext::new(ctx, is_private_editable),
+            None,
             receiver,
             local_name,
             func,
@@ -201,10 +168,7 @@ impl Completions {
             Visible::Editable => true,
             Visible::No => return,
         };
-        self.add_opt(render_const(
-            RenderContext::new(ctx).private_editable(is_private_editable),
-            konst,
-        ));
+        self.add_opt(render_const(RenderContext::new(ctx, is_private_editable), konst));
     }
 
     pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) {
@@ -213,10 +177,7 @@ impl Completions {
             Visible::Editable => true,
             Visible::No => return,
         };
-        self.add_opt(render_type_alias(
-            RenderContext::new(ctx).private_editable(is_private_editable),
-            type_alias,
-        ));
+        self.add_opt(render_type_alias(RenderContext::new(ctx, is_private_editable), type_alias));
     }
 
     pub(crate) fn add_type_alias_with_eq(
@@ -224,7 +185,7 @@ impl Completions {
         ctx: &CompletionContext,
         type_alias: hir::TypeAlias,
     ) {
-        self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias));
+        self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx, false), type_alias));
     }
 
     pub(crate) fn add_qualified_enum_variant(
@@ -233,7 +194,8 @@ impl Completions {
         variant: hir::Variant,
         path: hir::ModPath,
     ) {
-        self.add_opt(render_variant_lit(RenderContext::new(ctx), None, variant, Some(path)));
+        let item = render_variant(RenderContext::new(ctx, false), None, None, variant, Some(path));
+        self.add(item);
     }
 
     pub(crate) fn add_enum_variant(
@@ -242,7 +204,8 @@ impl Completions {
         variant: hir::Variant,
         local_name: Option<hir::Name>,
     ) {
-        self.add_opt(render_variant_lit(RenderContext::new(ctx), local_name, variant, None));
+        let item = render_variant(RenderContext::new(ctx, false), None, local_name, variant, None);
+        self.add(item);
     }
 
     pub(crate) fn add_field(
@@ -257,12 +220,7 @@ impl Completions {
             Visible::Editable => true,
             Visible::No => return,
         };
-        let item = render_field(
-            RenderContext::new(ctx).private_editable(is_private_editable),
-            receiver,
-            field,
-            ty,
-        );
+        let item = render_field(RenderContext::new(ctx, is_private_editable), receiver, field, ty);
         self.add(item);
     }
 
@@ -273,7 +231,7 @@ impl Completions {
         path: Option<hir::ModPath>,
         local_name: Option<hir::Name>,
     ) {
-        let item = render_struct_literal(RenderContext::new(ctx), strukt, path, local_name);
+        let item = render_struct_literal(RenderContext::new(ctx, false), strukt, path, local_name);
         self.add_opt(item);
     }
 
@@ -284,7 +242,7 @@ impl Completions {
         path: Option<hir::ModPath>,
         local_name: Option<hir::Name>,
     ) {
-        let item = render_union_literal(RenderContext::new(ctx), un, path, local_name);
+        let item = render_union_literal(RenderContext::new(ctx, false), un, path, local_name);
         self.add_opt(item);
     }
 
@@ -295,7 +253,7 @@ impl Completions {
         field: usize,
         ty: &hir::Type,
     ) {
-        let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
+        let item = render_tuple_field(RenderContext::new(ctx, false), receiver, field, ty);
         self.add(item);
     }
 
@@ -314,12 +272,7 @@ impl Completions {
         variant: hir::Variant,
         local_name: Option<hir::Name>,
     ) {
-        self.add_opt(render_variant_pat(
-            RenderContext::new(ctx),
-            variant,
-            local_name.clone(),
-            None,
-        ));
+        self.add_opt(render_variant_pat(RenderContext::new(ctx, false), variant, local_name, None));
     }
 
     pub(crate) fn add_qualified_variant_pat(
@@ -328,8 +281,7 @@ impl Completions {
         variant: hir::Variant,
         path: hir::ModPath,
     ) {
-        let path = Some(&path);
-        self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, path));
+        self.add_opt(render_variant_pat(RenderContext::new(ctx, false), variant, None, Some(path)));
     }
 
     pub(crate) fn add_struct_pat(
@@ -338,7 +290,7 @@ impl Completions {
         strukt: hir::Struct,
         local_name: Option<hir::Name>,
     ) {
-        self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name));
+        self.add_opt(render_struct_pat(RenderContext::new(ctx, false), strukt, local_name));
     }
 }
 
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index 6c8878a7bb..aee2bbb53c 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -198,7 +198,7 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
             })
             .filter_map(|import| {
                 render_resolution_with_import(
-                    RenderContext::new(ctx),
+                    RenderContext::new(ctx, false),
                     ImportEdit { import, scope: import_scope.clone() },
                 )
             }),
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index 6c17da07d6..958c892b8d 100644
--- a/crates/ide_completion/src/completions/pattern.rs
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -54,7 +54,8 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
     {
         if refutable || single_variant_enum(e) {
             super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
-                acc.add_qualified_variant_pat(ctx, variant, path);
+                acc.add_qualified_variant_pat(ctx, variant, path.clone());
+                acc.add_qualified_enum_variant(ctx, variant, path);
             });
         }
     }
@@ -62,7 +63,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
     // FIXME: ideally, we should look at the type we are matching against and
     // suggest variants + auto-imports
     ctx.process_all_names(&mut |name, res| {
-        let add_simple_path = match res {
+        let add_resolution = match res {
             hir::ScopeDef::ModuleDef(def) => match def {
                 hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
                     acc.add_struct_pat(ctx, strukt, Some(name.clone()));
@@ -75,11 +76,8 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
                     true
                 }
                 hir::ModuleDef::Adt(hir::Adt::Enum(e)) => refutable || single_variant_enum(e),
-                hir::ModuleDef::Const(..) => refutable,
-                hir::ModuleDef::Module(..) => true,
-                hir::ModuleDef::Macro(mac) if mac.is_fn_like(ctx.db) => {
-                    return acc.add_macro(ctx, mac, name)
-                }
+                hir::ModuleDef::Const(..) | hir::ModuleDef::Module(..) => refutable,
+                hir::ModuleDef::Macro(mac) => mac.is_fn_like(ctx.db),
                 _ => false,
             },
             hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() {
@@ -87,19 +85,13 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
                     acc.add_struct_pat(ctx, strukt, Some(name.clone()));
                     true
                 }
-                Some(hir::Adt::Enum(e)) => refutable || single_variant_enum(e),
-                Some(hir::Adt::Union(_)) => true,
-                _ => false,
+                Some(hir::Adt::Enum(_)) => refutable,
+                _ => true,
             },
-            ScopeDef::GenericParam(hir::GenericParam::ConstParam(_)) => true,
-            ScopeDef::GenericParam(_)
-            | ScopeDef::AdtSelfType(_)
-            | ScopeDef::Local(_)
-            | ScopeDef::Label(_)
-            | ScopeDef::Unknown => false,
+            _ => false,
         };
-        if add_simple_path {
-            acc.add_resolution_simple(ctx, name, res);
+        if add_resolution {
+            acc.add_resolution(ctx, name, res);
         }
     });
 }
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs
index 5509ec922f..264b3784bf 100644
--- a/crates/ide_completion/src/completions/record.rs
+++ b/crates/ide_completion/src/completions/record.rs
@@ -84,16 +84,13 @@ pub(crate) fn complete_record_literal(
     match ctx.expected_type.as_ref()?.as_adt()? {
         hir::Adt::Struct(strukt) if ctx.path_qual().is_none() => {
             let module = if let Some(module) = ctx.module { module } else { strukt.module(ctx.db) };
-            let path = module
-                .find_use_path(ctx.db, hir::ModuleDef::from(strukt))
-                .filter(|it| it.len() > 1);
+            let path = module.find_use_path(ctx.db, hir::ModuleDef::from(strukt));
 
             acc.add_struct_literal(ctx, strukt, path, None);
         }
         hir::Adt::Union(un) if ctx.path_qual().is_none() => {
             let module = if let Some(module) = ctx.module { module } else { un.module(ctx.db) };
-            let path =
-                module.find_use_path(ctx.db, hir::ModuleDef::from(un)).filter(|it| it.len() > 1);
+            let path = module.find_use_path(ctx.db, hir::ModuleDef::from(un));
 
             acc.add_union_literal(ctx, un, path, None);
         }
@@ -135,7 +132,7 @@ fn baz() {
     #[test]
     fn literal_struct_completion_from_sub_modules() {
         check_edit(
-            "submod::Struct {…}",
+            "Struct {…}",
             r#"
 mod submod {
     pub struct Struct {
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index ed59eb6bd3..1ad233494a 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -58,7 +58,7 @@ pub(super) enum PathKind {
 
 #[derive(Debug)]
 pub(crate) struct PathCompletionCtx {
-    /// If this is a call with () already there (or {} in case of record patterns)
+    /// If this is a call with () already there
     pub(super) has_call_parens: bool,
     /// Whether this path stars with a `::`.
     pub(super) is_absolute_path: bool,
@@ -890,7 +890,6 @@ impl<'a> CompletionContext<'a> {
                         Some(PathKind::Pat)
                     },
                     ast::RecordPat(it) => {
-                        path_ctx.has_call_parens = true;
                         pat_ctx = Some(pattern_context_for(original_file, it.into()));
                         Some(PathKind::Pat)
                     },
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index 10211cd971..0ed346c55e 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -3,12 +3,13 @@
 
 pub(crate) mod macro_;
 pub(crate) mod function;
+pub(crate) mod enum_variant;
 pub(crate) mod const_;
 pub(crate) mod pattern;
 pub(crate) mod type_alias;
-pub(crate) mod variant;
+pub(crate) mod struct_literal;
+pub(crate) mod compound;
 pub(crate) mod union_literal;
-pub(crate) mod literal;
 
 use hir::{AsAssocItem, HasAttrs, HirDisplay, ScopeDef};
 use ide_db::{helpers::item_name, RootDatabase, SnippetCap, SymbolKind};
@@ -17,30 +18,22 @@ use syntax::{SmolStr, SyntaxKind, TextRange};
 use crate::{
     context::{PathCompletionCtx, PathKind},
     item::{CompletionRelevanceTypeMatch, ImportEdit},
-    render::{function::render_fn, literal::render_variant_lit, macro_::render_macro},
+    render::{enum_variant::render_variant, function::render_fn, macro_::render_macro},
     CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
 };
 /// Interface for data and methods required for items rendering.
-#[derive(Debug, Clone)]
+#[derive(Debug)]
 pub(crate) struct RenderContext<'a> {
     completion: &'a CompletionContext<'a>,
     is_private_editable: bool,
-    import_to_add: Option<ImportEdit>,
 }
 
 impl<'a> RenderContext<'a> {
-    pub(crate) fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> {
-        RenderContext { completion, is_private_editable: false, import_to_add: None }
-    }
-
-    pub(crate) fn private_editable(mut self, private_editable: bool) -> Self {
-        self.is_private_editable = private_editable;
-        self
-    }
-
-    pub(crate) fn import_to_add(mut self, import_to_add: Option<ImportEdit>) -> Self {
-        self.import_to_add = import_to_add;
-        self
+    pub(crate) fn new(
+        completion: &'a CompletionContext<'a>,
+        is_private_editable: bool,
+    ) -> RenderContext<'a> {
+        RenderContext { completion, is_private_editable }
     }
 
     fn snippet_cap(&self) -> Option<SnippetCap> {
@@ -146,14 +139,6 @@ pub(crate) fn render_resolution(
     render_resolution_(ctx, local_name, None, resolution)
 }
 
-pub(crate) fn render_resolution_simple(
-    ctx: RenderContext<'_>,
-    local_name: hir::Name,
-    resolution: ScopeDef,
-) -> CompletionItem {
-    render_resolution_simple_(ctx, local_name, None, resolution)
-}
-
 pub(crate) fn render_resolution_with_import(
     ctx: RenderContext<'_>,
     import_edit: ImportEdit,
@@ -177,42 +162,31 @@ fn render_resolution_(
     let _p = profile::span("render_resolution");
     use hir::ModuleDef::*;
 
-    match resolution {
-        ScopeDef::ModuleDef(Macro(mac)) => {
-            let ctx = ctx.import_to_add(import_to_add);
-            return render_macro(ctx, local_name, mac);
-        }
+    let db = ctx.db();
+
+    let kind = match resolution {
         ScopeDef::ModuleDef(Function(func)) => {
-            let ctx = ctx.import_to_add(import_to_add);
-            return render_fn(ctx, Some(local_name), func);
+            return render_fn(ctx, import_to_add, Some(local_name), func);
         }
         ScopeDef::ModuleDef(Variant(var)) if ctx.completion.pattern_ctx.is_none() => {
-            let ctx = ctx.clone().import_to_add(import_to_add.clone());
-            if let Some(item) = render_variant_lit(ctx, Some(local_name.clone()), var, None) {
-                return item;
+            return render_variant(ctx, import_to_add, Some(local_name), var, None);
+        }
+        ScopeDef::ModuleDef(Macro(mac)) => {
+            return render_macro(ctx, import_to_add, local_name, mac)
+        }
+        ScopeDef::Unknown => {
+            let mut item = CompletionItem::new(
+                CompletionItemKind::UnresolvedReference,
+                ctx.source_range(),
+                local_name.to_smol_str(),
+            );
+            if let Some(import_to_add) = import_to_add {
+                item.add_import(import_to_add);
             }
+            return item.build();
         }
-        _ => (),
-    }
-    render_resolution_simple_(ctx, local_name, import_to_add, resolution)
-}
-
-fn render_resolution_simple_(
-    ctx: RenderContext<'_>,
-    local_name: hir::Name,
-    import_to_add: Option<ImportEdit>,
-    resolution: ScopeDef,
-) -> CompletionItem {
-    let _p = profile::span("render_resolution");
-    use hir::ModuleDef::*;
 
-    let db = ctx.db();
-    let ctx = ctx.import_to_add(import_to_add);
-    let kind = match resolution {
-        ScopeDef::Unknown => CompletionItemKind::UnresolvedReference,
-        ScopeDef::ModuleDef(Function(_)) => CompletionItemKind::SymbolKind(SymbolKind::Function),
         ScopeDef::ModuleDef(Variant(_)) => CompletionItemKind::SymbolKind(SymbolKind::Variant),
-        ScopeDef::ModuleDef(Macro(_)) => CompletionItemKind::SymbolKind(SymbolKind::Macro),
         ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module),
         ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt {
             hir::Adt::Struct(_) => SymbolKind::Struct,
@@ -279,7 +253,7 @@ fn render_resolution_simple_(
     item.set_documentation(scope_def_docs(db, resolution))
         .set_deprecated(scope_def_is_deprecated(&ctx, resolution));
 
-    if let Some(import_to_add) = ctx.import_to_add {
+    if let Some(import_to_add) = import_to_add {
         item.add_import(import_to_add);
     }
     item.build()
@@ -603,7 +577,7 @@ fn main() { let _: m::Spam = S$0 }
                         kind: SymbolKind(
                             Variant,
                         ),
-                        lookup: "Spam::Bar(…)",
+                        lookup: "Spam::Bar",
                         detail: "m::Spam::Bar(i32)",
                         relevance: CompletionRelevance {
                             exact_name_match: false,
@@ -1182,7 +1156,6 @@ fn main() {
             "#,
             expect![[r#"
                 lc s [type+name+local]
-                st S [type]
                 st S []
                 fn main() []
                 fn foo(…) []
@@ -1199,7 +1172,6 @@ fn main() {
             "#,
             expect![[r#"
                 lc ssss [type+local]
-                st S [type]
                 st S []
                 fn main() []
                 fn foo(…) []
diff --git a/crates/ide_completion/src/render/variant.rs b/crates/ide_completion/src/render/compound.rs
similarity index 76%
rename from crates/ide_completion/src/render/variant.rs
rename to crates/ide_completion/src/render/compound.rs
index a37b4237c4..c7f3bd1f79 100644
--- a/crates/ide_completion/src/render/variant.rs
+++ b/crates/ide_completion/src/render/compound.rs
@@ -1,6 +1,6 @@
 //! Code common to structs, unions, and enum variants.
 
-use crate::context::CompletionContext;
+use crate::render::RenderContext;
 use hir::{db::HirDatabase, HasAttrs, HasVisibility, HirDisplay, StructKind};
 use ide_db::SnippetCap;
 use itertools::Itertools;
@@ -9,19 +9,19 @@ use syntax::SmolStr;
 /// A rendered struct, union, or enum variant, split into fields for actual
 /// auto-completion (`literal`, using `field: ()`) and display in the
 /// completions menu (`detail`, using `field: type`).
-pub(crate) struct RenderedLiteral {
+pub(crate) struct RenderedCompound {
     pub(crate) literal: String,
     pub(crate) detail: String,
 }
 
 /// Render a record type (or sub-type) to a `RenderedCompound`. Use `None` for
 /// the `name` argument for an anonymous type.
-pub(crate) fn render_record_lit(
+pub(crate) fn render_record(
     db: &dyn HirDatabase,
     snippet_cap: Option<SnippetCap>,
     fields: &[hir::Field],
-    path: &str,
-) -> RenderedLiteral {
+    name: Option<&str>,
+) -> RenderedCompound {
     let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| {
         if snippet_cap.is_some() {
             f(&format_args!("{}: ${{{}:()}}", field.name(db), idx + 1))
@@ -34,20 +34,20 @@ pub(crate) fn render_record_lit(
         f(&format_args!("{}: {}", field.name(db), field.ty(db).display(db)))
     });
 
-    RenderedLiteral {
-        literal: format!("{} {{ {} }}", path, completions),
-        detail: format!("{} {{ {} }}", path, types),
+    RenderedCompound {
+        literal: format!("{} {{ {} }}", name.unwrap_or(""), completions),
+        detail: format!("{} {{ {} }}", name.unwrap_or(""), types),
     }
 }
 
 /// Render a tuple type (or sub-type) to a `RenderedCompound`. Use `None` for
 /// the `name` argument for an anonymous type.
-pub(crate) fn render_tuple_lit(
+pub(crate) fn render_tuple(
     db: &dyn HirDatabase,
     snippet_cap: Option<SnippetCap>,
     fields: &[hir::Field],
-    path: &str,
-) -> RenderedLiteral {
+    name: Option<&str>,
+) -> RenderedCompound {
     let completions = fields.iter().enumerate().format_with(", ", |(idx, _), f| {
         if snippet_cap.is_some() {
             f(&format_args!("${{{}:()}}", idx + 1))
@@ -58,9 +58,9 @@ pub(crate) fn render_tuple_lit(
 
     let types = fields.iter().format_with(", ", |field, f| f(&field.ty(db).display(db)));
 
-    RenderedLiteral {
-        literal: format!("{}({})", path, completions),
-        detail: format!("{}({})", path, types),
+    RenderedCompound {
+        literal: format!("{}({})", name.unwrap_or(""), completions),
+        detail: format!("{}({})", name.unwrap_or(""), types),
     }
 }
 
@@ -68,20 +68,20 @@ pub(crate) fn render_tuple_lit(
 /// fields, plus a boolean for whether the list is comprehensive (contains no
 /// private fields and its item is not marked `#[non_exhaustive]`).
 pub(crate) fn visible_fields(
-    ctx: &CompletionContext,
+    ctx: &RenderContext<'_>,
     fields: &[hir::Field],
     item: impl HasAttrs,
 ) -> Option<(Vec<hir::Field>, bool)> {
-    let module = ctx.module?;
+    let module = ctx.completion.module?;
     let n_fields = fields.len();
     let fields = fields
         .iter()
-        .filter(|field| field.is_visible_from(ctx.db, module))
+        .filter(|field| field.is_visible_from(ctx.db(), module))
         .copied()
         .collect::<Vec<_>>();
 
     let fields_omitted =
-        n_fields - fields.len() > 0 || item.attrs(ctx.db).by_key("non_exhaustive").exists();
+        n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists();
     Some((fields, fields_omitted))
 }
 
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs
new file mode 100644
index 0000000000..5b485005d3
--- /dev/null
+++ b/crates/ide_completion/src/render/enum_variant.rs
@@ -0,0 +1,101 @@
+//! Renderer for `enum` variants.
+
+use hir::{HasAttrs, StructKind};
+use ide_db::SymbolKind;
+use syntax::SmolStr;
+
+use crate::{
+    item::{CompletionItem, ImportEdit},
+    render::{
+        compound::{format_literal_label, render_record, render_tuple, RenderedCompound},
+        compute_ref_match, compute_type_match, RenderContext,
+    },
+    CompletionRelevance,
+};
+
+pub(crate) fn render_variant(
+    ctx: RenderContext<'_>,
+    import_to_add: Option<ImportEdit>,
+    local_name: Option<hir::Name>,
+    variant: hir::Variant,
+    path: Option<hir::ModPath>,
+) -> CompletionItem {
+    let _p = profile::span("render_enum_variant");
+    render(ctx, local_name, variant, path, import_to_add)
+}
+
+fn render(
+    ctx @ RenderContext { completion, .. }: RenderContext<'_>,
+    local_name: Option<hir::Name>,
+    variant: hir::Variant,
+    path: Option<hir::ModPath>,
+    import_to_add: Option<ImportEdit>,
+) -> CompletionItem {
+    let db = completion.db;
+    let name = local_name.unwrap_or_else(|| variant.name(db));
+    let variant_kind = variant.kind(db);
+
+    let (qualified_name, short_qualified_name, qualified) = match path {
+        Some(path) => {
+            let short = hir::ModPath::from_segments(
+                hir::PathKind::Plain,
+                path.segments().iter().skip(path.segments().len().saturating_sub(2)).cloned(),
+            );
+            (path, short, true)
+        }
+        None => (name.clone().into(), name.into(), false),
+    };
+    let qualified_name = qualified_name.to_string();
+    let short_qualified_name: SmolStr = short_qualified_name.to_string().into();
+
+    let mut rendered = match variant_kind {
+        StructKind::Tuple => {
+            render_tuple(db, ctx.snippet_cap(), &variant.fields(db), Some(&qualified_name))
+        }
+        StructKind::Record => {
+            render_record(db, ctx.snippet_cap(), &variant.fields(db), Some(&qualified_name))
+        }
+        StructKind::Unit => {
+            RenderedCompound { literal: qualified_name.clone(), detail: qualified_name.clone() }
+        }
+    };
+
+    if ctx.snippet_cap().is_some() {
+        rendered.literal.push_str("$0");
+    }
+
+    let mut item = CompletionItem::new(
+        SymbolKind::Variant,
+        ctx.source_range(),
+        format_literal_label(&qualified_name, variant_kind),
+    );
+
+    item.set_documentation(variant.docs(db))
+        .set_deprecated(ctx.is_deprecated(variant))
+        .detail(rendered.detail);
+
+    match ctx.snippet_cap() {
+        Some(snippet_cap) => item.insert_snippet(snippet_cap, rendered.literal),
+        None => item.insert_text(rendered.literal),
+    };
+
+    if let Some(import_to_add) = import_to_add {
+        item.add_import(import_to_add);
+    }
+
+    if qualified {
+        item.lookup_by(short_qualified_name);
+    }
+
+    let ty = variant.parent_enum(completion.db).ty(completion.db);
+    item.set_relevance(CompletionRelevance {
+        type_match: compute_type_match(completion, &ty),
+        ..ctx.completion_relevance()
+    });
+
+    if let Some(ref_match) = compute_ref_match(completion, &ty) {
+        item.ref_match(ref_match);
+    }
+
+    item.build()
+}
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index 7df13988ad..2b9f82fc54 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -8,7 +8,7 @@ use syntax::SmolStr;
 
 use crate::{
     context::{CompletionContext, PathCompletionCtx, PathKind},
-    item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance},
+    item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit},
     patterns::ImmediateLocation,
     render::{compute_exact_name_match, compute_ref_match, compute_type_match, RenderContext},
 };
@@ -20,21 +20,23 @@ enum FuncKind {
 
 pub(crate) fn render_fn(
     ctx: RenderContext<'_>,
+    import_to_add: Option<ImportEdit>,
     local_name: Option<hir::Name>,
     func: hir::Function,
 ) -> CompletionItem {
     let _p = profile::span("render_fn");
-    render(ctx, local_name, func, FuncKind::Function)
+    render(ctx, local_name, func, FuncKind::Function, import_to_add)
 }
 
 pub(crate) fn render_method(
     ctx: RenderContext<'_>,
+    import_to_add: Option<ImportEdit>,
     receiver: Option<hir::Name>,
     local_name: Option<hir::Name>,
     func: hir::Function,
 ) -> CompletionItem {
     let _p = profile::span("render_method");
-    render(ctx, local_name, func, FuncKind::Method(receiver))
+    render(ctx, local_name, func, FuncKind::Method(receiver), import_to_add)
 }
 
 fn render(
@@ -42,6 +44,7 @@ fn render(
     local_name: Option<hir::Name>,
     func: hir::Function,
     func_kind: FuncKind,
+    import_to_add: Option<ImportEdit>,
 ) -> CompletionItem {
     let db = completion.db;
 
@@ -95,18 +98,17 @@ fn render(
         _ => (),
     }
 
-    match ctx.import_to_add {
-        Some(import_to_add) => {
-            item.add_import(import_to_add);
-        }
-        None => {
-            if let Some(actm) = func.as_assoc_item(db) {
-                if let Some(trt) = actm.containing_trait_or_trait_impl(db) {
-                    item.trait_name(trt.name(db).to_smol_str());
-                }
+    if import_to_add.is_none() {
+        if let Some(actm) = func.as_assoc_item(db) {
+            if let Some(trt) = actm.containing_trait_or_trait_impl(db) {
+                item.trait_name(trt.name(db).to_smol_str());
             }
         }
     }
+
+    if let Some(import_to_add) = import_to_add {
+        item.add_import(import_to_add);
+    }
     item.build()
 }
 
@@ -190,7 +192,7 @@ fn should_add_parens(ctx: &CompletionContext) -> bool {
         Some(PathCompletionCtx { kind: Some(PathKind::Expr), has_call_parens: true, .. }) => {
             return false
         }
-        Some(PathCompletionCtx { kind: Some(PathKind::Use | PathKind::Type), .. }) => {
+        Some(PathCompletionCtx { kind: Some(PathKind::Use), .. }) => {
             cov_mark::hit!(no_parens_in_use_item);
             return false;
         }
diff --git a/crates/ide_completion/src/render/literal.rs b/crates/ide_completion/src/render/literal.rs
deleted file mode 100644
index d91e80f90c..0000000000
--- a/crates/ide_completion/src/render/literal.rs
+++ /dev/null
@@ -1,174 +0,0 @@
-//! Renderer for `enum` variants.
-
-use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind};
-use ide_db::SymbolKind;
-
-use crate::{
-    context::{CompletionContext, PathCompletionCtx},
-    item::CompletionItem,
-    render::{
-        compute_ref_match, compute_type_match,
-        variant::{
-            format_literal_label, render_record_lit, render_tuple_lit, visible_fields,
-            RenderedLiteral,
-        },
-        RenderContext,
-    },
-    CompletionItemKind, CompletionRelevance,
-};
-
-pub(crate) fn render_variant_lit(
-    ctx: RenderContext<'_>,
-    local_name: Option<hir::Name>,
-    variant: hir::Variant,
-    path: Option<hir::ModPath>,
-) -> Option<CompletionItem> {
-    let _p = profile::span("render_enum_variant");
-    let db = ctx.db();
-
-    let name = local_name.unwrap_or_else(|| variant.name(db));
-    render(ctx, Variant::EnumVariant(variant), name, path)
-}
-
-pub(crate) fn render_struct_literal(
-    ctx: RenderContext<'_>,
-    strukt: hir::Struct,
-    path: Option<hir::ModPath>,
-    local_name: Option<hir::Name>,
-) -> Option<CompletionItem> {
-    let _p = profile::span("render_struct_literal");
-    let db = ctx.db();
-
-    let name = local_name.unwrap_or_else(|| strukt.name(db));
-    render(ctx, Variant::Struct(strukt), name, path)
-}
-
-fn render(
-    ctx @ RenderContext { completion, .. }: RenderContext<'_>,
-    thing: Variant,
-    name: hir::Name,
-    path: Option<hir::ModPath>,
-) -> Option<CompletionItem> {
-    if let Some(PathCompletionCtx { has_call_parens: true, .. }) = completion.path_context {
-        return None;
-    }
-    let db = completion.db;
-    let fields = thing.fields(completion)?;
-
-    let (qualified_name, short_qualified_name, qualified) = match path {
-        Some(path) => {
-            let short = hir::ModPath::from_segments(
-                hir::PathKind::Plain,
-                path.segments().iter().skip(path.segments().len().saturating_sub(2)).cloned(),
-            );
-            (path, short, true)
-        }
-        None => (name.clone().into(), name.into(), false),
-    };
-    let qualified_name = qualified_name.to_string();
-    let snippet_cap = ctx.snippet_cap();
-
-    let kind = thing.kind(db);
-    let mut rendered = match kind {
-        StructKind::Tuple => render_tuple_lit(db, snippet_cap, &fields, &qualified_name),
-        StructKind::Record => render_record_lit(db, snippet_cap, &fields, &qualified_name),
-        StructKind::Unit => {
-            RenderedLiteral { literal: qualified_name.clone(), detail: qualified_name.clone() }
-        }
-    };
-
-    if snippet_cap.is_some() {
-        rendered.literal.push_str("$0");
-    }
-
-    let mut item = CompletionItem::new(
-        CompletionItemKind::SymbolKind(thing.symbol_kind()),
-        ctx.source_range(),
-        format_literal_label(&qualified_name, kind),
-    );
-
-    item.detail(rendered.detail);
-
-    match snippet_cap {
-        Some(snippet_cap) => item.insert_snippet(snippet_cap, rendered.literal),
-        None => item.insert_text(rendered.literal),
-    };
-
-    if qualified {
-        item.lookup_by(format_literal_label(&short_qualified_name.to_string(), kind));
-    }
-    item.set_documentation(thing.docs(db)).set_deprecated(thing.is_deprecated(&ctx));
-
-    let ty = thing.ty(db);
-    item.set_relevance(CompletionRelevance {
-        type_match: compute_type_match(ctx.completion, &ty),
-        ..ctx.completion_relevance()
-    });
-    if let Some(ref_match) = compute_ref_match(completion, &ty) {
-        item.ref_match(ref_match);
-    }
-
-    if let Some(import_to_add) = ctx.import_to_add {
-        item.add_import(import_to_add);
-    }
-    Some(item.build())
-}
-
-#[derive(Clone, Copy)]
-enum Variant {
-    Struct(hir::Struct),
-    EnumVariant(hir::Variant),
-}
-
-impl Variant {
-    fn fields(self, ctx: &CompletionContext) -> Option<Vec<hir::Field>> {
-        let fields = match self {
-            Variant::Struct(it) => it.fields(ctx.db),
-            Variant::EnumVariant(it) => it.fields(ctx.db),
-        };
-        let (visible_fields, fields_omitted) = match self {
-            Variant::Struct(it) => visible_fields(ctx, &fields, it)?,
-            Variant::EnumVariant(it) => visible_fields(ctx, &fields, it)?,
-        };
-        if !fields_omitted {
-            Some(visible_fields)
-        } else {
-            None
-        }
-    }
-
-    fn kind(self, db: &dyn HirDatabase) -> StructKind {
-        match self {
-            Variant::Struct(it) => it.kind(db),
-            Variant::EnumVariant(it) => it.kind(db),
-        }
-    }
-
-    fn symbol_kind(self) -> SymbolKind {
-        match self {
-            Variant::Struct(_) => SymbolKind::Struct,
-            Variant::EnumVariant(_) => SymbolKind::Variant,
-        }
-    }
-
-    fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
-        match self {
-            Variant::Struct(it) => it.docs(db),
-            Variant::EnumVariant(it) => it.docs(db),
-        }
-    }
-
-    fn is_deprecated(self, ctx: &RenderContext<'_>) -> bool {
-        match self {
-            Variant::Struct(it) => ctx.is_deprecated(it),
-            Variant::EnumVariant(it) => ctx.is_deprecated(it),
-        }
-    }
-
-    fn ty(self, db: &dyn HirDatabase) -> hir::Type {
-        match self {
-            Variant::Struct(it) => it.ty(db),
-            Variant::EnumVariant(it) => it.parent_enum(db).ty(db),
-        }
-    }
-}
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index 9f848febeb..d3b0de429c 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -4,21 +4,27 @@ use hir::{Documentation, HirDisplay};
 use ide_db::SymbolKind;
 use syntax::SmolStr;
 
-use crate::{context::PathKind, item::CompletionItem, render::RenderContext};
+use crate::{
+    context::PathKind,
+    item::{CompletionItem, ImportEdit},
+    render::RenderContext,
+};
 
 pub(crate) fn render_macro(
     ctx: RenderContext<'_>,
+    import_to_add: Option<ImportEdit>,
     name: hir::Name,
     macro_: hir::Macro,
 ) -> CompletionItem {
     let _p = profile::span("render_macro");
-    render(ctx, name, macro_)
+    render(ctx, name, macro_, import_to_add)
 }
 
 fn render(
     ctx @ RenderContext { completion, .. }: RenderContext<'_>,
     name: hir::Name,
     macro_: hir::Macro,
+    import_to_add: Option<ImportEdit>,
 ) -> CompletionItem {
     let source_range = if completion.is_immediately_after_macro_bang() {
         cov_mark::hit!(completes_macro_call_if_cursor_at_bang_token);
@@ -46,7 +52,12 @@ fn render(
         .set_documentation(docs)
         .set_relevance(ctx.completion_relevance());
 
+    if let Some(import_to_add) = import_to_add {
+        item.add_import(import_to_add);
+    }
+
     let name = &*name;
+
     match ctx.snippet_cap() {
         Some(cap) if needs_bang && !completion.path_is_call() => {
             let snippet = format!("{}!{}$0{}", name, bra, ket);
@@ -62,9 +73,6 @@ fn render(
             item.insert_text(name);
         }
     };
-    if let Some(import_to_add) = ctx.import_to_add {
-        item.add_import(import_to_add);
-    }
 
     item.build()
 }
diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs
index efceb85718..c2d51b1252 100644
--- a/crates/ide_completion/src/render/pattern.rs
+++ b/crates/ide_completion/src/render/pattern.rs
@@ -1,13 +1,13 @@
 //! Renderer for patterns.
 
-use hir::{db::HirDatabase, HasAttrs, Name, StructKind};
+use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind};
 use ide_db::SnippetCap;
 use itertools::Itertools;
 use syntax::SmolStr;
 
 use crate::{
     context::{ParamKind, PatternContext},
-    render::{variant::visible_fields, RenderContext},
+    render::RenderContext,
     CompletionItem, CompletionItemKind,
 };
 
@@ -19,7 +19,7 @@ pub(crate) fn render_struct_pat(
     let _p = profile::span("render_struct_pat");
 
     let fields = strukt.fields(ctx.db());
-    let (visible_fields, fields_omitted) = visible_fields(ctx.completion, &fields, strukt)?;
+    let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, strukt)?;
 
     if visible_fields.is_empty() {
         // Matching a struct without matching its fields is pointless, unlike matching a Variant without its fields
@@ -36,14 +36,14 @@ pub(crate) fn render_variant_pat(
     ctx: RenderContext<'_>,
     variant: hir::Variant,
     local_name: Option<Name>,
-    path: Option<&hir::ModPath>,
+    path: Option<hir::ModPath>,
 ) -> Option<CompletionItem> {
     let _p = profile::span("render_variant_pat");
 
     let fields = variant.fields(ctx.db());
-    let (visible_fields, fields_omitted) = visible_fields(ctx.completion, &fields, variant)?;
+    let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, variant)?;
 
-    let name = match path {
+    let name = match &path {
         Some(path) => path.to_string().into(),
         None => local_name.unwrap_or_else(|| variant.name(ctx.db())).to_smol_str(),
     };
@@ -78,7 +78,9 @@ fn render_pat(
     fields_omitted: bool,
 ) -> Option<String> {
     let mut pat = match kind {
-        StructKind::Tuple => render_tuple_as_pat(ctx.snippet_cap(), fields, name, fields_omitted),
+        StructKind::Tuple if ctx.snippet_cap().is_some() => {
+            render_tuple_as_pat(fields, name, fields_omitted)
+        }
         StructKind::Record => {
             render_record_as_pat(ctx.db(), ctx.snippet_cap(), fields, name, fields_omitted)
         }
@@ -111,53 +113,49 @@ fn render_record_as_pat(
     fields_omitted: bool,
 ) -> String {
     let fields = fields.iter();
-    match snippet_cap {
-        Some(_) => {
-            format!(
-                "{name} {{ {}{} }}",
-                fields.enumerate().format_with(", ", |(idx, field), f| {
-                    f(&format_args!("{}${}", field.name(db), idx + 1))
-                }),
-                if fields_omitted { ", .." } else { "" },
-                name = name
-            )
-        }
-        None => {
-            format!(
-                "{name} {{ {}{} }}",
-                fields.map(|field| field.name(db)).format(", "),
-                if fields_omitted { ", .." } else { "" },
-                name = name
-            )
-        }
+    if snippet_cap.is_some() {
+        format!(
+            "{name} {{ {}{} }}",
+            fields
+                .enumerate()
+                .map(|(idx, field)| format!("{}${}", field.name(db), idx + 1))
+                .format(", "),
+            if fields_omitted { ", .." } else { "" },
+            name = name
+        )
+    } else {
+        format!(
+            "{name} {{ {}{} }}",
+            fields.map(|field| field.name(db)).format(", "),
+            if fields_omitted { ", .." } else { "" },
+            name = name
+        )
     }
 }
 
-fn render_tuple_as_pat(
-    snippet_cap: Option<SnippetCap>,
+fn render_tuple_as_pat(fields: &[hir::Field], name: &str, fields_omitted: bool) -> String {
+    format!(
+        "{name}({}{})",
+        fields.iter().enumerate().map(|(idx, _)| format!("${}", idx + 1)).format(", "),
+        if fields_omitted { ", .." } else { "" },
+        name = name
+    )
+}
+
+fn visible_fields(
+    ctx: &RenderContext<'_>,
     fields: &[hir::Field],
-    name: &str,
-    fields_omitted: bool,
-) -> String {
-    let fields = fields.iter();
-    match snippet_cap {
-        Some(_) => {
-            format!(
-                "{name}({}{})",
-                fields
-                    .enumerate()
-                    .format_with(", ", |(idx, _), f| { f(&format_args!("${}", idx + 1)) }),
-                if fields_omitted { ", .." } else { "" },
-                name = name
-            )
-        }
-        None => {
-            format!(
-                "{name}({}{})",
-                fields.enumerate().map(|(idx, _)| idx).format(", "),
-                if fields_omitted { ", .." } else { "" },
-                name = name
-            )
-        }
-    }
+    item: impl HasAttrs,
+) -> Option<(Vec<hir::Field>, bool)> {
+    let module = ctx.completion.module?;
+    let n_fields = fields.len();
+    let fields = fields
+        .iter()
+        .filter(|field| field.is_visible_from(ctx.db(), module))
+        .copied()
+        .collect::<Vec<_>>();
+
+    let fields_omitted =
+        n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists();
+    Some((fields, fields_omitted))
 }
diff --git a/crates/ide_completion/src/render/struct_literal.rs b/crates/ide_completion/src/render/struct_literal.rs
new file mode 100644
index 0000000000..a686be6691
--- /dev/null
+++ b/crates/ide_completion/src/render/struct_literal.rs
@@ -0,0 +1,91 @@
+//! Renderer for `struct` literal.
+
+use hir::{HasAttrs, Name, StructKind};
+use syntax::SmolStr;
+
+use crate::{
+    render::compound::{
+        format_literal_label, render_record, render_tuple, visible_fields, RenderedCompound,
+    },
+    render::RenderContext,
+    CompletionItem, CompletionItemKind,
+};
+
+pub(crate) fn render_struct_literal(
+    ctx: RenderContext<'_>,
+    strukt: hir::Struct,
+    path: Option<hir::ModPath>,
+    local_name: Option<Name>,
+) -> Option<CompletionItem> {
+    let _p = profile::span("render_struct_literal");
+
+    let fields = strukt.fields(ctx.db());
+    let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, strukt)?;
+
+    if fields_omitted {
+        // If some fields are private you can't make `struct` literal.
+        return None;
+    }
+
+    let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_smol_str();
+
+    let rendered = render_literal(&ctx, path, &name, strukt.kind(ctx.db()), &visible_fields)?;
+
+    Some(build_completion(&ctx, name, rendered, strukt.kind(ctx.db()), strukt))
+}
+
+fn build_completion(
+    ctx: &RenderContext<'_>,
+    name: SmolStr,
+    rendered: RenderedCompound,
+    kind: StructKind,
+    def: impl HasAttrs + Copy,
+) -> CompletionItem {
+    let mut item = CompletionItem::new(
+        CompletionItemKind::Snippet,
+        ctx.source_range(),
+        format_literal_label(&name, kind),
+    );
+
+    item.set_documentation(ctx.docs(def))
+        .set_deprecated(ctx.is_deprecated(def))
+        .detail(&rendered.detail)
+        .set_relevance(ctx.completion_relevance());
+    match ctx.snippet_cap() {
+        Some(snippet_cap) => item.insert_snippet(snippet_cap, rendered.literal),
+        None => item.insert_text(rendered.literal),
+    };
+    item.build()
+}
+
+fn render_literal(
+    ctx: &RenderContext<'_>,
+    path: Option<hir::ModPath>,
+    name: &str,
+    kind: StructKind,
+    fields: &[hir::Field],
+) -> Option<RenderedCompound> {
+    let path_string;
+
+    let qualified_name = if let Some(path) = path {
+        path_string = path.to_string();
+        &path_string
+    } else {
+        name
+    };
+
+    let mut rendered = match kind {
+        StructKind::Tuple if ctx.snippet_cap().is_some() => {
+            render_tuple(ctx.db(), ctx.snippet_cap(), fields, Some(qualified_name))
+        }
+        StructKind::Record => {
+            render_record(ctx.db(), ctx.snippet_cap(), fields, Some(qualified_name))
+        }
+        _ => return None,
+    };
+
+    if ctx.snippet_cap().is_some() {
+        rendered.literal.push_str("$0");
+    }
+    Some(rendered)
+}
diff --git a/crates/ide_completion/src/render/union_literal.rs b/crates/ide_completion/src/render/union_literal.rs
index aafedaf5aa..80499e102b 100644
--- a/crates/ide_completion/src/render/union_literal.rs
+++ b/crates/ide_completion/src/render/union_literal.rs
@@ -1,12 +1,11 @@
 //! Renderer for `union` literals.
 
 use hir::{HirDisplay, Name, StructKind};
-use ide_db::SymbolKind;
 use itertools::Itertools;
 
 use crate::{
     render::{
-        variant::{format_literal_label, visible_fields},
+        compound::{format_literal_label, visible_fields},
         RenderContext,
     },
     CompletionItem, CompletionItemKind,
@@ -26,13 +25,13 @@ pub(crate) fn render_union_literal(
     };
 
     let mut item = CompletionItem::new(
-        CompletionItemKind::SymbolKind(SymbolKind::Union),
+        CompletionItemKind::Snippet,
         ctx.source_range(),
         format_literal_label(&name, StructKind::Record),
     );
 
     let fields = un.fields(ctx.db());
-    let (fields, fields_omitted) = visible_fields(ctx.completion, &fields, un)?;
+    let (fields, fields_omitted) = visible_fields(&ctx, &fields, un)?;
 
     if fields.is_empty() {
         return None;
diff --git a/crates/ide_db/Cargo.toml b/crates/ide_db/Cargo.toml
index c71a213c40..26b51739cf 100644
--- a/crates/ide_db/Cargo.toml
+++ b/crates/ide_db/Cargo.toml
@@ -35,5 +35,5 @@ limit = { path = "../limit", version = "0.0.0" }
 [dev-dependencies]
 test_utils = { path = "../test_utils" }
 sourcegen = { path = "../sourcegen" }
-xshell = "0.2.0"
+xshell = "0.2.1"
 expect-test = "1.2.0-pre.1"
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 68d728b41f..079e56c9fa 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -75,7 +75,7 @@ jemallocator = { version = "0.4.1", package = "tikv-jemallocator", optional = tr
 [dev-dependencies]
 expect-test = "1.2.0-pre.1"
 jod-thread = "0.1.0"
-xshell = "0.2.0"
+xshell = "0.2.1"
 
 test_utils = { path = "../test_utils" }
 sourcegen = { path = "../sourcegen" }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 3d5737316b..a93e18a9b0 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -244,6 +244,8 @@ config_data! {
 
         /// Whether to render trailing colons for parameter hints, and trailing colons for parameter hints.
         inlayHints_renderColons: bool               = "true",
+        /// Whether to show inlay type hints for method chains.
+        inlayHints_chainingHints: bool              = "true",
         /// Maximum length for inlay hints. Set to null to have an unlimited length.
         inlayHints_maxLength: Option<usize>         = "25",
         /// Whether to show function parameter name inlay hints at the call
@@ -251,10 +253,6 @@ config_data! {
         inlayHints_parameterHints: bool             = "true",
         /// Whether to show inlay type hints for variables.
         inlayHints_typeHints: bool                  = "true",
-        /// Whether to show inlay type hints for method chains.
-        inlayHints_chainingHints: bool              = "true",
-        /// Whether to show inlay type hints for return types of closures with blocks.
-        inlayHints_closureReturnTypeHints: bool     = "false",
         /// Whether to hide inlay hints for constructors.
         inlayHints_hideNamedConstructorHints: bool  = "false",
 
@@ -854,7 +852,6 @@ impl Config {
             type_hints: self.data.inlayHints_typeHints,
             parameter_hints: self.data.inlayHints_parameterHints,
             chaining_hints: self.data.inlayHints_chainingHints,
-            closure_return_type_hints: self.data.inlayHints_closureReturnTypeHints,
             hide_named_constructor_hints: self.data.inlayHints_hideNamedConstructorHints,
             max_length: self.data.inlayHints_maxLength,
         }
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index e9e8bdb6c5..6a1c2f56ca 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -422,31 +422,26 @@ pub(crate) fn inlay_hint(
         label: lsp_ext::InlayHintLabel::String(match inlay_hint.kind {
             InlayKind::ParameterHint if render_colons => format!("{}:", inlay_hint.label),
             InlayKind::TypeHint if render_colons => format!(": {}", inlay_hint.label),
-            InlayKind::ClosureReturnTypeHint => format!(" -> {}", inlay_hint.label),
             _ => inlay_hint.label.to_string(),
         }),
         position: match inlay_hint.kind {
             InlayKind::ParameterHint => position(line_index, inlay_hint.range.start()),
-            InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => {
+            InlayKind::TypeHint | InlayKind::ChainingHint => {
                 position(line_index, inlay_hint.range.end())
             }
         },
         kind: match inlay_hint.kind {
             InlayKind::ParameterHint => Some(lsp_ext::InlayHintKind::PARAMETER),
-            InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => {
-                Some(lsp_ext::InlayHintKind::TYPE)
-            }
+            InlayKind::TypeHint | InlayKind::ChainingHint => Some(lsp_ext::InlayHintKind::TYPE),
         },
         tooltip: None,
         padding_left: Some(match inlay_hint.kind {
             InlayKind::TypeHint => !render_colons,
-            InlayKind::ParameterHint | InlayKind::ClosureReturnTypeHint => false,
+            InlayKind::ParameterHint => false,
             InlayKind::ChainingHint => true,
         }),
         padding_right: Some(match inlay_hint.kind {
-            InlayKind::TypeHint | InlayKind::ChainingHint | InlayKind::ClosureReturnTypeHint => {
-                false
-            }
+            InlayKind::TypeHint | InlayKind::ChainingHint => false,
             InlayKind::ParameterHint => true,
         }),
     }
diff --git a/crates/sourcegen/Cargo.toml b/crates/sourcegen/Cargo.toml
index 10587d913d..57e183af3f 100644
--- a/crates/sourcegen/Cargo.toml
+++ b/crates/sourcegen/Cargo.toml
@@ -10,4 +10,4 @@ rust-version = "1.57"
 doctest = false
 
 [dependencies]
-xshell = "0.2.0"
+xshell = "0.2.1"
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index d11d658a27..a69a77c7ee 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -368,28 +368,18 @@ pub fn expr_empty_block() -> ast::Expr {
 pub fn expr_path(path: ast::Path) -> ast::Expr {
     expr_from_text(&path.to_string())
 }
-pub fn expr_continue(label: Option<ast::Lifetime>) -> ast::Expr {
-    match label {
-        Some(label) => expr_from_text(&format!("continue {}", label)),
-        None => expr_from_text("continue"),
-    }
+pub fn expr_continue() -> ast::Expr {
+    expr_from_text("continue")
 }
 // Consider `op: SyntaxKind` instead for nicer syntax at the call-site?
 pub fn expr_bin_op(lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::Expr {
     expr_from_text(&format!("{} {} {}", lhs, op, rhs))
 }
-pub fn expr_break(label: Option<ast::Lifetime>, expr: Option<ast::Expr>) -> ast::Expr {
-    let mut s = String::from("break");
-
-    if let Some(label) = label {
-        format_to!(s, " {}", label);
-    }
-
-    if let Some(expr) = expr {
-        format_to!(s, " {}", expr);
+pub fn expr_break(expr: Option<ast::Expr>) -> ast::Expr {
+    match expr {
+        Some(expr) => expr_from_text(&format!("break {}", expr)),
+        None => expr_from_text("break"),
     }
-
-    expr_from_text(&s)
 }
 pub fn expr_return(expr: Option<ast::Expr>) -> ast::Expr {
     match expr {
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 5793b4c057..dd819abdd2 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -352,6 +352,11 @@ Whether to show `Run` action. Only applies when
 --
 Whether to render trailing colons for parameter hints, and trailing colons for parameter hints.
 --
+[[rust-analyzer.inlayHints.chainingHints]]rust-analyzer.inlayHints.chainingHints (default: `true`)::
++
+--
+Whether to show inlay type hints for method chains.
+--
 [[rust-analyzer.inlayHints.maxLength]]rust-analyzer.inlayHints.maxLength (default: `25`)::
 +
 --
@@ -368,16 +373,6 @@ site.
 --
 Whether to show inlay type hints for variables.
 --
-[[rust-analyzer.inlayHints.chainingHints]]rust-analyzer.inlayHints.chainingHints (default: `true`)::
-+
---
-Whether to show inlay type hints for method chains.
---
-[[rust-analyzer.inlayHints.closureReturnTypeHints]]rust-analyzer.inlayHints.closureReturnTypeHints (default: `false`)::
-+
---
-Whether to show inlay type hints for return types of closures with blocks.
---
 [[rust-analyzer.inlayHints.hideNamedConstructorHints]]rust-analyzer.inlayHints.hideNamedConstructorHints (default: `false`)::
 +
 --
diff --git a/editors/code/package.json b/editors/code/package.json
index 8315819774..6ccd6af80c 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -771,6 +771,11 @@
                     "default": true,
                     "type": "boolean"
                 },
+                "rust-analyzer.inlayHints.chainingHints": {
+                    "markdownDescription": "Whether to show inlay type hints for method chains.",
+                    "default": true,
+                    "type": "boolean"
+                },
                 "rust-analyzer.inlayHints.maxLength": {
                     "markdownDescription": "Maximum length for inlay hints. Set to null to have an unlimited length.",
                     "default": 25,
@@ -790,16 +795,6 @@
                     "default": true,
                     "type": "boolean"
                 },
-                "rust-analyzer.inlayHints.chainingHints": {
-                    "markdownDescription": "Whether to show inlay type hints for method chains.",
-                    "default": true,
-                    "type": "boolean"
-                },
-                "rust-analyzer.inlayHints.closureReturnTypeHints": {
-                    "markdownDescription": "Whether to show inlay type hints for return types of closures with blocks.",
-                    "default": false,
-                    "type": "boolean"
-                },
                 "rust-analyzer.inlayHints.hideNamedConstructorHints": {
                     "markdownDescription": "Whether to hide inlay hints for constructors.",
                     "default": false,
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 5d3ef40459..cdbaa67fe2 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -103,7 +103,6 @@ export class Config {
             typeHints: this.get<boolean>("inlayHints.typeHints"),
             parameterHints: this.get<boolean>("inlayHints.parameterHints"),
             chainingHints: this.get<boolean>("inlayHints.chainingHints"),
-            closureReturnTypeHints: this.get<boolean>("inlayHints.closureReturnTypeHints"),
             hideNamedConstructorHints: this.get<boolean>("inlayHints.hideNamedConstructorHints"),
             smallerHints: this.get<boolean>("inlayHints.smallerHints"),
             maxLength: this.get<null | number>("inlayHints.maxLength"),
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts
index 3f7ddedddf..441370a677 100644
--- a/editors/code/src/inlay_hints.ts
+++ b/editors/code/src/inlay_hints.ts
@@ -14,8 +14,7 @@ export function activateInlayHints(ctx: Ctx) {
 
             const anyEnabled = ctx.config.inlayHints.typeHints
                 || ctx.config.inlayHints.parameterHints
-                || ctx.config.inlayHints.chainingHints
-                || ctx.config.inlayHints.closureReturnTypeHints;
+                || ctx.config.inlayHints.chainingHints;
             const enabled = ctx.config.inlayHints.enable && anyEnabled;
             if (!enabled) return;
 
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index d9adf88c02..de6a102427 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -10,6 +10,6 @@ rust-version = "1.57"
 anyhow = "1.0.26"
 flate2 = "1.0"
 write-json = "0.1.0"
-xshell = "0.2.0"
+xshell = "0.2.1"
 xflags = "0.2.1"
 # Avoid adding more dependencies to this crate
diff --git a/xtask/src/metrics.rs b/xtask/src/metrics.rs
index 27dae5a449..b683b32e6d 100644
--- a/xtask/src/metrics.rs
+++ b/xtask/src/metrics.rs
@@ -17,7 +17,7 @@ impl flags::Metrics {
     pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> {
         let mut metrics = Metrics::new(sh)?;
         if !self.dry_run {
-            let _ = sh.remove_path("./target/release");
+            sh.remove_path("./target/release")?;
         }
         if !Path::new("./target/rustc-perf").exists() {
             sh.create_dir("./target/rustc-perf")?;
@@ -51,14 +51,10 @@ impl flags::Metrics {
                 "git clone --depth 1 https://{metrics_token}@github.com/rust-analyzer/metrics.git"
             )
             .run()?;
-
-            {
-                let mut file =
-                    fs::File::options().append(true).open("target/metrics/metrics.json")?;
-                writeln!(file, "{}", metrics.json())?;
-            }
-
             let _d = sh.push_dir("metrics");
+
+            let mut file = fs::File::options().append(true).open("metrics.json")?;
+            writeln!(file, "{}", metrics.json())?;
             cmd!(sh, "git add .").run()?;
             cmd!(sh, "git -c user.name=Bot -c user.email=dummy@example.com commit --message 📈")
                 .run()?;


Test patch:
diff --git a/crates/ide_completion/src/tests/pattern.rs b/crates/ide_completion/src/tests/pattern.rs
index 50d5e01979..891c1346df 100644
--- a/crates/ide_completion/src/tests/pattern.rs
+++ b/crates/ide_completion/src/tests/pattern.rs
@@ -150,7 +150,6 @@ fn foo() {
             bn Tuple             Tuple($1)$0
             st Tuple
             ev Variant
-            md module
             en SingleVariantEnum
             st Unit
             ma makro!(…)         macro_rules! makro
@@ -172,7 +171,6 @@ fn foo(a$0) {
             st Record
             bn Tuple     Tuple($1): Tuple$0
             st Tuple
-            md module
             st Unit
             ma makro!(…) macro_rules! makro
         "#]],
@@ -189,7 +187,6 @@ fn foo(a$0: Tuple) {
             st Record
             bn Tuple     Tuple($1)$0
             st Tuple
-            md module
             st Unit
             ma makro!(…) macro_rules! makro
         "#]],
@@ -231,6 +228,7 @@ fn foo() {
         expect![[r#"
             kw ref
             kw mut
+            ev E::X  E::X
             en E
             ma m!(…) macro_rules! m
         "#]],
@@ -380,67 +378,3 @@ fn foo() {
         "#]],
     )
 }
-
-#[test]
-fn completes_no_delims_if_existing() {
-    check_empty(
-        r#"
-struct Bar(u32);
-fn foo() {
-    match Bar(0) {
-        B$0(b) => {}
-    }
-}
-"#,
-        expect![[r#"
-            kw self::
-            kw super::
-            kw crate::
-        "#]],
-    );
-    check_empty(
-        r#"
-struct Foo { bar: u32 }
-fn foo() {
-    match Foo { bar: 0 } {
-        F$0 { bar } => {}
-    }
-}
-"#,
-        expect![[r#"
-            kw return
-            kw self
-            kw super
-            kw crate
-            st Foo
-            fn foo()  fn()
-            bt u32
-        "#]],
-    );
-    check_empty(
-        r#"
-enum Enum {
-    TupleVariant(u32)
-}
-fn foo() {
-    match Enum::TupleVariant(0) {
-        Enum::T$0(b) => {}
-    }
-}
-"#,
-        expect![[r#""#]],
-    );
-    check_empty(
-        r#"
-enum Enum {
-    RecordVariant { field: u32 }
-}
-fn foo() {
-    match (Enum::RecordVariant { field: 0 }) {
-        Enum::RecordV$0 { field } => {}
-    }
-}
-"#,
-        expect![[r#""#]],
-    );
-}
diff --git a/crates/ide_completion/src/tests/record.rs b/crates/ide_completion/src/tests/record.rs
index 0322ecbe39..5e9367960f 100644
--- a/crates/ide_completion/src/tests/record.rs
+++ b/crates/ide_completion/src/tests/record.rs
@@ -166,7 +166,7 @@ fn main() {
             kw true
             kw false
             kw return
-            st Foo {…}              Foo { foo1: u32, foo2: u32 }
+            sn Foo {…}              Foo { foo1: u32, foo2: u32 }
             fd ..Default::default()
             fd foo1                 u32
             fd foo2                 u32