[CFE] Fix crash when using pattern in for loop when language version is too low Fixes https://github.com/dart-lang/sdk/issues/59656 Change-Id: If59bb9271b8bf1db70ae63101f95444e3eb7cdc2 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/399105 Reviewed-by: Johnni Winther <johnniwinther@google.com> Commit-Queue: Jens Johansen <jensj@google.com>
diff --git a/pkg/front_end/lib/src/kernel/body_builder.dart b/pkg/front_end/lib/src/kernel/body_builder.dart index 781df23..5109399 100644 --- a/pkg/front_end/lib/src/kernel/body_builder.dart +++ b/pkg/front_end/lib/src/kernel/body_builder.dart
@@ -7601,7 +7601,7 @@ @override void handleForInLoopParts(Token? awaitToken, Token forToken, Token leftParenthesis, Token? patternKeyword, Token inKeyword) { - debugEvent("ForIntLoopParts"); + debugEvent("ForInLoopParts"); assert(checkState(forToken, [ unionOfKinds([ ValueKinds.Expression, @@ -7614,6 +7614,7 @@ ValueKinds.ProblemBuilder, ValueKinds.Pattern, ValueKinds.Statement, // Variable for non-pattern for-in loop. + ValueKinds.ParserRecovery, ]), ])); Object expression = pop() as Object; @@ -7755,6 +7756,9 @@ isFinal: false); } else if (lvalue is AmbiguousBuilder) { elements.expressionProblem = toValue(lvalue); + } else if (lvalue is ParserRecovery) { + elements.expressionProblem = buildProblem( + cfe.messageSyntheticToken, lvalue.charOffset, noLength); } else { Message message = forest.isVariablesDeclaration(lvalue) ? cfe.messageForInLoopExactlyOneVariable @@ -7805,6 +7809,7 @@ ValueKinds.ProblemBuilder, ValueKinds.Pattern, ValueKinds.Statement, + ValueKinds.ParserRecovery, ]), ])); Statement body = popStatement(endToken);
diff --git a/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart b/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart new file mode 100644 index 0000000..3be5d69 --- /dev/null +++ b/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart
@@ -0,0 +1,13 @@ +// 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. + +// @dart=2.12 + +void foo() { + var repoPaths = [(user: "a", repo: "b")]; + for (var (:user, :repo) in repoPaths) { + print(user); + print(repo); + } +}
diff --git a/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart.strong.expect b/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart.strong.expect new file mode 100644 index 0000000..8365ab2 --- /dev/null +++ b/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart.strong.expect
@@ -0,0 +1,63 @@ +library; +// +// Problems in library: +// +// 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. +// Try removing the `@dart=` annotation or setting the language version to 3.0 or higher. +// var repoPaths = [(user: "a", repo: "b")]; +// ^ +// 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. +// // @dart=2.12 +// ^^^^^^^^^^^^^ +// +// 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 '('. +// Try inserting an identifier before '('. +// for (var (:user, :repo) in repoPaths) { +// ^ +// +// 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. +// Try replacing the colon with the keyword 'in'. +// for (var (:user, :repo) in repoPaths) { +// ^ +// +// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:18: Error: Expected ')' before this. +// for (var (:user, :repo) in repoPaths) { +// ^ +// +// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:14: Error: Undefined name 'user'. +// for (var (:user, :repo) in repoPaths) { +// ^^^^ +// +// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:10:11: Error: Undefined name 'user'. +// print(user); +// ^^^^ +// +// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:11:11: Error: Undefined name 'repo'. +// print(repo); +// ^^^^ +// +// 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. +// for (var (:user, :repo) in repoPaths) { +// ^ +// +import self as self; +import "dart:core" as core; + +static method foo() → void { + core::List<dynamic> repoPaths = <dynamic>[let final core::String #t1 = "a" in invalid-expression "This requires the experimental 'records' language feature to be enabled."]; + { + 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. + for (var (:user, :repo) in repoPaths) { + ^"; + 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'. + for (var (:user, :repo) in repoPaths) { + ^^^^") { + 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'. + print(user); + ^^^^"); + 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'. + print(repo); + ^^^^"); + } + } +}
diff --git a/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart.strong.modular.expect b/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart.strong.modular.expect new file mode 100644 index 0000000..8365ab2 --- /dev/null +++ b/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart.strong.modular.expect
@@ -0,0 +1,63 @@ +library; +// +// Problems in library: +// +// 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. +// Try removing the `@dart=` annotation or setting the language version to 3.0 or higher. +// var repoPaths = [(user: "a", repo: "b")]; +// ^ +// 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. +// // @dart=2.12 +// ^^^^^^^^^^^^^ +// +// 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 '('. +// Try inserting an identifier before '('. +// for (var (:user, :repo) in repoPaths) { +// ^ +// +// 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. +// Try replacing the colon with the keyword 'in'. +// for (var (:user, :repo) in repoPaths) { +// ^ +// +// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:18: Error: Expected ')' before this. +// for (var (:user, :repo) in repoPaths) { +// ^ +// +// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:14: Error: Undefined name 'user'. +// for (var (:user, :repo) in repoPaths) { +// ^^^^ +// +// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:10:11: Error: Undefined name 'user'. +// print(user); +// ^^^^ +// +// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:11:11: Error: Undefined name 'repo'. +// print(repo); +// ^^^^ +// +// 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. +// for (var (:user, :repo) in repoPaths) { +// ^ +// +import self as self; +import "dart:core" as core; + +static method foo() → void { + core::List<dynamic> repoPaths = <dynamic>[let final core::String #t1 = "a" in invalid-expression "This requires the experimental 'records' language feature to be enabled."]; + { + 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. + for (var (:user, :repo) in repoPaths) { + ^"; + 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'. + for (var (:user, :repo) in repoPaths) { + ^^^^") { + 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'. + print(user); + ^^^^"); + 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'. + print(repo); + ^^^^"); + } + } +}
diff --git a/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart.strong.outline.expect b/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart.strong.outline.expect new file mode 100644 index 0000000..4a8528a --- /dev/null +++ b/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart.strong.outline.expect
@@ -0,0 +1,5 @@ +library; +import self as self; + +static method foo() → void + ;
diff --git a/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart.strong.transformed.expect b/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart.strong.transformed.expect new file mode 100644 index 0000000..e78cad9 --- /dev/null +++ b/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart.strong.transformed.expect
@@ -0,0 +1,69 @@ +library; +// +// Problems in library: +// +// 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. +// Try removing the `@dart=` annotation or setting the language version to 3.0 or higher. +// var repoPaths = [(user: "a", repo: "b")]; +// ^ +// 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. +// // @dart=2.12 +// ^^^^^^^^^^^^^ +// +// 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 '('. +// Try inserting an identifier before '('. +// for (var (:user, :repo) in repoPaths) { +// ^ +// +// 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. +// Try replacing the colon with the keyword 'in'. +// for (var (:user, :repo) in repoPaths) { +// ^ +// +// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:18: Error: Expected ')' before this. +// for (var (:user, :repo) in repoPaths) { +// ^ +// +// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:9:14: Error: Undefined name 'user'. +// for (var (:user, :repo) in repoPaths) { +// ^^^^ +// +// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:10:11: Error: Undefined name 'user'. +// print(user); +// ^^^^ +// +// pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart:11:11: Error: Undefined name 'repo'. +// print(repo); +// ^^^^ +// +// 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. +// for (var (:user, :repo) in repoPaths) { +// ^ +// +import self as self; +import "dart:core" as core; + +static method foo() → void { + 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."); + { + 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. + for (var (:user, :repo) in repoPaths) { + ^"; + { + 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'. + for (var (:user, :repo) in repoPaths) { + ^^^^".{core::Iterable::iterator}{core::Iterator<Never>}; + for (; :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) { + final dynamic #t2 = :sync-for-iterator.{core::Iterator::current}{Never}; + { + 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'. + print(user); + ^^^^"); + 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'. + print(repo); + ^^^^"); + } + } + } + } +}
diff --git a/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart.textual_outline.expect b/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart.textual_outline.expect new file mode 100644 index 0000000..7bc3030 --- /dev/null +++ b/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart.textual_outline.expect
@@ -0,0 +1,3 @@ +// @dart = 2.12 + +void foo() {}
diff --git a/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart.textual_outline_modelled.expect new file mode 100644 index 0000000..7bc3030 --- /dev/null +++ b/pkg/front_end/testcases/regress/use_pattern_in_for_loop_when_language_version_is_too_low.dart.textual_outline_modelled.expect
@@ -0,0 +1,3 @@ +// @dart = 2.12 + +void foo() {}