Skip to content

Commit d07005b

Browse files
scheglovCommit Bot
authored and
Commit Bot
committed
Issue 49106. Update the context type for index in read/write.
Bug: #49106 Change-Id: I522269360c6664ac4a9129cb71bf378c9dfca812 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/256647 Reviewed-by: Paul Berry <[email protected]> Commit-Queue: Konstantin Shcheglov <[email protected]>
1 parent c50869a commit d07005b

File tree

6 files changed

+163
-25
lines changed

6 files changed

+163
-25
lines changed

pkg/analyzer/lib/src/dart/element/extensions.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:analyzer/dart/element/element.dart';
66
import 'package:analyzer/dart/element/type.dart';
77
import 'package:analyzer/src/dart/element/element.dart';
88
import 'package:analyzer/src/generated/utilities_dart.dart';
9+
import 'package:collection/collection.dart';
910
import 'package:meta/meta_meta.dart';
1011

1112
extension ElementAnnotationExtensions on ElementAnnotation {
@@ -109,6 +110,16 @@ extension ExecutableElementExtension on ExecutableElement {
109110
}
110111
}
111112

113+
extension ExecutableElementExtensionQuestion on ExecutableElement? {
114+
DartType? get firstParameterType {
115+
final self = this;
116+
if (self is MethodElement) {
117+
return self.parameters.firstOrNull?.type;
118+
}
119+
return null;
120+
}
121+
}
122+
112123
extension ParameterElementExtensions on ParameterElement {
113124
/// Return [ParameterElement] with the specified properties replaced.
114125
ParameterElement copyWith({

pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,11 @@ class PropertyElementResolver with ScopeHelpers {
6767
);
6868
}
6969

70-
return _toIndexResult(result);
70+
return _toIndexResult(
71+
result,
72+
hasRead: hasRead,
73+
hasWrite: hasWrite,
74+
);
7175
}
7276

7377
var targetType = target.typeOrThrow;
@@ -127,7 +131,11 @@ class PropertyElementResolver with ScopeHelpers {
127131
);
128132
}
129133

130-
return _toIndexResult(result);
134+
return _toIndexResult(
135+
result,
136+
hasRead: hasRead,
137+
hasWrite: hasWrite,
138+
);
131139
}
132140

133141
PropertyElementResolverResult resolvePrefixedIdentifier({
@@ -328,20 +336,6 @@ class PropertyElementResolver with ScopeHelpers {
328336
}
329337
}
330338

331-
DartType? _computeIndexContextType({
332-
required ExecutableElement? readElement,
333-
required ExecutableElement? writeElement,
334-
}) {
335-
var method = writeElement ?? readElement;
336-
var parameters = method is MethodElement ? method.parameters : null;
337-
338-
if (parameters != null && parameters.isNotEmpty) {
339-
return parameters[0].type;
340-
}
341-
342-
return null;
343-
}
344-
345339
bool _isAccessible(ExecutableElement element) {
346340
return element.isAccessibleIn2(_definingLibrary);
347341
}
@@ -848,17 +842,22 @@ class PropertyElementResolver with ScopeHelpers {
848842
);
849843
}
850844

851-
PropertyElementResolverResult _toIndexResult(ResolutionResult result) {
845+
PropertyElementResolverResult _toIndexResult(
846+
ResolutionResult result, {
847+
required bool hasRead,
848+
required bool hasWrite,
849+
}) {
852850
var readElement = result.getter;
853851
var writeElement = result.setter;
854852

853+
final contextType = hasRead
854+
? readElement.firstParameterType
855+
: writeElement.firstParameterType;
856+
855857
return PropertyElementResolverResult(
856858
readElementRequested: readElement,
857859
writeElementRequested: writeElement,
858-
indexContextType: _computeIndexContextType(
859-
readElement: readElement,
860-
writeElement: writeElement,
861-
),
860+
indexContextType: contextType,
862861
);
863862
}
864863
}

pkg/analyzer/lib/src/generated/resolver.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -787,7 +787,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
787787
var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(node.index);
788788
checkIndexExpressionIndex(
789789
node.index,
790-
readElement: result.readElement as ExecutableElement?,
790+
readElement: hasRead ? result.readElement as ExecutableElement? : null,
791791
writeElement: result.writeElement as ExecutableElement?,
792792
whyNotPromoted: whyNotPromoted,
793793
);

pkg/analyzer/test/src/dart/resolution/index_expression_test.dart

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'package:analyzer/src/error/codes.dart';
56
import 'package:test_reflective_loader/test_reflective_loader.dart';
67

