From 0f20127529992d72e317f4837b9d0d5925a6fdf2 Mon Sep 17 00:00:00 2001 From: MichaelMitchell-at <=> Date: Thu, 15 Aug 2024 04:34:41 -0400 Subject: [PATCH 1/3] fix(isolated_declarations): Support expando functions --- crates/oxc_isolated_declarations/src/lib.rs | 71 ++++++++++++++++++- .../tests/fixtures/expando-function.ts | 12 +++- .../tests/snapshots/expando-function.snap | 15 +++- 3 files changed, 93 insertions(+), 5 deletions(-) diff --git a/crates/oxc_isolated_declarations/src/lib.rs b/crates/oxc_isolated_declarations/src/lib.rs index 8485e01d03833..7cd9702109a50 100644 --- a/crates/oxc_isolated_declarations/src/lib.rs +++ b/crates/oxc_isolated_declarations/src/lib.rs @@ -26,7 +26,7 @@ use oxc_allocator::Allocator; use oxc_ast::{ast::*, AstBuilder, Visit}; use oxc_diagnostics::OxcDiagnostic; use oxc_span::{Atom, SourceType, SPAN}; -use rustc_hash::FxHashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use crate::scope::ScopeTree; @@ -405,6 +405,67 @@ impl<'a> IsolatedDeclarations<'a> { } pub fn report_error_for_expando_function(&self, stmts: &oxc_allocator::Vec<'a, Statement<'a>>) { + let mut assignable_properties_for_namespace = FxHashMap::<&str, FxHashSet>::default(); + for stmt in stmts { + let Statement::ExportNamedDeclaration(decl) = stmt else { continue }; + let Some(Declaration::TSModuleDeclaration(decl)) = &decl.declaration else { continue }; + if decl.kind != TSModuleDeclarationKind::Namespace { + continue; + } + let TSModuleDeclarationName::Identifier(ident) = &decl.id else { continue }; + let Some(TSModuleDeclarationBody::TSModuleBlock(block)) = &decl.body else { + continue; + }; + for stmt in &block.body { + let Statement::ExportNamedDeclaration(decl) = stmt else { continue }; + match &decl.declaration { + Some(Declaration::VariableDeclaration(var)) => { + for declarator in &var.declarations { + if let Some(name) = declarator.id.get_identifier() { + assignable_properties_for_namespace + .entry(&ident.name) + .or_default() + .insert(name); + } + } + } + Some(Declaration::FunctionDeclaration(func)) => { + if let Some(id) = func.id.as_ref() { + assignable_properties_for_namespace + .entry(&ident.name) + .or_default() + .insert(id.name.clone()); + } + } + Some(Declaration::ClassDeclaration(cls)) => { + if let Some(id) = cls.id.as_ref() { + assignable_properties_for_namespace + .entry(&ident.name) + .or_default() + .insert(id.name.clone()); + } + } + Some(Declaration::TSEnumDeclaration(decl)) => { + assignable_properties_for_namespace + .entry(&ident.name) + .or_default() + .insert(decl.id.name.clone()); + } + Some(Declaration::UsingDeclaration(decl)) => { + for declarator in &decl.declarations { + if let Some(name) = declarator.id.get_identifier() { + assignable_properties_for_namespace + .entry(&ident.name) + .or_default() + .insert(name); + } + } + } + _ => {} + } + } + } + let mut can_expando_function_names = FxHashSet::default(); for stmt in stmts { match stmt { @@ -468,7 +529,13 @@ impl<'a> IsolatedDeclarations<'a> { &assignment.left { if let Expression::Identifier(ident) = &static_member_expr.object { - if can_expando_function_names.contains(&ident.name) { + if can_expando_function_names.contains(&ident.name) + && !assignable_properties_for_namespace + .get(&ident.name.as_str()) + .map_or(false, |properties| { + properties.contains(&static_member_expr.property.name) + }) + { self.error(function_with_assigning_properties( static_member_expr.span, )); diff --git a/crates/oxc_isolated_declarations/tests/fixtures/expando-function.ts b/crates/oxc_isolated_declarations/tests/fixtures/expando-function.ts index 8633029cc6b0f..c12598b5a07a5 100644 --- a/crates/oxc_isolated_declarations/tests/fixtures/expando-function.ts +++ b/crates/oxc_isolated_declarations/tests/fixtures/expando-function.ts @@ -2,7 +2,7 @@ export function foo(): void {} foo.apply = () => {} export const bar = (): void => {} -bar.call = ()=> {} +bar.call = () => {} export namespace NS { @@ -10,6 +10,14 @@ export namespace NS { goo.length = 10 } +export namespace foo { + let bar = 42; + export let baz = 100; +} + +foo.bar = 42; +foo.baz = 100; + // unexported const zoo = (): void => {} -zoo.toString = ()=> {} \ No newline at end of file +zoo.toString = () => {} \ No newline at end of file diff --git a/crates/oxc_isolated_declarations/tests/snapshots/expando-function.snap b/crates/oxc_isolated_declarations/tests/snapshots/expando-function.snap index f62c6293d980c..7842f8ed2463a 100644 --- a/crates/oxc_isolated_declarations/tests/snapshots/expando-function.snap +++ b/crates/oxc_isolated_declarations/tests/snapshots/expando-function.snap @@ -9,6 +9,9 @@ export declare const bar: () => void; export declare namespace NS { export const goo: () => void; } +export declare namespace foo { + export let baz: number; +} ==================== Errors ==================== @@ -38,7 +41,17 @@ export declare namespace NS { | properties assigned to this function. ,-[5:1] 4 | export const bar = (): void => {} - 5 | bar.call = ()=> {} + 5 | bar.call = () => {} : ^^^^^^^^ 6 | `---- + + 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 | + 18 | foo.bar = 42; + : ^^^^^^^ + 19 | foo.baz = 100; + `---- From ddd39e5aba7eebc42eee5249203512711c54046d Mon Sep 17 00:00:00 2001 From: MichaelMitchell-at <=> Date: Thu, 15 Aug 2024 05:48:19 -0400 Subject: [PATCH 2/3] refactor out method --- crates/oxc_isolated_declarations/src/lib.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/oxc_isolated_declarations/src/lib.rs b/crates/oxc_isolated_declarations/src/lib.rs index 7cd9702109a50..c6ce198479cda 100644 --- a/crates/oxc_isolated_declarations/src/lib.rs +++ b/crates/oxc_isolated_declarations/src/lib.rs @@ -404,7 +404,9 @@ impl<'a> IsolatedDeclarations<'a> { }) } - pub fn report_error_for_expando_function(&self, stmts: &oxc_allocator::Vec<'a, Statement<'a>>) { + fn get_assignable_properties_for_namespaces( + stmts: &'a oxc_allocator::Vec<'a, Statement<'a>>, + ) -> FxHashMap<&'a str, FxHashSet> { let mut assignable_properties_for_namespace = FxHashMap::<&str, FxHashSet>::default(); for stmt in stmts { let Statement::ExportNamedDeclaration(decl) = stmt else { continue }; @@ -465,6 +467,12 @@ impl<'a> IsolatedDeclarations<'a> { } } } + assignable_properties_for_namespace + } + + pub fn report_error_for_expando_function(&self, stmts: &oxc_allocator::Vec<'a, Statement<'a>>) { + let assignable_properties_for_namespace = + IsolatedDeclarations::get_assignable_properties_for_namespaces(stmts); let mut can_expando_function_names = FxHashSet::default(); for stmt in stmts { From 5e3eb97ffc11aa72e52286b8b81e8d499174e645 Mon Sep 17 00:00:00 2001 From: MichaelMitchell-at <=> Date: Thu, 15 Aug 2024 06:37:34 -0400 Subject: [PATCH 3/3] add test --- .../tests/fixtures/expando-function.ts | 6 ++++++ .../tests/snapshots/expando-function.snap | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/oxc_isolated_declarations/tests/fixtures/expando-function.ts b/crates/oxc_isolated_declarations/tests/fixtures/expando-function.ts index c12598b5a07a5..6856fbe2c75ab 100644 --- a/crates/oxc_isolated_declarations/tests/fixtures/expando-function.ts +++ b/crates/oxc_isolated_declarations/tests/fixtures/expando-function.ts @@ -11,10 +11,16 @@ export namespace NS { } export namespace foo { + // declaration must be exported let bar = 42; export let baz = 100; } +// namespace must be exported +namespace foo { + export let bar = 42; +} + foo.bar = 42; foo.baz = 100; diff --git a/crates/oxc_isolated_declarations/tests/snapshots/expando-function.snap b/crates/oxc_isolated_declarations/tests/snapshots/expando-function.snap index 7842f8ed2463a..fe3405c670e92 100644 --- a/crates/oxc_isolated_declarations/tests/snapshots/expando-function.snap +++ b/crates/oxc_isolated_declarations/tests/snapshots/expando-function.snap @@ -49,9 +49,9 @@ export declare namespace foo { 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 | - 18 | foo.bar = 42; + ,-[24:1] + 23 | + 24 | foo.bar = 42; : ^^^^^^^ - 19 | foo.baz = 100; + 25 | foo.baz = 100; `----