Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 86 additions & 10 deletions crates/oxc_ast/src/ast/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,7 @@ pub struct ParenthesizedExpression<'a> {
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
#[repr(u8)]
pub enum Statement<'a> {
// Statements
BlockStatement(Box<'a, BlockStatement<'a>>),
Expand All @@ -1215,7 +1216,19 @@ pub enum Statement<'a> {
WithStatement(Box<'a, WithStatement<'a>>),

ModuleDeclaration(Box<'a, ModuleDeclaration<'a>>),
Declaration(Declaration<'a>),

// SAFETY: Discriminants and types here MUST match discriminants for same variants in `Declaration`,
// to allow transmuting between the two types
VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 32,
FunctionDeclaration(Box<'a, Function<'a>>) = 33,
ClassDeclaration(Box<'a, Class<'a>>) = 34,
UsingDeclaration(Box<'a, UsingDeclaration<'a>>) = 35,

TSTypeAliasDeclaration(Box<'a, TSTypeAliasDeclaration<'a>>) = 36,
TSInterfaceDeclaration(Box<'a, TSInterfaceDeclaration<'a>>) = 37,
TSEnumDeclaration(Box<'a, TSEnumDeclaration<'a>>) = 38,
TSModuleDeclaration(Box<'a, TSModuleDeclaration<'a>>) = 39,
TSImportEqualsDeclaration(Box<'a, TSImportEqualsDeclaration<'a>>) = 40,
}

impl<'a> Statement<'a> {
Expand All @@ -1229,6 +1242,46 @@ impl<'a> Statement<'a> {
| Statement::WhileStatement(_)
)
}

#[inline]
pub fn is_declaration(&self) -> bool {
matches!(
self,
Self::VariableDeclaration(_)
| Self::FunctionDeclaration(_)
| Self::ClassDeclaration(_)
| Self::UsingDeclaration(_)
| Self::TSTypeAliasDeclaration(_)
| Self::TSInterfaceDeclaration(_)
| Self::TSEnumDeclaration(_)
| Self::TSModuleDeclaration(_)
| Self::TSImportEqualsDeclaration(_)
)
}

