about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_llvm/src/asm.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs54
-rw-r--r--compiler/rustc_codegen_llvm/src/consts.rs11
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs71
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs16
-rw-r--r--compiler/rustc_codegen_llvm/src/type_.rs48
-rw-r--r--src/bootstrap/src/core/build_steps/setup.rs4
-rw-r--r--src/doc/rustc-dev-guide/src/tests/directives.md14
-rw-r--r--src/doc/rustc/src/SUMMARY.md2
-rw-r--r--src/doc/rustc/src/platform-support.md6
-rw-r--r--src/doc/rustc/src/platform-support/aarch64-unknown-linux-gnu.md50
-rw-r--r--src/doc/rustc/src/platform-support/aarch64-unknown-none.md5
-rw-r--r--src/doc/rustc/src/platform-support/arm-linux.md4
-rw-r--r--src/doc/rustc/src/platform-support/armv7-unknown-linux-gnueabi.md51
-rw-r--r--src/doc/rustc/src/platform-support/armv7a-none-eabi.md5
-rw-r--r--src/doc/rustc/src/platform-support/armv7r-none-eabi.md7
-rw-r--r--src/doc/rustc/src/platform-support/armv8r-none-eabihf.md7
-rw-r--r--src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md6
-rw-r--r--src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md6
-rw-r--r--src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md6
-rw-r--r--src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md6
-rw-r--r--src/doc/rustc/src/platform-support/unknown-uefi.md8
-rw-r--r--src/etc/rust_analyzer_eglot.el8
-rw-r--r--src/etc/rust_analyzer_helix.toml14
-rw-r--r--src/etc/rust_analyzer_settings.json8
-rw-r--r--src/etc/rust_analyzer_zed.json23
-rw-r--r--src/librustdoc/clean/cfg.rs267
-rw-r--r--src/librustdoc/clean/mod.rs1
-rw-r--r--src/librustdoc/clean/types.rs264
-rw-r--r--src/tools/compiletest/src/common.rs6
-rw-r--r--src/tools/compiletest/src/directives.rs97
-rw-r--r--src/tools/compiletest/src/directives/tests.rs142
-rw-r--r--src/tools/compiletest/src/edition.rs35
-rw-r--r--src/tools/compiletest/src/lib.rs4
-rw-r--r--triagebot.toml4
35 files changed, 839 insertions, 425 deletions
diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs
index cc09fa5b69b..93b1cf272ab 100644
--- a/compiler/rustc_codegen_llvm/src/asm.rs
+++ b/compiler/rustc_codegen_llvm/src/asm.rs
@@ -538,9 +538,7 @@ pub(crate) fn inline_asm_call<'ll>(
             bx.const_u64(u64::from(span.lo().to_u32()) | (u64::from(span.hi().to_u32()) << 32)),
         )
     }));
-    let md = unsafe { llvm::LLVMMDNodeInContext2(bx.llcx, srcloc.as_ptr(), srcloc.len()) };
-    let md = bx.get_metadata_value(md);
-    llvm::LLVMSetMetadata(call, kind, md);
+    bx.cx.set_metadata_node(call, kind, &srcloc);
 
     Some(call)
 }
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 5271d0b4bb8..e7cb18ab22d 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -1,12 +1,12 @@
 use std::borrow::{Borrow, Cow};
+use std::iter;
 use std::ops::Deref;
-use std::{iter, ptr};
 
 use rustc_ast::expand::typetree::FncTree;
 pub(crate) mod autodiff;
 pub(crate) mod gpu_offload;
 
-use libc::{c_char, c_uint, size_t};
+use libc::{c_char, c_uint};
 use rustc_abi as abi;
 use rustc_abi::{Align, Size, WrappingRange};
 use rustc_codegen_ssa::MemFlags;
@@ -396,10 +396,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
             md.push(weight(is_cold));
         }
 
-        unsafe {
-            let md_node = llvm::LLVMMDNodeInContext2(self.cx.llcx, md.as_ptr(), md.len() as size_t);
-            self.cx.set_metadata(switch, llvm::MD_prof, md_node);
-        }
+        self.cx.set_metadata_node(switch, llvm::MD_prof, &md);
     }
 
     fn invoke(
@@ -801,22 +798,16 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
             return;
         }
 
-        unsafe {
-            let llty = self.cx.val_ty(load);
-            let md = [
-                llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.start)),
-                llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.end.wrapping_add(1))),
-            ];
-            let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, md.as_ptr(), md.len());
-            self.set_metadata(load, llvm::MD_range, md);
-        }
+        let llty = self.cx.val_ty(load);
+        let md = [
+            llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.start)),
+            llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.end.wrapping_add(1))),
+        ];
+        self.set_metadata_node(load, llvm::MD_range, &md);
     }
 
     fn nonnull_metadata(&mut self, load: &'ll Value) {
-        unsafe {
-            let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0);
-            self.set_metadata(load, llvm::MD_nonnull, md);
-        }
+        self.set_metadata_node(load, llvm::MD_nonnull, &[]);
     }
 
     fn store(&mut self, val: &'ll Value, ptr: &'ll Value, align: Align) -> &'ll Value {
@@ -865,8 +856,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
                     //
                     // [1]: https://llvm.org/docs/LangRef.html#store-instruction
                     let one = llvm::LLVMValueAsMetadata(self.cx.const_i32(1));
-                    let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, &one, 1);
-                    self.set_metadata(store, llvm::MD_nontemporal, md);
+                    self.set_metadata_node(store, llvm::MD_nontemporal, &[one]);
                 }
             }
             store
@@ -1381,10 +1371,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
     }
 
     fn set_invariant_load(&mut self, load: &'ll Value) {
-        unsafe {
-            let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0);
-            self.set_metadata(load, llvm::MD_invariant_load, md);
-        }
+        self.set_metadata_node(load, llvm::MD_invariant_load, &[]);
     }
 
     fn lifetime_start(&mut self, ptr: &'ll Value, size: Size) {
@@ -1528,25 +1515,16 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
 }
 impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
     fn align_metadata(&mut self, load: &'ll Value, align: Align) {
-        unsafe {
-            let md = [llvm::LLVMValueAsMetadata(self.cx.const_u64(align.bytes()))];
-            let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, md.as_ptr(), md.len());
-            self.set_metadata(load, llvm::MD_align, md);
-        }
+        let md = [llvm::LLVMValueAsMetadata(self.cx.const_u64(align.bytes()))];
+        self.set_metadata_node(load, llvm::MD_align, &md);
     }
 
     fn noundef_metadata(&mut self, load: &'ll Value) {
-        unsafe {
-            let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0);
-            self.set_metadata(load, llvm::MD_noundef, md);
-        }
+        self.set_metadata_node(load, llvm::MD_noundef, &[]);
     }
 
     pub(crate) fn set_unpredictable(&mut self, inst: &'ll Value) {
-        unsafe {
-            let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0);
-            self.set_metadata(inst, llvm::MD_unpredictable, md);
-        }
+        self.set_metadata_node(inst, llvm::MD_unpredictable, &[]);
     }
 }
 impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index 40375ef6510..b4ca85a26c8 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -494,16 +494,7 @@ impl<'ll> CodegenCx<'ll, '_> {
                 let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len());
                 let alloc = self.create_metadata(bytes);
                 let data = [section, alloc];
