| ... | ... | @@ -177,23 +177,57 @@ pub fn parse_expr(l: &mut LexState) -> Result<usize, OxcDiagnostic> { |
| 177 | 177 | pub fn parse_expr_without_gt(l: &mut LexState) -> Result<usize, OxcDiagnostic> { |
| 178 | 178 | use oxc_ast::ast::{BinaryOperator, Expression}; |
| 179 | 179 | |
| 180 | let rest = l.peek_rest(); |
| 180 | 181 | let mut allocator = Allocator::new(); |
| 181 | | let expr = parse_expr_extra(l.peek_rest(), l.offset().cast_signed(), &mut allocator)?; |
| 182 | 182 | |
| 183 | | let mut e = &expr; |
| 184 | | while let Expression::BinaryExpression(bin) = e { |
| 185 | | if bin.operator == BinaryOperator::GreaterThan |
| 186 | | || bin.operator == BinaryOperator::GreaterEqualThan |
| 187 | | || bin.operator == BinaryOperator::ShiftRight |
| 188 | | || bin.operator == BinaryOperator::ShiftRightZeroFill |
| 189 | | { |
| 190 | | e = &bin.left; |
| 191 | | } else { |
| 192 | | break; |
| 183 | // First, try parsing the full source and using AST walking to strip trailing `>`. |
| 184 | // This handles balanced cases like `arr[a > 0]` and `fn(a > b)` correctly. |
| 185 | if let Ok(expr) = parse_expr_extra(rest, l.offset().cast_signed(), &mut allocator) { |
| 186 | let mut e = &expr; |
| 187 | while let Expression::BinaryExpression(bin) = e { |
| 188 | if bin.operator == BinaryOperator::GreaterThan |
| 189 | || bin.operator == BinaryOperator::GreaterEqualThan |
| 190 | || bin.operator == BinaryOperator::ShiftRight |
| 191 | || bin.operator == BinaryOperator::ShiftRightZeroFill |
| 192 | { |
| 193 | e = &bin.left; |
| 194 | } else { |
| 195 | break; |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | let length = e.span().end as usize; |
| 200 | l.advance(length); |
| 201 | return Ok(length); |
| 202 | } |
| 203 | |
| 204 | // If full parse failed (e.g., due to JSX-like content after the expression), |
| 205 | // try parsing prefixes ending at each `>` position. |
| 206 | let gt_positions: Vec<usize> = rest |
| 207 | .char_indices() |
| 208 | .filter(|(_, c)| *c == '>') |
| 209 | .map(|(i, _)| i) |
| 210 | .collect(); |
| 211 | |
| 212 | // Try from longest to shortest prefix, return first success |
| 213 | for &pos in gt_positions.iter().rev() { |
| 214 | let candidate = &rest[..pos]; |
| 215 | allocator.reset(); |
| 216 | if let Ok(expr) = parse_expr_extra(candidate, l.offset().cast_signed(), &mut allocator) { |
| 217 | // Check that the parse consumed most of the candidate (not just a prefix) |
| 218 | let length = expr.span().end as usize; |
| 219 | // If the expression spans close to the full candidate, use it |
| 220 | if length >= pos.saturating_sub(1) { |
| 221 | l.advance(length); |
| 222 | return Ok(length); |
| 223 | } |
| 193 | 224 | } |
| 194 | 225 | } |
| 195 | 226 | |
| 196 | | let length = e.span().end as usize; |
| 227 | // Last resort: return error from the original parse attempt |
| 228 | allocator.reset(); |
| 229 | let expr = parse_expr_extra(rest, l.offset().cast_signed(), &mut allocator)?; |
| 230 | let length = expr.span().end as usize; |
| 197 | 231 | l.advance(length); |
| 198 | 232 | Ok(length) |
| 199 | 233 | } |