Repository: rust-lang/rust-analyzer
Pull request #19109 (2025-02-07 13:56:55)

fix: Do not show safety hints for extern items lacking semantics
Fixes https://github.com/rust-lang/rust-analyzer/issues/19108


Patch:
diff --git a/crates/hir-def/src/dyn_map.rs b/crates/hir-def/src/dyn_map.rs
index 0f73595347..e9318d146d 100644
--- a/crates/hir-def/src/dyn_map.rs
+++ b/crates/hir-def/src/dyn_map.rs
@@ -31,9 +31,9 @@ pub mod keys {
 
     use crate::{
         dyn_map::{DynMap, Policy},
-        BlockId, ConstId, EnumId, EnumVariantId, ExternCrateId, FieldId, FunctionId, ImplId,
-        LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, StaticId, StructId, TraitAliasId,
-        TraitId, TypeAliasId, TypeOrConstParamId, UnionId, UseId,
+        BlockId, ConstId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FieldId, FunctionId,
+        ImplId, LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, StaticId, StructId,
+        TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, UnionId, UseId,
     };
 
     pub type Key<K, V> = crate::dyn_map::Key<AstPtr<K>, V, AstPtrPolicy<K, V>>;
@@ -44,6 +44,7 @@ pub mod keys {
     pub const STATIC: Key<ast::Static, StaticId> = Key::new();
     pub const TYPE_ALIAS: Key<ast::TypeAlias, TypeAliasId> = Key::new();
     pub const IMPL: Key<ast::Impl, ImplId> = Key::new();
+    pub const EXTERN_BLOCK: Key<ast::ExternBlock, ExternBlockId> = Key::new();
     pub const TRAIT: Key<ast::Trait, TraitId> = Key::new();
     pub const TRAIT_ALIAS: Key<ast::TraitAlias, TraitAliasId> = Key::new();
     pub const STRUCT: Key<ast::Struct, StructId> = Key::new();
diff --git a/crates/hir-def/src/expr_store.rs b/crates/hir-def/src/expr_store.rs
index 5ff6a7ffe5..9df6eaade7 100644
--- a/crates/hir-def/src/expr_store.rs
+++ b/crates/hir-def/src/expr_store.rs
@@ -112,9 +112,9 @@ pub struct ExpressionStoreSourceMap {
     // AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map
     // to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected).
     expr_map: FxHashMap<ExprSource, ExprOrPatId>,
-    expr_map_back: ArenaMap<ExprId, ExprOrPatSource>,
+    expr_map_back: ArenaMap<ExprId, ExprSource>,
 
-    pat_map: FxHashMap<PatSource, ExprOrPatId>,
+    pat_map: FxHashMap<PatSource, PatId>,
     pat_map_back: ArenaMap<PatId, ExprOrPatSource>,
 
     label_map: FxHashMap<LabelSource, LabelId>,
@@ -606,12 +606,12 @@ impl Index<TypeRefId> for ExpressionStore {
 impl ExpressionStoreSourceMap {
     pub fn expr_or_pat_syntax(&self, id: ExprOrPatId) -> Result<ExprOrPatSource, SyntheticSyntax> {
         match id {
-            ExprOrPatId::ExprId(id) => self.expr_syntax(id),
+            ExprOrPatId::ExprId(id) => self.expr_syntax(id).map(|it| it.map(AstPtr::wrap_left)),
             ExprOrPatId::PatId(id) => self.pat_syntax(id),
         }
     }
 
-    pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprOrPatSource, SyntheticSyntax> {
+    pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> {
         self.expr_map_back.get(expr).cloned().ok_or(SyntheticSyntax)
     }
 
@@ -633,7 +633,7 @@ impl ExpressionStoreSourceMap {
         self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax)
     }
 
-    pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<ExprOrPatId> {
+    pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
         self.pat_map.get(&node.map(AstPtr::new)).cloned()
     }
 
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 6e505a6b11..88f770da02 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -44,8 +44,8 @@ use crate::{
             FormatPlaceholder, FormatSign, FormatTrait,
         },
         Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
-        Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability, OffsetOf, Pat, PatId,
-        RecordFieldPat, RecordLitField, Statement,
+        Expr, ExprId, Item, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
+        OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
     },
     item_scope::BuiltinShadowMode,
     lang_item::LangItem,
@@ -1784,33 +1784,23 @@ impl ExprCollector<'_> {
                         self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
                             this.collect_pat_opt(expanded_pat, binding_list)
                         });
-                    self.source_map.pat_map.insert(src, pat.into());
+                    self.source_map.pat_map.insert(src, pat);
                     return pat;
                 }
                 None => Pat::Missing,
             },
+            // FIXME: implement in a way that also builds source map and calculates assoc resolutions in type inference.
             ast::Pat::RangePat(p) => {
-                let mut range_part_lower = |p: Option<ast::Pat>| -> Option<ExprId> {
-                    p.and_then(|it| {
-                        let ptr = PatPtr::new(&it);
-                        match &it {
-                            ast::Pat::LiteralPat(it) => Some(self.alloc_expr_from_pat(
-                                Expr::Literal(pat_literal_to_hir(it)?.0),
-                                ptr,
-                            )),
-                            ast::Pat::IdentPat(ident) if ident.is_simple_ident() => ident
-                                .name()
-                                .map(|name| name.as_name())
-                                .map(Path::from)
-                                .map(|path| self.alloc_expr_from_pat(Expr::Path(path), ptr)),
-                            ast::Pat::PathPat(p) => p
-                                .path()
-                                .and_then(|path| self.parse_path(path))
-                                .map(|parsed| self.alloc_expr_from_pat(Expr::Path(parsed), ptr)),
-                            // We only need to handle literal, ident (if bare) and path patterns here,
-                            // as any other pattern as a range pattern operand is semantically invalid.
-                            _ => None,
+                let mut range_part_lower = |p: Option<ast::Pat>| {
+                    p.and_then(|it| match &it {
+                        ast::Pat::LiteralPat(it) => {
+                            Some(Box::new(LiteralOrConst::Literal(pat_literal_to_hir(it)?.0)))
                         }
+                        pat @ (ast::Pat::IdentPat(_) | ast::Pat::PathPat(_)) => {
+                            let subpat = self.collect_pat(pat.clone(), binding_list);
+                            Some(Box::new(LiteralOrConst::Const(subpat)))
+                        }
+                        _ => None,
                     })
                 };
                 let start = range_part_lower(p.start());
@@ -1873,7 +1863,7 @@ impl ExprCollector<'_> {
                             }
                         });
                     if let Some(pat) = pat.left() {
-                        self.source_map.pat_map.insert(src, pat.into());
+                        self.source_map.pat_map.insert(src, pat);
                     }
                     pat
                 }
@@ -2500,7 +2490,7 @@ impl ExprCollector<'_> {
     fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
         let src = self.expander.in_file(ptr);
         let id = self.store.exprs.alloc(expr);
-        self.source_map.expr_map_back.insert(id, src.map(AstPtr::wrap_left));
+        self.source_map.expr_map_back.insert(id, src);
         self.source_map.expr_map.insert(src, id.into());
         id
     }
@@ -2512,7 +2502,7 @@ impl ExprCollector<'_> {
     fn alloc_expr_desugared_with_ptr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
         let src = self.expander.in_file(ptr);
         let id = self.store.exprs.alloc(expr);
-        self.source_map.expr_map_back.insert(id, src.map(AstPtr::wrap_left));
+        self.source_map.expr_map_back.insert(id, src);
         // We intentionally don't fill this as it could overwrite a non-desugared entry
         // self.source_map.expr_map.insert(src, id);
         id
@@ -2536,20 +2526,11 @@ impl ExprCollector<'_> {
         self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_left));
         id
     }
-
-    fn alloc_expr_from_pat(&mut self, expr: Expr, ptr: PatPtr) -> ExprId {
-        let src = self.expander.in_file(ptr);
-        let id = self.store.exprs.alloc(expr);
-        self.source_map.pat_map.insert(src, id.into());
-        self.source_map.expr_map_back.insert(id, src.map(AstPtr::wrap_right));
-        id
-    }
-
     fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
         let src = self.expander.in_file(ptr);
         let id = self.store.pats.alloc(pat);
         self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_right));
-        self.source_map.pat_map.insert(src, id.into());
+        self.source_map.pat_map.insert(src, id);
         id
     }
     // FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs
index 82ad756dc2..6a0b1e5197 100644
--- a/crates/hir-def/src/expr_store/pretty.rs
+++ b/crates/hir-def/src/expr_store/pretty.rs
@@ -6,7 +6,10 @@ use itertools::Itertools;
 use span::Edition;
 
 use crate::{
-    hir::{Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, Statement},
+    hir::{
+        Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, LiteralOrConst, Movability,
+        Statement,
+    },
     pretty::{print_generic_args, print_path, print_type_ref},
 };
 
@@ -653,11 +656,11 @@ impl Printer<'_> {
             }
             Pat::Range { start, end } => {
                 if let Some(start) = start {
-                    self.print_expr(*start);
+                    self.print_literal_or_const(start);
                 }
                 w!(self, "..=");
                 if let Some(end) = end {
-                    self.print_expr(*end);
+                    self.print_literal_or_const(end);
                 }
             }
             Pat::Slice { prefix, slice, suffix } => {
@@ -754,6 +757,13 @@ impl Printer<'_> {
         }
     }
 
