mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 10:49:00 +00:00
[nnbd_migration] Implement if/for in map/set
Change-Id: Ie8dd7dbfdee5a52b8b0e25a6450dc77bf5bd935d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/115777 Reviewed-by: Paul Berry <paulberry@google.com> Commit-Queue: Mike Fairhurst <mfairhurst@google.com>
This commit is contained in:
parent
da235a57f8
commit
e34d71feee
2 changed files with 177 additions and 73 deletions
|
@ -135,11 +135,25 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
|
|||
DecoratedType _currentFunctionType;
|
||||
|
||||
/// The [DecoratedType] of the innermost list or set literal being visited, or
|
||||
/// `null` if the visitor is not inside any function or method.
|
||||
/// `null` if the visitor is not inside any list or set.
|
||||
///
|
||||
/// This is needed to construct the appropriate nullability constraints for
|
||||
/// ui as code list elements.
|
||||
DecoratedType _currentLiteralType;
|
||||
/// ui as code elements.
|
||||
DecoratedType _currentLiteralElementType;
|
||||
|
||||
/// The key [DecoratedType] of the innermost map literal being visited, or
|
||||
/// `null` if the visitor is not inside any map.
|
||||
///
|
||||
/// This is needed to construct the appropriate nullability constraints for
|
||||
/// ui as code elements.
|
||||
DecoratedType _currentMapKeyType;
|
||||
|
||||
/// The value [DecoratedType] of the innermost map literal being visited, or
|
||||
/// `null` if the visitor is not inside any map.
|
||||
///
|
||||
/// This is needed to construct the appropriate nullability constraints for
|
||||
/// ui as code elements.
|
||||
DecoratedType _currentMapValueType;
|
||||
|
||||
/// Information about the most recently visited binary expression whose
|
||||
/// boolean value could possibly affect nullability analysis.
|
||||
|
@ -821,24 +835,33 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
|
|||
|
||||
@override
|
||||
DecoratedType visitListLiteral(ListLiteral node) {
|
||||
final previousLiteralType = _currentLiteralType;
|
||||
final previousLiteralType = _currentLiteralElementType;
|
||||
try {
|
||||
var listType = node.staticType as InterfaceType;
|
||||
if (node.typeArguments == null) {
|
||||
_currentLiteralType = DecoratedType.forImplicitType(
|
||||
_currentLiteralElementType = DecoratedType.forImplicitType(
|
||||
_typeProvider, listType.typeArguments[0], _graph);
|
||||
} else {
|
||||
_currentLiteralType = _variables.decoratedTypeAnnotation(
|
||||
_currentLiteralElementType = _variables.decoratedTypeAnnotation(
|
||||
source, node.typeArguments.arguments[0]);
|
||||
}
|
||||
node.elements.forEach(_handleCollectionElement);
|
||||
return DecoratedType(listType, _graph.never,
|
||||
typeArguments: [_currentLiteralType]);
|
||||
typeArguments: [_currentLiteralElementType]);
|
||||
} finally {
|
||||
_currentLiteralType = previousLiteralType;
|
||||
_currentLiteralElementType = previousLiteralType;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
DecoratedType visitMapLiteralEntry(MapLiteralEntry node) {
|
||||
assert(_currentMapKeyType != null);
|
||||
assert(_currentMapValueType != null);
|
||||
_handleAssignment(node.key, destinationType: _currentMapKeyType);
|
||||
_handleAssignment(node.value, destinationType: _currentMapValueType);
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
DecoratedType visitMethodDeclaration(MethodDeclaration node) {
|
||||
_handleExecutableDeclaration(node, node.declaredElement, node.metadata,
|
||||
|
@ -1026,59 +1049,50 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
|
|||
var typeArguments = node.typeArguments?.arguments;
|
||||
|
||||
if (node.isSet) {
|
||||
DecoratedType elementType;
|
||||
if (typeArguments == null) {
|
||||
assert(setOrMapType.typeArguments.length == 1);
|
||||
elementType = DecoratedType.forImplicitType(
|
||||
_typeProvider, setOrMapType.typeArguments[0], _graph);
|
||||
} else {
|
||||
assert(typeArguments.length == 1);
|
||||
elementType =
|
||||
_variables.decoratedTypeAnnotation(source, typeArguments[0]);
|
||||
}
|
||||
for (var element in node.elements) {
|
||||
if (element is Expression) {
|
||||
_handleAssignment(element, destinationType: elementType);
|
||||
final previousLiteralType = _currentLiteralElementType;
|
||||
try {
|
||||
if (typeArguments == null) {
|
||||
assert(setOrMapType.typeArguments.length == 1);
|
||||
_currentLiteralElementType = DecoratedType.forImplicitType(
|
||||
_typeProvider, setOrMapType.typeArguments[0], _graph);
|
||||
} else {
|
||||
// Handle spread and control flow elements.
|
||||
element.accept(this);
|
||||
// TODO(mfairhurst)
|
||||
_unimplemented(node, 'Spread or control flow element');
|
||||
assert(typeArguments.length == 1);
|
||||
_currentLiteralElementType =
|
||||
_variables.decoratedTypeAnnotation(source, typeArguments[0]);
|
||||
}
|
||||
node.elements.forEach(_handleCollectionElement);
|
||||
return DecoratedType(setOrMapType, _graph.never,
|
||||
typeArguments: [_currentLiteralElementType]);
|
||||
} finally {
|
||||
_currentLiteralElementType = previousLiteralType;
|
||||
}
|
||||
return DecoratedType(setOrMapType, _graph.never,
|
||||
typeArguments: [elementType]);
|
||||
} else {
|
||||
assert(node.isMap);
|
||||
DecoratedType keyType;
|
||||
DecoratedType valueType;
|
||||
|
||||
if (typeArguments == null) {
|
||||
assert(setOrMapType.typeArguments.length == 2);
|
||||
keyType = DecoratedType.forImplicitType(
|
||||
_typeProvider, setOrMapType.typeArguments[0], _graph);
|
||||
valueType = DecoratedType.forImplicitType(
|
||||
_typeProvider, setOrMapType.typeArguments[1], _graph);
|
||||
} else {
|
||||
assert(typeArguments.length == 2);
|
||||
keyType = _variables.decoratedTypeAnnotation(source, typeArguments[0]);
|
||||
valueType =
|
||||
_variables.decoratedTypeAnnotation(source, typeArguments[1]);
|
||||
}
|
||||
|
||||
for (var element in node.elements) {
|
||||
if (element is MapLiteralEntry) {
|
||||
_handleAssignment(element.key, destinationType: keyType);
|
||||
_handleAssignment(element.value, destinationType: valueType);
|
||||
final previousKeyType = _currentMapKeyType;
|
||||
final previousValueType = _currentMapValueType;
|
||||
try {
|
||||
if (typeArguments == null) {
|
||||
assert(setOrMapType.typeArguments.length == 2);
|
||||
_currentMapKeyType = DecoratedType.forImplicitType(
|
||||
_typeProvider, setOrMapType.typeArguments[0], _graph);
|
||||
_currentMapValueType = DecoratedType.forImplicitType(
|
||||
_typeProvider, setOrMapType.typeArguments[1], _graph);
|
||||
} else {
|
||||
// Handle spread and control flow elements.
|
||||
element.accept(this);
|
||||
// TODO(mfairhurst)
|
||||
_unimplemented(node, 'Spread or control flow element');
|
||||
assert(typeArguments.length == 2);
|
||||
_currentMapKeyType =
|
||||
_variables.decoratedTypeAnnotation(source, typeArguments[0]);
|
||||
_currentMapValueType =
|
||||
_variables.decoratedTypeAnnotation(source, typeArguments[1]);
|
||||
}
|
||||
|
||||
node.elements.forEach(_handleCollectionElement);
|
||||
return DecoratedType(setOrMapType, _graph.never,
|
||||
typeArguments: [_currentMapKeyType, _currentMapValueType]);
|
||||
} finally {
|
||||
_currentMapKeyType = previousKeyType;
|
||||
_currentMapValueType = previousValueType;
|
||||
}
|
||||
return DecoratedType(setOrMapType, _graph.never,
|
||||
typeArguments: [keyType, valueType]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1507,8 +1521,9 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
|
|||
|
||||
DecoratedType _handleCollectionElement(CollectionElement element) {
|
||||
if (element is Expression) {
|
||||
assert(_currentLiteralType != null);
|
||||
return _handleAssignment(element, destinationType: _currentLiteralType);
|
||||
assert(_currentLiteralElementType != null);
|
||||
return _handleAssignment(element,
|
||||
destinationType: _currentLiteralElementType);
|
||||
} else {
|
||||
return element.accept(this);
|
||||
}
|
||||
|
|
|
@ -1536,6 +1536,50 @@ void f(List<int> ints) {
|
|||
hard: false);
|
||||
}
|
||||
|
||||
test_for_element_map() async {
|
||||
await analyze('''
|
||||
void f(List<String> strs, List<int> ints) {
|
||||
<String, int>{
|
||||
for (String s in strs)
|
||||
for (int i in ints)
|
||||
s: i,
|
||||
};
|
||||
}
|
||||
''');
|
||||
|
||||
assertNullCheck(
|
||||
checkExpression('strs)\n'),
|
||||
assertEdge(decoratedTypeAnnotation('List<String> strs').node, never,
|
||||
hard: true));
|
||||
assertNullCheck(
|
||||
checkExpression('ints)\n'),
|
||||
assertEdge(decoratedTypeAnnotation('List<int> ints').node, never,
|
||||
hard: false));
|
||||
|
||||
var keyTypeNode = decoratedTypeAnnotation('String, int>{').node;
|
||||
var valueTypeNode = decoratedTypeAnnotation('int>{').node;
|
||||
assertEdge(decoratedTypeAnnotation('String s').node, keyTypeNode,
|
||||
hard: false);
|
||||
assertEdge(decoratedTypeAnnotation('int i').node, valueTypeNode,
|
||||
hard: false);
|
||||
}
|
||||
|
||||
test_for_element_set() async {
|
||||
await analyze('''
|
||||
void f(List<int> ints) {
|
||||
<int>{for(int i in ints) i};
|
||||
}
|
||||
''');
|
||||
|
||||
assertNullCheck(
|
||||
checkExpression('ints) i'),
|
||||
assertEdge(decoratedTypeAnnotation('List<int> ints').node, never,
|
||||
hard: true));
|
||||
assertEdge(decoratedTypeAnnotation('int i').node,
|
||||
decoratedTypeAnnotation('int>{').node,
|
||||
hard: false);
|
||||
}
|
||||
|
||||
test_for_with_declaration() async {
|
||||
await analyze('''
|
||||
main() {
|
||||
|
@ -1903,25 +1947,6 @@ void f(bool b, int i, int j) {
|
|||
assertEdge(decoratedTypeAnnotation('int j').node, never, hard: true);
|
||||
}
|
||||
|
||||
test_if_element() async {
|
||||
await analyze('''
|
||||
void f(bool b) {
|
||||
int i1 = null;
|
||||
int i2 = null;
|
||||
<int>[if (b) i1 else i2];
|
||||
}
|
||||
''');
|
||||
|
||||
assertNullCheck(checkExpression('b) i1'),
|
||||
assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true));
|
||||
assertEdge(decoratedTypeAnnotation('int i1').node,
|
||||
decoratedTypeAnnotation('int>[').node,
|
||||
hard: false);
|
||||
assertEdge(decoratedTypeAnnotation('int i2').node,
|
||||
decoratedTypeAnnotation('int>[').node,
|
||||
hard: false);
|
||||
}
|
||||
|
||||
@failingTest
|
||||
test_if_element_guard_equals_null() async {
|
||||
// failing because of an unimplemented exception in conditional modification
|
||||
|
@ -1946,6 +1971,51 @@ dynamic f(int i, int j, int k) {
|
|||
expect(discard.pureCondition, true);
|
||||
}
|
||||
|
||||
test_if_element_list() async {
|
||||
await analyze('''
|
||||
void f(bool b) {
|
||||
int i1 = null;
|
||||
int i2 = null;
|
||||
<int>[if (b) i1 else i2];
|
||||
}
|
||||
''');
|
||||
|
||||
assertNullCheck(checkExpression('b) i1'),
|
||||
assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true));
|
||||
assertEdge(decoratedTypeAnnotation('int i1').node,
|
||||
decoratedTypeAnnotation('int>[').node,
|
||||
hard: false);
|
||||
assertEdge(decoratedTypeAnnotation('int i2').node,
|
||||
decoratedTypeAnnotation('int>[').node,
|
||||
hard: false);
|
||||
}
|
||||
|
||||
test_if_element_map() async {
|
||||
await analyze('''
|
||||
void f(bool b) {
|
||||
int i1 = null;
|
||||
int i2 = null;
|
||||
String s1 = null;
|
||||
String s2 = null;
|
||||
<String, int>{if (b) s1: i1 else s2: i2};
|
||||
}
|
||||
''');
|
||||
|
||||
assertNullCheck(checkExpression('b) s1'),
|
||||
assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true));
|
||||
|
||||
var keyTypeNode = decoratedTypeAnnotation('String, int>{').node;
|
||||
var valueTypeNode = decoratedTypeAnnotation('int>{').node;
|
||||
assertEdge(decoratedTypeAnnotation('String s1').node, keyTypeNode,
|
||||
hard: false);
|
||||
assertEdge(decoratedTypeAnnotation('String s2').node, keyTypeNode,
|
||||
hard: false);
|
||||
assertEdge(decoratedTypeAnnotation('int i1').node, valueTypeNode,
|
||||
hard: false);
|
||||
assertEdge(decoratedTypeAnnotation('int i2').node, valueTypeNode,
|
||||
hard: false);
|
||||
}
|
||||
|
||||
test_if_element_nested() async {
|
||||
await analyze('''
|
||||
void f(bool b1, bool b2) {
|
||||
|
@ -1973,6 +2043,25 @@ void f(bool b1, bool b2) {
|
|||
hard: false);
|
||||
}
|
||||
|
||||
test_if_element_set() async {
|
||||
await analyze('''
|
||||
void f(bool b) {
|
||||
int i1 = null;
|
||||
int i2 = null;
|
||||
<int>{if (b) i1 else i2};
|
||||
}
|
||||
''');
|
||||
|
||||
assertNullCheck(checkExpression('b) i1'),
|
||||
assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true));
|
||||
assertEdge(decoratedTypeAnnotation('int i1').node,
|
||||
decoratedTypeAnnotation('int>{').node,
|
||||
hard: false);
|
||||
assertEdge(decoratedTypeAnnotation('int i2').node,
|
||||
decoratedTypeAnnotation('int>{').node,
|
||||
hard: false);
|
||||
}
|
||||
|
||||
test_if_guard_equals_null() async {
|
||||
await analyze('''
|
||||
int f(int i, int j, int k) {
|
||||
|
|
Loading…
Reference in a new issue