Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions crates/oxc_parser/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -601,3 +601,17 @@ pub fn enum_member_cannot_have_numeric_name(span: Span) -> OxcDiagnostic {
pub fn index_signature_one_parameter(span: Span) -> OxcDiagnostic {
ts_error("1096", "An index signature must have exactly one parameter.").with_label(span)
}

#[cold]
pub fn mixed_coalesce(span: Span) -> OxcDiagnostic {
OxcDiagnostic::error("Logical expressions and coalesce expressions cannot be mixed")
.with_help("Wrap either expression by parentheses")
.with_label(span)
}

#[cold]
pub fn unexpected_exponential(x0: &str, span1: Span) -> OxcDiagnostic {
OxcDiagnostic::error("Unexpected exponentiation expression")
.with_help(format!("Wrap {x0} expression in parentheses to enforce operator precedence"))
.with_label(span1)
}
50 changes: 37 additions & 13 deletions crates/oxc_parser/src/js/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,7 @@ impl<'a> ParserImpl<'a> {
) -> Expression<'a> {
let lhs_span = self.start_span();

let lhs_parenthesized = self.at(Kind::LParen);
// [+In] PrivateIdentifier in ShiftExpression[?Yield, ?Await]
let lhs = if self.ctx.has_in() && self.at(Kind::PrivateIdentifier) {
let left = self.parse_private_identifier();
Expand All @@ -1092,14 +1093,15 @@ impl<'a> ParserImpl<'a> {
expr
};

self.parse_binary_expression_rest(lhs_span, lhs, lhs_precedence)
self.parse_binary_expression_rest(lhs_span, lhs, lhs_parenthesized, lhs_precedence)
}

/// Section 13.6 - 13.13 Binary Expression
fn parse_binary_expression_rest(
&mut self,
lhs_span: u32,
lhs: Expression<'a>,
lhs_parenthesized: bool,
min_precedence: Precedence,
) -> Expression<'a> {
// Pratt Parsing Algorithm
Expand Down Expand Up @@ -1145,22 +1147,44 @@ impl<'a> ParserImpl<'a> {
}

self.bump_any(); // bump operator
let rhs_parenthesized = self.at(Kind::LParen);
let rhs = self.parse_binary_expression_or_higher(left_precedence);

lhs = if kind.is_logical_operator() {
self.ast.expression_logical(
self.end_span(lhs_span),
lhs,
map_logical_operator(kind),
rhs,
)
let span = self.end_span(lhs_span);
let op = map_logical_operator(kind);
// check mixed coalesce
if op == LogicalOperator::Coalesce {
let mut maybe_mixed_coalesce_expr = None;
if let Expression::LogicalExpression(rhs) = &rhs {
if !rhs_parenthesized {
maybe_mixed_coalesce_expr = Some(rhs);
}
} else if let Expression::LogicalExpression(lhs) = &lhs {
if !lhs_parenthesized {
maybe_mixed_coalesce_expr = Some(lhs);
}
}
if let Some(expr) = maybe_mixed_coalesce_expr {
if matches!(expr.operator, LogicalOperator::And | LogicalOperator::Or) {
self.error(diagnostics::mixed_coalesce(span));
}
}
}
self.ast.expression_logical(span, lhs, op, rhs)
} else if kind.is_binary_operator() {
self.ast.expression_binary(
self.end_span(lhs_span),
lhs,
map_binary_operator(kind),
rhs,
)
let span = self.end_span(lhs_span);
let op = map_binary_operator(kind);
if op == BinaryOperator::Exponential && !lhs_parenthesized {
if let Some(key) = match lhs {
Expression::AwaitExpression(_) => Some("await"),
Expression::UnaryExpression(_) => Some("unary"),
_ => None,
} {
self.error(diagnostics::unexpected_exponential(key, lhs.span()));
}
}
self.ast.expression_binary(span, lhs, op, rhs)
} else {
break;
};
Expand Down
52 changes: 1 addition & 51 deletions crates/oxc_semantic/src/checker/javascript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use oxc_ecmascript::{BoundNames, IsSimpleParameterList, PropName};
use oxc_span::{GetSpan, ModuleKind, Span};
use oxc_syntax::{
number::NumberBase,
operator::{AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator},
operator::{AssignmentOperator, UnaryOperator},
scope::ScopeFlags,
symbol::SymbolFlags,
};
Expand Down Expand Up @@ -1015,56 +1015,6 @@ pub fn check_object_expression(obj_expr: &ObjectExpression, ctx: &SemanticBuilde
}
}