-                let meta =
-                    unsafe { llvm::LLVMMDNodeInContext2(self.llcx, data.as_ptr(), data.len()) };
-                let val = self.get_metadata_value(meta);
-                unsafe {
-                    llvm::LLVMAddNamedMetadataOperand(
-                        self.llmod,
-                        c"wasm.custom_sections".as_ptr(),
-                        val,
-                    )
-                };
+                self.module_add_named_metadata_node(self.llmod(), c"wasm.custom_sections", &data);
             }
         } else {
             base::set_link_section(g, attrs);
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index b1da6f7c740..922575dd63c 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -34,7 +34,7 @@ use smallvec::SmallVec;
 use crate::back::write::to_llvm_code_model;
 use crate::callee::get_fn;
 use crate::debuginfo::metadata::apply_vcall_visibility_metadata;
-use crate::llvm::{Metadata, MetadataKindId};
+use crate::llvm::{Metadata, MetadataKindId, Module};
 use crate::type_::Type;
 use crate::value::Value;
 use crate::{attributes, common, coverageinfo, debuginfo, llvm, llvm_util};
@@ -495,14 +495,7 @@ pub(crate) unsafe fn create_module<'ll>(
         format!("rustc version {}", option_env!("CFG_VERSION").expect("CFG_VERSION"));
 
     let name_metadata = cx.create_metadata(rustc_producer.as_bytes());
-
-    unsafe {
-        llvm::LLVMAddNamedMetadataOperand(
-            llmod,
-            c"llvm.ident".as_ptr(),
-            &cx.get_metadata_value(llvm::LLVMMDNodeInContext2(llcx, &name_metadata, 1)),
-        );
-    }
+    cx.module_add_named_metadata_node(llmod, c"llvm.ident", &[name_metadata]);
 
     // Emit RISC-V specific target-abi metadata
     // to workaround lld as the LTO plugin not
@@ -1002,6 +995,11 @@ impl CodegenCx<'_, '_> {
 }
 
 impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
+    /// Wrapper for `LLVMMDNodeInContext2`, i.e. `llvm::MDNode::get`.
+    pub(crate) fn md_node_in_context(&self, md_list: &[&'ll Metadata]) -> &'ll Metadata {
+        unsafe { llvm::LLVMMDNodeInContext2(self.llcx(), md_list.as_ptr(), md_list.len()) }
+    }
+
     /// A wrapper for [`llvm::LLVMSetMetadata`], but it takes `Metadata` as a parameter instead of `Value`.
     pub(crate) fn set_metadata<'a>(
         &self,
@@ -1012,6 +1010,61 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
         let node = self.get_metadata_value(md);
         llvm::LLVMSetMetadata(val, kind_id, node);
     }
+
+    /// Helper method for the sequence of calls:
+    /// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata)
+    /// - `LLVMMetadataAsValue` (to adapt that node to an `llvm::Value`)
+    /// - `LLVMSetMetadata` (to set that node as metadata of `kind_id` for `instruction`)
+    pub(crate) fn set_metadata_node(
+        &self,
+        instruction: &'ll Value,
+        kind_id: MetadataKindId,
+        md_list: &[&'ll Metadata],
+    ) {
+        let md = self.md_node_in_context(md_list);
+        self.set_metadata(instruction, kind_id, md);
+    }
+
+    /// Helper method for the sequence of calls:
+    /// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata)
+    /// - `LLVMMetadataAsValue` (to adapt that node to an `llvm::Value`)
+    /// - `LLVMAddNamedMetadataOperand` (to set that node as metadata of `kind_name` for `module`)
+    pub(crate) fn module_add_named_metadata_node(
+        &self,
+        module: &'ll Module,
+        kind_name: &CStr,
+        md_list: &[&'ll Metadata],
+    ) {
+        let md = self.md_node_in_context(md_list);
+        let md_as_val = self.get_metadata_value(md);
+        unsafe { llvm::LLVMAddNamedMetadataOperand(module, kind_name.as_ptr(), md_as_val) };
+    }
+
+    /// Helper method for the sequence of calls:
+    /// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata)
+    /// - `LLVMRustGlobalAddMetadata` (to set that node as metadata of `kind_id` for `global`)
+    pub(crate) fn global_add_metadata_node(
+        &self,
+        global: &'ll Value,
+        kind_id: MetadataKindId,
+        md_list: &[&'ll Metadata],
+    ) {
+        let md = self.md_node_in_context(md_list);
+        unsafe { llvm::LLVMRustGlobalAddMetadata(global, kind_id, md) };
+    }
+
+    /// Helper method for the sequence of calls:
+    /// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata)
+    /// - `LLVMGlobalSetMetadata` (to set that node as metadata of `kind_id` for `global`)
+    pub(crate) fn global_set_metadata_node(
+        &self,
+        global: &'ll Value,
+        kind_id: MetadataKindId,
+        md_list: &[&'ll Metadata],
+    ) {
+        let md = self.md_node_in_context(md_list);
+        unsafe { llvm::LLVMGlobalSetMetadata(global, kind_id, md) };
+    }
 }
 
 impl HasDataLayout for CodegenCx<'_, '_> {
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index bc20c759413..2f9e7cae54f 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -1607,17 +1607,11 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>(
     let trait_ref_typeid = typeid_for_trait_ref(cx.tcx, trait_ref);
     let typeid = cx.create_metadata(trait_ref_typeid.as_bytes());
 
-    unsafe {
-        let v = [llvm::LLVMValueAsMetadata(cx.const_usize(0)), typeid];
-        llvm::LLVMRustGlobalAddMetadata(
-            vtable,
-            llvm::MD_type,
-            llvm::LLVMMDNodeInContext2(cx.llcx, v.as_ptr(), v.len()),
-        );
-        let vcall_visibility = llvm::LLVMValueAsMetadata(cx.const_u64(vcall_visibility as u64));
-        let vcall_visibility_metadata = llvm::LLVMMDNodeInContext2(cx.llcx, &vcall_visibility, 1);
-        llvm::LLVMGlobalSetMetadata(vtable, llvm::MD_vcall_visibility, vcall_visibility_metadata);
-    }
+    let type_ = [llvm::LLVMValueAsMetadata(cx.const_usize(0)), typeid];
+    cx.global_add_metadata_node(vtable, llvm::MD_type, &type_);
+
+    let vcall_visibility = [llvm::LLVMValueAsMetadata(cx.const_u64(vcall_visibility as u64))];
+    cx.global_set_metadata_node(vtable, llvm::MD_vcall_visibility, &vcall_visibility);
 }
 
 /// Creates debug information for the given vtable, which is for the
diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs
index 5b97898a4b8..1e5cf8374e3 100644
--- a/compiler/rustc_codegen_llvm/src/type_.rs
+++ b/compiler/rustc_codegen_llvm/src/type_.rs
@@ -302,26 +302,14 @@ impl<'ll, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
 impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
     fn add_type_metadata(&self, function: &'ll Value, typeid: &[u8]) {
         let typeid_metadata = self.create_metadata(typeid);
-        unsafe {
-            let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
-            llvm::LLVMRustGlobalAddMetadata(
-                function,
-                llvm::MD_type,
-                llvm::LLVMMDNodeInContext2(self.llcx, v.as_ptr(), v.len()),
-            )
-        }
+        let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
+        self.global_add_metadata_node(function, llvm::MD_type, &v);
     }
 
     fn set_type_metadata(&self, function: &'ll Value, typeid: &[u8]) {
         let typeid_metadata = self.create_metadata(typeid);
-        unsafe {
-            let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
-            llvm::LLVMGlobalSetMetadata(
-                function,
-                llvm::MD_type,
-                llvm::LLVMMDNodeInContext2(self.llcx, v.as_ptr(), v.len()),
-            )
-        }
+        let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
+        self.global_set_metadata_node(function, llvm::MD_type, &v);
     }
 
     fn typeid_metadata(&self, typeid: &[u8]) -> Option<&'ll Metadata> {
@@ -329,32 +317,12 @@ impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
     }
 
     fn add_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) {
-        let kcfi_type_metadata = self.const_u32(kcfi_typeid);
-        unsafe {
-            llvm::LLVMRustGlobalAddMetadata(
-                function,
-                llvm::MD_kcfi_type,
-                llvm::LLVMMDNodeInContext2(
-                    self.llcx,
-                    &llvm::LLVMValueAsMetadata(kcfi_type_metadata),
-                    1,
-                ),
-            )
-        }
+        let kcfi_type_metadata = [llvm::LLVMValueAsMetadata(self.const_u32(kcfi_typeid))];
+        self.global_add_metadata_node(function, llvm::MD_kcfi_type, &kcfi_type_metadata);
     }
 
     fn set_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) {
-        let kcfi_type_metadata = self.const_u32(kcfi_typeid);
-        unsafe {
-            llvm::LLVMGlobalSetMetadata(
-                function,
-                llvm::MD_kcfi_type,
-                llvm::LLVMMDNodeInContext2(
-                    self.llcx,
-                    &llvm::LLVMValueAsMetadata(kcfi_type_metadata),
-                    1,
-                ),
-            )
-        }
+        let kcfi_type_metadata = [llvm::LLVMValueAsMetadata(self.const_u32(kcfi_typeid))];
+        self.global_set_metadata_node(function, llvm::MD_kcfi_type, &kcfi_type_metadata);
     }
 }
diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs
index 9f9af1d9abe..4b0080f1c80 100644
--- a/src/bootstrap/src/core/build_steps/setup.rs
+++ b/src/bootstrap/src/core/build_steps/setup.rs
@@ -587,6 +587,7 @@ Select which editor you would like to set up [default: None]: ";
                 "631c837b0e98ae35fd48b0e5f743b1ca60adadf2d0a2b23566ba25df372cf1a9",
                 "080955765db84bb6cbf178879f489c4e2369397626a6ecb3debedb94a9d0b3ce",
                 "f501475c6654187091c924ae26187fa5791d74d4a8ab3fb61fbbe4c0275aade1",
+                "e260553b71e4773c30a63c4b23b42b279fc73e72f95b775c47b7b7c511c51595",
             ],
             EditorKind::Helix => &[
                 "2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233",
@@ -594,6 +595,7 @@ Select which editor you would like to set up [default: None]: ";
                 "f252dcc30ca85a193a699581e5e929d5bd6c19d40d7a7ade5e257a9517a124a5",
                 "198c195ed0c070d15907b279b8b4ea96198ca71b939f5376454f3d636ab54da5",
                 "1c43ead340b20792b91d02b08494ee68708e7e09f56b6766629b4b72079208f1",
+                "eec09a09452682060afd23dd5d3536ccac5615b3cdbf427366446901215fb9f6",
             ],
             EditorKind::Vim | EditorKind::VsCode => &[
                 "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8",
@@ -610,6 +612,7 @@ Select which editor you would like to set up [default: None]: ";
                 "f954316090936c7e590c253ca9d524008375882fa13c5b41d7e2547a896ff893",
                 "701b73751efd7abd6487f2c79348dab698af7ac4427b79fa3d2087c867144b12",
                 "a61df796c0c007cb6512127330564e49e57d558dec715703916a928b072a1054",
+                "02a49ac2d31f00ef6e4531c44e00dac51cea895112e480553f1ba060b3942a47",
             ],
             EditorKind::Zed => &[
                 "bbce727c269d1bd0c98afef4d612eb4ce27aea3c3a8968c5f10b31affbc40b6c",
@@ -617,6 +620,7 @@ Select which editor you would like to set up [default: None]: ";
                 "2e96bf0d443852b12f016c8fc9840ab3d0a2b4fe0b0fb3a157e8d74d5e7e0e26",
                 "4fadd4c87389a601a27db0d3d74a142fa3a2e656ae78982e934dbe24bee32ad6",
                 "f0bb3d23ab1a49175ab0ef5c4071af95bb03d01d460776cdb716d91333443382",
+                "5ef83292111d9a8bb63b6afc3abf42d0bc78fe24985f0d2e039e73258b5dab8f",
             ],
         }
     }
diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md
index 4be78fac4ec..3b6c8917464 100644
--- a/src/doc/rustc-dev-guide/src/tests/directives.md
+++ b/src/doc/rustc-dev-guide/src/tests/directives.md
@@ -262,6 +262,20 @@ Consider writing the test as a proper incremental test instead.
 
 </div>
 
+#### The edition directive
+
+The `//@ edition` directive can take an exact edition, a bounded half-open range of editions or a left-bounded half-open range of editions, this affects which edition is used by `./x test` to run the test. For example:
+
+- A test with the `//@ edition: 2018` directive will only run under the 2018 edition.
+- A test with the `//@ edition: 2015..2021` directive can be run under both the 2015 and 2018 editions. However, CI will only run the test with the lowest edition possible (2015 in this case).
+- A test with the `//@ edition: 2018..` directive will run under any edition greater or equal than 2018. However, CI will only run the test with the lowest edition possible (2018 in this case).
+
+You can also force `./x test` to use a specific edition by passing the `-- --edition=` argument. However, tests with the `//@ edition` directive will clamp the value passed to the argument. For example, if we run `./x test -- --edition=2015`:
+
+- A test with the `//@ edition: 2018` will run with the 2018 edition. 
+- A test with the `//@ edition: 2015..2021` will be run with the 2015 edition. 
+- A test with the `//@ edition: 2018..` will run with the 2018 edition. 
+
 ### Rustdoc
 
 | Directive   | Explanation                                                  | Supported test suites                   | Possible values           |
diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md
index 67882bb3813..a9ce738a013 100644
--- a/src/doc/rustc/src/SUMMARY.md
+++ b/src/doc/rustc/src/SUMMARY.md
@@ -47,6 +47,7 @@
     - [\*-apple-watchos](platform-support/apple-watchos.md)
     - [\*-apple-visionos](platform-support/apple-visionos.md)
     - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md)
+    - [aarch64-unknown-linux-gnu](platform-support/aarch64-unknown-linux-gnu.md)
     - [aarch64-unknown-linux-musl](platform-support/aarch64-unknown-linux-musl.md)
     - [aarch64-unknown-none*](platform-support/aarch64-unknown-none.md)
     - [aarch64_be-unknown-none-softfloat](platform-support/aarch64_be-unknown-none-softfloat.md)
@@ -67,6 +68,7 @@
     - [arm\*-unknown-linux-\*](./platform-support/arm-linux.md)
         - [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md)
         - [armv5te-unknown-linux-gnueabi](platform-support/armv5te-unknown-linux-gnueabi.md)
+        - [armv7-unknown-linux-gnueabi](platform-support/armv7-unknown-linux-gnueabi.md)
         - [armv7-unknown-linux-uclibceabi](platform-support/armv7-unknown-linux-uclibceabi.md)
         - [armv7-unknown-linux-uclibceabihf](platform-support/armv7-unknown-linux-uclibceabihf.md)
     - [armv6k-nintendo-3ds](platform-support/armv6k-nintendo-3ds.md)
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index d0b6ed51bc1..263ea1ddb42 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -34,7 +34,7 @@ target | notes
 -------|-------
 [`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+)
 [`aarch64-pc-windows-msvc`](platform-support/windows-msvc.md) | ARM64 Windows MSVC
-`aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1+, glibc 2.17+)
+[`aarch64-unknown-linux-gnu`](platform-support/aarch64-unknown-linux-gnu.md) | ARM64 Linux (kernel 4.1+, glibc 2.17+)
 [`i686-pc-windows-msvc`](platform-support/windows-msvc.md) | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment]
 `i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+, Pentium 4) [^x86_32-floats-return-ABI]
 [`x86_64-pc-windows-gnu`](platform-support/windows-gnu.md) | 64-bit MinGW (Windows 10+, Windows Server 2016+)
@@ -93,7 +93,7 @@ target | notes
 [`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ARM64 OpenHarmony
 `arm-unknown-linux-gnueabi` | Armv6 Linux (kernel 3.2+, glibc 2.17)
 `arm-unknown-linux-gnueabihf` | Armv6 Linux, hardfloat (kernel 3.2+, glibc 2.17)