#[inline]
pub fn as_declaration(&self) -> Option<&Declaration<'a>> {
if self.is_declaration() {
#[allow(clippy::ptr_as_ptr)]
// SAFETY: Transmute is safe because discriminants + types are identical between
// `Statement` and `Declaration` for declaration variants
Some(unsafe { &*(self as *const _ as *const Declaration<'_>) })
} else {
None
}
}

#[inline]
pub fn as_declaration_mut(&mut self) -> Option<&mut Declaration<'a>> {
if self.is_declaration() {
#[allow(clippy::ptr_as_ptr)]
// SAFETY: Transmute is safe because discriminants + types are identical between
// `Statement` and `Declaration` for declaration variants
Some(unsafe { &mut *(self as *mut _ as *mut Declaration<'_>) })
} else {
None
}
}
}

/// Directive Prologue
Expand Down Expand Up @@ -1269,17 +1322,20 @@ pub struct BlockStatement<'a> {
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
#[repr(u8)]
pub enum Declaration<'a> {
VariableDeclaration(Box<'a, VariableDeclaration<'a>>),
FunctionDeclaration(Box<'a, Function<'a>>),
ClassDeclaration(Box<'a, Class<'a>>),
UsingDeclaration(Box<'a, UsingDeclaration<'a>>),
// SAFETY: Discriminants and types here MUST match discriminants for same variants in `Statement`,
// to allow transmuting between the two types
VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 32,
FunctionDeclaration(Box<'a, Function<'a>>) = 33,
ClassDeclaration(Box<'a, Class<'a>>) = 34,
UsingDeclaration(Box<'a, UsingDeclaration<'a>>) = 35,

TSTypeAliasDeclaration(Box<'a, TSTypeAliasDeclaration<'a>>),
TSInterfaceDeclaration(Box<'a, TSInterfaceDeclaration<'a>>),
TSEnumDeclaration(Box<'a, TSEnumDeclaration<'a>>),
TSModuleDeclaration(Box<'a, TSModuleDeclaration<'a>>),
TSImportEqualsDeclaration(Box<'a, TSImportEqualsDeclaration<'a>>),
TSTypeAliasDeclaration(Box<'a, TSTypeAliasDeclaration<'a>>) = 36,
TSInterfaceDeclaration(Box<'a, TSInterfaceDeclaration<'a>>) = 37,
TSEnumDeclaration(Box<'a, TSEnumDeclaration<'a>>) = 38,
TSModuleDeclaration(Box<'a, TSModuleDeclaration<'a>>) = 39,
TSImportEqualsDeclaration(Box<'a, TSImportEqualsDeclaration<'a>>) = 40,
}

impl<'a> Declaration<'a> {
Expand All @@ -1305,6 +1361,26 @@ impl<'a> Declaration<'a> {
}
}

impl<'a> From<Declaration<'a>> for Statement<'a> {
fn from(val: Declaration<'a>) -> Self {
// Compiler should implement this as zero-cost transmute as discriminants
// for `Declaration` and `Statement` are aligned
match val {
Declaration::VariableDeclaration(decl) => Statement::VariableDeclaration(decl),
Declaration::FunctionDeclaration(decl) => Statement::FunctionDeclaration(decl),
Declaration::ClassDeclaration(decl) => Statement::ClassDeclaration(decl),
Declaration::UsingDeclaration(decl) => Statement::UsingDeclaration(decl),
Declaration::TSTypeAliasDeclaration(decl) => Statement::TSTypeAliasDeclaration(decl),
Declaration::TSInterfaceDeclaration(decl) => Statement::TSInterfaceDeclaration(decl),
Declaration::TSEnumDeclaration(decl) => Statement::TSEnumDeclaration(decl),
Declaration::TSModuleDeclaration(decl) => Statement::TSModuleDeclaration(decl),
Declaration::TSImportEqualsDeclaration(decl) => {
Statement::TSImportEqualsDeclaration(decl)
}
}
}
}

/// Variable Declaration
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
Expand Down
10 changes: 3 additions & 7 deletions crates/oxc_ast/src/ast_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,11 +261,7 @@ impl<'a> AstBuilder<'a> {
declarations: Vec<'a, VariableDeclarator<'a>>,
is_await: bool,
) -> Statement<'a> {
Statement::Declaration(Declaration::UsingDeclaration(self.alloc(UsingDeclaration {
span,
is_await,
declarations,
})))
Statement::UsingDeclaration(self.alloc(UsingDeclaration { span, is_await, declarations }))
}

pub fn do_while_statement(
Expand Down Expand Up @@ -806,7 +802,7 @@ impl<'a> AstBuilder<'a> {

/* ---------- Functions ---------- */
pub fn function_declaration(&self, func: Box<'a, Function<'a>>) -> Statement<'a> {
Statement::Declaration(Declaration::FunctionDeclaration(func))
Statement::FunctionDeclaration(func)
}

pub fn formal_parameters(
Expand Down Expand Up @@ -916,7 +912,7 @@ impl<'a> AstBuilder<'a> {
}

pub fn class_declaration(&self, class: Box<'a, Class<'a>>) -> Statement<'a> {
Statement::Declaration(Declaration::ClassDeclaration(class))
Statement::ClassDeclaration(class)
}

pub fn static_block(&self, span: Span, body: Vec<'a, Statement<'a>>) -> ClassElement<'a> {
Expand Down
10 changes: 9 additions & 1 deletion crates/oxc_ast/src/span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,15 @@ impl<'a> GetSpan for Statement<'a> {
Self::WhileStatement(stmt) => stmt.span,
Self::WithStatement(stmt) => stmt.span,
Self::ModuleDeclaration(decl) => decl.span(),
Self::Declaration(decl) => decl.span(),
Self::VariableDeclaration(decl) => decl.span,
Self::FunctionDeclaration(decl) => decl.span,
Self::ClassDeclaration(decl) => decl.span,
Self::UsingDeclaration(decl) => decl.span,
Self::TSTypeAliasDeclaration(decl) => decl.span,
Self::TSInterfaceDeclaration(decl) => decl.span,
Self::TSEnumDeclaration(decl) => decl.span,
Self::TSModuleDeclaration(decl) => decl.span,
Self::TSImportEqualsDeclaration(decl) => decl.span,
}
}
}
Expand Down
15 changes: 14 additions & 1 deletion crates/oxc_ast/src/visit/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,7 @@ pub mod walk {
}
}

