[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:
Mike Fairhurst 2019-09-06 17:36:09 +00:00 committed by commit-bot@chromium.org
parent da235a57f8
commit e34d71feee
2 changed files with 177 additions and 73 deletions

View file

@ -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);
}

View file

@ -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) {