+    fn print_literal_or_const(&mut self, literal_or_const: &LiteralOrConst) {
+        match literal_or_const {
+            LiteralOrConst::Literal(l) => self.print_literal(l),
+            LiteralOrConst::Const(c) => self.print_pat(*c),
+        }
+    }
+
     fn print_literal(&mut self, literal: &Literal) {
         match literal {
             Literal::String(it) => w!(self, "{:?}", it),
diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs
index 494644d8ef..0dcddf162b 100644
--- a/crates/hir-def/src/hir.rs
+++ b/crates/hir-def/src/hir.rs
@@ -55,20 +55,12 @@ impl ExprOrPatId {
         }
     }
 
-    pub fn is_expr(&self) -> bool {
-        matches!(self, Self::ExprId(_))
-    }
-
     pub fn as_pat(self) -> Option<PatId> {
         match self {
             Self::PatId(v) => Some(v),
             _ => None,
         }
     }
-
-    pub fn is_pat(&self) -> bool {
-        matches!(self, Self::PatId(_))
-    }
 }
 stdx::impl_from!(ExprId, PatId for ExprOrPatId);
 
@@ -579,8 +571,8 @@ pub enum Pat {
         ellipsis: bool,
     },
     Range {
-        start: Option<ExprId>,
-        end: Option<ExprId>,
+        start: Option<Box<LiteralOrConst>>,
+        end: Option<Box<LiteralOrConst>>,
     },
     Slice {
         prefix: Box<[PatId]>,
diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs
index 65a39c5656..0ca1eb9bcf 100644
--- a/crates/hir-def/src/item_scope.rs
+++ b/crates/hir-def/src/item_scope.rs
@@ -18,8 +18,8 @@ use crate::{
     db::DefDatabase,
     per_ns::{Item, MacrosItem, PerNs, TypesItem, ValuesItem},
     visibility::{Visibility, VisibilityExplicitness},
-    AdtId, BuiltinType, ConstId, ExternCrateId, FxIndexMap, HasModule, ImplId, LocalModuleId,
-    Lookup, MacroId, ModuleDefId, ModuleId, TraitId, UseId,
+    AdtId, BuiltinType, ConstId, ExternBlockId, ExternCrateId, FxIndexMap, HasModule, ImplId,
+    LocalModuleId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId, UseId,
 };
 
 #[derive(Debug, Default)]
@@ -158,6 +158,8 @@ pub struct ItemScope {
     declarations: Vec<ModuleDefId>,
 
     impls: Vec<ImplId>,
+    #[allow(clippy::box_collection)]
+    extern_blocks: Option<Box<Vec<ExternBlockId>>>,
     unnamed_consts: Vec<ConstId>,
     /// Traits imported via `use Trait as _;`.
     unnamed_trait_imports: FxHashMap<TraitId, Item<()>>,
@@ -319,6 +321,10 @@ impl ItemScope {
         self.extern_crate_decls.iter().copied()
     }
 
+    pub fn extern_blocks(&self) -> impl Iterator<Item = ExternBlockId> + '_ {
+        self.extern_blocks.iter().flat_map(|it| it.iter()).copied()
+    }
+
     pub fn use_decls(&self) -> impl ExactSizeIterator<Item = UseId> + '_ {
         self.use_decls.iter().copied()
     }
@@ -469,6 +475,10 @@ impl ItemScope {
         self.impls.push(imp);
     }
 
+    pub(crate) fn define_extern_block(&mut self, extern_block: ExternBlockId) {
+        self.extern_blocks.get_or_insert_default().push(extern_block);
+    }
+
     pub(crate) fn define_extern_crate_decl(&mut self, extern_crate: ExternCrateId) {
         self.extern_crate_decls.push(extern_crate);
     }
@@ -806,7 +816,11 @@ impl ItemScope {
             use_imports_types,
             use_imports_macros,
             macro_invocations,
+            extern_blocks,
         } = self;
+        if let Some(it) = extern_blocks {
+            it.shrink_to_fit();
+        }
         types.shrink_to_fit();
         values.shrink_to_fit();
         macros.shrink_to_fit();
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index 06276335b7..254c137991 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -1759,16 +1759,20 @@ impl ModCollector<'_, '_> {
                         );
                     }
                 }
-                ModItem::ExternBlock(block) => self.collect(
-                    &self.item_tree[block].children,
-                    ItemContainerId::ExternBlockId(
-                        ExternBlockLoc {
-                            container: module,
-                            id: ItemTreeId::new(self.tree_id, block),
-                        }
-                        .intern(db),
-                    ),
-                ),
+                ModItem::ExternBlock(block) => {
+                    let extern_block_id = ExternBlockLoc {
+                        container: module,
+                        id: ItemTreeId::new(self.tree_id, block),
+                    }
+                    .intern(db);
+                    self.def_collector.def_map.modules[self.module_id]
+                        .scope
+                        .define_extern_block(extern_block_id);
+                    self.collect(
+                        &self.item_tree[block].children,
+                        ItemContainerId::ExternBlockId(extern_block_id),
+                    )
+                }
                 ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac], container),
                 ModItem::MacroRules(id) => self.collect_macro_rules(id, module),
                 ModItem::Macro2(id) => self.collect_macro_def(id, module),
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index 59aaf85164..0b5f131924 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -440,9 +440,7 @@ impl ExprValidator {
                             return;
                         };
                         let root = source_ptr.file_syntax(db.upcast());