-`armv7-unknown-linux-gnueabihf` | Armv7-A Linux, hardfloat (kernel 3.2+, glibc 2.17)
+[`armv7-unknown-linux-gnueabihf`](platform-support/armv7-unknown-linux-gnueabi.md) | Armv7-A Linux, hardfloat (kernel 3.2+, glibc 2.17)
 [`armv7-unknown-linux-ohos`](platform-support/openharmony.md) | Armv7-A OpenHarmony
 [`loongarch64-unknown-linux-gnu`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19+, glibc 2.36), LSX required
 [`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19+, musl 1.2.5), LSX required
@@ -159,7 +159,7 @@ target | std | notes
 [`armv5te-unknown-linux-gnueabi`](platform-support/armv5te-unknown-linux-gnueabi.md) | ✓ | Armv5TE Linux (kernel 4.4+, glibc 2.23)
 `armv5te-unknown-linux-musleabi` | ✓ | Armv5TE Linux with musl 1.2.3
 [`armv7-linux-androideabi`](platform-support/android.md) | ✓ | Armv7-A Android
-`armv7-unknown-linux-gnueabi` | ✓ | Armv7-A Linux (kernel 4.15+, glibc 2.27)
+[`armv7-unknown-linux-gnueabi`](platform-support/armv7-unknown-linux-gnueabi.md) | ✓ | Armv7-A Linux (kernel 4.15+, glibc 2.27)
 `armv7-unknown-linux-musleabi` | ✓ | Armv7-A Linux with musl 1.2.3
 `armv7-unknown-linux-musleabihf` | ✓ | Armv7-A Linux with musl 1.2.3, hardfloat
 [`armv7a-none-eabi`](platform-support/armv7a-none-eabi.md) | * | Bare Armv7-A
diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-gnu.md b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-gnu.md
new file mode 100644
index 00000000000..2003a3cb9ea
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-gnu.md
@@ -0,0 +1,50 @@
+# `aarch64-unknown-linux-gnu`
+
+**Tier: 1 (with Host Tools)**
+
+Target for 64-bit little endian ARMv8-A Linux 4.1+ programs using glibc 2.17+.
+
+## Target maintainers
+
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
+
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
+
+## Requirements
+
+Building the target itself requires a 64-bit little endian ARMv8-A compiler that is supported by
+`cc-rs`.
+
+## Building the target
+
+The target can be built by enabling it for a `rustc` build:
+
+```toml
+[build]
+target = ["aarch64-unknown-linux-gnu"]
+```
+
+If cross-compiling, make sure your C compiler is included in `$PATH`, then add it to the
+`bootstrap.toml`:
+
+```toml
+[target.aarch64-unknown-linux-musl]
+cc = "aarch64-linux-gnu-gcc"
+cxx = "aarch64-linux-gnu-g++"
+ar = "aarch64-linux-gnu-ar"
+linker = "aarch64-linux-gnu-gcc"
+```
+
+## Building Rust programs
+
+This target is distributed through `rustup`, and otherwise requires no special configuration.
+
+## Cross-compilation
+
+This target can be cross-compiled from any host.
+
+## Testing
+
+This target can be tested as normal with `x.py` on a 64-bit little endian ARMv8-A host or via QEMU
+emulation.
diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md
index 7e18e8c157f..3d776677d23 100644
--- a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md
+++ b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md
@@ -14,9 +14,12 @@ Processors in this family include the [Arm Cortex-A35, 53, 76, etc][aarch64-cpus
 
 ## Target maintainers
 
-[Rust Embedded Devices Working Group Arm Team]
+- [Rust Embedded Devices Working Group Arm Team]
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
 
 [Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
 
 ## Target CPU and Target Feature options
 
diff --git a/src/doc/rustc/src/platform-support/arm-linux.md b/src/doc/rustc/src/platform-support/arm-linux.md
index 5f40743f3d0..c461a1a3403 100644
--- a/src/doc/rustc/src/platform-support/arm-linux.md
+++ b/src/doc/rustc/src/platform-support/arm-linux.md
@@ -14,8 +14,8 @@ Linux (but not Android). Those targets are:
 * [`armv5te-unknown-linux-gnueabi`](armv5te-unknown-linux-gnueabi.md)
 * `armv5te-unknown-linux-musleabi`
 * `armv5te-unknown-linux-uclibceabi`
-* `armv7-unknown-linux-gnueabi`
-* `armv7-unknown-linux-gnueabihf`
+* [`armv7-unknown-linux-gnueabi`](armv7-unknown-linux-gnueabi.md)
+* [`armv7-unknown-linux-gnueabihf`](armv7-unknown-linux-gnueabi.md)
 * `armv7-unknown-linux-musleabi`
 * `armv7-unknown-linux-musleabihf`
 * `armv7-unknown-linux-ohos`
diff --git a/src/doc/rustc/src/platform-support/armv7-unknown-linux-gnueabi.md b/src/doc/rustc/src/platform-support/armv7-unknown-linux-gnueabi.md
new file mode 100644
index 00000000000..c2fe63a4908
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/armv7-unknown-linux-gnueabi.md
@@ -0,0 +1,51 @@
+# `armv7-unknown-linux-gnueabi` and `armv7-unknown-linux-gnueabihf`
+
+* **Tier: 2 (with Host Tools)** for `armv7-unknown-linux-gnueabihf`
+* **Tier: 2** for `armv7-unknown-linux-gnueabi`
+
+Target for 32-bit little endian ARMv7-A Linux 3.2+ programs using glibc 2.17+.
+
+## Target maintainers
+
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
+
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
+
+## Requirements
+
+Building the targets themselves requires a 32-bit little endian ARMv7-A compiler that is supported
+by `cc-rs`.
+
+## Building the target
+
+These targets can be built by enabling it for a `rustc` build:
+
+```toml
+[build]
+target = ["armv7-unknown-linux-gnueabihf", "armv7-unknown-linux-gnueabi"]
+```
+
+If cross-compiling, make sure your C compiler is included in `$PATH`, then add it to the
+`bootstrap.toml`:
+
+```toml
+[target.aarch64-unknown-linux-musl]
+cc = "arm-linux-gnu-gcc"
+cxx = "arm-linux-gnu-g++"
+ar = "arm-linux-gnu-ar"
+linker = "arm-linux-gnu-gcc"
+```
+
+## Building Rust programs
+
+These targets is distributed through `rustup`, and otherwise requires no special configuration.
+
+## Cross-compilation
+
+These targets can be cross-compiled from any host.
+
+## Testing
+
+These targets can be tested as normal with `x.py` on a 32-bit little endian ARMv7-A host or via
+QEMU emulation.
diff --git a/src/doc/rustc/src/platform-support/armv7a-none-eabi.md b/src/doc/rustc/src/platform-support/armv7a-none-eabi.md
index 3dadda86a5f..22278a0a7dc 100644
--- a/src/doc/rustc/src/platform-support/armv7a-none-eabi.md
+++ b/src/doc/rustc/src/platform-support/armv7a-none-eabi.md
@@ -19,9 +19,12 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all
 
 ## Target maintainers
 
-[Rust Embedded Devices Working Group Arm Team]
+- [Rust Embedded Devices Working Group Arm Team]
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
 
 [Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
 
 ## Requirements
 
diff --git a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md
index c1252b4a4bf..9429eb6ab8a 100644
--- a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md
+++ b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md
@@ -15,10 +15,13 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all
 
 ## Target maintainers
 
-[@chrisnc](https://github.com/chrisnc)
-[Rust Embedded Devices Working Group Arm Team]
+- [@chrisnc](https://github.com/chrisnc)
+- [Rust Embedded Devices Working Group Arm Team]
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
 
 [Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
 
 ## Requirements
 
diff --git a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md
index 0d5a36c3ee2..e465eb79f49 100644
--- a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md
+++ b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md
@@ -17,10 +17,13 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all
 
 ## Target maintainers
 
-[@chrisnc](https://github.com/chrisnc)
-[Rust Embedded Devices Working Group Arm Team]
+- [@chrisnc](https://github.com/chrisnc)
+- [Rust Embedded Devices Working Group Arm Team]
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
 
 [Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
 
 ## Requirements
 
diff --git a/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md
index 98dcf9bd396..192e013d3a4 100644
--- a/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md
+++ b/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md
@@ -22,7 +22,11 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all
 
 ## Target maintainers
 
-[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
+- [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
+
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
 
 ## Target CPU and Target Feature options
 
diff --git a/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md
index d8f3970c8bf..b04cb7bfacf 100644
--- a/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md
+++ b/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md
@@ -23,7 +23,11 @@ only option because there is no FPU support in [Armv7-M].
 
 ## Target maintainers
 
-[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
+- [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
+
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
 
 ## Target CPU and Target Feature options
 
diff --git a/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md
index b16d450275d..104520854b4 100644
--- a/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md
+++ b/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md
@@ -23,7 +23,11 @@ only option because there is no FPU support in [Armv8-M] Baseline.
 
 ## Target maintainers
 
-[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
+- [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
+
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
 
 ## Target CPU and Target Feature options
 
diff --git a/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md
index a2d515d07ea..5cc535ce376 100644
--- a/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md
+++ b/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md
@@ -26,7 +26,11 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all
 
 ## Target maintainers
 
-[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
+- [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
+
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
 
 ## Target CPU and Target Feature options
 
diff --git a/src/doc/rustc/src/platform-support/unknown-uefi.md b/src/doc/rustc/src/platform-support/unknown-uefi.md
index 9587590d12d..e8989616b84 100644
--- a/src/doc/rustc/src/platform-support/unknown-uefi.md
+++ b/src/doc/rustc/src/platform-support/unknown-uefi.md
@@ -13,8 +13,12 @@ Available targets:
 
 ## Target maintainers
 
-[@dvdhrm](https://github.com/dvdhrm)
-[@nicholasbishop](https://github.com/nicholasbishop)
+- [@dvdhrm](https://github.com/dvdhrm)
+- [@nicholasbishop](https://github.com/nicholasbishop)
+- (for `aarch64-unknown-uefi` only) [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
+
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
 
 ## Requirements
 
diff --git a/src/etc/rust_analyzer_eglot.el b/src/etc/rust_analyzer_eglot.el
index 3151cb1a6e7..e5abf67235a 100644
--- a/src/etc/rust_analyzer_eglot.el
+++ b/src/etc/rust_analyzer_eglot.el
@@ -6,6 +6,8 @@
                                        :overrideCommand ["python3"
                                                          "x.py"
                                                          "check"
+                                                         "--build-dir"
+                                                         "build-rust-analyzer"
                                                          "--json-output"])
                  :linkedProjects ["Cargo.toml"
                                   "compiler/rustc_codegen_cranelift/Cargo.toml"
@@ -13,9 +15,9 @@
                                   "library/Cargo.toml"
                                   "src/bootstrap/Cargo.toml"
                                   "src/tools/rust-analyzer/Cargo.toml"]
-                 :rustfmt ( :overrideCommand ["build/host/rustfmt/bin/rustfmt"
+                 :rustfmt ( :overrideCommand ["build-rust-analyzer/host/rustfmt/bin/rustfmt"
                                               "--edition=2024"])
-                 :procMacro ( :server "build/host/stage0/libexec/rust-analyzer-proc-macro-srv"
+                 :procMacro ( :server "build-rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv"
                                       :enable t)
                  :cargo ( :buildScripts ( :enable t
                                                   :invocationLocation "root"
@@ -23,6 +25,8 @@
                                                   :overrideCommand ["python3"
                                                                     "x.py"
                                                                     "check"
+                                                                    "--build-dir"
+                                                                    "build-rust-analyzer"
                                                                     "--json-output"
                                                                     "--compile-time-deps"])]
                                         :sysrootSrc "./library"
diff --git a/src/etc/rust_analyzer_helix.toml b/src/etc/rust_analyzer_helix.toml
index 8c1782a1abc..e2de2a374cb 100644
--- a/src/etc/rust_analyzer_helix.toml
+++ b/src/etc/rust_analyzer_helix.toml
@@ -1,10 +1,10 @@
 # This config uses a separate build directory for rust-analyzer,
 # so that r-a's checks don't block user `x` commands and vice-verse.
-# R-a's build directory is located in `build/rust-analyzer`.
+# R-a's build directory is located in `build-rust-analyzer`.
 #
 # To build rustfmt and proc macro server for r-a run the following command:
 # ```
-# x b proc-macro-srv-cli rustfmt --stage 0 --build-dir build/rust-analyzer
+# x b proc-macro-srv-cli rustfmt --stage 0 --build-dir build-rust-analyzer
 # ```
 
 [language-server.rust-analyzer.config]
@@ -26,17 +26,17 @@ overrideCommand = [
     "check",
     "--json-output",
     "--build-dir",
-    "build/rust-analyzer",
+    "build-rust-analyzer",
 ]
 
 [language-server.rust-analyzer.config.rustfmt]
 overrideCommand = [
-    "build/rust-analyzer/host/rustfmt/bin/rustfmt",
+    "build-rust-analyzer/host/rustfmt/bin/rustfmt",
     "--edition=2024"
 ]
 
 [language-server.rust-analyzer.config.procMacro]
-server = "build/rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv"
+server = "build-rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv"
 enable = true
 
 [language-server.rust-analyzer.config.rustc]
@@ -58,6 +58,6 @@ overrideCommand = [
     "check",
     "--json-output",
     "--build-dir",
-    "build/rust-analyzer",
-    "--compile-time-deps"
+    "build-rust-analyzer",
+    "--compile-time-deps",
 ]
diff --git a/src/etc/rust_analyzer_settings.json b/src/etc/rust_analyzer_settings.json
index b31169857c5..f89e8a2df18 100644
--- a/src/etc/rust_analyzer_settings.json
+++ b/src/etc/rust_analyzer_settings.json
@@ -5,6 +5,8 @@
         "python3",
         "x.py",
         "check",
+        "--build-dir",
+        "build-rust-analyzer",
         "--json-output"
     ],
     "rust-analyzer.linkedProjects": [
@@ -16,10 +18,10 @@
         "src/tools/rust-analyzer/Cargo.toml"
     ],
     "rust-analyzer.rustfmt.overrideCommand": [
-        "${workspaceFolder}/build/host/rustfmt/bin/rustfmt",
+        "${workspaceFolder}/build-rust-analyzer/host/rustfmt/bin/rustfmt",
         "--edition=2024"
     ],
-    "rust-analyzer.procMacro.server": "${workspaceFolder}/build/host/stage0/libexec/rust-analyzer-proc-macro-srv",
+    "rust-analyzer.procMacro.server": "${workspaceFolder}/build-rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv",
     "rust-analyzer.procMacro.enable": true,
     "rust-analyzer.cargo.buildScripts.enable": true,
     "rust-analyzer.cargo.buildScripts.invocationStrategy": "once",
@@ -27,6 +29,8 @@
         "python3",
         "x.py",
         "check",
