Skip to content

Commit 6d9a4f1

Browse files
committed
Issue 42757. Implement null spread element typing with null-safety.
Bug: #42757 Change-Id: I6c0080402d8eb13aef8097ecf70025736002c3d1 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/155680 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
1 parent 8c316a9 commit 6d9a4f1

6 files changed

Lines changed: 430 additions & 73 deletions

File tree

pkg/analyzer/lib/src/dart/resolver/typed_literal_resolver.dart

Lines changed: 81 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -176,22 +176,37 @@ class TypedLiteralResolver {
176176
return _typeProvider.dynamicType;
177177
} else if (element is SpreadElement) {
178178
var expressionType = element.expression.staticType;
179+
180+
var iterableType = expressionType.asInstanceOf(
181+
_typeProvider.iterableElement,
182+
);
183+
if (iterableType != null) {
184+
return iterableType.typeArguments[0];
185+
}
186+
179187
if (expressionType.isDynamic) {
180-
return expressionType;
181-
} else if (expressionType is InterfaceType) {
188+
return _typeProvider.dynamicType;
189+
}
190+
191+
if (_typeSystem.isNonNullableByDefault) {
192+
if (_typeSystem.isSubtypeOf2(expressionType, NeverTypeImpl.instance)) {
193+
return NeverTypeImpl.instance;
194+
}
195+
if (_typeSystem.isSubtypeOf2(expressionType, _typeSystem.nullNone)) {
196+
if (element.isNullAware) {
197+
return NeverTypeImpl.instance;
198+
}
199+
return _typeProvider.dynamicType;
200+
}
201+
} else {
182202
if (expressionType.isDartCoreNull) {
183203
if (element.isNullAware) {
184204
return expressionType;
185205
}
186-
} else {
187-
var iterableType = expressionType.asInstanceOf(
188-
_typeProvider.iterableElement,
189-
);
190-
if (iterableType != null) {
191-
return iterableType.typeArguments[0];
192-
}
206+
return _typeProvider.dynamicType;
193207
}
194208
}
209+
195210
// TODO(brianwilkerson) Report this as an error.
196211
return _typeProvider.dynamicType;
197212
}
@@ -332,39 +347,69 @@ class TypedLiteralResolver {
332347
valueType: element.value.staticType);
333348
} else if (element is SpreadElement) {
334349
DartType expressionType = element.expression.staticType;
335-
bool isNull = expressionType.isDartCoreNull;
336-
if (!isNull && expressionType is InterfaceType) {
337-
if (_typeSystem.isSubtypeOf2(
338-
expressionType, _typeProvider.iterableForSetMapDisambiguation)) {
339-
InterfaceType iterableType =
340-
expressionType.asInstanceOf(_typeProvider.iterableElement);
341-
return _InferredCollectionElementTypeInformation(
342-
elementType: iterableType.typeArguments[0],
343-
keyType: null,
344-
valueType: null);
345-
} else if (_typeSystem.isSubtypeOf2(
346-
expressionType, _typeProvider.mapForSetMapDisambiguation)) {
347-
InterfaceType mapType =
348-
expressionType.asInstanceOf(_typeProvider.mapElement);
349-
List<DartType> typeArguments = mapType.typeArguments;
350-
return _InferredCollectionElementTypeInformation(
351-
elementType: null,
352-
keyType: typeArguments[0],
353-
valueType: typeArguments[1]);
354-
}
355-
} else if (expressionType.isDynamic) {
350+
351+
var iterableType = expressionType.asInstanceOf(
352+
_typeProvider.iterableElement,
353+
);
354+
if (iterableType != null) {
356355
return _InferredCollectionElementTypeInformation(
357-
elementType: expressionType,
358-
keyType: expressionType,
359-
valueType: expressionType);
360-
} else if (isNull && element.isNullAware) {
356+
elementType: iterableType.typeArguments[0],
357+
keyType: null,
358+
valueType: null,
359+
);
360+
}
361+
362+
var mapType = expressionType.asInstanceOf(
363+
_typeProvider.mapElement,
364+
);
365+
if (mapType != null) {
366+
return _InferredCollectionElementTypeInformation(
367+
elementType: null,
368+
keyType: mapType.typeArguments[0],
369+
valueType: mapType.typeArguments[1],
370+
);
371+
}
372+
373+
if (expressionType.isDynamic) {
361374
return _InferredCollectionElementTypeInformation(
375+
elementType: expressionType,
376+
keyType: expressionType,
377+
valueType: expressionType,
378+
);
379+
}
380+
381+
if (_typeSystem.isNonNullableByDefault) {
382+
if (_typeSystem.isSubtypeOf2(expressionType, NeverTypeImpl.instance)) {
383+
return _InferredCollectionElementTypeInformation(
384+
elementType: NeverTypeImpl.instance,
385+
keyType: NeverTypeImpl.instance,
386+
valueType: NeverTypeImpl.instance,
387+
);
388+
}
389+
if (_typeSystem.isSubtypeOf2(expressionType, _typeSystem.nullNone)) {
390+
if (element.isNullAware) {
391+
return _InferredCollectionElementTypeInformation(
392+
elementType: NeverTypeImpl.instance,
393+
keyType: NeverTypeImpl.instance,
394+
valueType: NeverTypeImpl.instance,
395+
);
396+
}
397+
}
398+
} else {
399+
if (expressionType.isDartCoreNull && element.isNullAware) {
400+
return _InferredCollectionElementTypeInformation(
362401
elementType: expressionType,
363402
keyType: expressionType,
364-
valueType: expressionType);
403+
valueType: expressionType,
404+
);
405+
}
365406
}
407+
366408
return _InferredCollectionElementTypeInformation(
367-
elementType: null, keyType: null, valueType: null);
409+
elementType: null,
410+
keyType: null,
411+
valueType: null,
412+
);
368413
} else {
369414
throw StateError('Unknown element type ${element.runtimeType}');
370415
}

