Repository: rustwasm/wasm-bindgen (rust)

Issue:
`wasm-bindgen` no longer works with feature resolver version 1
```console
$ cargo +stable new repro --edition=2018
$ cd repro
$ cargo +stable add wasm-bindgen@=0.2.96   # =0.2.95 works
$ cargo +stable check
...
error[E0554]: `#![feature]` may not be used on the stable release channel
 --> ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasm-bindgen-macro-0.2.96/src/lib.rs:4:5
  |
4 |     feature(allow_internal_unstable),
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For more information about this error, try `rustc --explain E0554`.
error: could not compile `wasm-bindgen-macro` (lib) due to 1 previous error

$ rustc +stable --version --verbose
rustc 1.83.0 (90b35a623 2024-11-26)
binary: rustc
commit-hash: 90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf
commit-date: 2024-11-26
host: x86_64-unknown-linux-gnu
release: 1.83.0
LLVM version: 19.1.1
```

Pull request:
Re-establish feature resolver version 1 compatibility
These changes re-establish feature resolver version 1 compatibility.

Instead of using crate features we use macros defined in `wasm_bindgen::__rt`. This was possible because we moved `allow_internal_unstable` from the proc-macro to these new macros.

Thanks @taiki-e for proposing and finding this solution.

Fixes #4304.
Replaces #4306.
Reverts #4284.
Reverts #4307.

Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index bf6acf42..5263ee20 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -68,12 +68,12 @@ jobs:
     - run: cargo clippy --no-deps --all-features -p example-tests -- -D warnings
     - run: cargo clippy --no-deps --all-features -p wasm-bindgen-externref-xform -- -D warnings
     - run: cargo clippy --no-deps --all-features --target wasm32-unknown-unknown -p wasm-bindgen-futures -- -D warnings
-    - run: cargo clippy --no-deps --features spans,strict-macro -p wasm-bindgen-macro -- -D warnings
-    - run: cargo clippy --no-deps --features extra-traits,spans,strict-macro -p wasm-bindgen-macro-support -- -D warnings
+    - run: cargo clippy --no-deps --all-features -p wasm-bindgen-macro -- -D warnings
+    - run: cargo clippy --no-deps --all-features -p wasm-bindgen-macro-support -- -D warnings
     - run: cargo clippy --no-deps --all-features -p wasm-bindgen-multi-value-xform -- -D warnings
     - run: cargo clippy --no-deps --all-features -p wasm-bindgen-shared -- -D warnings
     - run: cargo clippy --no-deps --all-features --target wasm32-unknown-unknown -p wasm-bindgen-test -- -D warnings
-    - run: cargo clippy --no-deps -p wasm-bindgen-test-macro -- -D warnings
+    - run: cargo clippy --no-deps --all-features -p wasm-bindgen-test-macro -- -D warnings
     - run: cargo clippy --no-deps --all-features -p wasm-bindgen-threads-xform -- -D warnings
     - run: cargo clippy --no-deps --all-features --target wasm32-unknown-unknown -p typescript-tests -- -D warnings
     - run: cargo clippy --no-deps --all-features -p wasm-bindgen-wasm-conventions -- -D warnings
@@ -540,7 +540,7 @@ jobs:
         path: docs.tar.gz
 
   msrv-resolver:
-    name: Check for feature resolver v1 error message
+    name: Check feature resolver version 1 compatibility
     runs-on: ubuntu-latest
     strategy:
       fail-fast: false
@@ -565,7 +565,7 @@ jobs:
         cargo update -p bumpalo --precise 3.12.0
         cargo update -p log --precise 0.4.18
         cargo update -p scoped-tls --precise 1.0.0
-    - run: diff <(cargo build --target ${{ matrix.target }} ${{ matrix.features }} --message-format json | jq -r "select(.reason == \"compiler-message\") | .message.message") error-${{ matrix.rust }}.txt
+    - run: cargo build --target ${{ matrix.target }} ${{ matrix.features }}
 
   msrv-lib:
     name: Check MSRV for libraries
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c2e4d998..9a10caba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,9 +5,6 @@
 
 ### Added
 
-* Add clear error message to communicate new feature resolver version requirements.
-  [#4312](https://github.com/rustwasm/wasm-bindgen/pull/4312)
-
 * Add support for multi-threading in Node.js.
   [#4318](https://github.com/rustwasm/wasm-bindgen/pull/4318)
 
@@ -27,6 +24,9 @@
 * Fix macro-hygiene for calls to `std::thread_local!`.
   [#4315](https://github.com/rustwasm/wasm-bindgen/pull/4315)
 
+* Fix feature resolver version 1 compatibility.
+  [#4327](https://github.com/rustwasm/wasm-bindgen/pull/4327)
+
 --------------------------------------------------------------------------------
 
 ## [0.2.97](https://github.com/rustwasm/wasm-bindgen/compare/0.2.96...0.2.97)
diff --git a/Cargo.toml b/Cargo.toml
index d0aaa348..a9597a0f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -47,21 +47,6 @@ serde = { version = "1.0", optional = true }
 serde_json = { version = "1.0", optional = true }
 wasm-bindgen-macro = { path = "crates/macro", version = "=0.2.97", default-features = false }
 
-[target.'cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"), target_feature = "atomics"))'.dependencies]
-wasm-bindgen-macro = { path = "crates/macro", version = "=0.2.97", default-features = false, features = [
-  "atomics",
-] }
-
-[target.'cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"), wasm_bindgen_unstable_test_coverage))'.dependencies]
-wasm-bindgen-macro = { path = "crates/macro", version = "=0.2.97", default-features = false, features = [
-  "coverage",
-] }
-
-[target.'cfg(__wasm_bindgen_resolver_1)'.dependencies]
-wasm-bindgen-macro = { path = "crates/macro", version = "=0.2.97", default-features = false, features = [
-  "xxx_resolver_1",
-] }
-
 [dev-dependencies]
 wasm-bindgen-test = { path = 'crates/test' }
 
@@ -90,7 +75,7 @@ too_many_arguments = "allow"
 type_complexity = "allow"
 
 [workspace]
-exclude = ["crates/msrv"]
+exclude = ["crates/msrv/resolver", "crates/msrv/lib", "crates/msrv/cli"]
 members = [
   "benchmarks",
   "crates/cli",
diff --git a/README.md b/README.md
index 05266a62..d8e07bc0 100644
--- a/README.md
+++ b/README.md
@@ -114,8 +114,6 @@ Libraries that are released on [crates.io](https://crates.io) have a MSRV of v1.
 
 CLI tools and their corresponding support libraries have a MSRV of v1.76. Changes to the MSRV will be accompanied by a patch version bump.
 
-[Feature resolver version 2](https://doc.rust-lang.org/1.83.0/cargo/reference/resolver.html#feature-resolver-version-2) is required.
-
 ## License
 
 This project is licensed under either of
diff --git a/crates/backend/Cargo.toml b/crates/backend/Cargo.toml
index 70323125..3e0603b9 100644
--- a/crates/backend/Cargo.toml
+++ b/crates/backend/Cargo.toml
@@ -14,18 +14,14 @@ rust-version = "1.57"
 version = "0.2.97"
 
 [features]
-atomics = []
-coverage = []
 default = ["std"]
 extra-traits = ["syn/extra-traits"]
 spans = []
 std = []
-xxx_resolver_1 = []
 
 [dependencies]
 bumpalo = "3.0.0"
 log = "0.4"
-once_cell = "1.12"
 proc-macro2 = "1.0"
 quote = '1.0'
 syn = { version = '2.0', features = ['full'] }
diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs
index a715ec75..23599fc8 100644
--- a/crates/backend/src/codegen.rs
+++ b/crates/backend/src/codegen.rs
@@ -2,13 +2,12 @@ use crate::ast;
 use crate::encode;
 use crate::encode::EncodeChunk;
 use crate::Diagnostic;
-use once_cell::sync::Lazy;
 use proc_macro2::{Ident, Span, TokenStream};
 use quote::format_ident;
 use quote::quote_spanned;
 use quote::{quote, ToTokens};
+use std::cell::RefCell;
 use std::collections::{HashMap, HashSet};
-use std::sync::Mutex;
 use syn::parse_quote;
 use syn::spanned::Spanned;
 use wasm_bindgen_shared as shared;
@@ -224,7 +223,6 @@ impl ToTokens for ast::Struct {
         let free_fn = Ident::new(&shared::free_function(&name_str), Span::call_site());
         let unwrap_fn = Ident::new(&shared::unwrap_function(&name_str), Span::call_site());
         let wasm_bindgen = &self.wasm_bindgen;
-        let maybe_no_coverage = coverage();
         (quote! {
             #[automatically_derived]
             impl #wasm_bindgen::describe::WasmDescribe for #name {
@@ -296,11 +294,11 @@ impl ToTokens for ast::Struct {
             #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))]
             #[automatically_derived]
             const _: () = {
+                #wasm_bindgen::__wbindgen_coverage! {
                 #[no_mangle]
                 #[doc(hidden)]
                 // `allow_delayed` is whether it's ok to not actually free the `ptr` immediately
                 // if it's still borrowed.
-                #maybe_no_coverage
                 pub unsafe extern "C" fn #free_fn(ptr: u32, allow_delayed: u32) {
                     use #wasm_bindgen::__rt::alloc::rc::Rc;
 
@@ -315,6 +313,7 @@ impl ToTokens for ast::Struct {
                         let _ = <#name as #wasm_bindgen::convert::FromWasmAbi>::from_abi(ptr);
                     }
                 }
+                }
             };
 
             #[automatically_derived]
@@ -476,7 +475,6 @@ impl ToTokens for ast::StructField {
             quote! { assert_copy::<#ty>() }
         };
         let maybe_assert_copy = respan(maybe_assert_copy, ty);
-        let maybe_no_coverage = coverage();
 
         // Split this out so that it isn't affected by `quote_spanned!`.
         //
@@ -495,9 +493,9 @@ impl ToTokens for ast::StructField {
         (quote! {
             #[automatically_derived]
             const _: () = {
+                #wasm_bindgen::__wbindgen_coverage! {
                 #[cfg_attr(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")), no_mangle)]
                 #[doc(hidden)]
-                #maybe_no_coverage
                 pub unsafe extern "C" fn #getter(js: u32)
                     -> #wasm_bindgen::convert::WasmRet<<#ty as #wasm_bindgen::convert::IntoWasmAbi>::Abi>
                 {
@@ -512,6 +510,7 @@ impl ToTokens for ast::StructField {
                     let val = #val;
                     <#ty as IntoWasmAbi>::into_abi(val).into()
                 }
+                }
             };
         })
         .to_tokens(tokens);
@@ -537,9 +536,9 @@ impl ToTokens for ast::StructField {
             #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))]
             #[automatically_derived]
             const _: () = {
+                #wasm_bindgen::__wbindgen_coverage! {
                 #[no_mangle]
                 #[doc(hidden)]
-                #maybe_no_coverage
                 pub unsafe extern "C" fn #setter(
                     js: u32,
                     #(#args,)*
@@ -553,6 +552,7 @@ impl ToTokens for ast::StructField {
                     let val = <#ty as FromWasmAbi>::from_abi(val);
                     (*js).borrow_mut().#rust_name = val;
                 }
+                }
             };
         })
         .to_tokens(tokens);
@@ -788,23 +788,22 @@ impl TryToTokens for ast::Export {
             quote! {}
         };
 
-        let maybe_no_coverage = coverage();
-
         (quote! {
             #[automatically_derived]
             const _: () = {
+                #wasm_bindgen::__wbindgen_coverage! {
                 #(#attrs)*
                 #[cfg_attr(
                     all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")),
                     export_name = #export_name,
                 )]
-                #maybe_no_coverage
                 pub unsafe extern "C" fn #generated_name(#(#args),*) -> #wasm_bindgen::convert::WasmRet<#projection::Abi> {
                     #start_check
 
                     let #ret = #call;
                     #convert_ret
                 }
+                }
             };
         })
         .to_tokens(into);
@@ -1159,8 +1158,6 @@ impl ToTokens for ast::StringEnum {
         let hole = variant_count + 1;
         let attrs = &self.rust_attrs;
 
-        let maybe_no_coverage = coverage();
-
         let invalid_to_str_msg = format!(
             "Converting an invalid string enum ({}) back to a string is currently not supported",
             enum_name
@@ -1247,7 +1244,6 @@ impl ToTokens for ast::StringEnum {
 
             #[automatically_derived]
             impl #wasm_bindgen::describe::WasmDescribe for #enum_name {
-                #maybe_no_coverage
                 fn describe() {
                     use #wasm_bindgen::describe::*;
                     inform(STRING_ENUM);
@@ -1546,7 +1542,6 @@ impl ToTokens for ast::Enum {
         } else {
             quote! { u32 }
         };
-        let maybe_no_coverage = coverage();
         let cast_clauses = self.variants.iter().map(|variant| {
             let variant_name = &variant.name;
             quote! {
@@ -1594,7 +1589,6 @@ impl ToTokens for ast::Enum {
 
             #[automatically_derived]
             impl #wasm_bindgen::describe::WasmDescribe for #enum_name {
-                #maybe_no_coverage
                 fn describe() {
                     use #wasm_bindgen::describe::*;
                     inform(ENUM);
@@ -1778,20 +1772,9 @@ fn thread_local_import(
                     __inner: &_VAL,
                 }
             };
-            #[cfg(all(not(feature = "std"), not(feature = "atomics")))]
-            let inner = quote! {
-                static _VAL: #wasm_bindgen::__rt::LazyCell<#actual_ty> = #wasm_bindgen::__rt::LazyCell::new(init);
-                #wasm_bindgen::JsThreadLocal {
-                    __inner: &_VAL,
-                }
-            };
-            #[cfg(all(not(feature = "std"), feature = "atomics"))]
+            #[cfg(not(feature = "std"))]
             let inner = quote! {
-                #[thread_local]
-                static _VAL: #wasm_bindgen::__rt::LazyCell<#actual_ty> = #wasm_bindgen::__rt::LazyCell::new(init);
-                #wasm_bindgen::JsThreadLocal {
-                    __inner: || unsafe {  #wasm_bindgen::__rt::LazyCell::force(&_VAL) as *const #actual_ty },
-                }
+                #wasm_bindgen::__wbindgen_thread_local!(#wasm_bindgen, #actual_ty)
             };
 
             quote! {
@@ -1847,20 +1830,16 @@ impl<T: ToTokens> ToTokens for Descriptor<'_, T> {
         // It's up to the descriptors themselves to ensure they have unique
         // names for unique items imported, currently done via `ShortHash` and
         // hashing appropriate data into the symbol name.
-        static DESCRIPTORS_EMITTED: Lazy<Mutex<HashSet<String>>> = Lazy::new(Default::default);
+        thread_local! {
+            static DESCRIPTORS_EMITTED: RefCell<HashSet<String>> = RefCell::default();
+        }
 
         let ident = self.ident;
 
-        if !DESCRIPTORS_EMITTED
-            .lock()
-            .unwrap()
-            .insert(ident.to_string())
-        {
+        if !DESCRIPTORS_EMITTED.with(|list| list.borrow_mut().insert(ident.to_string())) {
             return;
         }
 
-        let maybe_no_coverage = coverage();
-
         let name = Ident::new(&format!("__wbindgen_describe_{}", ident), ident.span());
         let inner = &self.inner;
         let attrs = &self.attrs;
@@ -1869,16 +1848,17 @@ impl<T: ToTokens> ToTokens for Descriptor<'_, T> {
             #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))]
             #[automatically_derived]
             const _: () = {
+                #wasm_bindgen::__wbindgen_coverage! {
                 #(#attrs)*
                 #[no_mangle]
                 #[doc(hidden)]
-                #maybe_no_coverage
                 pub extern "C" fn #name() {
                     use #wasm_bindgen::describe::*;
                     // See definition of `link_mem_intrinsics` for what this is doing
                     #wasm_bindgen::__rt::link_mem_intrinsics();
                     #inner
                 }
+                }
             };
         })
         .to_tokens(tokens);
@@ -1963,10 +1943,3 @@ fn respan(input: TokenStream, span: &dyn ToTokens) -> TokenStream {
     }
     new_tokens.into_iter().collect()
 }
-
-fn coverage() -> Option<TokenStream> {
-    #[cfg(all(not(feature = "xxx_resolver_1"), feature = "coverage"))]
-    return Some(quote! { #[coverage(off)] });
-    #[cfg(any(feature = "xxx_resolver_1", not(feature = "coverage")))]
-    None
-}
diff --git a/crates/macro-support/Cargo.toml b/crates/macro-support/Cargo.toml
index ed92337b..41c28fc1 100644
--- a/crates/macro-support/Cargo.toml
+++ b/crates/macro-support/Cargo.toml
@@ -14,14 +14,11 @@ rust-version = "1.57"
 version = "0.2.97"
 
 [features]
-atomics = ["wasm-bindgen-backend/atomics"]
-coverage = ["wasm-bindgen-backend/coverage"]
 default = ["std"]
 extra-traits = ["syn/extra-traits"]
 spans = ["wasm-bindgen-backend/spans"]
 std = ["wasm-bindgen-backend/std"]
 strict-macro = []
-xxx_resolver_1 = ["wasm-bindgen-backend/xxx_resolver_1"]
 
 [dependencies]
 proc-macro2 = "1.0"
diff --git a/crates/macro/Cargo.toml b/crates/macro/Cargo.toml
index ebb4377f..e6a5f56d 100644
--- a/crates/macro/Cargo.toml
+++ b/crates/macro/Cargo.toml
@@ -17,14 +17,11 @@ version = "0.2.97"
 proc-macro = true
 
 [features]
-atomics = ["wasm-bindgen-macro-support/atomics"]
-coverage = ["wasm-bindgen-macro-support/coverage"]
 default = ["std"]
 spans = ["wasm-bindgen-macro-support/spans"]
 std = ["wasm-bindgen-macro-support/std"]
 strict-macro = ["wasm-bindgen-macro-support/strict-macro"]
 xxx_debug_only_print_generated_code = []
-xxx_resolver_1 = ["wasm-bindgen-macro-support/xxx_resolver_1"]
 
 [dependencies]
 quote = "1.0"
diff --git a/crates/macro/src/lib.rs b/crates/macro/src/lib.rs
index 5ae63303..aa4ab3f0 100644
--- a/crates/macro/src/lib.rs
+++ b/crates/macro/src/lib.rs
@@ -1,37 +1,11 @@
 #![doc(html_root_url = "https://docs.rs/wasm-bindgen-macro/0.2")]
-#![cfg_attr(
-    all(
-        not(feature = "xxx_resolver_1"),
-        any(feature = "coverage", all(not(feature = "std"), feature = "atomics"))
-    ),
-    feature(allow_internal_unstable),
-    allow(internal_features)
-)]
 
 extern crate proc_macro;
 
 use proc_macro::TokenStream;
 use quote::quote;
 
-#[cfg(feature = "xxx_resolver_1")]
-compile_error!("Feature resolver version 2 or up is required.\n\
-                Please add `resolver = \"2\"` to your `Cargo.toml`.\n\
-                \n\
-                See <https://doc.rust-lang.org/1.83.0/cargo/reference/resolver.html#feature-resolver-version-2>.");
-
 #[proc_macro_attribute]
