mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
refactor(isolated-declarations): simplify handling VariableDeclaration transform (#5916)
This commit is contained in:
parent
2fd5c2a53b
commit
67b42205a2
2 changed files with 81 additions and 107 deletions
|
|
@ -19,7 +19,7 @@ mod scope;
|
||||||
mod signatures;
|
mod signatures;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
use std::{cell::RefCell, collections::VecDeque, mem};
|
use std::{cell::RefCell, mem};
|
||||||
|
|
||||||
use diagnostics::function_with_assigning_properties;
|
use diagnostics::function_with_assigning_properties;
|
||||||
use oxc_allocator::{Allocator, CloneIn};
|
use oxc_allocator::{Allocator, CloneIn};
|
||||||
|
|
@ -156,7 +156,6 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let filtered_stmts = Self::remove_function_overloads_implementation(filtered_stmts);
|
let filtered_stmts = Self::remove_function_overloads_implementation(filtered_stmts);
|
||||||
|
|
||||||
let mut stmts = self.ast.vec_from_iter(filtered_stmts);
|
let mut stmts = self.ast.vec_from_iter(filtered_stmts);
|
||||||
|
|
@ -183,10 +182,10 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
// https://github.com/microsoft/TypeScript/pull/58912
|
// https://github.com/microsoft/TypeScript/pull/58912
|
||||||
let mut need_empty_export_marker = true;
|
let mut need_empty_export_marker = true;
|
||||||
|
|
||||||
let mut new_stmts = Vec::new();
|
let mut new_stmts = self.ast.vec();
|
||||||
let mut variables_declarations = VecDeque::new();
|
|
||||||
let mut variable_transformed_indexes = VecDeque::new();
|
// Use span to identify transformed nodes
|
||||||
let mut transformed_indexes = FxHashSet::default();
|
let mut transformed_spans: FxHashSet<Span> = FxHashSet::default();
|
||||||
|
|
||||||
let filtered_stmts = stmts.iter().filter_map(|stmt| {
|
let filtered_stmts = stmts.iter().filter_map(|stmt| {
|
||||||
if stmt.is_declaration() || stmt.is_module_declaration() {
|
if stmt.is_declaration() || stmt.is_module_declaration() {
|
||||||
|
|
@ -205,32 +204,17 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
for mut stmt in filtered_stmts {
|
for mut stmt in filtered_stmts {
|
||||||
match stmt {
|
match stmt {
|
||||||
match_declaration!(Statement) => {
|
match_declaration!(Statement) => {
|
||||||
match stmt.to_declaration() {
|
if let Declaration::TSModuleDeclaration(decl) = stmt.to_declaration() {
|
||||||
Declaration::VariableDeclaration(decl) => {
|
if self.has_internal_annotation(decl.span) {
|
||||||
if self.has_internal_annotation(decl.span) {
|
continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
variables_declarations.push_back(
|
|
||||||
// SAFETY: `ast.copy` is unsound! We need to fix.
|
|
||||||
unsafe { self.ast.copy(&decl.declarations) }
|
|
||||||
.into_iter()
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
);
|
|
||||||
variable_transformed_indexes.push_back(FxHashSet::default());
|
|
||||||
}
|
}
|
||||||
Declaration::TSModuleDeclaration(decl) => {
|
// declare global { ... } or declare module "foo" { ... }
|
||||||
if self.has_internal_annotation(decl.span) {
|
// We need to emit it anyway
|
||||||
continue;
|
if decl.kind.is_global() || decl.id.is_string_literal() {
|
||||||
}
|
// We need to visit the module declaration to collect all references
|
||||||
// declare global { ... } or declare module "foo" { ... }
|
self.scope.visit_ts_module_declaration(decl);
|
||||||
// We need to emit it anyway
|
transformed_spans.insert(decl.span);
|
||||||
if decl.kind.is_global() || decl.id.is_string_literal() {
|
|
||||||
// We need to visit the module declaration to collect all references
|
|
||||||
self.scope.visit_ts_module_declaration(decl);
|
|
||||||
transformed_indexes.insert(new_stmts.len());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
if !self.has_internal_annotation(stmt.span()) {
|
if !self.has_internal_annotation(stmt.span()) {
|
||||||
new_stmts.push(stmt);
|
new_stmts.push(stmt);
|
||||||
|
|
@ -242,16 +226,16 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
if self.has_internal_annotation(decl.span) {
|
if self.has_internal_annotation(decl.span) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
transformed_indexes.insert(new_stmts.len());
|
transformed_spans.insert(decl.span);
|
||||||
if let Some((var_decl, new_decl)) =
|
if let Some((var_decl, new_decl)) =
|
||||||
self.transform_export_default_declaration(decl)
|
self.transform_export_default_declaration(decl)
|
||||||
{
|
{
|
||||||
if let Some(var_decl) = var_decl {
|
if let Some(var_decl) = var_decl {
|
||||||
|
transformed_spans.insert(var_decl.span);
|
||||||
self.scope.visit_variable_declaration(&var_decl);
|
self.scope.visit_variable_declaration(&var_decl);
|
||||||
new_stmts.push(Statement::VariableDeclaration(
|
new_stmts.push(Statement::VariableDeclaration(
|
||||||
self.ast.alloc(var_decl),
|
self.ast.alloc(var_decl),
|
||||||
));
|
));
|
||||||
transformed_indexes.insert(new_stmts.len());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.scope.visit_export_default_declaration(&new_decl);
|
self.scope.visit_export_default_declaration(&new_decl);
|
||||||
|
|
@ -266,7 +250,7 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
if self.has_internal_annotation(decl.span) {
|
if self.has_internal_annotation(decl.span) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
transformed_indexes.insert(new_stmts.len());
|
transformed_spans.insert(decl.span);
|
||||||
if let Some(new_decl) = self.transform_export_named_declaration(decl) {
|
if let Some(new_decl) = self.transform_export_named_declaration(decl) {
|
||||||
*decl = self.ast.alloc(new_decl);
|
*decl = self.ast.alloc(new_decl);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -281,7 +265,7 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
if self.has_internal_annotation(module_declaration.span()) {
|
if self.has_internal_annotation(module_declaration.span()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
transformed_indexes.insert(new_stmts.len());
|
transformed_spans.insert(module_declaration.span());
|
||||||
self.scope.visit_module_declaration(module_declaration);
|
self.scope.visit_module_declaration(module_declaration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -292,105 +276,100 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let last_transformed_len = transformed_spans.len();
|
||||||
// 5. Transform statements until no more transformation can be done
|
// 5. Transform statements until no more transformation can be done
|
||||||
let last_transformed_len = transformed_indexes.len();
|
loop {
|
||||||
let mut last_reference_len = 0;
|
let cur_transformed_len = transformed_spans.len();
|
||||||
while last_reference_len != self.scope.references_len() {
|
for stmt in new_stmts.iter_mut() {
|
||||||
last_reference_len = self.scope.references_len();
|
let Some(decl) = stmt.as_declaration_mut() else { continue };
|
||||||
|
|
||||||
let mut variables_declarations_iter = variables_declarations.iter_mut();
|
// Skip transformed declarations
|
||||||
let mut variable_transformed_indexes_iter = variable_transformed_indexes.iter_mut();
|
if transformed_spans.contains(&decl.span()) {
|
||||||
|
|
||||||
for (i, stmt) in new_stmts.iter_mut().enumerate() {
|
|
||||||
if transformed_indexes.contains(&i) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let Some(decl) = stmt.as_declaration() else { continue };
|
|
||||||
|
|
||||||
if let Declaration::VariableDeclaration(_) = decl {
|
if let Declaration::VariableDeclaration(declaration) = decl {
|
||||||
let Some(cur_variable_declarations) = variables_declarations_iter.next() else {
|
let mut all_declarator_has_transformed = true;
|
||||||
unreachable!()
|
for declarator in declaration.declarations.iter_mut() {
|
||||||
};
|
if transformed_spans.contains(&declarator.span) {
|
||||||
let Some(cur_transformed_indexes) = variable_transformed_indexes_iter.next()
|
|
||||||
else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
|
|
||||||
for (ii, declarator) in cur_variable_declarations.iter_mut().enumerate() {
|
|
||||||
if cur_transformed_indexes.contains(&ii) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(decl) = self.transform_variable_declarator(declarator, true) {
|
if let Some(new_declarator) =
|
||||||
self.scope.visit_variable_declarator(&decl);
|
self.transform_variable_declarator(declarator, true)
|
||||||
cur_transformed_indexes.insert(ii);
|
{
|
||||||
*declarator = decl;
|
self.scope.visit_variable_declarator(&new_declarator);
|
||||||
|
transformed_spans.insert(new_declarator.span());
|
||||||
|
*declarator = new_declarator;
|
||||||
|
} else {
|
||||||
|
all_declarator_has_transformed = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(decl) = self.transform_declaration(decl, true) {
|
if all_declarator_has_transformed {
|
||||||
self.scope.visit_declaration(&decl);
|
declaration.declare = self.is_declare();
|
||||||
transformed_indexes.insert(i);
|
transformed_spans.insert(declaration.span);
|
||||||
*stmt = Statement::from(decl);
|
}
|
||||||
|
} else if let Some(new_decl) = self.transform_declaration(decl, true) {
|
||||||
|
self.scope.visit_declaration(&new_decl);
|
||||||
|
transformed_spans.insert(new_decl.span());
|
||||||
|
*decl = new_decl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use transformed_spans to weather we need to continue
|
||||||
|
if cur_transformed_len == transformed_spans.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If any declaration is transformed in previous step, we don't need to add empty export marker
|
||||||
|
if last_transformed_len != 0 && last_transformed_len == transformed_spans.len() {
|
||||||
|
need_empty_export_marker = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Transform variable/using declarations, import statements, remove unused imports
|
// 6. Transform variable/using declarations, import statements, remove unused imports
|
||||||
// 7. Return transformed statements
|
new_stmts.retain_mut(|stmt| {
|
||||||
let mut new_ast_stmts = self.ast.vec_with_capacity(transformed_indexes.len());
|
if transformed_spans.contains(&stmt.span()) {
|
||||||
for (index, stmt) in new_stmts.into_iter().enumerate() {
|
return true;
|
||||||
|
}
|
||||||
match stmt {
|
match stmt {
|
||||||
_ if transformed_indexes.contains(&index) => {
|
|
||||||
new_ast_stmts.push(stmt);
|
|
||||||
}
|
|
||||||
Statement::VariableDeclaration(decl) => {
|
|
||||||
let indexes =
|
|
||||||
variable_transformed_indexes.pop_front().unwrap_or_else(|| unreachable!());
|
|
||||||
let declarations =
|
|
||||||
variables_declarations.pop_front().unwrap_or_else(|| unreachable!());
|
|
||||||
|
|
||||||
if !indexes.is_empty() {
|
|
||||||
let variables_declaration = self
|
|
||||||
.transform_variable_declaration_with_new_declarations(
|
|
||||||
&decl,
|
|
||||||
self.ast.vec_from_iter(
|
|
||||||
declarations
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter(|(i, _)| indexes.contains(i))
|
|
||||||
.map(|(_, decl)| decl),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
new_ast_stmts.push(Statement::VariableDeclaration(variables_declaration));
|
|
||||||
transformed_indexes.insert(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Statement::ImportDeclaration(decl) => {
|
Statement::ImportDeclaration(decl) => {
|
||||||
// We must transform this in the end, because we need to know all references
|
// We must transform this in the end, because we need to know all references
|
||||||
if decl.specifiers.is_none() {
|
if decl.specifiers.is_none() {
|
||||||
new_ast_stmts.push(Statement::ImportDeclaration(decl));
|
true
|
||||||
} else if let Some(decl) = self.transform_import_declaration(&decl) {
|
} else if let Some(new_decl) = self.transform_import_declaration(decl) {
|
||||||
new_ast_stmts.push(Statement::ImportDeclaration(decl));
|
*decl = new_decl;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
Statement::VariableDeclaration(ref mut decl) => {
|
||||||
|
// If here just one declaration, that means this variable declaration doesn't referenced by any other
|
||||||
|
if decl.declarations.len() == 1 {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
// Remove unreferenced declarations
|
||||||
|
decl.declarations
|
||||||
|
.retain(|declarator| transformed_spans.contains(&declarator.span));
|
||||||
|
decl.declare = self.is_declare();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
if !transformed_indexes.is_empty() && last_transformed_len == transformed_indexes.len() {
|
|
||||||
need_empty_export_marker = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if need_empty_export_marker {
|
if need_empty_export_marker {
|
||||||
let specifiers = self.ast.vec();
|
let specifiers = self.ast.vec();
|
||||||
let kind = ImportOrExportKind::Value;
|
let kind = ImportOrExportKind::Value;
|
||||||
let empty_export =
|
let empty_export =
|
||||||
self.ast.alloc_export_named_declaration(SPAN, None, specifiers, None, kind, NONE);
|
self.ast.alloc_export_named_declaration(SPAN, None, specifiers, None, kind, NONE);
|
||||||
new_ast_stmts
|
new_stmts
|
||||||
.push(Statement::from(ModuleDeclaration::ExportNamedDeclaration(empty_export)));
|
.push(Statement::from(ModuleDeclaration::ExportNamedDeclaration(empty_export)));
|
||||||
}
|
}
|
||||||
|
|
||||||
new_ast_stmts
|
new_stmts
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_function_overloads_implementation<T>(
|
pub fn remove_function_overloads_implementation<T>(
|
||||||
|
|
|
||||||
|
|
@ -55,11 +55,6 @@ impl<'a> ScopeTree<'a> {
|
||||||
scope.value_references.contains(name) || scope.type_references.contains(name)
|
scope.value_references.contains(name) || scope.type_references.contains(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn references_len(&self) -> usize {
|
|
||||||
let scope = self.levels.last().unwrap();
|
|
||||||
scope.value_references.len() + scope.type_references.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_value_binding(&mut self, ident: Atom<'a>) {
|
fn add_value_binding(&mut self, ident: Atom<'a>) {
|
||||||
let scope = self.levels.last_mut().unwrap();
|
let scope = self.levels.last_mut().unwrap();
|
||||||
scope.value_bindings.insert(ident);
|
scope.value_bindings.insert(ident);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue