mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 10:10:22 +00:00
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:
parent
15c1487874
commit
6eed35b60d
4 changed files with 139 additions and 23 deletions
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
}
|
Loading…
Reference in a new issue