Repository: rust-lang/rust-analyzer
Pull request #17967 (2024-08-26 09:00:13)

internal: Lay basic ground work for standalone mbe tests
Most of our mbe hir-def tests don't actually do anything name res relevant, we can (and should) move those down the stack into `mbe/hir-expand`.

Veykril: @bors r+
bors: :pushpin: Commit 68e81f9b9b46ccdfc5bb2a11ed4a0f846c2bc502 has been approved by `Veykril`

It is now in the [queue](https://bors.rust-lang.org/homu/queue/rust-analyzer) for this repository.

<!-- @bors r=Veykril 68e81f9b9b46ccdfc5bb2a11ed4a0f846c2bc502 -->
<!-- homu: {"type":"Approved","sha":"68e81f9b9b46ccdfc5bb2a11ed4a0f846c2bc502","approver":"Veykril","queue":"https://bors.rust-lang.org/homu/queue/rust-analyzer"} -->
bors: :hourglass: Testing commit 68e81f9b9b46ccdfc5bb2a11ed4a0f846c2bc502 with merge c1150dd079b3535a595d1e9c371406ea32d66ac4...
<!-- homu: {"type":"BuildStarted","head_sha":"68e81f9b9b46ccdfc5bb2a11ed4a0f846c2bc502","merge_sha":"c1150dd079b3535a595d1e9c371406ea32d66ac4"} -->
bors: :broken_heart: Test failed - [checks-actions](https://github.com/rust-lang/rust-analyzer/actions/runs/10556936192/job/29243842240)
<!-- homu: {"type":"BuildFailed","builder_url":"https://github.com/rust-lang/rust-analyzer/actions/runs/10556936192/job/29243842240","builder_name":"checks-actions"} -->
Veykril: @bors r+
bors: :pushpin: Commit c8c59606e8b27f1e7672d8914c405d990e910e9a has been approved by `Veykril`

It is now in the [queue](https://bors.rust-lang.org/homu/queue/rust-analyzer) for this repository.

<!-- @bors r=Veykril c8c59606e8b27f1e7672d8914c405d990e910e9a -->
<!-- homu: {"type":"Approved","sha":"c8c59606e8b27f1e7672d8914c405d990e910e9a","approver":"Veykril","queue":"https://bors.rust-lang.org/homu/queue/rust-analyzer"} -->
bors: :hourglass: Testing commit c8c59606e8b27f1e7672d8914c405d990e910e9a with merge 210b40df3b9c7bbc88752daa62fdb264b9378542...
<!-- homu: {"type":"BuildStarted","head_sha":"c8c59606e8b27f1e7672d8914c405d990e910e9a","merge_sha":"210b40df3b9c7bbc88752daa62fdb264b9378542"} -->
bors: :broken_heart: Test failed - [checks-actions](https://github.com/rust-lang/rust-analyzer/actions/runs/10560654304/job/29254979681)
<!-- homu: {"type":"BuildFailed","builder_url":"https://github.com/rust-lang/rust-analyzer/actions/runs/10560654304/job/29254979681","builder_name":"checks-actions"} -->
bors: :umbrella: The latest upstream changes (presumably #17814) made this pull request unmergeable. Please [resolve the merge conflicts](https://rustc-dev-guide.rust-lang.org/git.html#rebasing-and-conflicts).

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 4e9319f13a..8733508be7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1029,6 +1029,7 @@ version = "0.0.0"
 dependencies = [
  "arrayvec",
  "cov-mark",
+ "expect-test",
  "intern",
  "parser",
  "rustc-hash",
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index 032e4a8e4a..3616fa9fd8 100644
--- a/crates/base-db/src/input.rs
+++ b/crates/base-db/src/input.rs
@@ -374,6 +374,37 @@ impl CrateGraph {
         self.arena.alloc(data)
     }
 
+    /// Remove the crate from crate graph. If any crates depend on this crate, the dependency would be replaced
+    /// with the second input.
+    pub fn remove_and_replace(
+        &mut self,
+        id: CrateId,
+        replace_with: CrateId,
+    ) -> Result<(), CyclicDependenciesError> {
+        for (x, data) in self.arena.iter() {
+            if x == id {
+                continue;
+            }
+            for edge in &data.dependencies {
+                if edge.crate_id == id {
+                    self.check_cycle_after_dependency(edge.crate_id, replace_with)?;
+                }
+            }
+        }
+        // if everything was ok, start to replace
+        for (x, data) in self.arena.iter_mut() {
+            if x == id {
+                continue;
+            }
+            for edge in &mut data.dependencies {
+                if edge.crate_id == id {
+                    edge.crate_id = replace_with;
+                }
+            }
+        }
+        Ok(())
+    }
+
     pub fn add_dep(
         &mut self,
         from: CrateId,
@@ -381,17 +412,26 @@ impl CrateGraph {
     ) -> Result<(), CyclicDependenciesError> {
         let _p = tracing::info_span!("add_dep").entered();
 
-        // Check if adding a dep from `from` to `to` creates a cycle. To figure
-        // that out, look for a  path in the *opposite* direction, from `to` to
-        // `from`.
-        if let Some(path) = self.find_path(&mut FxHashSet::default(), dep.crate_id, from) {
+        self.check_cycle_after_dependency(from, dep.crate_id)?;
+
+        self.arena[from].add_dep(dep);
+        Ok(())
+    }
+
+    /// Check if adding a dep from `from` to `to` creates a cycle. To figure
+    /// that out, look for a  path in the *opposite* direction, from `to` to
+    /// `from`.
+    fn check_cycle_after_dependency(
+        &self,
+        from: CrateId,
+        to: CrateId,
+    ) -> Result<(), CyclicDependenciesError> {
+        if let Some(path) = self.find_path(&mut FxHashSet::default(), to, from) {
             let path = path.into_iter().map(|it| (it, self[it].display_name.clone())).collect();
             let err = CyclicDependenciesError { path };
-            assert!(err.from().0 == from && err.to().0 == dep.crate_id);
+            assert!(err.from().0 == from && err.to().0 == to);
             return Err(err);
         }
-
-        self.arena[from].add_dep(dep);
         Ok(())
     }
 
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index 19c3c9c43f..7ae2fa1e39 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -55,6 +55,7 @@ pub use crate::files::{AstId, ErasedAstId, FileRange, InFile, InMacroFile, InRea
 
 pub use mbe::{DeclarativeMacro, ValueResult};
 pub use span::{HirFileId, MacroCallId, MacroFileId};
+pub use syntax_bridge::insert_whitespace_into_node;
 
 pub mod tt {
     pub use span::Span;
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index e5c493ac56..8b6cde975f 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -11,7 +11,7 @@ use hir_def::{
     ConstBlockLoc, EnumVariantId, GeneralConstId, StaticId,
 };
 use hir_expand::Lookup;
-use stdx::{never, IsNoneOr};
+use stdx::never;
 use triomphe::Arc;
 
 use crate::{
@@ -184,22 +184,6 @@ pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option<u128> {
     }
 }
 
-pub fn try_const_isize(db: &dyn HirDatabase, c: &Const) -> Option<i128> {
-    match &c.data(Interner).value {
-        chalk_ir::ConstValue::BoundVar(_) => None,
-        chalk_ir::ConstValue::InferenceVar(_) => None,
-        chalk_ir::ConstValue::Placeholder(_) => None,
-        chalk_ir::ConstValue::Concrete(c) => match &c.interned {
-            ConstScalar::Bytes(it, _) => Some(i128::from_le_bytes(pad16(it, true))),
-            ConstScalar::UnevaluatedConst(c, subst) => {
-                let ec = db.const_eval(*c, subst.clone(), None).ok()?;
-                try_const_isize(db, &ec)
-            }
-            _ => None,
-        },
-    }
-}
-
 pub(crate) fn const_eval_recover(
     _: &dyn HirDatabase,
     _: &Cycle,
@@ -272,8 +256,8 @@ pub(crate) fn const_eval_discriminant_variant(
 ) -> Result<i128, ConstEvalError> {
     let def = variant_id.into();
     let body = db.body(def);
-    let loc = variant_id.lookup(db.upcast());
     if body.exprs[body.body_expr] == Expr::Missing {
+        let loc = variant_id.lookup(db.upcast());
         let prev_idx = loc.index.checked_sub(1);
         let value = match prev_idx {
             Some(prev_idx) => {
@@ -285,21 +269,13 @@ pub(crate) fn const_eval_discriminant_variant(
         };
         return Ok(value);
     }
-
-    let repr = db.enum_data(loc.parent).repr;
-    let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed());
-
     let mir_body = db.monomorphized_mir_body(
         def,
         Substitution::empty(Interner),
         db.trait_environment_for_body(def),
     )?;
     let c = interpret_mir(db, mir_body, false, None).0?;
-    let c = if is_signed {
-        try_const_isize(db, &c).unwrap()
-    } else {
-        try_const_usize(db, &c).unwrap() as i128
-    };
+    let c = try_const_usize(db, &c).unwrap() as i128;
     Ok(c)
 }
 
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 9536f12558..527ecf04c3 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -136,6 +136,7 @@ pub use {
         },
         hygiene::{marks_rev, SyntaxContextExt},
         inert_attr_macro::AttributeTemplate,
+        insert_whitespace_into_node,
         name::Name,
         proc_macro::{ProcMacros, ProcMacrosBuilder},
         tt, ExpandResult, HirFileId, HirFileIdExt, MacroFileId, MacroFileIdExt,
@@ -2207,35 +2208,6 @@ impl Function {
         db.function_data(self.id).is_async()
     }
 
-    pub fn returns_impl_future(self, db: &dyn HirDatabase) -> bool {
-        if self.is_async(db) {
-            return true;
-        }
-
-        let Some(impl_traits) = self.ret_type(db).as_impl_traits(db) else { return false };
-        let Some(future_trait_id) =
-            db.lang_item(self.ty(db).env.krate, LangItem::Future).and_then(|t| t.as_trait())
-        else {
-            return false;
-        };
-        let Some(sized_trait_id) =
-            db.lang_item(self.ty(db).env.krate, LangItem::Sized).and_then(|t| t.as_trait())
-        else {
-            return false;
-        };
-
-        let mut has_impl_future = false;
-        impl_traits
-            .filter(|t| {
-                let fut = t.id == future_trait_id;
-                has_impl_future |= fut;
-                !fut && t.id != sized_trait_id
-            })
-            // all traits but the future trait must be auto traits
-            .all(|t| t.is_auto(db))
-            && has_impl_future
-    }
-
     /// Does this function have `#[test]` attribute?
     pub fn is_test(self, db: &dyn HirDatabase) -> bool {
         db.function_data(self.id).attrs.is_test()
diff --git a/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs b/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs
deleted file mode 100644
index fafc3448a8..0000000000
--- a/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs
+++ /dev/null
@@ -1,206 +0,0 @@
-use hir::Semantics;
-use ide_db::{
-    assists::{AssistId, AssistKind},
-    source_change::SourceChangeBuilder,
-    RootDatabase,
-};
-use syntax::{ast, AstNode};
-
-use crate::{AssistContext, Assists};
-
-// Assist: explicit_enum_discriminant
-//
-// Adds explicit discriminant to all enum variants.
-//
-// ```
-// enum TheEnum$0 {
-//     Foo,
-//     Bar,
-//     Baz = 42,
-//     Quux,
-// }
-// ```
-// ->
-// ```
-// enum TheEnum {
-//     Foo = 0,
-//     Bar = 1,
-//     Baz = 42,
-//     Quux = 43,
-// }
-// ```
-pub(crate) fn explicit_enum_discriminant(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
-    let enum_node = ctx.find_node_at_offset::<ast::Enum>()?;
-    let enum_def = ctx.sema.to_def(&enum_node)?;
-
-    let is_data_carrying = enum_def.is_data_carrying(ctx.db());
-    let has_primitive_repr = enum_def.repr(ctx.db()).and_then(|repr| repr.int).is_some();
-
-    // Data carrying enums without a primitive repr have no stable discriminants.
-    if is_data_carrying && !has_primitive_repr {
-        return None;
-    }
-
-    let variant_list = enum_node.variant_list()?;
-
-    // Don't offer the assist if the enum has no variants or if all variants already have an
-    // explicit discriminant.
-    if variant_list.variants().all(|variant_node| variant_node.expr().is_some()) {
-        return None;
-    }
-
-    acc.add(
-        AssistId("explicit_enum_discriminant", AssistKind::RefactorRewrite),
-        "Add explicit enum discriminants",
-        enum_node.syntax().text_range(),
-        |builder| {
-            for variant_node in variant_list.variants() {
-                add_variant_discriminant(&ctx.sema, builder, &variant_node);
-            }
-        },
-    );
-
-    Some(())
-}
-
-fn add_variant_discriminant(
-    sema: &Semantics<'_, RootDatabase>,
-    builder: &mut SourceChangeBuilder,
-    variant_node: &ast::Variant,
-) {
-    if variant_node.expr().is_some() {
-        return;
-    }
-
-    let Some(variant_def) = sema.to_def(variant_node) else {
-        return;
-    };
-    let Ok(discriminant) = variant_def.eval(sema.db) else {
-        return;
-    };
-
-    let variant_range = variant_node.syntax().text_range();
-
-    builder.insert(variant_range.end(), format!(" = {discriminant}"));
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::{check_assist, check_assist_not_applicable};
-
-    use super::explicit_enum_discriminant;
-
-    #[test]
-    fn non_primitive_repr_non_data_bearing_add_discriminant() {
-        check_assist(
-            explicit_enum_discriminant,
-            r#"
-enum TheEnum$0 {
-    Foo,
-    Bar,
-    Baz = 42,
-    Quux,
-    FooBar = -5,
-    FooBaz,
-}
-"#,
-            r#"
-enum TheEnum {
-    Foo = 0,
-    Bar = 1,
-    Baz = 42,
-    Quux = 43,
-    FooBar = -5,
-    FooBaz = -4,
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn primitive_repr_data_bearing_add_discriminant() {
-        check_assist(
-            explicit_enum_discriminant,
-            r#"
-#[repr(u8)]
-$0enum TheEnum {
-    Foo { x: u32 },
-    Bar,
-    Baz(String),
-    Quux,
-}
-"#,
-            r#"
-#[repr(u8)]
-enum TheEnum {
-    Foo { x: u32 } = 0,
-    Bar = 1,
-    Baz(String) = 2,
-    Quux = 3,
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn non_primitive_repr_data_bearing_not_applicable() {
-        check_assist_not_applicable(
-            explicit_enum_discriminant,
-            r#"
-enum TheEnum$0 {
-    Foo,
-    Bar(u16),
-    Baz,
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn primitive_repr_non_data_bearing_add_discriminant() {
-        check_assist(
-            explicit_enum_discriminant,
-            r#"
-#[repr(i64)]
-enum TheEnum {
-    Foo = 1 << 63,
-    Bar,
-    Baz$0 = 0x7fff_ffff_ffff_fffe,
-    Quux,
-}
-"#,
-            r#"
-#[repr(i64)]
-enum TheEnum {
-    Foo = 1 << 63,
-    Bar = -9223372036854775807,
-    Baz = 0x7fff_ffff_ffff_fffe,
-    Quux = 9223372036854775807,
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn discriminants_already_explicit_not_applicable() {
-        check_assist_not_applicable(
-            explicit_enum_discriminant,
-            r#"
-enum TheEnum$0 {
-    Foo = 0,
-    Bar = 4,
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn empty_enum_not_applicable() {
-        check_assist_not_applicable(
-            explicit_enum_discriminant,
-            r#"
-enum TheEnum$0 {}
-"#,
-        );
-    }
-}
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index b2ccd1fde8..c88cb3d5ea 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -136,7 +136,6 @@ mod handlers {
     mod destructure_tuple_binding;
     mod desugar_doc_comment;
     mod expand_glob_import;
-    mod explicit_enum_discriminant;
     mod extract_expressions_from_format_string;
     mod extract_function;
     mod extract_module;
@@ -267,7 +266,6 @@ mod handlers {
             destructure_tuple_binding::destructure_tuple_binding,
             destructure_struct_binding::destructure_struct_binding,
             expand_glob_import::expand_glob_import,
-            explicit_enum_discriminant::explicit_enum_discriminant,
             extract_expressions_from_format_string::extract_expressions_from_format_string,
             extract_struct_from_enum_variant::extract_struct_from_enum_variant,
             extract_type_alias::extract_type_alias,
diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs
index e93bb8db71..fc6e1ebf05 100644
--- a/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -31,14 +31,14 @@
 //! }
 //! ```
 
-use hir::{HasAttrs, Name};
+use hir::HasAttrs;
 use ide_db::{
     documentation::HasDocs, path_transform::PathTransform,
     syntax_helpers::insert_whitespace_into_node, traits::get_missing_assoc_items, SymbolKind,
 };
 use syntax::{
-    ast::{self, edit_in_place::AttrsOwnerEdit, make, HasGenericArgs, HasTypeBounds},
-    format_smolstr, ted, AstNode, SmolStr, SyntaxElement, SyntaxKind, TextRange, ToSmolStr, T,
+    ast::{self, edit_in_place::AttrsOwnerEdit, HasTypeBounds},
+    format_smolstr, AstNode, SmolStr, SyntaxElement, SyntaxKind, TextRange, ToSmolStr, T,
 };
 use text_edit::TextEdit;
 
@@ -178,36 +178,12 @@ fn add_function_impl(
     func: hir::Function,
     impl_def: hir::Impl,
 ) {
-    let fn_name = &func.name(ctx.db);
-    let sugar: &[_] = if func.is_async(ctx.db) {
-        &[AsyncSugaring::Async, AsyncSugaring::Desugar]
-    } else if func.returns_impl_future(ctx.db) {
-        &[AsyncSugaring::Plain, AsyncSugaring::Resugar]
-    } else {
-        &[AsyncSugaring::Plain]
-    };
-    for &sugaring in sugar {
-        add_function_impl_(acc, ctx, replacement_range, func, impl_def, fn_name, sugaring);
-    }
-}
+    let fn_name = func.name(ctx.db);
 
-fn add_function_impl_(
-    acc: &mut Completions,
-    ctx: &CompletionContext<'_>,
-    replacement_range: TextRange,
-    func: hir::Function,
-    impl_def: hir::Impl,
-    fn_name: &Name,
-    async_sugaring: AsyncSugaring,
-) {
-    let async_ = if let AsyncSugaring::Async | AsyncSugaring::Resugar = async_sugaring {
-        "async "
-    } else {
-        ""
-    };
+    let is_async = func.is_async(ctx.db);
     let label = format_smolstr!(
         "{}fn {}({})",
-        async_,
+        if is_async { "async " } else { "" },
         fn_name.display(ctx.db, ctx.edition),
         if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." }
     );
@@ -219,14 +195,22 @@ fn add_function_impl_(
     });
 
     let mut item = CompletionItem::new(completion_kind, replacement_range, label, ctx.edition);
-    item.lookup_by(format!("{}fn {}", async_, fn_name.display(ctx.db, ctx.edition)))
-        .set_documentation(func.docs(ctx.db))
-        .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() });
+    item.lookup_by(format!(
+        "{}fn {}",
+        if is_async { "async " } else { "" },
+        fn_name.display(ctx.db, ctx.edition)
+    ))
+    .set_documentation(func.docs(ctx.db))
+    .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() });
 
     if let Some(source) = ctx.sema.source(func) {
-        if let Some(transformed_fn) =
-            get_transformed_fn(ctx, source.value, impl_def, async_sugaring)
-        {
+        let assoc_item = ast::AssocItem::Fn(source.value);
+        if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) {
+            let transformed_fn = match transformed_item {
+                ast::AssocItem::Fn(func) => func,
+                _ => unreachable!(),
+            };
+
             let function_decl = function_declaration(&transformed_fn, source.file_id.is_macro());
             match ctx.config.snippet_cap {
                 Some(cap) => {
@@ -243,14 +227,6 @@ fn add_function_impl_(
     }
 }
 
-#[derive(Copy, Clone)]
-enum AsyncSugaring {
-    Desugar,
-    Resugar,
-    Async,
-    Plain,
-}
-
 /// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc.
 fn get_transformed_assoc_item(
     ctx: &CompletionContext<'_>,
@@ -275,82 +251,6 @@ fn get_transformed_assoc_item(
     Some(assoc_item)
 }
 
-/// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc.
-fn get_transformed_fn(
-    ctx: &CompletionContext<'_>,
-    fn_: ast::Fn,
-    impl_def: hir::Impl,
-    async_: AsyncSugaring,
-) -> Option<ast::Fn> {
-    let trait_ = impl_def.trait_(ctx.db)?;
-    let source_scope = &ctx.sema.scope(fn_.syntax())?;
-    let target_scope = &ctx.sema.scope(ctx.sema.source(impl_def)?.syntax().value)?;
-    let transform = PathTransform::trait_impl(
-        target_scope,
-        source_scope,
-        trait_,
-        ctx.sema.source(impl_def)?.value,
-    );
-
-    let fn_ = fn_.clone_for_update();
-    // FIXME: Paths in nested macros are not handled well. See
-    // `macro_generated_assoc_item2` test.
-    transform.apply(fn_.syntax());
-    fn_.remove_attrs_and_docs();
-    match async_ {
-        AsyncSugaring::Desugar => {
-            match fn_.ret_type() {
-                Some(ret_ty) => {
-                    let ty = ret_ty.ty()?;
-                    ted::replace(
-                        ty.syntax(),
-                        make::ty(&format!("impl Future<Output = {ty}>"))
-                            .syntax()
-                            .clone_for_update(),
-                    );
-                }
-                None => ted::append_child(
-                    fn_.param_list()?.syntax(),
-                    make::ret_type(make::ty("impl Future<Output = ()>"))
-                        .syntax()
-                        .clone_for_update(),
-                ),
-            }
-            fn_.async_token().unwrap().detach();
-        }
-        AsyncSugaring::Resugar => {
-            let ty = fn_.ret_type()?.ty()?;
-            match &ty {
-                // best effort guessing here
-                ast::Type::ImplTraitType(t) => {
-                    let output = t.type_bound_list()?.bounds().find_map(|b| match b.ty()? {
-                        ast::Type::PathType(p) => {
-                            let p = p.path()?.segment()?;
-                            if p.name_ref()?.text() != "Future" {
-                                return None;
-                            }
-                            match p.generic_arg_list()?.generic_args().next()? {
-                                ast::GenericArg::AssocTypeArg(a)
-                                    if a.name_ref()?.text() == "Output" =>
-                                {
-                                    a.ty()
-                                }
-                                _ => None,
-                            }
-                        }
-                        _ => None,
-                    })?;
-                    ted::replace(ty.syntax(), output.syntax());
-                }
-                _ => (),
-            }
-            ted::prepend_child(fn_.syntax(), make::token(T![async]));
-        }
-        AsyncSugaring::Async | AsyncSugaring::Plain => (),
-    }
-    Some(fn_)
-}
-
 fn add_type_alias_impl(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
@@ -1501,134 +1401,6 @@ trait Tr {
 impl Tr for () {
     type Item = $0;
 }
-"#,
-        );
-    }
-
-    #[test]
-    fn impl_fut() {
-        check_edit(
-            "fn foo",
-            r#"
-//- minicore: future, send, sized
-use core::future::Future;
-
-trait DesugaredAsyncTrait {
-    fn foo(&self) -> impl Future<Output = usize> + Send;
-}
-
-impl DesugaredAsyncTrait for () {
-    $0
-}
-"#,
-            r#"
-use core::future::Future;
-
-trait DesugaredAsyncTrait {
-    fn foo(&self) -> impl Future<Output = usize> + Send;
-}
-
-impl DesugaredAsyncTrait for () {
-    fn foo(&self) -> impl Future<Output = usize> + Send {
-    $0
-}
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn impl_fut_resugared() {
-        check_edit(
-            "async fn foo",
-            r#"
-//- minicore: future, send, sized
-use core::future::Future;
-
-trait DesugaredAsyncTrait {
-    fn foo(&self) -> impl Future<Output = usize> + Send;
-}
-
-impl DesugaredAsyncTrait for () {
-    $0
-}
-"#,
-            r#"
-use core::future::Future;
-
-trait DesugaredAsyncTrait {
-    fn foo(&self) -> impl Future<Output = usize> + Send;
-}
-
-impl DesugaredAsyncTrait for () {
-    async fn foo(&self) -> usize {
-    $0
-}
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn async_desugared() {
-        check_edit(
-            "fn foo",
-            r#"
-//- minicore: future, send, sized
-use core::future::Future;
-
-trait DesugaredAsyncTrait {
-    async fn foo(&self) -> usize;
-}
-
-impl DesugaredAsyncTrait for () {
-    $0
-}
-"#,
-            r#"
-use core::future::Future;
-
-trait DesugaredAsyncTrait {
-    async fn foo(&self) -> usize;
-}
-
-impl DesugaredAsyncTrait for () {
-     fn foo(&self) -> impl Future<Output = usize> {
-    $0
-}
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn async_() {
-        check_edit(
-            "async fn foo",
-            r#"
-//- minicore: future, send, sized
-use core::future::Future;
-
-trait DesugaredAsyncTrait {
-    async fn foo(&self) -> usize;
-}
-
-impl DesugaredAsyncTrait for () {
-    $0
-}
-"#,
-            r#"
-use core::future::Future;
-
-trait DesugaredAsyncTrait {
-    async fn foo(&self) -> usize;
-}
-
-impl DesugaredAsyncTrait for () {
-    async fn foo(&self) -> usize {
-    $0
-}
-}
 "#,
         );
     }
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index 3cf29987fa..8a2068e903 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -36,7 +36,7 @@ pub mod generated {
 pub mod syntax_helpers {
     pub mod format_string;
     pub mod format_string_exprs;
-    pub mod insert_whitespace_into_node;
+    pub use hir::insert_whitespace_into_node;
     pub mod node_ext;
 
     pub use parser::LexedStr;
diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml
index 756d42ef57..413a0254a6 100644
--- a/crates/mbe/Cargo.toml
+++ b/crates/mbe/Cargo.toml
@@ -30,6 +30,7 @@ syntax-bridge.workspace = true
 
 [dev-dependencies]
 test-utils.workspace = true
+expect-test.workspace = true
 
 [features]
 in-rust-tree = ["parser/in-rust-tree", "tt/in-rust-tree", "syntax/in-rust-tree"]
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 88785537c7..f7b6e8f54b 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -11,6 +11,8 @@ mod parser;
 
 #[cfg(test)]
 mod benchmark;
+#[cfg(test)]
+mod tests;
 
 use span::{Edition, Span, SyntaxContextId};
 use syntax_bridge::to_parser_input;
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index 4fc9ef3d36..7834238ace 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -1554,6 +1554,6 @@ fn add_proc_macro_dep(crate_graph: &mut CrateGraph, from: CrateId, to: CrateId,
 
 fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
     if let Err(err) = graph.add_dep(from, dep) {
-        tracing::warn!("{}", err)
+        tracing::error!("{}", err)
     }
 }
diff --git a/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs b/crates/syntax-bridge/src/insert_whitespace_into_node.rs
similarity index 97%
rename from crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
rename to crates/syntax-bridge/src/insert_whitespace_into_node.rs
index dd4a665e8e..a61fca9ec5 100644
--- a/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
+++ b/crates/syntax-bridge/src/insert_whitespace_into_node.rs
@@ -7,8 +7,6 @@ use syntax::{
     SyntaxNode, SyntaxToken, WalkEvent, T,
 };
 
-// FIXME: It would also be cool to share logic here and in the mbe tests,
-// which are pretty unreadable at the moment.
 /// Renders a [`SyntaxNode`] with whitespace inserted between tokens that require them.
 pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode {
     let mut indent = 0;
diff --git a/crates/syntax-bridge/src/lib.rs b/crates/syntax-bridge/src/lib.rs
index 56e43e82ed..f23b33127f 100644
--- a/crates/syntax-bridge/src/lib.rs
+++ b/crates/syntax-bridge/src/lib.rs
@@ -17,6 +17,7 @@ use tt::{
     token_to_literal,
 };
 
+pub mod insert_whitespace_into_node;
 mod to_parser_input;
 pub use to_parser_input::to_parser_input;
 // FIXME: we probably should re-think  `token_tree_to_syntax_node` interfaces
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 2eb9c1ec5a..abf1a1f382 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -1162,7 +1162,7 @@ pub mod tokens {
 
     pub(super) static SOURCE_FILE: LazyLock<Parse<SourceFile>> = LazyLock::new(|| {
         SourceFile::parse(
-            "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, async { let _ @ [] })\n;\n\nimpl A for B where: {}", Edition::CURRENT,
+            "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, { let _ @ [] })\n;\n\nimpl A for B where: {}", Edition::CURRENT,
         )
     });
 
diff --git a/crates/syntax/src/ted.rs b/crates/syntax/src/ted.rs
index 8592df1597..29788d05e8 100644
--- a/crates/syntax/src/ted.rs
+++ b/crates/syntax/src/ted.rs
@@ -147,11 +147,6 @@ pub fn append_child_raw(node: &(impl Into<SyntaxNode> + Clone), child: impl Elem
     insert_raw(position, child);
 }
 
-pub fn prepend_child(node: &(impl Into<SyntaxNode> + Clone), child: impl Element) {
-    let position = Position::first_child_of(node);
-    insert(position, child);
-}
-
 fn ws_before(position: &Position, new: &SyntaxElement) -> Option<SyntaxToken> {
     let prev = match &position.repr {
         PositionRepr::FirstChild(_) => return None,
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs
index bc40e03c5a..a26444e9ea 100644
--- a/crates/vfs/src/lib.rs
+++ b/crates/vfs/src/lib.rs
@@ -67,7 +67,7 @@ pub struct FileId(u32);
 // pub struct FileId(NonMaxU32);
 
 impl FileId {
-    pub const MAX: u32 = 0x7fff_ffff;
+    const MAX: u32 = 0x7fff_ffff;
 
     #[inline]
     pub const fn from_raw(raw: u32) -> FileId {


Test patch:
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs
index fc1460870c..85fb90fdfb 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -389,7 +389,7 @@ m! { foo# bar }
 
 m! { Foo,# Bar }
 "#,
-        expect![[r##"
+        expect![[r#"
 macro_rules! m {
     ($($i:ident),*) => ($(mod $i {} )*);
     ($($i:ident)#*) => ($(fn $i() {} )*);
@@ -404,27 +404,29 @@ fn bar() {}
 
 struct Foo;
 struct Bar;
-"##]],
+"#]],
     );
 }
 
 #[test]
 fn test_match_group_pattern_with_multiple_defs() {
+    // FIXME: The pretty printer breaks by leaving whitespace here, +syntaxctxt is used to avoid that
     check(
         r#"
 macro_rules! m {
     ($($i:ident),*) => ( impl Bar { $(fn $i() {})* } );
 }
+// +syntaxctxt
 m! { foo, bar }
 "#,
         expect![[r#"
 macro_rules! m {
     ($($i:ident),*) => ( impl Bar { $(fn $i() {})* } );
 }
-impl Bar {
-    fn foo() {}
-    fn bar() {}
-}
+impl#\1# Bar#\1# {#\1#
+    fn#\1# foo#\0#(#\1#)#\1# {#\1#}#\1#
+    fn#\1# bar#\0#(#\1#)#\1# {#\1#}#\1#
+}#\1#
 "#]],
     );
 }
@@ -480,12 +482,12 @@ macro_rules! m {
 }
 m!{#abc}
 "#,
-        expect![[r##"
+        expect![[r#"
 macro_rules! m {
     ($($i:ident)* #abc) => ( fn baz() { $($i ();)* } );
 }
 fn baz() {}
-"##]],
+"#]],
     )
 }
 
@@ -1189,13 +1191,13 @@ macro_rules! m {
 m! { cfg(target_os = "windows") }
 m! { hello::world }
 "#,
-        expect![[r##"
+        expect![[r#"
 macro_rules! m {
     ($m:meta) => ( #[$m] fn bar() {} )
 }
 #[cfg(target_os = "windows")] fn bar() {}
 #[hello::world] fn bar() {}
-"##]],
+"#]],
     );
 }
 
@@ -1213,7 +1215,7 @@ m! {
     */
 }
 "#,
-        expect![[r##"
+        expect![[r#"
 macro_rules! m {
     ($(#[$m:meta])+) => ( $(#[$m])+ fn bar() {} )
 }
@@ -1221,7 +1223,7 @@ macro_rules! m {
 #[doc = r"
         MultiLines Doc
     "] fn bar() {}
-"##]],
+"#]],
     );
 }
 
@@ -1234,12 +1236,12 @@ macro_rules! m {
 }
 m! { #[doc = concat!("The `", "bla", "` lang item.")] }
 "#,
-        expect![[r##"
+        expect![[r#"
 macro_rules! m {
     (#[$m:meta]) => ( #[$m] fn bar() {} )
 }
 #[doc = concat!("The `", "bla", "` lang item.")] fn bar() {}
-"##]],
+"#]],
     );
 }
 
@@ -1257,7 +1259,7 @@ m! {
     */
 }
 "#,
-        expect![[r##"
+        expect![[r#"
 macro_rules! m {
     ($(#[$ m:meta])+) => ( $(#[$m])+ fn bar() {} )
 }
@@ -1265,7 +1267,7 @@ macro_rules! m {
 #[doc = r"
         莊生曉夢迷蝴蝶，望帝春心託杜鵑。
     "] fn bar() {}
-"##]],
+"#]],
     );
 }
 
@@ -1342,10 +1344,10 @@ fn test_tt_composite2() {
 macro_rules! m { ($($tt:tt)*) => { abs!(=> $($tt)*); } }
 m! {#}
 "#,
-        expect![[r##"
+        expect![[r#"
 macro_rules! m { ($($tt:tt)*) => { abs!(=> $($tt)*); } }
 abs!( = > #);
-"##]],
+"#]],
     );
 }
 
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index 485f72e92c..894ef1d73e 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -139,7 +139,7 @@ STRUCT!{struct D3DVSHADERCAPS2_0 {Caps: u8,}}
 
 STRUCT!{#[cfg_attr(target_arch = "x86", repr(packed))] struct D3DCONTENTPROTECTIONCAPS {Caps : u8 ,}}
 "#,
-        expect![[r##"
+        expect![[r#"
 macro_rules! STRUCT {
     ($(#[$attrs:meta])* struct $name:ident {
         $($field:ident: $ftype:ty,)+
@@ -194,7 +194,7 @@ impl Clone for D3DCONTENTPROTECTIONCAPS {
         }
     }
 }
-"##]],
+"#]],
     );
 }
 
@@ -214,7 +214,7 @@ macro_rules! int_base {
 }
 int_base!{Binary for isize as usize -> Binary}
 "#,
-        expect![[r##"
+        expect![[r#"
 macro_rules! int_base {
     ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => {
         #[stable(feature = "rust1", since = "1.0.0")]
@@ -230,7 +230,7 @@ macro_rules! int_base {
         Binary.fmt_int(*self as usize, f)
     }
 }
-"##]],
+"#]],
     );
 }
 
@@ -318,7 +318,7 @@ impl_fn_for_zst !   {
 }
 
 "#,
-        expect![[r##"
+        expect![[r#"
 macro_rules! impl_fn_for_zst  {
     {$( $( #[$attr: meta] )*
     struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )? Fn =
@@ -410,7 +410,7 @@ impl FnOnce<(char, )> for CharEscapeDefault {
     }
 }
 
-"##]],
+"#]],
     );
 }
 
@@ -511,7 +511,7 @@ cfg_if! {
     @__apply cfg(all(not(any(not(any(target_os = "solaris", target_os = "illumos")))))),
 }
 "#,
-        expect![[r##"
+        expect![[r#"
 macro_rules! cfg_if {
     ($(if #[cfg($($meta:meta),*)] { $($it:item)* } )else* else { $($it2:item)* })
     => {
@@ -534,7 +534,7 @@ __cfg_if_items! {
 }
 
 
-"##]],
+"#]],
     );
 }
 
@@ -618,7 +618,7 @@ RIDL!{interface ID3D11Asynchronous(ID3D11AsynchronousVtbl): ID3D11DeviceChild(ID
     fn GetDataSize(&mut self) -> UINT
 }}
 "#,
-        expect![[r##"
+        expect![[r#"
 #[macro_export]
 macro_rules! RIDL {
     (interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident)
@@ -639,7 +639,7 @@ impl ID3D11Asynchronous {
         ((*self .lpVtbl).GetDataSize)(self )
     }
 }
-"##]],
+"#]],
     );
 }
 
@@ -676,7 +676,7 @@ quick_error ! (
 );
 
 "#,
-        expect![[r##"
+        expect![[r#"
 macro_rules! quick_error {
     (SORT [enum $name:ident $( #[$meta:meta] )*]
         items [$($( #[$imeta:meta] )*
@@ -697,7 +697,7 @@ macro_rules! quick_error {
 }
 quick_error!(ENUMINITION[enum Wrapped#[derive(Debug)]]body[]queue[ = > One: UNIT[] = > Two: TUPLE[s: String]]);
 
-"##]],
+"#]],
     )
 }
 
@@ -746,7 +746,7 @@ delegate_impl ! {
     [G, &'a mut G, deref] pub trait Data: GraphBase {@section type type NodeWeight;}
 }
 "#,
-        expect![[r##"
+        expect![[r#"
 macro_rules! delegate_impl {
     ([$self_type:ident, $self_wrap:ty, $self_map:ident]
      pub trait $name:ident $(: $sup:ident)* $(+ $more_sup:ident)* {
@@ -785,7 +785,7 @@ macro_rules! delegate_impl {
     }
 }
 impl <> Data for &'amut G where G: Data {}
-"##]],
+"#]],
     );
 }
 
@@ -959,14 +959,14 @@ macro_rules! with_std {
 
 with_std! {mod m;mod f;}
 "#,
-        expect![[r##"
+        expect![[r#"
 macro_rules! with_std {
     ($($i:item)*) => ($(#[cfg(feature = "std")]$i)*)
 }
 
 #[cfg(feature = "std")] mod m;
 #[cfg(feature = "std")] mod f;
-"##]],
+"#]],
     )
 }
 
diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs
index 7f76119251..f9ccb06ce8 100644
--- a/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -1,6 +1,6 @@
-//! This module contains tests for macro expansion. Effectively, it covers `tt`,
-//! `mbe`, `proc_macro_api` and `hir_expand` crates. This might seem like a
-//! wrong architecture at the first glance, but is intentional.
+//! This module contains integration tests for macro expansion with name resolution. Effectively, it
+//! covers `tt`, `mbe`, `proc_macro_api` and `hir_expand` crates. This might seem like a  wrong
+//! architecture at the first glance, but is intentional.
 //!
 //! Physically, macro expansion process is intertwined with name resolution. You
 //! can not expand *just* the syntax. So, to be able to write integration tests
diff --git a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
index 6f605c0cb3..c0178adc9a 100644
--- a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
+++ b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
@@ -16,12 +16,12 @@ fn attribute_macro_attr_censoring() {
 #[attr1] #[proc_macros::identity] #[attr2]
 struct S;
 "#,
-        expect![[r##"
+        expect![[r#"
 #[attr1] #[proc_macros::identity] #[attr2]
 struct S;
 
 #[attr1]
-#[attr2] struct S;"##]],
+#[attr2] struct S;"#]],
     );
 }
 
@@ -39,7 +39,7 @@ fn derive_censoring() {
 #[attr2]
 struct S;
 "#,
-        expect![[r##"
+        expect![[r#"
 #[attr1]
 #[derive(Foo)]
 #[derive(proc_macros::DeriveIdentity)]
@@ -49,7 +49,7 @@ struct S;
 
 #[attr1]
 #[derive(Bar)]
-#[attr2] struct S;"##]],
+#[attr2] struct S;"#]],
     );
 }
 
@@ -62,14 +62,14 @@ fn attribute_macro_syntax_completion_1() {
 #[proc_macros::identity_when_valid]
 fn foo() { bar.baz(); blub }
 "#,
-        expect![[r##"
+        expect![[r#"
 #[proc_macros::identity_when_valid]
 fn foo() { bar.baz(); blub }
 
 fn foo() {
     bar.baz();
     blub
-}"##]],
+}"#]],
     );
 }
 
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 48e12a8107..dce7bbf342 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -909,29 +909,6 @@ fn qux(bar: Bar, baz: Baz) {}
     )
 }
 
-#[test]
-fn doctest_explicit_enum_discriminant() {
-    check_doc_test(
-        "explicit_enum_discriminant",
-        r#####"
-enum TheEnum$0 {
-    Foo,
-    Bar,
-    Baz = 42,
-    Quux,
-}
-"#####,
-        r#####"
-enum TheEnum {
-    Foo = 0,
-    Bar = 1,
-    Baz = 42,
-    Quux = 43,
-}
-"#####,
-    )
-}
-
 #[test]
 fn doctest_extract_expressions_from_format_string() {
     check_doc_test(
diff --git a/crates/ide-completion/src/tests/item_list.rs b/crates/ide-completion/src/tests/item_list.rs
index 532d4928ef..8aad7bfc3a 100644
--- a/crates/ide-completion/src/tests/item_list.rs
+++ b/crates/ide-completion/src/tests/item_list.rs
@@ -313,7 +313,6 @@ impl Test for () {
             ct const CONST1: () =
             fn async fn function2()
             fn fn function1()
-            fn fn function2()
             ma makro!(…)            macro_rules! makro
             md module
             ta type Type1 =
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index d7ae6c7508..f4528dd754 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -182,13 +182,26 @@ fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
 
 fn check_actions(ra_fixture: &str, expect: Expect) {
     let (analysis, file_id, position) = fixture::range_or_position(ra_fixture);
-    let hover = analysis
+    let mut hover = analysis
         .hover(
             &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG },
             FileRange { file_id, range: position.range_or_empty() },
         )
         .unwrap()
         .unwrap();
+    // stub out ranges into minicore as they can change every now and then
+    hover.info.actions.iter_mut().for_each(|action| match action {
+        super::HoverAction::GoToType(act) => act.iter_mut().for_each(|data| {
+            if data.nav.file_id == file_id {
+                return;
+            }
+            data.nav.full_range = TextRange::empty(span::TextSize::new(!0));
+            if let Some(range) = &mut data.nav.focus_range {
+                *range = TextRange::empty(span::TextSize::new(!0));
+            }
+        }),
+        _ => (),
+    });
     expect.assert_debug_eq(&hover.info.actions)
 }
 
@@ -200,10 +213,23 @@ fn check_hover_range(ra_fixture: &str, expect: Expect) {
 
 fn check_hover_range_actions(ra_fixture: &str, expect: Expect) {
     let (analysis, range) = fixture::range(ra_fixture);
-    let hover = analysis
+    let mut hover = analysis
         .hover(&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, range)
         .unwrap()
         .unwrap();
+    // stub out ranges into minicore as they can change every now and then
+    hover.info.actions.iter_mut().for_each(|action| match action {
+        super::HoverAction::GoToType(act) => act.iter_mut().for_each(|data| {
+            if data.nav.file_id == range.file_id {
+                return;
+            }
+            data.nav.full_range = TextRange::empty(span::TextSize::new(!0));
+            if let Some(range) = &mut data.nav.focus_range {
+                *range = TextRange::empty(span::TextSize::new(!0));
+            }
+        }),
+        _ => (),
+    });
     expect.assert_debug_eq(&hover.info.actions);
 }
 
@@ -483,8 +509,8 @@ fn main() {
                                 file_id: FileId(
                                     1,
                                 ),
-                                full_range: 633..868,
-                                focus_range: 694..700,
+                                full_range: 4294967295..4294967295,
+                                focus_range: 4294967295..4294967295,
                                 name: "FnOnce",
                                 kind: Trait,
                                 container_name: "function",
@@ -3104,26 +3130,26 @@ struct S{ f1: u32 }
 fn main() { let s$0t = S{ f1:0 }; }
 "#,
         expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::S",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..19,
-                                    focus_range: 7..8,
-                                    name: "S",
-                                    kind: Struct,
-                                    description: "struct S",
-                                },
+            [
+                GoToType(
+                    [
+                        HoverGotoTypeData {
+                            mod_path: "test::S",
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                full_range: 0..19,
+                                focus_range: 7..8,
+                                name: "S",
+                                kind: Struct,
+                                description: "struct S",
                             },
-                        ],
-                    ),
-                ]
-            "#]],
+                        },
+                    ],
+                ),
+            ]
+        "#]],
     );
 }
 
@@ -3616,8 +3642,8 @@ pub mod future {
                                 file_id: FileId(
                                     1,
                                 ),
-                                full_range: 21..69,
-                                focus_range: 60..66,
+                                full_range: 4294967295..4294967295,
+                                focus_range: 4294967295..4294967295,
                                 name: "Future",
                                 kind: Trait,
                                 container_name: "future",
@@ -8479,8 +8505,8 @@ impl Iterator for S {
                                 file_id: FileId(
                                     1,
                                 ),
-                                full_range: 7802..8044,
-                                focus_range: 7867..7873,
+                                full_range: 4294967295..4294967295,
+                                focus_range: 4294967295..4294967295,
                                 name: "Future",
                                 kind: Trait,
                                 container_name: "future",
@@ -8493,8 +8519,8 @@ impl Iterator for S {
                                 file_id: FileId(
                                     1,
                                 ),
-                                full_range: 8674..9173,
-                                focus_range: 8751..8759,
+                                full_range: 4294967295..4294967295,
+                                focus_range: 4294967295..4294967295,
                                 name: "Iterator",
                                 kind: Trait,
                                 container_name: "iterator",
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
new file mode 100644
index 0000000000..5422c9ae64
--- /dev/null
+++ b/crates/mbe/src/tests.rs
@@ -0,0 +1,179 @@
+//! Tests specific to declarative macros, aka macros by example. This covers
+//! both stable `macro_rules!` macros as well as unstable `macro` macros.
+// FIXME: Move more of the nameres independent tests from
+// crates\hir-def\src\macro_expansion_tests\mod.rs to this
+use expect_test::expect;
+use span::{Edition, EditionedFileId, ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContextId};
+use stdx::format_to;
+use syntax_bridge::insert_whitespace_into_node::insert_ws_into;
+use tt::{TextRange, TextSize};
+
+use crate::DeclarativeMacro;
+
+fn check_(
+    def_edition: Edition,
+    call_edition: Edition,
+    macro2: bool,
+    decl: &str,
+    arg: &str,
+    render_debug: bool,
+    expect: expect_test::Expect,
+    parse: parser::TopEntryPoint,
+) {
+    let decl_tt = &syntax_bridge::parse_to_token_tree(
+        def_edition,
+        SpanAnchor {
+            file_id: EditionedFileId::new(FileId::from_raw(0), def_edition),
+            ast_id: ErasedFileAstId::from_raw(0),
+        },
+        SyntaxContextId::ROOT,
+        decl,
+    )
+    .unwrap();
+    let mac = if macro2 {
+        DeclarativeMacro::parse_macro2(None, decl_tt, |_| def_edition)
+    } else {
+        DeclarativeMacro::parse_macro_rules(decl_tt, |_| def_edition)
+    };
+    let call_anchor = SpanAnchor {
+        file_id: EditionedFileId::new(FileId::from_raw(1), call_edition),
+        ast_id: ErasedFileAstId::from_raw(0),
+    };
+    let arg_tt =
+        syntax_bridge::parse_to_token_tree(call_edition, call_anchor, SyntaxContextId::ROOT, arg)
+            .unwrap();
+    let res = mac.expand(
+        &arg_tt,
+        |_| (),
+        Span {
+            range: TextRange::up_to(TextSize::of(arg)),
+            anchor: call_anchor,
+            ctx: SyntaxContextId::ROOT,
+        },
+        def_edition,
+    );
+    let mut expect_res = String::new();
+    if let Some(err) = res.err {
+        format_to!(expect_res, "{err:#?}\n\n",);
+    }
+    if render_debug {
+        format_to!(expect_res, "{:#?}\n\n", res.value.0);
+    }
+    let (node, _) = syntax_bridge::token_tree_to_syntax_node(&res.value.0, parse, def_edition);
+    format_to!(expect_res, "{}", insert_ws_into(node.syntax_node()));
+    expect.assert_eq(&expect_res);
+}
+
+fn check(
+    def_edition: Edition,
+    call_edition: Edition,
+    decl: &str,
+    arg: &str,
+    expect: expect_test::Expect,
+) {
+    check_(
+        def_edition,
+        call_edition,
+        false,
+        decl,
+        arg,
+        true,
+        expect,
+        parser::TopEntryPoint::SourceFile,
+    );
+}
+
+#[test]
+fn token_mapping_smoke_test() {
+    check(
+        Edition::CURRENT,
+        Edition::CURRENT,
+        r#"
+( struct $ident:ident ) => {
+    struct $ident {
+        map: ::std::collections::HashSet<()>,
+    }
+};
+"#,
+        r#"
+struct MyTraitMap2
+"#,
+        expect![[r#"
+            SUBTREE $$ 1:0@0..20#0 1:0@0..20#0
+              IDENT   struct 0:0@34..40#0
+              IDENT   MyTraitMap2 1:0@8..19#0
+              SUBTREE {} 0:0@48..49#0 0:0@100..101#0
+                IDENT   map 0:0@58..61#0
+                PUNCH   : [alone] 0:0@61..62#0
+                PUNCH   : [joint] 0:0@63..64#0
+                PUNCH   : [alone] 0:0@64..65#0
+                IDENT   std 0:0@65..68#0
+                PUNCH   : [joint] 0:0@68..69#0
+                PUNCH   : [alone] 0:0@69..70#0
+                IDENT   collections 0:0@70..81#0
+                PUNCH   : [joint] 0:0@81..82#0
+                PUNCH   : [alone] 0:0@82..83#0
+                IDENT   HashSet 0:0@83..90#0
+                PUNCH   < [alone] 0:0@90..91#0
+                SUBTREE () 0:0@91..92#0 0:0@92..93#0
+                PUNCH   > [joint] 0:0@93..94#0
+                PUNCH   , [alone] 0:0@94..95#0
+
+            struct MyTraitMap2 {
+                map: ::std::collections::HashSet<()>,
+            }"#]],
+    );
+}
+
+#[test]
+fn token_mapping_floats() {
+    // Regression test for https://github.com/rust-lang/rust-analyzer/issues/12216
+    // (and related issues)
+    check(
+        Edition::CURRENT,
+        Edition::CURRENT,
+        r#"
+($($tt:tt)*) => {
+    $($tt)*
+};
+"#,
+        r#"
+fn main() {
+    1;
+    1.0;
+    ((1,),).0.0;
+    let x = 1;
+}
+"#,
+        expect![[r#"
+            SUBTREE $$ 1:0@0..63#0 1:0@0..63#0
+              IDENT   fn 1:0@1..3#0
+              IDENT   main 1:0@4..8#0
+              SUBTREE () 1:0@8..9#0 1:0@9..10#0
+              SUBTREE {} 1:0@11..12#0 1:0@61..62#0
+                LITERAL Integer 1 1:0@17..18#0
+                PUNCH   ; [alone] 1:0@18..19#0
+                LITERAL Float 1.0 1:0@24..27#0
+                PUNCH   ; [alone] 1:0@27..28#0
+                SUBTREE () 1:0@33..34#0 1:0@39..40#0
+                  SUBTREE () 1:0@34..35#0 1:0@37..38#0
+                    LITERAL Integer 1 1:0@35..36#0
+                    PUNCH   , [alone] 1:0@36..37#0
+                  PUNCH   , [alone] 1:0@38..39#0
+                PUNCH   . [alone] 1:0@40..41#0
+                LITERAL Float 0.0 1:0@41..44#0
+                PUNCH   ; [alone] 1:0@44..45#0
+                IDENT   let 1:0@50..53#0
+                IDENT   x 1:0@54..55#0
+                PUNCH   = [alone] 1:0@56..57#0
+                LITERAL Integer 1 1:0@58..59#0
+                PUNCH   ; [alone] 1:0@59..60#0
+
+            fn main(){
+                1;
+                1.0;
+                ((1,),).0.0;
+                let x = 1;
+            }"#]],
+    );
+}