Analyze null-aware index expression reads.

R=brianwilkerson@google.com

Change-Id: Idcd787434eae070e87adf267a016bda2a4e5703e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/115600
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2019-09-05 00:58:46 +00:00 committed by commit-bot@chromium.org
parent 15c1487874
commit 6eed35b60d
4 changed files with 139 additions and 23 deletions

View file

@ -998,7 +998,10 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
@override
void visitIndexExpression(IndexExpression node) {
_checkForArgumentTypeNotAssignableForArgument(node.index);
_checkForNullableDereference(node.target);
if (node.leftBracket.type !=
TokenType.QUESTION_PERIOD_OPEN_SQUARE_BRACKET) {
_checkForNullableDereference(node.target);
}
super.visitIndexExpression(node);
}

View file

@ -593,15 +593,25 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<void> {
*/
@override
void visitIndexExpression(IndexExpression node) {
DartType type;
if (node.inSetterContext()) {
ExecutableElement staticMethodElement = node.staticElement;
DartType staticType = _computeArgumentType(staticMethodElement);
_recordStaticType(node, staticType);
var parameters = node.staticElement?.parameters;
if (parameters?.length == 2) {
type = parameters[1].type;
}
} else {
ExecutableElement staticMethodElement = node.staticElement;
DartType staticType = _computeStaticReturnType(staticMethodElement);
_recordStaticType(node, staticType);
type = node.staticElement?.returnType;
}
type ??= _dynamicType;
if (_nonNullableEnabled) {
if (node.leftBracket.type ==
TokenType.QUESTION_PERIOD_OPEN_SQUARE_BRACKET) {
type = _typeSystem.makeNullable(type);
}
}
_recordStaticType(node, type);
}
/**
@ -1235,22 +1245,6 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<void> {
}
}
/**
* Record that the static type of the given node is the type of the second argument to the method
* represented by the given element.
*
* @param element the element representing the method invoked by the given node
*/
DartType _computeArgumentType(ExecutableElement element) {
if (element != null) {
List<ParameterElement> parameters = element.parameters;
if (parameters != null && parameters.length == 2) {
return parameters[1].type;
}
}
return _dynamicType;
}
DartType _computeElementType(CollectionElement element) {
if (element is ForElement) {
return _computeElementType(element.body);

View file

@ -195,4 +195,90 @@ class IndexExpressionWithNnbdTest extends IndexExpressionTest {
AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
..contextFeatures = FeatureSet.forTesting(
sdkVersion: '2.3.0', additionalFeatures: [Feature.non_nullable]);
@override
bool get typeToStringWithNullability => true;
test_read_nullable() async {
await assertNoErrorsInCode(r'''
class A {
bool operator[](int index) => false;
}
main(A? a) {
a?.[0];
}
''');
var indexElement = findElement.method('[]');
var indexExpression = findNode.index('a?.[0]');
assertElement(indexExpression, indexElement);
assertAuxElement(indexExpression, null);
assertType(indexExpression, 'bool?');
}
@failingTest
test_readWrite_nullable() async {
await assertNoErrorsInCode(r'''
class A {
num operator[](int index) => 0;
void operator[]=(int index, num value) {}
}
main(A? a) {
a?.[0] += 1.2;
}
''');
var indexElement = findElement.method('[]');
var indexEqElement = findElement.method('[]=');
var numPlusElement = numElement.getMethod('+');
var indexExpression = findNode.index('a?.[0]');
assertElement(indexExpression, indexEqElement);
assertAuxElement(indexExpression, indexElement);
assertParameterElement(
indexExpression.index,
indexEqElement.parameters[0],
);
assertType(indexExpression, 'num?');
var assignment = indexExpression.parent as AssignmentExpression;
assertElement(assignment, numPlusElement);
assertType(assignment, 'num?');
assertParameterElement(
assignment.rightHandSide,
numPlusElement.parameters[0],
);
}
@failingTest
test_write_nullable() async {
await assertNoErrorsInCode(r'''
class A {
void operator[]=(int index, num value) {}
}
main(A? a) {
a?.[0] = 1.2;
}
''');
var indexEqElement = findElement.method('[]=');
var indexExpression = findNode.index('a?.[0]');
assertElement(indexExpression, indexEqElement);
assertAuxElement(indexExpression, null);
assertParameterElement(
indexExpression.index,
indexEqElement.parameters[0],
);
assertType(indexExpression, 'num?');
var assignment = indexExpression.parent as AssignmentExpression;
assertElement(assignment, null);
assertType(assignment, 'double?');
assertParameterElement(assignment.rightHandSide, null);
}
}

View file

@ -0,0 +1,33 @@
// Copyright (c) 2019, 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.
// SharedOptions=--enable-experiment=non-nullable
void main() {}
void f1(NotGeneric x) {
x[0] + 1; //# 01: ok
}
void f2(NotGeneric? x) {
x?.[0] + 1; //# 02: compile-time error
}
void f3<T extends num>(Generic<T>? x) {
x?.[0] + 1; //# 03: compile-time error
}
void f4<T extends num>(Generic<T?> x) {
x[0] + 1; //# 04: compile-time error
}
class NotGeneric {
int operator[](int index) => throw 'unreachable';
void operator[]=(int index, int value) => throw 'unreachable';
}
class Generic<T> {
T operator[](int index) => throw 'unreachable';
void operator[]=(int index, T value) => throw 'unreachable';
}