diff --git a/crates/oxc_linter/src/generated/rule_runner_impls.rs b/crates/oxc_linter/src/generated/rule_runner_impls.rs index e888fdfa176a8..4a645d09c0909 100644 --- a/crates/oxc_linter/src/generated/rule_runner_impls.rs +++ b/crates/oxc_linter/src/generated/rule_runner_impls.rs @@ -575,7 +575,7 @@ impl RuleRunner for crate::rules::eslint::no_prototype_builtins::NoPrototypeBuil impl RuleRunner for crate::rules::eslint::no_redeclare::NoRedeclare { const NODE_TYPES: Option<&AstTypesBitset> = None; - const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::RunOnSymbol; + const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::RunOnce; } impl RuleRunner for crate::rules::eslint::no_regex_spaces::NoRegexSpaces { @@ -631,7 +631,7 @@ impl RuleRunner for crate::rules::eslint::no_setter_return::NoSetterReturn { impl RuleRunner for crate::rules::eslint::no_shadow_restricted_names::NoShadowRestrictedNames { const NODE_TYPES: Option<&AstTypesBitset> = None; - const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::RunOnSymbol; + const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::RunOnce; } impl RuleRunner for crate::rules::eslint::no_sparse_arrays::NoSparseArrays { @@ -757,7 +757,7 @@ impl RuleRunner impl RuleRunner for crate::rules::eslint::no_unused_vars::NoUnusedVars { const NODE_TYPES: Option<&AstTypesBitset> = None; - const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::RunOnSymbol; + const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::RunOnce; } impl RuleRunner for crate::rules::eslint::no_useless_backreference::NoUselessBackreference { diff --git a/crates/oxc_linter/src/lib.rs b/crates/oxc_linter/src/lib.rs index 3bce2a8e6d50e..075c42c149f31 100644 --- a/crates/oxc_linter/src/lib.rs +++ b/crates/oxc_linter/src/lib.rs @@ -250,16 +250,6 @@ impl Linter { } } - for symbol in semantic.scoping().symbol_ids() { - for (rule, ctx) in &rules { - if !with_runtime_optimization - || rule.run_info().is_run_on_symbol_implemented() - { - rule.run_on_symbol(symbol, ctx); - } - } - } - // Run rules on nodes for node in semantic.nodes() { for (rule, ctx) in &rules_by_ast_type[node.kind().ty() as usize] { @@ -288,12 +278,6 @@ impl Linter { rule.run_once(ctx); } - if !with_runtime_optimization || run_info.is_run_on_symbol_implemented() { - for symbol in semantic.scoping().symbol_ids() { - rule.run_on_symbol(symbol, ctx); - } - } - if !with_runtime_optimization || run_info.is_run_implemented() { // For smaller files, benchmarking showed it was faster to iterate over all rules and just check the // node types as we go, rather than pre-bucketing rules by AST node type and doing extra allocations. diff --git a/crates/oxc_linter/src/rule.rs b/crates/oxc_linter/src/rule.rs index 5412eb9f5706a..786d37f5f0824 100644 --- a/crates/oxc_linter/src/rule.rs +++ b/crates/oxc_linter/src/rule.rs @@ -5,7 +5,7 @@ use std::{fmt, hash::Hash}; use schemars::{JsonSchema, SchemaGenerator, schema::Schema}; use serde::{Deserialize, Serialize}; -use oxc_semantic::{AstTypesBitset, SymbolId}; +use oxc_semantic::AstTypesBitset; use crate::{ AstNode, FixKind, @@ -30,11 +30,6 @@ pub trait Rule: Sized + Default + fmt::Debug { #[inline] fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {} - /// Visit each symbol - #[expect(unused_variables)] - #[inline] - fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext<'_>) {} - /// Run only once. Useful for inspecting scopes and trivias etc. #[expect(unused_variables)] #[inline] @@ -72,8 +67,8 @@ pub trait RuleRunner: Rule { const NODE_TYPES: Option<&AstTypesBitset>; /// What `Rule` functions are implemented by this `Rule`. For example, if a rule only - /// implements `run_on_symbol`, then the linter can skip calling `run` and `run_once`, so - /// this value would be tagged as [`RuleRunFunctionsImplemented::RunOnSymbol`]. + /// implements `run_once`, then the linter can skip calling `run`, so + /// this value would be tagged as [`RuleRunFunctionsImplemented::RunOnce`]. const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Unknown; fn types_info(&self) -> Option<&'static AstTypesBitset> { @@ -94,8 +89,6 @@ pub enum RuleRunFunctionsImplemented { Run, /// Only `run_once` is implemented RunOnce, - /// Only `run_on_symbol` is implemented - RunOnSymbol, /// Only `run_on_jest_node` is implemented RunOnJestNode, } @@ -109,10 +102,6 @@ impl RuleRunFunctionsImplemented { matches!(self, Self::RunOnce | Self::Unknown) } - pub fn is_run_on_symbol_implemented(self) -> bool { - matches!(self, Self::RunOnSymbol | Self::Unknown) - } - pub fn is_run_on_jest_node_implemented(self) -> bool { matches!(self, Self::RunOnJestNode | Self::Unknown) } diff --git a/crates/oxc_linter/src/rules/eslint/no_redeclare.rs b/crates/oxc_linter/src/rules/eslint/no_redeclare.rs index 642f29d2641aa..108c45ad4a9ed 100644 --- a/crates/oxc_linter/src/rules/eslint/no_redeclare.rs +++ b/crates/oxc_linter/src/rules/eslint/no_redeclare.rs @@ -3,7 +3,6 @@ use javascript_globals::GLOBALS; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{ModuleKind, Span}; -use oxc_syntax::symbol::SymbolId; use crate::{ context::{ContextHost, LintContext}, @@ -81,48 +80,50 @@ impl Rule for NoRedeclare { Self { built_in_globals } } - fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext) { - let name = ctx.scoping().symbol_name(symbol_id); - let decl_span = ctx.scoping().symbol_span(symbol_id); - let is_builtin = self.built_in_globals - && (GLOBALS["builtin"].contains_key(name) || ctx.globals().is_enabled(name)); + fn run_once(&self, ctx: &LintContext) { + for symbol_id in ctx.scoping().symbol_ids() { + let name = ctx.scoping().symbol_name(symbol_id); + let decl_span = ctx.scoping().symbol_span(symbol_id); + let is_builtin = self.built_in_globals + && (GLOBALS["builtin"].contains_key(name) || ctx.globals().is_enabled(name)); - if is_builtin { - ctx.diagnostic(no_redeclare_as_builtin_in_diagnostic(name, decl_span)); - } + if is_builtin { + ctx.diagnostic(no_redeclare_as_builtin_in_diagnostic(name, decl_span)); + } - if ctx.source_type().is_typescript() { - let mut iter = ctx.scoping().symbol_redeclarations(symbol_id).iter().filter(|rd| { - if is_builtin { - if rd.span != decl_span { - ctx.diagnostic(no_redeclare_as_builtin_in_diagnostic(name, rd.span)); + if ctx.source_type().is_typescript() { + let mut iter = ctx.scoping().symbol_redeclarations(symbol_id).iter().filter(|rd| { + if is_builtin { + if rd.span != decl_span { + ctx.diagnostic(no_redeclare_as_builtin_in_diagnostic(name, rd.span)); + } + return false; } - return false; - } - if rd.flags.is_function() { - let node = ctx.nodes().get_node(rd.declaration); - if let Some(func) = node.kind().as_function() { - return !func.is_ts_declare_function(); + if rd.flags.is_function() { + let node = ctx.nodes().get_node(rd.declaration); + if let Some(func) = node.kind().as_function() { + return !func.is_ts_declare_function(); + } } + true + }); + + if let Some(first) = iter.next() { + iter.fold(first, |prev, next| { + ctx.diagnostic(no_redeclare_diagnostic(name, prev.span, next.span)); + next + }); } - true - }); - if let Some(first) = iter.next() { - iter.fold(first, |prev, next| { - ctx.diagnostic(no_redeclare_diagnostic(name, prev.span, next.span)); - next - }); + continue; } - return; - } - - for windows in ctx.scoping().symbol_redeclarations(symbol_id).windows(2) { - if is_builtin { - ctx.diagnostic(no_redeclare_as_builtin_in_diagnostic(name, windows[1].span)); - } else { - ctx.diagnostic(no_redeclare_diagnostic(name, windows[0].span, windows[1].span)); + for windows in ctx.scoping().symbol_redeclarations(symbol_id).windows(2) { + if is_builtin { + ctx.diagnostic(no_redeclare_as_builtin_in_diagnostic(name, windows[1].span)); + } else { + ctx.diagnostic(no_redeclare_diagnostic(name, windows[0].span, windows[1].span)); + } } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_shadow_restricted_names.rs b/crates/oxc_linter/src/rules/eslint/no_shadow_restricted_names.rs index d75ad50d8b0d6..d289c56e0f50e 100644 --- a/crates/oxc_linter/src/rules/eslint/no_shadow_restricted_names.rs +++ b/crates/oxc_linter/src/rules/eslint/no_shadow_restricted_names.rs @@ -3,7 +3,6 @@ use oxc_ast::AstKind; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; -use oxc_syntax::symbol::SymbolId; use serde_json::Value; const PRE_DEFINE_VAR: [&str; 5] = ["Infinity", "NaN", "arguments", "eval", "undefined"]; @@ -93,34 +92,38 @@ impl Rule for NoShadowRestrictedNames { })) } - fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext<'_>) { - let name = ctx.scoping().symbol_name(symbol_id); + fn run_once(&self, ctx: &LintContext) { + for symbol_id in ctx.scoping().symbol_ids() { + let name = ctx.scoping().symbol_name(symbol_id); - if !(PRE_DEFINE_VAR.contains(&name) || self.0.report_global_this && name == "globalThis") { - return; - } - - if name == "undefined" { - // Allow to declare `undefined` variable but not allow to assign value to it. - let node_id = ctx.scoping().symbol_declaration(symbol_id); - if let AstKind::VariableDeclarator(declarator) = ctx.nodes().kind(node_id) - && declarator.init.is_none() - && ctx - .scoping() - .get_resolved_references(symbol_id) - .all(|reference| !reference.is_write()) + if !(PRE_DEFINE_VAR.contains(&name) + || self.0.report_global_this && name == "globalThis") { - return; + continue; + } + + if name == "undefined" { + // Allow to declare `undefined` variable but not allow to assign value to it. + let node_id = ctx.scoping().symbol_declaration(symbol_id); + if let AstKind::VariableDeclarator(declarator) = ctx.nodes().kind(node_id) + && declarator.init.is_none() + && ctx + .scoping() + .get_resolved_references(symbol_id) + .all(|reference| !reference.is_write()) + { + continue; + } } - } - let redeclarations = ctx.scoping().symbol_redeclarations(symbol_id); - if redeclarations.is_empty() { - let span = ctx.scoping().symbol_span(symbol_id); - ctx.diagnostic(no_shadow_restricted_names_diagnostic(name, span)); - } else { - for rd in redeclarations { - ctx.diagnostic(no_shadow_restricted_names_diagnostic(name, rd.span)); + let redeclarations = ctx.scoping().symbol_redeclarations(symbol_id); + if redeclarations.is_empty() { + let span = ctx.scoping().symbol_span(symbol_id); + ctx.diagnostic(no_shadow_restricted_names_diagnostic(name, span)); + } else { + for rd in redeclarations { + ctx.diagnostic(no_shadow_restricted_names_diagnostic(name, rd.span)); + } } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_unused_vars/mod.rs b/crates/oxc_linter/src/rules/eslint/no_unused_vars/mod.rs index 8972bbc4a4c66..9d522753de9e3 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unused_vars/mod.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unused_vars/mod.rs @@ -14,7 +14,7 @@ use std::ops::Deref; use options::{IgnorePattern, NoUnusedVarsOptions}; use oxc_ast::AstKind; use oxc_macros::declare_oxc_lint; -use oxc_semantic::{AstNode, ScopeFlags, SymbolFlags, SymbolId}; +use oxc_semantic::{AstNode, ScopeFlags, SymbolFlags}; use oxc_span::GetSpan; use symbol::Symbol; @@ -205,13 +205,15 @@ impl Rule for NoUnusedVars { Self(Box::new(NoUnusedVarsOptions::try_from(value).unwrap())) } - fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext<'_>) { - let symbol = Symbol::new(ctx, ctx.module_record(), symbol_id); - if Self::should_skip_symbol(&symbol) { - return; - } + fn run_once(&self, ctx: &LintContext) { + for symbol in ctx.scoping().symbol_ids() { + let symbol = Symbol::new(ctx, ctx.module_record(), symbol); + if Self::should_skip_symbol(&symbol) { + continue; + } - self.run_on_symbol_internal(&symbol, ctx); + self.run_on_symbol_internal(&symbol, ctx); + } } fn should_run(&self, ctx: &ContextHost) -> bool { diff --git a/crates/oxc_macros/src/declare_all_lint_rules.rs b/crates/oxc_macros/src/declare_all_lint_rules.rs index 11f4d3768b388..30042e9890d44 100644 --- a/crates/oxc_macros/src/declare_all_lint_rules.rs +++ b/crates/oxc_macros/src/declare_all_lint_rules.rs @@ -150,12 +150,6 @@ pub fn declare_all_lint_rules(metadata: AllLintRulesMeta) -> TokenStream { } } - pub(super) fn run_on_symbol<'a>(&self, symbol_id: SymbolId, ctx: &LintContext<'a>) { - match self { - #(Self::#struct_names(rule) => rule.run_on_symbol(symbol_id, ctx)),* - } - } - pub(super) fn run_once<'a>(&self, ctx: &LintContext<'a>) { match self { #(Self::#struct_names(rule) => rule.run_once(ctx)),* diff --git a/tasks/linter_codegen/src/main.rs b/tasks/linter_codegen/src/main.rs index c243149bd9030..bec83592dee5d 100644 --- a/tasks/linter_codegen/src/main.rs +++ b/tasks/linter_codegen/src/main.rs @@ -70,7 +70,6 @@ pub fn generate_rule_runner_impls() -> io::Result<()> { let rule_run_info_init = if rule_run_info.len() == 1 { match rule_run_info.iter().next().map(String::as_str) { Some("run") => "RuleRunFunctionsImplemented::Run".to_string(), - Some("run_on_symbol") => "RuleRunFunctionsImplemented::RunOnSymbol".to_string(), Some("run_once") => "RuleRunFunctionsImplemented::RunOnce".to_string(), Some("run_on_jest_node") => { "RuleRunFunctionsImplemented::RunOnJestNode".to_string()