mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 02:17:14 +00:00
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:
parent
c50869a8cb
commit
d07005b4a2
|
@ -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({
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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]}) {}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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('''
|
||||
|
|
Loading…
Reference in a new issue