Connect g/setters which override fields (w/ implicit g/setters)
Change-Id: I4eeb507e23962fbf53f1026e14f4e2ae607aa7e6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/128105 Reviewed-by: Mike Fairhurst <mfairhurst@google.com> Commit-Queue: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
bc40858f94
commit
dbb56e5e10
@@ -1011,21 +1011,15 @@ class B extends A {
|
||||
details: ["A nullable value is assigned"]);
|
||||
}
|
||||
|
||||
@FailingTest(
|
||||
reason: "Currently crashes with: Bad state: A decorated type for void "
|
||||
"set m(int _m) should have been stored by the NodeBuilder via "
|
||||
"recordDecoratedElementType")
|
||||
test_parameter_fromOverriddenField_explicit() async {
|
||||
await buildInfoForSingleTestFile('''
|
||||
UnitInfo unit = await buildInfoForSingleTestFile('''
|
||||
class A {
|
||||
int m;
|
||||
}
|
||||
class B extends A {
|
||||
void set m(Object p) {}
|
||||
}
|
||||
void f(A a) {
|
||||
a.m = null;
|
||||
}
|
||||
void f(A a) => a.m = null;
|
||||
''', migratedContent: '''
|
||||
class A {
|
||||
int? m;
|
||||
@@ -1033,13 +1027,21 @@ class A {
|
||||
class B extends A {
|
||||
void set m(Object? p) {}
|
||||
}
|
||||
void f(A a) {
|
||||
a.m = null;
|
||||
}
|
||||
void f(A a) => a.m = null;
|
||||
''');
|
||||
// TODO(srawlins): Write expectations similar to
|
||||
// test_parameter_fromMultipleOverridden_explicit above, once the test stops
|
||||
// crashing.
|
||||
List<RegionInfo> regions = unit.fixRegions;
|
||||
expect(regions, hasLength(2));
|
||||
assertRegion(region: regions[0], offset: 15, details: [
|
||||
// TODO(srawlins): I suspect this should be removed...
|
||||
"A nullable value is assigned",
|
||||
"An explicit 'null' is assigned in the function 'f'",
|
||||
]);
|
||||
assertRegion(region: regions[1], offset: 61, details: [
|
||||
// TODO(srawlins): Improve this message to include "B.m".
|
||||
"The corresponding parameter in the overridden method is nullable"
|
||||
]);
|
||||
assertDetail(detail: regions[0].details[1], offset: 90, length: 4);
|
||||
assertDetail(detail: regions[1].details[0], offset: 12, length: 3);
|
||||
}
|
||||
|
||||
test_parameter_named_omittedInCall() async {
|
||||
|
||||
@@ -1811,65 +1811,90 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
|
||||
overriddenElement = overriddenElement.declaration;
|
||||
var overriddenClass =
|
||||
overriddenElement.enclosingElement as ClassElement;
|
||||
var decoratedOverriddenFunctionType =
|
||||
_variables.decoratedElementType(overriddenElement);
|
||||
var decoratedSupertype = _decoratedClassHierarchy
|
||||
.getDecoratedSupertype(classElement, overriddenClass);
|
||||
var substitution = decoratedSupertype.asSubstitution;
|
||||
var overriddenFunctionType =
|
||||
decoratedOverriddenFunctionType.substitute(substitution);
|
||||
if (returnType == null) {
|
||||
_unionDecoratedTypes(
|
||||
_currentFunctionType.returnType,
|
||||
overriddenFunctionType.returnType,
|
||||
ReturnTypeInheritanceOrigin(source, node));
|
||||
if (overriddenElement is PropertyAccessorElement &&
|
||||
overriddenElement.isSynthetic) {
|
||||
assert(node is MethodDeclaration);
|
||||
var method = node as MethodDeclaration;
|
||||
var decoratedOverriddenField =
|
||||
_variables.decoratedElementType(overriddenElement.variable);
|
||||
var overriddenFieldType =
|
||||
decoratedOverriddenField.substitute(substitution);
|
||||
if (method.isGetter) {
|
||||
_checkAssignment(ReturnTypeInheritanceOrigin(source, node),
|
||||
source: _currentFunctionType.returnType,
|
||||
destination: overriddenFieldType,
|
||||
hard: true);
|
||||
} else {
|
||||
assert(method.isSetter);
|
||||
DecoratedType currentParameterType =
|
||||
_currentFunctionType.positionalParameters.single;
|
||||
DecoratedType overriddenParameterType = overriddenFieldType;
|
||||
_checkAssignment(ParameterInheritanceOrigin(source, node),
|
||||
source: overriddenParameterType,
|
||||
destination: currentParameterType,
|
||||
hard: true);
|
||||
}
|
||||
} else {
|
||||
_checkAssignment(ReturnTypeInheritanceOrigin(source, node),
|
||||
source: _currentFunctionType.returnType,
|
||||
destination: overriddenFunctionType.returnType,
|
||||
hard: true);
|
||||
}
|
||||
if (parameters != null) {
|
||||
int positionalParameterCount = 0;
|
||||
for (var parameter in parameters.parameters) {
|
||||
NormalFormalParameter normalParameter;
|
||||
if (parameter is NormalFormalParameter) {
|
||||
normalParameter = parameter;
|
||||
} else {
|
||||
normalParameter =
|
||||
(parameter as DefaultFormalParameter).parameter;
|
||||
}
|
||||
DecoratedType currentParameterType;
|
||||
DecoratedType overriddenParameterType;
|
||||
if (parameter.isNamed) {
|
||||
var name = normalParameter.identifier.name;
|
||||
currentParameterType =
|
||||
_currentFunctionType.namedParameters[name];
|
||||
overriddenParameterType =
|
||||
overriddenFunctionType.namedParameters[name];
|
||||
} else {
|
||||
if (positionalParameterCount <
|
||||
_currentFunctionType.positionalParameters.length) {
|
||||
currentParameterType = _currentFunctionType
|
||||
.positionalParameters[positionalParameterCount];
|
||||
}
|
||||
if (positionalParameterCount <
|
||||
overriddenFunctionType.positionalParameters.length) {
|
||||
overriddenParameterType = overriddenFunctionType
|
||||
.positionalParameters[positionalParameterCount];
|
||||
}
|
||||
positionalParameterCount++;
|
||||
}
|
||||
if (overriddenParameterType != null) {
|
||||
var origin = ParameterInheritanceOrigin(source, node);
|
||||
if (_isUntypedParameter(normalParameter)) {
|
||||
_unionDecoratedTypes(
|
||||
overriddenParameterType, currentParameterType, origin);
|
||||
var decoratedOverriddenFunctionType =
|
||||
_variables.decoratedElementType(overriddenElement);
|
||||
var overriddenFunctionType =
|
||||
decoratedOverriddenFunctionType.substitute(substitution);
|
||||
if (returnType == null) {
|
||||
_unionDecoratedTypes(
|
||||
_currentFunctionType.returnType,
|
||||
overriddenFunctionType.returnType,
|
||||
ReturnTypeInheritanceOrigin(source, node));
|
||||
} else {
|
||||
_checkAssignment(ReturnTypeInheritanceOrigin(source, node),
|
||||
source: _currentFunctionType.returnType,
|
||||
destination: overriddenFunctionType.returnType,
|
||||
hard: true);
|
||||
}
|
||||
if (parameters != null) {
|
||||
int positionalParameterCount = 0;
|
||||
for (var parameter in parameters.parameters) {
|
||||
NormalFormalParameter normalParameter;
|
||||
if (parameter is NormalFormalParameter) {
|
||||
normalParameter = parameter;
|
||||
} else {
|
||||
_checkAssignment(origin,
|
||||
source: overriddenParameterType,
|
||||
destination: currentParameterType,
|
||||
hard: true);
|
||||
normalParameter =
|
||||
(parameter as DefaultFormalParameter).parameter;
|
||||
}
|
||||
DecoratedType currentParameterType;
|
||||
DecoratedType overriddenParameterType;
|
||||
if (parameter.isNamed) {
|
||||
var name = normalParameter.identifier.name;
|
||||
currentParameterType =
|
||||
_currentFunctionType.namedParameters[name];
|
||||
overriddenParameterType =
|
||||
overriddenFunctionType.namedParameters[name];
|
||||
} else {
|
||||
if (positionalParameterCount <
|
||||
_currentFunctionType.positionalParameters.length) {
|
||||
currentParameterType = _currentFunctionType
|
||||
.positionalParameters[positionalParameterCount];
|
||||
}
|
||||
if (positionalParameterCount <
|
||||
overriddenFunctionType.positionalParameters.length) {
|
||||
overriddenParameterType = overriddenFunctionType
|
||||
.positionalParameters[positionalParameterCount];
|
||||
}
|
||||
positionalParameterCount++;
|
||||
}
|
||||
if (overriddenParameterType != null) {
|
||||
var origin = ParameterInheritanceOrigin(source, node);
|
||||
if (_isUntypedParameter(normalParameter)) {
|
||||
_unionDecoratedTypes(
|
||||
overriddenParameterType, currentParameterType, origin);
|
||||
} else {
|
||||
_checkAssignment(origin,
|
||||
source: overriddenParameterType,
|
||||
destination: currentParameterType,
|
||||
hard: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1760,6 +1760,68 @@ void g(C<int?> c) { // (3)
|
||||
await _checkSingleFileChanges(content, expected);
|
||||
}
|
||||
|
||||
test_getter_implicit_returnType_overrides_implicit_getter() async {
|
||||
var content = '''
|
||||
class A {
|
||||
final String s = "x";
|
||||
}
|
||||
class C implements A {
|
||||
get s => false ? "y" : null;
|
||||
}
|
||||
''';
|
||||
var expected = '''
|
||||
class A {
|
||||
final String? s = "x";
|
||||
}
|
||||
class C implements A {
|
||||
get s => false ? "y" : null;
|
||||
}
|
||||
''';
|
||||
await _checkSingleFileChanges(content, expected);
|
||||
}
|
||||
|
||||
test_getter_overrides_implicit_getter() async {
|
||||
var content = '''
|
||||
class A {
|
||||
final String s = "x";
|
||||
}
|
||||
class C implements A {
|
||||
String get s => false ? "y" : null;
|
||||
}
|
||||
''';
|
||||
var expected = '''
|
||||
class A {
|
||||
final String? s = "x";
|
||||
}
|
||||
class C implements A {
|
||||
String? get s => false ? "y" : null;
|
||||
}
|
||||
''';
|
||||
await _checkSingleFileChanges(content, expected);
|
||||
}
|
||||
|
||||
test_getter_overrides_implicit_getter_with_generics() async {
|
||||
var content = '''
|
||||
class A<T> {
|
||||
final T value;
|
||||
A(this.value);
|
||||
}
|
||||
class C implements A<String/*!*/> {
|
||||
String get value => false ? "y" : null;
|
||||
}
|
||||
''';
|
||||
var expected = '''
|
||||
class A<T> {
|
||||
final T? value;
|
||||
A(this.value);
|
||||
}
|
||||
class C implements A<String/*!*/> {
|
||||
String? get value => false ? "y" : null;
|
||||
}
|
||||
''';
|
||||
await _checkSingleFileChanges(content, expected);
|
||||
}
|
||||
|
||||
test_getter_topLevel() async {
|
||||
var content = '''
|
||||
int get g => 0;
|
||||
@@ -2514,7 +2576,7 @@ main() {
|
||||
test_null_aware_setter_invocation_null_target() async {
|
||||
var content = '''
|
||||
class C {
|
||||
void set x(int value);
|
||||
void set x(int value) {}
|
||||
}
|
||||
int f(C c) => c?.x = 1;
|
||||
main() {
|
||||
@@ -2523,7 +2585,7 @@ main() {
|
||||
''';
|
||||
var expected = '''
|
||||
class C {
|
||||
void set x(int value);
|
||||
void set x(int value) {}
|
||||
}
|
||||
int? f(C? c) => c?.x = 1;
|
||||
main() {
|
||||
@@ -2536,7 +2598,7 @@ main() {
|
||||
test_null_aware_setter_invocation_null_value() async {
|
||||
var content = '''
|
||||
class C {
|
||||
void set x(int value);
|
||||
void set x(int value) {}
|
||||
}
|
||||
int f(C c) => c?.x = 1;
|
||||
main() {
|
||||
@@ -2545,7 +2607,7 @@ main() {
|
||||
''';
|
||||
var expected = '''
|
||||
class C {
|
||||
void set x(int value);
|
||||
void set x(int value) {}
|
||||
}
|
||||
int? f(C? c) => c?.x = 1;
|
||||
main() {
|
||||
@@ -2995,6 +3057,30 @@ int? recover() {
|
||||
await _checkSingleFileChanges(content, expected);
|
||||
}
|
||||
|
||||
test_setter_overrides_implicit_setter() async {
|
||||
var content = '''
|
||||
class A {
|
||||
String s = "x";
|
||||
}
|
||||
class C implements A {
|
||||
String get s => "x";
|
||||
void set s(String value) {}
|
||||
}
|
||||
f() => A().s = null;
|
||||
''';
|
||||
var expected = '''
|
||||
class A {
|
||||
String? s = "x";
|
||||
}
|
||||
class C implements A {
|
||||
String get s => "x";
|
||||
void set s(String? value) {}
|
||||
}
|
||||
f() => A().s = null;
|
||||
''';
|
||||
await _checkSingleFileChanges(content, expected);
|
||||
}
|
||||
|
||||
test_single_file_multiple_changes() async {
|
||||
var content = '''
|
||||
int f() => null;
|
||||
|
||||
@@ -2418,6 +2418,20 @@ bool f(Object x) => x is _P<Object>;
|
||||
// a more specific test with assertions.
|
||||
}
|
||||
|
||||
test_getter_overrides_implicit_getter() async {
|
||||
await analyze('''
|
||||
class A {
|
||||
final String/*1*/ s = "x";
|
||||
}
|
||||
class C implements A {
|
||||
String/*2*/ get s => false ? "y" : null;
|
||||
}
|
||||
''');
|
||||
var string1 = decoratedTypeAnnotation('String/*1*/');
|
||||
var string2 = decoratedTypeAnnotation('String/*2*/');
|
||||
assertEdge(string2.node, string1.node, hard: true);
|
||||
}
|
||||
|
||||
test_if_condition() async {
|
||||
await analyze('''
|
||||
void f(bool b) {
|
||||
@@ -5124,6 +5138,22 @@ Set<String> f() {
|
||||
hard: false);
|
||||
}
|
||||
|
||||
test_setter_overrides_implicit_setter() async {
|
||||
await analyze('''
|
||||
class A {
|
||||
String/*1*/ s = "x";
|
||||
}
|
||||
class C implements A {
|
||||
String get s => "x";
|
||||
void set s(String/*2*/ value) {}
|
||||
}
|
||||
f() => A().s = null;
|
||||
''');
|
||||
var string1 = decoratedTypeAnnotation('String/*1*/');
|
||||
var string2 = decoratedTypeAnnotation('String/*2*/');
|
||||
assertEdge(string1.node, string2.node, hard: true);
|
||||
}
|
||||
|
||||
test_simpleIdentifier_function() async {
|
||||
await analyze('''
|
||||
int f() => null;
|
||||
|
||||
Reference in New Issue
Block a user