mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-30 23:42:45 +00:00 
			
		
		
		
	 3709d11212
			
		
	
	
		3709d11212
		
	
	
	
	
		
			
			Instead of passing the continuously merged initial forbidden token set
(with the new additional forbidden tokens from each parsed secondary
expression) to the next call of parse_secondary_expression(), keep a
copy of the original set and use it as the base for parsing the next
secondary expression.
This bug prevented us from properly parsing the following expression:
```js
0 ?? 0 ? 0 : 0 || 0
```
...due to LogicalExpression with LogicalOp::NullishCoalescing returning
both DoubleAmpersand and DoublePipe in its forbidden token set.
The following correct AST is now generated:
Program
  (Children)
    ExpressionStatement
      ConditionalExpression
        (Test)
          LogicalExpression
            NumericLiteral 0
            ??
            NumericLiteral 0
        (Consequent)
          NumericLiteral 0
        (Alternate)
          LogicalExpression
            NumericLiteral 0
            ||
            NumericLiteral 0
An alternate solution I explored was only merging the original forbidden
token set with the one of the last parsed secondary expression which is
then passed to match_secondary_expression(); however that led to an
incorrect AST (note the alternate expression):
Program
  (Children)
    ExpressionStatement
      LogicalExpression
        ConditionalExpression
          (Test)
            LogicalExpression
              NumericLiteral 0
              ??
              NumericLiteral 0
          (Consequent)
            NumericLiteral 0
          (Alternate)
            NumericLiteral 0
        ||
        NumericLiteral 0
Truth be told, I don't know enough about the inner workings of the
parser to fully explain the difference. AFAICT this patch has no
unintended side effects in its current form though.
Fixes #18087.
		
	
			
		
			
				
	
	
		
			41 lines
		
	
	
	
		
			1.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			41 lines
		
	
	
	
		
			1.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| test("mixing coalescing and logical operators isn't allowed", () => {
 | |
|     expect("if (0) a ?? b || c").not.toEval();
 | |
|     expect("if (0) a ?? b && c").not.toEval();
 | |
|     expect("if (0) a ?? b * c || d").not.toEval();
 | |
|     expect("if (0) a ?? b * c && d").not.toEval();
 | |
|     expect("if (0) a && b ?? c").not.toEval();
 | |
|     expect("if (0) a || b ?? c").not.toEval();
 | |
|     expect("if (0) a && b * c ?? d").not.toEval();
 | |
|     expect("if (0) a || b * c ?? d").not.toEval();
 | |
| });
 | |
| 
 | |
| test("mixing coalescing and logical operators with parens", () => {
 | |
|     expect("if (0) a ?? (b || c)").toEval();
 | |
|     expect("if (0) (a ?? b) && c").toEval();
 | |
|     expect("if (0) a ?? (b * c || d)").toEval();
 | |
|     expect("if (0) (a ?? b * c) && d").toEval();
 | |
|     expect("if (0) a && (b ?? c)").toEval();
 | |
|     expect("if (0) (a || b) ?? c").toEval();
 | |
|     expect("if (0) a && (b * c) ?? d").not.toEval();
 | |
|     expect("if (0) a || (b * c) ?? d").not.toEval();
 | |
| });
 | |
| 
 | |
| test("mixing coalescing and logical operators in ternary expressions", () => {
 | |
|     expect("0 || 0 ? 0 : 0 ?? 0").toEval();
 | |
|     expect("0 ?? 0 ? 0 : 0 || 0").toEval();
 | |
|     expect("0 ? 0 || 0 : 0 ?? 0").toEval();
 | |
|     expect("0 ? 0 ?? 0 : 0 || 0").toEval();
 | |
|     expect("0 && 0 ? 0 ?? 0 : 0 || 0").toEval();
 | |
|     expect("0 ?? 0 ? 0 && 0 : 0 || 0").toEval();
 | |
|     expect("0 ?? 0 ? 0 || 0 : 0 && 0").toEval();
 | |
|     expect("0 || 0 ? 0 ?? 0 : 0 && 0").toEval();
 | |
|     expect("0 && 0 ? 0 || 0 : 0 ?? 0").toEval();
 | |
|     expect("0 || 0 ? 0 && 0 : 0 ?? 0").toEval();
 | |
| });
 | |
| 
 | |
| test("mixing coalescing and logical operators when 'in' isn't allowed", () => {
 | |
|     expect("for (a ?? b || c in a; false;);").not.toEval();
 | |
|     expect("for (a ?? b && c in a; false;);").not.toEval();
 | |
|     expect("for (a || b ?? c in a; false;);").not.toEval();
 | |
|     expect("for (a && b ?? c in a; false;);").not.toEval();
 | |
| });
 |