Repository: rust-analyzer/rust-analyzer
Pull request #10438 (2021-10-03 10:43:09)

minor: Simplify
bors r+

bors[bot]: Build failed:
  * [Rust (macos-latest)](https://github.com/rust-analyzer/rust-analyzer/runs/3781679593)
Veykril: bors retry
bors[bot]: Canceled.
Veykril: bors retry

Patch:
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index 242b7b40e1..e83210f4f9 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -4,7 +4,6 @@ use indexmap::IndexMap;
 
 use hir::Semantics;
 use ide_db::{
-    call_info::FnCallNode,
     defs::{Definition, NameClass, NameRefClass},
     helpers::pick_best_token,
     search::FileReference,
@@ -101,23 +100,27 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
             _ => None,
         })
         .flatten()
-        .filter_map(|node| FnCallNode::with_node_exact(&node))
+        .filter_map(ast::CallableExpr::cast)
         .filter_map(|call_node| {
-            let name_ref = call_node.name_ref()?;
-            let func_target = match call_node {
-                FnCallNode::CallExpr(expr) => {
-                    let callable = sema.type_of_expr(&expr.expr()?)?.original.as_callable(db)?;
+            let (nav_target, range) = match call_node {
+                ast::CallableExpr::Call(call) => {
+                    let expr = call.expr()?;
+                    let callable = sema.type_of_expr(&expr)?.original.as_callable(db)?;
                     match callable.kind() {
-                        hir::CallableKind::Function(it) => it.try_to_nav(db),
+                        hir::CallableKind::Function(it) => {
+                            let range = expr.syntax().text_range();
+                            it.try_to_nav(db).zip(Some(range))
+                        }
                         _ => None,
                     }
                 }
-                FnCallNode::MethodCallExpr(expr) => {
+                ast::CallableExpr::MethodCall(expr) => {
+                    let range = expr.name_ref()?.syntax().text_range();
                     let function = sema.resolve_method_call(&expr)?;
-                    function.try_to_nav(db)
+                    function.try_to_nav(db).zip(Some(range))
                 }
             }?;
-            Some((func_target, name_ref.syntax().text_range()))
+            Some((nav_target, range))
         })
         .for_each(|(nav, range)| calls.add(nav, range));
 
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index cce342272f..a495e6c543 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -1899,51 +1899,6 @@ fn func$0() {
 fn function() {
     function();
 }
-"#,
-        )
-    }
-
-    #[test]
-    fn in_macro_multi_mapping() {
-        check(
-            "a",
-            r#"
-fn foo() {
-    macro_rules! match_ast2 {
-        ($node:ident {
-            $( $res:expr, )*
-        }) => {{
-            $( if $node { $res } else )*
-            { loop {} }
-        }};
-    }
-    let $0d = 3;
-    match_ast2! {
-        d {
-            d,
-            d,
-        }
-    };
-}
-"#,
-            r#"
-fn foo() {
-    macro_rules! match_ast2 {
-        ($node:ident {
-            $( $res:expr, )*
-        }) => {{
-            $( if $node { $res } else )*
-            { loop {} }
-        }};
-    }
-    let a = 3;
-    match_ast2! {
-        a {
-            a,
-            a,
-        }
-    };
-}
 "#,
         )
     }
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index 2e91f8d4ab..585ef31daf 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -1090,7 +1090,7 @@ fn make_call(ctx: &AssistContext, fun: &Function, indent: IndentLevel) -> String
 
     let args = make::arg_list(fun.params.iter().map(|param| param.to_arg(ctx)));
     let name = fun.name.clone();
-    let mut call_expr = if fun.self_param.is_some() {
+    let call_expr = if fun.self_param.is_some() {
         let self_arg = make::expr_path(make::ext::ident_path("self"));
         make::expr_method_call(self_arg, name, args)
     } else {
@@ -1100,9 +1100,6 @@ fn make_call(ctx: &AssistContext, fun: &Function, indent: IndentLevel) -> String
 
     let handler = FlowHandler::from_ret_ty(fun, &ret_ty);
 
-    if fun.control_flow.is_async {
-        call_expr = make::expr_await(call_expr);
-    }
     let expr = handler.make_call_expr(call_expr).indent(indent);
 
     let mut_modifier = |var: &OutlivedLocal| if var.mut_usage_outside_body { "mut " } else { "" };
@@ -1122,8 +1119,10 @@ fn make_call(ctx: &AssistContext, fun: &Function, indent: IndentLevel) -> String
             buf.push_str(") = ");
         }
     }
-
     format_to!(buf, "{}", expr);
+    if fun.control_flow.is_async {
+        buf.push_str(".await");
+    }
     let insert_comma = fun
         .body
         .parent()
@@ -3871,70 +3870,6 @@ async fn $0fun_name() {
 
 async fn some_function() {
 
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn extract_with_await_and_result_not_producing_match_expr() {
-        check_assist(
-            extract_function,
-            r#"
-async fn foo() -> Result<(), ()> {
-    $0async {}.await;
-    Err(())?$0
-}
-"#,
-            r#"
-async fn foo() -> Result<(), ()> {
-    fun_name().await?
-}
-
-async fn $0fun_name() -> _ {
-    async {}.await;
-    Err(())?
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn extract_with_await_and_result_producing_match_expr() {
-        check_assist(
-            extract_function,
-            r#"
-async fn foo() -> i32 {
-    loop {
-        let n = 1;$0
-        let k = async { 1 }.await;
-        if k == 42 {
-            break 3;
-        }
-        let m = k + 1;$0
-        let h = 1 + m;
-    }
-}
-"#,
-            r#"
-async fn foo() -> i32 {
-    loop {
-        let n = 1;
-        let m = match fun_name().await {
-            Ok(value) => value,
-            Err(value) => break value,
-        };
-        let h = 1 + m;
-    }
-}
-
-async fn $0fun_name() -> Result<i32, i32> {
-    let k = async { 1 }.await;
-    if k == 42 {
-        return Err(3);
-    }
-    let m = k + 1;
-    Ok(m)
 }
 "#,
         );
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs
index 55e5da7fe7..9b4e1380a4 100644
--- a/crates/ide_assists/src/handlers/extract_variable.rs
+++ b/crates/ide_assists/src/handlers/extract_variable.rs
@@ -1,7 +1,6 @@
 use stdx::format_to;
 use syntax::{
     ast::{self, AstNode},
-    NodeOrToken,
     SyntaxKind::{
         BLOCK_EXPR, BREAK_EXPR, CLOSURE_EXPR, COMMENT, LOOP_EXPR, MATCH_ARM, MATCH_GUARD,
         PATH_EXPR, RETURN_EXPR,
@@ -31,26 +30,20 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
     if ctx.frange.range.is_empty() {
         return None;
     }
-    let node = match ctx.covering_element() {
-        NodeOrToken::Node(it) => it,
-        NodeOrToken::Token(it) if it.kind() == COMMENT => {
-            cov_mark::hit!(extract_var_in_comment_is_not_applicable);
-            return None;
-        }
-        NodeOrToken::Token(it) => it.parent()?,
-    };
-    let node = node.ancestors().take_while(|anc| anc.text_range() == node.text_range()).last()?;
+    let node = ctx.covering_element();
+    if node.kind() == COMMENT {
+        cov_mark::hit!(extract_var_in_comment_is_not_applicable);
+        return None;
+    }
     let to_extract = node
-        .descendants()
-        .take_while(|it| ctx.frange.range.contains_range(it.text_range()))
+        .ancestors()
+        .take_while(|it| it.text_range().contains_range(ctx.frange.range))
         .find_map(valid_target_expr)?;
-
     if let Some(ty_info) = ctx.sema.type_of_expr(&to_extract) {
         if ty_info.adjusted().is_unit() {
             return None;
         }
     }
-
     let anchor = Anchor::from(&to_extract)?;
     let indent = anchor.syntax().prev_sibling_or_token()?.as_token()?.clone();
     let target = to_extract.syntax().text_range();
@@ -153,11 +146,8 @@ enum Anchor {
 
 impl Anchor {
     fn from(to_extract: &ast::Expr) -> Option<Anchor> {
-        to_extract
-            .syntax()
-            .ancestors()
-            .take_while(|it| !ast::Item::can_cast(it.kind()) || ast::MacroCall::can_cast(it.kind()))
-            .find_map(|node| {
+        to_extract.syntax().ancestors().take_while(|it| !ast::Item::can_cast(it.kind())).find_map(
+            |node| {
                 if let Some(expr) =
                     node.parent().and_then(ast::StmtList::cast).and_then(|it| it.tail_expr())
                 {
@@ -191,7 +181,8 @@ impl Anchor {
                     return Some(Anchor::Before(node));
                 }
                 None
-            })
+            },
+        )
     }
 
     fn syntax(&self) -> &SyntaxNode {
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs
index 8a115087da..64e5c23f7c 100644
--- a/crates/ide_assists/src/handlers/generate_function.rs
+++ b/crates/ide_assists/src/handlers/generate_function.rs
@@ -51,27 +51,6 @@ pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext) -> Optio
     gen_fn(acc, ctx).or_else(|| gen_method(acc, ctx))
 }
 
-enum FuncExpr {
-    Func(ast::CallExpr),
-    Method(ast::MethodCallExpr),
-}
-
-impl FuncExpr {
-    fn arg_list(&self) -> Option<ast::ArgList> {
-        match self {
-            FuncExpr::Func(fn_call) => fn_call.arg_list(),
-            FuncExpr::Method(m_call) => m_call.arg_list(),
-        }
-    }
-
-    fn syntax(&self) -> &SyntaxNode {
-        match self {
-            FuncExpr::Func(fn_call) => fn_call.syntax(),
-            FuncExpr::Method(m_call) => m_call.syntax(),
-        }
-    }
-}
-
 fn gen_fn(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
     let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
@@ -254,7 +233,8 @@ impl FunctionBuilder {
         let needs_pub = target_module.is_some();
         let target_module = target_module.or_else(|| current_module(target.syntax(), ctx))?;
         let fn_name = make::name(fn_name);
-        let (type_params, params) = fn_args(ctx, target_module, FuncExpr::Func(call.clone()))?;
+        let (type_params, params) =
+            fn_args(ctx, target_module, ast::CallableExpr::Call(call.clone()))?;
 
         let await_expr = call.syntax().parent().and_then(ast::AwaitExpr::cast);
         let is_async = await_expr.is_some();
@@ -284,7 +264,8 @@ impl FunctionBuilder {
         let needs_pub =
             !module_is_descendant(&current_module(call.syntax(), ctx)?, &target_module, ctx);
         let fn_name = make::name(&name.text());
-        let (type_params, params) = fn_args(ctx, target_module, FuncExpr::Method(call.clone()))?;
+        let (type_params, params) =
+            fn_args(ctx, target_module, ast::CallableExpr::MethodCall(call.clone()))?;
 
         let await_expr = call.syntax().parent().and_then(ast::AwaitExpr::cast);
         let is_async = await_expr.is_some();
@@ -392,7 +373,7 @@ fn get_fn_target(
             file = in_file;
             target
         }
-        None => next_space_for_fn_after_call_site(FuncExpr::Func(call))?,
+        None => next_space_for_fn_after_call_site(ast::CallableExpr::Call(call))?,
     };
     Some((target.clone(), file, get_insert_offset(&target)))
 }
@@ -438,7 +419,7 @@ impl GeneratedFunctionTarget {
 fn fn_args(
     ctx: &AssistContext,
     target_module: hir::Module,
-    call: FuncExpr,
+    call: ast::CallableExpr,
 ) -> Option<(Option<ast::GenericParamList>, ast::ParamList)> {
     let mut arg_names = Vec::new();
     let mut arg_types = Vec::new();
@@ -468,8 +449,8 @@ fn fn_args(
         None,
         make::param_list(
             match call {
-                FuncExpr::Func(_) => None,
-                FuncExpr::Method(_) => Some(make::self_param()),
+                ast::CallableExpr::Call(_) => None,
+                ast::CallableExpr::MethodCall(_) => Some(make::self_param()),
             },
             params,
         ),
@@ -553,7 +534,7 @@ fn fn_arg_type(
 /// directly after the current block
 /// We want to write the generated function directly after
 /// fns, impls or macro calls, but inside mods
-fn next_space_for_fn_after_call_site(expr: FuncExpr) -> Option<GeneratedFunctionTarget> {
+fn next_space_for_fn_after_call_site(expr: ast::CallableExpr) -> Option<GeneratedFunctionTarget> {
     let mut ancestors = expr.syntax().ancestors().peekable();
     let mut last_ancestor: Option<SyntaxNode> = None;
     while let Some(next_ancestor) = ancestors.next() {
diff --git a/crates/ide_assists/src/handlers/inline_call.rs b/crates/ide_assists/src/handlers/inline_call.rs
index d252d61a69..b3dc95df41 100644
--- a/crates/ide_assists/src/handlers/inline_call.rs
+++ b/crates/ide_assists/src/handlers/inline_call.rs
@@ -12,7 +12,7 @@ use ide_db::{
 use itertools::{izip, Itertools};
 use syntax::{
     ast::{self, edit_in_place::Indent, HasArgList, PathExpr},
-    ted, AstNode, SyntaxNode,
+    ted, AstNode,
 };
 
 use crate::{
@@ -178,7 +178,7 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
     let name_ref: ast::NameRef = ctx.find_node_at_offset()?;
     let call_info = CallInfo::from_name_ref(name_ref.clone())?;
     let (function, label) = match &call_info.node {
-        CallExprNode::Call(call) => {
+        ast::CallableExpr::Call(call) => {
             let path = match call.expr()? {
                 ast::Expr::PathExpr(path) => path.path(),
                 _ => None,
@@ -190,7 +190,7 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
             };
             (function, format!("Inline `{}`", path))
         }
-        CallExprNode::MethodCallExpr(call) => {
+        ast::CallableExpr::MethodCall(call) => {
             (ctx.sema.resolve_method_call(call)?, format!("Inline `{}`", name_ref))
         }
     };
@@ -223,8 +223,8 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
 
             builder.replace_ast(
                 match call_info.node {
-                    CallExprNode::Call(it) => ast::Expr::CallExpr(it),
-                    CallExprNode::MethodCallExpr(it) => ast::Expr::MethodCallExpr(it),
+                    ast::CallableExpr::Call(it) => ast::Expr::CallExpr(it),
+                    ast::CallableExpr::MethodCall(it) => ast::Expr::MethodCallExpr(it),
                 },
                 replacement,
             );
@@ -232,22 +232,8 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
     )
 }
 
-enum CallExprNode {
-    Call(ast::CallExpr),
-    MethodCallExpr(ast::MethodCallExpr),
-}
-
-impl CallExprNode {
-    fn syntax(&self) -> &SyntaxNode {
-        match self {
-            CallExprNode::Call(it) => it.syntax(),
-            CallExprNode::MethodCallExpr(it) => it.syntax(),
-        }
-    }
-}
-
 struct CallInfo {
-    node: CallExprNode,
+    node: ast::CallableExpr,
     arguments: Vec<ast::Expr>,
     generic_arg_list: Option<ast::GenericArgList>,
 }
@@ -261,7 +247,7 @@ impl CallInfo {
             arguments.extend(call.arg_list()?.args());
             Some(CallInfo {
                 generic_arg_list: call.generic_arg_list(),
-                node: CallExprNode::MethodCallExpr(call),
+                node: ast::CallableExpr::MethodCall(call),
                 arguments,
             })
         } else if let Some(segment) = ast::PathSegment::cast(parent) {
@@ -271,7 +257,7 @@ impl CallInfo {
 
             Some(CallInfo {
                 arguments: call.arg_list()?.args().collect(),
-                node: CallExprNode::Call(call),
+                node: ast::CallableExpr::Call(call),
                 generic_arg_list: segment.generic_arg_list(),
             })
         } else {
@@ -367,8 +353,9 @@ fn inline(
                 ted::replace(usage.syntax(), &replacement.syntax().clone_for_update());
             }
         };
-        // izip confuses RA due to our lack of hygiene info currently losing us typeinfo
+        // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors
         let usages: &[ast::PathExpr] = &*usages;
+        let expr: &ast::Expr = expr;
         match usages {
             // inline single use closure arguments
             [usage]
@@ -414,8 +401,8 @@ fn inline(
     }
 
     let original_indentation = match node {
-        CallExprNode::Call(it) => it.indent_level(),
-        CallExprNode::MethodCallExpr(it) => it.indent_level(),
+        ast::CallableExpr::Call(it) => it.indent_level(),
+        ast::CallableExpr::MethodCall(it) => it.indent_level(),
     };
     body.reindent_to(original_indentation);
 
diff --git a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs
index 22675fbee0..3a89c2b996 100644
--- a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs
@@ -210,7 +210,11 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext)
                 ast::Expr::BlockExpr(block) => block,
                 expr => make::block_expr(iter::empty(), Some(expr)),
             };
-            let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) };
+            let else_expr = match else_expr {
+                ast::Expr::BlockExpr(block) if block.is_empty() => None,
+                ast::Expr::TupleExpr(tuple) if tuple.fields().next().is_none() => None,
+                expr => Some(expr),
+            };
             let if_let_expr = make::expr_if(
                 condition,
                 then_block,
@@ -253,10 +257,7 @@ fn pick_pattern_and_expr_order(
 
 fn is_empty_expr(expr: &ast::Expr) -> bool {
     match expr {
-        ast::Expr::BlockExpr(expr) => match expr.stmt_list() {
-            Some(it) => it.statements().next().is_none() && it.tail_expr().is_none(),
-            None => true,
-        },
+        ast::Expr::BlockExpr(expr) => expr.is_empty(),
         ast::Expr::TupleExpr(expr) => expr.fields().next().is_none(),
         _ => false,
     }
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs
index e1d85f0411..922a411aed 100644
--- a/crates/ide_db/src/call_info.rs
+++ b/crates/ide_db/src/call_info.rs
@@ -6,7 +6,7 @@ use stdx::format_to;
 use syntax::{
     algo,
     ast::{self, HasArgList, HasName},
-    match_ast, AstNode, Direction, SyntaxNode, SyntaxToken, TextRange, TextSize,
+    AstNode, Direction, SyntaxToken, TextRange, TextSize,
 };
 
 use crate::RootDatabase;
@@ -25,9 +25,11 @@ impl CallInfo {
     pub fn parameter_labels(&self) -> impl Iterator<Item = &str> + '_ {
         self.parameters.iter().map(move |&it| &self.signature[it])
     }
+
     pub fn parameter_ranges(&self) -> &[TextRange] {
         &self.parameters
     }
+
     fn push_param(&mut self, param: &str) {
         if !self.signature.ends_with('(') {
             self.signature.push_str(", ");
@@ -115,31 +117,24 @@ fn call_info_impl(
     token: SyntaxToken,
 ) -> Option<(hir::Callable, Option<usize>)> {
     // Find the calling expression and it's NameRef
-    let calling_node = FnCallNode::with_node(&token.parent()?)?;
+    let parent = token.parent()?;
+    let calling_node = parent.ancestors().filter_map(ast::CallableExpr::cast).find(|it| {
+        it.arg_list()
+            .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start()))
+    })?;
 
     let callable = match &calling_node {
-        FnCallNode::CallExpr(call) => {
-            sema.type_of_expr(&call.expr()?)?.adjusted().as_callable(sema.db)?
+        ast::CallableExpr::Call(call) => {
+            let expr = call.expr()?;
+            sema.type_of_expr(&expr)?.adjusted().as_callable(sema.db)
         }
-        FnCallNode::MethodCallExpr(call) => sema.resolve_method_call_as_callable(call)?,
-    };
+        ast::CallableExpr::MethodCall(call) => sema.resolve_method_call_as_callable(call),
+    }?;
     let active_param = if let Some(arg_list) = calling_node.arg_list() {
-        // Number of arguments specified at the call site
-        let num_args_at_callsite = arg_list.args().count();
-
-        let arg_list_range = arg_list.syntax().text_range();
-        if !arg_list_range.contains_inclusive(token.text_range().start()) {
-            cov_mark::hit!(call_info_bad_offset);
-            return None;
-        }
-        let param = std::cmp::min(
-            num_args_at_callsite,
-            arg_list
-                .args()
-                .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
-                .count(),
-        );
-
+        let param = arg_list
+            .args()
+            .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
+            .count();
         Some(param)
     } else {
         None
@@ -175,60 +170,5 @@ impl ActiveParameter {
     }
 }
 
-#[derive(Debug)]
-pub enum FnCallNode {
-    CallExpr(ast::CallExpr),
-    MethodCallExpr(ast::MethodCallExpr),
-}
-
-impl FnCallNode {
-    fn with_node(syntax: &SyntaxNode) -> Option<FnCallNode> {
-        syntax.ancestors().find_map(|node| {
-            match_ast! {
-                match node {
-                    ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
-                    ast::MethodCallExpr(it) => {
-                        let arg_list = it.arg_list()?;
-                        if !arg_list.syntax().text_range().contains_range(syntax.text_range()) {
-                            return None;
-                        }
-                        Some(FnCallNode::MethodCallExpr(it))
-                    },
-                    _ => None,
-                }
-            }
-        })
-    }
-
-    pub fn with_node_exact(node: &SyntaxNode) -> Option<FnCallNode> {
-        match_ast! {
-            match node {
-                ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
-                ast::MethodCallExpr(it) => Some(FnCallNode::MethodCallExpr(it)),
-                _ => None,
-            }
-        }
-    }
-
-    pub fn name_ref(&self) -> Option<ast::NameRef> {
-        match self {
-            FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()? {
-                ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
-                _ => return None,
-            }),
-            FnCallNode::MethodCallExpr(call_expr) => {
-                call_expr.syntax().children().find_map(ast::NameRef::cast)
-            }
-        }
-    }
-
-    fn arg_list(&self) -> Option<ast::ArgList> {
-        match self {
-            FnCallNode::CallExpr(expr) => expr.arg_list(),
-            FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
-        }
-    }
-}
-
 #[cfg(test)]
 mod tests;
diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs
index fcf1a654b0..0ca0dc8c6b 100644
--- a/crates/ide_db/src/rename.rs
+++ b/crates/ide_db/src/rename.rs
@@ -291,26 +291,23 @@ pub fn source_edit_from_references(
     new_name: &str,
 ) -> TextEdit {
     let mut edit = TextEdit::builder();
-    // macros can cause multiple refs to occur for the same text range, so keep track of what we have edited so far
-    let mut edited_ranges = Vec::new();
-    for &FileReference { range, ref name, .. } in references {
-        let has_emitted_edit = match name {
+    for reference in references {
+        let has_emitted_edit = match &reference.name {
             // if the ranges differ then the node is inside a macro call, we can't really attempt
             // to make special rewrites like shorthand syntax and such, so just rename the node in
             // the macro input
-            ast::NameLike::NameRef(name_ref) if name_ref.syntax().text_range() == range => {
+            ast::NameLike::NameRef(name_ref)
+                if name_ref.syntax().text_range() == reference.range =>
+            {
                 source_edit_from_name_ref(&mut edit, name_ref, new_name, def)
             }
-            ast::NameLike::Name(name) if name.syntax().text_range() == range => {
+            ast::NameLike::Name(name) if name.syntax().text_range() == reference.range => {
                 source_edit_from_name(&mut edit, name, new_name)
             }
             _ => false,
         };
         if !has_emitted_edit {
-            if !edited_ranges.contains(&range.start()) {
-                edit.replace(range, new_name.to_string());
-                edited_ranges.push(range.start());
-            }
+            edit.replace(reference.range, new_name.to_string());
         }
     }
 
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs
index 06c21dfd70..7d8f97c775 100644
--- a/crates/mbe/src/expander/matcher.rs
+++ b/crates/mbe/src/expander/matcher.rs
@@ -63,7 +63,7 @@ use std::rc::Rc;
 
 use crate::{
     expander::{Binding, Bindings, Fragment},
-    parser::{Op, RepeatKind, Separator},
+    parser::{Op, OpDelimited, OpDelimitedIter, RepeatKind, Separator},
     tt_iter::TtIter,
     ExpandError, MetaTemplate,
 };
@@ -750,64 +750,6 @@ fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &MetaTemplate) {
     }
 }
 
-impl MetaTemplate {
-    fn iter_delimited<'a>(&'a self, delimited: Option<&'a tt::Delimiter>) -> OpDelimitedIter<'a> {
-        OpDelimitedIter { inner: &self.0, idx: 0, delimited }
-    }
-}
-
-#[derive(Debug, Clone, Copy)]
-enum OpDelimited<'a> {
-    Op(&'a Op),
-    Open,
-    Close,
-}
-
-#[derive(Debug, Clone, Copy)]
-struct OpDelimitedIter<'a> {
-    inner: &'a [Op],
-    delimited: Option<&'a tt::Delimiter>,
-    idx: usize,
-}
-
-impl<'a> OpDelimitedIter<'a> {
-    fn is_eof(&self) -> bool {
-        let len = self.inner.len() + if self.delimited.is_some() { 2 } else { 0 };
-        self.idx >= len
-    }
-
-    fn peek(&self) -> Option<OpDelimited<'a>> {
-        match self.delimited {
-            None => self.inner.get(self.idx).map(OpDelimited::Op),
-            Some(_) => match self.idx {
-                0 => Some(OpDelimited::Open),
-                i if i == self.inner.len() + 1 => Some(OpDelimited::Close),
-                i => self.inner.get(i - 1).map(OpDelimited::Op),
-            },
-        }
-    }
-
-    fn reset(&self) -> Self {
-        Self { inner: self.inner, idx: 0, delimited: self.delimited }
-    }
-}
-
-impl<'a> Iterator for OpDelimitedIter<'a> {
-    type Item = OpDelimited<'a>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let res = self.peek();
-        self.idx += 1;
-        res
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        let len = self.inner.len() + if self.delimited.is_some() { 2 } else { 0 };
-        let remain = len.saturating_sub(self.idx);
-        (remain, Some(remain))
-    }
-}
-
 impl<'a> TtIter<'a> {
     fn expect_separator(&mut self, separator: &Separator, idx: usize) -> bool {
         let mut fork = self.clone();
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 8470ea0aaf..0a1b7cc848 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -19,7 +19,7 @@ mod token_map;
 use std::fmt;
 
 use crate::{
-    parser::{MetaTemplate, Op},
+    parser::{parse_pattern, parse_template, MetaTemplate, Op},
     tt_iter::TtIter,
 };
 
@@ -275,8 +275,8 @@ impl Rule {
             .expect_subtree()
             .map_err(|()| ParseError::Expected("expected subtree".to_string()))?;
 
-        let lhs = MetaTemplate::parse_pattern(lhs)?;
-        let rhs = MetaTemplate::parse_template(rhs)?;
+        let lhs = MetaTemplate(parse_pattern(lhs)?);
+        let rhs = MetaTemplate(parse_template(rhs)?);
 
         Ok(crate::Rule { lhs, rhs })
     }
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index 4ce818f9b0..deed884d2d 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -3,48 +3,75 @@
 
 use smallvec::SmallVec;
 use syntax::SmolStr;
+use tt::Delimiter;
 
 use crate::{tt_iter::TtIter, ParseError};
 
-/// Consider
-///
-/// ```
-/// macro_rules! an_macro {
-///     ($x:expr + $y:expr) => ($y * $x)
-/// }
-/// ```
-///
-/// Stuff to the left of `=>` is a [`MetaTemplate`] pattern (which is matched
-/// with input).
-///
-/// Stuff to the right is a [`MetaTemplate`] template which is used to produce
-/// output.
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub(crate) struct MetaTemplate(pub(crate) Vec<Op>);
 
-impl MetaTemplate {
-    pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Result<MetaTemplate, ParseError> {
-        MetaTemplate::parse(pattern, Mode::Pattern)
+#[derive(Debug, Clone, Copy)]
+pub(crate) enum OpDelimited<'a> {
+    Op(&'a Op),
+    Open,
+    Close,
+}
+
+#[derive(Debug, Clone, Copy)]
+pub(crate) struct OpDelimitedIter<'a> {
+    inner: &'a Vec<Op>,
+    delimited: Option<&'a Delimiter>,
+    idx: usize,
+}
+
+impl<'a> OpDelimitedIter<'a> {
+    pub(crate) fn is_eof(&self) -> bool {
+        let len = self.inner.len() + if self.delimited.is_some() { 2 } else { 0 };
+        self.idx >= len
     }
 
-    pub(crate) fn parse_template(template: &tt::Subtree) -> Result<MetaTemplate, ParseError> {
-        MetaTemplate::parse(template, Mode::Template)
+    pub(crate) fn peek(&self) -> Option<OpDelimited<'a>> {
+        match self.delimited {
+            None => self.inner.get(self.idx).map(OpDelimited::Op),
+            Some(_) => match self.idx {
+                0 => Some(OpDelimited::Open),
+                i if i == self.inner.len() + 1 => Some(OpDelimited::Close),
+                i => self.inner.get(i - 1).map(OpDelimited::Op),
+            },
+        }
     }
 
-    pub(crate) fn iter(&self) -> impl Iterator<Item = &Op> {
-        self.0.iter()
+    pub(crate) fn reset(&self) -> Self {
+        Self { inner: self.inner, idx: 0, delimited: self.delimited }
     }
+}
 
-    fn parse(tt: &tt::Subtree, mode: Mode) -> Result<MetaTemplate, ParseError> {
-        let mut src = TtIter::new(tt);
+impl<'a> Iterator for OpDelimitedIter<'a> {
+    type Item = OpDelimited<'a>;
 
-        let mut res = Vec::new();
-        while let Some(first) = src.next() {
-            let op = next_op(first, &mut src, mode)?;
-            res.push(op)
-        }
+    fn next(&mut self) -> Option<Self::Item> {
+        let res = self.peek();
+        self.idx += 1;
+        res
+    }
 
-        Ok(MetaTemplate(res))
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let len = self.inner.len() + if self.delimited.is_some() { 2 } else { 0 };
+        let remain = len.saturating_sub(self.idx);
+        (remain, Some(remain))
+    }
+}
+
+impl<'a> MetaTemplate {
+    pub(crate) fn iter(&self) -> impl Iterator<Item = &Op> {
+        self.0.iter()
+    }
+
+    pub(crate) fn iter_delimited(
+        &'a self,
+        delimited: Option<&'a Delimiter>,
+    ) -> OpDelimitedIter<'a> {
+        OpDelimitedIter { inner: &self.0, idx: 0, delimited }
     }
 }
 
@@ -53,7 +80,7 @@ pub(crate) enum Op {
     Var { name: SmolStr, kind: Option<SmolStr>, id: tt::TokenId },
     Repeat { tokens: MetaTemplate, kind: RepeatKind, separator: Option<Separator> },
     Leaf(tt::Leaf),
-    Subtree { tokens: MetaTemplate, delimiter: Option<tt::Delimiter> },
+    Subtree { tokens: MetaTemplate, delimiter: Option<Delimiter> },
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -98,12 +125,29 @@ impl Separator {
     }
 }
 
+pub(crate) fn parse_template(template: &tt::Subtree) -> Result<Vec<Op>, ParseError> {
+    parse_inner(template, Mode::Template).into_iter().collect()
+}
+
+pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Result<Vec<Op>, ParseError> {
+    parse_inner(pattern, Mode::Pattern).into_iter().collect()
+}
+
 #[derive(Clone, Copy)]
 enum Mode {
     Pattern,
     Template,
 }
 
+fn parse_inner(tt: &tt::Subtree, mode: Mode) -> Vec<Result<Op, ParseError>> {
+    let mut src = TtIter::new(tt);
+    std::iter::from_fn(move || {
+        let first = src.next()?;
+        Some(next_op(first, &mut src, mode))
+    })
+    .collect()
+}
+
 macro_rules! err {
     ($($tt:tt)*) => {
         ParseError::UnexpectedToken(($($tt)*).to_string())
@@ -127,8 +171,10 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul
             match second {
                 tt::TokenTree::Subtree(subtree) => {
                     let (separator, kind) = parse_repeat(src)?;
-                    let tokens = MetaTemplate::parse(subtree, mode)?;
-                    Op::Repeat { tokens, separator, kind }
+                    let tokens = parse_inner(subtree, mode)
+                        .into_iter()
+                        .collect::<Result<Vec<Op>, ParseError>>()?;
+                    Op::Repeat { tokens: MetaTemplate(tokens), separator, kind }
                 }
                 tt::TokenTree::Leaf(leaf) => match leaf {
                     tt::Leaf::Punct(_) => {
@@ -159,8 +205,9 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul
         }
         tt::TokenTree::Leaf(tt) => Op::Leaf(tt.clone()),
         tt::TokenTree::Subtree(subtree) => {
-            let tokens = MetaTemplate::parse(subtree, mode)?;
-            Op::Subtree { tokens, delimiter: subtree.delimiter }
+            let tokens =
+                parse_inner(subtree, mode).into_iter().collect::<Result<Vec<Op>, ParseError>>()?;
+            Op::Subtree { tokens: MetaTemplate(tokens), delimiter: subtree.delimiter }
         }
     };
     Ok(res)
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs
index e57c3d2fe4..8732593af9 100644
--- a/crates/syntax/src/ast.rs
+++ b/crates/syntax/src/ast.rs
@@ -18,7 +18,7 @@ use crate::{
 };
 
 pub use self::{
-    expr_ext::{ArrayExprKind, BlockModifier, ElseBranch, LiteralKind},
+    expr_ext::{ArrayExprKind, BlockModifier, CallableExpr, ElseBranch, LiteralKind},
     generated::{nodes::*, tokens::*},
     node_ext::{
         AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind,
diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs
index 7363ad905a..f421f34b3d 100644
--- a/crates/syntax/src/ast/expr_ext.rs
+++ b/crates/syntax/src/ast/expr_ext.rs
@@ -10,7 +10,7 @@ use crate::{
     },
     AstToken,
     SyntaxKind::*,
-    SyntaxToken, T,
+    SyntaxNode, SyntaxToken, T,
 };
 
 impl ast::HasAttrs for ast::Expr {}
@@ -312,3 +312,41 @@ impl ast::RecordExprField {
         self.syntax().ancestors().find_map(ast::RecordExpr::cast).unwrap()
     }
 }
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum CallableExpr {
+    Call(ast::CallExpr),
+    MethodCall(ast::MethodCallExpr),
+}
+
+impl ast::HasAttrs for CallableExpr {}
+impl ast::HasArgList for CallableExpr {}
+
+impl AstNode for CallableExpr {
+    fn can_cast(kind: parser::SyntaxKind) -> bool
+    where
+        Self: Sized,
+    {
+        ast::CallExpr::can_cast(kind) || ast::MethodCallExpr::can_cast(kind)
+    }
+
+    fn cast(syntax: SyntaxNode) -> Option<Self>
+    where
+        Self: Sized,
+    {
+        if let Some(it) = ast::CallExpr::cast(syntax.clone()) {
+            Some(Self::Call(it))
+        } else if let Some(it) = ast::MethodCallExpr::cast(syntax.clone()) {
+            Some(Self::MethodCall(it))
+        } else {
+            None
+        }
+    }
+
+    fn syntax(&self) -> &SyntaxNode {
+        match self {
+            Self::Call(it) => it.syntax(),
+            Self::MethodCall(it) => it.syntax(),
+        }
+    }
+}
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 40c0cd7a37..dc13e916c8 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -299,9 +299,6 @@ pub fn expr_return(expr: Option<ast::Expr>) -> ast::Expr {
 pub fn expr_try(expr: ast::Expr) -> ast::Expr {
     expr_from_text(&format!("{}?", expr))
 }
-pub fn expr_await(expr: ast::Expr) -> ast::Expr {
-    expr_from_text(&format!("{}.await", expr))
-}
 pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr {
     expr_from_text(&format!("match {} {}", expr, match_arm_list))
 }
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 800befb04b..a07f02e445 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -10,7 +10,10 @@ use parser::SyntaxKind;
 use rowan::{GreenNodeData, GreenTokenData};
 
 use crate::{
-    ast::{self, support, AstNode, AstToken, HasAttrs, HasGenericParams, HasName, SyntaxNode},
+    ast::{
+        self, support, AstNode, AstToken, HasAttrs, HasGenericParams, HasModuleItem, HasName,
+        SyntaxNode,
+    },
     NodeOrToken, SmolStr, SyntaxElement, SyntaxToken, TokenText, T,
 };
 
@@ -51,6 +54,13 @@ impl ast::HasModuleItem for ast::StmtList {}
 
 impl ast::BlockExpr {
     // FIXME: remove all these methods, they belong to ast::StmtList
+    pub fn items(&self) -> impl Iterator<Item = ast::Item> {
+        self.stmt_list().into_iter().flat_map(|it| it.items())
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.statements().next().is_none() && self.tail_expr().is_none()
+    }
     pub fn statements(&self) -> impl Iterator<Item = ast::Stmt> {
         self.stmt_list().into_iter().flat_map(|it| it.statements())
     }
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 0b226a985d..3970f4ebb3 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -9,11 +9,11 @@
             "version": "0.4.0-dev",
             "license": "MIT OR Apache-2.0",
             "dependencies": {
-                "d3": "^7.1.0",
+                "d3": "^7.0.0",
                 "d3-graphviz": "^4.0.0",
                 "https-proxy-agent": "^5.0.0",
                 "node-fetch": "^2.6.1",
-                "vscode-languageclient": "8.0.0-next.2"
+                "vscode-languageclient": "^7.1.0-next.5"
             },
             "devDependencies": {
                 "@types/glob": "^7.1.4",
@@ -895,9 +895,9 @@
             }
         },
         "node_modules/d3": {
-            "version": "7.1.0",
-            "resolved": "https://registry.npmjs.org/d3/-/d3-7.1.0.tgz",
-            "integrity": "sha512-gfc489DnwSwd6Bh+BMhR7lzfFj+Mw+HCXn4OFQwCVQFz6H8jbEt/vWqR5oClpj4uxehJsA7ecRKEVUiKHNL1bg==",
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/d3/-/d3-7.0.0.tgz",
+            "integrity": "sha512-t+jEKGO2jQiSBLJYYq6RFc500tsCeXBB4x41oQaSnZD3Som95nQrlw9XJGrFTMUOQOkwSMauWy9+8Tz1qm9UZw==",
             "dependencies": {
                 "d3-array": "3",
                 "d3-axis": "3",
@@ -3753,39 +3753,39 @@
             }
         },
         "node_modules/vscode-jsonrpc": {
-            "version": "8.0.0-next.2",
-            "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.0-next.2.tgz",
-            "integrity": "sha512-gxUyTBAjmwGkiHW/UaRScre2s4i98P8M7gnc3VB4DrVQUm3vQ0idi2cN9nbkfcjATx+uEt8C22j+MLN/8UzsJA==",
+            "version": "7.0.0-next.1",
+            "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-7.0.0-next.1.tgz",
+            "integrity": "sha512-dEmliPZGbSyIcEeKRGzosCy7y7zsc8FXg1l5BBOGgMUbemlo3vUnsa2GFqpILJwJvlbvkRcF2QASNwIlKe9J7g==",
             "engines": {
                 "node": ">=8.0.0 || >=10.0.0"
             }
         },
         "node_modules/vscode-languageclient": {
-            "version": "8.0.0-next.2",
-            "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.0.0-next.2.tgz",
-            "integrity": "sha512-ZmWnfZCwxV/FAci9jVMBnhwtdt9qkItXKqlmAU69pBSBgcr0fRrjEHOnzWG7BcuhxE0kti6/Y9bOEIn1L+OoDQ==",
+            "version": "7.1.0-next.5",
+            "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.1.0-next.5.tgz",
+            "integrity": "sha512-TpzpAhpdCNJHaLzptFRs54xsU6xTmaiVPCse0W0rRB5jJBPjOBKilrFPMMm/sJA0y8Yxa9sOvZaNu6WPg3dYAw==",
             "dependencies": {
                 "minimatch": "^3.0.4",
                 "semver": "^7.3.4",
-                "vscode-languageserver-protocol": "3.17.0-next.8"
+                "vscode-languageserver-protocol": "3.17.0-next.6"
             },
             "engines": {
-                "vscode": "^1.58.0"
+                "vscode": "^1.53.0"
             }
         },
         "node_modules/vscode-languageserver-protocol": {
-            "version": "3.17.0-next.8",
-            "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.0-next.8.tgz",
-            "integrity": "sha512-P89vSuJ+FA5JzFmcOoZN13Ig1yd6LsiPOig0O5m5BSGuO/rplQegCd9J0wKpaTy7trf/SYHRoypnbUBdzy14sg==",
+            "version": "3.17.0-next.6",
+            "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.0-next.6.tgz",
+            "integrity": "sha512-f1kGsoOpISB5jSqQNeMDl2446enxVahyux2e5vZap6pu/TC+2UlvPT4DCR0gPph95KOQZweL9zq1SzLoPdqhuA==",
             "dependencies": {
-                "vscode-jsonrpc": "8.0.0-next.2",
-                "vscode-languageserver-types": "3.17.0-next.3"
+                "vscode-jsonrpc": "7.0.0-next.1",
+                "vscode-languageserver-types": "3.17.0-next.2"
             }
         },
         "node_modules/vscode-languageserver-types": {
-            "version": "3.17.0-next.3",
-            "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.0-next.3.tgz",
-            "integrity": "sha512-VQcXnhKYxUW6OiRMhG++SzmZYMJwusXknJGd+FfdOnS1yHAo734OHyR0e2eEHDlv0/oWc8RZPgx/VKSKyondVg=="
+            "version": "3.17.0-next.2",
+            "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.0-next.2.tgz",
+            "integrity": "sha512-L5S2kNLCgYJMVWgsZjBaorMM/6+itAfvOyl6Kv1bgFzDNaUKm9HsnUlehjpWPdV5DqnfJhJ5E03Z+/3Mw8ii+Q=="
         },
         "node_modules/vscode-test": {
             "version": "1.5.2",
@@ -4645,9 +4645,9 @@
             "dev": true
         },
         "d3": {
-            "version": "7.1.0",
-            "resolved": "https://registry.npmjs.org/d3/-/d3-7.1.0.tgz",
-            "integrity": "sha512-gfc489DnwSwd6Bh+BMhR7lzfFj+Mw+HCXn4OFQwCVQFz6H8jbEt/vWqR5oClpj4uxehJsA7ecRKEVUiKHNL1bg==",
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/d3/-/d3-7.0.0.tgz",
+            "integrity": "sha512-t+jEKGO2jQiSBLJYYq6RFc500tsCeXBB4x41oQaSnZD3Som95nQrlw9XJGrFTMUOQOkwSMauWy9+8Tz1qm9UZw==",
             "requires": {
                 "d3-array": "3",
                 "d3-axis": "3",
@@ -6818,33 +6818,33 @@
             }
         },
         "vscode-jsonrpc": {
-            "version": "8.0.0-next.2",
-            "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.0-next.2.tgz",
-            "integrity": "sha512-gxUyTBAjmwGkiHW/UaRScre2s4i98P8M7gnc3VB4DrVQUm3vQ0idi2cN9nbkfcjATx+uEt8C22j+MLN/8UzsJA=="
+            "version": "7.0.0-next.1",
+            "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-7.0.0-next.1.tgz",
+            "integrity": "sha512-dEmliPZGbSyIcEeKRGzosCy7y7zsc8FXg1l5BBOGgMUbemlo3vUnsa2GFqpILJwJvlbvkRcF2QASNwIlKe9J7g=="
         },
         "vscode-languageclient": {
-            "version": "8.0.0-next.2",
-            "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.0.0-next.2.tgz",
-            "integrity": "sha512-ZmWnfZCwxV/FAci9jVMBnhwtdt9qkItXKqlmAU69pBSBgcr0fRrjEHOnzWG7BcuhxE0kti6/Y9bOEIn1L+OoDQ==",
+            "version": "7.1.0-next.5",
+            "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.1.0-next.5.tgz",
+            "integrity": "sha512-TpzpAhpdCNJHaLzptFRs54xsU6xTmaiVPCse0W0rRB5jJBPjOBKilrFPMMm/sJA0y8Yxa9sOvZaNu6WPg3dYAw==",
             "requires": {
                 "minimatch": "^3.0.4",
                 "semver": "^7.3.4",
-                "vscode-languageserver-protocol": "3.17.0-next.8"
+                "vscode-languageserver-protocol": "3.17.0-next.6"
             }
         },
         "vscode-languageserver-protocol": {
-            "version": "3.17.0-next.8",
-            "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.0-next.8.tgz",
-            "integrity": "sha512-P89vSuJ+FA5JzFmcOoZN13Ig1yd6LsiPOig0O5m5BSGuO/rplQegCd9J0wKpaTy7trf/SYHRoypnbUBdzy14sg==",
+            "version": "3.17.0-next.6",
+            "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.0-next.6.tgz",
+            "integrity": "sha512-f1kGsoOpISB5jSqQNeMDl2446enxVahyux2e5vZap6pu/TC+2UlvPT4DCR0gPph95KOQZweL9zq1SzLoPdqhuA==",
             "requires": {
-                "vscode-jsonrpc": "8.0.0-next.2",
-                "vscode-languageserver-types": "3.17.0-next.3"
+                "vscode-jsonrpc": "7.0.0-next.1",
+                "vscode-languageserver-types": "3.17.0-next.2"
             }
         },
         "vscode-languageserver-types": {
-            "version": "3.17.0-next.3",
-            "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.0-next.3.tgz",
-            "integrity": "sha512-VQcXnhKYxUW6OiRMhG++SzmZYMJwusXknJGd+FfdOnS1yHAo734OHyR0e2eEHDlv0/oWc8RZPgx/VKSKyondVg=="
+            "version": "3.17.0-next.2",
+            "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.0-next.2.tgz",
+            "integrity": "sha512-L5S2kNLCgYJMVWgsZjBaorMM/6+itAfvOyl6Kv1bgFzDNaUKm9HsnUlehjpWPdV5DqnfJhJ5E03Z+/3Mw8ii+Q=="
         },
         "vscode-test": {
             "version": "1.5.2",
diff --git a/editors/code/package.json b/editors/code/package.json
index 5f38d1c126..a9593024d4 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -38,8 +38,8 @@
     "dependencies": {
         "https-proxy-agent": "^5.0.0",
         "node-fetch": "^2.6.1",
-        "vscode-languageclient": "8.0.0-next.2",
-        "d3": "^7.1.0",
+        "vscode-languageclient": "^7.1.0-next.5",
+        "d3": "^7.0.0",
         "d3-graphviz": "^4.0.0"
     },
     "devDependencies": {
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 726620578c..f69b7fdf56 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -66,7 +66,7 @@ async function tryActivate(context: vscode.ExtensionContext) {
     ctx.pushCleanup(configureLanguage());
 
     vscode.workspace.onDidChangeConfiguration(
-        _ => ctx?.client?.sendNotification('workspace/didChangeConfiguration', { settings: "" }).catch(log.error),
+        _ => ctx?.client?.sendNotification('workspace/didChangeConfiguration', { settings: "" }),
         null,
         ctx.subscriptions,
     );


Test patch:
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs
index b585085f38..8cdfcd027a 100644
--- a/crates/ide_db/src/call_info/tests.rs
+++ b/crates/ide_db/src/call_info/tests.rs
@@ -362,7 +362,6 @@ pub fn foo(mut r: WriteHandler<()>) {
 
 #[test]
 fn call_info_bad_offset() {
-    cov_mark::check!(call_info_bad_offset);
     check(
         r#"
 fn foo(x: u32, y: u32) -> u32 {x + y}