mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(isolated-declarations): report error for expando functions (#3872)
close: #3703
This commit is contained in:
parent
a85d313a43
commit
5e2baf3edd
4 changed files with 142 additions and 1 deletions
|
|
@ -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.",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
@ -105,6 +107,7 @@ impl<'a> IsolatedDeclarations<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
self.report_error_for_expando_function(stmts);
|
||||
new_ast_stmts
|
||||
}
|
||||
|
||||
|
|
@ -321,6 +324,7 @@ impl<'a> IsolatedDeclarations<'a> {
|
|||
.push(Statement::from(ModuleDeclaration::ExportNamedDeclaration(empty_export)));
|
||||
}
|
||||
|
||||
self.report_error_for_expando_function(stmts);
|
||||
new_ast_stmts
|
||||
}
|
||||
|
||||
|
|
@ -393,6 +397,85 @@ 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() {
|
||||
if self.scope.has_reference(&id.name) {
|
||||
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() {
|
||||
if self.scope.has_reference(&name) {
|
||||
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()
|
||||
|
|
|
|||
15
crates/oxc_isolated_declarations/tests/fixtures/expando-function.ts
vendored
Normal file
15
crates/oxc_isolated_declarations/tests/fixtures/expando-function.ts
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
export function foo(): void {}
|
||||
foo.apply = () => {}
|
||||
|
||||
export const bar = (): void => {}
|
||||
bar.call = ()=> {}
|
||||
|
||||
|
||||
export namespace NS {
|
||||
export const goo = (): void => {}
|
||||
goo.length = 10
|
||||
}
|
||||
|
||||
// unexported
|
||||
const zoo = (): void => {}
|
||||
zoo.toString = ()=> {}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
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 goo: () => 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.
|
||||
,-[10:3]
|
||||
9 | export const goo = (): void => {}
|
||||
10 | goo.length = 10
|
||||
: ^^^^^^^^^^
|
||||
11 | }
|
||||
`----
|
||||
|
||||
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.
|
||||
,-[2:1]
|
||||
1 | export function foo(): void {}
|
||||
2 | foo.apply = () => {}
|
||||
: ^^^^^^^^^
|
||||
3 |
|
||||
`----
|
||||
|
||||
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.
|
||||
,-[5:1]
|
||||
4 | export const bar = (): void => {}
|
||||
5 | bar.call = ()=> {}
|
||||
: ^^^^^^^^
|
||||
6 |
|
||||
`----
|
||||
Loading…
Reference in a new issue