+        "--build-dir",
+        "build-rust-analyzer",
         "--json-output",
         "--compile-time-deps"
     ],
diff --git a/src/etc/rust_analyzer_zed.json b/src/etc/rust_analyzer_zed.json
index 7eace92500e..d98a082a9b8 100644
--- a/src/etc/rust_analyzer_zed.json
+++ b/src/etc/rust_analyzer_zed.json
@@ -7,7 +7,15 @@
             "enable": true,
             "invocationLocation": "root",
             "invocationStrategy": "once",
-            "overrideCommand": ["python3", "x.py", "check", "--json-output", "--compile-time-deps"]
+            "overrideCommand": [
+              "python3",
+              "x.py",
+              "check",
+              "--build-dir",
+              "build-rust-analyzer",
+              "--compile-time-deps",
+              "--json-output"
+            ]
           },
           "extraEnv": {
             "RUSTC_BOOTSTRAP": "1"
@@ -17,7 +25,14 @@
         "check": {
           "invocationLocation": "root",
           "invocationStrategy": "once",
-          "overrideCommand": ["python3", "x.py", "check", "--json-output"]
+          "overrideCommand": [
+            "python3",
+            "x.py",
+            "check",
+            "--json-output",
+            "--build-dir",
+            "build-rust-analyzer"
+          ]
         },
         "linkedProjects": [
           "Cargo.toml",
@@ -29,14 +44,14 @@
         ],
         "procMacro": {
           "enable": true,
-          "server": "build/host/stage0/libexec/rust-analyzer-proc-macro-srv"
+          "server": "build-rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv"
         },
         "rustc": {
           "source": "./Cargo.toml"
         },
         "rustfmt": {
           "overrideCommand": [
-            "build/host/rustfmt/bin/rustfmt",
+            "build-rust-analyzer/host/rustfmt/bin/rustfmt",
             "--edition=2024"
           ]
         },
diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs
index aa614b73dea..881a81b22f0 100644
--- a/src/librustdoc/clean/cfg.rs
+++ b/src/librustdoc/clean/cfg.rs
@@ -3,14 +3,18 @@
 // FIXME: Once the portability lint RFC is implemented (see tracking issue #41619),
 // switch to use those structures instead.
 
+use std::sync::Arc;
 use std::{fmt, mem, ops};
 
 use itertools::Either;
 use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit};
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_hir::attrs::AttributeKind;
+use rustc_middle::ty::TyCtxt;
 use rustc_session::parse::ParseSess;
 use rustc_span::Span;
 use rustc_span::symbol::{Symbol, sym};
+use {rustc_ast as ast, rustc_hir as hir};
 
 use crate::display::{Joined as _, MaybeDisplay, Wrapped};
 use crate::html::escape::Escape;
@@ -600,3 +604,264 @@ impl fmt::Display for Display<'_> {
         }
     }
 }
+
+/// This type keeps track of (doc) cfg information as we go down the item tree.
+#[derive(Clone, Debug)]
+pub(crate) struct CfgInfo {
+    /// List of currently active `doc(auto_cfg(hide(...)))` cfgs, minus currently active
+    /// `doc(auto_cfg(show(...)))` cfgs.
+    hidden_cfg: FxHashSet<Cfg>,
+    /// Current computed `cfg`. Each time we enter a new item, this field is updated as well while
+    /// taking into account the `hidden_cfg` information.
+    current_cfg: Cfg,
+    /// Whether the `doc(auto_cfg())` feature is enabled or not at this point.
+    auto_cfg_active: bool,
+    /// If the parent item used `doc(cfg(...))`, then we don't want to overwrite `current_cfg`,
+    /// instead we will concatenate with it. However, if it's not the case, we need to overwrite
+    /// `current_cfg`.
+    parent_is_doc_cfg: bool,
+}
+
+impl Default for CfgInfo {
+    fn default() -> Self {
+        Self {
+            hidden_cfg: FxHashSet::from_iter([
+                Cfg::Cfg(sym::test, None),
+                Cfg::Cfg(sym::doc, None),
+                Cfg::Cfg(sym::doctest, None),
+            ]),
+            current_cfg: Cfg::True,
+            auto_cfg_active: true,
+            parent_is_doc_cfg: false,
+        }
+    }
+}
+
+fn show_hide_show_conflict_error(
+    tcx: TyCtxt<'_>,
+    item_span: rustc_span::Span,
+    previous: rustc_span::Span,
+) {
+    let mut diag = tcx.sess.dcx().struct_span_err(
+        item_span,
+        format!(
+            "same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item"
+        ),
+    );
+    diag.span_note(previous, "first change was here");
+    diag.emit();
+}
+
+/// This functions updates the `hidden_cfg` field of the provided `cfg_info` argument.
+///
+/// It also checks if a same `cfg` is present in both `auto_cfg(hide(...))` and
+/// `auto_cfg(show(...))` on the same item and emits an error if it's the case.
+///
+/// Because we go through a list of `cfg`s, we keep track of the `cfg`s we saw in `new_show_attrs`
+/// and in `new_hide_attrs` arguments.
+fn handle_auto_cfg_hide_show(
+    tcx: TyCtxt<'_>,
+    cfg_info: &mut CfgInfo,
+    sub_attr: &MetaItemInner,
+    is_show: bool,
+    new_show_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
+    new_hide_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
+) {
+    if let MetaItemInner::MetaItem(item) = sub_attr
+        && let MetaItemKind::List(items) = &item.kind
+    {
+        for item in items {
+            // FIXME: Report in case `Cfg::parse` reports an error?
+            if let Ok(Cfg::Cfg(key, value)) = Cfg::parse(item) {
+                if is_show {
+                    if let Some(span) = new_hide_attrs.get(&(key, value)) {
+                        show_hide_show_conflict_error(tcx, item.span(), *span);
+                    } else {
+                        new_show_attrs.insert((key, value), item.span());
+                    }
+                    cfg_info.hidden_cfg.remove(&Cfg::Cfg(key, value));
+                } else {
+                    if let Some(span) = new_show_attrs.get(&(key, value)) {
+                        show_hide_show_conflict_error(tcx, item.span(), *span);
+                    } else {
+                        new_hide_attrs.insert((key, value), item.span());
+                    }
+                    cfg_info.hidden_cfg.insert(Cfg::Cfg(key, value));
+                }
+            }
+        }
+    }
+}
+
+pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute> + Clone>(
+    attrs: I,
+    tcx: TyCtxt<'_>,
+    cfg_info: &mut CfgInfo,
+) -> Option<Arc<Cfg>> {
+    fn single<T: IntoIterator>(it: T) -> Option<T::Item> {
+        let mut iter = it.into_iter();
+        let item = iter.next()?;
+        if iter.next().is_some() {
+            return None;
+        }
+        Some(item)
+    }
+
+    fn check_changed_auto_active_status(
+        changed_auto_active_status: &mut Option<rustc_span::Span>,
+        attr: &ast::MetaItem,
+        cfg_info: &mut CfgInfo,
+        tcx: TyCtxt<'_>,
+        new_value: bool,
+    ) -> bool {
+        if let Some(first_change) = changed_auto_active_status {
+            if cfg_info.auto_cfg_active != new_value {
+                tcx.sess
+                    .dcx()
+                    .struct_span_err(
+                        vec![*first_change, attr.span],
+                        "`auto_cfg` was disabled and enabled more than once on the same item",
+                    )
+                    .emit();
+                return true;
+            }
+        } else {
+            *changed_auto_active_status = Some(attr.span);
+        }
+        cfg_info.auto_cfg_active = new_value;
+        false
+    }
+
+    let mut new_show_attrs = FxHashMap::default();
+    let mut new_hide_attrs = FxHashMap::default();
+
+    let mut doc_cfg = attrs
+        .clone()
+        .filter(|attr| attr.has_name(sym::doc))
+        .flat_map(|attr| attr.meta_item_list().unwrap_or_default())
+        .filter(|attr| attr.has_name(sym::cfg))
+        .peekable();
+    // If the item uses `doc(cfg(...))`, then we ignore the other `cfg(...)` attributes.
+    if doc_cfg.peek().is_some() {
+        let sess = tcx.sess;
+        // We overwrite existing `cfg`.
+        if !cfg_info.parent_is_doc_cfg {
+            cfg_info.current_cfg = Cfg::True;
+            cfg_info.parent_is_doc_cfg = true;
+        }
+        for attr in doc_cfg {
+            if let Some(cfg_mi) =
+                attr.meta_item().and_then(|attr| rustc_expand::config::parse_cfg(attr, sess))
+            {
+                match Cfg::parse(cfg_mi) {
+                    Ok(new_cfg) => cfg_info.current_cfg &= new_cfg,
+                    Err(e) => {
+                        sess.dcx().span_err(e.span, e.msg);
+                    }
+                }
+            }
+        }
+    } else {
+        cfg_info.parent_is_doc_cfg = false;
+    }
+
+    let mut changed_auto_active_status = None;
+
+    // We get all `doc(auto_cfg)`, `cfg` and `target_feature` attributes.
+    for attr in attrs {
+        if let Some(ident) = attr.ident()
+            && ident.name == sym::doc
+            && let Some(attrs) = attr.meta_item_list()
+        {
+            for attr in attrs.iter().filter(|attr| attr.has_name(sym::auto_cfg)) {
+                let MetaItemInner::MetaItem(attr) = attr else {
+                    continue;
+                };
+                match &attr.kind {
+                    MetaItemKind::Word => {
+                        if check_changed_auto_active_status(
+                            &mut changed_auto_active_status,
+                            attr,
+                            cfg_info,
+                            tcx,
+                            true,
+                        ) {
+                            return None;
+                        }
+                    }
+                    MetaItemKind::NameValue(lit) => {
+                        if let LitKind::Bool(value) = lit.kind {
+                            if check_changed_auto_active_status(
+                                &mut changed_auto_active_status,
+                                attr,
+                                cfg_info,
+                                tcx,
+                                value,
+                            ) {
+                                return None;
+                            }
+                        }
+                    }
+                    MetaItemKind::List(sub_attrs) => {
+                        if check_changed_auto_active_status(
+                            &mut changed_auto_active_status,
+                            attr,
+                            cfg_info,
+                            tcx,
+                            true,
+                        ) {
+                            return None;
+                        }
+                        for sub_attr in sub_attrs.iter() {
+                            if let Some(ident) = sub_attr.ident()
+                                && (ident.name == sym::show || ident.name == sym::hide)
+                            {
+                                handle_auto_cfg_hide_show(
+                                    tcx,
+                                    cfg_info,
+                                    &sub_attr,
+                                    ident.name == sym::show,
+                                    &mut new_show_attrs,
+                                    &mut new_hide_attrs,
+                                );
+                            }
+                        }
+                    }
+                }
+            }
+        } else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr {
+            // Treat `#[target_feature(enable = "feat")]` attributes as if they were
+            // `#[doc(cfg(target_feature = "feat"))]` attributes as well.
+            for (feature, _) in features {
+                cfg_info.current_cfg &= Cfg::Cfg(sym::target_feature, Some(*feature));
+            }
+            continue;
+        } else if !cfg_info.parent_is_doc_cfg
+            && let Some(ident) = attr.ident()
+            && matches!(ident.name, sym::cfg | sym::cfg_trace)
+            && let Some(attr) = single(attr.meta_item_list()?)
+            && let Ok(new_cfg) = Cfg::parse(&attr)
+        {
+            cfg_info.current_cfg &= new_cfg;
+        }
+    }
+
+    // If `doc(auto_cfg)` feature is disabled and `doc(cfg())` wasn't used, there is nothing
+    // to be done here.
+    if !cfg_info.auto_cfg_active && !cfg_info.parent_is_doc_cfg {
+        None
+    } else if cfg_info.parent_is_doc_cfg {
+        if cfg_info.current_cfg == Cfg::True {
+            None
+        } else {
+            Some(Arc::new(cfg_info.current_cfg.clone()))
+        }
+    } else {
+        // If `doc(auto_cfg)` feature is enabled, we want to collect all `cfg` items, we remove the
+        // hidden ones afterward.
+        match cfg_info.current_cfg.strip_hidden(&cfg_info.hidden_cfg) {
+            None | Some(Cfg::True) => None,
+            Some(cfg) => Some(Arc::new(cfg)),
+        }
+    }
+}
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index c6339dd4755..4fd8d245089 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -58,6 +58,7 @@ use tracing::{debug, instrument};
 use utils::*;
 use {rustc_ast as ast, rustc_hir as hir};
 