pkg/analyzer/lib/src/error/literal_element_verifier.dart

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:analyzer/dart/ast/ast.dart';
77
import 'package:analyzer/dart/element/type.dart';
88
import 'package:analyzer/dart/element/type_provider.dart';
99
import 'package:analyzer/error/listener.dart';
10+
import 'package:analyzer/src/dart/element/type.dart';
1011
import 'package:analyzer/src/dart/element/type_system.dart';
1112
import 'package:analyzer/src/error/codes.dart';
1213

@@ -131,18 +132,33 @@ class LiteralElementVerifier {
131132
var expressionType = expression.staticType;
132133
if (expressionType.isDynamic) return;
133134

134-
if (expressionType.isDartCoreNull) {
135-
if (!isNullAware) {
135+
if (typeSystem.isNonNullableByDefault) {
136+
if (typeSystem.isSubtypeOf2(expressionType, NeverTypeImpl.instance)) {
137+
return;
138+
}
139+
if (typeSystem.isSubtypeOf2(expressionType, typeSystem.nullNone)) {
140+
if (isNullAware) {
141+
return;
142+
}
136143
errorReporter.reportErrorForNode(
137144
CompileTimeErrorCode.NOT_NULL_AWARE_NULL_SPREAD,
138145
expression,
139146
);
147+
return;
148+
}
149+
} else {
150+
if (expressionType.isDartCoreNull) {
151+
if (isNullAware) {
152+
return;
153+
}
154+
errorReporter.reportErrorForNode(
155+
CompileTimeErrorCode.NOT_NULL_AWARE_NULL_SPREAD,
156+
expression,
157+
);
158+
return;
140159
}
141-
return;
142160
}
143161

144-
expressionType = typeSystem.resolveToBound(expressionType);
145-
146162
var iterableType = expressionType.asInstanceOf(
147163
typeProvider.iterableElement,
148164
);
@@ -173,18 +189,33 @@ class LiteralElementVerifier {
173189
var expressionType = expression.staticType;
174190
if (expressionType.isDynamic) return;
175191

176-
if (expressionType.isDartCoreNull) {
177-
if (!isNullAware) {
192+
if (typeSystem.isNonNullableByDefault) {
193+
if (typeSystem.isSubtypeOf2(expressionType, NeverTypeImpl.instance)) {
194+
return;
195+
}
196+
if (typeSystem.isSubtypeOf2(expressionType, typeSystem.nullNone)) {
197+
if (isNullAware) {
198+
return;
199+
}
178200
errorReporter.reportErrorForNode(
179201
CompileTimeErrorCode.NOT_NULL_AWARE_NULL_SPREAD,
180202
expression,
181203
);
204+
return;
205+
}
206+
} else {
207+
if (expressionType.isDartCoreNull) {
208+
if (isNullAware) {
209+
return;
210+
}
211+
errorReporter.reportErrorForNode(
212+
CompileTimeErrorCode.NOT_NULL_AWARE_NULL_SPREAD,
213+
expression,
214+
);
215+
return;
182216
}
183-
return;
184217
}
185218

186-
expressionType = typeSystem.resolveToBound(expressionType);
187-
188219
var mapType = expressionType.asInstanceOf(
189220
typeProvider.mapElement,
190221
);

pkg/analyzer/test/src/dart/resolution/type_inference/list_literal_test.dart

Lines changed: 101 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -262,32 +262,82 @@ var a = [if (0 < 1) ...c else ...d];
262262
assertType(findNode.listLiteral('[if'), 'List<dynamic>');
263263
}
264264

265-
test_noContext_noTypeArgs_spread_nullAware_nullAndNotNull() async {
265+
test_noContext_noTypeArgs_spread_nullAware_null() async {
266266
await assertNoErrorsInCode('''
267-
f() async {
268-
var futureNull = Future.value(null);
267+
void f(Null a) async {
269268
// ignore:unused_local_variable
270-
var a = [1, ...?await futureNull, 2];
269+
var v = [...?a];
271270
}
272271
''');
273272
assertType(
274273
findNode.listLiteral('['),
275274
typeStringByNullability(
276-
nullable: 'List<int?>',
277-
legacy: 'List<int>',
275+
nullable: 'List<Never>',
276+
legacy: 'List<Null>',
278277
),
279278
);
280279
}
281280

282-
test_noContext_noTypeArgs_spread_nullAware_onlyNull() async {
281+
test_noContext_noTypeArgs_spread_nullAware_null2() async {
283282
await assertNoErrorsInCode('''
284-
f() async {
285-
var futureNull = Future.value(null);
283+
void f(Null a) async {
284+
// ignore:unused_local_variable
285+
var v = [1, ...?a, 2];
286+
}
287+
''');
288+
assertType(
289+
findNode.listLiteral('['),
290+
typeStringByNullability(
291+
nullable: 'List<int>',
292+
legacy: 'List<int>',
293+
),
294+
);
295+
}
296+
297+
test_noContext_noTypeArgs_spread_nullAware_typeParameter_implementsNull() async {
298+
await resolveTestCode('''
299+
void f<T extends Null>(T a) async {
300+
// ignore:unused_local_variable
301+
var v = [...?a];
302+
}
303+
''');
304+
assertType(
305+
findNode.listLiteral('['),
306+
typeStringByNullability(
307+
nullable: 'List<Never>',
308+
legacy: 'List<dynamic>',
309+
),
310+
);
311+
}
312+
313+
test_noContext_noTypeArgs_spread_typeParameter_implementsIterable() async {
314+
await resolveTestCode('''
315+
void f<T extends List<int>>(T a) {
316+
// ignore:unused_local_variable
317+
var v = [...a];
318+
}
319+
''');
320+
assertType(findNode.listLiteral('[...'), 'List<int>');
321+
}
322+
323+
test_noContext_noTypeArgs_spread_typeParameter_notImplementsIterable() async {
324+
await resolveTestCode('''
325+
void f<T extends num>(T a) {
286326
// ignore:unused_local_variable
287-
var a = [...?await futureNull];
327+
var v = [...a];
288328
}
289329
''');
290-
assertType(findNode.listLiteral('['), 'List<Null>');
330+
assertType(findNode.listLiteral('[...'), 'List<dynamic>');
331+
}
332+
333+
test_noContext_noTypeArgs_spread_typeParameter_notImplementsIterable2() async {
334+
await resolveTestCode('''
335+
void f<T extends num>(T a) {
336+
// ignore:unused_local_variable
337+
var v = [...a, 0];
338+
}
339+
''');
340+
assertType(findNode.listLiteral('[...'), 'List<dynamic>');
291341
}
292342

293343
test_noContext_typeArgs_expression_conflict() async {
@@ -360,4 +410,44 @@ main() {
360410
assertType(findNode.listLiteral('[1,'), 'List<int?>');
361411
assertType(findNode.listLiteral('[[0'), 'List<List<int?>>');
362412
}
413+
414+
test_noContext_noTypeArgs_spread_never() async {
415+
await assertNoErrorsInCode('''
416+
void f(Never a) async {
417+
// ignore:unused_local_variable
418+
var v = [...a];
419+
}
420+
''');
421+
assertType(findNode.listLiteral('['), 'List<Never>');
422+
}
423+
424+
test_noContext_noTypeArgs_spread_nullAware_never() async {
425+
await assertNoErrorsInCode('''
426+
void f(Never a) async {
427+
// ignore:unused_local_variable
428+
var v = [...a];
429+
}
430+
''');
431+
assertType(findNode.listLiteral('['), 'List<Never>');
432+
}
433+
434+
test_noContext_noTypeArgs_spread_nullAware_typeParameter_implementsNever() async {
435+
await resolveTestCode('''
436+
void f<T extends Never>(T a) async {
437+
// ignore:unused_local_variable
438+
var v = [...?a];
439+
}
440+
''');
441+
assertType(findNode.listLiteral('['), 'List<Never>');
442+
}
443+
444+
test_noContext_noTypeArgs_spread_typeParameter_implementsNever() async {
445+
await assertNoErrorsInCode('''
446+
void f<T extends Never>(T a) async {
447+
// ignore:unused_local_variable
448+
var v = [...a];
449+
}
450+
''');
451+
assertType(findNode.listLiteral('['), 'List<Never>');
452+
}
363453
}

0 commit comments

Comments
 (0)