Resolve RecordPattern(s).

Change-Id: I229efb591cbb2d6cfe77b38155526f881fee7dbe
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/264891
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2022-10-21 09:10:27 +00:00 committed by Commit Queue
parent b03b9c3712
commit 6c60f506c8
5 changed files with 549 additions and 21 deletions

View file

@ -10543,7 +10543,7 @@ class RecordPatternFieldNameImpl extends AstNodeImpl
/// '(' [RecordPatternField] (',' [RecordPatternField])* ')'
@experimental
class RecordPatternImpl extends DartPatternImpl implements RecordPattern {
final NodeListImpl<RecordPatternField> _fields = NodeListImpl._();
final NodeListImpl<RecordPatternFieldImpl> _fields = NodeListImpl._();
@override
final Token leftParenthesis;
@ -10553,7 +10553,7 @@ class RecordPatternImpl extends DartPatternImpl implements RecordPattern {
RecordPatternImpl({
required this.leftParenthesis,
required List<RecordPatternField> fields,
required List<RecordPatternFieldImpl> fields,
required this.rightParenthesis,
}) {
_fields._initialize(this, fields);
@ -10566,7 +10566,7 @@ class RecordPatternImpl extends DartPatternImpl implements RecordPattern {
Token get endToken => rightParenthesis;
@override
NodeList<RecordPatternField> get fields => _fields;
NodeList<RecordPatternFieldImpl> get fields => _fields;
@override
ChildEntities get _childEntities => super._childEntities
@ -10587,7 +10587,12 @@ class RecordPatternImpl extends DartPatternImpl implements RecordPattern {
DartType matchedType,
Map<PromotableElement, VariableTypeInfo<AstNode, DartType>> typeInfos,
MatchContext<AstNode, Expression> context) {
// TODO(scheglov) https://github.com/dart-lang/sdk/issues/50066
resolverVisitor.recordPatternResolver.resolve(
node: this,
matchedType: matchedType,
typeInfos: typeInfos,
context: context,
);
}
@override

View file

@ -0,0 +1,73 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// 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:_fe_analyzer_shared/src/type_inference/type_analysis_result.dart';
import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/extensions.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/resolver.dart';
class RecordPatternResolver {
final ResolverVisitor resolverVisitor;
int _positionalFieldIndex = 0;
RecordPatternResolver(this.resolverVisitor);
TypeProviderImpl get _typeProvider => resolverVisitor.typeProvider;
void resolve({
required RecordPatternImpl node,
required DartType matchedType,
required Map<PromotableElement, VariableTypeInfo<AstNode, DartType>>
typeInfos,
required MatchContext<AstNode, Expression> context,
}) {
_positionalFieldIndex = 0;
for (var field in node.fields) {
var fieldType = _resolveFieldType(matchedType, field);
field.pattern
.resolvePattern(resolverVisitor, fieldType, typeInfos, context);
}
}
DartType _resolveFieldType(
DartType matchedType,
RecordPatternFieldImpl field,
) {
if (matchedType is RecordType) {
var fieldNameNode = field.fieldName;
if (fieldNameNode != null) {
var nameToken = fieldNameNode.name;
nameToken ??= field.pattern.variablePattern?.name;
if (nameToken == null) {
resolverVisitor.errorReporter.reportErrorForNode(
CompileTimeErrorCode.MISSING_EXTRACTOR_PATTERN_GETTER_NAME,
field,
);
return _typeProvider.dynamicType;
}
var fieldName = nameToken.lexeme;
var recordField = matchedType.fieldByName(fieldName);
if (recordField != null) {
return recordField.type;
}
} else {
if (_positionalFieldIndex < matchedType.positionalFields.length) {
return matchedType.positionalFields[_positionalFieldIndex++].type;
}
}
} else if (matchedType.isDynamic) {
return _typeProvider.dynamicType;
}
return resolverVisitor.typeSystem.objectQuestion;
}
}

View file

@ -4634,11 +4634,14 @@ class AstBuilder extends StackListener {
void handleRecordPattern(Token token, int count) {
debugEvent("RecordPattern");
var fields = popTypedList2<RecordPatternField>(count);
push(RecordPatternImpl(
var fields = popTypedList2<RecordPatternFieldImpl>(count);
push(
RecordPatternImpl(
leftParenthesis: token,
fields: fields,
rightParenthesis: token.endGroup!));
rightParenthesis: token.endGroup!,
),
);
}
@override

View file

@ -57,6 +57,7 @@ import 'package:analyzer/src/dart/resolver/prefix_expression_resolver.dart';
import 'package:analyzer/src/dart/resolver/prefixed_identifier_resolver.dart';
import 'package:analyzer/src/dart/resolver/property_element_resolver.dart';
import 'package:analyzer/src/dart/resolver/record_literal_resolver.dart';
import 'package:analyzer/src/dart/resolver/record_pattern_resolver.dart';
import 'package:analyzer/src/dart/resolver/scope.dart';
import 'package:analyzer/src/dart/resolver/shared_type_analyzer.dart';
import 'package:analyzer/src/dart/resolver/simple_identifier_resolver.dart';
@ -281,6 +282,9 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
late final ExtractorPatternResolver extractorPatternResolver =
ExtractorPatternResolver(this);
late final RecordPatternResolver recordPatternResolver =
RecordPatternResolver(this);
final bool genericMetadataIsEnabled;
/// Stack for obtaining rewritten expressions. Prior to visiting an

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,7 +15,7 @@ main() {
@reflectiveTest
class RecordPatternResolutionTest extends PatternsResolutionTest {
test_fields_empty() async {
test_dynamicType_empty() async {
await assertNoErrorsInCode(r'''
void f(x) {
switch (x) {
@ -24,42 +25,171 @@ void f(x) {
}
''');
final node = findNode.switchPatternCase('case').pattern;
assertParsedNodeText(node, r'''
assertResolvedNodeText(node, r'''
RecordPattern
leftParenthesis: (
rightParenthesis: )
''');
}
test_fields_pair() async {
test_dynamicType_named_variable_untyped() async {
await assertNoErrorsInCode(r'''
void f(x) {
switch (x) {
case (1, 2):
case (foo: var y):
break;
}
}
''');
final node = findNode.switchPatternCase('case').pattern;
assertParsedNodeText(node, r'''
assertResolvedNodeText(node, r'''
RecordPattern
leftParenthesis: (
fields
RecordPatternField
pattern: ConstantPattern
expression: IntegerLiteral
literal: 1
RecordPatternField
pattern: ConstantPattern
expression: IntegerLiteral
literal: 2
fieldName: RecordPatternFieldName
name: foo
colon: :
pattern: VariablePattern
keyword: var
name: y
declaredElement: hasImplicitType y@46
type: dynamic
fieldElement: <null>
rightParenthesis: )
''');
}
test_fields_singleton() async {
test_dynamicType_positional_variable_untyped() async {
await assertNoErrorsInCode(r'''
void f(x) {
switch (x) {
case (var y):
break;
}
}
''');
final node = findNode.switchPatternCase('case').pattern;
assertResolvedNodeText(node, r'''
ParenthesizedPattern
leftParenthesis: (
pattern: VariablePattern
keyword: var
name: y
declaredElement: hasImplicitType y@41
type: dynamic
rightParenthesis: )
''');
}
test_interfaceType_empty() async {
await assertNoErrorsInCode(r'''
void f(Object? x) {
switch (x) {
case ():
break;
}
}
''');
final node = findNode.switchPatternCase('case').pattern;
assertResolvedNodeText(node, r'''
RecordPattern
leftParenthesis: (
rightParenthesis: )
''');
}
test_interfaceType_named_constant() async {
await assertNoErrorsInCode(r'''
void f(Object? x) {
switch (x) {
case (foo: 0):
break;
}
}
''');
final node = findNode.switchPatternCase('case').pattern;
assertResolvedNodeText(node, r'''
RecordPattern
leftParenthesis: (
fields
RecordPatternField
fieldName: RecordPatternFieldName
name: foo
colon: :
pattern: ConstantPattern
expression: IntegerLiteral
literal: 0
staticType: int
fieldElement: <null>
rightParenthesis: )
''');
}
test_interfaceType_named_variable_typed() async {
await assertNoErrorsInCode(r'''
void f(Object? x) {
switch (x) {
case (foo: int y):
break;
}
}
''');
final node = findNode.switchPatternCase('case').pattern;
assertResolvedNodeText(node, r'''
RecordPattern
leftParenthesis: (
fields
RecordPatternField
fieldName: RecordPatternFieldName
name: foo
colon: :
pattern: VariablePattern
type: NamedType
name: SimpleIdentifier
token: int
staticElement: dart:core::@class::int
staticType: null
type: int
name: y
declaredElement: y@54
type: int
fieldElement: <null>
rightParenthesis: )
''');
}
test_interfaceType_named_variable_untyped() async {
await assertNoErrorsInCode(r'''
void f(Object? x) {
switch (x) {
case (foo: var y):
break;
}
}
''');
final node = findNode.switchPatternCase('case').pattern;
assertResolvedNodeText(node, r'''
RecordPattern
leftParenthesis: (
fields
RecordPatternField
fieldName: RecordPatternFieldName
name: foo
colon: :
pattern: VariablePattern
keyword: var
name: y
declaredElement: hasImplicitType y@54
type: Object?
fieldElement: <null>
rightParenthesis: )
''');
}
test_interfaceType_positional_constant() async {
await assertNoErrorsInCode(r'''
void f(Object? x) {
switch (x) {
case (0,):
break;
@ -67,7 +197,7 @@ void f(x) {
}
''');
final node = findNode.switchPatternCase('case').pattern;
assertParsedNodeText(node, r'''
assertResolvedNodeText(node, r'''
RecordPattern
leftParenthesis: (
fields
@ -75,6 +205,319 @@ RecordPattern
pattern: ConstantPattern
expression: IntegerLiteral
literal: 0
staticType: int
fieldElement: <null>
rightParenthesis: )
''');
}
test_interfaceType_positional_variable_typed() async {
await assertNoErrorsInCode(r'''
void f(Object? x) {
switch (x) {
case (int y,):
break;
}
}
''');
final node = findNode.switchPatternCase('case').pattern;
assertResolvedNodeText(node, r'''
RecordPattern
leftParenthesis: (
fields
RecordPatternField
pattern: VariablePattern
type: NamedType
name: SimpleIdentifier
token: int
staticElement: dart:core::@class::int
staticType: null
type: int
name: y
declaredElement: y@49
type: int
fieldElement: <null>
rightParenthesis: )
''');
}
test_interfaceType_positional_variable_untyped() async {
await assertNoErrorsInCode(r'''
void f(Object? x) {
switch (x) {
case (var y,):
break;
}
}
''');
final node = findNode.switchPatternCase('case').pattern;
assertResolvedNodeText(node, r'''
RecordPattern
leftParenthesis: (
fields
RecordPatternField
pattern: VariablePattern
keyword: var
name: y
declaredElement: hasImplicitType y@49
type: Object?
fieldElement: <null>
rightParenthesis: )
''');
}
test_recordType_empty() async {
await assertNoErrorsInCode(r'''
void f(() x) {
switch (x) {
case ():
break;
}
}
''');
final node = findNode.switchPatternCase('case').pattern;
assertResolvedNodeText(node, r'''
RecordPattern
leftParenthesis: (
rightParenthesis: )
''');
}
test_recordType_mixed() async {
await assertNoErrorsInCode(r'''
void f((int, double, {String foo}) x) {
switch (x) {
case (var a, foo: var b, var c):
break;
}
}
''');
final node = findNode.switchPatternCase('case').pattern;
assertResolvedNodeText(node, r'''
RecordPattern
leftParenthesis: (
fields
RecordPatternField
pattern: VariablePattern
keyword: var
name: a
declaredElement: hasImplicitType a@69
type: int
fieldElement: <null>
RecordPatternField
fieldName: RecordPatternFieldName
name: foo
colon: :
pattern: VariablePattern
keyword: var
name: b
declaredElement: hasImplicitType b@81
type: String
fieldElement: <null>
RecordPatternField
pattern: VariablePattern
keyword: var
name: c
declaredElement: hasImplicitType c@88
type: double
fieldElement: <null>
rightParenthesis: )
''');
}
test_recordType_named_hasName_unresolved() async {
await assertNoErrorsInCode(r'''
void f(({int foo}) x) {
switch (x) {
case (bar: var a):
break;
}
}
''');
final node = findNode.switchPatternCase('case').pattern;
assertResolvedNodeText(node, r'''
RecordPattern
leftParenthesis: (
fields
RecordPatternField
fieldName: RecordPatternFieldName
name: bar
colon: :
pattern: VariablePattern
keyword: var
name: a
declaredElement: hasImplicitType a@58
type: Object?
fieldElement: <null>
rightParenthesis: )
''');
}
test_recordType_named_hasName_variable() async {
await assertNoErrorsInCode(r'''
void f(({int foo}) x) {
switch (x) {
case (foo: var y):
break;
}
}
''');
final node = findNode.switchPatternCase('case').pattern;
assertResolvedNodeText(node, r'''
RecordPattern
leftParenthesis: (
fields
RecordPatternField
fieldName: RecordPatternFieldName
name: foo
colon: :
pattern: VariablePattern
keyword: var
name: y
declaredElement: hasImplicitType y@58
type: int
fieldElement: <null>
rightParenthesis: )
''');
}
test_recordType_named_noName_constant() async {
await assertErrorsInCode(r'''
void f(({int foo}) x) {
switch (x) {
case (: 0):
break;
}
}
''', [
error(CompileTimeErrorCode.MISSING_EXTRACTOR_PATTERN_GETTER_NAME, 49, 3),
]);
final node = findNode.switchPatternCase('case').pattern;
assertResolvedNodeText(node, r'''
RecordPattern
leftParenthesis: (
fields
RecordPatternField
fieldName: RecordPatternFieldName
colon: :
pattern: ConstantPattern
expression: IntegerLiteral
literal: 0
staticType: int
fieldElement: <null>
rightParenthesis: )
''');
}
test_recordType_named_noName_variable() async {
await assertNoErrorsInCode(r'''
void f(({int foo}) x) {
switch (x) {
case (: var foo):
break;
}
}
''');
final node = findNode.switchPatternCase('case').pattern;
assertResolvedNodeText(node, r'''
RecordPattern
leftParenthesis: (
fields
RecordPatternField
fieldName: RecordPatternFieldName
colon: :
pattern: VariablePattern
keyword: var
name: foo
declaredElement: hasImplicitType foo@55
type: int
fieldElement: <null>
rightParenthesis: )
''');
}
test_recordType_named_noName_variable_nullCheck() async {
await assertNoErrorsInCode(r'''
void f(({int? foo}) x) {
switch (x) {
case (: var foo?):
break;
}
}
''');
final node = findNode.switchPatternCase('case').pattern;
assertResolvedNodeText(node, r'''
RecordPattern
leftParenthesis: (
fields
RecordPatternField
fieldName: RecordPatternFieldName
colon: :
pattern: PostfixPattern
operand: VariablePattern
keyword: var
name: foo
declaredElement: hasImplicitType foo@56
type: int
operator: ?
fieldElement: <null>
rightParenthesis: )
''');
}
test_recordType_positional_tooMany() async {
await assertNoErrorsInCode(r'''
void f((int,) x) {
switch (x) {
case (var a, var b):
break;
}
}
''');
final node = findNode.switchPatternCase('case').pattern;
assertResolvedNodeText(node, r'''
RecordPattern
leftParenthesis: (
fields
RecordPatternField
pattern: VariablePattern
keyword: var
name: a
declaredElement: hasImplicitType a@48
type: int
fieldElement: <null>
RecordPatternField
pattern: VariablePattern
keyword: var
name: b
declaredElement: hasImplicitType b@55
type: Object?
fieldElement: <null>
rightParenthesis: )
''');
}
test_recordType_positional_variable() async {
await assertNoErrorsInCode(r'''
void f((int,) x) {
switch (x) {
case (var a,):
break;
}
}
''');
final node = findNode.switchPatternCase('case').pattern;
assertResolvedNodeText(node, r'''
RecordPattern
leftParenthesis: (
fields
RecordPatternField
pattern: VariablePattern
keyword: var
name: a
declaredElement: hasImplicitType a@48
type: int
fieldElement: <null>
rightParenthesis: )
''');
}