diff --git a/crates/oxc_isolated_declarations/src/diagnostics.rs b/crates/oxc_isolated_declarations/src/diagnostics.rs index 4577147b7d3805..6b6edfdd94652d 100644 --- a/crates/oxc_isolated_declarations/src/diagnostics.rs +++ b/crates/oxc_isolated_declarations/src/diagnostics.rs @@ -112,7 +112,6 @@ pub fn implicitly_adding_undefined_to_type(span: Span) -> OxcDiagnostic { .with_label(span) } -#[allow(dead_code)] pub fn function_with_assigning_properties(span: Span) -> OxcDiagnostic { OxcDiagnostic::error( "TS9023: Assigning properties to functions without declaring them is not supported with --isolatedDeclarations. Add an explicit declaration for the properties assigned to this function.", diff --git a/crates/oxc_isolated_declarations/src/lib.rs b/crates/oxc_isolated_declarations/src/lib.rs index 285b653b4f96c4..e40e9064cc5a37 100644 --- a/crates/oxc_isolated_declarations/src/lib.rs +++ b/crates/oxc_isolated_declarations/src/lib.rs @@ -19,11 +19,13 @@ mod types; use std::{cell::RefCell, collections::VecDeque, mem}; +use diagnostics::function_with_assigning_properties; use oxc_allocator::Allocator; #[allow(clippy::wildcard_imports)] use oxc_ast::{ast::*, AstBuilder, Visit}; use oxc_diagnostics::OxcDiagnostic; use oxc_span::{Atom, SourceType, SPAN}; +use rustc_hash::FxHashSet; use crate::scope::ScopeTree; @@ -95,6 +97,8 @@ impl<'a> IsolatedDeclarations<'a> { &mut self, stmts: &oxc_allocator::Vec<'a, Statement<'a>>, ) -> oxc_allocator::Vec<'a, Statement<'a>> { + self.report_error_for_expando_function(stmts); + let mut new_ast_stmts = self.ast.new_vec::>(); for stmt in Self::remove_function_overloads_implementation(self.ast.copy(stmts)) { if let Some(decl) = stmt.as_declaration() { @@ -112,6 +116,8 @@ impl<'a> IsolatedDeclarations<'a> { &mut self, stmts: &oxc_allocator::Vec<'a, Statement<'a>>, ) -> oxc_allocator::Vec<'a, Statement<'a>> { + self.report_error_for_expando_function(stmts); + // https://github.com/microsoft/TypeScript/pull/58912 let mut need_empty_export_marker = true; @@ -119,6 +125,7 @@ impl<'a> IsolatedDeclarations<'a> { let mut variables_declarations = VecDeque::new(); let mut variable_transformed_indexes = VecDeque::new(); let mut transformed_indexes = Vec::new(); + // 1. Collect all declarations, module declarations // 2. Transform export declarations // 3. Collect all bindings / reference from module declarations @@ -393,6 +400,81 @@ impl<'a> IsolatedDeclarations<'a> { }) } + pub fn report_error_for_expando_function(&self, stmts: &oxc_allocator::Vec<'a, Statement<'a>>) { + let mut can_expando_function_names = FxHashSet::default(); + for stmt in stmts { + match stmt { + Statement::ExportNamedDeclaration(decl) => match decl.declaration.as_ref() { + Some(Declaration::FunctionDeclaration(func)) => { + if func.body.is_some() { + if let Some(id) = func.id.as_ref() { + can_expando_function_names.insert(id.name.clone()); + } + } + } + Some(Declaration::VariableDeclaration(decl)) => { + for declarator in &decl.declarations { + if declarator.id.type_annotation.is_none() + && declarator.init.as_ref().is_some_and(Expression::is_function) + { + if let Some(name) = declarator.id.get_identifier() { + can_expando_function_names.insert(name.clone()); + } + } + } + } + _ => (), + }, + Statement::ExportDefaultDeclaration(decl) => { + if let ExportDefaultDeclarationKind::FunctionDeclaration(func) = + &decl.declaration + { + if func.body.is_some() { + if let Some(id) = func.id.as_ref() { + can_expando_function_names.insert(id.name.clone()); + } + } + } + } + Statement::FunctionDeclaration(func) => { + if func.body.is_some() { + if let Some(id) = func.id.as_ref() { + can_expando_function_names.insert(id.name.clone()); + } + } + } + Statement::VariableDeclaration(decl) => { + for declarator in &decl.declarations { + if declarator.id.type_annotation.is_none() + && declarator.init.as_ref().is_some_and(Expression::is_function) + { + if let Some(name) = declarator.id.get_identifier() { + can_expando_function_names.insert(name.clone()); + } + } + } + } + Statement::ExpressionStatement(stmt) => { + if let Expression::AssignmentExpression(assignment) = &stmt.expression { + if let AssignmentTarget::StaticMemberExpression(static_member_expr) = + &assignment.left + { + if let Expression::Identifier(ident) = &static_member_expr.object { + if can_expando_function_names.contains(&ident.name) { + self.error(function_with_assigning_properties( + static_member_expr.span, + )); + } + } + } + } + } + + _ => {} + } + } + } + pub fn is_declare(&self) -> bool { // If we are in a module block, we don't need to add declare !self.scope.is_ts_module_block_flag() diff --git a/crates/oxc_isolated_declarations/tests/fixtures/expando-function.ts b/crates/oxc_isolated_declarations/tests/fixtures/expando-function.ts new file mode 100644 index 00000000000000..c3201ede2b1d0c --- /dev/null +++ b/crates/oxc_isolated_declarations/tests/fixtures/expando-function.ts @@ -0,0 +1,28 @@ +export function foo(): void {} +foo.apply = () => {} +foo.call = ()=> {} +foo.bind = ()=> {} +foo.caller = ()=> {} +foo.toString = ()=> {} +foo.length = 10 +foo.length = 10 + +export const bar = (): void => {} +bar.apply = () => {} +bar.call = ()=> {} +bar.bind = ()=> {} +bar.caller = ()=> {} +bar.toString = ()=> {} +bar.length = 10 +bar.length = 10 + +export namespace NS { + export const bar = (): void => {} + bar.apply = () => {} + bar.call = ()=> {} + bar.bind = ()=> {} + bar.caller = ()=> {} + bar.toString = ()=> {} + bar.length = 10 + bar.length = 10 +} diff --git a/crates/oxc_isolated_declarations/tests/snapshots/expando-function.snap b/crates/oxc_isolated_declarations/tests/snapshots/expando-function.snap new file mode 100644 index 00000000000000..4e5ebc9a591a4f --- /dev/null +++ b/crates/oxc_isolated_declarations/tests/snapshots/expando-function.snap @@ -0,0 +1,224 @@ +--- +source: crates/oxc_isolated_declarations/tests/mod.rs +input_file: crates/oxc_isolated_declarations/tests/fixtures/expando-function.ts +--- +==================== .D.TS ==================== + +export declare function foo(): void; +export declare const bar: () => void; +export declare module NS { + export const bar: () => void; +} + + +==================== Errors ==================== + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[7:1] + 6 | export function foo(): void {} + 7 | foo.apply = () => {} + : ^^^^^^^^^ + 8 | foo.call = ()=> {} + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[8:1] + 7 | foo.apply = () => {} + 8 | foo.call = ()=> {} + : ^^^^^^^^ + 9 | foo.bind = ()=> {} + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[9:1] + 8 | foo.call = ()=> {} + 9 | foo.bind = ()=> {} + : ^^^^^^^^ + 10 | foo.caller = ()=> {} + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[10:1] + 9 | foo.bind = ()=> {} + 10 | foo.caller = ()=> {} + : ^^^^^^^^^^ + 11 | foo.toString = ()=> {} + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[11:1] + 10 | foo.caller = ()=> {} + 11 | foo.toString = ()=> {} + : ^^^^^^^^^^^^ + 12 | foo.length = 10 + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[12:1] + 11 | foo.toString = ()=> {} + 12 | foo.length = 10 + : ^^^^^^^^^^ + 13 | foo.length = 10 + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[13:1] + 12 | foo.length = 10 + 13 | foo.length = 10 + : ^^^^^^^^^^ + 14 | + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[16:1] + 15 | export const bar = (): void => {} + 16 | bar.apply = () => {} + : ^^^^^^^^^ + 17 | bar.call = ()=> {} + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[17:1] + 16 | bar.apply = () => {} + 17 | bar.call = ()=> {} + : ^^^^^^^^ + 18 | bar.bind = ()=> {} + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[18:1] + 17 | bar.call = ()=> {} + 18 | bar.bind = ()=> {} + : ^^^^^^^^ + 19 | bar.caller = ()=> {} + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[19:1] + 18 | bar.bind = ()=> {} + 19 | bar.caller = ()=> {} + : ^^^^^^^^^^ + 20 | bar.toString = ()=> {} + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[20:1] + 19 | bar.caller = ()=> {} + 20 | bar.toString = ()=> {} + : ^^^^^^^^^^^^ + 21 | bar.length = 10 + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[21:1] + 20 | bar.toString = ()=> {} + 21 | bar.length = 10 + : ^^^^^^^^^^ + 22 | bar.length = 10 + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[22:1] + 21 | bar.length = 10 + 22 | bar.length = 10 + : ^^^^^^^^^^ + 23 | + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[26:3] + 25 | export const bar = (): void => {} + 26 | bar.apply = () => {} + : ^^^^^^^^^ + 27 | bar.call = ()=> {} + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[27:3] + 26 | bar.apply = () => {} + 27 | bar.call = ()=> {} + : ^^^^^^^^ + 28 | bar.bind = ()=> {} + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[28:3] + 27 | bar.call = ()=> {} + 28 | bar.bind = ()=> {} + : ^^^^^^^^ + 29 | bar.caller = ()=> {} + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[29:3] + 28 | bar.bind = ()=> {} + 29 | bar.caller = ()=> {} + : ^^^^^^^^^^ + 30 | bar.toString = ()=> {} + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[30:3] + 29 | bar.caller = ()=> {} + 30 | bar.toString = ()=> {} + : ^^^^^^^^^^^^ + 31 | bar.length = 10 + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[31:3] + 30 | bar.toString = ()=> {} + 31 | bar.length = 10 + : ^^^^^^^^^^ + 32 | bar.length = 10 + `---- + + x TS9023: Assigning properties to functions without declaring them is not + | supported with --isolatedDeclarations. Add an explicit declaration for the + | properties assigned to this function. + ,-[32:3] + 31 | bar.length = 10 + 32 | bar.length = 10 + : ^^^^^^^^^^ + 33 | } + `----