Support for resolving RecordType fields through type parameters bound with it.
Bug: https://github.com/dart-lang/sdk/issues/50006
Change-Id: I8b9fcff2c7bb560a764a336c8dceccbfdd06cdaa
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/260420
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/dart/resolver/lexical_lookup.dart b/pkg/analyzer/lib/src/dart/resolver/lexical_lookup.dart
index 5cd5b33..201311c 100644
--- a/pkg/analyzer/lib/src/dart/resolver/lexical_lookup.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/lexical_lookup.dart
@@ -62,9 +62,13 @@
/// The [FunctionType] referenced with `call`.
final FunctionType? callFunctionType;
+ /// The field referenced in a [RecordType].
+ final RecordTypeField? recordField;
+
LexicalLookupResult({
this.requested,
this.recovery,
this.callFunctionType,
+ this.recordField,
});
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/prefixed_identifier_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/prefixed_identifier_resolver.dart
index 60e06a3..7bdbdd1 100644
--- a/pkg/analyzer/lib/src/dart/resolver/prefixed_identifier_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/prefixed_identifier_resolver.dart
@@ -34,14 +34,18 @@
if (prefixElement is! PrefixElement) {
final prefixType = node.prefix.staticType;
// TODO(scheglov) It would be nice to rewrite all such cases.
- if (prefixType is RecordType) {
- final propertyAccess = PropertyAccessImpl(
- node.prefix,
- node.period,
- node.identifier,
- );
- NodeReplacer.replace(node, propertyAccess);
- return propertyAccess;
+ if (prefixType != null) {
+ final prefixTypeResolved =
+ _resolver.typeSystem.resolveToBound(prefixType);
+ if (prefixTypeResolved is RecordType) {
+ final propertyAccess = PropertyAccessImpl(
+ node.prefix,
+ node.period,
+ node.identifier,
+ );
+ NodeReplacer.replace(node, propertyAccess);
+ return propertyAccess;
+ }
}
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
index 2da4373..f7e53ac 100644
--- a/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
@@ -238,12 +238,21 @@
if (hasRead) {
var readLookup = LexicalLookup.resolveGetter(scopeLookupResult) ??
_resolver.thisLookupGetter(node);
+
final callFunctionType = readLookup?.callFunctionType;
if (callFunctionType != null) {
return PropertyElementResolverResult(
functionTypeCallType: callFunctionType,
);
}
+
+ final recordField = readLookup?.recordField;
+ if (recordField != null) {
+ return PropertyElementResolverResult(
+ recordField: recordField,
+ );
+ }
+
readElementRequested = _resolver.toLegacyElement(readLookup?.requested);
if (readElementRequested is PropertyAccessorElement &&
!readElementRequested.isStatic) {
@@ -450,24 +459,6 @@
return PropertyElementResolverResult();
}
- if (targetType is RecordType) {
- final name = propertyName.name;
- final field = targetType.fieldByName(name);
- if (field != null) {
- if (hasWrite) {
- AssignmentVerifier(_definingLibrary, errorReporter).verify(
- node: propertyName,
- requested: null,
- recovery: null,
- receiverType: targetType,
- );
- }
- return PropertyElementResolverResult(
- recordField: field,
- );
- }
- }
-
var result = _resolver.typePropertyResolver.resolve(
receiver: target,
receiverType: targetType,
@@ -511,6 +502,7 @@
readElementRecovery: result.setter,
writeElementRequested: result.setter,
writeElementRecovery: result.getter,
+ recordField: result.recordField,
);
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/resolution_result.dart b/pkg/analyzer/lib/src/dart/resolver/resolution_result.dart
index 52653d4..7c7cdc7 100644
--- a/pkg/analyzer/lib/src/dart/resolver/resolution_result.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/resolution_result.dart
@@ -47,6 +47,9 @@
/// The [FunctionType] referenced with `call`.
final FunctionType? callFunctionType;
+ /// The field referenced in a [RecordType].
+ final RecordTypeField? recordField;
+
/// Initialize a newly created result to represent resolving a single
/// reading and / or writing result.
ResolutionResult({
@@ -55,6 +58,7 @@
this.setter,
this.needsSetterError = true,
this.callFunctionType,
+ this.recordField,
}) : state = _ResolutionResultState.single;
/// Initialize a newly created result with no elements and the given [state].
@@ -63,7 +67,8 @@
needsGetterError = true,
setter = null,
needsSetterError = true,
- callFunctionType = null;
+ callFunctionType = null,
+ recordField = null;
/// Return `true` if this result represents the case where multiple ambiguous
/// elements were found.
diff --git a/pkg/analyzer/lib/src/dart/resolver/simple_identifier_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/simple_identifier_resolver.dart
index 5df5896..2136d81 100644
--- a/pkg/analyzer/lib/src/dart/resolver/simple_identifier_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/simple_identifier_resolver.dart
@@ -9,7 +9,6 @@
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/element.dart';
-import 'package:analyzer/src/dart/element/extensions.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
@@ -38,18 +37,6 @@
return;
}
- final thisType = _resolver.thisType;
- if (thisType is RecordType) {
- final wasResolved = _resolveOfThisRecordType(
- node: node,
- recordType: thisType,
- contextType: contextType,
- );
- if (wasResolved) {
- return;
- }
- }
-
_resolver.checkUnreachableNode(node);
_resolver.checkReadOfNotAssignedLocalVariable(node, node.staticElement);
@@ -194,6 +181,14 @@
return;
}
+ final recordField = result.recordField;
+ if (recordField != null) {
+ _inferenceHelper.recordStaticType(node, recordField.type,
+ contextType: contextType);
+ _currentAlreadyResolved = true;
+ return;
+ }
+
var element = hasRead ? result.readElement : result.writeElement;
var enclosingClass = _resolver.enclosingClass;
@@ -294,22 +289,6 @@
contextType: contextType);
}
- /// This can happen only inside an extension.
- bool _resolveOfThisRecordType({
- required SimpleIdentifierImpl node,
- required RecordType recordType,
- required DartType? contextType,
- }) {
- final field = recordType.fieldByName(node.name);
- if (field != null) {
- _inferenceHelper.recordStaticType(node, field.type,
- contextType: contextType);
- return true;
- } else {
- return false;
- }
- }
-
/// TODO(scheglov) this is duplicate
void _setExtensionIdentifierType(IdentifierImpl node) {
if (node is SimpleIdentifierImpl && node.inDeclarationContext()) {
diff --git a/pkg/analyzer/lib/src/dart/resolver/this_lookup.dart b/pkg/analyzer/lib/src/dart/resolver/this_lookup.dart
index 4102c69..8b79e08 100644
--- a/pkg/analyzer/lib/src/dart/resolver/this_lookup.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/this_lookup.dart
@@ -40,6 +40,13 @@
);
}
+ final recordField = propertyResult.recordField;
+ if (recordField != null) {
+ return LexicalLookupResult(
+ recordField: recordField,
+ );
+ }
+
var getterElement = propertyResult.getter;
if (getterElement != null) {
return LexicalLookupResult(requested: getterElement);
diff --git a/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
index adf83b5..f220df5 100644
--- a/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
@@ -8,6 +8,7 @@
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/diagnostic/diagnostic.dart';
import 'package:analyzer/src/dart/element/element.dart';
+import 'package:analyzer/src/dart/element/extensions.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
@@ -203,6 +204,13 @@
}
if (receiverTypeResolved is RecordType) {
+ final field = receiverTypeResolved.fieldByName(name);
+ if (field != null) {
+ return ResolutionResult(
+ recordField: field,
+ needsGetterError: false,
+ );
+ }
_needsGetterError = true;
_needsSetterError = true;
}
diff --git a/pkg/analyzer/test/src/dart/resolution/property_access_test.dart b/pkg/analyzer/test/src/dart/resolution/property_access_test.dart
index b28c322..bb6816e 100644
--- a/pkg/analyzer/test/src/dart/resolution/property_access_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/property_access_test.dart
@@ -399,6 +399,29 @@
''');
}
+ test_ofRecordType_namedField_ofTypeParameter() async {
+ await assertNoErrorsInCode(r'''
+void f<T extends ({int foo})>(T r) {
+ r.foo;
+}
+''');
+
+ final node = findNode.propertyAccess(r'foo;');
+ assertResolvedNodeText(node, r'''
+PropertyAccess
+ target: SimpleIdentifier
+ token: r
+ staticElement: self::@function::f::@parameter::r
+ staticType: T
+ operator: .
+ propertyName: SimpleIdentifier
+ token: foo
+ staticElement: <null>
+ staticType: int
+ staticType: int
+''');
+ }
+
test_ofRecordType_Object_hashCode() async {
await assertNoErrorsInCode('''
void f(({int foo}) r) {
@@ -622,6 +645,29 @@
''');
}
+ test_ofRecordType_positionalField_ofTypeParameter() async {
+ await assertNoErrorsInCode(r'''
+void f<T extends (int, String)>(T r) {
+ r.$0;
+}
+''');
+
+ final node = findNode.propertyAccess(r'$0;');
+ assertResolvedNodeText(node, r'''
+PropertyAccess
+ target: SimpleIdentifier
+ token: r
+ staticElement: self::@function::f::@parameter::r
+ staticType: T
+ operator: .
+ propertyName: SimpleIdentifier
+ token: $0
+ staticElement: <null>
+ staticType: int
+ staticType: int
+''');
+ }
+
test_ofRecordType_unresolved() async {
await assertErrorsInCode('''
void f(({int foo}) r) {
diff --git a/pkg/analyzer/test/src/dart/resolution/simple_identifier_test.dart b/pkg/analyzer/test/src/dart/resolution/simple_identifier_test.dart
index e899108..85fdc46 100644
--- a/pkg/analyzer/test/src/dart/resolution/simple_identifier_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/simple_identifier_test.dart
@@ -116,6 +116,42 @@
''');
}
+ test_inExtension_onRecordType_fromTypeParameterBound_named() async {
+ await assertNoErrorsInCode('''
+extension E<T extends ({int foo})> on T {
+ void f() {
+ foo;
+ }
+}
+''');
+
+ final node = findNode.simple('foo;');
+ assertResolvedNodeText(node, r'''
+SimpleIdentifier
+ token: foo
+ staticElement: <null>
+ staticType: int
+''');
+ }
+
+ test_inExtension_onRecordType_fromTypeParameterBound_positional() async {
+ await assertNoErrorsInCode(r'''
+extension E<T extends (int, String)> on T {
+ void f() {
+ $0;
+ }
+}
+''');
+
+ final node = findNode.simple(r'$0;');
+ assertResolvedNodeText(node, r'''
+SimpleIdentifier
+ token: $0
+ staticElement: <null>
+ staticType: int
+''');
+ }
+
test_inExtension_onRecordType_named() async {
await assertNoErrorsInCode('''
extension E on ({int foo}) {