Repository: rust-lang/rust-analyzer
Pull request #12576 (2022-06-18 06:28:09)

feat: add fold range for multi line match arm list
fix: #11893 


Patch:
diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs
index 608a74dc15..15e431f9c5 100644
--- a/crates/ide-completion/src/completions/flyimport.rs
+++ b/crates/ide-completion/src/completions/flyimport.rs
@@ -123,7 +123,7 @@ pub(crate) fn import_on_the_fly_path(
                 | PathKind::Type { .. }
                 | PathKind::Attr { .. }
                 | PathKind::Derive { .. }
-                | PathKind::Pat { .. }),
+                | PathKind::Pat),
             qualified,
             ..
         } => (Some(kind), qualified),
@@ -183,7 +183,7 @@ pub(crate) fn import_on_the_fly_pat(
         return None;
     }
     let kind = match pat_ctx {
-        PatternContext { record_pat: None, .. } => PathKind::Pat { pat_ctx: pat_ctx.clone() },
+        PatternContext { record_pat: None, .. } => PathKind::Pat,
         _ => return None,
     };
 
@@ -229,17 +229,15 @@ fn import_on_the_fly(
                 PathKind::Expr { .. }
                 | PathKind::Type { .. }
                 | PathKind::Item { .. }
-                | PathKind::Pat { .. },
+                | PathKind::Pat,
                 ItemInNs::Macros(mac),
             ) => mac.is_fn_like(ctx.db),
             (PathKind::Item { .. }, _) => true,
 
             (PathKind::Expr { .. }, ItemInNs::Types(_) | ItemInNs::Values(_)) => true,
 
