mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-15 14:49:36 +00:00
feat(ast)!: remove duplicated VariableDeclarator::kind (#25319)
## Summary
- Removes `VariableDeclarator::kind`.
- Makes `VariableDeclaration::kind` the single source of truth for every declarator in its declaration list.
- Makes contradictory parent and child declaration kinds unrepresentable.
## Breaking Change
The Rust AST changes from:
```rust
pub struct VariableDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub kind: VariableDeclarationKind,
pub declarations: Vec<'a, VariableDeclarator<'a>>,
#[ts]
pub declare: bool,
}
pub struct VariableDeclarator<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
#[estree(skip)]
pub kind: VariableDeclarationKind,
#[estree(via = VariableDeclaratorId)]
pub id: BindingPattern<'a>,
#[ts]
#[estree(skip)]
pub type_annotation: Option<Box<'a, TSTypeAnnotation<'a>>>,
pub init: Option<Expression<'a>>,
#[ts]
pub definite: bool,
}
```
to:
```rust
pub struct VariableDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub kind: VariableDeclarationKind,
pub declarations: Vec<'a, VariableDeclarator<'a>>,
#[ts]
pub declare: bool,
}
pub struct VariableDeclarator<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
#[estree(via = VariableDeclaratorId)]
pub id: BindingPattern<'a>,
#[ts]
#[estree(skip)]
pub type_annotation: Option<Box<'a, TSTypeAnnotation<'a>>>,
pub init: Option<Expression<'a>>,
#[ts]
pub definite: bool,
}
```
## Why?
A variable declaration has one declaration kind shared by every declarator in its declaration list:
```js
const first = 1, second = 2;
```
Previously, the AST stored that kind on both the parent declaration and every child declarator:
```text
VariableDeclaration {
kind: Const,
declarations: [
VariableDeclarator { kind: Const, ... },
VariableDeclarator { kind: Const, ... },
],
}
```
This allowed contradictory ASTs to be constructed:
```text
VariableDeclaration {
kind: Const,
declarations: [
VariableDeclarator { kind: Var, ... },
],
}
```
Different consumers read different copies:
- Code generation used `VariableDeclaration::kind`.
- Semantic binding used `VariableDeclarator::kind`.
- Lint rules, transforms, and minifier passes used a mixture of both.
As a result, one AST could be emitted as `const` while its binding was treated as function-scoped `var`.
Transforms also had to keep the copies synchronized manually. For example, const-to-let normalization updated the parent and then looped through every declarator to update each child. Explicit resource management performed similar paired mutations for `using` declarations.
Keeping the kind only on `VariableDeclaration` removes this synchronization requirement and prevents consumers from observing conflicting declaration semantics.
## Declaration forms
The declaration kind now belongs exclusively to the node that owns the declaration list:
```js
let value;
```
```text
VariableDeclaration {
kind: Let,
declarations: [
VariableDeclarator { id: value, ... },
],
}
```
Multiple declarators share the same parent kind:
```js
const first = 1, second = 2;
```
```text
VariableDeclaration {
kind: Const,
declarations: [
VariableDeclarator { id: first, ... },
VariableDeclarator { id: second, ... },
],
}
```
## How to migrate
Code that previously read the kind from a declarator:
```rust
if declarator.kind.is_const() {
// ...
}
```
should now read it from the containing declaration:
```rust
if declaration.kind.is_const() {
// ...
}
```
Consumers operating through an AST node store should retrieve the parent `VariableDeclaration`:
```rust
let AstKind::VariableDeclaration(declaration) =
nodes.parent_kind(declarator.node_id())
else {
unreachable!();
};
let kind = declaration.kind;
```
Common migrations:
| Previous representation | New representation |
| --- | --- |
| `declarator.kind` | Containing `declaration.kind` |
| Match `VariableDeclarator::kind` during semantic binding | Read the parent `VariableDeclaration` from ancestry |
| Pass `kind` to `VariableDeclarator::new` | Remove the `kind` argument |
| Update parent and every child kind | Update only `VariableDeclaration::kind` |
| Recover kind after detaching a declarator | Capture or pass the parent kind before detaching it |
| Inspect a symbol’s declarator kind in lint rules | Resolve its parent declaration through the node store |
When constructing declarators, use the generated kindless `VariableDeclarator` builders and place them inside a `VariableDeclaration` carrying the required kind.
fixes https://github.com/oxc-project/backlog/issues/237
This commit is contained in:
1 parent
4abff11c80
commit
6be314f226
73 files changed
+228
-326
No files matched your search
@@ -1650,7 +1650,7 @@ function deserializeVariableDeclarator(pos) {
|
||||
type: "VariableDeclarator",
|
||||
id: null,
|
||||
init: null,
|
||||
definite: deserializeBool(pos + 13),
|
||||
definite: deserializeBool(pos + 12),
|
||||
start: (start = deserializeI32(pos)),
|
||||
end: (end = deserializeI32(pos + 4)),
|
||||
range: [start, end],
|
||||
|
||||
@@ -1216,8 +1216,6 @@ pub enum VariableDeclarationKind {
|
||||
pub struct VariableDeclarator<'a> {
|
||||
pub node_id: Cell<NodeId>,
|
||||
pub span: Span,
|
||||
#[estree(skip)]
|
||||
pub kind: VariableDeclarationKind,
|
||||
#[estree(via = VariableDeclaratorId)]
|
||||
pub id: BindingPattern<'a>,
|
||||
#[ts]
|
||||
|
||||
@@ -412,13 +412,12 @@ const _: () = {
|
||||
assert!(size_of::<VariableDeclarationKind>() == 1);
|
||||
assert!(align_of::<VariableDeclarationKind>() == 1);
|
||||
|
||||
// Padding: 2 bytes
|
||||
// Padding: 3 bytes
|
||||
assert!(size_of::<VariableDeclarator>() == 56);
|
||||
assert!(align_of::<VariableDeclarator>() == 8);
|
||||
assert!(offset_of!(VariableDeclarator, span) == 0);
|
||||
assert!(offset_of!(VariableDeclarator, node_id) == 8);
|
||||
assert!(offset_of!(VariableDeclarator, kind) == 12);
|
||||
assert!(offset_of!(VariableDeclarator, definite) == 13);
|
||||
assert!(offset_of!(VariableDeclarator, definite) == 12);
|
||||
assert!(offset_of!(VariableDeclarator, id) == 16);
|
||||
assert!(offset_of!(VariableDeclarator, type_annotation) == 32);
|
||||
assert!(offset_of!(VariableDeclarator, init) == 40);
|
||||
@@ -2250,13 +2249,12 @@ const _: () = if cfg!(target_family = "wasm") || align_of::<u64>() == 8 {
|
||||
assert!(size_of::<VariableDeclarationKind>() == 1);
|
||||
assert!(align_of::<VariableDeclarationKind>() == 1);
|
||||
|
||||
// Padding: 2 bytes
|
||||
// Padding: 3 bytes
|
||||
assert!(size_of::<VariableDeclarator>() == 36);
|
||||
assert!(align_of::<VariableDeclarator>() == 4);
|
||||
assert!(offset_of!(VariableDeclarator, span) == 0);
|
||||
assert!(offset_of!(VariableDeclarator, node_id) == 8);
|
||||
assert!(offset_of!(VariableDeclarator, kind) == 12);
|
||||
assert!(offset_of!(VariableDeclarator, definite) == 13);
|
||||
assert!(offset_of!(VariableDeclarator, definite) == 12);
|
||||
assert!(offset_of!(VariableDeclarator, id) == 16);
|
||||
assert!(offset_of!(VariableDeclarator, type_annotation) == 24);
|
||||
assert!(offset_of!(VariableDeclarator, init) == 28);
|
||||
|
||||
@@ -10008,7 +10008,6 @@ impl<'a> VariableDeclarator<'a> {
|
||||
///
|
||||
/// ## Parameters
|
||||
/// * `span`: The [`Span`] covering this node
|
||||
/// * `kind`
|
||||
/// * `id`
|
||||
/// * `type_annotation`
|
||||
/// * `init`
|
||||
@@ -10016,7 +10015,6 @@ impl<'a> VariableDeclarator<'a> {
|
||||
#[inline]
|
||||
pub fn new(
|
||||
span: Span,
|
||||
kind: VariableDeclarationKind,
|
||||
id: BindingPattern<'a>,
|
||||
type_annotation: Option<ArenaBox<'a, TSTypeAnnotation<'a>>>,
|
||||
init: Option<Expression<'a>>,
|
||||
@@ -10027,7 +10025,6 @@ impl<'a> VariableDeclarator<'a> {
|
||||
VariableDeclarator {
|
||||
node_id: Cell::new(builder.node_id()),
|
||||
span,
|
||||
kind,
|
||||
id,
|
||||
type_annotation,
|
||||
init,
|
||||
|
||||
@@ -1570,7 +1570,6 @@ impl<'new_alloc> CloneIn<'new_alloc> for VariableDeclarator<'_> {
|
||||
VariableDeclarator {
|
||||
node_id: CloneIn::clone_in_impl(&self.node_id, with_semantic_ids, allocator),
|
||||
span: CloneIn::clone_in_impl(&self.span, with_semantic_ids, allocator),
|
||||
kind: CloneIn::clone_in_impl(&self.kind, with_semantic_ids, allocator),
|
||||
id: CloneIn::clone_in_impl(&self.id, with_semantic_ids, allocator),
|
||||
type_annotation: CloneIn::clone_in_impl(
|
||||
&self.type_annotation,
|
||||
|
||||
@@ -779,8 +779,7 @@ impl ContentEq for VariableDeclarationKind {
|
||||
|
||||
impl ContentEq for VariableDeclarator<'_> {
|
||||
fn content_eq(&self, other: &Self) -> bool {
|
||||
ContentEq::content_eq(&self.kind, &other.kind)
|
||||
&& ContentEq::content_eq(&self.id, &other.id)
|
||||
ContentEq::content_eq(&self.id, &other.id)
|
||||
&& ContentEq::content_eq(&self.type_annotation, &other.type_annotation)
|
||||
&& ContentEq::content_eq(&self.init, &other.init)
|
||||
&& ContentEq::content_eq(&self.definite, &other.definite)
|
||||
|
||||
@@ -767,7 +767,6 @@ impl<'a> Dummy<'a> for VariableDeclarator<'a> {
|
||||
Self {
|
||||
node_id: Dummy::dummy(allocator),
|
||||
span: Dummy::dummy(allocator),
|
||||
kind: Dummy::dummy(allocator),
|
||||
id: Dummy::dummy(allocator),
|
||||
type_annotation: Dummy::dummy(allocator),
|
||||
init: Dummy::dummy(allocator),
|
||||
|
||||
@@ -247,7 +247,7 @@ pub static STRUCTS: phf::Map<&'static str, StructDetails> = ::phf::Map {
|
||||
(
|
||||
"VariableDeclarator",
|
||||
StructDetails {
|
||||
field_order: Some(&[1, 0, 2, 4, 5, 6, 3]),
|
||||
field_order: Some(&[1, 0, 3, 4, 5, 2]),
|
||||
is_node: true,
|
||||
is_transparent: false,
|
||||
},
|
||||
|
||||
@@ -3326,11 +3326,6 @@ impl<'a> AstNode<'a, VariableDeclarator<'a>> {
|
||||
self.inner.node_id()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn kind(&self) -> VariableDeclarationKind {
|
||||
self.inner.kind
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> &AstNode<'a, BindingPattern<'a>> {
|
||||
let following_span_start = self
|
||||
|
||||
@@ -27,7 +27,7 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||
} else {
|
||||
let declarations = ArenaVec::from_iter_in(
|
||||
decl.declarations.iter().filter_map(|declarator| {
|
||||
self.transform_variable_declarator(declarator, check_binding)
|
||||
self.transform_variable_declarator(declarator, decl.kind, check_binding)
|
||||
}),
|
||||
self,
|
||||
);
|
||||
@@ -46,6 +46,7 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||
pub(crate) fn transform_variable_declarator(
|
||||
&self,
|
||||
decl: &VariableDeclarator<'a>,
|
||||
kind: VariableDeclarationKind,
|
||||
check_binding: bool,
|
||||
) -> Option<VariableDeclarator<'a>> {
|
||||
if decl.id.is_destructuring_pattern() {
|
||||
@@ -69,16 +70,14 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||
if decl.type_annotation.is_none() {
|
||||
if let Some(init_expr) = &decl.init {
|
||||
// if kind is const and it doesn't need to infer type from expression
|
||||
if decl.kind.is_const() && !Self::is_need_to_infer_type_from_expression(init_expr) {
|
||||
if kind.is_const() && !Self::is_need_to_infer_type_from_expression(init_expr) {
|
||||
if let Expression::TemplateLiteral(lit) = init_expr {
|
||||
init =
|
||||
self.transform_template_to_string(lit).map(Expression::StringLiteral);
|
||||
} else {
|
||||
init = Some(init_expr.clone_in(self.allocator()));
|
||||
}
|
||||
} else if !decl.kind.is_const()
|
||||
|| !matches!(init_expr, Expression::TemplateLiteral(_))
|
||||
{
|
||||
} else if !kind.is_const() || !matches!(init_expr, Expression::TemplateLiteral(_)) {
|
||||
// otherwise, we need to infer type from expression
|
||||
binding_type = self.infer_type_from_expression(init_expr);
|
||||
}
|
||||
@@ -101,15 +100,7 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||
let type_annotation =
|
||||
binding_type.map(|ts_type| TSTypeAnnotation::boxed(SPAN, ts_type, self));
|
||||
|
||||
Some(VariableDeclarator::new(
|
||||
decl.span,
|
||||
decl.kind,
|
||||
id,
|
||||
type_annotation,
|
||||
init,
|
||||
decl.definite,
|
||||
self,
|
||||
))
|
||||
Some(VariableDeclarator::new(decl.span, id, type_annotation, init, decl.definite, self))
|
||||
}
|
||||
|
||||
fn transform_ts_module_block(
|
||||
|
||||
@@ -348,7 +348,7 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||
}
|
||||
|
||||
if let Some(new_declarator) =
|
||||
self.transform_variable_declarator(declarator, true)
|
||||
self.transform_variable_declarator(declarator, declaration.kind, true)
|
||||
{
|
||||
self.scope.visit_variable_declarator(&new_declarator);
|
||||
transformed_variable_declarator.insert(declarator.span, new_declarator);
|
||||
|
||||
@@ -125,8 +125,7 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||
self.error(default_export_inferred(expr.span()));
|
||||
}
|
||||
|
||||
let declaration =
|
||||
VariableDeclarator::new(SPAN, kind, id, type_annotation, None, false, self);
|
||||
let declaration = VariableDeclarator::new(SPAN, id, type_annotation, None, false, self);
|
||||
|
||||
let variable_statement = Statement::new_variable_declaration(
|
||||
decl_span,
|
||||
|
||||
@@ -22,6 +22,18 @@ use crate::{
|
||||
utils::{get_function_nearest_jsdoc_node, is_regexp_callee},
|
||||
};
|
||||
|
||||
/// Get the declaration kind for a variable declarator from its parent declaration.
|
||||
pub fn variable_declaration_kind(
|
||||
declarator: &VariableDeclarator<'_>,
|
||||
ctx: &LintContext<'_>,
|
||||
) -> VariableDeclarationKind {
|
||||
let AstKind::VariableDeclaration(declaration) = ctx.nodes().parent_kind(declarator.node_id())
|
||||
else {
|
||||
unreachable!();
|
||||
};
|
||||
declaration.kind
|
||||
}
|
||||
|
||||
/// Test if an AST node is a boolean value that never changes. Specifically we
|
||||
/// test for:
|
||||
/// 1. Literal booleans (`true` or `false`)
|
||||
|
||||
@@ -139,6 +139,7 @@ impl Rule for InitDeclarations {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let declaration_is_const = decl.kind == VariableDeclarationKind::Const;
|
||||
for v in &decl.declarations {
|
||||
let BindingPattern::BindingIdentifier(identifier) = &v.id else {
|
||||
continue;
|
||||
@@ -166,7 +167,7 @@ impl Rule for InitDeclarations {
|
||||
));
|
||||
}
|
||||
AlwaysNever::Never if is_initialized && !config.ignore_for_loop_init => {
|
||||
if matches!(&v.kind, VariableDeclarationKind::Const) {
|
||||
if declaration_is_const {
|
||||
continue;
|
||||
}
|
||||
ctx.diagnostic(init_declarations_diagnostic(
|
||||
|
||||
@@ -5,7 +5,7 @@ use oxc_macros::declare_oxc_lint;
|
||||
use oxc_semantic::{AstNode, SymbolId};
|
||||
use oxc_span::Span;
|
||||
|
||||
use crate::{context::LintContext, rule::Rule};
|
||||
use crate::{ast_util::variable_declaration_kind, context::LintContext, rule::Rule};
|
||||
|
||||
fn no_const_assign_diagnostic(name: &str, decl_span: Span, assign_span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn(format!("Unexpected re-assignment of `const` variable {name}."))
|
||||
@@ -60,11 +60,10 @@ declare_oxc_lint!(
|
||||
|
||||
impl Rule for NoConstAssign {
|
||||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
match node.kind() {
|
||||
AstKind::VariableDeclarator(decl) if decl.kind.is_const() || decl.kind.is_using() => {
|
||||
decl.id.bound_names(&mut |ident| check_symbol_id(ident.symbol_id(), ctx));
|
||||
}
|
||||
_ => {}
|
||||
let AstKind::VariableDeclarator(decl) = node.kind() else { return };
|
||||
let kind = variable_declaration_kind(decl, ctx);
|
||||
if kind.is_const() || kind.is_using() {
|
||||
decl.id.bound_names(&mut |ident| check_symbol_id(ident.symbol_id(), ctx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use oxc_syntax::operator::UnaryOperator;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{AstNode, context::LintContext, rule::Rule};
|
||||
use crate::{AstNode, ast_util::variable_declaration_kind, context::LintContext, rule::Rule};
|
||||
|
||||
enum NoMagicNumberReportReason {
|
||||
MustUseConst,
|
||||
@@ -325,7 +325,7 @@ impl Rule for NoMagicNumbers {
|
||||
let parent_kind = nodes.parent_kind(config.node.id());
|
||||
let span = config.node.kind().span();
|
||||
|
||||
let Some(reason) = self.get_report_reason(&parent_kind) else {
|
||||
let Some(reason) = self.get_report_reason(&parent_kind, ctx) else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -517,11 +517,14 @@ impl NoMagicNumbers {
|
||||
|
||||
fn get_report_reason<'a>(
|
||||
&self,
|
||||
parent_kind: &'a AstKind<'a>,
|
||||
parent_kind: &AstKind<'a>,
|
||||
ctx: &LintContext<'a>,
|
||||
) -> Option<NoMagicNumberReportReason> {
|
||||
match parent_kind {
|
||||
AstKind::VariableDeclarator(declarator) => {
|
||||
if self.enforce_const && declarator.kind != VariableDeclarationKind::Const {
|
||||
if self.enforce_const
|
||||
&& variable_declaration_kind(declarator, ctx) != VariableDeclarationKind::Const
|
||||
{
|
||||
return Some(NoMagicNumberReportReason::MustUseConst);
|
||||
}
|
||||
|
||||
|
||||
@@ -59,14 +59,14 @@ impl Rule for NoUnassignedVars {
|
||||
let AstKind::VariableDeclarator(declarator) = node.kind() else {
|
||||
return;
|
||||
};
|
||||
if declarator.init.is_some() || declarator.kind.is_const() {
|
||||
if declarator.init.is_some() {
|
||||
return;
|
||||
}
|
||||
let parent_node = ctx.nodes().parent_node(node.id());
|
||||
let AstKind::VariableDeclaration(parent) = parent_node.kind() else {
|
||||
return;
|
||||
};
|
||||
if parent.declare {
|
||||
if parent.kind.is_const() || parent.declare {
|
||||
return;
|
||||
}
|
||||
let grand_parent = ctx.nodes().parent_node(parent_node.id());
|
||||
|
||||
@@ -15,6 +15,7 @@ use oxc_span::Span;
|
||||
|
||||
use crate::{
|
||||
AstNode,
|
||||
ast_util::variable_declaration_kind,
|
||||
context::LintContext,
|
||||
rule::{DefaultRuleConfig, Rule},
|
||||
};
|
||||
@@ -243,7 +244,7 @@ fn binding_context(ctx: &LintContext, id: NodeId) -> BindingContext {
|
||||
};
|
||||
}
|
||||
AstKind::VariableDeclarator(declarator) => {
|
||||
return if declarator.kind.is_using() {
|
||||
return if variable_declaration_kind(declarator, ctx).is_using() {
|
||||
BindingContext::Using
|
||||
} else {
|
||||
destructure_context.unwrap_or(BindingContext::Plain)
|
||||
|
||||
@@ -5,7 +5,8 @@ use oxc_semantic::{NodeId, Semantic};
|
||||
|
||||
use super::{NoUnusedVars, Symbol, options::ArgsOption};
|
||||
use crate::{
|
||||
ModuleRecord,
|
||||
LintContext, ModuleRecord,
|
||||
ast_util::variable_declaration_kind,
|
||||
rules::eslint::no_unused_vars::binding_pattern::{BindingContext, HasAnyUsedBinding},
|
||||
};
|
||||
|
||||
@@ -165,8 +166,10 @@ impl NoUnusedVars {
|
||||
&self,
|
||||
symbol: &Symbol<'_, 'a>,
|
||||
decl: &VariableDeclarator<'a>,
|
||||
ctx: &LintContext<'a>,
|
||||
) -> bool {
|
||||
if decl.kind.is_var() && self.vars.is_local() && symbol.is_root() {
|
||||
if variable_declaration_kind(decl, ctx).is_var() && self.vars.is_local() && symbol.is_root()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -175,7 +178,7 @@ impl NoUnusedVars {
|
||||
return true;
|
||||
}
|
||||
|
||||
if self.ignore_using_declarations && decl.kind.is_using() {
|
||||
if self.ignore_using_declarations && variable_declaration_kind(decl, ctx).is_using() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -302,7 +302,7 @@ impl NoUnusedVars {
|
||||
}
|
||||
}
|
||||
AstKind::VariableDeclarator(decl) => {
|
||||
if self.is_allowed_variable_declaration(symbol, decl) {
|
||||
if self.is_allowed_variable_declaration(symbol, decl, ctx) {
|
||||
return;
|
||||
}
|
||||
let report = match symbol.references().rev().find(|r| r.is_write()) {
|
||||
|
||||
@@ -12,6 +12,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
AstNode,
|
||||
ast_util::variable_declaration_kind,
|
||||
context::LintContext,
|
||||
rule::{Rule, TupleRuleConfig},
|
||||
};
|
||||
@@ -255,7 +256,7 @@ impl Rule for PreferDestructuring {
|
||||
|
||||
// Skip `using` and `await using` declarations - destructuring doesn't apply to them
|
||||
if matches!(
|
||||
declarator.kind,
|
||||
variable_declaration_kind(declarator, ctx),
|
||||
VariableDeclarationKind::Using | VariableDeclarationKind::AwaitUsing
|
||||
) {
|
||||
return;
|
||||
|
||||
@@ -14,6 +14,7 @@ use oxc_syntax::operator::{AssignmentOperator, BinaryOperator};
|
||||
use crate::{
|
||||
AstNode,
|
||||
ast_util::get_declaration_of_variable,
|
||||
ast_util::variable_declaration_kind,
|
||||
context::LintContext,
|
||||
rule::{DefaultRuleConfig, Rule},
|
||||
utils::is_regexp_callee,
|
||||
@@ -326,7 +327,7 @@ fn resolve_const_initializer<'a>(
|
||||
let declaration = get_declaration_of_variable(ident, ctx.semantic())?;
|
||||
let AstKind::VariableDeclarator(decl) = declaration.kind() else { return None };
|
||||
|
||||
if !decl.kind.is_const() || !decl.id.is_binding_identifier() {
|
||||
if !variable_declaration_kind(decl, ctx).is_const() || !decl.id.is_binding_identifier() {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,10 @@ use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::{AstNode, ast_util::is_method_call, context::LintContext, rule::Rule};
|
||||
use crate::{
|
||||
AstNode, ast_util::is_method_call, ast_util::variable_declaration_kind, context::LintContext,
|
||||
rule::Rule,
|
||||
};
|
||||
|
||||
fn bad_char_at_comparison_diagnostic(
|
||||
character_access: Span,
|
||||
@@ -140,13 +143,15 @@ fn is_definitely_string(expr: &Expression, ctx: &LintContext) -> bool {
|
||||
let declaration =
|
||||
ctx.nodes().get_node(ctx.scoping().symbol_declaration(symbol_id)).kind();
|
||||
|
||||
declaration.as_variable_declarator().is_some_and(is_definitely_string_declarator)
|
||||
declaration
|
||||
.as_variable_declarator()
|
||||
.is_some_and(|declarator| is_definitely_string_declarator(declarator, ctx))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_definitely_string_declarator(declarator: &VariableDeclarator) -> bool {
|
||||
fn is_definitely_string_declarator(declarator: &VariableDeclarator, ctx: &LintContext) -> bool {
|
||||
if declarator
|
||||
.type_annotation
|
||||
.as_ref()
|
||||
@@ -155,7 +160,9 @@ fn is_definitely_string_declarator(declarator: &VariableDeclarator) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
if !declarator.kind.is_const() || !declarator.id.is_binding_identifier() {
|
||||
if !variable_declaration_kind(declarator, ctx).is_const()
|
||||
|| !declarator.id.is_binding_identifier()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ use oxc_syntax::scope::ScopeFlags;
|
||||
|
||||
use crate::{
|
||||
AstNode,
|
||||
ast_util::variable_declaration_kind,
|
||||
ast_util::{
|
||||
get_declaration_from_reference_id, get_declaration_of_variable, get_enclosing_function,
|
||||
},
|
||||
@@ -1130,7 +1131,7 @@ fn is_stable_value<'a, 'b>(
|
||||
}
|
||||
|
||||
// if the variables is a constant, and the initializer is a literal, then it's a stable value. (excluding regex literals)
|
||||
if declaration.kind == VariableDeclarationKind::Const
|
||||
if variable_declaration_kind(declaration, ctx) == VariableDeclarationKind::Const
|
||||
&& (matches!(
|
||||
init.get_inner_expression(),
|
||||
Expression::BooleanLiteral(_)
|
||||
|
||||
@@ -7,7 +7,7 @@ use oxc_macros::declare_oxc_lint;
|
||||
use oxc_semantic::AstNode;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::{context::LintContext, rule::Rule};
|
||||
use crate::{ast_util::variable_declaration_kind, context::LintContext, rule::Rule};
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct ConsistentExistenceIndexCheck;
|
||||
@@ -97,7 +97,9 @@ impl Rule for ConsistentExistenceIndexCheck {
|
||||
let node = ctx.nodes().get_node(declaration_node_id);
|
||||
|
||||
if let AstKind::VariableDeclarator(variables_declarator) = node.kind() {
|
||||
if variables_declarator.kind != VariableDeclarationKind::Const {
|
||||
if variable_declaration_kind(variables_declarator, ctx)
|
||||
!= VariableDeclarationKind::Const
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::{
|
||||
AstNode,
|
||||
ast_util::variable_declaration_kind,
|
||||
ast_util::{get_declaration_of_variable, is_method_call},
|
||||
context::LintContext,
|
||||
rule::Rule,
|
||||
@@ -133,7 +134,9 @@ fn get_const_variable_initializer<'a>(
|
||||
return None;
|
||||
};
|
||||
|
||||
if !decl.kind.is_const() || !is_same_binding_identifier(&decl.id, ident) {
|
||||
if !variable_declaration_kind(decl, ctx).is_const()
|
||||
|| !is_same_binding_identifier(&decl.id, ident)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
@@ -227,10 +227,11 @@ impl Rule for NoUselessUndefined {
|
||||
AstKind::VariableDeclarator(variable_declarator) => {
|
||||
let grand_parent_node = ctx.nodes().parent_node(parent_node.id());
|
||||
let grand_parent_node_kind = grand_parent_node.kind();
|
||||
let AstKind::VariableDeclaration(_) = grand_parent_node_kind else {
|
||||
let AstKind::VariableDeclaration(declaration) = grand_parent_node_kind
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if variable_declarator.kind == VariableDeclarationKind::Const {
|
||||
if declaration.kind == VariableDeclarationKind::Const {
|
||||
return;
|
||||
}
|
||||
if is_has_function_return_type(parent_node, ctx) {
|
||||
|
||||
@@ -11,6 +11,7 @@ use oxc_span::Span;
|
||||
|
||||
use crate::{
|
||||
AstNode,
|
||||
ast_util::variable_declaration_kind,
|
||||
ast_util::{get_symbol_id_of_variable, is_method_call},
|
||||
context::LintContext,
|
||||
rule::Rule,
|
||||
@@ -160,7 +161,7 @@ fn const_variable_initializer_kind(
|
||||
let AstKind::VariableDeclarator(declarator) = node.kind() else {
|
||||
return None;
|
||||
};
|
||||
if !declarator.kind.is_const() {
|
||||
if !variable_declaration_kind(declarator, ctx).is_const() {
|
||||
return None;
|
||||
}
|
||||
let init = declarator.init.as_ref()?.get_inner_expression();
|
||||
|
||||
@@ -17,6 +17,7 @@ use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::{
|
||||
AstNode,
|
||||
ast_util::variable_declaration_kind,
|
||||
context::LintContext,
|
||||
fixer::{RuleFix, RuleFixer},
|
||||
rule::Rule,
|
||||
@@ -583,7 +584,7 @@ fn is_obviously_non_array_receiver(expr: &Expression, ctx: &LintContext) -> bool
|
||||
let AstKind::VariableDeclarator(declarator) = ctx.symbol_declaration(symbol_id).kind() else {
|
||||
return false;
|
||||
};
|
||||
if declarator.kind != VariableDeclarationKind::Const {
|
||||
if variable_declaration_kind(declarator, ctx) != VariableDeclarationKind::Const {
|
||||
return false;
|
||||
}
|
||||
declarator
|
||||
|
||||
@@ -11,7 +11,7 @@ use oxc_syntax::node::NodeId;
|
||||
use oxc_syntax::symbol::SymbolId;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{context::LintContext, rule::Rule};
|
||||
use crate::{ast_util::variable_declaration_kind, context::LintContext, rule::Rule};
|
||||
|
||||
const PATH_MODULES: [&str; 2] = ["path", "node:path"];
|
||||
const URL_MODULES: [&str; 2] = ["url", "node:url"];
|
||||
@@ -329,7 +329,7 @@ fn check_variable_declarator(
|
||||
ctx: &LintContext<'_>,
|
||||
visited: &mut FxHashSet<SymbolId>,
|
||||
) -> bool {
|
||||
if !variable_declarator.kind.is_const() {
|
||||
if !variable_declaration_kind(variable_declarator, ctx).is_const() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -392,7 +392,9 @@ fn iterate_problems_from_filename(
|
||||
}
|
||||
|
||||
let AstKind::VariableDeclarator(parent) = ctx.nodes().kind(parent_id) else { return };
|
||||
if !parent.kind.is_const() || parent.init.as_ref().is_none_or(|init| init.span() != node_span) {
|
||||
if !variable_declaration_kind(parent, ctx).is_const()
|
||||
|| parent.init.as_ref().is_none_or(|init| init.span() != node_span)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,10 @@ use oxc_macros::declare_oxc_lint;
|
||||
use oxc_semantic::ScopeId;
|
||||
use oxc_span::Span;
|
||||
|
||||
use crate::{AstNode, ast_util::is_method_call, context::LintContext, rule::Rule};
|
||||
use crate::{
|
||||
AstNode, ast_util::is_method_call, ast_util::variable_declaration_kind, context::LintContext,
|
||||
rule::Rule,
|
||||
};
|
||||
|
||||
const ARRAY_METHODS_RETURNS_ARRAY: [&str; 15] = [
|
||||
"concat",
|
||||
@@ -133,7 +136,7 @@ impl Rule for PreferSetHas {
|
||||
return;
|
||||
};
|
||||
|
||||
if declarator.kind == VariableDeclarationKind::Var {
|
||||
if variable_declaration_kind(declarator, ctx) == VariableDeclarationKind::Var {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::{
|
||||
AstNode,
|
||||
ast_util::variable_declaration_kind,
|
||||
ast_util::{get_declaration_of_variable, is_method_call},
|
||||
context::LintContext,
|
||||
fixer::Fix,
|
||||
@@ -149,7 +150,7 @@ fn is_set<'a>(maybe_set: &Expression<'a>, ctx: &LintContext<'a>) -> bool {
|
||||
return false;
|
||||
};
|
||||
|
||||
if !var_decl.kind.is_const() {
|
||||
if !variable_declaration_kind(var_decl, ctx).is_const() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,10 @@ use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::{AstNode, ast_util::is_method_call, context::LintContext, rule::Rule};
|
||||
use crate::{
|
||||
AstNode, ast_util::is_method_call, ast_util::variable_declaration_kind, context::LintContext,
|
||||
rule::Rule,
|
||||
};
|
||||
|
||||
fn prefer_top_level_await_over_promise_chain_diagnostic(span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn("Prefer top-level await over using a promise chain.").with_label(span)
|
||||
@@ -159,7 +162,7 @@ impl Rule for PreferTopLevelAwait {
|
||||
|
||||
match declaration.kind() {
|
||||
AstKind::VariableDeclarator(var_decl)
|
||||
if var_decl.kind == VariableDeclarationKind::Const =>
|
||||
if variable_declaration_kind(var_decl, ctx) == VariableDeclarationKind::Const =>
|
||||
{
|
||||
let Some(init) = &var_decl.init else { return };
|
||||
|
||||
|
||||
@@ -9,7 +9,10 @@ use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::{AstNode, context::LintContext, frameworks::FrameworkOptions, rule::Rule};
|
||||
use crate::{
|
||||
AstNode, ast_util::variable_declaration_kind, context::LintContext,
|
||||
frameworks::FrameworkOptions, rule::Rule,
|
||||
};
|
||||
|
||||
fn multiple_arguments_diagnostic(span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn("Unexpected multiple arguments.")
|
||||
@@ -165,7 +168,7 @@ fn get_identifier_resolved_reference<'a>(
|
||||
};
|
||||
|
||||
// `const` variable can not be overridden
|
||||
if declarator.kind == VariableDeclarationKind::Const {
|
||||
if variable_declaration_kind(declarator, ctx) == VariableDeclarationKind::Const {
|
||||
return declarator.init.as_ref();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,10 @@ use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::{AstNode, context::LintContext, frameworks::FrameworkOptions, rule::Rule};
|
||||
use crate::{
|
||||
AstNode, ast_util::variable_declaration_kind, context::LintContext,
|
||||
frameworks::FrameworkOptions, rule::Rule,
|
||||
};
|
||||
|
||||
fn referencing_locally_diagnostic(span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn("`defineOptions` is referencing locally declared variables.")
|
||||
@@ -205,7 +208,7 @@ fn is_non_local_reference(
|
||||
| AstKind::ImportNamespaceSpecifier(_) => true,
|
||||
AstKind::VariableDeclarator(declarator) => {
|
||||
// `const x = <literal>` is statically resolvable, so allowed
|
||||
declarator.kind.is_const()
|
||||
variable_declaration_kind(declarator, ctx).is_const()
|
||||
&& declarator.init.as_ref().is_some_and(is_literal_expression)
|
||||
}
|
||||
_ => false,
|
||||
|
||||
@@ -9,7 +9,10 @@ use oxc_ast_visit::{VisitJs, walk_js};
|
||||
use oxc_semantic::ScopeFlags;
|
||||
use oxc_span::Span;
|
||||
|
||||
use crate::{ast_util::get_declaration_from_reference_id, context::LintContext};
|
||||
use crate::{
|
||||
ast_util::get_declaration_from_reference_id, ast_util::variable_declaration_kind,
|
||||
context::LintContext,
|
||||
};
|
||||
|
||||
/// Finds `this` expressions without traversing into nested functions.
|
||||
pub struct ThisExpressionFinder {
|
||||
@@ -73,7 +76,7 @@ pub fn is_this_alias(ident: &IdentifierReference, ctx: &LintContext<'_>) -> bool
|
||||
_ => None,
|
||||
})
|
||||
.filter(|var| {
|
||||
var.kind == VariableDeclarationKind::Const
|
||||
variable_declaration_kind(var, ctx) == VariableDeclarationKind::Const
|
||||
&& matches!(&var.id, BindingPattern::BindingIdentifier(_))
|
||||
})
|
||||
.and_then(|var| var.init.as_ref())
|
||||
|
||||
@@ -5211,7 +5211,6 @@ impl<'a, 't> GetAddress for VariableDeclarationWithoutDeclarations<'a, 't> {
|
||||
pub(crate) const OFFSET_VARIABLE_DECLARATOR_NODE_ID: usize =
|
||||
offset_of!(VariableDeclarator, node_id);
|
||||
pub(crate) const OFFSET_VARIABLE_DECLARATOR_SPAN: usize = offset_of!(VariableDeclarator, span);
|
||||
pub(crate) const OFFSET_VARIABLE_DECLARATOR_KIND: usize = offset_of!(VariableDeclarator, kind);
|
||||
pub(crate) const OFFSET_VARIABLE_DECLARATOR_ID: usize = offset_of!(VariableDeclarator, id);
|
||||
pub(crate) const OFFSET_VARIABLE_DECLARATOR_TYPE_ANNOTATION: usize =
|
||||
offset_of!(VariableDeclarator, type_annotation);
|
||||
@@ -5239,14 +5238,6 @@ impl<'a, 't> VariableDeclaratorWithoutId<'a, 't> {
|
||||
unsafe { &*((self.0 as *const u8).add(OFFSET_VARIABLE_DECLARATOR_SPAN) as *const Span) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn kind(self) -> &'t VariableDeclarationKind {
|
||||
unsafe {
|
||||
&*((self.0 as *const u8).add(OFFSET_VARIABLE_DECLARATOR_KIND)
|
||||
as *const VariableDeclarationKind)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn type_annotation(self) -> &'t Option<ArenaBox<'a, TSTypeAnnotation<'a>>> {
|
||||
unsafe {
|
||||
@@ -5296,14 +5287,6 @@ impl<'a, 't> VariableDeclaratorWithoutTypeAnnotation<'a, 't> {
|
||||
unsafe { &*((self.0 as *const u8).add(OFFSET_VARIABLE_DECLARATOR_SPAN) as *const Span) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn kind(self) -> &'t VariableDeclarationKind {
|
||||
unsafe {
|
||||
&*((self.0 as *const u8).add(OFFSET_VARIABLE_DECLARATOR_KIND)
|
||||
as *const VariableDeclarationKind)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(self) -> &'t BindingPattern<'a> {
|
||||
unsafe {
|
||||
@@ -5353,14 +5336,6 @@ impl<'a, 't> VariableDeclaratorWithoutInit<'a, 't> {
|
||||
unsafe { &*((self.0 as *const u8).add(OFFSET_VARIABLE_DECLARATOR_SPAN) as *const Span) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn kind(self) -> &'t VariableDeclarationKind {
|
||||
unsafe {
|
||||
&*((self.0 as *const u8).add(OFFSET_VARIABLE_DECLARATOR_KIND)
|
||||
as *const VariableDeclarationKind)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(self) -> &'t BindingPattern<'a> {
|
||||
unsafe {
|
||||
|
||||
@@ -80,7 +80,7 @@ impl<'a> KeepVar<'a> {
|
||||
)
|
||||
},
|
||||
);
|
||||
VariableDeclarator::new(span, kind, id, None, None, false, builder)
|
||||
VariableDeclarator::new(span, id, None, None, false, builder)
|
||||
}),
|
||||
builder,
|
||||
);
|
||||
|
||||
@@ -11,6 +11,10 @@ use super::PeepholeOptimizations;
|
||||
|
||||
impl<'a> PeepholeOptimizations {
|
||||
pub fn init_symbol_value(decl: &VariableDeclarator<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
let Ancestor::VariableDeclarationDeclarations(declaration) = ctx.parent() else {
|
||||
unreachable!();
|
||||
};
|
||||
let declaration_kind = *declaration.kind();
|
||||
let BindingPattern::BindingIdentifier(ident) = &decl.id else { return };
|
||||
let Some(symbol_id) = ident.symbol_id.get() else { return };
|
||||
// Evaluate the initializer's constant once; reuse it for the value-context
|
||||
@@ -23,11 +27,11 @@ impl<'a> PeepholeOptimizations {
|
||||
// `SymbolValue::boolean_falsy`).
|
||||
let falsy_init = init_constant.as_ref().is_some_and(Self::is_falsy_constant);
|
||||
let declaration_in_body_statement_list =
|
||||
!decl.kind.is_var() || Self::is_declaration_in_body_statement_list(ctx);
|
||||
!declaration_kind.is_var() || Self::is_declaration_in_body_statement_list(ctx);
|
||||
let value = if Self::is_for_statement_init(ctx) {
|
||||
// for-statement initializers have their value set by the for statement itself.
|
||||
None
|
||||
} else if decl.kind.is_var()
|
||||
} else if declaration_kind.is_var()
|
||||
&& !Self::is_hoisted_var_inlineable(
|
||||
decl,
|
||||
symbol_id,
|
||||
|
||||
@@ -436,7 +436,10 @@ impl<'a> PeepholeOptimizations {
|
||||
|
||||
// If `join_vars` is off, but there are unused declarators ... just join them to make our code simpler.
|
||||
if !ctx.options().join_vars
|
||||
&& var_decl.declarations.iter().all(|d| !Self::should_remove_unused_declarator(d, ctx))
|
||||
&& var_decl
|
||||
.declarations
|
||||
.iter()
|
||||
.all(|d| !Self::should_remove_unused_declarator(d, var_decl.kind, ctx))
|
||||
{
|
||||
result.push(Statement::VariableDeclaration(var_decl));
|
||||
return;
|
||||
@@ -449,7 +452,7 @@ impl<'a> PeepholeOptimizations {
|
||||
}
|
||||
let VariableDeclaration { span, kind, declarations, declare, .. } = var_decl.unbox();
|
||||
for mut decl in declarations {
|
||||
if Self::should_remove_unused_declarator(&decl, ctx) {
|
||||
if Self::should_remove_unused_declarator(&decl, kind, ctx) {
|
||||
// `init` is `mut` because `remove_unused_expression` rewrites
|
||||
// it in place (peeling pure-call wrappers, etc). It is taken
|
||||
// out first because it may survive as an expression statement
|
||||
@@ -595,13 +598,14 @@ impl<'a> PeepholeOptimizations {
|
||||
if !matches!(&var_decl.kind, VariableDeclarationKind::Var | VariableDeclarationKind::Let) {
|
||||
return false;
|
||||
}
|
||||
let declaration_kind = var_decl.kind;
|
||||
for decl in var_decl.declarations.iter_mut().rev() {
|
||||
let BindingPattern::BindingIdentifier(kind) = &decl.id else {
|
||||
break;
|
||||
};
|
||||
if kind.name == id.name {
|
||||
if decl.init.is_none()
|
||||
&& (decl.kind == VariableDeclarationKind::Var
|
||||
&& (declaration_kind == VariableDeclarationKind::Var
|
||||
|| assign_expr.right.is_literal_value(true, ctx))
|
||||
{
|
||||
// "var a; a = b();" => "var a = b();"
|
||||
@@ -623,7 +627,7 @@ impl<'a> PeepholeOptimizations {
|
||||
}
|
||||
// should not move assignment above other variables for let
|
||||
// this could cause TDZ errors (e.g. `let a, b; b = a;`)
|
||||
if decl.kind == VariableDeclarationKind::Let {
|
||||
if declaration_kind == VariableDeclarationKind::Let {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1014,10 +1018,11 @@ impl<'a> PeepholeOptimizations {
|
||||
if let Some(init) = &mut for_stmt.init {
|
||||
match init {
|
||||
ForStatementInit::VariableDeclaration(var_decl) => {
|
||||
let declaration_kind = var_decl.kind;
|
||||
if let Some(first_decl) = var_decl.declarations.first_mut()
|
||||
&& let Some(first_decl_init) = first_decl.init.as_mut()
|
||||
{
|
||||
let is_block_scoped_decl = !first_decl.kind.is_var();
|
||||
let is_block_scoped_decl = !declaration_kind.is_var();
|
||||
Self::substitute_single_use_symbol_in_statement(
|
||||
first_decl_init,
|
||||
result,
|
||||
@@ -1039,13 +1044,14 @@ impl<'a> PeepholeOptimizations {
|
||||
}
|
||||
|
||||
if let Some(ForStatementInit::VariableDeclaration(var_decl)) = &mut for_stmt.init {
|
||||
let declaration_kind = var_decl.kind;
|
||||
let old_len = var_decl.declarations.len();
|
||||
var_decl.declarations.retain_mut(|decl| {
|
||||
let should_keep = !Self::should_remove_unused_declarator(decl, ctx)
|
||||
|| decl
|
||||
.init
|
||||
.as_ref()
|
||||
.is_some_and(|init| Self::has_side_effects_or_preserved_iife(init, ctx));
|
||||
let should_keep =
|
||||
!Self::should_remove_unused_declarator(decl, declaration_kind, ctx)
|
||||
|| decl.init.as_ref().is_some_and(|init| {
|
||||
Self::has_side_effects_or_preserved_iife(init, ctx)
|
||||
});
|
||||
if !should_keep {
|
||||
// Same leak hazard as `remove_unused_variable_declaration`:
|
||||
// the `retain` silently drops the declarator, so its refs
|
||||
|
||||
@@ -289,9 +289,6 @@ impl<'a> Normalize {
|
||||
if all_declarations_are_only_read {
|
||||
// mark all declarations as `let`
|
||||
decl.kind = VariableDeclarationKind::Let;
|
||||
for decl in &mut decl.declarations {
|
||||
decl.kind = VariableDeclarationKind::Let;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,13 +86,14 @@ impl<'a> PeepholeOptimizations {
|
||||
|
||||
pub fn should_remove_unused_declarator(
|
||||
decl: &VariableDeclarator<'a>,
|
||||
kind: VariableDeclarationKind,
|
||||
ctx: &TraverseCtx<'a>,
|
||||
) -> bool {
|
||||
if !Self::can_remove_unused_declarators(ctx) {
|
||||
return false;
|
||||
}
|
||||
// Unsafe to remove `using`, unable to statically determine usage of [Symbol.dispose].
|
||||
if decl.kind.is_using() {
|
||||
if kind.is_using() {
|
||||
return false;
|
||||
}
|
||||
match &decl.id {
|
||||
@@ -143,13 +144,14 @@ impl<'a> PeepholeOptimizations {
|
||||
if !Self::can_remove_unused_declarators(ctx) {
|
||||
return Some(stmt);
|
||||
}
|
||||
let kind = var_decl.kind;
|
||||
var_decl.declarations.retain(|decl| {
|
||||
debug_assert!(
|
||||
decl.init.is_none(),
|
||||
"callers must pass KeepVar output (init-less declarators); a declarator \
|
||||
with an init would need a `drop_*` walk for its references"
|
||||
);
|
||||
!Self::should_remove_unused_declarator(decl, ctx)
|
||||
!Self::should_remove_unused_declarator(decl, kind, ctx)
|
||||
});
|
||||
if var_decl.declarations.is_empty() {
|
||||
return None;
|
||||
|
||||
@@ -170,7 +170,7 @@ impl<'a> PeepholeOptimizations {
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
for declarator in &mut decl.declarations {
|
||||
Self::compress_variable_declarator(declarator, ctx);
|
||||
Self::compress_variable_declarator(declarator, decl.kind, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -995,8 +995,7 @@ impl<'a> PeepholeOptimizations {
|
||||
base_arr
|
||||
};
|
||||
|
||||
let new_decl =
|
||||
VariableDeclarator::new(SPAN, var_init.kind, r_id_pat, None, Some(arr), false, ctx);
|
||||
let new_decl = VariableDeclarator::new(SPAN, r_id_pat, None, Some(arr), false, ctx);
|
||||
// The old declarators (`e`, `a`, and `r`'s original init) are
|
||||
// replaced wholesale — walk them so refs inside (e.g. `e` in
|
||||
// `Array(e > 1 ? e - 1 : 0)`) reach `PassChanges`. The moved-out
|
||||
@@ -1055,10 +1054,14 @@ impl<'a> PeepholeOptimizations {
|
||||
}
|
||||
}
|
||||
|
||||
fn compress_variable_declarator(decl: &mut VariableDeclarator<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
fn compress_variable_declarator(
|
||||
decl: &mut VariableDeclarator<'a>,
|
||||
kind: VariableDeclarationKind,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
// Destructuring Pattern has error throwing side effect.
|
||||
if matches!(
|
||||
decl.kind,
|
||||
kind,
|
||||
VariableDeclarationKind::Const
|
||||
| VariableDeclarationKind::Using
|
||||
| VariableDeclarationKind::AwaitUsing
|
||||
@@ -1066,7 +1069,7 @@ impl<'a> PeepholeOptimizations {
|
||||
{
|
||||
return;
|
||||
}
|
||||
if !decl.kind.is_var()
|
||||
if !kind.is_var()
|
||||
&& decl.init.as_ref().is_some_and(|init| ctx.is_expression_undefined(init))
|
||||
&& let Some(old) = decl.init.take()
|
||||
{
|
||||
@@ -1880,7 +1883,10 @@ impl<'a> PeepholeOptimizations {
|
||||
return false;
|
||||
}
|
||||
// `using` runs `[Symbol.dispose]` at scope exit.
|
||||
if decl.kind().is_using() {
|
||||
let Ancestor::VariableDeclarationDeclarations(declaration) = ctx.ancestor(1) else {
|
||||
unreachable!();
|
||||
};
|
||||
if declaration.kind().is_using() {
|
||||
return false;
|
||||
}
|
||||
let BindingPattern::BindingIdentifier(ident) = decl.id() else {
|
||||
|
||||
@@ -134,7 +134,6 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
let init = self.eat(Kind::Eq).then(|| self.parse_assignment_expression_or_higher());
|
||||
let decl = VariableDeclarator::new(
|
||||
self.end_span(start),
|
||||
kind,
|
||||
id,
|
||||
type_annotation,
|
||||
init,
|
||||
@@ -143,13 +142,13 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
);
|
||||
if self.ctx.has_ambient()
|
||||
&& let Some(init) = &decl.init
|
||||
&& !decl.kind.is_using()
|
||||
&& !(decl.kind.is_const() && decl.type_annotation.is_none())
|
||||
&& !kind.is_using()
|
||||
&& !(kind.is_const() && decl.type_annotation.is_none())
|
||||
{
|
||||
self.error(diagnostics::initializers_not_allowed_in_ambient_contexts(init.span()));
|
||||
}
|
||||
if decl_parent == VariableDeclarationParent::Statement {
|
||||
self.check_missing_initializer(&decl);
|
||||
self.check_missing_initializer(&decl, kind);
|
||||
}
|
||||
if let Some(definite_start) = definite_start {
|
||||
let span = Span::sized(definite_start, 1);
|
||||
@@ -164,14 +163,18 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
decl
|
||||
}
|
||||
|
||||
pub(crate) fn check_missing_initializer(&mut self, decl: &VariableDeclarator<'a>) {
|
||||
pub(crate) fn check_missing_initializer(
|
||||
&mut self,
|
||||
decl: &VariableDeclarator<'a>,
|
||||
kind: VariableDeclarationKind,
|
||||
) {
|
||||
if decl.init.is_none() && !self.ctx.has_ambient() {
|
||||
if !matches!(decl.id, BindingPattern::BindingIdentifier(_)) {
|
||||
self.error(diagnostics::invalid_destructuring_declaration(decl.id.span()));
|
||||
} else if decl.kind == VariableDeclarationKind::Const {
|
||||
} else if kind == VariableDeclarationKind::Const {
|
||||
// It is a Syntax Error if Initializer is not present and IsConstantDeclaration of the LexicalDeclaration containing this LexicalBinding is true.
|
||||
self.error(diagnostics::missing_initializer_in_const(decl.id.span()));
|
||||
} else if decl.kind.is_using() {
|
||||
} else if kind.is_using() {
|
||||
self.error(diagnostics::using_declarations_must_be_initialized(decl.id.span()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -591,7 +591,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
self.expect(Kind::Semicolon);
|
||||
if let Some(ForStatementInit::VariableDeclaration(decl)) = &init {
|
||||
for d in &decl.declarations {
|
||||
self.check_missing_initializer(d);
|
||||
self.check_missing_initializer(d, decl.kind);
|
||||
}
|
||||
}
|
||||
let test = if matches!(self.cur_kind(), Kind::Semicolon | Kind::RParen) {
|
||||
|
||||
@@ -2261,7 +2261,6 @@ fn ox_build_gated_const_decl<'a>(
|
||||
) -> Statement<'a> {
|
||||
let declarator = VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Const,
|
||||
BindingPattern::new_binding_identifier(SPAN, ox_atom(ast, name), ast),
|
||||
None,
|
||||
Some(gating_expression.clone_in_with_semantic_ids(ast.allocator())),
|
||||
@@ -2806,15 +2805,8 @@ fn ox_add_imports_to_program<'a>(
|
||||
false,
|
||||
ast,
|
||||
);
|
||||
let declarator = VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Const,
|
||||
object_pattern,
|
||||
None,
|
||||
Some(require_call),
|
||||
false,
|
||||
ast,
|
||||
);
|
||||
let declarator =
|
||||
VariableDeclarator::new(SPAN, object_pattern, None, Some(require_call), false, ast);
|
||||
let decl = VariableDeclaration::boxed(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Const,
|
||||
|
||||
+3
-22
@@ -127,7 +127,6 @@ pub fn codegen_function<'a>(
|
||||
);
|
||||
let declarator = oxc_ast::ast::VariableDeclarator::new(
|
||||
SPAN,
|
||||
oxc_ast::ast::VariableDeclarationKind::Const,
|
||||
oxc_ast::ast::BindingPattern::new_binding_identifier(
|
||||
SPAN,
|
||||
ox_str(ast, &cache_name),
|
||||
@@ -720,7 +719,6 @@ fn ox_codegen_reactive_scope<'a>(
|
||||
if !cx.has_declared(decl.identifier) {
|
||||
let declarator = oxc_ast::ast::VariableDeclarator::new(
|
||||
SPAN,
|
||||
oxc::VariableDeclarationKind::Let,
|
||||
oxc_ast::ast::BindingPattern::new_binding_identifier(
|
||||
SPAN,
|
||||
ox_str(&cx.ast, &name),
|
||||
@@ -1073,15 +1071,7 @@ fn ox_codegen_for_in<'a>(
|
||||
let right = ox_codegen_instruction_value_to_expression(cx, &iterable_collection.value)?;
|
||||
let body = ox_codegen_block_statement(cx, loop_block)?;
|
||||
let body = oxc::Statement::BlockStatement(body);
|
||||
let declarator = oxc_ast::ast::VariableDeclarator::new(
|
||||
SPAN,
|
||||
var_decl_kind,
|
||||
lval,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
&cx.ast,
|
||||
);
|
||||
let declarator = oxc_ast::ast::VariableDeclarator::new(SPAN, lval, None, None, false, &cx.ast);
|
||||
let decl =
|
||||
oxc_ast::ast::VariableDeclaration::boxed(SPAN, var_decl_kind, [declarator], false, &cx.ast);
|
||||
let left = oxc::ForStatementLeft::VariableDeclaration(decl);
|
||||
@@ -1125,15 +1115,7 @@ fn ox_codegen_for_of<'a>(
|
||||
let right = ox_codegen_place_to_expression(cx, collection)?;
|
||||
let body = ox_codegen_block_statement(cx, loop_block)?;
|
||||
let body = oxc::Statement::BlockStatement(body);
|
||||
let declarator = oxc_ast::ast::VariableDeclarator::new(
|
||||
SPAN,
|
||||
var_decl_kind,
|
||||
lval,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
&cx.ast,
|
||||
);
|
||||
let declarator = oxc_ast::ast::VariableDeclarator::new(SPAN, lval, None, None, false, &cx.ast);
|
||||
let decl =
|
||||
oxc_ast::ast::VariableDeclaration::boxed(SPAN, var_decl_kind, [declarator], false, &cx.ast);
|
||||
let left = oxc::ForStatementLeft::VariableDeclaration(decl);
|
||||
@@ -1484,8 +1466,7 @@ fn ox_make_var_decl<'a>(
|
||||
id: oxc::BindingPattern<'a>,
|
||||
init: Option<oxc::Expression<'a>>,
|
||||
) -> oxc::Statement<'a> {
|
||||
let declarator =
|
||||
oxc_ast::ast::VariableDeclarator::new(SPAN, kind, id, None, init, false, &cx.ast);
|
||||
let declarator = oxc_ast::ast::VariableDeclarator::new(SPAN, id, None, init, false, &cx.ast);
|
||||
oxc::Statement::VariableDeclaration(oxc_ast::ast::VariableDeclaration::boxed(
|
||||
SPAN,
|
||||
kind,
|
||||
|
||||
@@ -17,12 +17,13 @@ pub trait Binder<'a> {
|
||||
|
||||
impl<'a> Binder<'a> for VariableDeclarator<'a> {
|
||||
fn bind(&self, builder: &mut SemanticBuilder<'a>) {
|
||||
let is_declare = matches!(
|
||||
builder.ancestry().parent_kind(),
|
||||
AstKind::VariableDeclaration(decl) if decl.declare
|
||||
);
|
||||
let AstKind::VariableDeclaration(declaration) = builder.ancestry().parent_kind() else {
|
||||
unreachable!();
|
||||
};
|
||||
let kind = declaration.kind;
|
||||
let is_declare = declaration.declare;
|
||||
|
||||
let (mut includes, excludes) = match self.kind {
|
||||
let (mut includes, excludes) = match kind {
|
||||
VariableDeclarationKind::Const
|
||||
| VariableDeclarationKind::Using
|
||||
| VariableDeclarationKind::AwaitUsing => (
|
||||
@@ -41,7 +42,7 @@ impl<'a> Binder<'a> for VariableDeclarator<'a> {
|
||||
includes |= SymbolFlags::Ambient;
|
||||
}
|
||||
|
||||
if self.kind.is_lexical() {
|
||||
if kind.is_lexical() {
|
||||
self.id.bound_names(&mut |ident| {
|
||||
let symbol_id = builder.declare_symbol(ident.span, ident.name, includes, excludes);
|
||||
ident.symbol_id.set(Some(symbol_id));
|
||||
|
||||
@@ -254,7 +254,7 @@ pub fn check_binding_identifier(ident: &BindingIdentifier, ctx: &SemanticBuilder
|
||||
// * It is a Syntax Error if the BoundNames of BindingList contains "let".
|
||||
for node_kind in ctx.ancestry().ancestor_kinds() {
|
||||
match node_kind {
|
||||
AstKind::VariableDeclarator(decl) => {
|
||||
AstKind::VariableDeclaration(decl) => {
|
||||
if decl.kind.is_lexical() {
|
||||
ctx.error(diagnostics::invalid_let_declaration(
|
||||
decl.kind.as_str(),
|
||||
@@ -616,7 +616,10 @@ pub fn check_variable_declarator_redeclaration(
|
||||
decl: &VariableDeclarator,
|
||||
ctx: &SemanticBuilder<'_>,
|
||||
) {
|
||||
if decl.kind != VariableDeclarationKind::Var {
|
||||
let AstKind::VariableDeclaration(declaration) = ctx.ancestry().parent_kind() else {
|
||||
unreachable!();
|
||||
};
|
||||
if declaration.kind != VariableDeclarationKind::Var {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -320,10 +320,15 @@ mod tests {
|
||||
.get_binding(semantic.scoping().root_scope_id(), static_ident!("a"))
|
||||
.unwrap();
|
||||
|
||||
let decl = semantic.symbol_declaration(top_level_a);
|
||||
match decl.kind() {
|
||||
AstKind::VariableDeclarator(decl) => {
|
||||
assert_eq!(decl.kind, VariableDeclarationKind::Let);
|
||||
let declarator_node = semantic.symbol_declaration(top_level_a);
|
||||
match declarator_node.kind() {
|
||||
AstKind::VariableDeclarator(_) => {
|
||||
let AstKind::VariableDeclaration(declaration) =
|
||||
semantic.nodes().parent_kind(declarator_node.id())
|
||||
else {
|
||||
unreachable!();
|
||||
};
|
||||
assert_eq!(declaration.kind, VariableDeclarationKind::Let);
|
||||
}
|
||||
kind => panic!("Expected VariableDeclarator for 'let', got {kind:?}"),
|
||||
}
|
||||
|
||||
@@ -1012,7 +1012,6 @@ impl<'a> ArrowFunctionConverter<'a> {
|
||||
);
|
||||
VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
binding.create_binding_pattern(ctx),
|
||||
None,
|
||||
Some(init),
|
||||
@@ -1189,7 +1188,6 @@ impl<'a> ArrowFunctionConverter<'a> {
|
||||
|
||||
Some(VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
arguments_var.create_binding_pattern(ctx),
|
||||
None,
|
||||
Some(init),
|
||||
@@ -1250,7 +1248,6 @@ impl<'a> ArrowFunctionConverter<'a> {
|
||||
Self::adjust_binding_scope(target_scope_id, &this_var, ctx);
|
||||
let variable_declarator = VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
this_var.create_binding_pattern(ctx),
|
||||
None,
|
||||
init,
|
||||
|
||||
@@ -192,7 +192,7 @@ impl<'a> ModuleImportsStore<'a> {
|
||||
let var_kind = VariableDeclarationKind::Var;
|
||||
let decl = {
|
||||
let init = Expression::new_call_expression(SPAN, callee, None, args, false, ctx);
|
||||
let decl = VariableDeclarator::new(SPAN, var_kind, id, None, Some(init), false, ctx);
|
||||
let decl = VariableDeclarator::new(SPAN, id, None, Some(init), false, ctx);
|
||||
[decl]
|
||||
};
|
||||
Statement::new_variable_declaration(SPAN, var_kind, decl, false, ctx)
|
||||
|
||||
@@ -146,15 +146,7 @@ impl<'a> VarDeclarationsStore<'a> {
|
||||
init: Option<Expression<'a>>,
|
||||
ast: &AstBuilder<'a>,
|
||||
) {
|
||||
let declarator = VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
ident,
|
||||
None,
|
||||
init,
|
||||
false,
|
||||
ast,
|
||||
);
|
||||
let declarator = VariableDeclarator::new(SPAN, ident, None, init, false, ast);
|
||||
self.insert_var_declarator(declarator, ast);
|
||||
}
|
||||
|
||||
@@ -166,15 +158,7 @@ impl<'a> VarDeclarationsStore<'a> {
|
||||
init: Option<Expression<'a>>,
|
||||
ast: &AstBuilder<'a>,
|
||||
) {
|
||||
let declarator = VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Let,
|
||||
ident,
|
||||
None,
|
||||
init,
|
||||
false,
|
||||
ast,
|
||||
);
|
||||
let declarator = VariableDeclarator::new(SPAN, ident, None, init, false, ast);
|
||||
self.insert_let_declarator(declarator, ast);
|
||||
}
|
||||
|
||||
|
||||
@@ -1015,7 +1015,6 @@ impl<'a> LegacyDecorator<'a> {
|
||||
);
|
||||
let declarator = VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Let,
|
||||
binding.create_spanned_binding_pattern(binding_span, ctx),
|
||||
None,
|
||||
Some(initializer),
|
||||
|
||||
@@ -798,7 +798,6 @@ impl<'a> AsyncGeneratorExecutor<'a> {
|
||||
let declarations = ArenaVec::from_value_in(
|
||||
VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
bound_ident.create_binding_pattern(ctx),
|
||||
None,
|
||||
Some(init),
|
||||
|
||||
@@ -122,9 +122,10 @@ impl<'a> AsyncGeneratorFunctions<'a> {
|
||||
let assignment_statement = match &mut stmt.left {
|
||||
ForStatementLeft::VariableDeclaration(variable) => {
|
||||
// for await (let i of test)
|
||||
let kind = variable.kind;
|
||||
let mut declarator = variable.declarations.pop().unwrap();
|
||||
declarator.init = Some(step_value);
|
||||
Statement::new_variable_declaration(SPAN, declarator.kind, [declarator], false, ctx)
|
||||
Statement::new_variable_declaration(SPAN, kind, [declarator], false, ctx)
|
||||
}
|
||||
left @ match_assignment_target!(ForStatementLeft) => {
|
||||
// for await (i of test), for await ({ i } of test)
|
||||
@@ -228,7 +229,6 @@ impl<'a> AsyncGeneratorFunctions<'a> {
|
||||
VariableDeclarationKind::Var,
|
||||
[VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
iterator_abrupt_completion.create_binding_pattern(ctx),
|
||||
None,
|
||||
Some(Expression::new_boolean_literal(SPAN, false, ctx)),
|
||||
@@ -243,7 +243,6 @@ impl<'a> AsyncGeneratorFunctions<'a> {
|
||||
VariableDeclarationKind::Var,
|
||||
[VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
iterator_had_error_key.create_binding_pattern(ctx),
|
||||
None,
|
||||
Some(Expression::new_boolean_literal(SPAN, false, ctx)),
|
||||
@@ -258,7 +257,6 @@ impl<'a> AsyncGeneratorFunctions<'a> {
|
||||
VariableDeclarationKind::Var,
|
||||
[VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
iterator_error_key.create_binding_pattern(ctx),
|
||||
None,
|
||||
None,
|
||||
@@ -284,7 +282,6 @@ impl<'a> AsyncGeneratorFunctions<'a> {
|
||||
[
|
||||
VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
iterator_key.create_binding_pattern(ctx),
|
||||
None,
|
||||
Some(iterator),
|
||||
@@ -293,7 +290,6 @@ impl<'a> AsyncGeneratorFunctions<'a> {
|
||||
),
|
||||
VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
step_key.create_binding_pattern(ctx),
|
||||
None,
|
||||
None,
|
||||
|
||||
@@ -216,7 +216,7 @@ impl<'a> ObjectRestSpread<'a> {
|
||||
let mut new_decls = vec![];
|
||||
|
||||
if let Some(id) = reference_builder.binding.take() {
|
||||
new_decls.push(VariableDeclarator::new(SPAN, state.kind, id, None, None, false, ctx));
|
||||
new_decls.push(VariableDeclarator::new(SPAN, id, None, None, false, ctx));
|
||||
}
|
||||
|
||||
let data = Self::walk_assignment_target(&mut assign_expr.left, &mut new_decls, state, ctx);
|
||||
@@ -457,8 +457,7 @@ impl<'a> ObjectRestSpread<'a> {
|
||||
}
|
||||
let bound_identifier = ctx.generate_uid_in_current_hoist_scope("ref");
|
||||
let id = bound_identifier.create_binding_pattern(ctx);
|
||||
let kind = VariableDeclarationKind::Var;
|
||||
decls.push(VariableDeclarator::new(SPAN, kind, id, None, None, false, ctx));
|
||||
decls.push(VariableDeclarator::new(SPAN, id, None, None, false, ctx));
|
||||
exprs.push(Expression::new_assignment_expression(
|
||||
SPAN,
|
||||
AssignmentOperator::Assign,
|
||||
@@ -636,7 +635,7 @@ impl<'a> ObjectRestSpread<'a> {
|
||||
if decl.kind.is_var() { ctx.current_hoist_scope_id() } else { scope_id };
|
||||
|
||||
Self::replace_rest_element(
|
||||
declarator.kind,
|
||||
decl.kind,
|
||||
&mut declarator.id,
|
||||
&mut block.body,
|
||||
old_scope_id,
|
||||
@@ -674,10 +673,8 @@ impl<'a> ObjectRestSpread<'a> {
|
||||
let bound_identifier = ctx.generate_uid("ref", ctx.current_hoist_scope_id(), flags);
|
||||
let id = bound_identifier.create_binding_pattern(ctx);
|
||||
let kind = VariableDeclarationKind::Var;
|
||||
let declarations = ArenaVec::from_value_in(
|
||||
VariableDeclarator::new(SPAN, kind, id, None, None, false, ctx),
|
||||
ctx,
|
||||
);
|
||||
let declarations =
|
||||
ArenaVec::from_value_in(VariableDeclarator::new(SPAN, id, None, None, false, ctx), ctx);
|
||||
let decl = VariableDeclaration::boxed(SPAN, kind, declarations, false, ctx);
|
||||
*left = ForStatementLeft::VariableDeclaration(decl);
|
||||
Self::try_replace_statement_with_block(body, scope_id, ctx);
|
||||
@@ -799,7 +796,7 @@ impl<'a> ObjectRestSpread<'a> {
|
||||
});
|
||||
let init = bound_identifier.create_read_expression(ctx);
|
||||
let declarations = ArenaVec::from_value_in(
|
||||
VariableDeclarator::new(SPAN, kind, id, None, Some(init), false, ctx),
|
||||
VariableDeclarator::new(SPAN, id, None, Some(init), false, ctx),
|
||||
ctx,
|
||||
);
|
||||
VariableDeclaration::boxed(SPAN, kind, declarations, false, ctx)
|
||||
@@ -819,7 +816,7 @@ impl<'a> ObjectRestSpread<'a> {
|
||||
if variable_declarator.init.is_some()
|
||||
&& Self::has_nested_object_rest(&variable_declarator.id)
|
||||
{
|
||||
let decls = self.transform_variable_declarator(variable_declarator, ctx);
|
||||
let decls = self.transform_variable_declarator(variable_declarator, decl.kind, ctx);
|
||||
new_decls.push((i, decls));
|
||||
}
|
||||
}
|
||||
@@ -836,6 +833,7 @@ impl<'a> ObjectRestSpread<'a> {
|
||||
fn transform_variable_declarator(
|
||||
&mut self,
|
||||
decl: &mut VariableDeclarator<'a>,
|
||||
kind: VariableDeclarationKind,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> ArenaVec<'a, VariableDeclarator<'a>> {
|
||||
// It is syntax error or inside for loop if missing initializer in destructuring pattern.
|
||||
@@ -845,7 +843,7 @@ impl<'a> ObjectRestSpread<'a> {
|
||||
// `for (var {...x} = {};;);` and `for (let {...x} = {};;);`
|
||||
// TODO: improve this by getting the value only once.
|
||||
let mut scope_id = ctx.current_scope_id();
|
||||
let mut symbol_flags = kind_to_symbol_flags(decl.kind);
|
||||
let mut symbol_flags = kind_to_symbol_flags(kind);
|
||||
let symbols = ctx.scoping();
|
||||
decl.id.bound_names(&mut |ident| {
|
||||
let symbol_id = ident.symbol_id();
|
||||
@@ -853,7 +851,7 @@ impl<'a> ObjectRestSpread<'a> {
|
||||
symbol_flags.insert(symbols.symbol_flags(symbol_id));
|
||||
});
|
||||
|
||||
let state = State::new(decl.kind, symbol_flags, scope_id);
|
||||
let state = State::new(kind, symbol_flags, scope_id);
|
||||
let mut new_decls = vec![];
|
||||
|
||||
let mut reference_builder = ReferenceBuilder::new(init, symbol_flags, scope_id, false, ctx);
|
||||
@@ -863,7 +861,6 @@ impl<'a> ObjectRestSpread<'a> {
|
||||
if let Some(id) = reference_builder.binding.take() {
|
||||
let decl = VariableDeclarator::new(
|
||||
SPAN,
|
||||
state.kind,
|
||||
id,
|
||||
None,
|
||||
Some(reference_builder.create_read_expression(ctx)),
|
||||
@@ -919,15 +916,8 @@ impl<'a> ObjectRestSpread<'a> {
|
||||
ctx,
|
||||
);
|
||||
if let BindingPatternOrAssignmentTarget::BindingPattern(lhs) = lhs {
|
||||
let decl = VariableDeclarator::new(
|
||||
lhs.span(),
|
||||
decl.kind,
|
||||
lhs,
|
||||
None,
|
||||
Some(rhs),
|
||||
false,
|
||||
ctx,
|
||||
);
|
||||
let decl =
|
||||
VariableDeclarator::new(lhs.span(), lhs, None, Some(rhs), false, ctx);
|
||||
temp_decls.push(decl);
|
||||
}
|
||||
}
|
||||
@@ -945,7 +935,6 @@ impl<'a> ObjectRestSpread<'a> {
|
||||
mem::swap(&mut binding_pattern, &mut decl.id);
|
||||
let decl = VariableDeclarator::new(
|
||||
decl.span,
|
||||
decl.kind,
|
||||
binding_pattern,
|
||||
None,
|
||||
Some(reference_builder.create_read_expression(ctx)),
|
||||
@@ -989,10 +978,9 @@ impl<'a> ObjectRestSpread<'a> {
|
||||
let id = mem::replace(pat, bound_identifier.create_binding_pattern(ctx));
|
||||
|
||||
let init = bound_identifier.create_read_expression(ctx);
|
||||
let mut decl =
|
||||
VariableDeclarator::new(SPAN, state.kind, id, None, Some(init), false, ctx);
|
||||
let mut decl = VariableDeclarator::new(SPAN, id, None, Some(init), false, ctx);
|
||||
let mut decls = self
|
||||
.transform_variable_declarator(&mut decl, ctx)
|
||||
.transform_variable_declarator(&mut decl, state.kind, ctx)
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
decls.extend(data);
|
||||
@@ -1058,15 +1046,7 @@ impl<'a> ObjectRestSpread<'a> {
|
||||
let p = bound_identifier.create_binding_pattern(ctx);
|
||||
let mut lhs = bound_identifier.create_read_expression(ctx);
|
||||
mem::swap(&mut lhs, expr);
|
||||
new_decls.push(VariableDeclarator::new(
|
||||
SPAN,
|
||||
state.kind,
|
||||
p,
|
||||
None,
|
||||
Some(lhs),
|
||||
false,
|
||||
ctx,
|
||||
));
|
||||
new_decls.push(VariableDeclarator::new(SPAN, p, None, Some(lhs), false, ctx));
|
||||
Some(ArrayExpressionElement::from(bound_identifier.create_read_expression(ctx)))
|
||||
}
|
||||
}
|
||||
@@ -1160,10 +1140,8 @@ impl<'a> SpreadPair<'a> {
|
||||
"excluded",
|
||||
SymbolFlags::BlockScopedVariable | SymbolFlags::ConstVariable,
|
||||
);
|
||||
let kind = VariableDeclarationKind::Const;
|
||||
let declarator = VariableDeclarator::new(
|
||||
SPAN,
|
||||
kind,
|
||||
bound_identifier.create_binding_pattern(ctx),
|
||||
None,
|
||||
Some(key_expression),
|
||||
|
||||
@@ -341,7 +341,6 @@ impl<'a> ClassProperties<'a> {
|
||||
VariableDeclarationKind::Var,
|
||||
[VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
super_binding.create_binding_pattern(ctx),
|
||||
None,
|
||||
Some(super_func),
|
||||
|
||||
@@ -18,7 +18,6 @@ pub(super) fn create_variable_declaration<'a>(
|
||||
let kind = VariableDeclarationKind::Var;
|
||||
let declarator = VariableDeclarator::new(
|
||||
SPAN,
|
||||
kind,
|
||||
binding.create_binding_pattern(ctx),
|
||||
None,
|
||||
Some(init),
|
||||
|
||||
@@ -84,7 +84,6 @@ impl<'a> Traverse<'a, TransformState<'a>> for ExplicitResourceManagement<'a> {
|
||||
decl.kind = VariableDeclarationKind::Const;
|
||||
|
||||
let variable_declarator = decl.declarations.first_mut().unwrap();
|
||||
variable_declarator.kind = VariableDeclarationKind::Const;
|
||||
|
||||
let variable_declarator_binding_ident =
|
||||
variable_declarator.id.get_binding_identifier().unwrap();
|
||||
@@ -107,7 +106,6 @@ impl<'a> Traverse<'a, TransformState<'a>> for ExplicitResourceManagement<'a> {
|
||||
variable_decl_kind,
|
||||
[VariableDeclarator::new(
|
||||
SPAN,
|
||||
variable_decl_kind,
|
||||
binding_pattern,
|
||||
None,
|
||||
Some(temp_id.create_read_expression(ctx)),
|
||||
@@ -432,7 +430,6 @@ impl<'a> Traverse<'a, TransformState<'a>> for ExplicitResourceManagement<'a> {
|
||||
VariableDeclarationKind::Var,
|
||||
[VariableDeclarator::new(
|
||||
span,
|
||||
VariableDeclarationKind::Var,
|
||||
var_id.create_spanned_binding_pattern(span, ctx),
|
||||
None,
|
||||
Some(expr),
|
||||
@@ -524,10 +521,6 @@ impl<'a> Traverse<'a, TransformState<'a>> for ExplicitResourceManagement<'a> {
|
||||
var_decl.kind = VariableDeclarationKind::Var;
|
||||
let mut export_specifiers = ArenaVec::new_in(ctx);
|
||||
|
||||
for decl in &mut var_decl.declarations {
|
||||
decl.kind = VariableDeclarationKind::Var;
|
||||
}
|
||||
|
||||
var_decl.bound_names(&mut |ident| {
|
||||
*ctx.scoping_mut().symbol_flags_mut(ident.symbol_id()) =
|
||||
SymbolFlags::FunctionScopedVariable;
|
||||
@@ -570,7 +563,6 @@ impl<'a> Traverse<'a, TransformState<'a>> for ExplicitResourceManagement<'a> {
|
||||
var_declaration.kind = VariableDeclarationKind::Var;
|
||||
|
||||
for decl in &mut var_declaration.declarations {
|
||||
decl.kind = VariableDeclarationKind::Var;
|
||||
decl.id.bound_names(&mut |c| {
|
||||
*ctx.scoping_mut().symbol_flags_mut(c.symbol_id()) =
|
||||
SymbolFlags::FunctionScopedVariable;
|
||||
@@ -749,7 +741,6 @@ impl<'a> ExplicitResourceManagement<'a> {
|
||||
VariableDeclarationKind::Var,
|
||||
[VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
using_ctx.create_binding_pattern(ctx),
|
||||
None,
|
||||
Some(Expression::new_call_expression(
|
||||
@@ -884,7 +875,6 @@ impl<'a> ExplicitResourceManagement<'a> {
|
||||
VariableDeclarationKind::Var,
|
||||
[VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
using_ctx.create_binding_pattern(ctx),
|
||||
None,
|
||||
Some(Expression::new_call_expression(SPAN, callee, None, [], false, ctx)),
|
||||
@@ -1015,7 +1005,6 @@ impl<'a> ExplicitResourceManagement<'a> {
|
||||
VariableDeclarationKind::Var,
|
||||
[VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
binding.create_spanned_binding_pattern(original_span, ctx),
|
||||
None,
|
||||
Some(class_expr),
|
||||
|
||||
@@ -195,15 +195,7 @@ impl<'a> JsxSource<'a> {
|
||||
let id = filename_var.create_binding_pattern(ctx);
|
||||
let source_path = Str::from_str_in(&ctx.state.source_path.to_string_lossy(), ctx);
|
||||
let init = Expression::new_string_literal(SPAN, source_path, None, ctx);
|
||||
let decl = VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
id,
|
||||
None,
|
||||
Some(init),
|
||||
false,
|
||||
ctx,
|
||||
);
|
||||
let decl = VariableDeclarator::new(SPAN, id, None, Some(init), false, ctx);
|
||||
Some(decl)
|
||||
}
|
||||
|
||||
|
||||
@@ -177,7 +177,6 @@ impl<'a> Traverse<'a, TransformState<'a>> for ReactRefresh<'a> {
|
||||
let calls = self.registrations.iter().map(|(binding, persistent_id)| {
|
||||
variable_declarator_items.push(VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
binding.create_binding_pattern(ctx),
|
||||
None,
|
||||
None,
|
||||
|
||||
@@ -231,7 +231,6 @@ impl<'a> TaggedTemplateTransform {
|
||||
|
||||
let variable = VariableDeclarator::new(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
binding.create_binding_pattern(ctx),
|
||||
None,
|
||||
None,
|
||||
|
||||
@@ -295,15 +295,8 @@ impl<'a> TypeScriptEnum {
|
||||
enum_symbol_id,
|
||||
ctx,
|
||||
);
|
||||
let decl = VariableDeclarator::new(
|
||||
span,
|
||||
kind,
|
||||
binding,
|
||||
None,
|
||||
Some(call_expression),
|
||||
false,
|
||||
ctx,
|
||||
);
|
||||
let decl =
|
||||
VariableDeclarator::new(span, binding, None, Some(call_expression), false, ctx);
|
||||
[decl]
|
||||
};
|
||||
let variable_declaration =
|
||||
|
||||
@@ -177,7 +177,7 @@ impl<'a> TypeScriptModule {
|
||||
)
|
||||
}
|
||||
};
|
||||
let decl = VariableDeclarator::new(SPAN, kind, binding, None, Some(init), false, ctx);
|
||||
let decl = VariableDeclarator::new(SPAN, binding, None, Some(init), false, ctx);
|
||||
|
||||
Some(Declaration::new_variable_declaration(SPAN, kind, [decl], false, ctx))
|
||||
}
|
||||
|
||||
@@ -254,11 +254,11 @@ impl<'a> TypeScriptNamespace {
|
||||
}
|
||||
}
|
||||
Declaration::VariableDeclaration(var_decl) => {
|
||||
var_decl.declarations.iter().for_each(|decl| {
|
||||
if !decl.kind.is_const() {
|
||||
if !var_decl.kind.is_const() {
|
||||
var_decl.declarations.iter().for_each(|decl| {
|
||||
ctx.state.error(namespace_exporting_non_const(decl.span));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
let stmts =
|
||||
Self::handle_variable_declaration(var_decl, &uid_binding, ctx);
|
||||
new_stmts.extend(stmts);
|
||||
@@ -338,7 +338,7 @@ impl<'a> TypeScriptNamespace {
|
||||
let kind = VariableDeclarationKind::Let;
|
||||
let decl = {
|
||||
let pattern = binding.create_spanned_binding_pattern(binding_span, ctx);
|
||||
VariableDeclarator::new(span, kind, pattern, None, None, false, ctx)
|
||||
VariableDeclarator::new(span, pattern, None, None, false, ctx)
|
||||
};
|
||||
Declaration::new_variable_declaration(span, kind, [decl], false, ctx)
|
||||
}
|
||||
|
||||
@@ -763,7 +763,7 @@ impl<'a> ModuleRunnerTransform<'a> {
|
||||
let init = Expression::new_await_expression(SPAN, call, ctx);
|
||||
|
||||
let kind = VariableDeclarationKind::Const;
|
||||
let declarator = VariableDeclarator::new(SPAN, kind, pattern, None, Some(init), false, ctx);
|
||||
let declarator = VariableDeclarator::new(SPAN, pattern, None, Some(init), false, ctx);
|
||||
let declaration =
|
||||
Declaration::new_variable_declaration(span, kind, [declarator], false, ctx);
|
||||
Statement::from(declaration)
|
||||
@@ -888,8 +888,7 @@ impl<'a> ModuleRunnerTransform<'a> {
|
||||
);
|
||||
let pattern = binding.create_binding_pattern(ctx);
|
||||
let kind = VariableDeclarationKind::Const;
|
||||
let declarator =
|
||||
VariableDeclarator::new(SPAN, kind, pattern, None, Some(right), false, ctx);
|
||||
let declarator = VariableDeclarator::new(SPAN, pattern, None, Some(right), false, ctx);
|
||||
let declaration =
|
||||
Declaration::new_variable_declaration(span, kind, [declarator], false, ctx);
|
||||
Statement::from(declaration)
|
||||
|
||||
@@ -5211,7 +5211,6 @@ impl<'a, 't> GetAddress for VariableDeclarationWithoutDeclarations<'a, 't> {
|
||||
pub(crate) const OFFSET_VARIABLE_DECLARATOR_NODE_ID: usize =
|
||||
offset_of!(VariableDeclarator, node_id);
|
||||
pub(crate) const OFFSET_VARIABLE_DECLARATOR_SPAN: usize = offset_of!(VariableDeclarator, span);
|
||||
pub(crate) const OFFSET_VARIABLE_DECLARATOR_KIND: usize = offset_of!(VariableDeclarator, kind);
|
||||
pub(crate) const OFFSET_VARIABLE_DECLARATOR_ID: usize = offset_of!(VariableDeclarator, id);
|
||||
pub(crate) const OFFSET_VARIABLE_DECLARATOR_TYPE_ANNOTATION: usize =
|
||||
offset_of!(VariableDeclarator, type_annotation);
|
||||
@@ -5239,14 +5238,6 @@ impl<'a, 't> VariableDeclaratorWithoutId<'a, 't> {
|
||||
unsafe { &*((self.0 as *const u8).add(OFFSET_VARIABLE_DECLARATOR_SPAN) as *const Span) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn kind(self) -> &'t VariableDeclarationKind {
|
||||
unsafe {
|
||||
&*((self.0 as *const u8).add(OFFSET_VARIABLE_DECLARATOR_KIND)
|
||||
as *const VariableDeclarationKind)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn type_annotation(self) -> &'t Option<ArenaBox<'a, TSTypeAnnotation<'a>>> {
|
||||
unsafe {
|
||||
@@ -5296,14 +5287,6 @@ impl<'a, 't> VariableDeclaratorWithoutTypeAnnotation<'a, 't> {
|
||||
unsafe { &*((self.0 as *const u8).add(OFFSET_VARIABLE_DECLARATOR_SPAN) as *const Span) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn kind(self) -> &'t VariableDeclarationKind {
|
||||
unsafe {
|
||||
&*((self.0 as *const u8).add(OFFSET_VARIABLE_DECLARATOR_KIND)
|
||||
as *const VariableDeclarationKind)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(self) -> &'t BindingPattern<'a> {
|
||||
unsafe {
|
||||
@@ -5353,14 +5336,6 @@ impl<'a, 't> VariableDeclaratorWithoutInit<'a, 't> {
|
||||
unsafe { &*((self.0 as *const u8).add(OFFSET_VARIABLE_DECLARATOR_SPAN) as *const Span) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn kind(self) -> &'t VariableDeclarationKind {
|
||||
unsafe {
|
||||
&*((self.0 as *const u8).add(OFFSET_VARIABLE_DECLARATOR_KIND)
|
||||
as *const VariableDeclarationKind)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(self) -> &'t BindingPattern<'a> {
|
||||
unsafe {
|
||||
|
||||
@@ -1365,7 +1365,7 @@ function deserializeVariableDeclarator(pos) {
|
||||
type: "VariableDeclarator",
|
||||
id: null,
|
||||
init: null,
|
||||
definite: deserializeBool(pos + 13),
|
||||
definite: deserializeBool(pos + 12),
|
||||
start: deserializeI32(pos),
|
||||
end: deserializeI32(pos + 4),
|
||||
},
|
||||
|
||||
@@ -1488,7 +1488,7 @@ function deserializeVariableDeclarator(pos) {
|
||||
type: "VariableDeclarator",
|
||||
id: null,
|
||||
init: null,
|
||||
definite: deserializeBool(pos + 13),
|
||||
definite: deserializeBool(pos + 12),
|
||||
start: deserializeI32(pos),
|
||||
end: deserializeI32(pos + 4),
|
||||
parent,
|
||||
|
||||
@@ -1493,7 +1493,7 @@ function deserializeVariableDeclarator(pos) {
|
||||
type: "VariableDeclarator",
|
||||
id: null,
|
||||
init: null,
|
||||
definite: deserializeBool(pos + 13),
|
||||
definite: deserializeBool(pos + 12),
|
||||
start: (start = deserializeI32(pos)),
|
||||
end: (end = deserializeI32(pos + 4)),
|
||||
range: [start, end],
|
||||
|
||||
@@ -1615,7 +1615,7 @@ function deserializeVariableDeclarator(pos) {
|
||||
type: "VariableDeclarator",
|
||||
id: null,
|
||||
init: null,
|
||||
definite: deserializeBool(pos + 13),
|
||||
definite: deserializeBool(pos + 12),
|
||||
start: (start = deserializeI32(pos)),
|
||||
end: (end = deserializeI32(pos + 4)),
|
||||
range: [start, end],
|
||||
|
||||
@@ -3000,7 +3000,7 @@ export class VariableDeclarator {
|
||||
|
||||
get definite() {
|
||||
const internal = this.#internal;
|
||||
return constructBool(internal.pos + 13, internal.ast);
|
||||
return constructBool(internal.pos + 12, internal.ast);
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
|
||||
Reference in new issue
Block a user