diff --git a/crates/ruff_dev/src/generate_rules_table.rs b/crates/ruff_dev/src/generate_rules_table.rs
index 8959796c4f..8499a6d900 100644
--- a/crates/ruff_dev/src/generate_rules_table.rs
+++ b/crates/ruff_dev/src/generate_rules_table.rs
@@ -3,6 +3,7 @@
//! Used for .
use itertools::Itertools;
+use std::borrow::Cow;
use strum::IntoEnumIterator;
use ruff_diagnostics::FixAvailability;
@@ -37,6 +38,16 @@ fn generate_table(table_out: &mut String, rules: impl IntoIterator- ,
let rule_name = rule.as_ref();
+ // If the message ends in a bracketed expression (like: "Use {replacement}"), escape the
+ // brackets. Otherwise, it'll be interpreted as an HTML attribute via the `attr_list`
+ // plugin. (Above, we'd convert to "Use {replacement\}".)
+ let message = rule.message_formats()[0];
+ let message = if let Some(prefix) = message.strip_suffix('}') {
+ Cow::Owned(format!("{prefix}\\}}"))
+ } else {
+ Cow::Borrowed(message)
+ };
+
#[allow(clippy::or_fun_call)]
table_out.push_str(&format!(
"| {0}{1} {{ #{0}{1} }} | {2} | {3} | {4} |",
@@ -46,7 +57,7 @@ fn generate_table(table_out: &mut String, rules: impl IntoIterator
- ,
.is_some()
.then_some(format_args!("[{rule_name}](rules/{rule_name}.md)"))
.unwrap_or(format_args!("{rule_name}")),
- rule.message_formats()[0],
+ message,
status_token,
));
table_out.push('\n');