-            (PathKind::Pat { .. }, ItemInNs::Types(_)) => true,
-            (PathKind::Pat { .. }, ItemInNs::Values(def)) => {
-                matches!(def, hir::ModuleDef::Const(_))
-            }
+            (PathKind::Pat, ItemInNs::Types(_)) => true,
+            (PathKind::Pat, ItemInNs::Values(def)) => matches!(def, hir::ModuleDef::Const(_)),
 
             (PathKind::Type { location }, ItemInNs::Types(ty)) => {
                 if matches!(location, TypeLocation::TypeBound) {
diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs
index c0c29d4cd2..701985d507 100644
--- a/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -95,12 +95,12 @@ pub(crate) fn complete_trait_impl_name_ref(
         NameRefContext {
             nameref,
             kind:
-                NameRefKind::Path(
+                Some(NameRefKind::Path(
                     path_ctx @ PathCompletionCtx {
                         kind: PathKind::Item { kind: ItemListKind::TraitImpl },
                         ..
                     },
-                ),
+                )),
         } if path_ctx.is_trivial_path() => complete_trait_impl(
             acc,
             ctx,
diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs
index dd08ef703e..1ecae1c484 100644
--- a/crates/ide-completion/src/completions/pattern.rs
+++ b/crates/ide-completion/src/completions/pattern.rs
@@ -107,7 +107,7 @@ pub(crate) fn pattern_path_completion(
     ctx: &CompletionContext,
     PathCompletionCtx { qualified, kind, .. }: &PathCompletionCtx,
 ) {
-    if !matches!(kind, PathKind::Pat { .. }) {
+    if !matches!(kind, PathKind::Pat) {
         return;
     }
     match qualified {
diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs
index 2cdd93f95b..9ffcee400d 100644
--- a/crates/ide-completion/src/completions/use_.rs
+++ b/crates/ide-completion/src/completions/use_.rs
@@ -20,12 +20,12 @@ pub(crate) fn complete_use_tree(
     let (qualified, name_ref, use_tree_parent) = match name_ref_ctx {
         NameRefContext {
             kind:
-                NameRefKind::Path(PathCompletionCtx {
+                Some(NameRefKind::Path(PathCompletionCtx {
                     kind: PathKind::Use,
                     qualified,
                     use_tree_parent,
                     ..
-                }),
+                })),
             nameref,
             ..
         } => (qualified, nameref, use_tree_parent),
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index d73cb6034d..5c408c0cc0 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -85,7 +85,7 @@ impl PathCompletionCtx {
 }
 
 /// The kind of path we are completing right now.
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub(super) enum PathKind {
     Expr {
         in_block_expr: bool,
@@ -110,9 +110,7 @@ pub(super) enum PathKind {
     Item {
         kind: ItemListKind,
     },
-    Pat {
-        pat_ctx: PatternContext,
-    },
+    Pat,
     Vis {
         has_in_token: bool,
     },
@@ -166,7 +164,7 @@ pub(super) enum Qualified {
 }
 
 /// The state of the pattern we are completing.
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug)]
 pub(super) struct PatternContext {
     pub(super) refutability: PatternRefutability,
     pub(super) param_ctx: Option<(ast::ParamList, ast::Param, ParamKind)>,
@@ -210,7 +208,7 @@ pub(super) enum NameKind {
     ConstParam,
     Enum,
     Function,
-    IdentPat(PatternContext),
+    IdentPat,
     MacroDef,
     MacroRules,
     /// Fake node
@@ -232,7 +230,8 @@ pub(super) enum NameKind {
 pub(super) struct NameRefContext {
     /// NameRef syntax in the original file
     pub(super) nameref: Option<ast::NameRef>,
-    pub(super) kind: NameRefKind,
+    // FIXME: This shouldn't be an Option
+    pub(super) kind: Option<NameRefKind>,
 }
 
 /// The kind of the NameRef we are completing.
@@ -244,7 +243,6 @@ pub(super) enum NameRefKind {
     Keyword(ast::Item),
     /// The record expression this nameref is a field of
     RecordExpr(ast::RecordExpr),
-    Pattern(PatternContext),
 }
 
 /// The identifier we are currently completing.
@@ -332,6 +330,7 @@ pub(crate) struct CompletionContext<'a> {
 
     // We might wanna split these out of CompletionContext
     pub(super) ident_ctx: IdentContext,
+    pub(super) pattern_ctx: Option<PatternContext>,
     pub(super) qualifier_ctx: QualifierCtx,
 
     pub(super) locals: FxHashMap<Name, Local>,
@@ -506,6 +505,7 @@ impl<'a> CompletionContext<'a> {
             previous_token: None,
             // dummy value, will be overwritten
             ident_ctx: IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: None },
+            pattern_ctx: None,
             qualifier_ctx: Default::default(),
             locals,
         };
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index fc6ee70a0b..0efeb85207 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -343,9 +343,9 @@ impl<'a> CompletionContext<'a> {
                 find_node_at_offset(&file_with_fake_ident, offset)
             {
                 let parent = name_ref.syntax().parent()?;
-                let (mut nameref_ctx, _) =
-                    Self::classify_name_ref(&self.sema, &original_file, name_ref, parent)?;
-                if let NameRefKind::Path(path_ctx) = &mut nameref_ctx.kind {
+                let (mut nameref_ctx, _, _) =
+                    Self::classify_name_ref(&self.sema, &original_file, name_ref, parent);
+                if let Some(NameRefKind::Path(path_ctx)) = &mut nameref_ctx.kind {
                     path_ctx.kind = PathKind::Derive {
                         existing_derives: self
                             .sema
@@ -427,14 +427,19 @@ impl<'a> CompletionContext<'a> {
             }
             ast::NameLike::NameRef(name_ref) => {
                 let parent = name_ref.syntax().parent()?;
-                let (nameref_ctx, qualifier_ctx) =
-                    Self::classify_name_ref(&self.sema, &original_file, name_ref, parent.clone())?;
+                let (nameref_ctx, pat_ctx, qualifier_ctx) =
+                    Self::classify_name_ref(&self.sema, &original_file, name_ref, parent.clone());
 
+                if !matches!(nameref_ctx.kind, Some(NameRefKind::Path(_))) {
+                    // FIXME: Pattern context should probably be part of ident_ctx
+                    self.pattern_ctx = pat_ctx;
+                }
                 self.qualifier_ctx = qualifier_ctx;
                 self.ident_ctx = IdentContext::NameRef(nameref_ctx);
             }
             ast::NameLike::Name(name) => {
-                let name_ctx = Self::classify_name(&self.sema, original_file, name)?;
+                let (name_ctx, pat_ctx) = Self::classify_name(&self.sema, original_file, name)?;
+                self.pattern_ctx = pat_ctx;
                 self.ident_ctx = IdentContext::Name(name_ctx);
             }
         }
@@ -472,8 +477,9 @@ impl<'a> CompletionContext<'a> {
         _sema: &Semantics<RootDatabase>,
         original_file: &SyntaxNode,
         name: ast::Name,
-    ) -> Option<NameContext> {
+    ) -> Option<(NameContext, Option<PatternContext>)> {
         let parent = name.syntax().parent()?;
+        let mut pat_ctx = None;
         let kind = match_ast! {
             match parent {
                 ast::Const(_) => NameKind::Const,
@@ -481,12 +487,15 @@ impl<'a> CompletionContext<'a> {
                 ast::Enum(_) => NameKind::Enum,
                 ast::Fn(_) => NameKind::Function,
                 ast::IdentPat(bind_pat) => {
-                    let mut pat_ctx = pattern_context_for(original_file, bind_pat.into());
-                    if let Some(record_field) = ast::RecordPatField::for_field_name(&name) {
-                        pat_ctx.record_pat = find_node_in_file_compensated(original_file, &record_field.parent_record_pat());
-                    }
+                    pat_ctx = Some({
+                        let mut pat_ctx = pattern_context_for(original_file, bind_pat.into());
+                        if let Some(record_field) = ast::RecordPatField::for_field_name(&name) {
+                            pat_ctx.record_pat = find_node_in_file_compensated(original_file, &record_field.parent_record_pat());
+                        }
+                        pat_ctx
+                    });
 
-                    NameKind::IdentPat(pat_ctx)
+                    NameKind::IdentPat
                 },
                 ast::MacroDef(_) => NameKind::MacroDef,
                 ast::MacroRules(_) => NameKind::MacroRules,
@@ -505,7 +514,7 @@ impl<'a> CompletionContext<'a> {
             }
         };
         let name = find_node_at_offset(&original_file, name.syntax().text_range().start());
-        Some(NameContext { name, kind })
+        Some((NameContext { name, kind }, pat_ctx))
     }
 
     fn classify_name_ref(
@@ -513,19 +522,20 @@ impl<'a> CompletionContext<'a> {
         original_file: &SyntaxNode,
         name_ref: ast::NameRef,
         parent: SyntaxNode,
-    ) -> Option<(NameRefContext, QualifierCtx)> {
+    ) -> (NameRefContext, Option<PatternContext>, QualifierCtx) {
         let nameref = find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
 
-        let make_res =
-            |kind| (NameRefContext { nameref: nameref.clone(), kind }, Default::default());
+        let mut res = (NameRefContext { nameref, kind: None }, None, QualifierCtx::default());
+        let (nameref_ctx, pattern_ctx, qualifier_ctx) = &mut res;
 
         if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) {
-            return find_node_in_file_compensated(original_file, &record_field.parent_record_lit())
-                .map(NameRefKind::RecordExpr)
-                .map(make_res);
+            nameref_ctx.kind =
+                find_node_in_file_compensated(original_file, &record_field.parent_record_lit())
+                    .map(NameRefKind::RecordExpr);
+            return res;
         }
         if let Some(record_field) = ast::RecordPatField::for_field_name_ref(&name_ref) {
-            let kind = NameRefKind::Pattern(PatternContext {
+            *pattern_ctx = Some(PatternContext {
                 param_ctx: None,
                 has_type_ascription: false,
                 ref_token: None,
@@ -539,7 +549,7 @@ impl<'a> CompletionContext<'a> {
                     record_field.parent_record_pat().clone().into(),
                 )
             });
-            return Some(make_res(kind));
+            return res;
         }
 
         let segment = match_ast! {
@@ -554,23 +564,23 @@ impl<'a> CompletionContext<'a> {
                         },
                         _ => false,
                     };
-                    let kind = NameRefKind::DotAccess(DotAccess {
+                    nameref_ctx.kind = Some(NameRefKind::DotAccess(DotAccess {
                         receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)),
                         kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal },
                         receiver
-                    });
-                    return Some(make_res(kind));
+                    }));
+                    return res;
                 },
                 ast::MethodCallExpr(method) => {
                     let receiver = find_opt_node_in_file(original_file, method.receiver());
-                    let kind = NameRefKind::DotAccess(DotAccess {
+                    nameref_ctx.kind = Some(NameRefKind::DotAccess(DotAccess {
                         receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)),
                         kind: DotAccessKind::Method { has_parens: method.arg_list().map_or(false, |it| it.l_paren_token().is_some()) },
                         receiver
-                    });
-                    return Some(make_res(kind));
+                    }));
+                    return res;
                 },
-                _ => return None,
+                _ => return res,
             }
         };
 
@@ -745,47 +755,52 @@ impl<'a> CompletionContext<'a> {
         };
 
         // Infer the path kind
-        let parent = path.syntax().parent()?;
-        let kind = match_ast! {
-                match parent {
-                    ast::PathType(it) => make_path_kind_type(it.into()),
+        let kind = path.syntax().parent().and_then(|it| {
+            match_ast! {
+                match it {
+                    ast::PathType(it) => Some(make_path_kind_type(it.into())),
                     ast::PathExpr(it) => {
                         if let Some(p) = it.syntax().parent() {
                             if ast::ExprStmt::can_cast(p.kind()) {
                                 if let Some(kind) = inbetween_body_and_decl_check(p) {
-                                    return Some(make_res(NameRefKind::Keyword(kind)));
+                                    nameref_ctx.kind = Some(NameRefKind::Keyword(kind));
+                                    return None;
                                 }
                             }
                         }
 
                         path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
 
-                        make_path_kind_expr(it.into())
+                        Some(make_path_kind_expr(it.into()))
                     },
                     ast::TupleStructPat(it) => {
                         path_ctx.has_call_parens = true;
-                        PathKind::Pat { pat_ctx: pattern_context_for(original_file, it.into())}
+                        *pattern_ctx = Some(pattern_context_for(original_file, it.into()));
+                        Some(PathKind::Pat)
                     },
                     ast::RecordPat(it) => {
                         path_ctx.has_call_parens = true;
-                        PathKind::Pat { pat_ctx: pattern_context_for(original_file, it.into())}
+                        *pattern_ctx = Some(pattern_context_for(original_file, it.into()));
+                        Some(PathKind::Pat)
                     },
                     ast::PathPat(it) => {
-                        PathKind::Pat { pat_ctx: pattern_context_for(original_file, it.into())}
+                        *pattern_ctx = Some(pattern_context_for(original_file, it.into()));
+                        Some(PathKind::Pat)
                     },
                     ast::MacroCall(it) => {
                         // A macro call in this position is usually a result of parsing recovery, so check that
                         if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
-                            return Some(make_res(NameRefKind::Keyword(kind)));
+                            nameref_ctx.kind = Some(NameRefKind::Keyword(kind));
+                            return None;
                         }
 
                         path_ctx.has_macro_bang = it.excl_token().is_some();
                         let parent = it.syntax().parent()?;
                         // Any path in an item list will be treated as a macro call by the parser
-                        match_ast! {
+                        let res = match_ast! {
                             match parent {
                                 ast::MacroExpr(expr) => make_path_kind_expr(expr.into()),
-                                ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(original_file, it.into())},
+                                ast::MacroPat(_) => PathKind::Pat,
                                 ast::MacroType(ty) => make_path_kind_type(ty.into()),
                                 ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module },
                                 ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() {
@@ -806,9 +821,10 @@ impl<'a> CompletionContext<'a> {
                                 ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile },
                                 _ => return None,
                             }
-                        }
+                        };
+                        Some(res)
                     },
-                    ast::Meta(meta) => {
+                    ast::Meta(meta) => (|| {
                         let attr = meta.parent_attr()?;
                         let kind = attr.kind();
                         let attached = attr.syntax().parent()?;
@@ -819,19 +835,24 @@ impl<'a> CompletionContext<'a> {
                         } else {
                             Some(attached.kind())
                         };
-                        PathKind::Attr {
+                        Some(PathKind::Attr {
                             kind,
                             annotated_item_kind,
-                        }
-                    },
-                    ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
-                    ast::UseTree(_) => PathKind::Use,
+                        })
+                    })(),
+                    ast::Visibility(it) => Some(PathKind::Vis { has_in_token: it.in_token().is_some() }),
+                    ast::UseTree(_) => Some(PathKind::Use),
                     _ => return None,
-
+                }
             }
-        };
+        });
 
-        path_ctx.kind = kind;
+        match kind {
+            Some(kind) => path_ctx.kind = kind,
+            // unresolved path kind, so this isn't really a path we should be completing,
+            // just some random identifier which might be in keyword position
+            None => return res,
+        }
         path_ctx.has_type_args = segment.generic_arg_list().is_some();
 
         // calculate the qualifier context
@@ -872,7 +893,6 @@ impl<'a> CompletionContext<'a> {
             }
         }
 
-        let mut qualifier_ctx = QualifierCtx::default();
         if path_ctx.is_trivial_path() {
             // fetch the full expression that may have qualifiers attached to it
             let top_node = match path_ctx.kind {
@@ -917,8 +937,8 @@ impl<'a> CompletionContext<'a> {
                                 if ![T![;], T!['}'], T!['{']].contains(&prev.kind()) {
                                     // This was inferred to be an item position path, but it seems
                                     // to be part of some other broken node which leaked into an item
-                                    // list
-                                    return None;
+                                    // list, so return without setting the path context
+                                    return res;
                                 }
                             }
                         }
@@ -926,7 +946,8 @@ impl<'a> CompletionContext<'a> {
                 }
             }
         }
-        Some((NameRefContext { nameref, kind: NameRefKind::Path(path_ctx) }, qualifier_ctx))
+        nameref_ctx.kind = Some(NameRefKind::Path(path_ctx));
+        res
     }
 }
 
diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs
index 27fe66e385..b5022dab93 100644
--- a/crates/ide-completion/src/lib.rs
+++ b/crates/ide-completion/src/lib.rs
@@ -24,7 +24,7 @@ use text_edit::TextEdit;
 
 use crate::{
     completions::Completions,
-    context::{CompletionContext, IdentContext, NameKind, NameRefContext, NameRefKind},
+    context::{CompletionContext, IdentContext, NameRefContext, NameRefKind},
 };
 
 pub use crate::{
@@ -151,8 +151,10 @@ pub fn completions(
 
     // prevent `(` from triggering unwanted completion noise
     if trigger_character == Some('(') {
-        if let IdentContext::NameRef(NameRefContext { kind: NameRefKind::Path(path_ctx), .. }) =
-            &ctx.ident_ctx
+        if let IdentContext::NameRef(NameRefContext {
+            kind: Some(NameRefKind::Path(path_ctx)),
+            ..
+        }) = &ctx.ident_ctx
         {
             completions::vis::complete_vis_path(&mut completions, ctx, path_ctx);
         }
@@ -168,12 +170,6 @@ pub fn completions(
                 completions::field::complete_field_list_record_variant(acc, ctx, name_ctx);
                 completions::item_list::trait_impl::complete_trait_impl_name(acc, ctx, name_ctx);
                 completions::mod_::complete_mod(acc, ctx, name_ctx);
-                if let NameKind::IdentPat(pattern_ctx) = &name_ctx.kind {
-                    completions::flyimport::import_on_the_fly_pat(acc, ctx, pattern_ctx);
-                    completions::fn_param::complete_fn_param(acc, ctx, pattern_ctx);
-                    completions::pattern::complete_pattern(acc, ctx, pattern_ctx);
-                    completions::record::complete_record_pattern_fields(acc, ctx, pattern_ctx);
-                }
             }
             IdentContext::NameRef(name_ctx @ NameRefContext { kind, .. }) => {
                 completions::item_list::trait_impl::complete_trait_impl_name_ref(
@@ -182,7 +178,7 @@ pub fn completions(
                 completions::use_::complete_use_tree(acc, ctx, name_ctx);
 
                 match kind {
-                    NameRefKind::Path(path_ctx) => {
+                    Some(NameRefKind::Path(path_ctx)) => {
                         completions::attribute::complete_attribute(acc, ctx, path_ctx);
                         completions::attribute::complete_derive(acc, ctx, path_ctx);
                         completions::dot::complete_undotted_self(acc, ctx, path_ctx);
@@ -198,27 +194,22 @@ pub fn completions(
                         completions::snippet::complete_item_snippet(acc, ctx, path_ctx);
                         completions::vis::complete_vis_path(acc, ctx, path_ctx);
                     }
-                    NameRefKind::DotAccess(dot_access) => {
+                    Some(NameRefKind::DotAccess(dot_access)) => {
                         completions::flyimport::import_on_the_fly_dot(acc, ctx, dot_access);
                         completions::dot::complete_dot(acc, ctx, dot_access);
                         completions::postfix::complete_postfix(acc, ctx, dot_access);
                     }
-                    NameRefKind::Keyword(item) => {
+                    Some(NameRefKind::Keyword(item)) => {
                         completions::keyword::complete_special_keywords(acc, ctx, item);
                     }
-                    NameRefKind::RecordExpr(record_expr) => {
+                    Some(NameRefKind::RecordExpr(record_expr)) => {
                         completions::record::complete_record_expr_fields_record_expr(
                             acc,
                             ctx,
                             record_expr,
                         );
                     }
-                    NameRefKind::Pattern(pattern_ctx) => {
-                        completions::flyimport::import_on_the_fly_pat(acc, ctx, pattern_ctx);
-                        completions::fn_param::complete_fn_param(acc, ctx, pattern_ctx);
-                        completions::pattern::complete_pattern(acc, ctx, pattern_ctx);
-                        completions::record::complete_record_pattern_fields(acc, ctx, pattern_ctx);
-                    }
+                    None => (),
                 }
             }
             IdentContext::Lifetime(lifetime_ctx) => {
@@ -234,6 +225,13 @@ pub fn completions(
             }
             IdentContext::UnexpandedAttrTT { .. } | IdentContext::String { .. } => (),
         }
+
+        if let Some(pattern_ctx) = &ctx.pattern_ctx {
+            completions::flyimport::import_on_the_fly_pat(acc, ctx, pattern_ctx);
+            completions::fn_param::complete_fn_param(acc, ctx, pattern_ctx);
+            completions::pattern::complete_pattern(acc, ctx, pattern_ctx);
+            completions::record::complete_record_pattern_fields(acc, ctx, pattern_ctx);
+        }
     }
 
     Some(completions)
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index c6091645ca..68908e330a 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -78,7 +78,7 @@ impl<'a> RenderContext<'a> {
         matches!(
             self.completion.ident_ctx,
             IdentContext::NameRef(NameRefContext {
-                kind: NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. }),
+                kind: Some(NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. })),
                 ..
             })
         )
@@ -218,7 +218,7 @@ fn render_resolution_(
             let ctx = ctx.import_to_add(import_to_add);
             return render_fn(ctx, Some(local_name), func);
         }
-        ScopeDef::ModuleDef(Variant(var)) => {
+        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;
@@ -293,11 +293,11 @@ fn render_resolution_simple_(
     let type_path_no_ty_args = matches!(
         ctx.completion.ident_ctx,
         IdentContext::NameRef(NameRefContext {
-            kind: NameRefKind::Path(PathCompletionCtx {
+            kind: Some(NameRefKind::Path(PathCompletionCtx {
                 kind: PathKind::Type { .. },
                 has_type_args: false,
                 ..
-            }),
+            })),
             ..
         })
     ) && ctx.completion.config.callable.is_some();
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index 48539a03b1..811a88704d 100644
--- a/crates/ide-completion/src/render/function.rs
+++ b/crates/ide-completion/src/render/function.rs
@@ -83,10 +83,10 @@ fn render(
         let qualified_path = matches!(
             ctx.completion.ident_ctx,
             IdentContext::NameRef(NameRefContext {
-                kind: NameRefKind::Path(PathCompletionCtx {
+                kind: Some(NameRefKind::Path(PathCompletionCtx {
                     qualified: Qualified::With { .. },
                     ..
-                }),
+                })),
                 ..
             })
         );
@@ -262,24 +262,24 @@ fn params(
     let has_dot_receiver = match ctx.ident_ctx {
         IdentContext::NameRef(NameRefContext {
             kind:
-                NameRefKind::DotAccess(DotAccess {
+                Some(NameRefKind::DotAccess(DotAccess {
                     kind: DotAccessKind::Method { has_parens: true },
                     ..
-                }),
+                })),
             ..
         }) => return None,
         IdentContext::NameRef(NameRefContext {
-            kind: NameRefKind::DotAccess(DotAccess { .. }),
+            kind: Some(NameRefKind::DotAccess(DotAccess { .. })),
             ..
         }) => true,
         IdentContext::NameRef(NameRefContext {
             kind:
-                NameRefKind::Path(
+                Some(NameRefKind::Path(
                     PathCompletionCtx {
                         kind: PathKind::Expr { .. }, has_call_parens: true, ..
                     }
                     | PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. },
-                ),
+                )),
             ..
         }) => return None,
         _ => false,
diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs
index 042c974257..1e03d066d1 100644
--- a/crates/ide-completion/src/render/literal.rs
+++ b/crates/ide-completion/src/render/literal.rs
@@ -55,14 +55,15 @@ fn render(
     let mut kind = thing.kind(db);
     let should_add_parens = match &completion.ident_ctx {
         IdentContext::NameRef(NameRefContext {
-            kind: NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. }),
+            kind: Some(NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. })),
             ..
         }) => false,
         IdentContext::NameRef(NameRefContext {
             kind:
-                NameRefKind::Path(PathCompletionCtx {
-                    kind: PathKind::Use | PathKind::Type { .. }, ..
-                }),
+                Some(NameRefKind::Path(PathCompletionCtx {
+                    kind: PathKind::Use | PathKind::Type { .. },
+                    ..
+                })),
             ..
         }) => false,
         _ => true,
diff --git a/crates/ide-completion/src/render/macro_.rs b/crates/ide-completion/src/render/macro_.rs
index 0c9c65f423..26690d22ab 100644
--- a/crates/ide-completion/src/render/macro_.rs
+++ b/crates/ide-completion/src/render/macro_.rs
@@ -35,7 +35,7 @@ fn render(
 
     let needs_bang = match &completion.ident_ctx {
         IdentContext::NameRef(NameRefContext {
-            kind: NameRefKind::Path(PathCompletionCtx { kind, has_macro_bang, .. }),
+            kind: Some(NameRefKind::Path(PathCompletionCtx { kind, has_macro_bang, .. })),
             ..
         }) => is_fn_like && *kind != PathKind::Use && !has_macro_bang,
         _ => is_fn_like,
diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs
index 74564bb3aa..33f169c1b9 100644
--- a/crates/ide-completion/src/render/pattern.rs
+++ b/crates/ide-completion/src/render/pattern.rs
@@ -7,8 +7,7 @@ use syntax::SmolStr;
 
 use crate::{
     context::{
-        IdentContext, NameContext, NameKind, NameRefContext, NameRefKind, ParamKind,
-        PathCompletionCtx, PathKind, PatternContext,
+        IdentContext, NameRefContext, NameRefKind, ParamKind, PathCompletionCtx, PatternContext,
     },
     render::{variant::visible_fields, RenderContext},
     CompletionItem, CompletionItemKind,
@@ -83,7 +82,7 @@ fn render_pat(
     let has_call_parens = matches!(
         ctx.completion.ident_ctx,
         IdentContext::NameRef(NameRefContext {
-            kind: NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. }),
+            kind: Some(NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. })),
             ..
         })
     );
@@ -98,27 +97,14 @@ fn render_pat(
         _ => name.to_owned(),
     };
 
-    let needs_ascription = !has_call_parens
-        && matches!(
-            &ctx.completion.ident_ctx,
-            IdentContext::NameRef(NameRefContext {
-                kind: NameRefKind::Path(PathCompletionCtx {
-                    kind: PathKind::Pat {
-                        pat_ctx
-                    },
-                    ..
-                }),
-                ..
-            }) | IdentContext::Name(NameContext {
-                kind: NameKind::IdentPat(pat_ctx), ..}
-            )
-            if matches!(pat_ctx, PatternContext {
-                param_ctx: Some((.., ParamKind::Function(_))),
-                has_type_ascription: false,
-                ..
-            })
-        );
-    if needs_ascription {
+    if matches!(
+        ctx.completion.pattern_ctx,
+        Some(PatternContext {
+            param_ctx: Some((.., ParamKind::Function(_))),
+            has_type_ascription: false,
+            ..
+        }) if !has_call_parens
+    ) {
         pat.push(':');
         pat.push(' ');
         pat.push_str(name);
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs
index e69e959d32..c694d95d53 100755
--- a/crates/ide/src/folding_ranges.rs
+++ b/crates/ide/src/folding_ranges.rs
@@ -24,6 +24,7 @@ pub enum FoldKind {
     Array,
     WhereClause,
     ReturnType,
+    MatchArm,
 }
 
 #[derive(Debug)]
@@ -117,6 +118,11 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
                                 res.push(Fold { range, kind: FoldKind::WhereClause })
                             }
                         },
+                        ast::MatchArm(match_arm) => {
+                            if let Some(range) = fold_range_for_multiline_match_arm(match_arm) {
+                                res.push(Fold {range, kind: FoldKind::MatchArm})
+                            }
+                        },
                         _ => (),
                     }
                 }
@@ -264,6 +270,16 @@ fn fold_range_for_where_clause(where_clause: ast::WhereClause) -> Option<TextRan
     None
 }
 
+fn fold_range_for_multiline_match_arm(match_arm: ast::MatchArm) -> Option<TextRange> {
+    if let Some(_) = fold_kind(match_arm.expr()?.syntax().kind()) {
+        return None;
+    }
+    if match_arm.expr()?.syntax().text().contains_char('\n') {
+        return Some(match_arm.expr()?.syntax().text_range());
+    }
+    None
+}
+
 #[cfg(test)]
 mod tests {
     use test_utils::extract_tags;
@@ -299,6 +315,7 @@ mod tests {
                 FoldKind::Array => "array",
                 FoldKind::WhereClause => "whereclause",
                 FoldKind::ReturnType => "returntype",
+                FoldKind::MatchArm => "matcharm",
             };
             assert_eq!(kind, &attr.unwrap());
         }
@@ -456,6 +473,36 @@ fn main() <fold block>{
         );
     }
 
+    #[test]
+    fn test_fold_multiline_non_block_match_arm() {
+        check(
+            r#"
+            fn main() <fold block>{
+                match foo <fold block>{
+                    block => <fold block>{
+                    }</fold>,
+                    matcharm => <fold matcharm>some.
+                        call().
+                        chain()</fold>,
+                    matcharm2
+                        => 0,
+                    match_expr => <fold matcharm>match foo2 <fold block>{
+                        bar => (),
+                    }</fold></fold>,
+                    array_list => <fold array>[
+                        1,
+                        2,
+                        3,
+                    ]</fold>,
+                    strustS => <fold matcharm>StructS <fold block>{
+                        a: 31,
+                    }</fold></fold>,
+                }</fold>
+            }</fold>
+            "#,
+        )
+    }
+
     #[test]
     fn fold_big_calls() {
         check(
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index f91ab6ea79..3d702fe8dc 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -670,7 +670,8 @@ pub(crate) fn folding_range(
         | FoldKind::Statics
         | FoldKind::WhereClause
         | FoldKind::ReturnType
-        | FoldKind::Array => None,
+        | FoldKind::Array
+        | FoldKind::MatchArm => None,
     };
 
     let range = range(line_index, fold.range);


Test patch:
diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs
index a3bc1025e9..ca9408fb13 100644
--- a/crates/ide-completion/src/tests/flyimport.rs
+++ b/crates/ide-completion/src/tests/flyimport.rs
@@ -1,7 +1,7 @@
 use expect_test::{expect, Expect};
 
 use crate::{
-    context::{IdentContext, NameContext, NameKind, NameRefKind},
+    context::NameRefKind,
     tests::{check_edit, check_edit_with_config, TEST_CONFIG},
 };
 
@@ -11,22 +11,17 @@ fn check(ra_fixture: &str, expect: Expect) {
     let ctx = crate::context::CompletionContext::new(&db, position, &config).unwrap();
 
     let mut acc = crate::completions::Completions::default();
-    if let IdentContext::Name(NameContext { kind: NameKind::IdentPat(pat_ctx), .. }) =
-        &ctx.ident_ctx
-    {
-        crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pat_ctx);
+    if let Some(pattern_ctx) = &ctx.pattern_ctx {
+        crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pattern_ctx);
     }
-    if let IdentContext::NameRef(name_ref_ctx) = &ctx.ident_ctx {
+    if let crate::context::IdentContext::NameRef(name_ref_ctx) = &ctx.ident_ctx {
         match &name_ref_ctx.kind {
-            NameRefKind::Path(path) => {
+            Some(NameRefKind::Path(path)) => {
                 crate::completions::flyimport::import_on_the_fly_path(&mut acc, &ctx, path);
             }
-            NameRefKind::DotAccess(dot_access) => {
+            Some(NameRefKind::DotAccess(dot_access)) => {
                 crate::completions::flyimport::import_on_the_fly_dot(&mut acc, &ctx, dot_access);
             }
-            NameRefKind::Pattern(pattern) => {
-                crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pattern);
-            }
             _ => (),
         }
     }