78
import 'context_collection_resolution.dart';
@@ -14,6 +15,136 @@ main() {
1415

1516
@reflectiveTest
1617
class IndexExpressionTest extends PubPackageResolutionTest {
18+
test_contextType_read() async {
19+
await assertNoErrorsInCode(r'''
20+
class A {
21+
bool operator [](int index) => false;
22+
operator []=(String index, bool value) {}
23+
}
24+
25+
void f(A a) {
26+
a[ g() ];
27+
}
28+
29+
T g<T>() => throw 0;
30+
''');
31+
32+
var node = findNode.methodInvocation('g()');
33+
assertResolvedNodeText(node, r'''
34+
MethodInvocation
35+
methodName: SimpleIdentifier
36+
token: g
37+
staticElement: self::@function::g
38+
staticType: T Function<T>()
39+
argumentList: ArgumentList
40+
leftParenthesis: (
41+
rightParenthesis: )
42+
parameter: self::@class::A::@method::[]::@parameter::index
43+
staticInvokeType: int Function()
44+
staticType: int
45+
typeArgumentTypes
46+
int
47+
''');
48+
}
49+
50+
test_contextType_readWrite_readLower() async {
51+
await assertNoErrorsInCode(r'''
52+
class A {
53+
int operator [](int index) => 0;
54+
operator []=(num index, int value) {}
55+
}
56+
57+
void f(A a) {
58+
a[ g() ]++;
59+
}
60+
61+
T g<T>() => throw 0;
62+
''');
63+
64+
var node = findNode.methodInvocation('g()');
65+
assertResolvedNodeText(node, r'''
66+
MethodInvocation
67+
methodName: SimpleIdentifier
68+
token: g
69+
staticElement: self::@function::g
70+
staticType: T Function<T>()
71+
argumentList: ArgumentList
72+
leftParenthesis: (
73+
rightParenthesis: )
74+
parameter: self::@class::A::@method::[]=::@parameter::index
75+
staticInvokeType: int Function()
76+
staticType: int
77+
typeArgumentTypes
78+
int
79+
''');
80+
}
81+
82+
test_contextType_readWrite_writeLower() async {
83+
await assertErrorsInCode(r'''
84+
class A {
85+
int operator [](num index) => 0;
86+
operator []=(int index, int value) {}
87+
}
88+
89+
void f(A a) {
90+
a[ g() ]++;
91+
}
92+
93+
T g<T>() => throw 0;
94+
''', [
95+
error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 107, 3),
96+
]);
97+
98+
var node = findNode.methodInvocation('g()');
99+
assertResolvedNodeText(node, r'''
100+
MethodInvocation
101+
methodName: SimpleIdentifier
102+
token: g
103+
staticElement: self::@function::g
104+
staticType: T Function<T>()
105+
argumentList: ArgumentList
106+
leftParenthesis: (
107+
rightParenthesis: )
108+
parameter: self::@class::A::@method::[]=::@parameter::index
109+
staticInvokeType: num Function()
110+
staticType: num
111+
typeArgumentTypes
112+
num
113+
''');
114+
}
115+
116+
test_contextType_write() async {
117+
await assertNoErrorsInCode(r'''
118+
class A {
119+
bool operator [](int index) => false;
120+
operator []=(String index, bool value) {}
121+
}
122+
123+
void f(A a) {
124+
a[ g() ] = true;
125+
}
126+
127+
T g<T>() => throw 0;
128+
''');
129+
130+
var node = findNode.methodInvocation('g()');
131+
assertResolvedNodeText(node, r'''
132+
MethodInvocation
133+
methodName: SimpleIdentifier
134+
token: g
135+
staticElement: self::@function::g
136+
staticType: T Function<T>()
137+
argumentList: ArgumentList
138+
leftParenthesis: (
139+
rightParenthesis: )
140+
parameter: self::@class::A::@method::[]=::@parameter::index
141+
staticInvokeType: String Function()
142+
staticType: String
143+
typeArgumentTypes
144+
String
145+
''');
146+
}
147+
17148
test_invalid_inDefaultValue_nullAware() async {
18149
await assertInvalidTestCode(r'''
19150
void f({a = b?[0]}) {}

pkg/nnbd_migration/test/api_test.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6586,7 +6586,6 @@ main() {
65866586
await _checkSingleFileChanges(content, expected);
65876587
}
65886588

6589-
@FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/49106')
65906589
Future<void> test_map_read_does_not_require_index_cast() async {
65916590
var content = '''
65926591
int f(Map<String, int> m, Object o) => m[o];

pkg/nnbd_migration/test/fix_builder_test.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,6 @@ _f(_C/*?*/ c) => c['foo'] += 0;
457457
changes: {findNode.simple('c['): isNullCheck});
458458
}
459459

460-
@FailingTest(reason: 'TODO(paulberry): decide if this is worth caring about')
461460
Future<void>
462461
test_assignmentTarget_indexExpression_compound_simple_check_rhs() async {
463462
await analyze('''
@@ -483,7 +482,6 @@ _f(_C<int, String> c) => c['foo'] += 1;
483482
visitAssignmentTarget(findNode.index('c['), 'int', 'int');
484483
}
485484

486-
@FailingTest(reason: 'TODO(paulberry): decide if this is worth caring about')
487485
Future<void>
488486
test_assignmentTarget_indexExpression_compound_substituted_check_rhs() async {
489487
await analyze('''

0 commit comments

Comments
 (0)