+pub(crate) use self::cfg::{CfgInfo, extract_cfg_from_attrs};
 pub(crate) use self::types::*;
 pub(crate) use self::utils::{krate, register_res, synthesize_auto_trait_and_blanket_impls};
 use crate::core::DocContext;
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 6e3dfa0ac22..f3662a67bbe 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -6,8 +6,7 @@ use std::{fmt, iter};
 use arrayvec::ArrayVec;
 use itertools::Either;
 use rustc_abi::{ExternAbi, VariantIdx};
-use rustc_ast::ast::{LitKind, MetaItemInner, MetaItemKind};
-use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
+use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation};
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
@@ -922,267 +921,6 @@ pub(crate) fn hir_attr_lists<'a, I: IntoIterator<Item = &'a hir::Attribute>>(
         .flatten()
 }
 
-/// This type keeps track of (doc) cfg information as we go down the item tree.
-#[derive(Clone, Debug)]
-pub(crate) struct CfgInfo {
-    /// List of currently active `doc(auto_cfg(hide(...)))` cfgs, minus currently active
-    /// `doc(auto_cfg(show(...)))` cfgs.
-    hidden_cfg: FxHashSet<Cfg>,
-    /// Current computed `cfg`. Each time we enter a new item, this field is updated as well while
-    /// taking into account the `hidden_cfg` information.
-    current_cfg: Cfg,
-    /// Whether the `doc(auto_cfg())` feature is enabled or not at this point.
-    auto_cfg_active: bool,
-    /// If the parent item used `doc(cfg(...))`, then we don't want to overwrite `current_cfg`,
-    /// instead we will concatenate with it. However, if it's not the case, we need to overwrite
-    /// `current_cfg`.
-    parent_is_doc_cfg: bool,
-}
-
-impl Default for CfgInfo {
-    fn default() -> Self {
-        Self {
-            hidden_cfg: FxHashSet::from_iter([
-                Cfg::Cfg(sym::test, None),
-                Cfg::Cfg(sym::doc, None),
-                Cfg::Cfg(sym::doctest, None),
-            ]),
-            current_cfg: Cfg::True,
-            auto_cfg_active: true,
-            parent_is_doc_cfg: false,
-        }
-    }
-}
-
-fn show_hide_show_conflict_error(
-    tcx: TyCtxt<'_>,
-    item_span: rustc_span::Span,
-    previous: rustc_span::Span,
-) {
-    let mut diag = tcx.sess.dcx().struct_span_err(
-        item_span,
-        format!(
-            "same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item"
-        ),
-    );
-    diag.span_note(previous, "first change was here");
-    diag.emit();
-}
-
-/// This functions updates the `hidden_cfg` field of the provided `cfg_info` argument.
-///
-/// It also checks if a same `cfg` is present in both `auto_cfg(hide(...))` and
-/// `auto_cfg(show(...))` on the same item and emits an error if it's the case.
-///
-/// Because we go through a list of `cfg`s, we keep track of the `cfg`s we saw in `new_show_attrs`
-/// and in `new_hide_attrs` arguments.
-fn handle_auto_cfg_hide_show(
-    tcx: TyCtxt<'_>,
-    cfg_info: &mut CfgInfo,
-    sub_attr: &MetaItemInner,
-    is_show: bool,
-    new_show_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
-    new_hide_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
-) {
-    if let MetaItemInner::MetaItem(item) = sub_attr
-        && let MetaItemKind::List(items) = &item.kind
-    {
-        for item in items {
-            // FIXME: Report in case `Cfg::parse` reports an error?
-            if let Ok(Cfg::Cfg(key, value)) = Cfg::parse(item) {
-                if is_show {
-                    if let Some(span) = new_hide_attrs.get(&(key, value)) {
-                        show_hide_show_conflict_error(tcx, item.span(), *span);
-                    } else {
-                        new_show_attrs.insert((key, value), item.span());
-                    }
-                    cfg_info.hidden_cfg.remove(&Cfg::Cfg(key, value));
-                } else {
-                    if let Some(span) = new_show_attrs.get(&(key, value)) {
-                        show_hide_show_conflict_error(tcx, item.span(), *span);
-                    } else {
-                        new_hide_attrs.insert((key, value), item.span());
-                    }
-                    cfg_info.hidden_cfg.insert(Cfg::Cfg(key, value));
-                }
-            }
-        }
-    }
-}
-
-pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute> + Clone>(
-    attrs: I,
-    tcx: TyCtxt<'_>,
-    cfg_info: &mut CfgInfo,
-) -> Option<Arc<Cfg>> {
-    fn single<T: IntoIterator>(it: T) -> Option<T::Item> {
-        let mut iter = it.into_iter();
-        let item = iter.next()?;
-        if iter.next().is_some() {
-            return None;
-        }
-        Some(item)
-    }
-
-    fn check_changed_auto_active_status(
-        changed_auto_active_status: &mut Option<rustc_span::Span>,
-        attr: &ast::MetaItem,
-        cfg_info: &mut CfgInfo,
-        tcx: TyCtxt<'_>,
-        new_value: bool,
-    ) -> bool {
-        if let Some(first_change) = changed_auto_active_status {
-            if cfg_info.auto_cfg_active != new_value {
-                tcx.sess
-                    .dcx()
-                    .struct_span_err(
-                        vec![*first_change, attr.span],
-                        "`auto_cfg` was disabled and enabled more than once on the same item",
-                    )
-                    .emit();
-                return true;
-            }
-        } else {
-            *changed_auto_active_status = Some(attr.span);
-        }
-        cfg_info.auto_cfg_active = new_value;
-        false
-    }
-
-    let mut new_show_attrs = FxHashMap::default();
-    let mut new_hide_attrs = FxHashMap::default();
-
-    let mut doc_cfg = attrs
-        .clone()
-        .filter(|attr| attr.has_name(sym::doc))
-        .flat_map(|attr| attr.meta_item_list().unwrap_or_default())
-        .filter(|attr| attr.has_name(sym::cfg))
-        .peekable();
-    // If the item uses `doc(cfg(...))`, then we ignore the other `cfg(...)` attributes.
-    if doc_cfg.peek().is_some() {
-        let sess = tcx.sess;
-        // We overwrite existing `cfg`.
-        if !cfg_info.parent_is_doc_cfg {
-            cfg_info.current_cfg = Cfg::True;
-            cfg_info.parent_is_doc_cfg = true;
-        }
-        for attr in doc_cfg {
-            if let Some(cfg_mi) =
-                attr.meta_item().and_then(|attr| rustc_expand::config::parse_cfg(attr, sess))
-            {
-                match Cfg::parse(cfg_mi) {
-                    Ok(new_cfg) => cfg_info.current_cfg &= new_cfg,
-                    Err(e) => {
-                        sess.dcx().span_err(e.span, e.msg);
-                    }
-                }
-            }
-        }
-    } else {
-        cfg_info.parent_is_doc_cfg = false;
-    }
-
-    let mut changed_auto_active_status = None;
-
-    // We get all `doc(auto_cfg)`, `cfg` and `target_feature` attributes.
-    for attr in attrs {
-        if let Some(ident) = attr.ident()
-            && ident.name == sym::doc
-            && let Some(attrs) = attr.meta_item_list()
-        {
-            for attr in attrs.iter().filter(|attr| attr.has_name(sym::auto_cfg)) {
-                let MetaItemInner::MetaItem(attr) = attr else {
-                    continue;
-                };
-                match &attr.kind {
-                    MetaItemKind::Word => {
-                        if check_changed_auto_active_status(
-                            &mut changed_auto_active_status,
-                            attr,
-                            cfg_info,
-                            tcx,
-                            true,
-                        ) {
-                            return None;
-                        }
-                    }
-                    MetaItemKind::NameValue(lit) => {
-                        if let LitKind::Bool(value) = lit.kind {
-                            if check_changed_auto_active_status(
-                                &mut changed_auto_active_status,
-                                attr,
-                                cfg_info,
-                                tcx,
-                                value,
-                            ) {
-                                return None;
-                            }
-                        }
-                    }
-                    MetaItemKind::List(sub_attrs) => {
-                        if check_changed_auto_active_status(
-                            &mut changed_auto_active_status,
-                            attr,
-                            cfg_info,
-                            tcx,
-                            true,
-                        ) {
-                            return None;
-                        }
-                        for sub_attr in sub_attrs.iter() {
-                            if let Some(ident) = sub_attr.ident()
-                                && (ident.name == sym::show || ident.name == sym::hide)
-                            {
-                                handle_auto_cfg_hide_show(
-                                    tcx,
-                                    cfg_info,
-                                    &sub_attr,
-                                    ident.name == sym::show,
-                                    &mut new_show_attrs,
-                                    &mut new_hide_attrs,
-                                );
-                            }
-                        }
-                    }
-                }
-            }
-        } else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr {
-            // Treat `#[target_feature(enable = "feat")]` attributes as if they were
-            // `#[doc(cfg(target_feature = "feat"))]` attributes as well.
-            for (feature, _) in features {
-                cfg_info.current_cfg &= Cfg::Cfg(sym::target_feature, Some(*feature));
-            }
-            continue;
-        } else if !cfg_info.parent_is_doc_cfg
-            && let Some(ident) = attr.ident()
-            && matches!(ident.name, sym::cfg | sym::cfg_trace)
-            && let Some(attr) = single(attr.meta_item_list()?)
-            && let Ok(new_cfg) = Cfg::parse(&attr)
-        {
-            cfg_info.current_cfg &= new_cfg;
-        }
-    }
-
-    // If `doc(auto_cfg)` feature is disabled and `doc(cfg())` wasn't used, there is nothing
-    // to be done here.
-    if !cfg_info.auto_cfg_active && !cfg_info.parent_is_doc_cfg {
-        None
-    } else if cfg_info.parent_is_doc_cfg {
-        if cfg_info.current_cfg == Cfg::True {
-            None
-        } else {
-            Some(Arc::new(cfg_info.current_cfg.clone()))
-        }
-    } else {
-        // If `doc(auto_cfg)` feature is enabled, we want to collect all `cfg` items, we remove the
-        // hidden ones afterward.
-        match cfg_info.current_cfg.strip_hidden(&cfg_info.hidden_cfg) {
-            None | Some(Cfg::True) => None,
-            Some(cfg) => Some(Arc::new(cfg)),
-        }
-    }
-}
-
 pub(crate) trait NestedAttributesExt {
     /// Returns `true` if the attribute list contains a specific `word`
     fn has_word(self, word: Symbol) -> bool
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 6d959948918..65db816ad1a 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -7,6 +7,7 @@ use build_helper::git::GitConfig;
 use camino::{Utf8Path, Utf8PathBuf};
 use semver::Version;
 
+use crate::edition::Edition;
 use crate::executor::ColorConfig;
 use crate::fatal;
 use crate::util::{Utf8PathBufExt, add_dylib_path, string_enum};
@@ -612,10 +613,7 @@ pub struct Config {
     pub git_hash: bool,
 
     /// The default Rust edition.
-    ///
-    /// FIXME: perform stronger validation for this. There are editions that *definitely* exists,
-    /// but there might also be "future" edition.
-    pub edition: Option<String>,
+    pub edition: Option<Edition>,
 
     // Configuration for various run-make tests frobbing things like C compilers or querying about
     // various LLVM component information.
diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs
index 0d85e76c2cc..a79978d036c 100644
--- a/src/tools/compiletest/src/directives.rs
+++ b/src/tools/compiletest/src/directives.rs
@@ -16,10 +16,11 @@ use crate::directives::directive_names::{
     KNOWN_DIRECTIVE_NAMES, KNOWN_HTMLDOCCK_DIRECTIVE_NAMES, KNOWN_JSONDOCCK_DIRECTIVE_NAMES,
 };
 use crate::directives::needs::CachedNeedsConditions;
+use crate::edition::{Edition, parse_edition};
 use crate::errors::ErrorKind;
 use crate::executor::{CollectedTestDesc, ShouldPanic};
-use crate::help;
 use crate::util::static_regex;
+use crate::{fatal, help};
 
 pub(crate) mod auxiliary;
 mod cfg;
@@ -437,10 +438,13 @@ impl TestProps {
                         panic!("`compiler-flags` directive should be spelled `compile-flags`");
                     }
 
-                    if let Some(edition) = config.parse_edition(ln, testfile) {
+                    if let Some(range) = parse_edition_range(config, ln, testfile) {
                         // The edition is added at the start, since flags from //@compile-flags must
                         // be passed to rustc last.
-                        self.compile_flags.insert(0, format!("--edition={}", edition.trim()));
+                        self.compile_flags.insert(
+                            0,
+                            format!("--edition={}", range.edition_to_test(config.edition)),
+                        );
                         has_edition = true;
                     }
 
@@ -1124,10 +1128,6 @@ impl Config {
         }
     }
 
-    fn parse_edition(&self, line: &DirectiveLine<'_>, testfile: &Utf8Path) -> Option<String> {
-        self.parse_name_value_directive(line, "edition", testfile)
-    }
-
     fn set_name_directive(&self, line: &DirectiveLine<'_>, directive: &str, value: &mut bool) {
         // If the flag is already true, don't bother looking at the directive.
         *value = *value || self.parse_name_directive(line, directive);
@@ -1769,3 +1769,86 @@ enum IgnoreDecision {
     Continue,
     Error { message: String },
 }
+
+fn parse_edition_range(
+    config: &Config,
+    line: &DirectiveLine<'_>,
+    testfile: &Utf8Path,
+) -> Option<EditionRange> {
+    let raw = config.parse_name_value_directive(line, "edition", testfile)?;
+    let line_number = line.line_number;
+
+    // Edition range is half-open: `[lower_bound, upper_bound)`
+    if let Some((lower_bound, upper_bound)) = raw.split_once("..") {
+        Some(match (maybe_parse_edition(lower_bound), maybe_parse_edition(upper_bound)) {
+            (Some(lower_bound), Some(upper_bound)) if upper_bound <= lower_bound => {
+                fatal!(
+                    "{testfile}:{line_number}: the left side of `//@ edition` cannot be greater than or equal to the right side"
+                );
+            }
+            (Some(lower_bound), Some(upper_bound)) => {
+                EditionRange::Range { lower_bound, upper_bound }
+            }
+            (Some(lower_bound), None) => EditionRange::RangeFrom(lower_bound),
+            (None, Some(_)) => {
+                fatal!(
+                    "{testfile}:{line_number}: `..edition` is not a supported range in `//@ edition`"
+                );
+            }
+            (None, None) => {
+                fatal!("{testfile}:{line_number}: `..` is not a supported range in `//@ edition`");
+            }
+        })
+    } else {
+        match maybe_parse_edition(&raw) {
+            Some(edition) => Some(EditionRange::Exact(edition)),
+            None => {
+                fatal!("{testfile}:{line_number}: empty value for `//@ edition`");
+            }
+        }
+    }
+}
+
+fn maybe_parse_edition(mut input: &str) -> Option<Edition> {
+    input = input.trim();
+    if input.is_empty() {
+        return None;
+    }
+    Some(parse_edition(input))
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+enum EditionRange {
+    Exact(Edition),
+    RangeFrom(Edition),
+    /// Half-open range: `[lower_bound, upper_bound)`
+    Range {
+        lower_bound: Edition,
+        upper_bound: Edition,
+    },
+}
+
+impl EditionRange {
+    fn edition_to_test(&self, requested: impl Into<Option<Edition>>) -> Edition {
+        let min_edition = Edition::Year(2015);
+        let requested = requested.into().unwrap_or(min_edition);
+
+        match *self {
+            EditionRange::Exact(exact) => exact,
+            EditionRange::RangeFrom(lower_bound) => {
+                if requested >= lower_bound {
+                    requested
+                } else {
+                    lower_bound
+                }
+            }
+            EditionRange::Range { lower_bound, upper_bound } => {
+                if requested >= lower_bound && requested < upper_bound {
+                    requested
+                } else {
+                    lower_bound
+                }
+            }
+        }
+    }
+}
diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs
index 16cf76be9a5..93621192d4b 100644
--- a/src/tools/compiletest/src/directives/tests.rs
+++ b/src/tools/compiletest/src/directives/tests.rs
@@ -4,10 +4,11 @@ use camino::Utf8Path;
 use semver::Version;
 
 use super::{
-    DirectivesCache, EarlyProps, extract_llvm_version, extract_version_range, iter_directives,
-    parse_normalize_rule,
+    DirectivesCache, EarlyProps, Edition, EditionRange, extract_llvm_version,
+    extract_version_range, iter_directives, parse_normalize_rule,
 };
 use crate::common::{Config, Debugger, TestMode};
+use crate::directives::parse_edition;
 use crate::executor::{CollectedTestDesc, ShouldPanic};
 
 fn make_test_description<R: Read>(
@@ -73,6 +74,7 @@ fn test_parse_normalize_rule() {
 struct ConfigBuilder {
     mode: Option<String>,
     channel: Option<String>,
+    edition: Option<Edition>,
     host: Option<String>,
     target: Option<String>,
     stage: Option<u32>,
@@ -96,6 +98,11 @@ impl ConfigBuilder {
         self
     }
 
+    fn edition(&mut self, e: Edition) -> &mut Self {
+        self.edition = Some(e);
+        self
+    }
+
     fn host(&mut self, s: &str) -> &mut Self {
         self.host = Some(s.to_owned());
         self
@@ -183,6 +190,10 @@ impl ConfigBuilder {
         ];
         let mut args: Vec<String> = args.iter().map(ToString::to_string).collect();
 
+        if let Some(edition) = &self.edition {
+            args.push(format!("--edition={edition}"));
+        }
+
         if let Some(ref llvm_version) = self.llvm_version {
             args.push("--llvm-version".to_owned());
             args.push(llvm_version.clone());
@@ -941,3 +952,130 @@ fn test_needs_target_std() {
     let config = cfg().target("x86_64-unknown-linux-gnu").build();
     assert!(!check_ignore(&config, "//@ needs-target-std"));
 }
+
+fn parse_edition_range(line: &str) -> Option<EditionRange> {
+    let config = cfg().build();
+    let line = super::DirectiveLine { line_number: 0, revision: None, raw_directive: line };
+
+    super::parse_edition_range(&config, &line, "tmp.rs".into())
+}
+
+#[test]
+fn test_parse_edition_range() {
+    assert_eq!(None, parse_edition_range("hello-world"));
+    assert_eq!(None, parse_edition_range("edition"));
+
+    assert_eq!(Some(EditionRange::Exact(2018.into())), parse_edition_range("edition: 2018"));
+    assert_eq!(Some(EditionRange::Exact(2021.into())), parse_edition_range("edition:2021"));
+    assert_eq!(Some(EditionRange::Exact(2024.into())), parse_edition_range("edition: 2024 "));
+    assert_eq!(Some(EditionRange::Exact(Edition::Future)), parse_edition_range("edition: future"));
+
+    assert_eq!(Some(EditionRange::RangeFrom(2018.into())), parse_edition_range("edition: 2018.."));
+    assert_eq!(Some(EditionRange::RangeFrom(2021.into())), parse_edition_range("edition:2021 .."));
+    assert_eq!(
+        Some(EditionRange::RangeFrom(2024.into())),
+        parse_edition_range("edition: 2024 .. ")
+    );
+    assert_eq!(
+        Some(EditionRange::RangeFrom(Edition::Future)),
+        parse_edition_range("edition: future.. ")
+    );
+
+    assert_eq!(
+        Some(EditionRange::Range { lower_bound: 2018.into(), upper_bound: 2024.into() }),
+        parse_edition_range("edition: 2018..2024")
+    );
+    assert_eq!(
+        Some(EditionRange::Range { lower_bound: 2015.into(), upper_bound: 2021.into() }),
+        parse_edition_range("edition:2015 .. 2021 ")
+    );
+    assert_eq!(
+        Some(EditionRange::Range { lower_bound: 2021.into(), upper_bound: 2027.into() }),
+        parse_edition_range("edition: 2021 .. 2027 ")
+    );
+    assert_eq!(
+        Some(EditionRange::Range { lower_bound: 2021.into(), upper_bound: Edition::Future }),
+        parse_edition_range("edition: 2021..future")
+    );
+}
+
+#[test]
+#[should_panic]
+fn test_parse_edition_range_empty() {
+    parse_edition_range("edition:");
+}
+
+#[test]
+#[should_panic]
+fn test_parse_edition_range_invalid_edition() {
+    parse_edition_range("edition: hello");
+}
+
+#[test]
+#[should_panic]
+fn test_parse_edition_range_double_dots() {
+    parse_edition_range("edition: ..");
+}
+
+#[test]
+#[should_panic]
+fn test_parse_edition_range_inverted_range() {
+    parse_edition_range("edition: 2021..2015");
+}
+
+#[test]
+#[should_panic]
+fn test_parse_edition_range_inverted_range_future() {
+    parse_edition_range("edition: future..2015");
+}
+
+#[test]
+#[should_panic]
+fn test_parse_edition_range_empty_range() {
+    parse_edition_range("edition: 2021..2021");
+}
+
+#[track_caller]
+fn assert_edition_to_test(
+    expected: impl Into<Edition>,
+    range: EditionRange,
+    default: Option<Edition>,
+) {
+    let mut cfg = cfg();
+    if let Some(default) = default {
+        cfg.edition(default);
+    }
+    assert_eq!(expected.into(), range.edition_to_test(cfg.build().edition));
+}
+
+#[test]
+fn test_edition_range_edition_to_test() {
+    let e2015 = parse_edition("2015");
+    let e2018 = parse_edition("2018");
+    let e2021 = parse_edition("2021");
+    let e2024 = parse_edition("2024");
+    let efuture = parse_edition("future");
+
+    let exact = EditionRange::Exact(2021.into());
+    assert_edition_to_test(2021, exact, None);
+    assert_edition_to_test(2021, exact, Some(e2018));
+    assert_edition_to_test(2021, exact, Some(efuture));
+
+    assert_edition_to_test(Edition::Future, EditionRange::Exact(Edition::Future), None);
+
+    let greater_equal_than = EditionRange::RangeFrom(2021.into());
+    assert_edition_to_test(2021, greater_equal_than, None);
+    assert_edition_to_test(2021, greater_equal_than, Some(e2015));
+    assert_edition_to_test(2021, greater_equal_than, Some(e2018));
+    assert_edition_to_test(2021, greater_equal_than, Some(e2021));
+    assert_edition_to_test(2024, greater_equal_than, Some(e2024));
+    assert_edition_to_test(Edition::Future, greater_equal_than, Some(efuture));
+
+    let range = EditionRange::Range { lower_bound: 2018.into(), upper_bound: 2024.into() };
+    assert_edition_to_test(2018, range, None);
+    assert_edition_to_test(2018, range, Some(e2015));
+    assert_edition_to_test(2018, range, Some(e2018));
+    assert_edition_to_test(2021, range, Some(e2021));
+    assert_edition_to_test(2018, range, Some(e2024));
+    assert_edition_to_test(2018, range, Some(efuture));
+}
diff --git a/src/tools/compiletest/src/edition.rs b/src/tools/compiletest/src/edition.rs
new file mode 100644
index 00000000000..36550cf5b2b
--- /dev/null
+++ b/src/tools/compiletest/src/edition.rs
@@ -0,0 +1,35 @@
+use crate::fatal;
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub enum Edition {
+    // Note that the ordering here is load-bearing, as we want the future edition to be greater than
+    // any year-based edition.
+    Year(u32),
+    Future,
+}
+
+impl std::fmt::Display for Edition {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Edition::Year(year) => write!(f, "{year}"),
+            Edition::Future => f.write_str("future"),
+        }
+    }
+}
+
+impl From<u32> for Edition {
+    fn from(value: u32) -> Self {
+        Edition::Year(value)
+    }
+}
+
+pub fn parse_edition(mut input: &str) -> Edition {
+    input = input.trim();
+    if input == "future" {
+        Edition::Future
+    } else {
+        Edition::Year(input.parse().unwrap_or_else(|_| {
+            fatal!("`{input}` doesn't look like an edition");
+        }))
+    }
+}
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index 15e31dadf97..2d759279f34 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -7,6 +7,7 @@ pub mod common;
 mod debuggers;
 pub mod diagnostics;
 pub mod directives;
+pub mod edition;
 pub mod errors;
 mod executor;
 mod json;
@@ -39,6 +40,7 @@ use crate::common::{
     expected_output_path, output_base_dir, output_relative_path,
 };
 use crate::directives::DirectivesCache;
+use crate::edition::parse_edition;
 use crate::executor::{CollectedTest, ColorConfig};
 
 /// Creates the `Config` instance for this invocation of compiletest.
@@ -449,7 +451,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
         has_enzyme,
         channel: matches.opt_str("channel").unwrap(),
         git_hash: matches.opt_present("git-hash"),
-        edition: matches.opt_str("edition"),
+        edition: matches.opt_str("edition").as_deref().map(parse_edition),
 
         cc: matches.opt_str("cc").unwrap(),
         cxx: matches.opt_str("cxx").unwrap(),
diff --git a/triagebot.toml b/triagebot.toml
index a04f8d28072..79b5c2d1b72 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -61,10 +61,6 @@ required-issue-label = "regression-from-stable-to-beta"
 # if the above conditions matches, the PR will receive these labels
 add-labels = ["beta-nominated"]
 
-[backport.t-compiler-stable-backport]
-required-pr-labels = ["T-compiler"]
-required-issue-label = "regression-from-stable-to-stable"
-add-labels = ["stable-nominated"]
 
 # ------------------------------------------------------------------------------
 # Ping groups