fix(transformer/typescript): typescript syntax within SimpleAssignmentTarget with MemberExpressions is not stripped (#4920)

close: #4870

I added the `move_identifier_reference` and `move_member_expression` methods used to take ownership in `ast_builder_impl`.  This way can let us get rid of `ast.copy`.

Another possible approach is to add `get_expression_owner` to `SimpleAssignmentTarget` and a `get_inner_expression_owner` method to `Expression`. And add an `into_xxxxx` method for `inherit_variants` macro

The implementation looks like this
```rs
let Some(expression) = self.get_expression_owner() else { return; }
match expr.get_inner_expression_owner() {
    Expression::Identifier(ident) => {
        *target = self.ctx.ast.simple_assignment_target_from_identifier_reference(ident);
    }
    inner_expr @ match_member_expression!(Expression) => {
        *target = SimpleAssignmentTarget::from(
            inner_expr.into_member_expression()
        );
    }
    _ => (),
}
```
This commit is contained in:
Dunqing 2024-08-16 05:05:10 +00:00
parent 508644a34e
commit 248a757b34
4 changed files with 67 additions and 7 deletions

View file

@ -69,6 +69,16 @@ impl<'a> AstBuilder<'a> {
unsafe { std::mem::transmute_copy(src) }
}
/// Moves the identifier reference out by replacing it with a dummy identifier reference.
#[inline]
pub fn move_identifier_reference(
self,
expr: &mut IdentifierReference<'a>,
) -> IdentifierReference<'a> {
let dummy = self.identifier_reference(expr.span(), "");
mem::replace(expr, dummy)
}
/// Moves the expression out by replacing it with a null expression.
#[inline]
pub fn move_expression(self, expr: &mut Expression<'a>) -> Expression<'a> {
@ -76,6 +86,18 @@ impl<'a> AstBuilder<'a> {
mem::replace(expr, null_expr)
}
/// Moves the member expression out by replacing it with a dummy expression.
#[inline]
pub fn move_member_expression(self, expr: &mut MemberExpression<'a>) -> MemberExpression<'a> {
let dummy = self.member_expression_computed(
expr.span(),
self.expression_null_literal(expr.span()),
self.expression_null_literal(expr.span()),
false,
);
mem::replace(expr, dummy)
}
#[inline]
pub fn move_statement(self, stmt: &mut Statement<'a>) -> Statement<'a> {
let empty_stmt = self.empty_statement(stmt.span());

View file

@ -216,6 +216,20 @@ impl<'a> Expression<'a> {
}
}
pub fn get_inner_expression_mut(&mut self) -> &mut Expression<'a> {
match self {
Expression::ParenthesizedExpression(expr) => expr.expression.get_inner_expression_mut(),
Expression::TSAsExpression(expr) => expr.expression.get_inner_expression_mut(),
Expression::TSSatisfiesExpression(expr) => expr.expression.get_inner_expression_mut(),
Expression::TSInstantiationExpression(expr) => {
expr.expression.get_inner_expression_mut()
}
Expression::TSNonNullExpression(expr) => expr.expression.get_inner_expression_mut(),
Expression::TSTypeAssertion(expr) => expr.expression.get_inner_expression_mut(),
_ => self,
}
}
pub fn is_identifier_reference(&self) -> bool {
matches!(self, Expression::Identifier(_))
}
@ -632,6 +646,17 @@ impl<'a> SimpleAssignmentTarget<'a> {
_ => None,
}
}
pub fn get_expression_mut(&mut self) -> Option<&mut Expression<'a>> {
match self {
Self::TSAsExpression(expr) => Some(&mut expr.expression),
Self::TSSatisfiesExpression(expr) => Some(&mut expr.expression),
Self::TSNonNullExpression(expr) => Some(&mut expr.expression),
Self::TSTypeAssertion(expr) => Some(&mut expr.expression),
Self::TSInstantiationExpression(expr) => Some(&mut expr.expression),
_ => None,
}
}
}
impl<'a> ArrayAssignmentTarget<'a> {

View file

@ -4,6 +4,7 @@ use std::{cell::Cell, rc::Rc};
use oxc_allocator::Vec as ArenaVec;
use oxc_ast::ast::*;
use oxc_diagnostics::OxcDiagnostic;
use oxc_semantic::SymbolFlags;
use oxc_span::{Atom, GetSpan, Span, SPAN};
use oxc_syntax::{
@ -222,11 +223,24 @@ impl<'a> TypeScriptAnnotations<'a> {
}
pub fn transform_simple_assignment_target(&mut self, target: &mut SimpleAssignmentTarget<'a>) {
if let Some(expr) = target.get_expression() {
if let Expression::Identifier(ident) = expr.get_inner_expression() {
// SAFETY: `ast.copy` is unsound! We need to fix.
let ident = unsafe { self.ctx.ast.copy(ident) };
*target = SimpleAssignmentTarget::AssignmentTargetIdentifier(ident);
if let Some(expr) = target.get_expression_mut() {
match expr.get_inner_expression_mut() {
// `foo!++` to `foo++`
Expression::Identifier(ident) => {
*target = self.ctx.ast.simple_assignment_target_from_identifier_reference(
self.ctx.ast.move_identifier_reference(ident),
);
}
// `foo.bar!++` to `foo.bar++`
inner_expr @ match_member_expression!(Expression) => {
*target = SimpleAssignmentTarget::from(
self.ctx.ast.move_member_expression(inner_expr.to_member_expression_mut()),
);
}
_ => {
// This should be never hit until more syntax is added to the JavaScript/TypeScrips
self.ctx.error(OxcDiagnostic::error("Cannot strip out typescript syntax if SimpleAssignmentTarget is not an IdentifierReference or MemberExpression"));
}
}
}
}

View file

@ -2,8 +2,7 @@ commit: d8086f14
transformer_typescript Summary:
AST Parsed : 6456/6456 (100.00%)
Positive Passed: 6452/6456 (99.94%)
Positive Passed: 6453/6456 (99.95%)
Mismatch: "compiler/constEnumNamespaceReferenceCausesNoImport2.ts"
Mismatch: "compiler/incrementOnNullAssertion.ts"
Mismatch: "conformance/externalModules/typeOnly/exportDeclaration.ts"
Mismatch: "conformance/jsx/inline/inlineJsxAndJsxFragPragmaOverridesCompilerOptions.tsx"