mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(transformer/async-to-generator): support inferring the function name from the ObjectPropertyValue's key (#7201)
Support for inferring function name from ObjectPropertyValue's key
For example:
```js
({ foo: async function() {} })
```
After this, we will able to infer `foo` for the object method
This commit is contained in:
parent
3a20b906f4
commit
1910227590
5 changed files with 157 additions and 23 deletions
|
|
@ -53,10 +53,14 @@
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use oxc_allocator::Box as ArenaBox;
|
use oxc_allocator::{Box as ArenaBox, String as ArenaString};
|
||||||
use oxc_ast::{ast::*, Visit, NONE};
|
use oxc_ast::{ast::*, AstBuilder, Visit, NONE};
|
||||||
use oxc_semantic::{ReferenceFlags, ScopeFlags, ScopeId, SymbolFlags};
|
use oxc_semantic::{ReferenceFlags, ScopeFlags, ScopeId, SymbolFlags};
|
||||||
use oxc_span::{Atom, GetSpan, SPAN};
|
use oxc_span::{Atom, GetSpan, SPAN};
|
||||||
|
use oxc_syntax::{
|
||||||
|
identifier::{is_identifier_name, is_identifier_part, is_identifier_start},
|
||||||
|
keyword::is_reserved_keyword,
|
||||||
|
};
|
||||||
use oxc_traverse::{Ancestor, BoundIdentifier, Traverse, TraverseCtx};
|
use oxc_traverse::{Ancestor, BoundIdentifier, Traverse, TraverseCtx};
|
||||||
|
|
||||||
use crate::{common::helper_loader::Helper, TransformCtx};
|
use crate::{common::helper_loader::Helper, TransformCtx};
|
||||||
|
|
@ -287,9 +291,7 @@ impl<'a, 'ctx> AsyncGeneratorExecutor<'a, 'ctx> {
|
||||||
let params = Self::create_placeholder_params(¶ms, scope_id, ctx);
|
let params = Self::create_placeholder_params(¶ms, scope_id, ctx);
|
||||||
let statements = ctx.ast.vec1(Self::create_apply_call_statement(&bound_ident, ctx));
|
let statements = ctx.ast.vec1(Self::create_apply_call_statement(&bound_ident, ctx));
|
||||||
let body = ctx.ast.alloc_function_body(SPAN, ctx.ast.vec(), statements);
|
let body = ctx.ast.alloc_function_body(SPAN, ctx.ast.vec(), statements);
|
||||||
let id = id.or_else(|| {
|
let id = id.or_else(|| Self::infer_function_id_from_parent_node(wrapper_scope_id, ctx));
|
||||||
Self::infer_function_id_from_variable_declarator(wrapper_scope_id, ctx)
|
|
||||||
});
|
|
||||||
Self::create_function(id, params, body, scope_id, ctx)
|
Self::create_function(id, params, body, scope_id, ctx)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -425,7 +427,7 @@ impl<'a, 'ctx> AsyncGeneratorExecutor<'a, 'ctx> {
|
||||||
let params = ctx.alloc(ctx.ast.move_formal_parameters(&mut arrow.params));
|
let params = ctx.alloc(ctx.ast.move_formal_parameters(&mut arrow.params));
|
||||||
let generator_function_id = arrow.scope_id();
|
let generator_function_id = arrow.scope_id();
|
||||||
ctx.scopes_mut().get_flags_mut(generator_function_id).remove(ScopeFlags::Arrow);
|
ctx.scopes_mut().get_flags_mut(generator_function_id).remove(ScopeFlags::Arrow);
|
||||||
let function_name = Self::get_function_name_from_parent_variable_declarator(ctx);
|
let function_name = Self::infer_function_name_from_parent_node(ctx);
|
||||||
|
|
||||||
if function_name.is_none() && !Self::is_function_length_affected(¶ms) {
|
if function_name.is_none() && !Self::is_function_length_affected(¶ms) {
|
||||||
return self.create_async_to_generator_call(
|
return self.create_async_to_generator_call(
|
||||||
|
|
@ -479,26 +481,80 @@ impl<'a, 'ctx> AsyncGeneratorExecutor<'a, 'ctx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Infers the function id from [`Ancestor::VariableDeclaratorInit`].
|
/// Infers the function id from [`TraverseCtx::parent`].
|
||||||
fn infer_function_id_from_variable_declarator(
|
fn infer_function_id_from_parent_node(
|
||||||
scope_id: ScopeId,
|
scope_id: ScopeId,
|
||||||
ctx: &mut TraverseCtx<'a>,
|
ctx: &mut TraverseCtx<'a>,
|
||||||
) -> Option<BindingIdentifier<'a>> {
|
) -> Option<BindingIdentifier<'a>> {
|
||||||
let name = Self::get_function_name_from_parent_variable_declarator(ctx)?;
|
let name = Self::infer_function_name_from_parent_node(ctx)?;
|
||||||
Some(
|
Some(
|
||||||
ctx.generate_binding(name, scope_id, SymbolFlags::FunctionScopedVariable)
|
ctx.generate_binding(name, scope_id, SymbolFlags::FunctionScopedVariable)
|
||||||
.create_binding_identifier(ctx),
|
.create_binding_identifier(ctx),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_function_name_from_parent_variable_declarator(
|
/// Infers the function name from the [`TraverseCtx::parent`].
|
||||||
ctx: &mut TraverseCtx<'a>,
|
fn infer_function_name_from_parent_node(ctx: &mut TraverseCtx<'a>) -> Option<Atom<'a>> {
|
||||||
) -> Option<Atom<'a>> {
|
match ctx.parent() {
|
||||||
let Ancestor::VariableDeclaratorInit(declarator) = ctx.parent() else {
|
// infer `foo` from `const foo = async function() {}`
|
||||||
return None;
|
Ancestor::VariableDeclaratorInit(declarator) => {
|
||||||
};
|
|
||||||
declarator.id().get_binding_identifier().map(|id| id.name.clone())
|
declarator.id().get_binding_identifier().map(|id| id.name.clone())
|
||||||
}
|
}
|
||||||
|
// infer `foo` from `({ foo: async function() {} })`
|
||||||
|
Ancestor::ObjectPropertyValue(property) if !*property.method() => {
|
||||||
|
property.key().static_name().map(|key| Self::normalize_function_name(&key, ctx.ast))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Normalizes the function name.
|
||||||
|
///
|
||||||
|
/// Examples:
|
||||||
|
///
|
||||||
|
/// // Valid
|
||||||
|
/// * `foo` -> `foo`
|
||||||
|
/// // Contains space
|
||||||
|
/// * `foo bar` -> `foo_bar`
|
||||||
|
/// // Reserved keyword
|
||||||
|
/// * `this` -> `_this`
|
||||||
|
/// * `arguments` -> `_arguments`
|
||||||
|
fn normalize_function_name(input: &str, ast: AstBuilder<'a>) -> Atom<'a> {
|
||||||
|
if !is_reserved_keyword(input) && is_identifier_name(input) {
|
||||||
|
return ast.atom(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut name = ArenaString::with_capacity_in(input.len() + 1, ast.allocator);
|
||||||
|
let mut capitalize_next = false;
|
||||||
|
|
||||||
|
let mut chars = input.chars();
|
||||||
|
if let Some(first) = chars.next() {
|
||||||
|
if is_identifier_start(first) {
|
||||||
|
name.push(first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for c in chars {
|
||||||
|
if c == ' ' {
|
||||||
|
name.push('_');
|
||||||
|
} else if !is_identifier_part(c) {
|
||||||
|
capitalize_next = true;
|
||||||
|
} else if capitalize_next {
|
||||||
|
name.push(c.to_ascii_uppercase());
|
||||||
|
capitalize_next = false;
|
||||||
|
} else {
|
||||||
|
name.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if name.is_empty() {
|
||||||
|
return ast.atom("_");
|
||||||
|
} else if is_reserved_keyword(name.as_str()) {
|
||||||
|
name.insert(0, '_');
|
||||||
|
}
|
||||||
|
|
||||||
|
ast.atom(name.into_bump_str())
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a [`Function`] with the specified params, body and scope_id.
|
/// Creates a [`Function`] with the specified params, body and scope_id.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
|
|
@ -825,7 +825,7 @@ Bindings mismatch:
|
||||||
after transform: ScopeId(22): ["T", "value"]
|
after transform: ScopeId(22): ["T", "value"]
|
||||||
rebuilt : ScopeId(29): ["value"]
|
rebuilt : ScopeId(29): ["value"]
|
||||||
Symbol flags mismatch for "_asyncToGenerator":
|
Symbol flags mismatch for "_asyncToGenerator":
|
||||||
after transform: SymbolId(26): SymbolFlags(Import)
|
after transform: SymbolId(27): SymbolFlags(Import)
|
||||||
rebuilt : SymbolId(0): SymbolFlags(FunctionScopedVariable)
|
rebuilt : SymbolId(0): SymbolFlags(FunctionScopedVariable)
|
||||||
Unresolved references mismatch:
|
Unresolved references mismatch:
|
||||||
after transform: ["Promise", "arguments", "require"]
|
after transform: ["Promise", "arguments", "require"]
|
||||||
|
|
@ -5338,17 +5338,17 @@ semantic error: Bindings mismatch:
|
||||||
after transform: ScopeId(0): ["LoadCallback", "_asyncToGenerator", "cb1", "cb2", "cb3", "fn1"]
|
after transform: ScopeId(0): ["LoadCallback", "_asyncToGenerator", "cb1", "cb2", "cb3", "fn1"]
|
||||||
rebuilt : ScopeId(0): ["_asyncToGenerator", "cb1", "cb2", "cb3", "fn1"]
|
rebuilt : ScopeId(0): ["_asyncToGenerator", "cb1", "cb2", "cb3", "fn1"]
|
||||||
Scope children mismatch:
|
Scope children mismatch:
|
||||||
after transform: ScopeId(0): [ScopeId(1), ScopeId(2), ScopeId(3), ScopeId(5), ScopeId(7), ScopeId(8), ScopeId(12), ScopeId(14), ScopeId(15), ScopeId(17)]
|
after transform: ScopeId(0): [ScopeId(1), ScopeId(2), ScopeId(5), ScopeId(7), ScopeId(8), ScopeId(12), ScopeId(14), ScopeId(16), ScopeId(17), ScopeId(19)]
|
||||||
rebuilt : ScopeId(0): [ScopeId(1), ScopeId(2), ScopeId(4), ScopeId(6), ScopeId(10), ScopeId(13)]
|
rebuilt : ScopeId(0): [ScopeId(1), ScopeId(4), ScopeId(6), ScopeId(8), ScopeId(12), ScopeId(15)]
|
||||||
Symbol flags mismatch for "_asyncToGenerator":
|
Symbol flags mismatch for "_asyncToGenerator":
|
||||||
after transform: SymbolId(10): SymbolFlags(Import)
|
after transform: SymbolId(12): SymbolFlags(Import)
|
||||||
rebuilt : SymbolId(0): SymbolFlags(FunctionScopedVariable)
|
rebuilt : SymbolId(0): SymbolFlags(FunctionScopedVariable)
|
||||||
Unresolved references mismatch:
|
Unresolved references mismatch:
|
||||||
after transform: ["Promise", "Record", "StateMachine", "arguments", "createMachine", "load", "require"]
|
after transform: ["Promise", "Record", "StateMachine", "arguments", "createMachine", "load", "require"]
|
||||||
rebuilt : ["Promise", "arguments", "createMachine", "load", "require"]
|
rebuilt : ["Promise", "arguments", "createMachine", "load", "require"]
|
||||||
Unresolved reference IDs mismatch for "Promise":
|
Unresolved reference IDs mismatch for "Promise":
|
||||||
after transform: [ReferenceId(2), ReferenceId(7), ReferenceId(8), ReferenceId(9), ReferenceId(11), ReferenceId(12), ReferenceId(13)]
|
after transform: [ReferenceId(2), ReferenceId(7), ReferenceId(8), ReferenceId(9), ReferenceId(11), ReferenceId(12), ReferenceId(13)]
|
||||||
rebuilt : [ReferenceId(3), ReferenceId(5), ReferenceId(7)]
|
rebuilt : [ReferenceId(3), ReferenceId(7), ReferenceId(9)]
|
||||||
|
|
||||||
tasks/coverage/typescript/tests/cases/compiler/contextuallyTypeGeneratorReturnTypeFromUnion.ts
|
tasks/coverage/typescript/tests/cases/compiler/contextuallyTypeGeneratorReturnTypeFromUnion.ts
|
||||||
semantic error: Bindings mismatch:
|
semantic error: Bindings mismatch:
|
||||||
|
|
@ -55663,7 +55663,7 @@ semantic error: Scope children mismatch:
|
||||||
after transform: ScopeId(0): [ScopeId(1), ScopeId(2)]
|
after transform: ScopeId(0): [ScopeId(1), ScopeId(2)]
|
||||||
rebuilt : ScopeId(0): [ScopeId(1)]
|
rebuilt : ScopeId(0): [ScopeId(1)]
|
||||||
Symbol flags mismatch for "_asyncToGenerator":
|
Symbol flags mismatch for "_asyncToGenerator":
|
||||||
after transform: SymbolId(10): SymbolFlags(Import)
|
after transform: SymbolId(11): SymbolFlags(Import)
|
||||||
rebuilt : SymbolId(0): SymbolFlags(FunctionScopedVariable)
|
rebuilt : SymbolId(0): SymbolFlags(FunctionScopedVariable)
|
||||||
|
|
||||||
tasks/coverage/typescript/tests/cases/conformance/types/rest/objectRestParameter.ts
|
tasks/coverage/typescript/tests/cases/conformance/types/rest/objectRestParameter.ts
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
commit: d20b314c
|
commit: d20b314c
|
||||||
|
|
||||||
Passed: 79/88
|
Passed: 80/89
|
||||||
|
|
||||||
# All Passed:
|
# All Passed:
|
||||||
* babel-plugin-transform-class-static-block
|
* babel-plugin-transform-class-static-block
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
const Normal = {
|
||||||
|
foo: async () => {
|
||||||
|
console.log(log)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const StringLiteralKey = {
|
||||||
|
['bar']: async () => {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const EmptyStringLiteralKey = {
|
||||||
|
['']: async () => {
|
||||||
|
console.log(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const InvalidStringLiteralKey = {
|
||||||
|
['#']: async () => {},
|
||||||
|
['this']: async () => {},
|
||||||
|
['#default']: async () => {},
|
||||||
|
['O X C']: async () => {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
var _this = this;
|
||||||
|
const Normal = {
|
||||||
|
foo: function () {
|
||||||
|
var _ref = babelHelpers.asyncToGenerator(function* () {
|
||||||
|
console.log(log);
|
||||||
|
});
|
||||||
|
return function foo() {
|
||||||
|
return _ref.apply(this, arguments);
|
||||||
|
};
|
||||||
|
}()
|
||||||
|
};
|
||||||
|
const StringLiteralKey = {
|
||||||
|
['bar']: function () {
|
||||||
|
var _ref2 = babelHelpers.asyncToGenerator(function* () {});
|
||||||
|
return function bar() {
|
||||||
|
return _ref2.apply(this, arguments);
|
||||||
|
};
|
||||||
|
}()
|
||||||
|
};
|
||||||
|
const EmptyStringLiteralKey = {
|
||||||
|
['']: function () {
|
||||||
|
var _ref3 = babelHelpers.asyncToGenerator(function* () {
|
||||||
|
console.log(_this);
|
||||||
|
});
|
||||||
|
return function _() {
|
||||||
|
return _ref3.apply(this, arguments);
|
||||||
|
};
|
||||||
|
}()
|
||||||
|
};
|
||||||
|
const InvalidStringLiteralKey = {
|
||||||
|
['#']: function () {
|
||||||
|
var _ref4 = babelHelpers.asyncToGenerator(function* () {});
|
||||||
|
return function _() {
|
||||||
|
return _ref4.apply(this, arguments);
|
||||||
|
};
|
||||||
|
}(),
|
||||||
|
['this']: function () {
|
||||||
|
var _ref5 = babelHelpers.asyncToGenerator(function* () {});
|
||||||
|
return function _this() {
|
||||||
|
return _ref5.apply(this, arguments);
|
||||||
|
};
|
||||||
|
}(),
|
||||||
|
['#default']: function () {
|
||||||
|
var _ref6 = babelHelpers.asyncToGenerator(function* () {});
|
||||||
|
return function _default() {
|
||||||
|
return _ref6.apply(this, arguments);
|
||||||
|
};
|
||||||
|
}(),
|
||||||
|
['O X C']: function () {
|
||||||
|
var _ref7 = babelHelpers.asyncToGenerator(function* () {});
|
||||||
|
return function O_X_C() {
|
||||||
|
return _ref7.apply(this, arguments);
|
||||||
|
};
|
||||||
|
}()
|
||||||
|
};
|
||||||
Loading…
Reference in a new issue