Skip to content

Commit

Permalink
feat(parser): fix incorrect AST for x?.f<T>()
Browse files Browse the repository at this point in the history
  • Loading branch information
Boshen committed Nov 21, 2024
1 parent 885e37f commit 39d74b6
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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![
Expand Down
3 changes: 3 additions & 0 deletions crates/oxc_linter/src/rules/oxc/no_optional_chaining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!([{
Expand Down
18 changes: 18 additions & 0 deletions crates/oxc_linter/src/snapshots/no_optional_chaining.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 17 additions & 15 deletions crates/oxc_parser/src/js/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
}
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 3 additions & 1 deletion tasks/transform_conformance/snapshots/babel.snap.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit 39d74b6

Please sign in to comment.