diff --git a/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/listener_map.rs b/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/listener_map.rs
index 4739173f6..57fcd85e9 100644
--- a/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/listener_map.rs
+++ b/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/listener_map.rs
@@ -251,11 +251,21 @@ impl<'a> ListenerMap for AstNode<'a> {
AstKind::Class(class) => {
class.report_effects_when_called(options);
}
+ AstKind::ImportDefaultSpecifier(specifier) => {
+ if !has_comment_about_side_effect_check(specifier.span, options.ctx) {
+ options.ctx.diagnostic(NoSideEffectsDiagnostic::CallImport(specifier.span));
+ }
+ }
+ AstKind::ImportSpecifier(specifier) => {
+ let span = specifier.local.span;
+ if !has_comment_about_side_effect_check(span, options.ctx) {
+ options.ctx.diagnostic(NoSideEffectsDiagnostic::CallImport(span));
+ }
+ }
_ => {}
}
}
fn report_effects_when_mutated(&self, options: &NodeListenerOptions) {
- #[allow(clippy::single_match)]
match self.kind() {
AstKind::VariableDeclarator(decl) => {
if let Some(init) = &decl.init {
@@ -272,6 +282,15 @@ impl<'a> ListenerMap for AstNode<'a> {
.ctx
.diagnostic(NoSideEffectsDiagnostic::MutateParameter(Span::new(start, end)));
}
+ AstKind::ImportDefaultSpecifier(specifier) => {
+ options.ctx.diagnostic(NoSideEffectsDiagnostic::MutateImport(specifier.span));
+ }
+ AstKind::ImportSpecifier(specifier) => {
+ options.ctx.diagnostic(NoSideEffectsDiagnostic::MutateImport(specifier.local.span));
+ }
+ AstKind::ImportNamespaceSpecifier(specifier) => {
+ options.ctx.diagnostic(NoSideEffectsDiagnostic::MutateImport(specifier.local.span));
+ }
_ => {}
}
}
@@ -754,6 +773,11 @@ impl<'a> ListenerMap for IdentifierReference<'a> {
}
fn report_effects_when_called(&self, options: &NodeListenerOptions) {
+ // TODO: change to `isPureFunction`
+ if has_pure_notation(self.span, options.ctx) {
+ return;
+ }
+
let ctx = options.ctx;
if let Some(symbol_id) = get_symbol_id_of_variable(self, ctx) {
let symbol_table = ctx.semantic().symbols();
diff --git a/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/mod.rs b/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/mod.rs
index fb930c2a7..3651e9e67 100644
--- a/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/mod.rs
+++ b/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/mod.rs
@@ -38,6 +38,10 @@ enum NoSideEffectsDiagnostic {
#[diagnostic(severity(warning))]
MutateOfThis(#[label] Span),
+ #[error("eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of mutating imported variable")]
+ #[diagnostic(severity(warning))]
+ MutateImport(#[label] Span),
+
#[error("eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling")]
#[diagnostic(severity(warning))]
Call(#[label] Span),
@@ -54,6 +58,10 @@ enum NoSideEffectsDiagnostic {
#[diagnostic(severity(warning))]
CallParameter(#[label] Span),
+ #[error("eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling imported function")]
+ #[diagnostic(severity(warning))]
+ CallImport(#[label] Span),
+
#[error("eslint-plugin-tree-shaking(no-side-effects-in-initialization): Debugger statements are side-effects")]
#[diagnostic(severity(warning))]
Debugger(#[label] Span),
@@ -234,23 +242,24 @@ fn test() {
"const x = ext, y = ()=>{const x = ()=>{}; x()}; y()",
// Identifier when mutated
"const x = {}; x.y = ext",
- // // IfStatement
+ // IfStatement
"let y;if (ext > 0) {y = 1} else {y = 2}",
"if (false) {ext()}",
"if (true) {} else {ext()}",
- // // ImportDeclaration
- // r#"import "import""#,
- // r#"import x from "import-default""#,
- // r#"import {x} from "import""#,
- // r#"import {x as y} from "import""#,
- // r#"import * as x from "import""#,
- // r#"import /* tree-shaking no-side-effects-when-called */ x from "import-default-no-effects"; x()"#,
- // r#"import /* test */ /*tree-shaking no-side-effects-when-called */ x from "import-default-no-effects"; x()"#,
+ // ImportDeclaration
+ r#"import "import""#,
+ r#"import x from "import-default""#,
+ r#"import {x} from "import""#,
+ r#"import {x as y} from "import""#,
+ r#"import * as x from "import""#,
+ r#"import /* tree-shaking no-side-effects-when-called */ x from "import-default-no-effects"; x()"#,
+ r#"import /* test */ /*tree-shaking no-side-effects-when-called */ x from "import-default-no-effects"; x()"#,
+ // TODO: Current only support the comment next to code.
// r#"import /* tree-shaking no-side-effects-when-called*/ /* test */ x from "import-default-no-effects"; x()"#,
- // r#"import {/* tree-shaking no-side-effects-when-called */ x} from "import-no-effects"; x()"#,
- // r#"import {x as /* tree-shaking no-side-effects-when-called */ y} from "import-no-effects"; y()"#,
- // r#"import {x} from "import"; /*@__PURE__*/ x()"#,
- // r#"import {x} from "import"; /* @__PURE__ */ x()"#,
+ r#"import {/* tree-shaking no-side-effects-when-called */ x} from "import-no-effects"; x()"#,
+ r#"import {x as /* tree-shaking no-side-effects-when-called */ y} from "import-no-effects"; y()"#,
+ r#"import {x} from "import"; /*@__PURE__*/ x()"#,
+ r#"import {x} from "import"; /* @__PURE__ */ x()"#,
// // JSXAttribute
// r#"class X {}; const x = "#,
// "class X {}; const x = ",
@@ -532,15 +541,15 @@ fn test() {
"if (1>0){ext()}",
"if (1<0){} else {ext()}",
"if (ext>0){ext()} else {ext()}",
- // // ImportDeclaration
- // r#"import x from "import-default"; x()"#,
- // r#"import x from "import-default"; x.z = 1"#,
- // r#"import {x} from "import"; x()"#,
- // r#"import {x} from "import"; x.z = 1"#,
- // r#"import {x as y} from "import"; y()"#,
- // r#"import {x as y} from "import"; y.a = 1"#,
- // r#"import * as y from "import"; y.x()"#,
- // r#"import * as y from "import"; y.x = 1"#,
+ // ImportDeclaration
+ r#"import x from "import-default"; x()"#,
+ r#"import x from "import-default"; x.z = 1"#,
+ r#"import {x} from "import"; x()"#,
+ r#"import {x} from "import"; x.z = 1"#,
+ r#"import {x as y} from "import"; y()"#,
+ r#"import {x as y} from "import"; y.a = 1"#,
+ r#"import * as y from "import"; y.x()"#,
+ r#"import * as y from "import"; y.x = 1"#,
// // JSXAttribute
// "class X {}; const x = ",
// "class X {}; class Y {constructor(){ext()}}; const x = />",
diff --git a/crates/oxc_linter/src/snapshots/no_side_effects_in_initialization.snap b/crates/oxc_linter/src/snapshots/no_side_effects_in_initialization.snap
index 84f006f27..f55643a0f 100644
--- a/crates/oxc_linter/src/snapshots/no_side_effects_in_initialization.snap
+++ b/crates/oxc_linter/src/snapshots/no_side_effects_in_initialization.snap
@@ -764,6 +764,54 @@ expression: no_side_effects_in_initialization
· ───
╰────
+ ⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling imported function
+ ╭─[no_side_effects_in_initialization.tsx:1:8]
+ 1 │ import x from "import-default"; x()
+ · ─
+ ╰────
+
+ ⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of mutating imported variable
+ ╭─[no_side_effects_in_initialization.tsx:1:8]
+ 1 │ import x from "import-default"; x.z = 1
+ · ─
+ ╰────
+
+ ⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling imported function
+ ╭─[no_side_effects_in_initialization.tsx:1:9]
+ 1 │ import {x} from "import"; x()
+ · ─
+ ╰────
+
+ ⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of mutating imported variable
+ ╭─[no_side_effects_in_initialization.tsx:1:9]
+ 1 │ import {x} from "import"; x.z = 1
+ · ─
+ ╰────
+
+ ⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling imported function
+ ╭─[no_side_effects_in_initialization.tsx:1:14]
+ 1 │ import {x as y} from "import"; y()
+ · ─
+ ╰────
+
+ ⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of mutating imported variable
+ ╭─[no_side_effects_in_initialization.tsx:1:14]
+ 1 │ import {x as y} from "import"; y.a = 1
+ · ─
+ ╰────
+
+ ⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling
+ ╭─[no_side_effects_in_initialization.tsx:1:30]
+ 1 │ import * as y from "import"; y.x()
+ · ───
+ ╰────
+
+ ⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of mutating imported variable
+ ╭─[no_side_effects_in_initialization.tsx:1:13]
+ 1 │ import * as y from "import"; y.x = 1
+ · ─
+ ╰────
+
⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling global function `ext`
╭─[no_side_effects_in_initialization.tsx:1:15]
1 │ const x = new ext()