-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Parenthesize calls within `new`-expression targets to prevent the call arguments being associated with the `new`-expression. * Negation of exponentiation and negation as the left operand of exponentiation are syntax errors, so parenthesize the exponentiation or left operand. "-2 ** n" --> "(-2) ** n" or "-(2 ** n)" * "e in e" needs to be parenthesized in more places in a for-statement to avoid printing a for-in statement. The missing place was the right operand of a binary operator. "for (i = (0 in o) || 1 in o" --> "for (i = (0 in o) || (1 in o)" Bug: #54534 Bug: b/313833546 Bug: b/314023208 Bug: b/314041897 Change-Id: I12fcd75ac5546a425b21cd68a9bda3786ceb243d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/347423 Commit-Queue: Stephen Adams <[email protected]> Reviewed-by: Sigmund Cherem <[email protected]>
- Loading branch information
Showing
8 changed files
with
273 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'package:expect/expect.dart'; | ||
import 'package:js_ast/js_ast.dart'; | ||
import 'print_helper.dart'; | ||
|
||
void main() { | ||
// Basic precedence. | ||
final aPlus1 = testExpression('a + 1'); | ||
final bTimes2 = testExpression('b * 2'); | ||
|
||
Expect.type<Binary>(aPlus1); | ||
Expect.type<Binary>(bTimes2); | ||
|
||
testExpression('# + x', aPlus1, 'a + 1 + x'); | ||
testExpression('x + #', aPlus1, 'x + (a + 1)'); | ||
testExpression('# * x', aPlus1, '(a + 1) * x'); | ||
testExpression('x * #', aPlus1, 'x * (a + 1)'); | ||
|
||
testExpression('# + x', bTimes2, 'b * 2 + x'); | ||
testExpression('x + #', bTimes2, 'x + b * 2'); | ||
testExpression('# * x', bTimes2, 'b * 2 * x'); | ||
testExpression('x * #', bTimes2, 'x * (b * 2)'); | ||
|
||
testExpression('# + #', [aPlus1, aPlus1], 'a + 1 + (a + 1)'); | ||
testExpression('# + #', [bTimes2, bTimes2], 'b * 2 + b * 2'); | ||
testExpression('# * #', [aPlus1, aPlus1], '(a + 1) * (a + 1)'); | ||
testExpression('# * #', [bTimes2, bTimes2], 'b * 2 * (b * 2)'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'package:js_ast/js_ast.dart'; | ||
import 'print_helper.dart'; | ||
|
||
void main() { | ||
final aPowB = testExpression('a ** b'); | ||
|
||
testExpression('# ** c', aPowB, '(a ** b) ** c'); | ||
testExpression('c ** #', aPowB, 'c ** a ** b'); | ||
|
||
// Miniparser parses with incorrect association: | ||
testExpression('a ** b ** c', '(a ** b) ** c'); | ||
|
||
testExpression('(a ** b) ** c', '(a ** b) ** c'); | ||
testExpression('a ** (b ** c)', 'a ** b ** c'); | ||
testExpression('a **= b'); | ||
|
||
// `-a**b` is a JavaScript parse error. Parentheses are required to | ||
// disambiguate between `(-a)**b` and `-(a**b)`. | ||
|
||
testExpression('-(2 ** n)'); | ||
|
||
testExpression('(-(2)) ** n', '(-2) ** n'); | ||
testExpression('(-2) ** n', '(-2) ** n'); | ||
|
||
final minus2 = js.number(-2); | ||
final negated2 = js('-#', js.number(2)); | ||
|
||
testExpression('# ** x', minus2, '(-2) ** x'); | ||
testExpression('# ** x', negated2, '(-2) ** x'); | ||
|
||
testExpression('-(2 ** n)'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'print_helper.dart'; | ||
|
||
void main() { | ||
// The mini-parser does not recognize for-in statements, mis-parsing them as for-statements. | ||
testStatement( | ||
'for(a in b; a in b; a in b);', | ||
'for ((a in b); a in b; a in b)\n ;', | ||
); | ||
|
||
final aInB = testExpression('a in b'); | ||
|
||
testStatement( | ||
'for(#;#;#);', | ||
[aInB, aInB, aInB], | ||
'for ((a in b); a in b; a in b)\n ;', | ||
); | ||
|
||
testStatement( | ||
'for(var v = (# || #);;);', | ||
[aInB, aInB], | ||
'for (var v = (a in b) || (a in b);;)\n ;', | ||
); | ||
|
||
testStatement( | ||
'for (u = (a + 1) * (b in z);;);', | ||
'for (u = (a + 1) * (b in z);;)\n ;', | ||
); | ||
|
||
testStatement( | ||
'for (u = (a + 1) * #;;);', | ||
aInB, | ||
'for (u = (a + 1) * (a in b);;)\n ;', | ||
); | ||
|
||
testStatement( | ||
'for (u = (1 + a) * 2, v = 1 || (b in z) || 2;;);', | ||
'for (u = (1 + a) * 2, v = 1 || (b in z) || 2;;)\n ;', | ||
); | ||
|
||
testStatement( | ||
'for (var v in x);', | ||
'for (var v in x)\n ;', | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'package:expect/expect.dart'; | ||
import 'package:js_ast/js_ast.dart'; | ||
|
||
/// Supports three calling patterns: | ||
/// | ||
/// testExpression(template) | ||
/// testExpression(template, expected) | ||
/// testExpression(template, arguments, expected) | ||
/// | ||
/// `template` is a String, possibly containing `#` placeholders. | ||
/// `arguments` can be a String, but only when `expected` is provided. | ||
Node testExpression(String expression, [optional1, String? optional2]) { | ||
return _test(js.call, expression, optional1, optional2); | ||
} | ||
|
||
/// Supports three calling patterns: | ||
/// | ||
/// testStatement(template) | ||
/// testStatement(template, expected) | ||
/// testStatement(template, arguments, expected) | ||
/// | ||
/// `template` is a String, possibly containing `#` placeholders. | ||
/// `arguments` can be a String, but only when `expected` is provided. | ||
Node testStatement(String expression, [optional1, String? optional2]) { | ||
return _test(js.statement, expression, optional1, optional2); | ||
} | ||
|
||
Node _test(Node Function(String, Object?) parse, String expression, optional1, | ||
String? optional2) { | ||
final String expected; | ||
final Object? arguments; // null, List, or Map. | ||
|
||
if (optional2 is String) { | ||
expected = optional2; | ||
arguments = optional1 ?? const []; | ||
} else if (optional1 is String) { | ||
expected = optional1; | ||
arguments = const []; | ||
} else { | ||
expected = expression; | ||
arguments = optional1; | ||
} | ||
|
||
Node node = parse(expression, arguments); | ||
String jsText = prettyPrint(node); | ||
Expect.stringEquals(expected.trim(), jsText.trim()); | ||
return node; | ||
} | ||
|
||
String prettyPrint(Node node) { | ||
JavaScriptPrintingOptions options = JavaScriptPrintingOptions( | ||
shouldCompressOutput: false, | ||
minifyLocalVariables: false, | ||
preferSemicolonToNewlineInMinifiedOutput: false); | ||
SimpleJavaScriptPrintingContext context = SimpleJavaScriptPrintingContext(); | ||
Printer printer = Printer(options, context); | ||
printer.visit(node); | ||
return context.getText(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'package:expect/expect.dart'; | ||
import 'package:js_ast/js_ast.dart'; | ||
import 'print_helper.dart'; | ||
|
||
void main() { | ||
final propertyCall = testExpression('a.f(1)'); | ||
Expect.type<Call>(propertyCall); | ||
|
||
testExpression('#.g(2)', propertyCall, 'a.f(1).g(2)'); | ||
|
||
// Calls in the `new` target need to be parenthesized to prevent the call | ||
// arguments from being taken as the `new` arguments. | ||
testExpression('new #.a()', propertyCall, 'new (a.f(1)).a()'); | ||
testExpression('new #(2)', testExpression('f(1)'), 'new (f(1))(2)'); | ||
testExpression('new #(2)', testExpression('f(1).x'), 'new (f(1)).x(2)'); | ||
testExpression('new #(2)', testExpression('f(1).x()'), 'new (f(1).x())(2)'); | ||
|
||
testExpression('new (f.x)()', 'new f.x()'); | ||
testExpression('new (f().x)()', 'new (f()).x()'); // Also ok: `new (f().x)()` | ||
testExpression('new (f.x())()', 'new (f.x())()'); | ||
|
||
testExpression('(new f.x(1))(2)', 'new f.x(1)(2)'); | ||
|
||
testExpression('new (new f(g(1).x))(2)', 'new new f(g(1).x)(2)'); | ||
|
||
testExpression('new f[g(1).x](2)'); | ||
testExpression('new (f()[g(1).x])(2)', 'new (f())[g(1).x](2)'); | ||
testExpression('new (f[g(1).x])(2)', 'new f[g(1).x](2)'); | ||
|
||
// All the operators that have a second expression that is not protected (by | ||
// being inside an argument list or `[]` index) have lower priority than the | ||
// `new` MemberExpression, so require parentheses regardless of whether they | ||
// contain a call. | ||
testExpression('new (f || g)(1)'); | ||
testExpression('new (f ** g)(3)'); | ||
testExpression('new (f(1) || g(2))(3)'); | ||
} |