-#[cfg_attr(
-    all(not(feature = "xxx_resolver_1"), feature = "coverage"),
-    allow_internal_unstable(coverage_attribute)
-)]
-#[cfg_attr(
-    all(
-        not(feature = "xxx_resolver_1"),
-        not(feature = "std"),
-        feature = "atomics"
-    ),
-    allow_internal_unstable(thread_local)
-)]
 pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream {
     match wasm_bindgen_macro_support::expand(attr.into(), input.into()) {
         Ok(tokens) => {
@@ -58,10 +32,6 @@ pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream {
 /// let worker = Worker::new(&wasm_bindgen::link_to!(module = "/src/worker.js"));
 /// ```
 #[proc_macro]
-#[cfg_attr(
-    all(not(feature = "xxx_resolver_1"), feature = "coverage"),
-    allow_internal_unstable(coverage_attribute)
-)]
 pub fn link_to(input: TokenStream) -> TokenStream {
     match wasm_bindgen_macro_support::expand_link_to(input.into()) {
         Ok(tokens) => {
@@ -78,10 +48,6 @@ pub fn link_to(input: TokenStream) -> TokenStream {
 }
 
 #[proc_macro_attribute]
-#[cfg_attr(
-    all(not(feature = "xxx_resolver_1"), feature = "coverage"),
-    allow_internal_unstable(coverage_attribute)
-)]
 pub fn __wasm_bindgen_class_marker(attr: TokenStream, input: TokenStream) -> TokenStream {
     match wasm_bindgen_macro_support::expand_class_marker(attr.into(), input.into()) {
         Ok(tokens) => {
diff --git a/crates/msrv/resolver/Cargo.toml b/crates/msrv/resolver/Cargo.toml
index 0f44e15a..6c7eeffb 100644
--- a/crates/msrv/resolver/Cargo.toml
+++ b/crates/msrv/resolver/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 edition = "2021"
-name = "msrv-library-test"
+name = "msrv-resolver-test"
 publish = false
 resolver = "1"
 version = "0.0.0"
diff --git a/crates/msrv/resolver/error-1.57.txt b/crates/msrv/resolver/error-1.57.txt
deleted file mode 100644
index 5a255fbc..00000000
--- a/crates/msrv/resolver/error-1.57.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-Feature resolver version 2 or up is required.
-Please add `resolver = "2"` to your `Cargo.toml`.
-
-See <https://doc.rust-lang.org/1.83.0/cargo/reference/resolver.html#feature-resolver-version-2>.
-aborting due to previous error
diff --git a/crates/msrv/resolver/error-stable.txt b/crates/msrv/resolver/error-stable.txt
deleted file mode 100644
index 5dd74366..00000000
--- a/crates/msrv/resolver/error-stable.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Feature resolver version 2 or up is required.
-Please add `resolver = "2"` to your `Cargo.toml`.
-
-See <https://doc.rust-lang.org/1.83.0/cargo/reference/resolver.html#feature-resolver-version-2>.
diff --git a/src/lib.rs b/src/lib.rs
index 6e17e245..73a61477 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -6,10 +6,15 @@
 //! interface.
 
 #![no_std]
-#![cfg_attr(wasm_bindgen_unstable_test_coverage, feature(coverage_attribute))]
+#![cfg_attr(
+    wasm_bindgen_unstable_test_coverage,
+    feature(coverage_attribute, allow_internal_unstable),
+    allow(internal_features)
+)]
 #![cfg_attr(
     all(not(feature = "std"), target_feature = "atomics"),
-    feature(thread_local)
+    feature(thread_local, allow_internal_unstable),
+    allow(internal_features)
 )]
 #![allow(coherence_leak_check)]
 #![doc(html_root_url = "https://docs.rs/wasm-bindgen/0.2")]
@@ -1708,6 +1713,54 @@ pub mod __rt {
         }
     }
 
+    #[macro_export]
+    #[doc(hidden)]
+    #[cfg(all(not(feature = "std"), not(target_feature = "atomics")))]
+    macro_rules! __wbindgen_thread_local {
+        ($wasm_bindgen:tt, $actual_ty:ty) => {{
+            static _VAL: $wasm_bindgen::__rt::LazyCell<$actual_ty> =
+                $wasm_bindgen::__rt::LazyCell::new(init);
+            $wasm_bindgen::JsThreadLocal { __inner: &_VAL }
+        }};
+    }
+
+    #[macro_export]
+    #[doc(hidden)]
+    #[cfg(all(not(feature = "std"), target_feature = "atomics"))]
+    #[allow_internal_unstable(thread_local)]
+    macro_rules! __wbindgen_thread_local {
+        ($wasm_bindgen:tt, $actual_ty:ty) => {{
+            #[thread_local]
+            static _VAL: $wasm_bindgen::__rt::LazyCell<$actual_ty> =
+                $wasm_bindgen::__rt::LazyCell::new(init);
+            $wasm_bindgen::JsThreadLocal {
+                __inner: || unsafe {
+                    $wasm_bindgen::__rt::LazyCell::force(&_VAL) as *const $actual_ty
+                },
+            }
+        }};
+    }
+
+    #[macro_export]
+    #[doc(hidden)]
+    #[cfg(not(wasm_bindgen_unstable_test_coverage))]
+    macro_rules! __wbindgen_coverage {
+        ($item:item) => {
+            $item
+        };
+    }
+
+    #[macro_export]
+    #[doc(hidden)]
+    #[cfg(wasm_bindgen_unstable_test_coverage)]
+    #[allow_internal_unstable(coverage_attribute)]
+    macro_rules! __wbindgen_coverage {
+        ($item:item) => {
+            #[coverage(off)]
+            $item
+        };
+    }
+
     #[inline]
     pub fn assert_not_null<T>(s: *mut T) {
         if s.is_null() {


Test patch:
diff --git a/crates/macro/ui-tests/wasm-bindgen.stderr b/crates/macro/ui-tests/wasm-bindgen.stderr
index 2ec13836..2dfacc7b 100644
--- a/crates/macro/ui-tests/wasm-bindgen.stderr
+++ b/crates/macro/ui-tests/wasm-bindgen.stderr
@@ -1,16 +1,10 @@
-error[E0433]: failed to resolve: could not find `convert` in `test`
+error[E0433]: failed to resolve: could not find `__wbindgen_coverage` in `test`
   --> ui-tests/wasm-bindgen.rs:37:1
    |
 37 | #[wasm_bindgen(wasm_bindgen = test)]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not find `convert` in `test`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not find `__wbindgen_coverage` in `test`
    |
    = note: this error originates in the attribute macro `wasm_bindgen` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: consider importing one of these items
-   |
-3  + use crate::test::test::convert;
-   |
-3  + use wasm_bindgen::convert;
-   |
 
 error[E0425]: cannot find function `future_to_promise` in module `test`
   --> ui-tests/wasm-bindgen.rs:40:1
diff --git a/crates/test-macro/Cargo.toml b/crates/test-macro/Cargo.toml
index cfbfc747..4997340e 100644
--- a/crates/test-macro/Cargo.toml
+++ b/crates/test-macro/Cargo.toml
@@ -12,10 +12,6 @@ version = "0.3.47"
 [lib]
 proc-macro = true
 
-[features]
-coverage = []
-xxx_resolver_1 = []
-
 [dependencies]
 proc-macro2 = "1.0"
 quote = "1.0"
diff --git a/crates/test-macro/src/lib.rs b/crates/test-macro/src/lib.rs
index 0916132d..510944cc 100644
--- a/crates/test-macro/src/lib.rs
+++ b/crates/test-macro/src/lib.rs
@@ -1,12 +1,6 @@
 //! See the README for `wasm-bindgen-test` for a bit more info about what's
 //! going on here.
 
-#![cfg_attr(
-    all(not(feature = "xxx_resolver_1"), feature = "coverage"),
-    feature(allow_internal_unstable),
-    allow(internal_features)
-)]
-
 extern crate proc_macro;
 
 use proc_macro2::*;
@@ -18,10 +12,6 @@ use std::sync::atomic::*;
 static CNT: AtomicUsize = AtomicUsize::new(0);
 
 #[proc_macro_attribute]
-#[cfg_attr(
-    all(not(feature = "xxx_resolver_1"), feature = "coverage"),
-    allow_internal_unstable(coverage_attribute)
-)]
 pub fn wasm_bindgen_test(
     attr: proc_macro::TokenStream,
     body: proc_macro::TokenStream,
@@ -109,21 +99,17 @@ pub fn wasm_bindgen_test(
     // main test harness. This is the entry point for all tests.
     let name = format_ident!("__wbgt_{}_{}", ident, CNT.fetch_add(1, Ordering::SeqCst));
     let wasm_bindgen_path = attributes.wasm_bindgen_path;
-    let coverage = if cfg!(feature = "coverage") {
-        Some(quote! { #[coverage(off)] })
-    } else {
-        None
-    };
     tokens.extend(
         quote! {
             const _: () = {
+                #wasm_bindgen_path::__rt::wasm_bindgen::__wbindgen_coverage! {
                 #[no_mangle]
                 #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))]
-                #coverage
                 pub extern "C" fn #name(cx: &#wasm_bindgen_path::__rt::Context) {
                     let test_name = ::core::concat!(::core::module_path!(), "::", ::core::stringify!(#ident));
                     #test_body
                 }
+                }
             };
         },
     );
diff --git a/crates/test/Cargo.toml b/crates/test/Cargo.toml
index 72825958..d3083b0e 100644
--- a/crates/test/Cargo.toml
+++ b/crates/test/Cargo.toml
@@ -23,12 +23,6 @@ wasm-bindgen-test-macro = { path = '../test-macro', version = '=0.3.47' }
 
 [target.'cfg(all(target_arch = "wasm32", wasm_bindgen_unstable_test_coverage))'.dependencies]
 minicov = "0.3"
-wasm-bindgen-test-macro = { path = '../test-macro', version = '=0.3.47', features = ["coverage"] }
-
-[target.'cfg(__wasm_bindgen_resolver_1)'.dependencies]
-wasm-bindgen-test-macro = { path = "../test-macro", version = "=0.3.47", features = [
-  "xxx_resolver_1",
-] }
 
 [lints.rust]
 unexpected_cfgs = { level = "warn", check-cfg = ['cfg(wasm_bindgen_unstable_test_coverage)'] }
diff --git a/crates/test/src/rt/mod.rs b/crates/test/src/rt/mod.rs
index 2d508c6e..0c28b134 100644
--- a/crates/test/src/rt/mod.rs
+++ b/crates/test/src/rt/mod.rs
@@ -99,6 +99,7 @@ use core::future::Future;
 use core::pin::Pin;
 use core::task::{self, Poll};
 use js_sys::{Array, Function, Promise};
+pub use wasm_bindgen;
 use wasm_bindgen::prelude::*;
 use wasm_bindgen_futures::future_to_promise;