Issue 49106. Update the context type for index in read/write.

Bug: https://github.com/dart-lang/sdk/issues/49106
Change-Id: I522269360c6664ac4a9129cb71bf378c9dfca812
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/256647
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2022-09-01 16:24:52 +00:00 committed by Commit Bot
parent c50869a8cb
commit d07005b4a2
6 changed files with 163 additions and 25 deletions

View file

@ -6,6 +6,7 @@ import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
import 'package:collection/collection.dart';
import 'package:meta/meta_meta.dart';
extension ElementAnnotationExtensions on ElementAnnotation {
@ -109,6 +110,16 @@ extension ExecutableElementExtension on ExecutableElement {
}
}
extension ExecutableElementExtensionQuestion on ExecutableElement? {
DartType? get firstParameterType {
final self = this;
if (self is MethodElement) {
return self.parameters.firstOrNull?.type;
}
return null;
}
}
extension ParameterElementExtensions on ParameterElement {
/// Return [ParameterElement] with the specified properties replaced.
ParameterElement copyWith({

View file

@ -67,7 +67,11 @@ class PropertyElementResolver with ScopeHelpers {
);
}
return _toIndexResult(result);
return _toIndexResult(
result,
hasRead: hasRead,
hasWrite: hasWrite,
);
}
var targetType = target.typeOrThrow;
@ -127,7 +131,11 @@ class PropertyElementResolver with ScopeHelpers {
);
}
return _toIndexResult(result);
return _toIndexResult(
result,
hasRead: hasRead,
hasWrite: hasWrite,
);
}
PropertyElementResolverResult resolvePrefixedIdentifier({
@ -328,20 +336,6 @@ class PropertyElementResolver with ScopeHelpers {
}
}
DartType? _computeIndexContextType({
required ExecutableElement? readElement,
required ExecutableElement? writeElement,
}) {
var method = writeElement ?? readElement;
var parameters = method is MethodElement ? method.parameters : null;
if (parameters != null && parameters.isNotEmpty) {
return parameters[0].type;
}
return null;
}
bool _isAccessible(ExecutableElement element) {
return element.isAccessibleIn2(_definingLibrary);
}
@ -848,17 +842,22 @@ class PropertyElementResolver with ScopeHelpers {
);
}
PropertyElementResolverResult _toIndexResult(ResolutionResult result) {
PropertyElementResolverResult _toIndexResult(
ResolutionResult result, {
required bool hasRead,
required bool hasWrite,
}) {
var readElement = result.getter;
var writeElement = result.setter;
final contextType = hasRead
? readElement.firstParameterType
: writeElement.firstParameterType;
return PropertyElementResolverResult(
readElementRequested: readElement,
writeElementRequested: writeElement,
indexContextType: _computeIndexContextType(
readElement: readElement,
writeElement: writeElement,
),
indexContextType: contextType,
);
}
}

View file

@ -787,7 +787,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(node.index);
checkIndexExpressionIndex(
node.index,
readElement: result.readElement as ExecutableElement?,
readElement: hasRead ? result.readElement as ExecutableElement? : null,
writeElement: result.writeElement as ExecutableElement?,
whyNotPromoted: whyNotPromoted,
);

View file

@ -2,6 +2,7 @@
// 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.
import 'package:analyzer/src/error/codes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'context_collection_resolution.dart';
@ -14,6 +15,136 @@ main() {
@reflectiveTest
class IndexExpressionTest extends PubPackageResolutionTest {
test_contextType_read() async {
await assertNoErrorsInCode(r'''
class A {
bool operator [](int index) => false;
operator []=(String index, bool value) {}
}
void f(A a) {
a[ g() ];
}
T g<T>() => throw 0;
''');
var node = findNode.methodInvocation('g()');
assertResolvedNodeText(node, r'''
MethodInvocation
methodName: SimpleIdentifier
token: g
staticElement: self::@function::g
staticType: T Function<T>()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
parameter: self::@class::A::@method::[]::@parameter::index
staticInvokeType: int Function()
staticType: int
typeArgumentTypes
int
''');
}
test_contextType_readWrite_readLower() async {
await assertNoErrorsInCode(r'''
class A {
int operator [](int index) => 0;
operator []=(num index, int value) {}
}
void f(A a) {
a[ g() ]++;
}
T g<T>() => throw 0;
''');
var node = findNode.methodInvocation('g()');
assertResolvedNodeText(node, r'''
MethodInvocation
methodName: SimpleIdentifier
token: g
staticElement: self::@function::g
staticType: T Function<T>()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
parameter: self::@class::A::@method::[]=::@parameter::index
staticInvokeType: int Function()
staticType: int
typeArgumentTypes
int
''');
}
test_contextType_readWrite_writeLower() async {
await assertErrorsInCode(r'''
class A {
int operator [](num index) => 0;
operator []=(int index, int value) {}
}
void f(A a) {
a[ g() ]++;
}
T g<T>() => throw 0;
''', [
error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 107, 3),
]);
var node = findNode.methodInvocation('g()');
assertResolvedNodeText(node, r'''
MethodInvocation
methodName: SimpleIdentifier
token: g
staticElement: self::@function::g
staticType: T Function<T>()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
parameter: self::@class::A::@method::[]=::@parameter::index
staticInvokeType: num Function()
staticType: num
typeArgumentTypes
num
''');
}
test_contextType_write() async {
await assertNoErrorsInCode(r'''
class A {
bool operator [](int index) => false;
operator []=(String index, bool value) {}
}
void f(A a) {
a[ g() ] = true;
}
T g<T>() => throw 0;
''');
var node = findNode.methodInvocation('g()');
assertResolvedNodeText(node, r'''
MethodInvocation
methodName: SimpleIdentifier
token: g
staticElement: self::@function::g
staticType: T Function<T>()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
parameter: self::@class::A::@method::[]=::@parameter::index
staticInvokeType: String Function()
staticType: String
typeArgumentTypes
String
''');
}
test_invalid_inDefaultValue_nullAware() async {
await assertInvalidTestCode(r'''
void f({a = b?[0]}) {}

View file

@ -6586,7 +6586,6 @@ main() {
await _checkSingleFileChanges(content, expected);
}
@FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/49106')
Future<void> test_map_read_does_not_require_index_cast() async {
var content = '''
int f(Map<String, int> m, Object o) => m[o];

View file

@ -457,7 +457,6 @@ _f(_C/*?*/ c) => c['foo'] += 0;
changes: {findNode.simple('c['): isNullCheck});
}
@FailingTest(reason: 'TODO(paulberry): decide if this is worth caring about')
Future<void>
test_assignmentTarget_indexExpression_compound_simple_check_rhs() async {
await analyze('''
@ -483,7 +482,6 @@ _f(_C<int, String> c) => c['foo'] += 1;
visitAssignmentTarget(findNode.index('c['), 'int', 'int');
}
@FailingTest(reason: 'TODO(paulberry): decide if this is worth caring about')
Future<void>
test_assignmentTarget_indexExpression_compound_substituted_check_rhs() async {
await analyze('''