diff --git a/crates/oxc_linter/src/rules/eslint/no_unsafe_optional_chaining.rs b/crates/oxc_linter/src/rules/eslint/no_unsafe_optional_chaining.rs index 5f9be9e87c962..d4ae93e9218ab 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unsafe_optional_chaining.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unsafe_optional_chaining.rs @@ -261,6 +261,10 @@ fn test() { "disallowArithmeticOperators": false }])), ), + ("x?.f();", None), + ("x?.f?.();", None), + ("f?.();", None), + ("a?.c?.b", None), ]; let fail = vec![ diff --git a/crates/oxc_linter/src/rules/oxc/no_optional_chaining.rs b/crates/oxc_linter/src/rules/oxc/no_optional_chaining.rs index 246e4daff03f9..dd8e5c4792f11 100644 --- a/crates/oxc_linter/src/rules/oxc/no_optional_chaining.rs +++ b/crates/oxc_linter/src/rules/oxc/no_optional_chaining.rs @@ -101,6 +101,9 @@ fn test() { ("a?.c?.b", None), ("foo?.bar!", None), ("foo?.[bar]!", None), + ("x?.f();", None), + ("x?.f?.();", None), + ("f?.();", None), ( "var x = a?.b", Some(serde_json::json!([{ diff --git a/crates/oxc_linter/src/snapshots/no_optional_chaining.snap b/crates/oxc_linter/src/snapshots/no_optional_chaining.snap index b0bb82ad4e0d9..6319382d54498 100644 --- a/crates/oxc_linter/src/snapshots/no_optional_chaining.snap +++ b/crates/oxc_linter/src/snapshots/no_optional_chaining.snap @@ -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(); + · ───────── + ╰──── + + ⚠ oxc(no-optional-chaining): Optional chaining is not allowed. + ╭─[no_optional_chaining.tsx:1:1] + 1 │ x?.f?.(); + · ─────────── + ╰──── + + ⚠ oxc(no-optional-chaining): Optional chaining is not allowed. + ╭─[no_optional_chaining.tsx:1:1] + 1 │ f?.(); + · ──────── + ╰──── + ⚠ oxc(no-optional-chaining): Optional chaining is not allowed. ╭─[no_optional_chaining.tsx:1:9] 1 │ var x = a?.b diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index 72987b17a35f5..9ab6db9f5d0a5 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -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`; + 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` - 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, diff --git a/tasks/transform_conformance/snapshots/babel.snap.md b/tasks/transform_conformance/snapshots/babel.snap.md index b6e0c7a475e0f..dd3442d5cced9 100644 --- a/tasks/transform_conformance/snapshots/babel.snap.md +++ b/tasks/transform_conformance/snapshots/babel.snap.md @@ -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: