Skip to content

Commit

Permalink
perf(es/lints): Make ES lints faster (#9837)
Browse files Browse the repository at this point in the history
**Description:**

The ES lints are causing some performance problems for turbopack.

**Related issue:**

 - Follow-up of #9833
  • Loading branch information
kdy1 authored Jan 6, 2025
1 parent 185efff commit d56a473
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 49 deletions.
7 changes: 7 additions & 0 deletions .changeset/great-owls-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
swc_core: patch
swc: patch
swc_ecma_lints: patch
---

perf(es/lints): Make ES lints faster
4 changes: 2 additions & 2 deletions crates/swc/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use swc_ecma_ast::{noop_pass, EsVersion, Expr, Pass, Program};
use swc_ecma_ext_transforms::jest;
use swc_ecma_lints::{
config::LintConfig,
rules::{lint_to_fold, LintParams},
rules::{lint_pass, LintParams},
};
use swc_ecma_loader::resolvers::{
lru::CachingResolver, node::NodeModulesResolver, tsc::TsConfigResolver,
Expand Down Expand Up @@ -709,7 +709,7 @@ impl Options {
None
},
Optional::new(
lint_to_fold(swc_ecma_lints::rules::all(LintParams {
lint_pass(swc_ecma_lints::rules::all(LintParams {
program: &program,
lint_config: &lints,
top_level_ctxt,
Expand Down
21 changes: 15 additions & 6 deletions crates/swc_ecma_lints/benches/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ use swc_common::{
FileName, Globals, Mark, SourceMap, SyntaxContext, GLOBALS,
};
use swc_ecma_ast::{EsVersion, Program};
use swc_ecma_lints::{config::LintConfig, rule::Rule, rules::LintParams};
use swc_ecma_lints::{
config::LintConfig,
rule::Rule,
rules::{lint_pass, LintParams},
};
use swc_ecma_parser::parse_file_as_module;
use swc_ecma_transforms_base::resolver;
use swc_ecma_visit::{Visit, VisitWith};
use swc_ecma_visit::Visit;

pub fn bench_files(c: &mut Criterion) {
let mut group = c.benchmark_group("es/lints/libs");
Expand Down Expand Up @@ -55,7 +59,12 @@ pub fn bench_files(c: &mut Criterion) {
b.iter(|| {
GLOBALS.set(&globals, || {
HANDLER.set(&handler, || {
run(cm.clone(), program.clone(), unresolved_mark, top_level_mark)
run(
cm.clone(),
&mut program.clone(),
unresolved_mark,
top_level_mark,
)
});
});
});
Expand All @@ -80,7 +89,7 @@ pub fn bench_files(c: &mut Criterion) {
criterion_group!(files, bench_files);
criterion_main!(files);

fn run(cm: Lrc<SourceMap>, program: Program, unresolved_mark: Mark, top_level_mark: Mark) {
fn run(cm: Lrc<SourceMap>, program: &mut Program, unresolved_mark: Mark, top_level_mark: Mark) {
let rules = swc_ecma_lints::rules::all(LintParams {
program: &program,
lint_config: &LintConfig::default(),
Expand All @@ -90,9 +99,9 @@ fn run(cm: Lrc<SourceMap>, program: Program, unresolved_mark: Mark, top_level_ma
source_map: cm.clone(),
});

let mut visitor = black_box(Visitors(rules));
let pass = black_box(lint_pass(rules));

program.visit_with(&mut visitor);
program.mutate(pass)
}

struct Visitors(Vec<Box<dyn Rule>>);
Expand Down
78 changes: 48 additions & 30 deletions crates/swc_ecma_lints/src/rules/const_assign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,61 @@ use swc_common::{collections::AHashMap, errors::HANDLER, Span};
use swc_ecma_ast::*;
use swc_ecma_visit::{noop_visit_type, Visit, VisitWith};

use crate::rule::{visitor_rule, Rule};
use crate::rule::Rule;

pub fn const_assign() -> Box<dyn Rule> {
visitor_rule(ConstAssign::default())
Box::new(ConstAssignRule)
}

#[derive(Debug, Default)]
struct ConstAssign {
const_vars: AHashMap<Id, Span>,
import_binding: AHashMap<Id, Span>,
#[derive(Debug)]
struct ConstAssignRule;

impl Rule for ConstAssignRule {
fn lint_module(&mut self, program: &Module) {
let mut const_vars = AHashMap::default();
let mut import_binding = AHashMap::default();

program.visit_children_with(&mut Collector {
const_vars: &mut const_vars,
import_binding: &mut import_binding,
var_decl_kind: None,
});

program.visit_children_with(&mut ConstAssign {
const_vars: &const_vars,
import_binding: &import_binding,
is_pat_decl: false,
});
}

fn lint_script(&mut self, program: &Script) {
let mut const_vars = AHashMap::default();
let mut import_binding = AHashMap::default();

program.visit_children_with(&mut Collector {
const_vars: &mut const_vars,
// I don't believe that import stmt exists in Script
// But it's ok. Let's pass it in.
import_binding: &mut import_binding,
var_decl_kind: None,
});

program.visit_children_with(&mut ConstAssign {
const_vars: &const_vars,
import_binding: &import_binding,
is_pat_decl: false,
});
}
}

struct ConstAssign<'a> {
const_vars: &'a AHashMap<Id, Span>,
import_binding: &'a AHashMap<Id, Span>,

is_pat_decl: bool,
}

impl ConstAssign {
impl ConstAssign<'_> {
fn check(&mut self, id: &Ident) {
if self.is_pat_decl {
return;
Expand Down Expand Up @@ -51,19 +91,9 @@ impl ConstAssign {
}
}

impl Visit for ConstAssign {
impl Visit for ConstAssign<'_> {
noop_visit_type!();

fn visit_module(&mut self, program: &Module) {
program.visit_children_with(&mut Collector {
const_vars: &mut self.const_vars,
import_binding: &mut self.import_binding,
var_decl_kind: None,
});

program.visit_children_with(self);
}

fn visit_binding_ident(&mut self, n: &BindingIdent) {
self.check(&Ident::from(n));
}
Expand All @@ -76,18 +106,6 @@ impl Visit for ConstAssign {
}
}

fn visit_script(&mut self, program: &Script) {
program.visit_children_with(&mut Collector {
const_vars: &mut self.const_vars,
// I don't believe that import stmt exists in Script
// But it's ok. Let's pass it in.
import_binding: &mut self.import_binding,
var_decl_kind: None,
});

program.visit_children_with(self);
}

fn visit_var_declarator(&mut self, var_declarator: &VarDeclarator) {
let old_is_pat_decl = self.is_pat_decl;
self.is_pat_decl = true;
Expand Down
1 change: 1 addition & 0 deletions crates/swc_ecma_lints/src/rules/duplicate_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@ impl Visit for TypeCollector<'_> {
}
}

#[cold]
fn emit_error(name: &str, span: Span, prev_span: Span) {
HANDLER.with(|handler| {
handler
Expand Down
23 changes: 13 additions & 10 deletions crates/swc_ecma_lints/src/rules/duplicate_exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,6 @@ impl DuplicateExports {
impl Visit for DuplicateExports {
noop_visit_type!();

fn visit_ts_module_decl(&mut self, d: &TsModuleDecl) {
if !d.declare {
let old = mem::take(self);
d.visit_children_with(self);
*self = old;
}
}

fn visit_export_default_decl(&mut self, d: &ExportDefaultDecl) {
if match &d.decl {
DefaultDecl::Fn(FnExpr { function: f, .. }) if f.body.is_none() => true,
Expand Down Expand Up @@ -137,13 +129,24 @@ impl Visit for DuplicateExports {
};
}

/// Noop. Just to ensure that the visitor do not recurse into stmt.
fn visit_stmt(&mut self, _: &Stmt) {}

fn visit_ts_export_assignment(&mut self, n: &TsExportAssignment) {
self.add_export_assign(n.span);
}

fn visit_ts_import_equals_decl(&mut self, n: &TsImportEqualsDecl) {
if n.is_export && !n.is_type_only {
self.add(&n.id)
}
}

fn visit_ts_export_assignment(&mut self, n: &TsExportAssignment) {
self.add_export_assign(n.span);
fn visit_ts_module_decl(&mut self, d: &TsModuleDecl) {
if !d.declare {
let old = mem::take(self);
d.visit_children_with(self);
*self = old;
}
}
}
4 changes: 3 additions & 1 deletion crates/swc_ecma_lints/src/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use std::sync::Arc;
use swc_common::{SourceMap, SyntaxContext};
use swc_ecma_ast::*;

#[deprecated = "Use lint_pass instead"]
pub use self::lint_pass as lint_to_fold;
use crate::{config::LintConfig, rule::Rule};

mod const_assign;
Expand Down Expand Up @@ -216,7 +218,7 @@ pub fn all(lint_params: LintParams) -> Vec<Box<dyn Rule>> {
rules
}

pub fn lint_to_fold<R>(r: R) -> impl Pass
pub fn lint_pass<R>(r: R) -> impl Pass
where
R: Rule,
{
Expand Down
1 change: 1 addition & 0 deletions crates/swc_ecma_lints/src/rules/no_dupe_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub fn no_dupe_args() -> Box<dyn Rule> {
#[derive(Debug, Default)]
struct NoDupeArgs {}

#[cold]
fn error(first: &BindingIdent, second: &BindingIdent) {
HANDLER.with(|handler| {
handler
Expand Down

0 comments on commit d56a473

Please sign in to comment.