Skip to content

Commit cce7aa4

Browse files
committed
chore: document rule-group severity relation and update rules_check
1 parent ef45056 commit cce7aa4

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
@@ -32,30 +32,22 @@ use std::slice;
3232
use std::str::FromStr;
3333

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

57-
impl std::error::Error for Errors {}
58-
5951
type Data = BTreeMap<&'static str, (RuleMetadata, RuleCategory)>;
6052
pub fn check_rules() -> anyhow::Result<()> {
6153
#[derive(Default)]
@@ -73,12 +65,74 @@ pub fn check_rules() -> anyhow::Result<()> {
7365
if !matches!(category, RuleCategory::Lint | RuleCategory::Action) {
7466
return;
7567
}
76-
if R::Group::NAME == "style" && R::METADATA.severity == Severity::Error {
77-
self.errors.push(Errors::style_rule_error(R::METADATA.name))
68+
let group = R::Group::NAME;
69+
let rule_name = R::METADATA.name;
70+
let rule_severity = R::METADATA.severity;
71+
if matches!(group, "a11y" | "correctness" | "security")
72+
&& rule_severity != Severity::Error
73+
&& !matches!(
74+
rule_name,
75+
// TODO: remove these exceptions in Biome 3.0
76+
"noNodejsModules"
77+
| "noPrivateImports"
78+
| "noUnusedFunctionParameters"
79+
| "noUnusedImports"
80+
| "noUnusedLabels"
81+
| "noUnusedPrivateClassMembers"
82+
| "noUnusedVariables"
83+
| "useImportExtensions"
84+
| "noNoninteractiveElementInteractions"
85+
| "noGlobalDirnameFilename"
86+
| "noProcessGlobal"
87+
| "noReactPropAssignments"
88+
| "noRestrictedElements"
89+
| "noSolidDestructuredProps"
90+
| "useJsonImportAttributes"
91+
| "useParseIntRadix"
92+
| "useSingleJsDocAsterisk"
93+
)
94+
{
95+
self.errors.push(Errors::new(format!(
96+
"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."
97+
)));
98+
} else if matches!(group, "complexity" | "style") && rule_severity == Severity::Error {
99+
self.errors.push(Errors::new(format!(
100+
"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."
101+
)));
102+
} else if group == "performance"
103+
&& rule_severity != Severity::Warning
104+
&& !matches!(
105+
rule_name,
106+
// TODO: remove these exceptions in Biome 3.0
107+
"noAwaitInLoops" | "useGoogleFontPreconnect" | "useSolidForComponent"
108+
)
109+
{
110+
self.errors.push(Errors::new(format!(
111+
"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."
112+
)));
113+
} else if group == "suspicious"
114+
&& rule_severity == Severity::Information
115+
&& !matches!(
116+
rule_name,
117+
// TODO: remove these exceptions in Biome 3.0
118+
"noAlert"
119+
| "noBitwiseOperators"
120+
| "noConstantBinaryExpressions"
121+
| "noUnassignedVariables"
122+
| "useStaticResponseMethods"
123+
| "noQuickfixBiome"
124+
| "noDuplicateFields"
125+
)
126+
{
127+
self.errors.push(Errors::new(format!(
128+
"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."
129+
)));
78130
} else if <R::Group as RuleGroup>::Category::CATEGORY == RuleCategory::Action
79-
&& R::METADATA.severity != Severity::Information
131+
&& rule_severity != Severity::Information
80132
{
81-
self.errors.push(Errors::action_error(R::METADATA.name));
133+
self.errors.push(Errors::new(format!(
134+
"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'."
135+
)));
82136
} else {
83137
self.groups
84138
.entry((<R::Group as RuleGroup>::NAME, R::METADATA.language))

0 commit comments

Comments
 (0)