Skip to content

Commit

Permalink
feat(linter/eslint): Implement no-label-var
Browse files Browse the repository at this point in the history
  • Loading branch information
jelly committed Jul 8, 2024
1 parent eb37918 commit eace435
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 0 deletions.
2 changes: 2 additions & 0 deletions crates/oxc_linter/src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ mod eslint {
pub mod no_inner_declarations;
pub mod no_irregular_whitespace;
pub mod no_iterator;
pub mod no_label_var;
pub mod no_loss_of_precision;
pub mod no_multi_str;
pub mod no_new;
Expand Down Expand Up @@ -451,6 +452,7 @@ oxc_macros::declare_all_lint_rules! {
eslint::no_case_declarations,
eslint::no_class_assign,
eslint::no_multi_str,
eslint::no_label_var,
eslint::require_await,
eslint::no_compare_neg_zero,
eslint::no_cond_assign,
Expand Down
79 changes: 79 additions & 0 deletions crates/oxc_linter/src/rules/eslint/no_label_var.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use oxc_ast::AstKind;
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;

use crate::{context::LintContext, rule::Rule, AstNode};

fn no_label_var_diagnostic(x0: &str, span0: Span, span1: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!(
"eslint(no-label-var): Found identifier '{x0}' with the same name as a label."
))
.with_labels([
span0.label(format!("Identifier '{x0}' found here.")),
span1.label("Label with the same name."),
])
}

#[derive(Debug, Default, Clone)]
pub struct NoLabelVar;

declare_oxc_lint!(
/// ### What it does
///
/// Disallow labels that share a name with a variable.
///
/// ### Why is this bad?
///
/// This rule aims to create clearer code by disallowing the bad practice of creating a label
/// that shares a name with a variable that is in scope.
///
/// ### Example
/// ```javascript
/// var x = foo;
/// function bar() {
/// x:
/// for (;;) {
/// break x;
/// }
/// }
/// ```
NoLabelVar,
style,
);

impl Rule for NoLabelVar {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
let AstKind::LabeledStatement(labeled_stmt) = node.kind() else { return };

if let Some(symbol_id) =
ctx.scopes().find_binding(node.scope_id(), &labeled_stmt.label.name)
{
let decl_span = ctx.symbols().get_span(symbol_id);
let label_decl = labeled_stmt.span.start;
ctx.diagnostic(no_label_var_diagnostic(
&labeled_stmt.label.name,
decl_span,
Span::new(label_decl, label_decl + 1),
));
}
}
}

#[test]
fn test() {
use crate::tester::Tester;

let pass = vec![
"function bar() { q: for(;;) { break q; } } function foo () { var q = t; }",
"function bar() { var x = foo; q: for(;;) { break q; } }",
];

let fail = vec![
"var x = foo; function bar() { x: for(;;) { break x; } }",
"function bar() { var x = foo; x: for(;;) { break x; } }",
"function bar(x) { x: for(;;) { break x; } }",
];

Tester::new(NoLabelVar::NAME, pass, fail).test_and_snapshot();
}
26 changes: 26 additions & 0 deletions crates/oxc_linter/src/snapshots/no_label_var.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
source: crates/oxc_linter/src/tester.rs
---
eslint(no-label-var): Found identifier 'x' with the same name as a label.
╭─[no_label_var.tsx:1:5]
1var x = foo; function bar() { x: for(;;) { break x; } }
· ┬ ┬
· │ ╰── Label with the same name.
· ╰── Identifier 'x' found here.
╰────

eslint(no-label-var): Found identifier 'x' with the same name as a label.
╭─[no_label_var.tsx:1:22]
1function bar() { var x = foo; x: for(;;) { break x; } }
· ┬ ┬
· │ ╰── Label with the same name.
· ╰── Identifier 'x' found here.
╰────

eslint(no-label-var): Found identifier 'x' with the same name as a label.
╭─[no_label_var.tsx:1:14]
1function bar(x) { x: for(;;) { break x; } }
· ┬ ┬
· │ ╰── Label with the same name.
· ╰── Identifier 'x' found here.
╰────

0 comments on commit eace435

Please sign in to comment.