diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl
index e8c7d49d076aa..1b9505c59e327 100644
--- a/stdlib/Test/src/Test.jl
+++ b/stdlib/Test/src/Test.jl
@@ -887,28 +887,7 @@ Note: Warnings generated by `@warn` cannot be tested with this macro. Use
 [`@test_logs`](@ref) instead.
 """
 macro test_warn(msg, expr)
-    quote
-        let fname = tempname()
-            try
-                f = open(fname, "w")
-                stdold = stderr
-                redirect_stderr(f)
-                ret = try
-                    # We deliberately don't use the thunk versions of open/redirect
-                    # to ensure that adding the macro does not change the toplevel-ness
-                    # of the resulting expression.
-                    $(esc(expr))
-                finally
-                    redirect_stderr(stdold)
-                    close(f)
-                end
-                @test contains_warn(read(fname, String), $(esc(msg)))
-                ret
-            finally
-                rm(fname, force=true)
-            end
-        end
-    end
+    test_warn_expr(expr, msg)
 end
 
 """
@@ -921,32 +900,35 @@ Note: The absence of warnings generated by `@warn` cannot be tested
 with this macro. Use [`@test_logs`](@ref) instead.
 """
 macro test_nowarn(expr)
-    quote
-        # Duplicate some code from `@test_warn` to allow printing the content of
-        # `stderr` again to `stderr` here while suppressing it for `@test_warn`.
-        # If that shouldn't be used, it would be possible to just use
-        #     @test_warn isempty $(esc(expr))
-        # here.
-        let fname = tempname()
-            try
-                f = open(fname, "w")
-                stdold = stderr
-                redirect_stderr(f)
-                ret = try
-                    $(esc(expr))
-                finally
-                    redirect_stderr(stdold)
-                    close(f)
-                end
-                stderr_content = read(fname, String)
-                print(stderr, stderr_content) # this is helpful for debugging
-                @test isempty(stderr_content)
-                ret
+    # allow printing the content of `stderr` again to `stderr` here while suppressing it
+    # for `@test_warn`. If that shouldn't be used, this could just be `test_warn_expr(expr, #=msg=#isempty)`
+    test_warn_expr(expr, function (s)
+        print(stderr, s) # this is helpful for debugging
+        isempty(s)
+    end)
+end
+
+function test_warn_expr(@nospecialize(expr), @nospecialize(msg))
+    return :(let fname = tempname()
+        try
+            f = open(fname, "w")
+            stdold = stderr
+            redirect_stderr(f)
+            ret = try
+                # We deliberately don't use the thunk versions of open/redirect
+                # to ensure that adding the macro does not change the toplevel-ness
+                # of the resulting expression.
+                $(esc(expr))
             finally
-                rm(fname, force=true)
+                redirect_stderr(stdold)
+                close(f)
             end
+            @test contains_warn(read(fname, String), $(esc(msg)))
+            ret
+        finally
+            rm(fname, force=true)
         end
-    end
+    end)
 end
 
 #-----------------------------------------------------------------------