Repository: rust-analyzer/rust-analyzer
Pull request #11395 (2022-02-02 11:20:24)

fix: Fix and re-enable format string completions
bors r+


Patch:
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index e091af4bf8..fe518c8642 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2787,7 +2787,6 @@ impl Type {
         db: &dyn HirDatabase,
         krate: Crate,
         traits_in_scope: &FxHashSet<TraitId>,
-        with_local_impls: Option<Module>,
         name: Option<&Name>,
         mut callback: impl FnMut(Type, Function) -> Option<T>,
     ) -> Option<T> {
@@ -2798,7 +2797,6 @@ impl Type {
             db,
             krate,
             traits_in_scope,
-            with_local_impls,
             name,
             &mut |ty, assoc_item_id| {
                 if let AssocItemId::FunctionId(func) = assoc_item_id {
@@ -2818,7 +2816,6 @@ impl Type {
         db: &dyn HirDatabase,
         krate: Crate,
         traits_in_scope: &FxHashSet<TraitId>,
-        with_local_impls: Option<Module>,
         name: Option<&Name>,
         callback: &mut dyn FnMut(&Ty, AssocItemId) -> ControlFlow<()>,
     ) {
@@ -2834,7 +2831,7 @@ impl Type {
             env,
             krate,
             traits_in_scope,
-            with_local_impls.and_then(|b| b.id.containing_block()).into(),
+            None,
             name,
             method_resolution::LookupMode::MethodCall,
             &mut |ty, id| callback(&ty.value, id),
@@ -2846,7 +2843,6 @@ impl Type {
         db: &dyn HirDatabase,
         krate: Crate,
         traits_in_scope: &FxHashSet<TraitId>,
-        with_local_impls: Option<Module>,
         name: Option<&Name>,
         mut callback: impl FnMut(Type, AssocItem) -> Option<T>,
     ) -> Option<T> {
@@ -2856,7 +2852,6 @@ impl Type {
             db,
             krate,
             traits_in_scope,
-            with_local_impls,
             name,
             &mut |ty, assoc_item_id| {
                 if let Some(res) = callback(self.derived(ty.clone()), assoc_item_id.into()) {
@@ -2874,7 +2869,6 @@ impl Type {
         db: &dyn HirDatabase,
         krate: Crate,
         traits_in_scope: &FxHashSet<TraitId>,
-        with_local_impls: Option<Module>,
         name: Option<&Name>,
         callback: &mut dyn FnMut(&Ty, AssocItemId) -> ControlFlow<()>,
     ) {
@@ -2889,7 +2883,7 @@ impl Type {
             env,
             krate,
             traits_in_scope,
-            with_local_impls.and_then(|b| b.id.containing_block()).into(),
+            None,
             name,
             method_resolution::LookupMode::Path,
             &mut |ty, id| callback(&ty.value, id),
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 4f1bdee705..54b1680214 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -996,7 +996,7 @@ impl<'a> InferenceContext<'a> {
                 self.trait_env.clone(),
                 krate,
                 &traits_in_scope,
-                self.resolver.module().into(),
+                self.resolver.module(),
                 method_name,
             )
         });
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs
index b63ef2ffdc..c33a697f05 100644
--- a/crates/hir_ty/src/infer/path.rs
+++ b/crates/hir_ty/src/infer/path.rs
@@ -227,7 +227,7 @@ impl<'a> InferenceContext<'a> {
             self.table.trait_env.clone(),
             krate,
             &traits_in_scope,
-            self.resolver.module().into(),
+            self.resolver.module(),
             Some(name),
             method_resolution::LookupMode::Path,
             move |_ty, item| {
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index c91b6f2e82..1a451ae79f 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -437,7 +437,7 @@ pub(crate) fn lookup_method(
     env: Arc<TraitEnvironment>,
     krate: CrateId,
     traits_in_scope: &FxHashSet<TraitId>,
-    visible_from_module: VisibleFromModule,
+    visible_from_module: Option<ModuleId>,
     name: &Name,
 ) -> Option<(Canonical<Ty>, FunctionId)> {
     iterate_method_candidates(
@@ -468,34 +468,6 @@ pub enum LookupMode {
     Path,
 }
 
-#[derive(Clone, Copy)]
-pub enum VisibleFromModule {
-    /// Filter for results that are visible from the given module
-    Filter(ModuleId),
-    /// Include impls from the given block.
-    IncludeBlock(BlockId),
-    /// Do nothing special in regards visibility
-    None,
-}
-
-impl From<Option<ModuleId>> for VisibleFromModule {
-    fn from(module: Option<ModuleId>) -> Self {
-        match module {
-            Some(module) => Self::Filter(module),
-            None => Self::None,
-        }
-    }
-}
-
-impl From<Option<BlockId>> for VisibleFromModule {
-    fn from(block: Option<BlockId>) -> Self {
-        match block {
-            Some(block) => Self::IncludeBlock(block),
-            None => Self::None,
-        }
-    }
-}
-
 // This would be nicer if it just returned an iterator, but that runs into
 // lifetime problems, because we need to borrow temp `CrateImplDefs`.
 // FIXME add a context type here?
@@ -505,7 +477,7 @@ pub fn iterate_method_candidates<T>(
     env: Arc<TraitEnvironment>,
     krate: CrateId,
     traits_in_scope: &FxHashSet<TraitId>,
-    visible_from_module: VisibleFromModule,
+    visible_from_module: Option<ModuleId>,
     name: Option<&Name>,
     mode: LookupMode,
     mut callback: impl FnMut(&Canonical<Ty>, AssocItemId) -> Option<T>,
@@ -538,7 +510,7 @@ pub fn iterate_method_candidates_dyn(
     env: Arc<TraitEnvironment>,
     krate: CrateId,
     traits_in_scope: &FxHashSet<TraitId>,
-    visible_from_module: VisibleFromModule,
+    visible_from_module: Option<ModuleId>,
     name: Option<&Name>,
     mode: LookupMode,
     callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
@@ -606,7 +578,7 @@ fn iterate_method_candidates_with_autoref(
     env: Arc<TraitEnvironment>,
     krate: CrateId,
     traits_in_scope: &FxHashSet<TraitId>,
-    visible_from_module: VisibleFromModule,
+    visible_from_module: Option<ModuleId>,
     name: Option<&Name>,
     mut callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
 ) -> ControlFlow<()> {
@@ -672,7 +644,7 @@ fn iterate_method_candidates_by_receiver(
     env: Arc<TraitEnvironment>,
     krate: CrateId,
     traits_in_scope: &FxHashSet<TraitId>,
-    visible_from_module: VisibleFromModule,
+    visible_from_module: Option<ModuleId>,
     name: Option<&Name>,
     mut callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
 ) -> ControlFlow<()> {
@@ -714,7 +686,7 @@ fn iterate_method_candidates_for_self_ty(
     env: Arc<TraitEnvironment>,
     krate: CrateId,
     traits_in_scope: &FxHashSet<TraitId>,
-    visible_from_module: VisibleFromModule,
+    visible_from_module: Option<ModuleId>,
     name: Option<&Name>,
     mut callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
 ) -> ControlFlow<()> {
@@ -825,7 +797,7 @@ fn iterate_inherent_methods(
     name: Option<&Name>,
     receiver_ty: Option<&Canonical<Ty>>,
     krate: CrateId,
-    visible_from_module: VisibleFromModule,
+    visible_from_module: Option<ModuleId>,
     callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
 ) -> ControlFlow<()> {
     let def_crates = match def_crates(db, &self_ty.value, krate) {
@@ -833,30 +805,35 @@ fn iterate_inherent_methods(
         None => return ControlFlow::Continue(()),
     };
 
-    let (module, block) = match visible_from_module {
-        VisibleFromModule::Filter(module) => (Some(module), module.containing_block()),
-        VisibleFromModule::IncludeBlock(block) => (None, Some(block)),
-        VisibleFromModule::None => (None, None),
-    };
-
-    if let Some(block_id) = block {
-        if let Some(impls) = db.inherent_impls_in_block(block_id) {
-            impls_for_self_ty(
-                &impls,
-                self_ty,
-                db,
-                env.clone(),
-                name,
-                receiver_ty,
-                module,
-                callback,
-            )?;
+    if let Some(module_id) = visible_from_module {
+        if let Some(block_id) = module_id.containing_block() {
+            if let Some(impls) = db.inherent_impls_in_block(block_id) {
+                impls_for_self_ty(
+                    &impls,
+                    self_ty,
+                    db,
+                    env.clone(),
+                    name,
+                    receiver_ty,
+                    visible_from_module,
+                    callback,
+                )?;
+            }
         }
     }
 
     for krate in def_crates {
         let impls = db.inherent_impls_in_crate(krate);
-        impls_for_self_ty(&impls, self_ty, db, env.clone(), name, receiver_ty, module, callback)?;
+        impls_for_self_ty(
+            &impls,
+            self_ty,
+            db,
+            env.clone(),
+            name,
+            receiver_ty,
+            visible_from_module,
+            callback,
+        )?;
     }
     return ControlFlow::Continue(());
 
diff --git a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
index 0d2daa8dc3..7fbbdb4f5e 100644
--- a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
+++ b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
@@ -156,7 +156,6 @@ fn is_ref_and_impls_iter_method(
             sema.db,
             krate,
             &traits_in_scope,
-            None,
             Some(&wanted_method),
             |_, func| {
                 if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) {
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index 877c5b0cef..59c52dcd04 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -34,13 +34,12 @@ use crate::{
 
 // Assist: extract_function
 //
-// Extracts selected statements and comments into new function.
+// Extracts selected statements into new function.
 //
 // ```
 // fn main() {
 //     let n = 1;
 //     $0let m = n + 2;
-//     // calculate
 //     let k = m + n;$0
 //     let g = 3;
 // }
@@ -55,7 +54,6 @@ use crate::{
 //
 // fn $0fun_name(n: i32) {
 //     let m = n + 2;
-//     // calculate
 //     let k = m + n;
 // }
 // ```
@@ -75,7 +73,6 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
         syntax::NodeOrToken::Node(n) => n,
         syntax::NodeOrToken::Token(t) => t.parent()?,
     };
-
     let body = extraction_target(&node, range)?;
     let container_info = body.analyze_container(&ctx.sema)?;
 
@@ -479,14 +476,11 @@ impl FunctionBody {
     }
 
     fn from_range(parent: ast::StmtList, selected: TextRange) -> FunctionBody {
-        let full_body = parent.syntax().children_with_tokens();
-
-        let mut text_range = full_body
-            .filter(|it| ast::Stmt::can_cast(it.kind()) || it.kind() == COMMENT)
-            .map(|element| element.text_range())
-            .filter(|&range| selected.intersect(range).filter(|it| !it.is_empty()).is_some())
+        let mut text_range = parent
+            .statements()
+            .map(|stmt| stmt.syntax().text_range())
+            .filter(|&stmt| selected.intersect(stmt).filter(|it| !it.is_empty()).is_some())
             .reduce(|acc, stmt| acc.cover(stmt));
-
         if let Some(tail_range) = parent
             .tail_expr()
             .map(|it| it.syntax().text_range())
@@ -1426,7 +1420,6 @@ fn make_body(
     } else {
         FlowHandler::from_ret_ty(fun, &ret_ty)
     };
-
     let block = match &fun.body {
         FunctionBody::Expr(expr) => {
             let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax());
@@ -1448,28 +1441,21 @@ fn make_body(
         FunctionBody::Span { parent, text_range } => {
             let mut elements: Vec<_> = parent
                 .syntax()
-                .children_with_tokens()
+                .children()
                 .filter(|it| text_range.contains_range(it.text_range()))
-                .map(|it| match &it {
-                    syntax::NodeOrToken::Node(n) => syntax::NodeOrToken::Node(
-                        rewrite_body_segment(ctx, &fun.params, &handler, &n),
-                    ),
-                    _ => it,
-                })
+                .map(|it| rewrite_body_segment(ctx, &fun.params, &handler, &it))
                 .collect();
 
-            let mut tail_expr = match &elements.last() {
-                Some(syntax::NodeOrToken::Node(node)) if ast::Expr::can_cast(node.kind()) => {
-                    ast::Expr::cast(node.clone())
-                }
-                _ => None,
+            let mut tail_expr = match elements.pop() {
+                Some(node) => ast::Expr::cast(node.clone()).or_else(|| {
+                    elements.push(node);
+                    None
+                }),
+                None => None,
             };
 
-            match tail_expr {
-                Some(_) => {
-                    elements.pop();
-                }
-                None => match fun.outliving_locals.as_slice() {
+            if tail_expr.is_none() {
+                match fun.outliving_locals.as_slice() {
                     [] => {}
                     [var] => {
                         tail_expr = Some(path_expr_from_local(ctx, var.local));
@@ -1479,27 +1465,22 @@ fn make_body(
                         let expr = make::expr_tuple(exprs);
                         tail_expr = Some(expr);
                     }
-                },
-            };
+                }
+            }
+
+            let elements = elements.into_iter().filter_map(|node| match ast::Stmt::cast(node) {
+                Some(stmt) => Some(stmt),
+                None => {
+                    stdx::never!("block contains non-statement");
+                    None
+                }
+            });
 
             let body_indent = IndentLevel(1);
-            let elements = elements
-                .into_iter()
-                .map(|node_or_token| match &node_or_token {
-                    syntax::NodeOrToken::Node(node) => match ast::Stmt::cast(node.clone()) {
-                        Some(stmt) => {
-                            let indented = stmt.dedent(old_indent).indent(body_indent);
-                            let ast_node = indented.syntax().clone_subtree();
-                            syntax::NodeOrToken::Node(ast_node)
-                        }
-                        _ => node_or_token,
-                    },
-                    _ => node_or_token,
-                })
-                .collect::<Vec<SyntaxElement>>();
+            let elements = elements.map(|stmt| stmt.dedent(old_indent).indent(body_indent));
             let tail_expr = tail_expr.map(|expr| expr.dedent(old_indent).indent(body_indent));
 
-            make::hacky_block_expr_with_comments(elements, tail_expr)
+            make::block_expr(elements, tail_expr)
         }
     };
 
@@ -4111,35 +4092,14 @@ fn foo() {
 "#,
             r#"
 fn foo() {
-    fun_name();
-}
-
-fn $0fun_name() {
     /**/
-    foo();
-    foo();
-    /**/
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn extract_does_not_tear_body_apart() {
-        check_assist(
-            extract_function,
-            r#"
-fn foo() {
-    $0foo();
-}$0
-"#,
-            r#"
-fn foo() {
     fun_name();
+    /**/
 }
 
 fn $0fun_name() {
     foo();
+    foo();
 }
 "#,
         );
@@ -4434,164 +4394,6 @@ pub fn testfn(arg: &mut Foo) {
 fn $0fun_name(arg: &mut Foo) {
     arg.field = 8;
 }
-"#,
-        );
-    }
-
-    #[test]
-    fn extract_function_copies_comment_at_start() {
-        check_assist(
-            extract_function,
-            r#"
-fn func() {
-    let i = 0;
-    $0// comment here!
-    let x = 0;$0
-}
-"#,
-            r#"
-fn func() {
-    let i = 0;
-    fun_name();
-}
-
-fn $0fun_name() {
-    // comment here!
-    let x = 0;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn extract_function_copies_comment_in_between() {
-        check_assist(
-            extract_function,
-            r#"
-fn func() {
-    let i = 0;$0
-    let a = 0;
-    // comment here!
-    let x = 0;$0
-}
-"#,
-            r#"
-fn func() {
-    let i = 0;
-    fun_name();
-}
-
-fn $0fun_name() {
-    let a = 0;
-    // comment here!
-    let x = 0;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn extract_function_copies_comment_at_end() {
-        check_assist(
-            extract_function,
-            r#"
-fn func() {
-    let i = 0;
-    $0let x = 0;
-    // comment here!$0
-}
-"#,
-            r#"
-fn func() {
-    let i = 0;
-    fun_name();
-}
-
-fn $0fun_name() {
-    let x = 0;
-    // comment here!
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn extract_function_copies_comment_indented() {
-        check_assist(
-            extract_function,
-            r#"
-fn func() {
-    let i = 0;
-    $0let x = 0;
-    while(true) {
-        // comment here!
-    }$0
-}
-"#,
-            r#"
-fn func() {
-    let i = 0;
-    fun_name();
-}
-
-fn $0fun_name() {
-    let x = 0;
-    while(true) {
-        // comment here!
-    }
-}
-"#,
-        );
-    }
-
-    // FIXME: we do want to preserve whitespace
-    #[test]
-    fn extract_function_does_not_preserve_whitespace() {
-        check_assist(
-            extract_function,
-            r#"
-fn func() {
-    let i = 0;
-    $0let a = 0;
-
-    let x = 0;$0
-}
-"#,
-            r#"
-fn func() {
-    let i = 0;
-    fun_name();
-}
-
-fn $0fun_name() {
-    let a = 0;
-    let x = 0;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn extract_function_long_form_comment() {
-        check_assist(
-            extract_function,
-            r#"
-fn func() {
-    let i = 0;
-    $0/* a comment */
-    let x = 0;$0
-}
-"#,
-            r#"
-fn func() {
-    let i = 0;
-    fun_name();
-}
-
-fn $0fun_name() {
-    /* a comment */
-    let x = 0;
-}
 "#,
         );
     }
diff --git a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
index cb3fbed219..15025cf0d0 100644
--- a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
+++ b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
@@ -93,9 +93,7 @@ fn get_impl_method(
     let krate = impl_def.module(db).krate();
     let ty = impl_def.self_ty(db);
     let traits_in_scope = scope.visible_traits();
-    ty.iterate_method_candidates(db, krate, &traits_in_scope, None, Some(fn_name), |_, func| {
-        Some(func)
-    })
+    ty.iterate_method_candidates(db, krate, &traits_in_scope, Some(fn_name), |_, func| Some(func))
 }
 
 #[cfg(test)]
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index a072d223e0..e399213731 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -253,7 +253,7 @@ fn enum_variants_with_paths(
 ) {
     let variants = enum_.variants(ctx.db);
 
-    let module = if let Some(module) = ctx.module {
+    let module = if let Some(module) = ctx.scope.module() {
         // Compute path from the completion site if available.
         module
     } else {
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index 2603ad2003..6b6759ebfd 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -62,6 +62,7 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
     Some(())
 }
 
+// FIXME?: Move this functionality to (un)qualified_path, make this module work solely for builtin/known attributes for their inputs?
 fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
     let is_inner = attribute.kind() == ast::AttrKind::Inner;
     let attribute_annotated_item_kind =
diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs
index 29fe096e13..5b22408a2c 100644
--- a/crates/ide_completion/src/completions/attribute/derive.rs
+++ b/crates/ide_completion/src/completions/attribute/derive.rs
@@ -80,7 +80,7 @@ fn flyimport_derive(acc: &mut Completions, ctx: &CompletionContext) -> Option<()
         return None;
     };
     let potential_import_name = ctx.token.to_string();
-    let module = ctx.module?;
+    let module = ctx.scope.module()?;
     let parent = ctx.token.parent()?;
     let user_input_lowercased = potential_import_name.to_lowercase();
     let import_assets = ImportAssets::for_fuzzy_path(
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index b8bab94170..539b423cb3 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -84,19 +84,12 @@ fn complete_methods(
             traits_in_scope.remove(&drop_trait.into());
         }
 
-        receiver.iterate_method_candidates(
-            ctx.db,
-            krate,
-            &traits_in_scope,
-            ctx.module,
-            None,
-            |_ty, func| {
-                if func.self_param(ctx.db).is_some() && seen_methods.insert(func.name(ctx.db)) {
-                    f(func);
-                }
-                None::<()>
-            },
-        );
+        receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
+            if func.self_param(ctx.db).is_some() && seen_methods.insert(func.name(ctx.db)) {
+                f(func);
+            }
+            None::<()>
+        });
     }
 }
 
@@ -272,37 +265,6 @@ fn foo(a: lib::A) { a.$0 }
         );
     }
 
-    #[test]
-    fn test_local_impls() {
-        check(
-            r#"
-//- /lib.rs crate:lib
-pub struct A {}
-mod m {
-    impl super::A {
-        pub fn pub_module_method(&self) {}
-    }
-    fn f() {
-        impl super::A {
-            pub fn pub_foreign_local_method(&self) {}
-        }
-    }
-}
-//- /main.rs crate:main deps:lib
-fn foo(a: lib::A) {
-    impl lib::A {
-        fn local_method(&self) {}
-    }
-    a.$0
-}
-"#,
-            expect![[r#"
-                me local_method()      fn(&self)
-                me pub_module_method() fn(&self)
-            "#]],
-        );
-    }
-
     #[test]
     fn test_doc_hidden_filtering() {
         check(
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index 782a119c9e..8994c25475 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -217,7 +217,7 @@ pub(crate) fn position_for_import(
 }
 
 fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
-    let current_module = ctx.module?;
+    let current_module = ctx.scope.module()?;
     if let Some(dot_receiver) = ctx.dot_receiver() {
         ImportAssets::for_fuzzy_method_call(
             current_module,
diff --git a/crates/ide_completion/src/completions/format_string.rs b/crates/ide_completion/src/completions/format_string.rs
index 0cb8a707e7..87052d020a 100644
--- a/crates/ide_completion/src/completions/format_string.rs
+++ b/crates/ide_completion/src/completions/format_string.rs
@@ -8,15 +8,14 @@ use crate::{context::CompletionContext, CompletionItem, CompletionItemKind, Comp
 
 /// Complete identifiers in format strings.
 pub(crate) fn format_string(acc: &mut Completions, ctx: &CompletionContext) {
-    if true {
-        return;
-    }
-    let string = match ast::String::cast(ctx.token.clone()) {
-        Some(it) if is_format_string(&it) => it,
+    let string = match ast::String::cast(ctx.token.clone())
+        .zip(ast::String::cast(ctx.original_token.clone()))
+    {
+        Some((expanded, original)) if is_format_string(&expanded) => original,
         _ => return,
     };
     let cursor = ctx.position.offset;
-    let lit_start = ctx.token.text_range().start();
+    let lit_start = ctx.original_token.text_range().start();
     let cursor_in_lit = cursor - lit_start;
 
     let prefix = &string.text()[..cursor_in_lit.into()];
@@ -39,72 +38,91 @@ pub(crate) fn format_string(acc: &mut Completions, ctx: &CompletionContext) {
 mod tests {
     use expect_test::{expect, Expect};
 
-    use crate::tests::completion_list_no_kw;
+    use crate::tests::{check_edit, completion_list_no_kw};
 
     fn check(ra_fixture: &str, expect: Expect) {
         let actual = completion_list_no_kw(ra_fixture);
         expect.assert_eq(&actual);
     }
 
+    #[test]
+    fn works_when_wrapped() {
+        check(
+            r#"
+macro_rules! format_args {
+    ($lit:literal $(tt:tt)*) => { 0 },
+}
+macro_rules! print {
+    ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
+}
+fn main() {
+    let foobar = 1;
+    print!("f$0");
+}
+"#,
+            expect![[]],
+        );
+    }
+
     #[test]
     fn no_completion_without_brace() {
         check(
             r#"
 macro_rules! format_args {
-($lit:literal $(tt:tt)*) => { 0 },
+    ($lit:literal $(tt:tt)*) => { 0 },
 }
 fn main() {
-let foobar = 1;
-format_args!("f$0");
+    let foobar = 1;
+    format_args!("f$0");
 }
 "#,
             expect![[]],
         );
     }
 
-    //     #[test]
-    //     fn completes_locals() {
-    //         check_edit(
-    //             "foobar",
-    //             r#"
-    // macro_rules! format_args {
-    //     ($lit:literal $(tt:tt)*) => { 0 },
-    // }
-    // fn main() {
-    //     let foobar = 1;
-    //     format_args!("{f$0");
-    // }
-    // "#,
-    //             r#"
-    // macro_rules! format_args {
-    //     ($lit:literal $(tt:tt)*) => { 0 },
-    // }
-    // fn main() {
-    //     let foobar = 1;
-    //     format_args!("{foobar");
-    // }
-    // "#,
-    //         );
-    //         check_edit(
-    //             "foobar",
-    //             r#"
-    // macro_rules! format_args {
-    //     ($lit:literal $(tt:tt)*) => { 0 },
-    // }
-    // fn main() {
-    //     let foobar = 1;
-    //     format_args!("{$0");
-    // }
-    // "#,
-    //             r#"
-    // macro_rules! format_args {
-    //     ($lit:literal $(tt:tt)*) => { 0 },
-    // }
-    // fn main() {
-    //     let foobar = 1;
-    //     format_args!("{foobar");
-    // }
-    // "#,
-    //         );
-    //     }
+    #[test]
+    fn completes_locals() {
+        check_edit(
+            "foobar",
+            r#"
+macro_rules! format_args {
+    ($lit:literal $(tt:tt)*) => { 0 },
+}
+fn main() {
+    let foobar = 1;
+    format_args!("{f$0");
+}
+"#,
+            r#"
+macro_rules! format_args {
+    ($lit:literal $(tt:tt)*) => { 0 },
+}
+fn main() {
+    let foobar = 1;
+    format_args!("{foobar");
+}
+"#,
+        );
+        check_edit(
+            "foobar",
+            r#"
+macro_rules! format_args {
+    ($lit:literal $(tt:tt)*) => { 0 },
+}
+fn main() {
+    let foobar = 1;
+    format_args!("{$0");
+}
+"#,
+            r#"
+macro_rules! format_args {
+    ($lit:literal $(tt:tt)*) => { 0 },
+}
+fn main() {
+    let foobar = 1;
+    format_args!("{foobar");
+}
+"#,
+        );
+    }
 }
diff --git a/crates/ide_completion/src/completions/mod_.rs b/crates/ide_completion/src/completions/mod_.rs
index 64e992c2e6..beef834581 100644
--- a/crates/ide_completion/src/completions/mod_.rs
+++ b/crates/ide_completion/src/completions/mod_.rs
@@ -22,7 +22,7 @@ pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op
 
     let _p = profile::span("completion::complete_mod");
 
-    let current_module = ctx.module?;
+    let current_module = ctx.scope.module()?;
 
     let module_definition_file =
         current_module.definition_source(ctx.db).file_id.original_file(ctx.db);
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index 85df19f1dd..499791abc9 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -51,7 +51,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
         None => return,
     };
 
-    let context_module = ctx.module;
+    let context_module = ctx.scope.module();
 
     match ctx.completion_location {
         Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
@@ -75,7 +75,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
     match kind {
         Some(PathKind::Vis { .. }) => {
             if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
-                if let Some(current_module) = ctx.module {
+                if let Some(current_module) = ctx.scope.module() {
                     if let Some(next) = current_module
                         .path_to_root(ctx.db)
                         .into_iter()
@@ -189,7 +189,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
                     ty
                 }
                 hir::ModuleDef::BuiltinType(builtin) => {
-                    let module = match ctx.module {
+                    let module = match ctx.scope.module() {
                         Some(it) => it,
                         None => return,
                     };
@@ -205,17 +205,10 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
             let krate = ctx.krate;
             if let Some(krate) = krate {
                 let traits_in_scope = ctx.scope.visible_traits();
-                ty.iterate_path_candidates(
-                    ctx.db,
-                    krate,
-                    &traits_in_scope,
-                    ctx.module,
-                    None,
-                    |_ty, item| {
-                        add_assoc_item(acc, ctx, item);
-                        None::<()>
-                    },
-                );
+                ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
+                    add_assoc_item(acc, ctx, item);
+                    None::<()>
+                });
 
                 // Iterate assoc types separately
                 ty.iterate_assoc_items(ctx.db, krate, |item| {
@@ -246,21 +239,14 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
 
                 let traits_in_scope = ctx.scope.visible_traits();
                 let mut seen = FxHashSet::default();
-                ty.iterate_path_candidates(
-                    ctx.db,
-                    krate,
-                    &traits_in_scope,
-                    ctx.module,
-                    None,
-                    |_ty, item| {
-                        // We might iterate candidates of a trait multiple times here, so deduplicate
-                        // them.
-                        if seen.insert(item) {
-                            add_assoc_item(acc, ctx, item);
-                        }
-                        None::<()>
-                    },
-                );
+                ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
+                    // We might iterate candidates of a trait multiple times here, so deduplicate
+                    // them.
+                    if seen.insert(item) {
+                        add_assoc_item(acc, ctx, item);
+                    }
+                    None::<()>
+                });
             }
         }
         hir::PathResolution::Macro(mac) => acc.add_macro(ctx, None, mac),
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs
index ec1ee292be..b066a46065 100644
--- a/crates/ide_completion/src/completions/record.rs
+++ b/crates/ide_completion/src/completions/record.rs
@@ -59,7 +59,8 @@ pub(crate) fn complete_record_literal(
     }
 
     if let hir::Adt::Struct(strukt) = ctx.expected_type.as_ref()?.as_adt()? {
-        let module = if let Some(module) = ctx.module { module } else { strukt.module(ctx.db) };
+        let module =
+            if let Some(module) = ctx.scope.module() { module } else { strukt.module(ctx.db) };
 
         let path = module.find_use_path(ctx.db, hir::ModuleDef::from(strukt));
 
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index a31a552dad..59c16c08d6 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -103,8 +103,6 @@ pub(crate) struct CompletionContext<'a> {
     pub(super) token: SyntaxToken,
     /// The crate of the current file.
     pub(super) krate: Option<hir::Crate>,
-    /// The crate of the `scope`.
-    pub(super) module: Option<hir::Module>,
     pub(super) expected_name: Option<NameOrNameRef>,
     pub(super) expected_type: Option<Type>,
 
@@ -340,7 +338,7 @@ impl<'a> CompletionContext<'a> {
         attrs: &hir::Attrs,
         defining_crate: hir::Crate,
     ) -> bool {
-        let module = match self.module {
+        let module = match self.scope.module() {
             Some(it) => it,
             None => return false,
         };
@@ -396,7 +394,6 @@ impl<'a> CompletionContext<'a> {
         let token = sema.descend_into_macros_single(original_token.clone());
         let scope = sema.scope_at_offset(&token.parent()?, offset);
         let krate = scope.krate();
-        let module = scope.module();
         let mut locals = vec![];
         scope.process_all_names(&mut |name, scope| {
             if let ScopeDef::Local(local) = scope {
@@ -413,7 +410,6 @@ impl<'a> CompletionContext<'a> {
             original_token,
             token,
             krate,
-            module,
             expected_name: None,
             expected_type: None,
             function_def: None,
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index 5feee48a96..15dacc8e46 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -1354,7 +1354,7 @@ fn main() {
     }
 
     #[test]
-    fn op_function_relevances() {
+    fn op_method_relevances() {
         check_relevance(
             r#"
 #[lang = "sub"]
@@ -1367,30 +1367,7 @@ fn foo(a: u32) { a.$0 }
             expect![[r#"
                 me sub(…) (as Sub) [op_method]
             "#]],
-        );
-        check_relevance(
-            r#"
-struct Foo;
-impl Foo {
-    fn new() -> Self {}
-}
-#[lang = "eq"]
-pub trait PartialEq<Rhs: ?Sized = Self> {
-    fn eq(&self, other: &Rhs) -> bool;
-    fn ne(&self, other: &Rhs) -> bool;
-}
-
-impl PartialEq for Foo {}
-fn main() {
-    Foo::$0
-}
-"#,
-            expect![[r#"
-                fn new() []
-                me eq(…) (as PartialEq) [op_method]
-                me ne(…) (as PartialEq) [op_method]
-            "#]],
-        );
+        )
     }
 
     #[test]
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index 224d4b054e..fc2cb933db 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -67,14 +67,16 @@ fn render(
     );
 
     let ret_type = func.ret_type(db);
-    let is_op_method = func
-        .as_assoc_item(ctx.db())
-        .and_then(|trait_| trait_.containing_trait_or_trait_impl(ctx.db()))
-        .map_or(false, |trait_| completion.is_ops_trait(trait_));
     item.set_relevance(CompletionRelevance {
         type_match: compute_type_match(completion, &ret_type),
         exact_name_match: compute_exact_name_match(completion, &call),
-        is_op_method,
+        is_op_method: match func_type {
+            FuncType::Method(_) => func
+                .as_assoc_item(ctx.db())
+                .and_then(|trait_| trait_.containing_trait_or_trait_impl(ctx.db()))
+                .map_or(false, |trait_| completion.is_ops_trait(trait_)),
+            _ => false,
+        },
         ..CompletionRelevance::default()
     });
 
diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs
index 641a15aa65..e486d9f2b9 100644
--- a/crates/ide_completion/src/render/pattern.rs
+++ b/crates/ide_completion/src/render/pattern.rs
@@ -144,7 +144,7 @@ fn visible_fields(
     fields: &[hir::Field],
     item: impl HasAttrs,
 ) -> Option<(Vec<hir::Field>, bool)> {
-    let module = ctx.completion.module?;
+    let module = ctx.completion.scope.module()?;
     let n_fields = fields.len();
     let fields = fields
         .iter()
diff --git a/crates/ide_completion/src/render/struct_literal.rs b/crates/ide_completion/src/render/struct_literal.rs
index 6be7b9d43b..bfbcc263b1 100644
--- a/crates/ide_completion/src/render/struct_literal.rs
+++ b/crates/ide_completion/src/render/struct_literal.rs
@@ -119,7 +119,7 @@ fn visible_fields(
     fields: &[hir::Field],
     item: impl HasAttrs,
 ) -> Option<(Vec<hir::Field>, bool)> {
-    let module = ctx.completion.module?;
+    let module = ctx.completion.scope.module()?;
     let n_fields = fields.len();
     let fields = fields
         .iter()
diff --git a/crates/ide_completion/src/snippet.rs b/crates/ide_completion/src/snippet.rs
index 3a4f713333..98cd3f8f33 100644
--- a/crates/ide_completion/src/snippet.rs
+++ b/crates/ide_completion/src/snippet.rs
@@ -185,8 +185,11 @@ fn import_edits(
             hir::PathResolution::Def(def) => def.into(),
             _ => return None,
         };
-        let path =
-            ctx.module?.find_use_path_prefixed(ctx.db, item, ctx.config.insert_use.prefix_kind)?;
+        let path = ctx.scope.module()?.find_use_path_prefixed(
+            ctx.db,
+            item,
+            ctx.config.insert_use.prefix_kind,
+        )?;
         Some((path.len() > 1).then(|| ImportEdit {
             import: LocatedImport::new(path.clone(), item, item, None),
             scope: import_scope.clone(),
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index c037c3e0f8..2356750bce 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -505,7 +505,6 @@ fn trait_applicable_items(
             current_crate,
             &trait_candidates,
             None,
-            None,
             |_, assoc| {
                 if required_assoc_items.contains(&assoc) {
                     if let AssocItem::Function(f) = assoc {
@@ -532,7 +531,6 @@ fn trait_applicable_items(
             current_crate,
             &trait_candidates,
             None,
-            None,
             |_, function| {
                 let assoc = function.as_assoc_item(db)?;
                 if required_assoc_items.contains(&assoc) {
diff --git a/crates/ide_ssr/src/resolving.rs b/crates/ide_ssr/src/resolving.rs
index 844b19779a..7902295d29 100644
--- a/crates/ide_ssr/src/resolving.rs
+++ b/crates/ide_ssr/src/resolving.rs
@@ -219,12 +219,10 @@ impl<'db> ResolutionScope<'db> {
         let resolved_qualifier = self.scope.speculative_resolve(&path.qualifier()?)?;
         if let hir::PathResolution::Def(hir::ModuleDef::Adt(adt)) = resolved_qualifier {
             let name = path.segment()?.name_ref()?;
-            let module = self.scope.module()?;
             adt.ty(self.scope.db).iterate_path_candidates(
                 self.scope.db,
-                module.krate(),
+                self.scope.module()?.krate(),
                 &self.scope.visible_traits(),
-                Some(module),
                 None,
                 |_ty, assoc_item| {
                     let item_name = assoc_item.name(self.scope.db)?;
diff --git a/crates/project_model/src/cfg_flag.rs b/crates/project_model/src/cfg_flag.rs
index f3dd8f5133..bfdfd458fb 100644
--- a/crates/project_model/src/cfg_flag.rs
+++ b/crates/project_model/src/cfg_flag.rs
@@ -1,7 +1,7 @@
 //! Parsing of CfgFlags as command line arguments, as in
 //!
 //! rustc main.rs --cfg foo --cfg 'feature="bar"'
-use std::{fmt, str::FromStr};
+use std::str::FromStr;
 
 use cfg::CfgOptions;
 
@@ -48,16 +48,3 @@ impl Extend<CfgFlag> for CfgOptions {
         }
     }
 }
-
-impl fmt::Display for CfgFlag {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            CfgFlag::Atom(atom) => f.write_str(atom),
-            CfgFlag::KeyValue { key, value } => {
-                f.write_str(key)?;
-                f.write_str("=")?;
-                f.write_str(value)
-            }
-        }
-    }
-}
diff --git a/crates/project_model/src/rustc_cfg.rs b/crates/project_model/src/rustc_cfg.rs
index 17e244d064..669aea0cd1 100644
--- a/crates/project_model/src/rustc_cfg.rs
+++ b/crates/project_model/src/rustc_cfg.rs
@@ -19,42 +19,38 @@ pub(crate) fn get(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Ve
     }
 
     match get_rust_cfgs(cargo_toml, target) {
-        Ok(rustc_cfgs) => {
-            tracing::debug!(
-                "rustc cfgs found: {:?}",
-                rustc_cfgs
-                    .lines()
-                    .map(|it| it.parse::<CfgFlag>().map(|it| it.to_string()))
-                    .collect::<Vec<_>>()
-            );
-            res.extend(rustc_cfgs.lines().filter_map(|it| it.parse().ok()));
-        }
-        Err(e) => tracing::error!("failed to get rustc cfgs: {e:?}"),
+        Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())),
+        Err(e) => tracing::error!("failed to get rustc cfgs: {:#}", e),
     }
 
     res
 }
 
 fn get_rust_cfgs(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Result<String> {
-    if let Some(cargo_toml) = cargo_toml {
-        let mut cargo_config = Command::new(toolchain::cargo());
-        cargo_config
-            .current_dir(cargo_toml.parent())
-            .args(&["-Z", "unstable-options", "rustc", "--print", "cfg"])
-            .env("RUSTC_BOOTSTRAP", "1");
-        if let Some(target) = target {
-            cargo_config.args(&["--target", target]);
+    let cargo_rust_cfgs = match cargo_toml {
+        Some(cargo_toml) => {
+            let mut cargo_config = Command::new(toolchain::cargo());
+            cargo_config
+                .current_dir(cargo_toml.parent())
+                .args(&["-Z", "unstable-options", "rustc", "--print", "cfg"])
+                .env("RUSTC_BOOTSTRAP", "1");
+            if let Some(target) = target {
+                cargo_config.args(&["--target", target]);
+            }
+            utf8_stdout(cargo_config).ok()
         }
-        match utf8_stdout(cargo_config) {
-            Ok(it) => return Ok(it),
-            Err(e) => tracing::debug!("{e:?}: falling back to querying rustc for cfgs"),
+        None => None,
+    };
+    match cargo_rust_cfgs {
+        Some(stdout) => Ok(stdout),
+        None => {
+            // using unstable cargo features failed, fall back to using plain rustc
+            let mut cmd = Command::new(toolchain::rustc());
+            cmd.args(&["--print", "cfg", "-O"]);
+            if let Some(target) = target {
+                cmd.args(&["--target", target]);
+            }
+            utf8_stdout(cmd)
         }
     }
-    // using unstable cargo features failed, fall back to using plain rustc
-    let mut cmd = Command::new(toolchain::rustc());
-    cmd.args(&["--print", "cfg", "-O"]);
-    if let Some(target) = target {
-        cmd.args(&["--target", target]);
-    }
-    utf8_stdout(cmd)
 }
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 8a1bcebbf6..f80c5e382c 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -329,31 +329,6 @@ pub fn block_expr(
     ast_from_text(&format!("fn f() {}", buf))
 }
 
-/// Ideally this function wouldn't exist since it involves manual indenting.
-/// It differs from `make::block_expr` by also supporting comments.
-///
-/// FIXME: replace usages of this with the mutable syntax tree API
-pub fn hacky_block_expr_with_comments(
-    elements: impl IntoIterator<Item = crate::SyntaxElement>,
-    tail_expr: Option<ast::Expr>,
-) -> ast::BlockExpr {
-    let mut buf = "{\n".to_string();
-    for node_or_token in elements.into_iter() {
-        match node_or_token {
-            rowan::NodeOrToken::Node(n) => format_to!(buf, "    {}\n", n),
-            rowan::NodeOrToken::Token(t) if t.kind() == SyntaxKind::COMMENT => {
-                format_to!(buf, "    {}\n", t)
-            }
-            _ => (),
-        }
-    }
-    if let Some(tail_expr) = tail_expr {
-        format_to!(buf, "    {}\n", tail_expr);
-    }
-    buf += "}";
-    ast_from_text(&format!("fn f() {}", buf))
-}
-
 pub fn expr_unit() -> ast::Expr {
     expr_from_text("()")
 }


Test patch:
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index 0ad4b3bc34..ab5ef91657 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -507,7 +507,6 @@ fn doctest_extract_function() {
 fn main() {
     let n = 1;
     $0let m = n + 2;
-    // calculate
     let k = m + n;$0
     let g = 3;
 }
@@ -521,7 +520,6 @@ fn main() {
 
 fn $0fun_name(n: i32) {
     let m = n + 2;
-    // calculate
     let k = m + n;
 }
 "#####,