mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +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 oxc_allocator::Box as ArenaBox;
|
||||
use oxc_ast::{ast::*, Visit, NONE};
|
||||
use oxc_allocator::{Box as ArenaBox, String as ArenaString};
|
||||
use oxc_ast::{ast::*, AstBuilder, Visit, NONE};
|
||||
use oxc_semantic::{ReferenceFlags, ScopeFlags, ScopeId, SymbolFlags};
|
||||
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 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 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 id = id.or_else(|| {
|
||||
Self::infer_function_id_from_variable_declarator(wrapper_scope_id, ctx)
|
||||
});
|
||||
let id = id.or_else(|| Self::infer_function_id_from_parent_node(wrapper_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 generator_function_id = arrow.scope_id();
|
||||
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) {
|
||||
return self.create_async_to_generator_call(
|
||||
|
|
@ -479,25 +481,79 @@ impl<'a, 'ctx> AsyncGeneratorExecutor<'a, 'ctx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Infers the function id from [`Ancestor::VariableDeclaratorInit`].
|
||||
fn infer_function_id_from_variable_declarator(
|
||||
/// Infers the function id from [`TraverseCtx::parent`].
|
||||
fn infer_function_id_from_parent_node(
|
||||
scope_id: ScopeId,
|
||||
ctx: &mut TraverseCtx<'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(
|
||||
ctx.generate_binding(name, scope_id, SymbolFlags::FunctionScopedVariable)
|
||||
.create_binding_identifier(ctx),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_function_name_from_parent_variable_declarator(
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Option<Atom<'a>> {
|
||||
let Ancestor::VariableDeclaratorInit(declarator) = ctx.parent() else {
|
||||
return None;
|
||||
};
|
||||
declarator.id().get_binding_identifier().map(|id| id.name.clone())
|
||||
/// Infers the function name from the [`TraverseCtx::parent`].
|
||||
fn infer_function_name_from_parent_node(ctx: &mut TraverseCtx<'a>) -> Option<Atom<'a>> {
|
||||
match ctx.parent() {
|
||||
// infer `foo` from `const foo = async function() {}`
|
||||
Ancestor::VariableDeclaratorInit(declarator) => {
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -825,7 +825,7 @@ Bindings mismatch:
|
|||
after transform: ScopeId(22): ["T", "value"]
|
||||
rebuilt : ScopeId(29): ["value"]
|
||||
Symbol flags mismatch for "_asyncToGenerator":
|
||||
after transform: SymbolId(26): SymbolFlags(Import)
|
||||
after transform: SymbolId(27): SymbolFlags(Import)
|
||||
rebuilt : SymbolId(0): SymbolFlags(FunctionScopedVariable)
|
||||
Unresolved references mismatch:
|
||||
after transform: ["Promise", "arguments", "require"]
|
||||
|
|
@ -5338,17 +5338,17 @@ semantic error: Bindings mismatch:
|
|||
after transform: ScopeId(0): ["LoadCallback", "_asyncToGenerator", "cb1", "cb2", "cb3", "fn1"]
|
||||
rebuilt : ScopeId(0): ["_asyncToGenerator", "cb1", "cb2", "cb3", "fn1"]
|
||||
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)]
|
||||
rebuilt : ScopeId(0): [ScopeId(1), ScopeId(2), ScopeId(4), ScopeId(6), ScopeId(10), ScopeId(13)]
|
||||
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(4), ScopeId(6), ScopeId(8), ScopeId(12), ScopeId(15)]
|
||||
Symbol flags mismatch for "_asyncToGenerator":
|
||||
after transform: SymbolId(10): SymbolFlags(Import)
|
||||
after transform: SymbolId(12): SymbolFlags(Import)
|
||||
rebuilt : SymbolId(0): SymbolFlags(FunctionScopedVariable)
|
||||
Unresolved references mismatch:
|
||||
after transform: ["Promise", "Record", "StateMachine", "arguments", "createMachine", "load", "require"]
|
||||
rebuilt : ["Promise", "arguments", "createMachine", "load", "require"]
|
||||
Unresolved reference IDs mismatch for "Promise":
|
||||
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
|
||||
semantic error: Bindings mismatch:
|
||||
|
|
@ -55663,7 +55663,7 @@ semantic error: Scope children mismatch:
|
|||
after transform: ScopeId(0): [ScopeId(1), ScopeId(2)]
|
||||
rebuilt : ScopeId(0): [ScopeId(1)]
|
||||
Symbol flags mismatch for "_asyncToGenerator":
|
||||
after transform: SymbolId(10): SymbolFlags(Import)
|
||||
after transform: SymbolId(11): SymbolFlags(Import)
|
||||
rebuilt : SymbolId(0): SymbolFlags(FunctionScopedVariable)
|
||||
|
||||
tasks/coverage/typescript/tests/cases/conformance/types/rest/objectRestParameter.ts
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
commit: d20b314c
|
||||
|
||||
Passed: 79/88
|
||||
Passed: 80/89
|
||||
|
||||
# All Passed:
|
||||
* 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