diff --git a/crates/oxc_linter/src/rules/eslint/no_const_assign.rs b/crates/oxc_linter/src/rules/eslint/no_const_assign.rs index 072cb7593..9c04e628a 100644 --- a/crates/oxc_linter/src/rules/eslint/no_const_assign.rs +++ b/crates/oxc_linter/src/rules/eslint/no_const_assign.rs @@ -72,6 +72,7 @@ fn test() { ("class X {} X = 1;", None), ("try {} catch (x) { x = 1; }", None), ("const a = 1; { let a = 2; { a += 1; } }", None), + ("const foo = 1;let bar;bar[foo ?? foo] = 42;", None), ]; let fail = vec![ diff --git a/crates/oxc_linter/src/rules/eslint/no_global_assign.rs b/crates/oxc_linter/src/rules/eslint/no_global_assign.rs index 178e94783..0875f9ba5 100644 --- a/crates/oxc_linter/src/rules/eslint/no_global_assign.rs +++ b/crates/oxc_linter/src/rules/eslint/no_global_assign.rs @@ -91,6 +91,7 @@ fn test() { ("top = 0;", None), // ("onload = 0;", None), // env: { browser: true } ("require = 0;", None), + ("window[parseInt('42', 10)] = 99;", None), // ("a = 1", None), // globals: { a: true } }, // ("/*global a:true*/ a = 1", None), ]; diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index 20306ee20..107d943a8 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -1731,12 +1731,15 @@ impl<'a> SemanticBuilder<'a> { self.current_reference_flag |= ReferenceFlag::Write; } AstKind::AssignmentExpression(expr) => { - if self.is_not_expression_statement_parent() - || expr.operator != AssignmentOperator::Assign + if expr.operator != AssignmentOperator::Assign + || self.is_not_expression_statement_parent() { self.current_reference_flag |= ReferenceFlag::Read; } } + AstKind::MemberExpression(_) => { + self.current_reference_flag = ReferenceFlag::Read; + } AstKind::AssignmentTarget(_) => { self.current_reference_flag |= ReferenceFlag::Write; } @@ -1798,8 +1801,8 @@ impl<'a> SemanticBuilder<'a> { self.current_reference_flag -= ReferenceFlag::Write; } AstKind::AssignmentExpression(expr) => { - if self.is_not_expression_statement_parent() - || expr.operator != AssignmentOperator::Assign + if expr.operator != AssignmentOperator::Assign + || self.is_not_expression_statement_parent() { self.current_reference_flag -= ReferenceFlag::Read; } @@ -1834,12 +1837,7 @@ impl<'a> SemanticBuilder<'a> { fn resolve_reference_usages(&self) -> ReferenceFlag { if self.in_type_definition { ReferenceFlag::Type - } else if self.current_reference_flag.is_write() - && !matches!( - self.nodes.parent_kind(self.current_node_id), - Some(AstKind::MemberExpression(_)) - ) - { + } else if self.current_reference_flag.is_write() { self.current_reference_flag } else { ReferenceFlag::Read diff --git a/crates/oxc_semantic/src/lib.rs b/crates/oxc_semantic/src/lib.rs index 0ad0784aa..7ef6b78af 100644 --- a/crates/oxc_semantic/src/lib.rs +++ b/crates/oxc_semantic/src/lib.rs @@ -327,6 +327,19 @@ mod tests { "let a; function foo(a) { return a }; foo(a = 1)", ReferenceFlag::read_write(), ), + // member expression + (SourceType::default(), "let a; a.b = 1", ReferenceFlag::read()), + (SourceType::default(), "let a; let b; b[a += 1] = 1", ReferenceFlag::read_write()), + ( + SourceType::default(), + "let a; let b; let c; b[c[a = c['a']] = 'c'] = 'b'", + ReferenceFlag::read_write(), + ), + ( + SourceType::default(), + "let a; let b; let c; a[c[b = c['a']] = 'c'] = 'b'", + ReferenceFlag::read(), + ), // typescript (typescript, "let a: number = 1; (a as any) = true", ReferenceFlag::write()), (typescript, "let a: number = 1; a = true as any", ReferenceFlag::write()),