#[allow(clippy::missing_panics_doc)]
pub fn walk_statement<'a, V: Visit<'a>>(visitor: &mut V, stmt: &Statement<'a>) {
match stmt {
Statement::BlockStatement(stmt) => visitor.visit_block_statement(stmt),
Expand All @@ -868,7 +869,19 @@ pub mod walk {
Statement::WithStatement(stmt) => visitor.visit_with_statement(stmt),

Statement::ModuleDeclaration(decl) => visitor.visit_module_declaration(decl),
Statement::Declaration(decl) => visitor.visit_declaration(decl),

Statement::VariableDeclaration(_)
| Statement::FunctionDeclaration(_)
| Statement::ClassDeclaration(_)
| Statement::UsingDeclaration(_)
| Statement::TSTypeAliasDeclaration(_)
| Statement::TSInterfaceDeclaration(_)
| Statement::TSEnumDeclaration(_)
| Statement::TSModuleDeclaration(_)
| Statement::TSImportEqualsDeclaration(_) => {
let decl = stmt.as_declaration().unwrap();
visitor.visit_declaration(decl);
}
}
}

Expand Down
15 changes: 14 additions & 1 deletion crates/oxc_ast/src/visit/visit_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,7 @@ pub mod walk_mut {
}
}

#[allow(clippy::missing_panics_doc)]
pub fn walk_statement_mut<'a, V: VisitMut<'a>>(visitor: &mut V, stmt: &mut Statement<'a>) {
match stmt {
Statement::BlockStatement(stmt) => visitor.visit_block_statement(stmt),
Expand All @@ -877,7 +878,19 @@ pub mod walk_mut {
Statement::WithStatement(stmt) => visitor.visit_with_statement(stmt),

Statement::ModuleDeclaration(decl) => visitor.visit_module_declaration(decl),
Statement::Declaration(decl) => visitor.visit_declaration(decl),

Statement::VariableDeclaration(_)
| Statement::FunctionDeclaration(_)
| Statement::ClassDeclaration(_)
| Statement::UsingDeclaration(_)
| Statement::TSTypeAliasDeclaration(_)
| Statement::TSInterfaceDeclaration(_)
| Statement::TSEnumDeclaration(_)
| Statement::TSModuleDeclaration(_)
| Statement::TSImportEqualsDeclaration(_) => {
let decl = stmt.as_declaration_mut().unwrap();
visitor.visit_declaration(decl);
}
}
}

Expand Down
14 changes: 13 additions & 1 deletion crates/oxc_codegen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for Statement<'a> {
Self::BreakStatement(stmt) => stmt.gen(p, ctx),
Self::ContinueStatement(stmt) => stmt.gen(p, ctx),
Self::DebuggerStatement(stmt) => stmt.gen(p, ctx),
Self::Declaration(decl) => decl.gen(p, ctx),
Self::DoWhileStatement(stmt) => stmt.gen(p, ctx),
Self::EmptyStatement(stmt) => stmt.gen(p, ctx),
Self::ExpressionStatement(stmt) => stmt.gen(p, ctx),
Expand All @@ -104,6 +103,19 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for Statement<'a> {
Self::TryStatement(stmt) => stmt.gen(p, ctx),
Self::WhileStatement(stmt) => stmt.gen(p, ctx),
Self::WithStatement(stmt) => stmt.gen(p, ctx),

Self::VariableDeclaration(_)
| Self::FunctionDeclaration(_)
| Self::ClassDeclaration(_)
| Self::UsingDeclaration(_)
| Self::TSTypeAliasDeclaration(_)
| Self::TSInterfaceDeclaration(_)
| Self::TSEnumDeclaration(_)
| Self::TSModuleDeclaration(_)
| Self::TSImportEqualsDeclaration(_) => {
let decl = self.as_declaration().unwrap();
decl.gen(p, ctx);
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ impl<const MINIFY: bool> Codegen<MINIFY> {
}
}
for stmt in statements {
if let Statement::Declaration(decl) = stmt {
if let Some(decl) = stmt.as_declaration() {
if decl.is_typescript_syntax()
&& !self.options.enable_typescript
&& !matches!(decl, Declaration::TSEnumDeclaration(_))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ pub fn check_block_statement(block: &BlockStatement) -> StatementReturnStatus {
#[cfg(test)]
mod tests {
use oxc_allocator::Allocator;
use oxc_ast::ast::{Declaration, Program};
use oxc_ast::ast::Program;
use oxc_parser::Parser;
use oxc_span::SourceType;

Expand All @@ -262,9 +262,7 @@ mod tests {
let program = ret.program;
let Program { body, .. } = program;
let stmt = body.first().unwrap();
let Statement::Declaration(Declaration::FunctionDeclaration(func)) = stmt else {
unreachable!()
};
let Statement::FunctionDeclaration(func) = stmt else { unreachable!() };

let first_statement = &func.body.as_ref().unwrap().statements[0];

Expand Down
48 changes: 23 additions & 25 deletions crates/oxc_linter/src/rules/eslint/no_case_declarations.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use oxc_ast::{
ast::{Declaration, Statement, VariableDeclarationKind},
ast::{Statement, VariableDeclarationKind},
AstKind,
};
use oxc_diagnostics::{
Expand Down Expand Up @@ -54,31 +54,29 @@ impl Rule for NoCaseDeclarations {
let consequent = &switch_case.consequent;

for stmt in consequent {
if let Statement::Declaration(dcl) = stmt {
match dcl {
Declaration::FunctionDeclaration(d) => {
let start = d.span.start;
let end = start + 8;
ctx.diagnostic(NoCaseDeclarationsDiagnostic(Span::new(start, end)));
}
Declaration::ClassDeclaration(d) => {
let start = d.span.start;
let end = start + 5;
ctx.diagnostic(NoCaseDeclarationsDiagnostic(Span::new(start, end)));
}
Declaration::VariableDeclaration(var) if var.kind.is_lexical() => {
let start = var.span.start;
let end = match var.kind {
VariableDeclarationKind::Var => unreachable!(),
VariableDeclarationKind::Const => 5,
VariableDeclarationKind::Let => 3,
};
let end = start + end;
ctx.diagnostic(NoCaseDeclarationsDiagnostic(Span::new(start, end)));
}
_ => {}
match stmt {
Statement::FunctionDeclaration(d) => {
let start = d.span.start;
let end = start + 8;
ctx.diagnostic(NoCaseDeclarationsDiagnostic(Span::new(start, end)));
}
};
Statement::ClassDeclaration(d) => {
let start = d.span.start;
let end = start + 5;
ctx.diagnostic(NoCaseDeclarationsDiagnostic(Span::new(start, end)));
}
Statement::VariableDeclaration(var) if var.kind.is_lexical() => {
let start = var.span.start;
let end = match var.kind {
VariableDeclarationKind::Var => unreachable!(),
VariableDeclarationKind::Const => 5,
VariableDeclarationKind::Let => 3,
};
let end = start + end;
ctx.diagnostic(NoCaseDeclarationsDiagnostic(Span::new(start, end)));
}
_ => {}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,10 @@ impl GetMethod for Statement<'_> {
fn get_method(&self) -> Option<Method> {
match self {
Statement::ModuleDeclaration(decl) => decl.get_method(),
Statement::Declaration(decl) => decl.get_method(),
_ => None,
_ => match self.as_declaration() {
Some(decl) => decl.get_method(),
None => None,
},
}
}
}
Expand Down
Loading