Skip to content

Commit 2ae211e

Browse files
jensjohaCommit Queue
authored andcommitted
[CFE] Fix crash when using pattern in for loop when language version is too low
Fixes #59656 Change-Id: If59bb9271b8bf1db70ae63101f95444e3eb7cdc2 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/399105 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Jens Johansen <[email protected]>
1 parent 903eea6 commit 2ae211e

8 files changed

+225
-1
lines changed

pkg/front_end/lib/src/kernel/body_builder.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7601,7 +7601,7 @@ class BodyBuilder extends StackListenerImpl
76017601
@override
76027602
void handleForInLoopParts(Token? awaitToken, Token forToken,
76037603
Token leftParenthesis, Token? patternKeyword, Token inKeyword) {
7604-
debugEvent("ForIntLoopParts");
7604+
debugEvent("ForInLoopParts");
76057605
assert(checkState(forToken, [
76067606
unionOfKinds([
76077607
ValueKinds.Expression,
@@ -7614,6 +7614,7 @@ class BodyBuilder extends StackListenerImpl
76147614
ValueKinds.ProblemBuilder,
76157615
ValueKinds.Pattern,
76167616
ValueKinds.Statement, // Variable for non-pattern for-in loop.
7617+
ValueKinds.ParserRecovery,
76177618
]),
76187619
]));
76197620
Object expression = pop() as Object;
@@ -7755,6 +7756,9 @@ class BodyBuilder extends StackListenerImpl
77557756
isFinal: false);
77567757
} else if (lvalue is AmbiguousBuilder) {
77577758
elements.expressionProblem = toValue(lvalue);
7759+
} else if (lvalue is ParserRecovery) {
7760+
elements.expressionProblem = buildProblem(
7761+
cfe.messageSyntheticToken, lvalue.charOffset, noLength);
77587762
} else {
77597763
Message message = forest.isVariablesDeclaration(lvalue)
77607764
? cfe.messageForInLoopExactlyOneVariable
@@ -7805,6 +7809,7 @@ class BodyBuilder extends StackListenerImpl
78057809
ValueKinds.ProblemBuilder,
78067810
ValueKinds.Pattern,
78077811
ValueKinds.Statement,
7812+
ValueKinds.ParserRecovery,
78087813
]),
78097814
]));
78107815
Statement body = popStatement(endToken);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// @dart=2.12
6+
7+
void foo() {
8+
var repoPaths = [(user: "a", repo: "b")];
9+
for (var (:user, :repo) in repoPaths) {
10+
print(user);
11+
print(repo);
12+
}
13+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:8:20: Error: The 'records' language feature is disabled for this library.
6+
// Try removing the `@dart=` annotation or setting the language version to 3.0 or higher.
7+
// var repoPaths = [(user: "a", repo: "b")];
8+
// ^
9+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:5:1: Context: This is the annotation that opts out this library from the 'records' language feature.
10+
// // @dart=2.12
11+
// ^^^^^^^^^^^^^
12+
//
13+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:12: Error: Expected an identifier, but got '('.
14+
// Try inserting an identifier before '('.
15+
// for (var (:user, :repo) in repoPaths) {
16+
// ^
17+
//
18+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:13: Error: For-in loops use 'in' rather than a colon.
19+
// Try replacing the colon with the keyword 'in'.
20+
// for (var (:user, :repo) in repoPaths) {
21+
// ^
22+
//
23+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:18: Error: Expected ')' before this.
24+
// for (var (:user, :repo) in repoPaths) {
25+
// ^
26+
//
27+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:14: Error: Undefined name 'user'.
28+
// for (var (:user, :repo) in repoPaths) {
29+
// ^^^^
30+
//
31+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:10:11: Error: Undefined name 'user'.
32+
// print(user);
33+
// ^^^^
34+
//
35+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:11:11: Error: Undefined name 'repo'.
36+
// print(repo);
37+
// ^^^^
38+
//
39+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:13: Error: This couldn't be parsed.
40+
// for (var (:user, :repo) in repoPaths) {
41+
// ^
42+
//
43+
import self as self;
44+
import "dart:core" as core;
45+
46+
static method foo() → void {
47+
core::List<dynamic> repoPaths = <dynamic>[let final core::String #t1 = "a" in invalid-expression "This requires the experimental 'records' language feature to be enabled."];
48+
{
49+
invalid-expression "pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:13: Error: This couldn't be parsed.
50+
for (var (:user, :repo) in repoPaths) {
51+
^";
52+
for (final dynamic #t2 in invalid-expression "pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:14: Error: Undefined name 'user'.
53+
for (var (:user, :repo) in repoPaths) {
54+
^^^^") {
55+
core::print(invalid-expression "pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:10:11: Error: Undefined name 'user'.
56+
print(user);
57+
^^^^");
58+
core::print(invalid-expression "pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:11:11: Error: Undefined name 'repo'.
59+
print(repo);
60+
^^^^");
61+
}
62+
}
63+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:8:20: Error: The 'records' language feature is disabled for this library.
6+
// Try removing the `@dart=` annotation or setting the language version to 3.0 or higher.
7+
// var repoPaths = [(user: "a", repo: "b")];
8+
// ^
9+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:5:1: Context: This is the annotation that opts out this library from the 'records' language feature.
10+
// // @dart=2.12
11+
// ^^^^^^^^^^^^^
12+
//
13+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:12: Error: Expected an identifier, but got '('.
14+
// Try inserting an identifier before '('.
15+
// for (var (:user, :repo) in repoPaths) {
16+
// ^
17+
//
18+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:13: Error: For-in loops use 'in' rather than a colon.
19+
// Try replacing the colon with the keyword 'in'.
20+
// for (var (:user, :repo) in repoPaths) {
21+
// ^
22+
//
23+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:18: Error: Expected ')' before this.
24+
// for (var (:user, :repo) in repoPaths) {
25+
// ^
26+
//
27+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:14: Error: Undefined name 'user'.
28+
// for (var (:user, :repo) in repoPaths) {
29+
// ^^^^
30+
//
31+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:10:11: Error: Undefined name 'user'.
32+
// print(user);
33+
// ^^^^
34+
//
35+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:11:11: Error: Undefined name 'repo'.
36+
// print(repo);
37+
// ^^^^
38+
//
39+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:13: Error: This couldn't be parsed.
40+
// for (var (:user, :repo) in repoPaths) {
41+
// ^
42+
//
43+
import self as self;
44+
import "dart:core" as core;
45+
46+
static method foo() → void {
47+
core::List<dynamic> repoPaths = <dynamic>[let final core::String #t1 = "a" in invalid-expression "This requires the experimental 'records' language feature to be enabled."];
48+
{
49+
invalid-expression "pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:13: Error: This couldn't be parsed.
50+
for (var (:user, :repo) in repoPaths) {
51+
^";
52+
for (final dynamic #t2 in invalid-expression "pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:14: Error: Undefined name 'user'.
53+
for (var (:user, :repo) in repoPaths) {
54+
^^^^") {
55+
core::print(invalid-expression "pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:10:11: Error: Undefined name 'user'.
56+
print(user);
57+
^^^^");
58+
core::print(invalid-expression "pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:11:11: Error: Undefined name 'repo'.
59+
print(repo);
60+
^^^^");
61+
}
62+
}
63+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
library;
2+
import self as self;
3+
4+
static method foo() → void
5+
;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:8:20: Error: The 'records' language feature is disabled for this library.
6+
// Try removing the `@dart=` annotation or setting the language version to 3.0 or higher.
7+
// var repoPaths = [(user: "a", repo: "b")];
8+
// ^
9+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:5:1: Context: This is the annotation that opts out this library from the 'records' language feature.
10+
// // @dart=2.12
11+
// ^^^^^^^^^^^^^
12+
//
13+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:12: Error: Expected an identifier, but got '('.
14+
// Try inserting an identifier before '('.
15+
// for (var (:user, :repo) in repoPaths) {
16+
// ^
17+
//
18+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:13: Error: For-in loops use 'in' rather than a colon.
19+
// Try replacing the colon with the keyword 'in'.
20+
// for (var (:user, :repo) in repoPaths) {
21+
// ^
22+
//
23+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:18: Error: Expected ')' before this.
24+
// for (var (:user, :repo) in repoPaths) {
25+
// ^
26+
//
27+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:14: Error: Undefined name 'user'.
28+
// for (var (:user, :repo) in repoPaths) {
29+
// ^^^^
30+
//
31+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:10:11: Error: Undefined name 'user'.
32+
// print(user);
33+
// ^^^^
34+
//
35+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:11:11: Error: Undefined name 'repo'.
36+
// print(repo);
37+
// ^^^^
38+
//
39+
// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:13: Error: This couldn't be parsed.
40+
// for (var (:user, :repo) in repoPaths) {
41+
// ^
42+
//
43+
import self as self;
44+
import "dart:core" as core;
45+
46+
static method foo() → void {
47+
core::List<dynamic> repoPaths = core::_GrowableList::_literal1<dynamic>(let final core::String #t1 = "a" in invalid-expression "This requires the experimental 'records' language feature to be enabled.");
48+
{
49+
invalid-expression "pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:13: Error: This couldn't be parsed.
50+
for (var (:user, :repo) in repoPaths) {
51+
^";
52+
{
53+
synthesized core::Iterator<Never> :sync-for-iterator = invalid-expression "pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:14: Error: Undefined name 'user'.
54+
for (var (:user, :repo) in repoPaths) {
55+
^^^^".{core::Iterable::iterator}{core::Iterator<Never>};
56+
for (; :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) {
57+
final dynamic #t2 = :sync-for-iterator.{core::Iterator::current}{Never};
58+
{
59+
core::print(invalid-expression "pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:10:11: Error: Undefined name 'user'.
60+
print(user);
61+
^^^^");
62+
core::print(invalid-expression "pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:11:11: Error: Undefined name 'repo'.
63+
print(repo);
64+
^^^^");
65+
}
66+
}
67+
}
68+
}
69+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// @dart = 2.12
2+
3+
void foo() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// @dart = 2.12
2+
3+
void foo() {}

0 commit comments

Comments
 (0)