diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 123f47b430a17..fdedb7e6a4afe 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1089,10 +1089,11 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
         ),
         opt::flag_s("", "test", "Build a test harness"),
         opt::opt_s("", "target", "Target triple for which the code is compiled", "TARGET"),
-        opt::multi_s("W", "warn", "Set lint warnings", "OPT"),
-        opt::multi_s("A", "allow", "Set lint allowed", "OPT"),
-        opt::multi_s("D", "deny", "Set lint denied", "OPT"),
-        opt::multi_s("F", "forbid", "Set lint forbidden", "OPT"),
+        opt::multi_s("A", "allow", "Set lint allowed", "LINT"),
+        opt::multi_s("W", "warn", "Set lint warnings", "LINT"),
+        opt::multi_s("", "force-warn", "Set lint force-warn", "LINT"),
+        opt::multi_s("D", "deny", "Set lint denied", "LINT"),
+        opt::multi_s("F", "forbid", "Set lint forbidden", "LINT"),
         opt::multi_s(
             "",
             "cap-lints",
@@ -1101,13 +1102,6 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
              level",
             "LEVEL",
         ),
-        opt::multi_s(
-            "",
-            "force-warn",
-            "Specifiy lints that should warn even if \
-             they are allowed somewhere else",
-            "LINT",
-        ),
         opt::multi_s("C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
         opt::flag_s("V", "version", "Print version info and exit"),
         opt::flag_s("v", "verbose", "Use verbose output"),
@@ -1163,19 +1157,10 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
 pub fn get_cmd_lint_options(
     matches: &getopts::Matches,
     error_format: ErrorOutputType,
-    debugging_opts: &DebuggingOptions,
 ) -> (Vec<(String, lint::Level)>, bool, Option<lint::Level>) {
     let mut lint_opts_with_position = vec![];
     let mut describe_lints = false;
 
-    if !debugging_opts.unstable_options && matches.opt_present("force-warn") {
-        early_error(
-            error_format,
-            "the `-Z unstable-options` flag must also be passed to enable \
-            the flag `--force-warn=lints`",
-        );
-    }
-
     for level in [lint::Allow, lint::Warn, lint::ForceWarn, lint::Deny, lint::Forbid] {
         for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) {
             if lint_name == "help" {
@@ -1965,8 +1950,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         .unwrap_or_else(|e| early_error(error_format, &e[..]));
 
     let mut debugging_opts = DebuggingOptions::build(matches, error_format);
-    let (lint_opts, describe_lints, lint_cap) =
-        get_cmd_lint_options(matches, error_format, &debugging_opts);
+    let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
 
     check_debug_option_stability(&debugging_opts, error_format, json_rendered);
 
diff --git a/src/doc/rustc/src/lints/levels.md b/src/doc/rustc/src/lints/levels.md
index 3e616f226ed76..7bd46fafadf83 100644
--- a/src/doc/rustc/src/lints/levels.md
+++ b/src/doc/rustc/src/lints/levels.md
@@ -1,11 +1,12 @@
 # Lint levels
 
-In `rustc`, lints are divided into four *levels*:
+In `rustc`, lints are divided into five *levels*:
 
 1. allow
 2. warn
-3. deny
-4. forbid
+3. force-warn
+4. deny
+5. forbid
 
 Each lint has a default level (explained in the lint listing later in this
 chapter), and the compiler has a default warning level. First, let's explain
@@ -57,6 +58,14 @@ warning: unused variable: `x`
   = note: to avoid this warning, consider using `_x` instead
 ```
 
+## force-warn
+
+'force-warn' is a special lint level. It's the same as 'warn' in that a lint
+at this level will produce a warning, but unlike the 'warn' level, the
+'force-warn' level cannot be overridden. If a lint is set to 'force-warn', it
+is guaranteed to warn: no more, no less. This is true even if the overall lint
+level is capped via cap-lints.
+
 ## deny
 
 A 'deny' lint produces an error if you violate it. For example, this code
@@ -87,11 +96,12 @@ This lint level gives you that.
 
 ## forbid
 
-'forbid' is a special lint level that's stronger than 'deny'. It's the same
-as 'deny' in that a lint at this level will produce an error, but unlike the
-'deny' level, the 'forbid' level can not be overridden to be anything lower
-than an error.  However, lint levels may still be capped with `--cap-lints`
-(see below) so `rustc --cap-lints warn` will make lints set to 'forbid' just
+'forbid' is a special lint level that fills the same role for 'deny' that
+'force-warn' does for 'warn'. It's the same as 'deny' in that a lint at this
+level will produce an error, but unlike the 'deny' level, the 'forbid' level
+can not be overridden to be anything lower than an error.  However, lint
+levels may still be capped with `--cap-lints` (see below) so `rustc --cap-
+lints warn` will make lints set to 'forbid' just
 warn.
 
 ## Configuring warning levels
@@ -113,8 +123,8 @@ certain lint levels. We'll talk about that last.
 
 ### Via compiler flag
 
-The `-A`, `-W`, `-D`, and `-F` flags let you turn one or more lints
-into allowed, warning, deny, or forbid levels, like this:
+The `-A`, `-W`, `--force-warn` `-D`, and `-F` flags let you turn one or more lints
+into allowed, warning, force-warn, deny, or forbid levels, like this:
 
 ```bash
 $ rustc lib.rs --crate-type=lib -W missing-docs
@@ -158,7 +168,7 @@ You can also pass each flag more than once for changing multiple lints:
 $ rustc lib.rs --crate-type=lib -D missing-docs -D unused-variables
 ```
 
-And of course, you can mix these four flags together:
+And of course, you can mix these five flags together:
 
 ```bash
 $ rustc lib.rs --crate-type=lib -D missing-docs -A unused-variables
@@ -176,6 +186,10 @@ You can make use of this behavior by overriding the level of one specific lint o
 $ rustc lib.rs --crate-type=lib -D unused -A unused-variables
 ```
 
+Since `force-warn` and `forbid` cannot be overridden, setting
+one of them will prevent any later level for the same lint from
+taking effect.
+
 ### Via an attribute
 
 You can also modify the lint level with a crate-wide attribute:
@@ -207,7 +221,8 @@ warning: missing documentation for a function
   | ^^^^^^^^^^^^
 ```
 
-All four, `warn`, `allow`, `deny`, and `forbid` all work this way.
+`warn`, `allow`, `deny`, and `forbid` all work this way. There is
+no way to set a lint to `force-warn` using an attribute.
 
 You can also pass in multiple lints per attribute:
 
diff --git a/src/doc/unstable-book/src/compiler-flags/force-warn.md b/src/doc/unstable-book/src/compiler-flags/force-warn.md
deleted file mode 100644
index 052de0f379e7f..0000000000000
--- a/src/doc/unstable-book/src/compiler-flags/force-warn.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# `force-warn`
-
-The tracking issue for this feature is: [#85512](https://github.com/rust-lang/rust/issues/85512).
-
-------------------------
-
-This feature allows you to cause any lint to produce a warning even if the lint has a different level by default or another level is set somewhere else. For instance, the `force-warn` option can be used to make a lint (e.g., `dead_code`) produce a warning even if that lint is allowed in code with `#![allow(dead_code)]`.
-
-## Example
-
-```rust,ignore (partial-example)
-#![allow(dead_code)]
-
-fn dead_function() {}
-// This would normally not produce a warning even though the
-// function is not used, because dead code is being allowed
-
-fn main() {}
-```
-
-We can force a warning to be produced by providing `--force-warn dead_code` to rustc.
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index eef6985ea30a0..97930f106994a 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -671,8 +671,7 @@ impl Options {
             return Err(1);
         }
 
-        let (lint_opts, describe_lints, lint_cap) =
-            get_cmd_lint_options(matches, error_format, &debugging_opts);
+        let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
 
         Ok(Options {
             input,
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 34fe808dae2e4..4eae813aff7b0 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -503,10 +503,11 @@ fn opts() -> Vec<RustcOptGroup> {
         unstable("disable-minification", |o| {
             o.optflagmulti("", "disable-minification", "Disable minification applied on JS files")
         }),
-        stable("warn", |o| o.optmulti("W", "warn", "Set lint warnings", "OPT")),
-        stable("allow", |o| o.optmulti("A", "allow", "Set lint allowed", "OPT")),
-        stable("deny", |o| o.optmulti("D", "deny", "Set lint denied", "OPT")),
-        stable("forbid", |o| o.optmulti("F", "forbid", "Set lint forbidden", "OPT")),
+        stable("allow", |o| o.optmulti("A", "allow", "Set lint allowed", "LINT")),
+        stable("warn", |o| o.optmulti("W", "warn", "Set lint warnings", "LINT")),
+        stable("force-warn", |o| o.optmulti("", "force-warn", "Set lint force-warn", "LINT")),
+        stable("deny", |o| o.optmulti("D", "deny", "Set lint denied", "LINT")),
+        stable("forbid", |o| o.optmulti("F", "forbid", "Set lint forbidden", "LINT")),
         stable("cap-lints", |o| {
             o.optmulti(
                 "",
@@ -517,14 +518,6 @@ fn opts() -> Vec<RustcOptGroup> {
                 "LEVEL",
             )
         }),
-        unstable("force-warn", |o| {
-            o.optopt(
-                "",
-                "force-warn",
-                "Lints that will warn even if allowed somewhere else",
-                "LINTS",
-            )
-        }),
         unstable("index-page", |o| {
             o.optopt("", "index-page", "Markdown file to be used as index page", "PATH")
         }),
diff --git a/src/test/run-make/unstable-flag-required/Makefile b/src/test/run-make/unstable-flag-required/Makefile
index a9aad54162f11..b8769d5f69051 100644
--- a/src/test/run-make/unstable-flag-required/Makefile
+++ b/src/test/run-make/unstable-flag-required/Makefile
@@ -2,4 +2,3 @@
 
 all:
 	$(RUSTDOC) --output-format=json x.html 2>&1 | diff - output-format-json.stderr
-	$(RUSTC) --force-warn dead_code x.rs 2>&1 | diff - force-warn.stderr
diff --git a/src/test/run-make/unstable-flag-required/force-warn.stderr b/src/test/run-make/unstable-flag-required/force-warn.stderr
deleted file mode 100644
index 1b70dc83bdb15..0000000000000
--- a/src/test/run-make/unstable-flag-required/force-warn.stderr
+++ /dev/null
@@ -1,2 +0,0 @@
-error: the `-Z unstable-options` flag must also be passed to enable the flag `--force-warn=lints`
-
diff --git a/src/test/ui/lint/cli-lint-override.rs b/src/test/ui/lint/cli-lint-override.rs
index a0a96d01be3f5..7d762b05c97b9 100644
--- a/src/test/ui/lint/cli-lint-override.rs
+++ b/src/test/ui/lint/cli-lint-override.rs
@@ -5,7 +5,7 @@
 //
 //[warn_deny] compile-flags: --warn missing_abi --deny missing_abi
 //[forbid_warn] compile-flags: --warn missing_abi --forbid missing_abi
-//[force_warn_deny] compile-flags: -Z unstable-options --force-warn missing_abi --allow missing_abi
+//[force_warn_deny] compile-flags: --force-warn missing_abi --allow missing_abi
 //[force_warn_deny] check-pass
 
 
diff --git a/src/test/ui/lint/cli-unknown-force-warn.rs b/src/test/ui/lint/cli-unknown-force-warn.rs
index 55544cc73781a..f3dea87a6b69a 100644
--- a/src/test/ui/lint/cli-unknown-force-warn.rs
+++ b/src/test/ui/lint/cli-unknown-force-warn.rs
@@ -1,7 +1,7 @@
 // Checks that rustc correctly errors when passed an invalid lint with
 // `--force-warn`. This is a regression test for issue #86958.
 //
-// compile-flags: -Z unstable-options --force-warn foo-qux
+// compile-flags: --force-warn foo-qux
 // error-pattern: unknown lint: `foo_qux`
 
 fn main() {}
diff --git a/src/test/ui/lint/cli-unknown-force-warn.stderr b/src/test/ui/lint/cli-unknown-force-warn.stderr
index 4367c3b4500d5..9ce9f405aee69 100644
--- a/src/test/ui/lint/cli-unknown-force-warn.stderr
+++ b/src/test/ui/lint/cli-unknown-force-warn.stderr
@@ -6,10 +6,6 @@ error[E0602]: unknown lint: `foo_qux`
    |
    = note: requested on the command line with `--force-warn foo_qux`
 
-error[E0602]: unknown lint: `foo_qux`
-   |
-   = note: requested on the command line with `--force-warn foo_qux`
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0602`.
diff --git a/src/test/ui/lint/force-warn/allow-warnings.rs b/src/test/ui/lint/force-warn/allow-warnings.rs
index 6ee5ba6793280..adcefc7ec78c4 100644
--- a/src/test/ui/lint/force-warn/allow-warnings.rs
+++ b/src/test/ui/lint/force-warn/allow-warnings.rs
@@ -1,6 +1,6 @@
 // --force-warn $LINT causes $LINT (which is warn-by-default) to warn
 // despite allowing all warnings in module
-// compile-flags: --force-warn dead_code -Zunstable-options
+// compile-flags: --force-warn dead_code
 // check-pass
 
 #![allow(warnings)]
diff --git a/src/test/ui/lint/force-warn/allowed-by-default-lint.rs b/src/test/ui/lint/force-warn/allowed-by-default-lint.rs
index fd0b886d84dbe..b24ab822d9303 100644
--- a/src/test/ui/lint/force-warn/allowed-by-default-lint.rs
+++ b/src/test/ui/lint/force-warn/allowed-by-default-lint.rs
@@ -1,5 +1,5 @@
 // --force-warn $LINT causes $LINT (which is allow-by-default) to warn
-// compile-flags: --force-warn elided_lifetimes_in_paths -Zunstable-options
+// compile-flags: --force-warn elided_lifetimes_in_paths
 // check-pass
 
 struct Foo<'a> {
diff --git a/src/test/ui/lint/force-warn/allowed-deny-by-default-lint.rs b/src/test/ui/lint/force-warn/allowed-deny-by-default-lint.rs
index 82a584ac97265..08e75a775d0b5 100644
--- a/src/test/ui/lint/force-warn/allowed-deny-by-default-lint.rs
+++ b/src/test/ui/lint/force-warn/allowed-deny-by-default-lint.rs
@@ -1,6 +1,6 @@
 // --force-warn $LINT causes $LINT (which is deny-by-default) to warn
 // despite $LINT being allowed in module
-// compile-flags: --force-warn const_err -Zunstable-options
+// compile-flags: --force-warn const_err
 // check-pass
 
 #![allow(const_err)]
diff --git a/src/test/ui/lint/force-warn/allowed-group-warn-by-default-lint.rs b/src/test/ui/lint/force-warn/allowed-group-warn-by-default-lint.rs
index 86ab12668a3e4..9b1edba41aafe 100644
--- a/src/test/ui/lint/force-warn/allowed-group-warn-by-default-lint.rs
+++ b/src/test/ui/lint/force-warn/allowed-group-warn-by-default-lint.rs
@@ -1,6 +1,6 @@
 // --force-warn $LINT causes $LINT (which is warn-by-default) to warn
 // despite $LINT_GROUP (which contains $LINT) being allowed
-// compile-flags: --force-warn bare_trait_objects -Zunstable-options
+// compile-flags: --force-warn bare_trait_objects
 // check-pass
 
 #![allow(rust_2018_idioms)]
diff --git a/src/test/ui/lint/force-warn/allowed-warn-by-default-lint.rs b/src/test/ui/lint/force-warn/allowed-warn-by-default-lint.rs
index 7204782a324e3..4ac29ff7d99b7 100644
--- a/src/test/ui/lint/force-warn/allowed-warn-by-default-lint.rs
+++ b/src/test/ui/lint/force-warn/allowed-warn-by-default-lint.rs
@@ -1,6 +1,6 @@
 // --force-warn $LINT causes $LINT (which is warn-by-default) to warn
 // despite $LINT being allowed in module
-// compile-flags: --force-warn dead_code -Zunstable-options
+// compile-flags: --force-warn dead_code
 // check-pass
 
 #![allow(dead_code)]
diff --git a/src/test/ui/lint/force-warn/cap-lints-allow.rs b/src/test/ui/lint/force-warn/cap-lints-allow.rs
index de3a1bd8dd719..9609ea994312f 100644
--- a/src/test/ui/lint/force-warn/cap-lints-allow.rs
+++ b/src/test/ui/lint/force-warn/cap-lints-allow.rs
@@ -1,6 +1,6 @@
 // --force-warn $LINT casuses $LINT to warn despite --cap-lints
 // set to allow
-// compile-flags: --cap-lints allow  --force-warn bare_trait_objects -Zunstable-options
+// compile-flags: --cap-lints allow  --force-warn bare_trait_objects
 // check-pass
 
 pub trait SomeTrait {}
diff --git a/src/test/ui/lint/force-warn/cap-lints-warn-allowed-warn-by-default-lint.rs b/src/test/ui/lint/force-warn/cap-lints-warn-allowed-warn-by-default-lint.rs
index 70fb90dc1992b..e65f156bfdc9a 100644
--- a/src/test/ui/lint/force-warn/cap-lints-warn-allowed-warn-by-default-lint.rs
+++ b/src/test/ui/lint/force-warn/cap-lints-warn-allowed-warn-by-default-lint.rs
@@ -1,6 +1,6 @@
 // --force-warn $LINT_GROUP causes $LINT to warn despite $LINT being
 // allowed in module and cap-lints set to warn
-// compile-flags: --cap-lints warn  --force-warn rust-2021-compatibility -Zunstable-options
+// compile-flags: --cap-lints warn  --force-warn rust-2021-compatibility
 // check-pass
 #![allow(ellipsis_inclusive_range_patterns)]
 
diff --git a/src/test/ui/lint/force-warn/deny-by-default-lint.rs b/src/test/ui/lint/force-warn/deny-by-default-lint.rs
index b0a15cc2fba06..e371029032fe9 100644
--- a/src/test/ui/lint/force-warn/deny-by-default-lint.rs
+++ b/src/test/ui/lint/force-warn/deny-by-default-lint.rs
@@ -1,5 +1,5 @@
 // --force-warn $LINT causes $LINT (which is deny-by-default) to warn
-// compile-flags: --force-warn const_err -Zunstable-options
+// compile-flags: --force-warn const_err
 // check-pass
 
 const C: i32 = 1 / 0;
diff --git a/src/test/ui/lint/force-warn/lint-group-allow-warnings.rs b/src/test/ui/lint/force-warn/lint-group-allow-warnings.rs
index e5dcd9a7ea16f..4b95f4d2dfbba 100644
--- a/src/test/ui/lint/force-warn/lint-group-allow-warnings.rs
+++ b/src/test/ui/lint/force-warn/lint-group-allow-warnings.rs
@@ -1,7 +1,7 @@
 // --force-warn $LINT_GROUP causes $LINT in $LINT_GROUP to warn
 // despite all warnings being allowed in module
 // warn-by-default lint to warn
-// compile-flags: --force-warn nonstandard_style -Zunstable-options
+// compile-flags: --force-warn nonstandard_style
 // check-pass
 
 #![allow(warnings)]
diff --git a/src/test/ui/lint/force-warn/lint-group-allowed-lint-group.rs b/src/test/ui/lint/force-warn/lint-group-allowed-lint-group.rs
index dc13b2b24748c..99cad614c25cd 100644
--- a/src/test/ui/lint/force-warn/lint-group-allowed-lint-group.rs
+++ b/src/test/ui/lint/force-warn/lint-group-allowed-lint-group.rs
@@ -1,6 +1,6 @@
 // --force-warn $LINT_GROUP causes $LINT to warn despite
 // $LINT_GROUP being allowed in module
-// compile-flags: --force-warn rust_2018_idioms -Zunstable-options
+// compile-flags: --force-warn rust_2018_idioms
 // check-pass
 
 #![allow(rust_2018_idioms)]
diff --git a/src/test/ui/lint/force-warn/lint-group-allowed-warn-by-default-lint.rs b/src/test/ui/lint/force-warn/lint-group-allowed-warn-by-default-lint.rs
index b7f79b3d4aa3b..f0aacd773401f 100644
--- a/src/test/ui/lint/force-warn/lint-group-allowed-warn-by-default-lint.rs
+++ b/src/test/ui/lint/force-warn/lint-group-allowed-warn-by-default-lint.rs
@@ -1,6 +1,6 @@
 // --force-warn $LINT_GROUP causes $LINT (which is warn-by-default) to warn
 // despite $LINT being allowed in module
-// compile-flags: --force-warn rust-2018-idioms -Zunstable-options
+// compile-flags: --force-warn rust-2018-idioms
 // check-pass
 
 #![allow(bare_trait_objects)]