fn unexpected_exponential(x0: &str, span1: Span) -> OxcDiagnostic {
OxcDiagnostic::error("Unexpected exponentiation expression")
.with_help(format!("Wrap {x0} expression in parentheses to enforce operator precedence"))
.with_label(span1)
}

pub fn check_binary_expression(binary_expr: &BinaryExpression, ctx: &SemanticBuilder<'_>) {
if binary_expr.operator == BinaryOperator::Exponential {
match binary_expr.left {
// async () => await 5 ** 6
// async () => await -5 ** 6
Expression::AwaitExpression(_) => {
ctx.error(unexpected_exponential("await", binary_expr.span));
}
// -5 ** 6
Expression::UnaryExpression(_) => {
ctx.error(unexpected_exponential("unary", binary_expr.span));
}
_ => {}
}
}
}

fn mixed_coalesce(span: Span) -> OxcDiagnostic {
OxcDiagnostic::error("Logical expressions and coalesce expressions cannot be mixed")
.with_help("Wrap either expression by parentheses")
.with_label(span)
}

pub fn check_logical_expression(logical_expr: &LogicalExpression, ctx: &SemanticBuilder<'_>) {
// check mixed coalesce
// a ?? b || c - a ?? (b || c)
// a ?? b && c - a ?? (b && c)
// a || b ?? c - (a || b) ?? c
// a && b ?? c - (a && b) ?? c
if logical_expr.operator == LogicalOperator::Coalesce {
let mut maybe_mixed_coalesce_expr = None;
if let Expression::LogicalExpression(rhs) = &logical_expr.right {
maybe_mixed_coalesce_expr = Some(rhs);
} else if let Expression::LogicalExpression(lhs) = &logical_expr.left {
maybe_mixed_coalesce_expr = Some(lhs);
}
if let Some(expr) = maybe_mixed_coalesce_expr {
if matches!(expr.operator, LogicalOperator::And | LogicalOperator::Or) {
ctx.error(mixed_coalesce(logical_expr.span));
}
}
}
}

fn super_private(span: Span) -> OxcDiagnostic {
OxcDiagnostic::error("Private fields cannot be accessed on super").with_label(span)
}
Expand Down
2 changes: 0 additions & 2 deletions crates/oxc_semantic/src/checker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,6 @@ pub fn check<'a>(node: &AstNode<'a>, ctx: &SemanticBuilder<'a>) {

AstKind::AssignmentExpression(expr) => js::check_assignment_expression(expr, ctx),
AstKind::AwaitExpression(expr) => js::check_await_expression(expr, node, ctx),
AstKind::BinaryExpression(expr) => js::check_binary_expression(expr, ctx),
AstKind::LogicalExpression(expr) => js::check_logical_expression(expr, ctx),
AstKind::MemberExpression(expr) => js::check_member_expression(expr, ctx),
AstKind::ObjectExpression(expr) => js::check_object_expression(expr, ctx),
AstKind::UnaryExpression(expr) => js::check_unary_expression(expr, ctx),
Expand Down
20 changes: 10 additions & 10 deletions tasks/coverage/snapshots/parser_babel.snap
Original file line number Diff line number Diff line change
Expand Up @@ -5128,21 +5128,21 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
× Unexpected exponentiation expression
╭─[babel/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/10/input.js:1:1]
1 │ -5 ** 6;
· ───────
· ──
╰────
help: Wrap unary expression in parentheses to enforce operator precedence

× Unexpected exponentiation expression
╭─[babel/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/11/input.js:1:1]
1 │ -(5) ** 6;
· ─────────
· ────
╰────
help: Wrap unary expression in parentheses to enforce operator precedence

× Unexpected exponentiation expression
╭─[babel/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/12/input.js:1:2]
1 │ (-5 ** 6);
· ───────
· ──
╰────
help: Wrap unary expression in parentheses to enforce operator precedence

Expand All @@ -5161,35 +5161,35 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
× Unexpected exponentiation expression
╭─[babel/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/15/input.js:1:1]
1 │ -(5) ** 6;
· ─────────
· ────
╰────
help: Wrap unary expression in parentheses to enforce operator precedence

