feat(parser): fix incorrect AST for x?.f<T>() (#7387)

This commit is contained in:
Boshen 2024-11-21 06:10:48 +00:00
parent 885e37f8eb
commit e6922df3bb
5 changed files with 45 additions and 16 deletions

View file

@ -261,6 +261,10 @@ fn test() {
"disallowArithmeticOperators": false
}])),
),
("x?.f<T>();", None),
("x?.f?.<T>();", None),
("f?.<Q>();", None),
("a?.c?.b<c>", None),
];
let fail = vec![

View file

@ -101,6 +101,9 @@ fn test() {
("a?.c?.b<c>", None),
("foo?.bar!", None),
("foo?.[bar]!", None),
("x?.f<T>();", None),
("x?.f?.<T>();", None),
("f?.<Q>();", None),
(
"var x = a?.b",
Some(serde_json::json!([{

View file

@ -74,6 +74,24 @@ snapshot_kind: text
· ───────────
╰────
⚠ oxc(no-optional-chaining): Optional chaining is not allowed.
╭─[no_optional_chaining.tsx:1:1]
1 │ x?.f<T>();
· ─────────
╰────
⚠ oxc(no-optional-chaining): Optional chaining is not allowed.
╭─[no_optional_chaining.tsx:1:1]
1 │ x?.f?.<T>();
· ───────────
╰────
⚠ oxc(no-optional-chaining): Optional chaining is not allowed.
╭─[no_optional_chaining.tsx:1:1]
1 │ f?.<Q>();
· ────────
╰────
⚠ oxc(no-optional-chaining): Optional chaining is not allowed.
╭─[no_optional_chaining.tsx:1:9]
1 │ var x = a?.b

View file

@ -3,7 +3,7 @@ use oxc_allocator::Box;
use oxc_ast::ast::*;
use oxc_diagnostics::Result;
use oxc_regular_expression::ast::Pattern;
use oxc_span::{Atom, Span};
use oxc_span::{Atom, GetSpan, Span};
use oxc_syntax::{
number::{BigintBase, NumberBase},
operator::BinaryOperator,
@ -576,25 +576,33 @@ impl<'a> ParserImpl<'a> {
let mut in_optional_chain = false;
let lhs = self.parse_member_expression_or_higher(&mut in_optional_chain)?;
let lhs = self.parse_call_expression_rest(span, lhs, &mut in_optional_chain)?;
if in_optional_chain {
if !in_optional_chain {
return Ok(lhs);
}
// Add `ChainExpression` to `a?.c?.b<c>`;
if let Expression::TSInstantiationExpression(mut expr) = lhs {
expr.expression = self.map_to_chain_expression(
expr.expression.span(),
self.ast.move_expression(&mut expr.expression),
);
Ok(Expression::TSInstantiationExpression(expr))
} else {
let span = self.end_span(span);
Ok(self.map_to_chain_expression(span, lhs))
} else {
Ok(lhs)
}
}
fn map_to_chain_expression(&mut self, span: Span, expr: Expression<'a>) -> Expression<'a> {
fn map_to_chain_expression(&self, span: Span, expr: Expression<'a>) -> Expression<'a> {
match expr {
match_member_expression!(Expression) => {
let member_expr = expr.into_member_expression();
self.ast.expression_chain(span, ChainElement::from(member_expr))
}
Expression::CallExpression(result) => {
self.ast.expression_chain(span, ChainElement::CallExpression(result))
Expression::CallExpression(e) => {
self.ast.expression_chain(span, ChainElement::CallExpression(e))
}
Expression::TSNonNullExpression(result) => {
self.ast.expression_chain(span, ChainElement::TSNonNullExpression(result))
Expression::TSNonNullExpression(e) => {
self.ast.expression_chain(span, ChainElement::TSNonNullExpression(e))
}
expr => expr,
}
@ -677,15 +685,9 @@ impl<'a> ParserImpl<'a> {
self.parse_tagged_template(lhs_span, expr, *in_optional_chain, type_parameters)?
}
Kind::LAngle | Kind::ShiftLeft => {
let optional_chain_span = (*in_optional_chain).then(|| self.end_span(lhs_span));
if let Some(Some(arguments)) =
self.try_parse(Self::parse_type_arguments_in_expression)
{
// `a?.c?.b<c>`
if let Some(optional_chain_span) = optional_chain_span {
*in_optional_chain = false;
lhs = self.map_to_chain_expression(optional_chain_span, lhs);
}
lhs = self.ast.expression_ts_instantiation(
self.end_span(lhs_span),
lhs,

View file

@ -1987,7 +1987,9 @@ after transform: ["C", "T"]
rebuilt : ["C"]
* type-arguments/optional-call/input.ts
x Output mismatch
Unresolved references mismatch:
after transform: ["Q", "T", "f", "x"]
rebuilt : ["f", "x"]
* type-arguments/tagged-template/input.ts
Unresolved references mismatch: