Skip to content

Commit ee2f5bf

Browse files
authored
chore: document rule-group severity relation and update rules_check (#7827)
1 parent 72afdfa commit ee2f5bf

File tree

2 files changed

+98
-20
lines changed

2 files changed

+98
-20
lines changed

crates/biome_analyze/CONTRIBUTING.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ The analyzer allows implementors to create **three different** types of rules:
2626
* [Biome lint rules inspired by other lint rules](#biome-lint-rules-inspired-by-other-lint-rules)
2727
- [`rule_category!` macro](#rule_category-macro)
2828
- [Rule severity](#rule-severity)
29+
- [Rule group and severity](#rule-group-and-severity)
2930
- [Rule domains](#rule-domains)
3031
- [Rule Options](#rule-options)
3132
* [Options for our example rule](#options-for-our-example-rule)
@@ -516,6 +517,28 @@ declare_lint_rule! {
516517
}
517518
```
518519

520+
#### Rule group and severity
521+
522+
> [!NOTE]
523+
> This section is relevant to Biome maintainers when they want to move (promote) a rule to a group that is not `nursery`.
524+
525+
We try to maintain consistency in the default severity level and group membership of the rules.
526+
For legacy reasons, we have some rules that don't follow these constraints.
527+
528+
- `correctness`, `security`, and `a11y` rules **must** have a severity set to `error`.
529+
530+
If `error` is too strict for a rule, then it should certainly be in another group (for example `suspicious` instead of `correctness`).
531+
532+
- `style` rules **must** have a severity set to `info` or `warn`. If in doubt, choose `info`.
533+
534+
- `complexity` rules **must** have a severity set to `warn` or `info`. If in doubt, choose `info`.
535+
536+
- `suspicious` rules **must** have a severity set to `warn` or `error`. If in doubt, choose `warn`.
537+
538+
- `performance` rules **must** have a severity set to `warn`.
539+
540+
- Actions **must** have a severity set to `info`.
541+
519542
#### Rule domains
520543

521544
Domains are very specific ways to collect rules that belong to the same "concept". Domains are a way for users to opt-in/opt-out rules that belong to the same domain.
@@ -548,6 +571,7 @@ Instead, if the rule is **recommended** but _doesn't have domains_, the rule is
548571
> [!NOTE]
549572
> Before adding a new domain, please consult with the maintainers of the project.
550573
574+
551575
#### Rule Options
552576

553577
Some rules may allow customization [using per-rule options in `biome.json`](https://biomejs.dev/linter/#rule-options).

xtask/rules_check/src/lib.rs

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,30 +31,22 @@ use biome_service::workspace::DocumentFileSource;
3131
use pulldown_cmark::{CodeBlockKind, Event, HeadingLevel, Parser, Tag, TagEnd};
3232

3333
#[derive(Debug)]
34-
struct Errors(String);
35-
34+
struct Errors {
35+
message: String,
36+
}
3637
impl Errors {
37-
fn style_rule_error(rule_name: impl Display) -> Self {
38-
Self(format!(
39-
"The rule '{rule_name}' that belongs to the group 'style' can't have Severity::Error. Lower down the severity or change the group.",
40-
))
41-
}
42-
43-
fn action_error(rule_name: impl Display) -> Self {
44-
Self(format!(
45-
"The rule '{rule_name}' is an action, and it must have Severity::Information. Lower down the severity.",
46-
))
38+
const fn new(message: String) -> Self {
39+
Self { message }
4740
}
4841
}
49-
42+
impl std::error::Error for Errors {}
5043
impl Display for Errors {
5144
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
52-
f.write_str(self.0.as_str())
45+
let Self { message } = self;
46+
f.write_str(message)
5347
}
5448
}
5549

56-
impl std::error::Error for Errors {}
57-
5850
type Data = BTreeMap<&'static str, (RuleMetadata, RuleCategory)>;
5951
pub fn check_rules() -> anyhow::Result<()> {
6052
#[derive(Default)]
@@ -72,12 +64,74 @@ pub fn check_rules() -> anyhow::Result<()> {
7264
if !matches!(category, RuleCategory::Lint | RuleCategory::Action) {
7365
return;
7466
}
75-
if R::Group::NAME == "style" && R::METADATA.severity == Severity::Error {
76-
self.errors.push(Errors::style_rule_error(R::METADATA.name))
67+
let group = R::Group::NAME;
68+
let rule_name = R::METADATA.name;
69+
let rule_severity = R::METADATA.severity;
70+
if matches!(group, "a11y" | "correctness" | "security")
71+
&& rule_severity != Severity::Error
72+
&& !matches!(
73+
rule_name,
74+
// TODO: remove these exceptions in Biome 3.0
75+
"noNodejsModules"
76+
| "noPrivateImports"
77+
| "noUnusedFunctionParameters"
78+
| "noUnusedImports"
79+
| "noUnusedLabels"
80+
| "noUnusedPrivateClassMembers"
81+
| "noUnusedVariables"
82+
| "useImportExtensions"
83+
| "noNoninteractiveElementInteractions"
84+
| "noGlobalDirnameFilename"
85+
| "noProcessGlobal"
86+
| "noReactPropAssignments"
87+
| "noRestrictedElements"
88+
| "noSolidDestructuredProps"
89+
| "useJsonImportAttributes"
90+
| "useParseIntRadix"
91+
| "useSingleJsDocAsterisk"
92+
)
93+
{
94+
self.errors.push(Errors::new(format!(
95+
"The rule '{rule_name}' belongs to the group '{group}' and has a severity set to '{rule_severity}'. Rules that belong to the group {group} must have a severity set to 'error'. Set the severity to 'error' or change the group of the rule."
96+
)));
97+
} else if matches!(group, "complexity" | "style") && rule_severity == Severity::Error {
98+
self.errors.push(Errors::new(format!(
99+
"The rule '{rule_name}' belongs to the group '{group}' and has a severity set to '{rule_severity}'. Rules that belong to the group '{group}' must not have a severity set to 'error'. Lower down the severity or change the group of the rule."
100+
)));
101+
} else if group == "performance"
102+
&& rule_severity != Severity::Warning
103+
&& !matches!(
104+
rule_name,
105+
// TODO: remove these exceptions in Biome 3.0
106+
"noAwaitInLoops" | "useGoogleFontPreconnect" | "useSolidForComponent"
107+
)
108+
{
109+
self.errors.push(Errors::new(format!(
110+
"The rule '{rule_name}' belongs to the group '{group}' and has a severity set to '{rule_severity}'. Rules that belong to the group '{group}' must have a severity set to 'warn'. Set the severity to 'warn' or change the group of the rule."
111+
)));
112+
} else if group == "suspicious"
113+
&& rule_severity == Severity::Information
114+
&& !matches!(
115+
rule_name,
116+
// TODO: remove these exceptions in Biome 3.0
117+
"noAlert"
118+
| "noBitwiseOperators"
119+
| "noConstantBinaryExpressions"
120+
| "noUnassignedVariables"
121+
| "useStaticResponseMethods"
122+
| "noQuickfixBiome"
123+
| "noDuplicateFields"
124+
)
125+
{
126+
self.errors.push(Errors::new(format!(
127+
"The rule '{rule_name}' belongs to the group '{group}' and has a severity set to '{rule_severity}'. Rules that belong to the group '{group}' must have a severity set to 'warn' or 'error'. Change the severity or change the group of the rule."
128+
)));
77129
} else if <R::Group as RuleGroup>::Category::CATEGORY == RuleCategory::Action
78-
&& R::METADATA.severity != Severity::Information
130+
&& rule_severity != Severity::Information
79131
{
80-
self.errors.push(Errors::action_error(R::METADATA.name));
132+
self.errors.push(Errors::new(format!(
133+
"The action '{rule_name}' has a severity set to '{rule_severity}'. Actions must have a severity set to 'info'. Set the severity of the rule to 'info'."
134+
)));
81135
} else {
82136
self.groups
83137
.entry((<R::Group as RuleGroup>::NAME, R::METADATA.language))

0 commit comments

Comments
 (0)