Skip to content

Commit 44e0d32

Browse files
nshahanCommit Queue
authored andcommitted
[ddc] Fix expression evaluation with wildcards
When wildcard method arguments should be removed from the scope before attempting to evaluate an expression. Otherwise their names are invalid and cause a Dart compile time error. Add tests for all forms of wildcards in expression evaluation. Change-Id: I21a7676a1495383926552a1039789f7d29256a67 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/431951 Reviewed-by: Nate Biggs <[email protected]> Commit-Queue: Nicholas Shahan <[email protected]>
1 parent c008be6 commit 44e0d32

File tree

2 files changed

+221
-5
lines changed

2 files changed

+221
-5
lines changed

pkg/dev_compiler/lib/src/kernel/expression_compiler.dart

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,16 @@ class ExpressionCompiler {
224224
}
225225
}
226226

227-
// remove undefined js variables (this allows us to get a reference error
228-
// from chrome on evaluation)
229-
dartScope.definitions.removeWhere(
230-
(variable, type) => !dartNameToJsValue.containsKey(variable));
231-
227+
dartScope.definitions.removeWhere((variable, type) =>
228+
// Remove undefined js variables (this allows us to get a reference
229+
// error from chrome on evaluation).
230+
!dartNameToJsValue.containsKey(variable) ||
231+
// Remove wildcard method arguments which are lowered to have Dart
232+
// names that are invalid for Dart compilations.
233+
// Wildcard local variables are not appearing here at this time.
234+
isWildcardLoweredFormalParameter(variable));
235+
236+
// Wildcard type parameters already matched by this existing test.
232237
dartScope.typeParameters
233238
.removeWhere((parameter) => !jsScope.containsKey(parameter.name));
234239

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
// Copyright (c) 2025, 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+
import 'package:dev_compiler/src/compiler/module_builder.dart'
6+
show ModuleFormat;
7+
import 'package:test/test.dart';
8+
9+
import '../shared_test_options.dart';
10+
import 'expression_compiler_e2e_suite.dart';
11+
12+
void main(List<String> args) async {
13+
var driver = await ExpressionEvaluationTestDriver.init();
14+
15+
group('Dart 3.7 language features', () {
16+
tearDownAll(() async {
17+
await driver.finish();
18+
});
19+
20+
group('(AMD module system)', () {
21+
var setup = SetupCompilerOptions(
22+
moduleFormat: ModuleFormat.amd,
23+
args: args,
24+
);
25+
runSharedTests(setup, driver);
26+
});
27+
28+
group('(DDC module system)', () {
29+
var setup = SetupCompilerOptions(
30+
moduleFormat: ModuleFormat.ddc,
31+
args: args,
32+
);
33+
runSharedTests(setup, driver);
34+
});
35+
});
36+
}
37+
38+
/// Shared tests for language features introduced in version 3.7.0.
39+
///
40+
/// Wildcards create local variables in scope that are unused. They should not
41+
/// interfere with expression evaluation in any way.
42+
void runSharedTests(
43+
SetupCompilerOptions setup, ExpressionEvaluationTestDriver driver) {
44+
group('Wildcard', () {
45+
const recordsSource = '''
46+
// @dart=3.7
47+
void main() {
48+
withWildcardArgument(1, "two");
49+
withWildcardLocalVariable("hello");
50+
withWildcardTypeArgument<DateTime, String>("world");
51+
}
52+
53+
void withWildcardArgument(int _, String __) {
54+
var d = Duration(seconds: 3);
55+
56+
// Breakpoint: bp
57+
print('hello world');
58+
}
59+
60+
void withWildcardLocalVariable(String __) {
61+
var _ = 99;
62+
String _ = 'never used';
63+
var d = Duration(seconds: 3);
64+
65+
// Breakpoint: bp2
66+
print('hello world');
67+
}
68+
69+
void withWildcardTypeArgument<_, T>(String __) {
70+
var d = Duration(seconds: 3);
71+
72+
// Breakpoint: bp3
73+
print('hello world');
74+
}
75+
''';
76+
77+
setUpAll(() async {
78+
await driver.initSource(setup, recordsSource);
79+
});
80+
81+
tearDownAll(() async {
82+
await driver.cleanupTest();
83+
});
84+
85+
test('method argument in scope do not break other evaluations', () async {
86+
await driver.checkInFrame(
87+
breakpointId: 'bp',
88+
expression: 'd.toString()',
89+
expectedResult: '0:00:03.000000');
90+
await driver.checkInFrame(
91+
breakpointId: 'bp',
92+
expression: '__.toString()',
93+
expectedResult: 'two');
94+
});
95+
96+
test('local variable in scope do not break other evaluations', () async {
97+
await driver.checkInFrame(
98+
breakpointId: 'bp2',
99+
expression: 'd.toString()',
100+
expectedResult: '0:00:03.000000');
101+
await driver.checkInFrame(
102+
breakpointId: 'bp2',
103+
expression: '__.toString()',
104+
expectedResult: 'hello');
105+
});
106+
107+
test('type argument in scope do not break other evaluations', () async {
108+
await driver.checkInFrame(
109+
breakpointId: 'bp3',
110+
expression: 'd.toString()',
111+
expectedResult: '0:00:03.000000');
112+
await driver.checkInFrame(
113+
breakpointId: 'bp3',
114+
expression: '__.toString()',
115+
expectedResult: 'world');
116+
await driver.checkInFrame(
117+
breakpointId: 'bp3',
118+
expression: 'T.toString()',
119+
expectedResult: 'String');
120+
});
121+
});
122+
123+
group('Wildcard in async scope', () {
124+
const recordsSource = '''
125+
// @dart=3.7
126+
Future<void> main() async {
127+
await withWildcardArgument(1, "two");
128+
await withWildcardLocalVariable("hello");
129+
await withWildcardTypeArgument<DateTime, String>("world");
130+
}
131+
132+
Future<void> withWildcardArgument(int _, String __) async {
133+
var d = Duration(seconds: 3);
134+
135+
// Use the argument in the scope to ensure chrome captures it.
136+
print(__);
137+
138+
// Breakpoint: bp
139+
print('hello world');
140+
}
141+
142+
Future<void> withWildcardLocalVariable(String __) async {
143+
var _ = 99;
144+
String _ = 'never used';
145+
var d = Duration(seconds: 3);
146+
147+
// Use the argument in the scope to ensure chrome captures it.
148+
print(__);
149+
150+
// Breakpoint: bp2
151+
print('hello world');
152+
}
153+
154+
Future<void> withWildcardTypeArgument<_, T>(String __) async {
155+
var d = Duration(seconds: 3);
156+
157+
// Use the arguments in the scope to ensure chrome captures it.
158+
print(__);
159+
print(T);
160+
161+
// Breakpoint: bp3
162+
print('hello world');
163+
}
164+
''';
165+
166+
setUpAll(() async {
167+
await driver.initSource(setup, recordsSource);
168+
});
169+
170+
tearDownAll(() async {
171+
await driver.cleanupTest();
172+
});
173+
174+
test('method argument in scope do not break other evaluations', () async {
175+
await driver.checkInFrame(
176+
breakpointId: 'bp',
177+
expression: 'd.toString()',
178+
expectedResult: '0:00:03.000000');
179+
await driver.checkInFrame(
180+
breakpointId: 'bp',
181+
expression: '__.toString()',
182+
expectedResult: 'two');
183+
});
184+
185+
test('local variable in scope do not break other evaluations', () async {
186+
await driver.checkInFrame(
187+
breakpointId: 'bp2',
188+
expression: 'd.toString()',
189+
expectedResult: '0:00:03.000000');
190+
await driver.checkInFrame(
191+
breakpointId: 'bp2',
192+
expression: '__.toString()',
193+
expectedResult: 'hello');
194+
});
195+
196+
test('type argument in scope do not break other evaluations', () async {
197+
await driver.checkInFrame(
198+
breakpointId: 'bp3',
199+
expression: 'd.toString()',
200+
expectedResult: '0:00:03.000000');
201+
await driver.checkInFrame(
202+
breakpointId: 'bp3',
203+
expression: '__.toString()',
204+
expectedResult: 'world');
205+
await driver.checkInFrame(
206+
breakpointId: 'bp3',
207+
expression: 'T.toString()',
208+
expectedResult: 'String');
209+
});
210+
});
211+
}

0 commit comments

Comments
 (0)