Skip to content

Commit

Permalink
feat(isolated-declarations): report error for expando functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Dunqing committed Jun 24, 2024
1 parent 8c61f9c commit 928b6db
Show file tree
Hide file tree
Showing 4 changed files with 334 additions and 1 deletion.
1 change: 0 additions & 1 deletion crates/oxc_isolated_declarations/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down
82 changes: 82 additions & 0 deletions crates/oxc_isolated_declarations/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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::<Statement<'a>>();
for stmt in Self::remove_function_overloads_implementation(self.ast.copy(stmts)) {
if let Some(decl) = stmt.as_declaration() {
Expand All @@ -112,13 +116,16 @@ 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;

let mut new_stmts = Vec::new();
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
Expand Down Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
224 changes: 224 additions & 0 deletions crates/oxc_isolated_declarations/tests/snapshots/expando-function.snap
Original file line number Diff line number Diff line change
@@ -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 | }
`----

0 comments on commit 928b6db

Please sign in to comment.