× Unexpected exponentiation expression
╭─[babel/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/16/input.js:1:2]
1 │ (-5 ** 6);
· ───────
· ──
╰────
help: Wrap unary expression in parentheses to enforce operator precedence

× Unexpected exponentiation expression
╭─[babel/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/await-before-exponential/input.js:1:13]
1 │ async () => await 5 ** 6;
· ────────────
· ───────
╰────
help: Wrap await expression in parentheses to enforce operator precedence

× Unexpected exponentiation expression
╭─[babel/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/await-unary-before-exponential/input.js:1:13]
1 │ async () => await -5 ** 6;
· ─────────────
· ────────
╰────
help: Wrap await expression in parentheses to enforce operator precedence

× Unexpected exponentiation expression
╭─[babel/packages/babel-parser/test/fixtures/es2016/exponentiation-operator/nested-unary-before-exponential/input.js:1:2]
1 │ (-+5 ** 6);
· ────────
· ───
╰────
help: Wrap unary expression in parentheses to enforce operator precedence

Expand Down Expand Up @@ -12441,14 +12441,14 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
× Unexpected exponentiation expression
╭─[babel/packages/babel-parser/test/fixtures/typescript/exponentiation/await-non-null-before-exponential/input.ts:1:14]
1 │ async (a) => await a! ** 6;
· ─────────────
· ────────
╰────
help: Wrap await expression in parentheses to enforce operator precedence

× Unexpected exponentiation expression
╭─[babel/packages/babel-parser/test/fixtures/typescript/exponentiation/unary-non-null-before-exponential/input.ts:1:8]
1 │ (a) => +a! ** 6;
· ────────
· ───
╰────
help: Wrap unary expression in parentheses to enforce operator precedence

Expand Down
14 changes: 7 additions & 7 deletions tasks/coverage/snapshots/parser_test262.snap
Original file line number Diff line number Diff line change
Expand Up @@ -16717,55 +16717,55 @@ Negative Passed: 4519/4519 (100.00%)
╭─[test262/test/language/expressions/exponentiation/exp-operator-syntax-error-bitnot-unary-expression-base.js:26:1]
25 │ $DONOTEVALUATE();
26 │ ~3 ** 2;
· ───────
· ──
╰────
help: Wrap unary expression in parentheses to enforce operator precedence

× Unexpected exponentiation expression
╭─[test262/test/language/expressions/exponentiation/exp-operator-syntax-error-delete-unary-expression-base.js:26:1]
25 │ $DONOTEVALUATE();
26 │ delete o.p ** 2;
· ───────────────
· ──────────
╰────
help: Wrap unary expression in parentheses to enforce operator precedence

× Unexpected exponentiation expression
╭─[test262/test/language/expressions/exponentiation/exp-operator-syntax-error-logical-not-unary-expression-base.js:26:1]
25 │ $DONOTEVALUATE();
26 │ !1 ** 2;
· ───────
· ──
╰────
help: Wrap unary expression in parentheses to enforce operator precedence

× Unexpected exponentiation expression
╭─[test262/test/language/expressions/exponentiation/exp-operator-syntax-error-negate-unary-expression-base.js:26:1]
25 │ $DONOTEVALUATE();
26 │ -3 ** 2;
· ───────
· ──
╰────
help: Wrap unary expression in parentheses to enforce operator precedence

× Unexpected exponentiation expression
╭─[test262/test/language/expressions/exponentiation/exp-operator-syntax-error-plus-unary-expression-base.js:26:1]
25 │ $DONOTEVALUATE();
26 │ +1 ** 2;
· ───────
· ──
╰────
help: Wrap unary expression in parentheses to enforce operator precedence

× Unexpected exponentiation expression
╭─[test262/test/language/expressions/exponentiation/exp-operator-syntax-error-typeof-unary-expression-base.js:26:1]
25 │ $DONOTEVALUATE();
26 │ typeof 1 ** 2;
· ─────────────
· ────────
╰────
help: Wrap unary expression in parentheses to enforce operator precedence

× Unexpected exponentiation expression
╭─[test262/test/language/expressions/exponentiation/exp-operator-syntax-error-void-unary-expression-base.js:26:1]
25 │ $DONOTEVALUATE();
26 │ void 1 ** 2;
· ───────────
· ──────
╰────
help: Wrap unary expression in parentheses to enforce operator precedence

Expand Down
Loading
Loading