-                        let either::Left(ast::Expr::IfExpr(if_expr)) =
-                            source_ptr.value.to_node(&root)
-                        else {
+                        let ast::Expr::IfExpr(if_expr) = source_ptr.value.to_node(&root) else {
                             return;
                         };
                         let mut top_if_expr = if_expr;
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs
index 41304bbd8a..84d8950b1a 100644
--- a/crates/hir-ty/src/mir.rs
+++ b/crates/hir-ty/src/mir.rs
@@ -10,7 +10,7 @@ use crate::{
     lang_items::is_box,
     mapping::ToChalk,
     CallableDefId, ClosureId, Const, ConstScalar, InferenceResult, Interner, MemoryMap,
-    Substitution, TraitEnvironment, Ty, TyExt, TyKind,
+    Substitution, TraitEnvironment, Ty, TyKind,
 };
 use base_db::CrateId;
 use chalk_ir::Mutability;
@@ -144,13 +144,6 @@ impl<V, T> ProjectionElem<V, T> {
         closure_field: impl FnOnce(ClosureId, &Substitution, usize) -> Ty,
         krate: CrateId,
     ) -> Ty {
-        // we only bail on mir building when there are type mismatches
-        // but error types may pop up resulting in us still attempting to build the mir
-        // so just propagate the error type
-        if base.is_unknown() {
-            return TyKind::Error.intern(Interner);
-        }
-
         if matches!(base.kind(Interner), TyKind::Alias(_) | TyKind::AssociatedType(..)) {
             base = normalize(
                 db,
@@ -173,7 +166,7 @@ impl<V, T> ProjectionElem<V, T> {
                     TyKind::Error.intern(Interner)
                 }
             },
-            ProjectionElem::Field(Either::Left(f)) => match base.kind(Interner) {
+            ProjectionElem::Field(Either::Left(f)) => match &base.kind(Interner) {
                 TyKind::Adt(_, subst) => {
                     db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst)
                 }
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index f88696692e..549450e9be 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -8,8 +8,8 @@ use hir_def::{
     data::adt::{StructKind, VariantData},
     expr_store::{Body, HygieneId},
     hir::{
-        ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm,
-        Pat, PatId, RecordFieldPat, RecordLitField,
+        ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal,
+        LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField,
     },
     lang_item::{LangItem, LangItemTarget},
     path::Path,
@@ -1358,10 +1358,20 @@ impl<'ctx> MirLowerCtx<'ctx> {
         Ok(())
     }
 
-    fn lower_literal_or_const_to_operand(&mut self, ty: Ty, loc: &ExprId) -> Result<Operand> {
-        match &self.body.exprs[*loc] {
-            Expr::Literal(l) => self.lower_literal_to_operand(ty, l),
-            Expr::Path(c) => {
+    fn lower_literal_or_const_to_operand(
+        &mut self,
+        ty: Ty,
+        loc: &LiteralOrConst,
+    ) -> Result<Operand> {
+        match loc {
+            LiteralOrConst::Literal(l) => self.lower_literal_to_operand(ty, l),
+            LiteralOrConst::Const(c) => {
+                let c = match &self.body.pats[*c] {
+                    Pat::Path(p) => p,
+                    _ => not_supported!(
+                        "only `char` and numeric types are allowed in range patterns"
+                    ),
+                };
                 let edition = self.edition();
                 let unresolved_name =
                     || MirLowerError::unresolved_path(self.db, c, edition, &self.body.types);
@@ -1382,9 +1392,6 @@ impl<'ctx> MirLowerCtx<'ctx> {
                     }
                 }
             }
-            _ => {
-                not_supported!("only `char` and numeric types are allowed in range patterns");
-            }
         }
     }
 
diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs
index 289175feef..2ffea34c85 100644
--- a/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -1,6 +1,6 @@
 //! MIR lowering for patterns
 
-use hir_def::{hir::ExprId, AssocItemId};
+use hir_def::{hir::LiteralOrConst, AssocItemId};
 
 use crate::{
     mir::{
@@ -207,7 +207,7 @@ impl MirLowerCtx<'_> {
                 )?
             }
             Pat::Range { start, end } => {
-                let mut add_check = |l: &ExprId, binop| -> Result<()> {
+                let mut add_check = |l: &LiteralOrConst, binop| -> Result<()> {
                     let lv =
                         self.lower_literal_or_const_to_operand(self.infer[pattern].clone(), l)?;
                     let else_target = *current_else.get_or_insert_with(|| self.new_basic_block());
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 5876529df9..64e982c42d 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -6,7 +6,6 @@
 use cfg::{CfgExpr, CfgOptions};
 use either::Either;
 use hir_def::{
-    expr_store::ExprOrPatPtr,
     hir::ExprOrPatId,
     path::{hir_segment_to_ast_segment, ModPath},
     type_ref::TypesSourceMap,
@@ -116,14 +115,14 @@ diagnostics![
 
 #[derive(Debug)]
 pub struct BreakOutsideOfLoop {
-    pub expr: InFile<ExprOrPatPtr>,
+    pub expr: InFile<AstPtr<ast::Expr>>,
     pub is_break: bool,
     pub bad_value_break: bool,
 }
 
 #[derive(Debug)]
 pub struct TypedHole {
-    pub expr: InFile<ExprOrPatPtr>,
+    pub expr: InFile<AstPtr<ast::Expr>>,
     pub expected: Type,
 }
 
@@ -222,26 +221,26 @@ pub struct NoSuchField {
 
 #[derive(Debug)]
 pub struct PrivateAssocItem {
-    pub expr_or_pat: InFile<ExprOrPatPtr>,
+    pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
     pub item: AssocItem,
 }
 
 #[derive(Debug)]
 pub struct MismatchedTupleStructPatArgCount {
-    pub expr_or_pat: InFile<ExprOrPatPtr>,
+    pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
     pub expected: usize,
     pub found: usize,
 }
 
 #[derive(Debug)]
 pub struct ExpectedFunction {
-    pub call: InFile<ExprOrPatPtr>,
+    pub call: InFile<AstPtr<ast::Expr>>,
     pub found: Type,
 }
 
 #[derive(Debug)]
 pub struct UnresolvedField {
-    pub expr: InFile<ExprOrPatPtr>,
+    pub expr: InFile<AstPtr<ast::Expr>>,
     pub receiver: Type,
     pub name: Name,
     pub method_with_same_name_exists: bool,
@@ -249,7 +248,7 @@ pub struct UnresolvedField {
 
 #[derive(Debug)]
 pub struct UnresolvedMethodCall {
-    pub expr: InFile<ExprOrPatPtr>,
+    pub expr: InFile<AstPtr<ast::Expr>>,
     pub receiver: Type,
     pub name: Name,
     pub field_with_same_name: Option<Type>,
@@ -258,17 +257,17 @@ pub struct UnresolvedMethodCall {
 
 #[derive(Debug)]
 pub struct UnresolvedAssocItem {
-    pub expr_or_pat: InFile<ExprOrPatPtr>,
+    pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
 }
 
 #[derive(Debug)]
 pub struct UnresolvedIdent {
-    pub node: InFile<(ExprOrPatPtr, Option<TextRange>)>,
+    pub node: InFile<(AstPtr<Either<ast::Expr, ast::Pat>>, Option<TextRange>)>,
 }
 
 #[derive(Debug)]
 pub struct PrivateField {
-    pub expr: InFile<ExprOrPatPtr>,
+    pub expr: InFile<AstPtr<ast::Expr>>,
     pub field: Field,
 }
 
@@ -281,7 +280,7 @@ pub enum UnsafeLint {
 
 #[derive(Debug)]
 pub struct MissingUnsafe {
-    pub node: InFile<ExprOrPatPtr>,
+    pub node: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
     pub lint: UnsafeLint,
     pub reason: UnsafetyReason,
 }
@@ -303,7 +302,7 @@ pub struct ReplaceFilterMapNextWithFindMap {
 
 #[derive(Debug)]
 pub struct MismatchedArgCount {
-    pub call_expr: InFile<ExprOrPatPtr>,
+    pub call_expr: InFile<AstPtr<ast::Expr>>,
     pub expected: usize,
     pub found: usize,
 }
@@ -322,7 +321,7 @@ pub struct NonExhaustiveLet {
 
 #[derive(Debug)]
 pub struct TypeMismatch {
-    pub expr_or_pat: InFile<ExprOrPatPtr>,
+    pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
     pub expected: Type,
     pub actual: Type,
 }
@@ -396,13 +395,13 @@ pub struct RemoveUnnecessaryElse {
 
 #[derive(Debug)]
 pub struct CastToUnsized {
-    pub expr: InFile<ExprOrPatPtr>,
+    pub expr: InFile<AstPtr<ast::Expr>>,
     pub cast_ty: Type,
 }
 
 #[derive(Debug)]
 pub struct InvalidCast {
-    pub expr: InFile<ExprOrPatPtr>,
+    pub expr: InFile<AstPtr<ast::Expr>>,
     pub error: CastError,
     pub expr_ty: Type,
     pub cast_ty: Type,
@@ -429,7 +428,9 @@ impl AnyDiagnostic {
                     .collect();
 
                 let record = match record {
-                    Either::Left(record_expr) => source_map.expr_syntax(record_expr).ok()?,
+                    Either::Left(record_expr) => {
+                        source_map.expr_syntax(record_expr).ok()?.map(AstPtr::wrap_left)
+                    }
                     Either::Right(record_pat) => source_map.pat_syntax(record_pat).ok()?,
                 };
                 let file = record.file_id;
@@ -473,7 +474,7 @@ impl AnyDiagnostic {
                     return Some(
                         ReplaceFilterMapNextWithFindMap {
                             file: next_source_ptr.file_id,
-                            next_expr: next_source_ptr.value.cast()?,
+                            next_expr: next_source_ptr.value,
                         }
                         .into(),
                     );
@@ -483,9 +484,7 @@ impl AnyDiagnostic {
                 match source_map.expr_syntax(match_expr) {
                     Ok(source_ptr) => {
                         let root = source_ptr.file_syntax(db.upcast());
-                        if let Either::Left(ast::Expr::MatchExpr(match_expr)) =
-                            &source_ptr.value.to_node(&root)
-                        {
+                        if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) {
                             match match_expr.expr() {
                                 Some(scrut_expr) if match_expr.match_arm_list().is_some() => {
                                     return Some(
@@ -562,7 +561,7 @@ impl AnyDiagnostic {
         let pat_syntax =
             |pat| source_map.pat_syntax(pat).inspect_err(|_| stdx::never!("synthetic syntax")).ok();
         let expr_or_pat_syntax = |id| match id {
-            ExprOrPatId::ExprId(expr) => expr_syntax(expr),
+            ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(|it| it.map(AstPtr::wrap_left)),
             ExprOrPatId::PatId(pat) => pat_syntax(pat),
         };
         Some(match d {
@@ -634,7 +633,7 @@ impl AnyDiagnostic {
             &InferenceDiagnostic::UnresolvedIdent { id } => {
                 let node = match id {
                     ExprOrPatId::ExprId(id) => match source_map.expr_syntax(id) {
-                        Ok(syntax) => syntax.map(|it| (it, None)),
+                        Ok(syntax) => syntax.map(|it| (it.wrap_left(), None)),
                         Err(SyntheticSyntax) => source_map
                             .format_args_implicit_capture(id)?
                             .map(|(node, range)| (node.wrap_left(), Some(range))),
@@ -653,7 +652,7 @@ impl AnyDiagnostic {
             }
             &InferenceDiagnostic::MismatchedTupleStructPatArgCount { pat, expected, found } => {
                 let expr_or_pat = match pat {
-                    ExprOrPatId::ExprId(expr) => expr_syntax(expr)?,
+                    ExprOrPatId::ExprId(expr) => expr_syntax(expr)?.map(AstPtr::wrap_left),
                     ExprOrPatId::PatId(pat) => {
                         let InFile { file_id, value } = pat_syntax(pat)?;
 
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs
index 537401afdc..72df07ef8c 100644
--- a/crates/hir/src/from_id.rs
+++ b/crates/hir/src/from_id.rs
@@ -49,6 +49,7 @@ from_id![
     (hir_def::LifetimeParamId, crate::LifetimeParam),
     (hir_def::MacroId, crate::Macro),
     (hir_def::ExternCrateId, crate::ExternCrateDecl),
+    (hir_def::ExternBlockId, crate::ExternBlock),
 ];
 
 impl From<AdtId> for Adt {
diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs
index a34b498083..82c90ac301 100644
--- a/crates/hir/src/has_source.rs
+++ b/crates/hir/src/has_source.rs
@@ -248,7 +248,7 @@ impl HasSource for Param {
                 let ast @ InFile { file_id, value } = source_map.expr_syntax(expr_id).ok()?;
                 let root = db.parse_or_expand(file_id);
                 match value.to_node(&root) {
-                    Either::Left(ast::Expr::ClosureExpr(it)) => it
+                    ast::Expr::ClosureExpr(it) => it
                         .param_list()?
                         .params()
                         .nth(self.idx)
@@ -301,7 +301,7 @@ impl HasSource for InlineAsmOperand {
             let root = src.file_syntax(db.upcast());
             return src
                 .map(|ast| match ast.to_node(&root) {
-                    Either::Left(ast::Expr::AsmExpr(asm)) => asm
+                    ast::Expr::AsmExpr(asm) => asm
                         .asm_pieces()
                         .filter_map(|it| match it {
                             ast::AsmPiece::AsmOperandNamed(it) => Some(it),
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 8b8203acb1..3c17ddb7b7 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -55,8 +55,8 @@ use hir_def::{
     resolver::{HasResolver, Resolver},
     type_ref::TypesSourceMap,
     AdtId, AssocItemId, AssocItemLoc, AttrDefId, CallableDefId, ConstId, ConstParamId,
-    CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId, FunctionId,
-    GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstId, ItemContainerId,
+    CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId,
+    FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstId, ItemContainerId,
     LifetimeParamId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId,
     SyntheticSyntax, TraitAliasId, TupleId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
 };
@@ -1957,7 +1957,7 @@ impl DefWithBody {
                 ExprOrPatId::PatId(pat) => source_map.pat_syntax(pat).map(Either::Right),
             };
             let expr_or_pat = match expr_or_pat {
-                Ok(Either::Left(expr)) => expr,
+                Ok(Either::Left(expr)) => expr.map(AstPtr::wrap_left),
                 Ok(Either::Right(InFile { file_id, value: pat })) => {
                     // cast from Either<Pat, SelfParam> -> Either<_, Pat>
                     let Some(ptr) = AstPtr::try_from_raw(pat.syntax_node_ptr()) else {
@@ -2003,7 +2003,7 @@ impl DefWithBody {
             match source_map.expr_syntax(node) {
                 Ok(node) => acc.push(
                     MissingUnsafe {
-                        node,
+                        node: node.map(|it| it.wrap_left()),
                         lint: UnsafeLint::DeprecatedSafe2024,
                         reason: UnsafetyReason::UnsafeFnCall,
                     }
@@ -2327,6 +2327,13 @@ impl Function {
         db.function_data(self.id).is_async()
     }
 
+    pub fn extern_block(self, db: &dyn HirDatabase) -> Option<ExternBlock> {
+        match self.id.lookup(db.upcast()).container {
+            ItemContainerId::ExternBlockId(id) => Some(ExternBlock { id }),
+            _ => None,
+        }
+    }
+
     pub fn returns_impl_future(self, db: &dyn HirDatabase) -> bool {
         if self.is_async(db) {
             return true;
@@ -2761,6 +2768,13 @@ impl Static {
         Type::from_value_def(db, self.id)
     }
 
+    pub fn extern_block(self, db: &dyn HirDatabase) -> Option<ExternBlock> {
+        match self.id.lookup(db.upcast()).container {
+            ItemContainerId::ExternBlockId(id) => Some(ExternBlock { id }),
+            _ => None,
+        }
+    }
+
     /// Evaluate the static initializer.
     pub fn eval(self, db: &dyn HirDatabase) -> Result<EvaluatedConst, ConstEvalError> {
         db.const_eval(self.id.into(), Substitution::empty(Interner), None)
@@ -2928,6 +2942,17 @@ impl HasVisibility for TypeAlias {
     }
 }
 
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct ExternBlock {
+    pub(crate) id: ExternBlockId,
+}
+
+impl ExternBlock {
+    pub fn module(self, db: &dyn HirDatabase) -> Module {
+        Module { id: self.id.module(db.upcast()) }
+    }
+}
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct StaticLifetime;
 
@@ -4592,7 +4617,10 @@ impl CaptureUsages {
             match span {
                 mir::MirSpan::ExprId(expr) => {
                     if let Ok(expr) = source_map.expr_syntax(expr) {
-                        result.push(CaptureUsageSource { is_ref, source: expr })
+                        result.push(CaptureUsageSource {
+                            is_ref,
+                            source: expr.map(AstPtr::wrap_left),
+                        })
                     }
                 }
                 mir::MirSpan::PatId(pat) => {
@@ -6177,9 +6205,15 @@ impl HasContainer for TraitAlias {
     }
 }
 
+impl HasContainer for ExternBlock {
+    fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+        ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container })
+    }
+}
+
 fn container_id_to_hir(c: ItemContainerId) -> ItemContainer {
     match c {
-        ItemContainerId::ExternBlockId(_id) => ItemContainer::ExternBlock(),
+        ItemContainerId::ExternBlockId(id) => ItemContainer::ExternBlock(ExternBlock { id }),
         ItemContainerId::ModuleId(id) => ItemContainer::Module(Module { id }),
         ItemContainerId::ImplId(id) => ItemContainer::Impl(Impl { id }),
         ItemContainerId::TraitId(id) => ItemContainer::Trait(Trait { id }),
@@ -6191,7 +6225,7 @@ pub enum ItemContainer {
     Trait(Trait),
     Impl(Impl),
     Module(Module),
-    ExternBlock(),
+    ExternBlock(ExternBlock),
     Crate(CrateId),
 }
 
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 882a27182f..c9145f7d21 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -1998,6 +1998,7 @@ to_def_impls![
     (crate::Adt, ast::Adt, adt_to_def),
     (crate::ExternCrateDecl, ast::ExternCrate, extern_crate_to_def),
     (crate::InlineAsmOperand, ast::AsmOperandNamed, asm_operand_to_def),
+    (crate::ExternBlock, ast::ExternBlock, extern_block_to_def),
     (MacroCallId, ast::MacroCall, macro_call_to_macro_call),
 ];
 
diff --git a/crates/hir/src/semantics/child_by_source.rs b/crates/hir/src/semantics/child_by_source.rs
index d5dfb98571..d0fdf5cbdf 100644
--- a/crates/hir/src/semantics/child_by_source.rs
+++ b/crates/hir/src/semantics/child_by_source.rs
@@ -74,6 +74,9 @@ impl ChildBySource for ItemScope {
     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
         self.declarations().for_each(|item| add_module_def(db, res, file_id, item));
         self.impls().for_each(|imp| insert_item_loc(db, res, file_id, imp, keys::IMPL));
+        self.extern_blocks().for_each(|extern_block| {
+            insert_item_loc(db, res, file_id, extern_block, keys::EXTERN_BLOCK)
+        });
         self.extern_crate_decls()
             .for_each(|ext| insert_item_loc(db, res, file_id, ext, keys::EXTERN_CRATE));
         self.use_decls().for_each(|ext| insert_item_loc(db, res, file_id, ext, keys::USE));
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 29d3736bae..4e61f5b5a3 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -92,10 +92,10 @@ use hir_def::{
         DynMap,
     },
     hir::{BindingId, Expr, LabelId},
-    AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId,
-    FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, Lookup, MacroId,
-    ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, UseId,
-    VariantId,
+    AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId,
+    ExternCrateId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId,
+    Lookup, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId,
+    UnionId, UseId, VariantId,
 };
 use hir_expand::{
     attrs::AttrId, name::AsName, ExpansionInfo, HirFileId, HirFileIdExt, InMacroFile, MacroCallId,
@@ -308,6 +308,12 @@ impl SourceToDefCtx<'_, '_> {
     ) -> Option<ExternCrateId> {
         self.to_def(src, keys::EXTERN_CRATE)
     }
+    pub(super) fn extern_block_to_def(
+        &mut self,
+        src: InFile<&ast::ExternBlock>,
+    ) -> Option<ExternBlockId> {
+        self.to_def(src, keys::EXTERN_BLOCK)
+    }
     #[allow(dead_code)]
     pub(super) fn use_to_def(&mut self, src: InFile<&ast::Use>) -> Option<UseId> {
         self.to_def(src, keys::USE)
@@ -352,7 +358,7 @@ impl SourceToDefCtx<'_, '_> {
         let src = src.cloned().map(ast::Pat::from);
         let pat_id = source_map.node_pat(src.as_ref())?;
         // the pattern could resolve to a constant, verify that this is not the case
-        if let crate::Pat::Bind { id, .. } = body[pat_id.as_pat()?] {
+        if let crate::Pat::Bind { id, .. } = body[pat_id] {
             Some((container, id))
         } else {
             None
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 9019863f7f..c687e2d8a0 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -18,7 +18,7 @@ use hir_def::{
         scope::{ExprScopes, ScopeId},
         Body, BodySourceMap, HygieneId,
     },
-    hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat},
+    hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat, PatId},
     lang_item::LangItem,
     lower::LowerCtx,
     nameres::MacroSubNs,
@@ -139,7 +139,7 @@ impl SourceAnalyzer {
         sm.node_expr(src.as_ref())
     }
 
-    fn pat_id(&self, pat: &ast::Pat) -> Option<ExprOrPatId> {
+    fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> {
         // FIXME: macros, see `expr_id`
         let src = InFile { file_id: self.file_id, value: pat };
         self.body_source_map()?.node_pat(src)
@@ -147,7 +147,7 @@ impl SourceAnalyzer {
 
     fn binding_id_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingId> {
         let pat_id = self.pat_id(&pat.clone().into())?;
-        if let Pat::Bind { id, .. } = self.body()?.pats[pat_id.as_pat()?] {
+        if let Pat::Bind { id, .. } = self.body()?.pats[pat_id] {
             Some(id)
         } else {
             None
@@ -210,20 +210,11 @@ impl SourceAnalyzer {
         db: &dyn HirDatabase,
         pat: &ast::Pat,
     ) -> Option<(Type, Option<Type>)> {
-        let expr_or_pat_id = self.pat_id(pat)?;
+        let pat_id = self.pat_id(pat)?;
         let infer = self.infer.as_ref()?;
-        let coerced = match expr_or_pat_id {
-            ExprOrPatId::ExprId(idx) => infer
-                .expr_adjustments
-                .get(&idx)
-                .and_then(|adjusts| adjusts.last().cloned())
-                .map(|adjust| adjust.target),
-            ExprOrPatId::PatId(idx) => {
-                infer.pat_adjustments.get(&idx).and_then(|adjusts| adjusts.last().cloned())
-            }
-        };
-
-        let ty = infer[expr_or_pat_id].clone();
+        let coerced =
+            infer.pat_adjustments.get(&pat_id).and_then(|adjusts| adjusts.last().cloned());
+        let ty = infer[pat_id].clone();
         let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty);
         Some((mk_ty(ty), coerced.map(mk_ty)))
     }
@@ -257,7 +248,7 @@ impl SourceAnalyzer {
     ) -> Option<BindingMode> {
         let id = self.pat_id(&pat.clone().into())?;
         let infer = self.infer.as_ref()?;
-        infer.binding_modes.get(id.as_pat()?).map(|bm| match bm {
+        infer.binding_modes.get(id).map(|bm| match bm {
             hir_ty::BindingMode::Move => BindingMode::Move,
             hir_ty::BindingMode::Ref(hir_ty::Mutability::Mut) => BindingMode::Ref(Mutability::Mut),
             hir_ty::BindingMode::Ref(hir_ty::Mutability::Not) => {
@@ -275,7 +266,7 @@ impl SourceAnalyzer {
         Some(
             infer
                 .pat_adjustments
-                .get(&pat_id.as_pat()?)?
+                .get(&pat_id)?
                 .iter()
                 .map(|ty| Type::new_with_resolver(db, &self.resolver, ty.clone()))
                 .collect(),
@@ -658,10 +649,10 @@ impl SourceAnalyzer {
         let field_name = field.field_name()?.as_name();
         let record_pat = ast::RecordPat::cast(field.syntax().parent().and_then(|p| p.parent())?)?;
         let pat_id = self.pat_id(&record_pat.into())?;
-        let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id.as_pat()?)?;
+        let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id)?;
         let variant_data = variant.variant_data(db.upcast());
         let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? };
-        let (adt, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id.as_pat()?)?.as_adt()?;
+        let (adt, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id)?.as_adt()?;
         let field_ty =
             db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst);
         Some((
@@ -691,20 +682,12 @@ impl SourceAnalyzer {
         db: &dyn HirDatabase,
         pat: &ast::IdentPat,
     ) -> Option<ModuleDef> {
-        let expr_or_pat_id = self.pat_id(&pat.clone().into())?;
+        let pat_id = self.pat_id(&pat.clone().into())?;
         let body = self.body()?;
-
-        let path = match expr_or_pat_id {
-            ExprOrPatId::ExprId(idx) => match &body[idx] {
-                Expr::Path(path) => path,
-                _ => return None,
-            },
-            ExprOrPatId::PatId(idx) => match &body[idx] {
-                Pat::Path(path) => path,
-                _ => return None,
-            },
+        let path = match &body[pat_id] {
+            Pat::Path(path) => path,
+            _ => return None,
         };
-
         let res = resolve_hir_path(db, &self.resolver, path, HygieneId::ROOT, TypesMap::EMPTY)?;
         match res {
             PathResolution::Def(def) => Some(def),
@@ -799,9 +782,8 @@ impl SourceAnalyzer {
                 }
                 prefer_value_ns = true;
             } else if let Some(path_pat) = parent().and_then(ast::PathPat::cast) {
-                let expr_or_pat_id = self.pat_id(&path_pat.into())?;
-                if let Some((assoc, subs)) = infer.assoc_resolutions_for_expr_or_pat(expr_or_pat_id)
-                {
+                let pat_id = self.pat_id(&path_pat.into())?;
+                if let Some((assoc, subs)) = infer.assoc_resolutions_for_pat(pat_id) {
                     let (assoc, subst) = match assoc {
                         AssocItemId::ConstId(const_id) => {
                             let (konst, subst) =
@@ -825,7 +807,7 @@ impl SourceAnalyzer {
                     return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst)));
                 }
                 if let Some(VariantId::EnumVariantId(variant)) =
-                    infer.variant_resolution_for_expr_or_pat(expr_or_pat_id)
+                    infer.variant_resolution_for_pat(pat_id)
                 {
                     return Some((PathResolution::Def(ModuleDef::Variant(variant.into())), None));
                 }
@@ -842,7 +824,7 @@ impl SourceAnalyzer {
                     || parent().and_then(ast::TupleStructPat::cast).map(ast::Pat::from);
                 if let Some(pat) = record_pat.or_else(tuple_struct_pat) {
                     let pat_id = self.pat_id(&pat)?;
-                    let variant_res_for_pat = infer.variant_resolution_for_pat(pat_id.as_pat()?);
+                    let variant_res_for_pat = infer.variant_resolution_for_pat(pat_id);
                     if let Some(VariantId::EnumVariantId(variant)) = variant_res_for_pat {
                         return Some((
                             PathResolution::Def(ModuleDef::Variant(variant.into())),
@@ -1098,7 +1080,7 @@ impl SourceAnalyzer {
         let body = self.body()?;
         let infer = self.infer.as_ref()?;
 
-        let pat_id = self.pat_id(&pattern.clone().into())?.as_pat()?;
+        let pat_id = self.pat_id(&pattern.clone().into())?;
         let substs = infer.type_of_pat[pat_id].as_adt()?.1;
 
         let (variant, missing_fields, _exhaustive) =
diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs
index 28e2853096..2c39a8fdfe 100644
--- a/crates/ide-completion/src/completions/postfix.rs
+++ b/crates/ide-completion/src/completions/postfix.rs
@@ -2,18 +2,17 @@
 
 mod format_like;
 
-use base_db::SourceDatabase;
-use hir::{ItemInNs, Semantics};
+use hir::ItemInNs;
+use ide_db::text_edit::TextEdit;
 use ide_db::{
     documentation::{Documentation, HasDocs},
     imports::insert_use::ImportScope,
-    text_edit::TextEdit,
     ty_filter::TryEnum,
-    RootDatabase, SnippetCap,
+    SnippetCap,
 };
 use stdx::never;
 use syntax::{
-    ast::{self, AstNode, AstToken},
+    ast::{self, make, AstNode, AstToken},
     SyntaxKind::{BLOCK_EXPR, EXPR_STMT, FOR_EXPR, IF_EXPR, LOOP_EXPR, STMT_LIST, WHILE_EXPR},
     TextRange, TextSize,
 };
@@ -49,8 +48,7 @@ pub(crate) fn complete_postfix(
     };
     let expr_ctx = &dot_access.ctx;
 
-    let receiver_text =
-        get_receiver_text(&ctx.sema, dot_receiver, receiver_is_ambiguous_float_literal);
+    let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal);
 
     let cap = match ctx.config.snippet_cap {
         Some(it) => it,
@@ -174,15 +172,13 @@ pub(crate) fn complete_postfix(
     // The rest of the postfix completions create an expression that moves an argument,
     // so it's better to consider references now to avoid breaking the compilation
 
-    let (dot_receiver_including_refs, prefix) = include_references(dot_receiver);
-    let mut receiver_text =
-        get_receiver_text(&ctx.sema, dot_receiver, receiver_is_ambiguous_float_literal);
-    receiver_text.insert_str(0, &prefix);
-    let postfix_snippet =
-        match build_postfix_snippet_builder(ctx, cap, &dot_receiver_including_refs) {
-            Some(it) => it,
-            None => return,
-        };
+    let (dot_receiver, node_to_replace_with) = include_references(dot_receiver);
+    let receiver_text =
+        get_receiver_text(&node_to_replace_with, receiver_is_ambiguous_float_literal);
+    let postfix_snippet = match build_postfix_snippet_builder(ctx, cap, &dot_receiver) {
+        Some(it) => it,
+        None => return,
+    };
 
     if !ctx.config.snippets.is_empty() {
         add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text);
@@ -226,7 +222,7 @@ pub(crate) fn complete_postfix(
     postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})"))
         .add_to(acc, ctx.db);
 
-    if let Some(parent) = dot_receiver_including_refs.syntax().parent().and_then(|p| p.parent()) {
+    if let Some(parent) = dot_receiver.syntax().parent().and_then(|p| p.parent()) {
         if matches!(parent.kind(), STMT_LIST | EXPR_STMT) {
             postfix_snippet("let", "let", &format!("let $0 = {receiver_text};"))
                 .add_to(acc, ctx.db);
@@ -235,9 +231,9 @@ pub(crate) fn complete_postfix(
         }
     }
 
-    if let ast::Expr::Literal(literal) = dot_receiver_including_refs.clone() {
+    if let ast::Expr::Literal(literal) = dot_receiver.clone() {
         if let Some(literal_text) = ast::String::cast(literal.token()) {
-            add_format_like_completions(acc, ctx, &dot_receiver_including_refs, cap, &literal_text);
+            add_format_like_completions(acc, ctx, &dot_receiver, cap, &literal_text);
         }
     }
 
@@ -264,20 +260,14 @@ pub(crate) fn complete_postfix(
     }
 }
 
-fn get_receiver_text(
-    sema: &Semantics<'_, RootDatabase>,
-    receiver: &ast::Expr,
-    receiver_is_ambiguous_float_literal: bool,
-) -> String {
-    // Do not just call `receiver.to_string()`, as that will mess up whitespaces inside macros.
-    let Some(mut range) = sema.original_range_opt(receiver.syntax()) else {
-        return receiver.to_string();
+fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
+    let mut text = if receiver_is_ambiguous_float_literal {
+        let text = receiver.syntax().text();
+        let without_dot = ..text.len() - TextSize::of('.');
+        text.slice(without_dot).to_string()
+    } else {
+        receiver.to_string()
     };
-    if receiver_is_ambiguous_float_literal {
-        range.range = TextRange::at(range.range.start(), range.range.len() - TextSize::of('.'))
-    }
-    let file_text = sema.db.file_text(range.file_id.file_id());
-    let mut text = file_text[range.range].to_owned();
 
     // The receiver texts should be interpreted as-is, as they are expected to be
     // normal Rust expressions.
@@ -294,7 +284,7 @@ fn escape_snippet_bits(text: &mut String) {
     stdx::replace(text, '$', "\\$");
 }
 
-fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) {
+fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) {
     let mut resulting_element = initial_element.clone();
 
     while let Some(field_expr) = resulting_element.syntax().parent().and_then(ast::FieldExpr::cast)
@@ -302,7 +292,7 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) {
         resulting_element = ast::Expr::from(field_expr);
     }
 
-    let mut prefix = String::new();
+    let mut new_element_opt = initial_element.clone();
 
     while let Some(parent_deref_element) =
         resulting_element.syntax().parent().and_then(ast::PrefixExpr::cast)
@@ -313,7 +303,7 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) {
 
         resulting_element = ast::Expr::from(parent_deref_element);
 
-        prefix.insert(0, '*');
+        new_element_opt = make::expr_prefix(syntax::T![*], new_element_opt).into();
     }
 
     if let Some(first_ref_expr) = resulting_element.syntax().parent().and_then(ast::RefExpr::cast) {
@@ -327,7 +317,7 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) {
             let exclusive = parent_ref_element.mut_token().is_some();
             resulting_element = ast::Expr::from(parent_ref_element);
 
-            prefix.insert_str(0, if exclusive { "&mut " } else { "&" });
+            new_element_opt = make::expr_ref(new_element_opt, exclusive);
         }
     } else {
         // If we do not find any ref expressions, restore
@@ -335,7 +325,7 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) {
         resulting_element = initial_element.clone();
     }
 
-    (resulting_element, prefix)
+    (resulting_element, new_element_opt)
 }
 
 fn build_postfix_snippet_builder<'ctx>(
@@ -911,31 +901,4 @@ fn main() {
 "#,
         );
     }
-
-    #[test]
-    fn inside_macro() {
-        check_edit(
-            "box",
-            r#"
-macro_rules! assert {
-    ( $it:expr $(,)? ) => { $it };
-}
-
-fn foo() {
-    let a = true;
-    assert!(if a == false { true } else { false }.$0);
-}
-        "#,
-            r#"
-macro_rules! assert {
-    ( $it:expr $(,)? ) => { $it };
-}
-
-fn foo() {
-    let a = true;
-    assert!(Box::new(if a == false { true } else { false }));
-}
-        "#,
-        );
-    }
 }
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index bad5360805..6f71c3d9bd 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -108,7 +108,7 @@ impl Definition {
                 ItemContainer::Trait(it) => Some(it.into()),
                 ItemContainer::Impl(it) => Some(it.into()),
                 ItemContainer::Module(it) => Some(it.into()),
-                ItemContainer::ExternBlock() | ItemContainer::Crate(_) => None,
+                ItemContainer::ExternBlock(_) | ItemContainer::Crate(_) => None,
             }
         }
         match self {
diff --git a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
index 0520bb3fe9..7126617cde 100644
--- a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
+++ b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
@@ -40,7 +40,7 @@ pub(crate) fn mismatched_arg_count(
     Diagnostic::new(
         DiagnosticCode::RustcHardError("E0107"),
         message,
-        invalid_args_range(ctx, d.call_expr, d.expected, d.found),
+        invalid_args_range(ctx, d.call_expr.map(AstPtr::wrap_left), d.expected, d.found),
     )
 }
 
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/crates/ide-diagnostics/src/handlers/unresolved_field.rs
index dfb03eee73..4accd181ca 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_field.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_field.rs
@@ -1,6 +1,5 @@
 use std::iter;
 
-use either::Either;
 use hir::{db::ExpandDatabase, Adt, FileRange, HasSource, HirDisplay, InFile, Struct, Union};
 use ide_db::text_edit::TextEdit;
 use ide_db::{
@@ -42,7 +41,7 @@ pub(crate) fn unresolved_field(
         ),
         adjusted_display_range(ctx, d.expr, &|expr| {
             Some(
-                match expr.left()? {
+                match expr {
                     ast::Expr::MethodCallExpr(it) => it.name_ref(),
                     ast::Expr::FieldExpr(it) => it.name_ref(),
                     _ => None,
@@ -73,7 +72,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<Vec<A
 fn field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<Assist> {
     // Get the FileRange of the invalid field access
     let root = ctx.sema.db.parse_or_expand(d.expr.file_id);
-    let expr = d.expr.value.to_node(&root).left()?;
+    let expr = d.expr.value.to_node(&root);
 
     let error_range = ctx.sema.original_range_opt(expr.syntax())?;
     let field_name = d.name.as_str();
@@ -264,7 +263,7 @@ fn record_field_layout(
 // FIXME: We should fill out the call here, move the cursor and trigger signature help
 fn method_fix(
     ctx: &DiagnosticsContext<'_>,
-    expr_ptr: &InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
+    expr_ptr: &InFile<AstPtr<ast::Expr>>,
 ) -> Option<Assist> {
     let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
     let expr = expr_ptr.value.to_node(&root);
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
index dd1b593e8f..4ab649cc16 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_method.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -35,7 +35,7 @@ pub(crate) fn unresolved_method(
         ),
         adjusted_display_range(ctx, d.expr, &|expr| {
             Some(
-                match expr.left()? {
+                match expr {
                     ast::Expr::MethodCallExpr(it) => it.name_ref(),
                     ast::Expr::FieldExpr(it) => it.name_ref(),
                     _ => None,
@@ -85,7 +85,7 @@ fn field_fix(
     let expr_ptr = &d.expr;
     let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
     let expr = expr_ptr.value.to_node(&root);
-    let (file_id, range) = match expr.left()? {
+    let (file_id, range) = match expr {
         ast::Expr::MethodCallExpr(mcall) => {
             let FileRange { range, file_id } =
                 ctx.sema.original_range_opt(mcall.receiver()?.syntax())?;
@@ -117,7 +117,7 @@ fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -
 
         let expr_ptr = &d.expr;
         let root = db.parse_or_expand(expr_ptr.file_id);
-        let expr: ast::Expr = expr_ptr.value.to_node(&root).left()?;
+        let expr: ast::Expr = expr_ptr.value.to_node(&root);
 
         let call = ast::MethodCallExpr::cast(expr.syntax().clone())?;
         let range = InFile::new(expr_ptr.file_id, call.syntax().text_range())
diff --git a/crates/ide-diagnostics/src/handlers/unused_variables.rs b/crates/ide-diagnostics/src/handlers/unused_variables.rs
index d5caf4de33..67ece56694 100644
--- a/crates/ide-diagnostics/src/handlers/unused_variables.rs
+++ b/crates/ide-diagnostics/src/handlers/unused_variables.rs
@@ -260,19 +260,6 @@ fn main() {
     let arr = [1, 2, 3, 4, 5];
     let [_x, _y @ ..] = arr;
 }
-"#,
-        );
-    }
-
-    // regression test as we used to panic in this scenario
-    #[test]
-    fn unknown_struct_pattern_param_type() {
-        check_diagnostics(
-            r#"
-struct S { field : u32 }
-fn f(S { field }: error) {
-      // ^^^^^ 💡 warn: unused variable
-}
 "#,
         );
     }
diff --git a/crates/ide/src/inlay_hints/extern_block.rs b/crates/ide/src/inlay_hints/extern_block.rs
index 2bc91b68ed..652dff0bc5 100644
--- a/crates/ide/src/inlay_hints/extern_block.rs
+++ b/crates/ide/src/inlay_hints/extern_block.rs
@@ -7,7 +7,7 @@ use crate::{InlayHint, InlayHintsConfig};
 
 pub(super) fn extern_block_hints(
     acc: &mut Vec<InlayHint>,
-    FamousDefs(_sema, _): &FamousDefs<'_, '_>,
+    FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
     _file_id: EditionedFileId,
     extern_block: ast::ExternBlock,
@@ -16,6 +16,7 @@ pub(super) fn extern_block_hints(
         return None;
     }
     let abi = extern_block.abi()?;
+    sema.to_def(&extern_block)?;
     acc.push(InlayHint {
         range: abi.syntax().text_range(),
         position: crate::InlayHintPosition::Before,
@@ -33,7 +34,7 @@ pub(super) fn extern_block_hints(
 
 pub(super) fn fn_hints(
     acc: &mut Vec<InlayHint>,
-    FamousDefs(_sema, _): &FamousDefs<'_, '_>,
+    FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
     _file_id: EditionedFileId,
     fn_: &ast::Fn,
@@ -43,14 +44,16 @@ pub(super) fn fn_hints(
     if !implicit_unsafe {
         return None;
     }
-    let fn_ = fn_.fn_token()?;
-    acc.push(item_hint(config, extern_block, fn_));
+    let fn_token = fn_.fn_token()?;
+    if sema.to_def(fn_).is_some_and(|def| def.extern_block(sema.db).is_some()) {
+        acc.push(item_hint(config, extern_block, fn_token));
+    }
     Some(())
 }
 
 pub(super) fn static_hints(
     acc: &mut Vec<InlayHint>,
-    FamousDefs(_sema, _): &FamousDefs<'_, '_>,
+    FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
     _file_id: EditionedFileId,
     static_: &ast::Static,
@@ -60,8 +63,10 @@ pub(super) fn static_hints(
     if !implicit_unsafe {
         return None;
     }
-    let static_ = static_.static_token()?;
-    acc.push(item_hint(config, extern_block, static_));
+    let static_token = static_.static_token()?;
+    if sema.to_def(static_).is_some_and(|def| def.extern_block(sema.db).is_some()) {
+        acc.push(item_hint(config, extern_block, static_token));
+    }
     Some(())
 }
 
diff --git a/crates/ide/src/inlay_hints/implicit_drop.rs b/crates/ide/src/inlay_hints/implicit_drop.rs
index 390139d214..58dc0fdf62 100644
--- a/crates/ide/src/inlay_hints/implicit_drop.rs
+++ b/crates/ide/src/inlay_hints/implicit_drop.rs
@@ -73,8 +73,8 @@ pub(super) fn hints(
                     _ => continue,
                 },
                 MirSpan::PatId(p) => match source_map.pat_syntax(p) {
-                    Ok(s) if !s.file_id.is_macro() => s.value.text_range(),
-                    _ => continue,
+                    Ok(s) => s.value.text_range(),
+                    Err(_) => continue,
                 },
                 MirSpan::BindingId(b) => {
                     match source_map
@@ -82,13 +82,13 @@ pub(super) fn hints(
                         .iter()
                         .find_map(|p| source_map.pat_syntax(*p).ok())
                     {
-                        Some(s) if !s.file_id.is_macro() => s.value.text_range(),
-                        _ => continue,
+                        Some(s) => s.value.text_range(),
+                        None => continue,
                     }
                 }
                 MirSpan::SelfParam => match source_map.self_param_syntax() {
-                    Some(s) if !s.file_id.is_macro() => s.value.text_range(),
-                    _ => continue,
+                    Some(s) => s.value.text_range(),
+                    None => continue,
                 },
                 MirSpan::Unknown => continue,
             };
diff --git a/docs/book/README.md b/docs/book/README.md
index 043524b234..a9d10df664 100644
--- a/docs/book/README.md
+++ b/docs/book/README.md
@@ -26,4 +26,4 @@ Start with the mdbook [User Guide](https://rust-lang.github.io/mdBook/guide/inst
 Four sections are generated dynamically: assists, configuration, diagnostics and features. Their content is found in the `generated.md` files
 of the respective book section, for example `src/configuration_generated.md`, and are included in the book via mdbook's
 [include](https://rust-lang.github.io/mdBook/format/mdbook.html#including-files) functionality. Generated files can be rebuilt by running the various
-test cases that generate them, or by simply running all of the `rust-analyzer` tests with `cargo test` and `cargo xtask codegen`.
+test cases that generate them, or by simply running all of the `rust-analyzer` tests with `cargo test`.
diff --git a/docs/book/book.toml b/docs/book/book.toml
index 5ca4badde8..ba3c1dede5 100644
--- a/docs/book/book.toml
+++ b/docs/book/book.toml
@@ -9,10 +9,10 @@ title = "rust-analyzer"
 edition = "2021"
 
 [output.html]
-edit-url-template = "https://github.com/rust-lang/rust-analyzer/edit/master/docs/book/{path}"
-git-repository-url = "https://github.com/rust-lang/rust-analyzer/tree/master/docs/book"
+edit-url-template = "https://github.com/rust-lang/rust-analyzer/edit/master/manual/{path}"
+git-repository-url = "https://github.com/rust-lang/rust-analyzer/tree/master/manual"
 mathjax-support = true
-site-url = "/book/"
+site-url = "/manual/"
 
 [output.html.playground]
 editable = true
@@ -34,3 +34,8 @@ use-boolean-and = true
 [output.html.fold]
 enable = true
 level = 3
+
+[preprocessor.toc]
+command = "mdbook-toc"
+renderer = ["html"]
+max-level = 3
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 86a066454a..6027f81331 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -23,7 +23,7 @@
                 "@typescript-eslint/parser": "^6.0.0",
                 "@vscode/test-electron": "^2.3.8",
                 "@vscode/vsce": "^3.0.0",
-                "esbuild": "^0.25.0",
+                "esbuild": "^0.18.12",
                 "eslint": "^8.44.0",
                 "eslint-config-prettier": "^8.8.0",
                 "ovsx": "^0.8.2",
@@ -256,429 +256,356 @@
                 "node": ">=16"
             }
         },
-        "node_modules/@esbuild/aix-ppc64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz",
-            "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==",
-            "cpu": [
-                "ppc64"
-            ],
-            "dev": true,
-            "license": "MIT",
-            "optional": true,
-            "os": [
-                "aix"
-            ],
-            "engines": {
-                "node": ">=18"
-            }
-        },
         "node_modules/@esbuild/android-arm": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz",
-            "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.12.tgz",
+            "integrity": "sha512-LIxaNIQfkFZbTLb4+cX7dozHlAbAshhFE5PKdro0l+FnCpx1GDJaQ2WMcqm+ToXKMt8p8Uojk/MFRuGyz3V5Sw==",
             "cpu": [
                 "arm"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "android"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/android-arm64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz",
-            "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.12.tgz",
+            "integrity": "sha512-BMAlczRqC/LUt2P97E4apTBbkvS9JTJnp2DKFbCwpZ8vBvXVbNdqmvzW/OsdtI/+mGr+apkkpqGM8WecLkPgrA==",
             "cpu": [
                 "arm64"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "android"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/android-x64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz",
-            "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.12.tgz",
+            "integrity": "sha512-zU5MyluNsykf5cOJ0LZZZjgAHbhPJ1cWfdH1ZXVMXxVMhEV0VZiZXQdwBBVvmvbF28EizeK7obG9fs+fpmS0eQ==",
             "cpu": [
                 "x64"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "android"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/darwin-arm64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz",
-            "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.12.tgz",
+            "integrity": "sha512-zUZMep7YONnp6954QOOwEBwFX9svlKd3ov6PkxKd53LGTHsp/gy7vHaPGhhjBmEpqXEXShi6dddjIkmd+NgMsA==",
             "cpu": [
                 "arm64"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "darwin"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/darwin-x64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz",
-            "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.12.tgz",
+            "integrity": "sha512-ohqLPc7i67yunArPj1+/FeeJ7AgwAjHqKZ512ADk3WsE3FHU9l+m5aa7NdxXr0HmN1bjDlUslBjWNbFlD9y12Q==",
             "cpu": [
                 "x64"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "darwin"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/freebsd-arm64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz",
-            "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.12.tgz",
+            "integrity": "sha512-GIIHtQXqgeOOqdG16a/A9N28GpkvjJnjYMhOnXVbn3EDJcoItdR58v/pGN31CHjyXDc8uCcRnFWmqaJt24AYJg==",
             "cpu": [
                 "arm64"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "freebsd"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/freebsd-x64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz",
-            "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.12.tgz",
+            "integrity": "sha512-zK0b9a1/0wZY+6FdOS3BpZcPc1kcx2G5yxxfEJtEUzVxI6n/FrC2Phsxj/YblPuBchhBZ/1wwn7AyEBUyNSa6g==",
             "cpu": [
                 "x64"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "freebsd"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/linux-arm": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz",
-            "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.12.tgz",
+            "integrity": "sha512-y75OijvrBE/1XRrXq1jtrJfG26eHeMoqLJ2dwQNwviwTuTtHGCojsDO6BJNF8gU+3jTn1KzJEMETytwsFSvc+Q==",
             "cpu": [
                 "arm"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "linux"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/linux-arm64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz",
-            "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.12.tgz",
+            "integrity": "sha512-JKgG8Q/LL/9sw/iHHxQyVMoQYu3rU3+a5Z87DxC+wAu3engz+EmctIrV+FGOgI6gWG1z1+5nDDbXiRMGQZXqiw==",
             "cpu": [
                 "arm64"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "linux"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/linux-ia32": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz",
-            "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.12.tgz",
+            "integrity": "sha512-yoRIAqc0B4lDIAAEFEIu9ttTRFV84iuAl0KNCN6MhKLxNPfzwCBvEMgwco2f71GxmpBcTtn7KdErueZaM2rEvw==",
             "cpu": [
                 "ia32"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "linux"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/linux-loong64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz",
-            "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.12.tgz",
+            "integrity": "sha512-qYgt3dHPVvf/MgbIBpJ4Sup/yb9DAopZ3a2JgMpNKIHUpOdnJ2eHBo/aQdnd8dJ21X/+sS58wxHtA9lEazYtXQ==",
             "cpu": [
                 "loong64"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "linux"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/linux-mips64el": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz",
-            "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.12.tgz",
+            "integrity": "sha512-wHphlMLK4ufNOONqukELfVIbnGQJrHJ/mxZMMrP2jYrPgCRZhOtf0kC4yAXBwnfmULimV1qt5UJJOw4Kh13Yfg==",
             "cpu": [
                 "mips64el"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "linux"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/linux-ppc64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz",
-            "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.12.tgz",
+            "integrity": "sha512-TeN//1Ft20ZZW41+zDSdOI/Os1bEq5dbvBvYkberB7PHABbRcsteeoNVZFlI0YLpGdlBqohEpjrn06kv8heCJg==",
             "cpu": [
                 "ppc64"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "linux"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/linux-riscv64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz",
-            "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.12.tgz",
+            "integrity": "sha512-AgUebVS4DoAblBgiB2ACQ/8l4eGE5aWBb8ZXtkXHiET9mbj7GuWt3OnsIW/zX+XHJt2RYJZctbQ2S/mDjbp0UA==",
             "cpu": [
                 "riscv64"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "linux"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/linux-s390x": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz",
-            "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.12.tgz",
+            "integrity": "sha512-dJ3Rb3Ei2u/ysSXd6pzleGtfDdc2MuzKt8qc6ls8vreP1G3B7HInX3i7gXS4BGeVd24pp0yqyS7bJ5NHaI9ing==",
             "cpu": [
                 "s390x"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "linux"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/linux-x64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz",
-            "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.12.tgz",
+            "integrity": "sha512-OrNJMGQbPaVyHHcDF8ybNSwu7TDOfX8NGpXCbetwOSP6txOJiWlgQnRymfC9ocR1S0Y5PW0Wb1mV6pUddqmvmQ==",
             "cpu": [
                 "x64"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "linux"
             ],
             "engines": {
-                "node": ">=18"
-            }
-        },
-        "node_modules/@esbuild/netbsd-arm64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz",
-            "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==",
-            "cpu": [
-                "arm64"
-            ],
-            "dev": true,
-            "license": "MIT",
-            "optional": true,
-            "os": [
-                "netbsd"
-            ],
-            "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/netbsd-x64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz",
-            "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.12.tgz",
+            "integrity": "sha512-55FzVCAiwE9FK8wWeCRuvjazNRJ1QqLCYGZVB6E8RuQuTeStSwotpSW4xoRGwp3a1wUsaVCdYcj5LGCASVJmMg==",
             "cpu": [
                 "x64"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "netbsd"
             ],
             "engines": {
-                "node": ">=18"
-            }
-        },
-        "node_modules/@esbuild/openbsd-arm64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz",
-            "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==",
-            "cpu": [
-                "arm64"
-            ],
-            "dev": true,
-            "license": "MIT",
-            "optional": true,
-            "os": [
-                "openbsd"
-            ],
-            "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/openbsd-x64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz",
-            "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.12.tgz",
+            "integrity": "sha512-qnluf8rfb6Y5Lw2tirfK2quZOBbVqmwxut7GPCIJsM8lc4AEUj9L8y0YPdLaPK0TECt4IdyBdBD/KRFKorlK3g==",
             "cpu": [
                 "x64"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "openbsd"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/sunos-x64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz",
-            "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.12.tgz",
+            "integrity": "sha512-+RkKpVQR7bICjTOPUpkTBTaJ4TFqQBX5Ywyd/HSdDkQGn65VPkTsR/pL4AMvuMWy+wnXgIl4EY6q4mVpJal8Kg==",
             "cpu": [
                 "x64"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "sunos"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/win32-arm64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz",
-            "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.12.tgz",
+            "integrity": "sha512-GNHuciv0mFM7ouzsU0+AwY+7eV4Mgo5WnbhfDCQGtpvOtD1vbOiRjPYG6dhmMoFyBjj+pNqQu2X+7DKn0KQ/Gw==",
             "cpu": [
                 "arm64"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "win32"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/win32-ia32": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz",
-            "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.12.tgz",
+            "integrity": "sha512-kR8cezhYipbbypGkaqCTWIeu4zID17gamC8YTPXYtcN3E5BhhtTnwKBn9I0PJur/T6UVwIEGYzkffNL0lFvxEw==",
             "cpu": [
                 "ia32"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "win32"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@esbuild/win32-x64": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz",
-            "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.12.tgz",
+            "integrity": "sha512-O0UYQVkvfM/jO8a4OwoV0mAKSJw+mjWTAd1MJd/1FCX6uiMdLmMRPK/w6e9OQ0ob2WGxzIm9va/KG0Ja4zIOgg==",
             "cpu": [
                 "x64"
             ],
             "dev": true,
-            "license": "MIT",
             "optional": true,
             "os": [
                 "win32"
             ],
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             }
         },
         "node_modules/@eslint-community/eslint-utils": {
@@ -2594,44 +2521,40 @@
             }
         },
         "node_modules/esbuild": {
-            "version": "0.25.0",
-            "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz",
-            "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==",
+            "version": "0.18.12",
+            "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.12.tgz",
+            "integrity": "sha512-XuOVLDdtsDslXStStduT41op21Ytmf4/BDS46aa3xPJ7X5h2eMWBF1oAe3QjUH3bDksocNXgzGUZ7XHIBya6Tg==",
             "dev": true,
             "hasInstallScript": true,
-            "license": "MIT",
             "bin": {
                 "esbuild": "bin/esbuild"
             },
             "engines": {
-                "node": ">=18"
+                "node": ">=12"
             },
             "optionalDependencies": {
-                "@esbuild/aix-ppc64": "0.25.0",
-                "@esbuild/android-arm": "0.25.0",
-                "@esbuild/android-arm64": "0.25.0",
-                "@esbuild/android-x64": "0.25.0",
-                "@esbuild/darwin-arm64": "0.25.0",
-                "@esbuild/darwin-x64": "0.25.0",
-                "@esbuild/freebsd-arm64": "0.25.0",
-                "@esbuild/freebsd-x64": "0.25.0",
-                "@esbuild/linux-arm": "0.25.0",
-                "@esbuild/linux-arm64": "0.25.0",
-                "@esbuild/linux-ia32": "0.25.0",
-                "@esbuild/linux-loong64": "0.25.0",
-                "@esbuild/linux-mips64el": "0.25.0",
-                "@esbuild/linux-ppc64": "0.25.0",
-                "@esbuild/linux-riscv64": "0.25.0",
-                "@esbuild/linux-s390x": "0.25.0",
-                "@esbuild/linux-x64": "0.25.0",
-                "@esbuild/netbsd-arm64": "0.25.0",
-                "@esbuild/netbsd-x64": "0.25.0",
-                "@esbuild/openbsd-arm64": "0.25.0",
-                "@esbuild/openbsd-x64": "0.25.0",
-                "@esbuild/sunos-x64": "0.25.0",
-                "@esbuild/win32-arm64": "0.25.0",
-                "@esbuild/win32-ia32": "0.25.0",
-                "@esbuild/win32-x64": "0.25.0"
+                "@esbuild/android-arm": "0.18.12",
+                "@esbuild/android-arm64": "0.18.12",
+                "@esbuild/android-x64": "0.18.12",
+                "@esbuild/darwin-arm64": "0.18.12",
+                "@esbuild/darwin-x64": "0.18.12",
+                "@esbuild/freebsd-arm64": "0.18.12",
+                "@esbuild/freebsd-x64": "0.18.12",
+                "@esbuild/linux-arm": "0.18.12",
+                "@esbuild/linux-arm64": "0.18.12",
+                "@esbuild/linux-ia32": "0.18.12",
+                "@esbuild/linux-loong64": "0.18.12",
+                "@esbuild/linux-mips64el": "0.18.12",
+                "@esbuild/linux-ppc64": "0.18.12",
+                "@esbuild/linux-riscv64": "0.18.12",
+                "@esbuild/linux-s390x": "0.18.12",
+                "@esbuild/linux-x64": "0.18.12",
+                "@esbuild/netbsd-x64": "0.18.12",
+                "@esbuild/openbsd-x64": "0.18.12",
+                "@esbuild/sunos-x64": "0.18.12",
+                "@esbuild/win32-arm64": "0.18.12",
+                "@esbuild/win32-ia32": "0.18.12",
+                "@esbuild/win32-x64": "0.18.12"
             }
         },
         "node_modules/escalade": {
diff --git a/editors/code/package.json b/editors/code/package.json
index 3f09033051..58571a540e 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -59,7 +59,7 @@
         "@typescript-eslint/parser": "^6.0.0",
         "@vscode/test-electron": "^2.3.8",
         "@vscode/vsce": "^3.0.0",
-        "esbuild": "^0.25.0",
+        "esbuild": "^0.18.12",
         "eslint": "^8.44.0",
         "eslint-config-prettier": "^8.8.0",
         "ovsx": "^0.8.2",
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index e84c259e97..18f49643dc 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -117,7 +117,7 @@ impl fmt::Display for Location {
         let path = self.file.strip_prefix(project_root()).unwrap().display().to_string();
         let path = path.replace('\\', "/");
         let name = self.file.file_name().unwrap();
-        write!(f, " [{}](/{}#{}) ", name.to_str().unwrap(), path, self.line)
+        write!(f, " [{}]({}#{}) ", name.to_str().unwrap(), path, self.line)
     }
 }
 


Test patch:
diff --git a/crates/hir-def/src/expr_store/tests.rs b/crates/hir-def/src/expr_store/tests.rs
index 16bf46d3e3..9bf1ddb479 100644
--- a/crates/hir-def/src/expr_store/tests.rs
+++ b/crates/hir-def/src/expr_store/tests.rs
@@ -1,10 +1,11 @@
 mod block;
 
-use crate::{hir::MatchArm, test_db::TestDB, ModuleDefId};
 use expect_test::{expect, Expect};
 use la_arena::RawIdx;
 use test_fixture::WithFixture;
 
+use crate::{test_db::TestDB, ModuleDefId};
+
 use super::*;
 
 fn lower(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (TestDB, Arc<Body>, DefWithBodyId) {
@@ -459,45 +460,3 @@ async fn foo(a: (), b: i32) -> u32 {
     expect!["fn foo(�: (), �: i32) -> impl ::core::future::Future::<Output = u32> �"]
         .assert_eq(&printed);
 }
-
-#[test]
-fn range_bounds_are_hir_exprs() {
-    let (_, body, _) = lower(
-        r#"
-pub const L: i32 = 6;
-mod x {
-    pub const R: i32 = 100;
-}
-const fn f(x: i32) -> i32 {
-    match x {
-        -1..=5 => x * 10,
-        L..=x::R => x * 100,
-        _ => x,
-    }
-}"#,
-    );
-
-    let mtch_arms = body
-        .exprs
-        .iter()
-        .find_map(|(_, expr)| {
-            if let Expr::Match { arms, .. } = expr {
-                return Some(arms);
-            }
-
-            None
-        })
-        .unwrap();
-
-    let MatchArm { pat, .. } = mtch_arms[1];
-    match body.pats[pat] {
-        Pat::Range { start, end } => {
-            let hir_start = &body.exprs[start.unwrap()];
-            let hir_end = &body.exprs[end.unwrap()];
-
-            assert!(matches!(hir_start, Expr::Path { .. }));
-            assert!(matches!(hir_end, Expr::Path { .. }));
-        }
-        _ => {}
-    }
-}