feat(rome_js_analyze): noLabelVar
MODIFY
crates/rome_js_analyze/src/registry.rs
crates/rome_js_analyze/src/registry.rs
@@ -41,6 +41,9 @@ where
if filter.match_rule::<NoImplicitBoolean>() {
rules.push::<NoImplicitBoolean>();
}
+ if filter.match_rule::<NoLabelVar>() {
+ rules.push::<NoLabelVar>();
+ }
if filter.match_rule::<NoMultipleSpacesInRegularExpressionLiterals>() {
rules.push::<NoMultipleSpacesInRegularExpressionLiterals>();
}

MODIFY
crates/rome_js_analyze/src/semantic_analyzers.rs
crates/rome_js_analyze/src/semantic_analyzers.rs
mod no_arguments;
pub(crate) use no_arguments::NoArguments;
+mod no_label_var;
+pub(crate) use no_label_var::NoLabelVar;

ADD
crates/rome_js_analyze/src/semantic_analyzers/no_label_var.rs
+use crate::{semantic_services::Semantic, JsRuleAction};
+use rome_analyze::{context::RuleContext, declare_rule, Rule, RuleCategory, RuleDiagnostic};
+use rome_console::markup;
+use rome_js_syntax::{JsLabeledStatement, JsSyntaxNode, JsSyntaxToken};
+use rome_rowan::AstNode;
+
+declare_rule! {
+ /// Disallow labels that share a name with a variable
+ ///
+ /// ## Examples
+ ///
+ /// ### Invalid
+ ///
+ /// ```js,expect_diagnostic
+ /// const x1 = "test";
+ /// x1: expr;
+ /// ```
+ ///
+ /// ### Valid
+ ///
+ /// ```js
+ /// const x = "test";
+ /// z: expr;
+ /// ```
+ pub(crate) NoLabelVar = "noLabelVar"
+}
+
+impl Rule for NoLabelVar {
+ const CATEGORY: RuleCategory = RuleCategory::Lint;
+
+ type Query = Semantic<JsLabeledStatement>;
+ /// The first element of the tuple is the name of the binding, the second element of the tuple is the label name
+ type State = (JsSyntaxNode, JsSyntaxToken);
+ type Signals = Option<Self::State>;
+
+ fn run(ctx: &RuleContext<Self>) -> Option<Self::State> {
+ let label_statement = ctx.query();
+
+ let label_token = label_statement.label_token().ok()?;
+ let name = label_token.text_trimmed();
+ let model = ctx.model();
+ // We search each scope from current scope until the global scope
+ // if we find a binding that has its name equal to label name, then we found a `LabelVar` issue.
+ for scope in model.scope(label_statement.syntax()).ancestors() {
+ if let Some(binding) = scope.get_binding(name) {
+ return Some((binding.syntax().clone(), label_token));
+ }
+ }
+ None
+ }
+
+ fn diagnostic(_: &RuleContext<Self>, state: &Self::State) -> Option<RuleDiagnostic> {
+ let (binding_syntax_node, label_token) = state;
+ let name = label_token.text_trimmed();
+
+ Some(RuleDiagnostic::warning(
+ label_token.text_trimmed_range(),
+ markup! {
+ "Do not use the "<Emphasis>{name}</Emphasis>" variable name as a label"
+ },
+ )
+ .secondary(binding_syntax_node.text_trimmed_range(), markup! {
+ "The variable is declared here"
+ },)
+ .footer_note(markup! {"Creating a label with the same name as an in-scope variable leads to confusion."}))
+ }
+
+ fn action(_: &RuleContext<Self>, _: &Self::State) -> Option<JsRuleAction> {
+ None
+ }
+}

ADD
crates/rome_js_analyze/tests/specs/noLabelVar.js
+// invalid
+const x1 = "test";
+x1: expr;
+// nested scope
+function test() {
+ {
+ x1: for (let i = 0; i < 10; i++) {
+ }
+ }
+}
+
+// valid
+const x = "test";
+z: expr;

MODIFY
website/src/docs/lint/rules/index.md
website/src/docs/lint/rules/index.md
@@ -77,6 +77,13 @@ Disallows empty destructuring patterns.
Disallow implicit <code>true</code> values on JSX boolean attributes
</div>
<div class="rule">
+<h3 data-toc-exclude id="noLabelVar">
+ <a href="/docs/lint/rules/noLabelVar">noLabelVar</a>
+ <a class="header-anchor" href="#noLabelVar"></a>
+</h3>
+Disallow labels that share a name with a variable
+</div>
+<div class="rule">
<h3 data-toc-exclude id="noMultipleSpacesInRegularExpressionLiterals">
<a href="/docs/lint/rules/noMultipleSpacesInRegularExpressionLiterals">noMultipleSpacesInRegularExpressionLiterals</a>
<a class="header-anchor" href="#noMultipleSpacesInRegularExpressionLiterals"></a>

MODIFY
xtask/codegen/src/generate_analyzer.rs
xtask/codegen/src/generate_analyzer.rs
@@ -48,7 +48,7 @@ fn generate_module(name: &'static str, entries: &mut Vec<String>) -> Result<()>
let rule_name = format_ident!("{}", rule_name);
rules.insert(
- index,
+ index.min(rules.len()),
quote! {
mod #module_name;
pub(crate) use #module_name::#rule_name;

feat(rome_js_analyze): noLabelVar (#2838)

Co-authored-by: Emanuele Stoppa <my.burning@gmail.com>
Co-authored-by: xunilrj <xunilrj@hotmail.com>