mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 22:41:41 +00:00
Basic resolution for ExtractorPattern.
Change-Id: Id129aa245ec792a169d43e9658f1531ffd1e67a5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/263044 Commit-Queue: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
parent
51cf541243
commit
f07b293ab8
|
@ -4337,6 +4337,10 @@ abstract class RecordPattern implements DartPattern {
|
|||
/// Clients may not extend, implement or mix-in this class.
|
||||
@experimental
|
||||
abstract class RecordPatternField implements AstNode {
|
||||
/// The element referenced explicitly by [fieldName], or implicitly by the
|
||||
/// variable pattern inside [pattern].
|
||||
Element? get fieldElement;
|
||||
|
||||
/// The name of the field, or `null` if the field is a positional field.
|
||||
RecordPatternFieldName? get fieldName;
|
||||
|
||||
|
|
|
@ -3359,6 +3359,9 @@ abstract class DartPatternImpl extends AstNodeImpl implements DartPattern {
|
|||
// have constants for pattern-related precedence values.
|
||||
Precedence get precedence => throw UnimplementedError();
|
||||
|
||||
/// The variable pattern, itself, or wrapped in a unary pattern.
|
||||
VariablePatternImpl? get variablePattern => null;
|
||||
|
||||
DartType computePatternSchema(ResolverVisitor resolverVisitor);
|
||||
|
||||
void resolvePattern(
|
||||
|
@ -4712,7 +4715,7 @@ class ExtensionOverrideImpl extends ExpressionImpl
|
|||
/// [Identifier] [TypeArgumentList]? '(' [RecordPatternField] ')'
|
||||
@experimental
|
||||
class ExtractorPatternImpl extends DartPatternImpl implements ExtractorPattern {
|
||||
final NodeListImpl<RecordPatternField> _fields = NodeListImpl._();
|
||||
final NodeListImpl<RecordPatternFieldImpl> _fields = NodeListImpl._();
|
||||
|
||||
@override
|
||||
final Token leftParenthesis;
|
||||
|
@ -4726,7 +4729,7 @@ class ExtractorPatternImpl extends DartPatternImpl implements ExtractorPattern {
|
|||
ExtractorPatternImpl(
|
||||
{required this.type,
|
||||
required this.leftParenthesis,
|
||||
required List<RecordPatternField> fields,
|
||||
required List<RecordPatternFieldImpl> fields,
|
||||
required this.rightParenthesis}) {
|
||||
_becomeParentOf(type);
|
||||
_fields._initialize(this, fields);
|
||||
|
@ -4739,7 +4742,7 @@ class ExtractorPatternImpl extends DartPatternImpl implements ExtractorPattern {
|
|||
Token get endToken => rightParenthesis;
|
||||
|
||||
@override
|
||||
NodeList<RecordPatternField> get fields => _fields;
|
||||
NodeList<RecordPatternFieldImpl> get fields => _fields;
|
||||
|
||||
@override
|
||||
ChildEntities get _childEntities => super._childEntities
|
||||
|
@ -4760,8 +4763,14 @@ class ExtractorPatternImpl extends DartPatternImpl implements ExtractorPattern {
|
|||
ResolverVisitor resolverVisitor,
|
||||
DartType matchedType,
|
||||
Map<PromotableElement, VariableTypeInfo<AstNode, DartType>> typeInfos,
|
||||
MatchContext<AstNode, Expression> context) {
|
||||
// TODO(scheglov) https://github.com/dart-lang/sdk/issues/50066
|
||||
MatchContext<AstNode, Expression> context,
|
||||
) {
|
||||
resolverVisitor.extractorPatternResolver.resolve(
|
||||
node: this,
|
||||
matchedType: matchedType,
|
||||
typeInfos: typeInfos,
|
||||
context: context,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -9588,6 +9597,9 @@ class ParenthesizedPatternImpl extends DartPatternImpl
|
|||
@override
|
||||
Token get endToken => rightParenthesis;
|
||||
|
||||
@override
|
||||
VariablePatternImpl? get variablePattern => pattern.variablePattern;
|
||||
|
||||
@override
|
||||
ChildEntities get _childEntities => super._childEntities
|
||||
..addToken('leftParenthesis', leftParenthesis)
|
||||
|
@ -10041,6 +10053,9 @@ class PostfixPatternImpl extends DartPatternImpl implements PostfixPattern {
|
|||
@override
|
||||
Token get endToken => operator;
|
||||
|
||||
@override
|
||||
VariablePatternImpl? get variablePattern => operand.variablePattern;
|
||||
|
||||
@override
|
||||
ChildEntities get _childEntities => super._childEntities
|
||||
..addNode('operand', operand)
|
||||
|
@ -10435,6 +10450,9 @@ class RecordLiteralImpl extends LiteralImpl implements RecordLiteral {
|
|||
/// [RecordPatternFieldName]? [DartPattern]
|
||||
@experimental
|
||||
class RecordPatternFieldImpl extends AstNodeImpl implements RecordPatternField {
|
||||
@override
|
||||
Element? fieldElement;
|
||||
|
||||
@override
|
||||
final RecordPatternFieldNameImpl? fieldName;
|
||||
|
||||
|
@ -13560,6 +13578,9 @@ class VariablePatternImpl extends DartPatternImpl implements VariablePattern {
|
|||
@override
|
||||
Token get endToken => name;
|
||||
|
||||
@override
|
||||
VariablePatternImpl? get variablePattern => this;
|
||||
|
||||
@override
|
||||
ChildEntities get _childEntities => super._childEntities
|
||||
..addToken('keyword', keyword)
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
// 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/nullability_suffix.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:analyzer/src/dart/ast/ast.dart';
|
||||
import 'package:analyzer/src/dart/ast/extensions.dart';
|
||||
import 'package:analyzer/src/dart/element/generic_inferrer.dart';
|
||||
import 'package:analyzer/src/dart/element/type_provider.dart';
|
||||
import 'package:analyzer/src/dart/element/type_schema.dart';
|
||||
import 'package:analyzer/src/generated/resolver.dart';
|
||||
|
||||
class ExtractorPatternResolver {
|
||||
final ResolverVisitor resolverVisitor;
|
||||
|
||||
ExtractorPatternResolver(this.resolverVisitor);
|
||||
|
||||
TypeProviderImpl get _typeProvider => resolverVisitor.typeProvider;
|
||||
|
||||
void resolve({
|
||||
required ExtractorPatternImpl node,
|
||||
required DartType matchedType,
|
||||
required Map<PromotableElement, VariableTypeInfo<AstNode, DartType>>
|
||||
typeInfos,
|
||||
required MatchContext<AstNode, Expression> context,
|
||||
}) {
|
||||
var inferredType = _inferType(
|
||||
matchedType: matchedType,
|
||||
typeNode: node.type,
|
||||
);
|
||||
|
||||
for (var field in node.fields) {
|
||||
// TODO(scheglov) Report an error.
|
||||
var nameToken =
|
||||
field.fieldName?.name ?? field.pattern.variablePattern!.name;
|
||||
var result = resolverVisitor.typePropertyResolver.resolve(
|
||||
receiver: null,
|
||||
receiverType: inferredType,
|
||||
name: nameToken.lexeme,
|
||||
propertyErrorEntity: nameToken,
|
||||
nameErrorEntity: node,
|
||||
);
|
||||
// TODO(scheglov) Report an error.
|
||||
var getter = result.getter;
|
||||
field.fieldElement = getter;
|
||||
var fieldType = getter?.returnType ?? _typeProvider.dynamicType;
|
||||
field.pattern
|
||||
.resolvePattern(resolverVisitor, fieldType, typeInfos, context);
|
||||
}
|
||||
}
|
||||
|
||||
DartType _inferType({
|
||||
required DartType matchedType,
|
||||
required NamedTypeImpl typeNode,
|
||||
}) {
|
||||
if (typeNode.typeArguments == null) {
|
||||
var typeNameElement = typeNode.name.staticElement;
|
||||
if (typeNameElement is InterfaceElement) {
|
||||
var typeParameters = typeNameElement.typeParameters;
|
||||
if (typeParameters.isNotEmpty) {
|
||||
var typeArguments = _inferTypeArguments(
|
||||
typeParameters: typeParameters,
|
||||
errorNode: typeNode,
|
||||
declaredType: typeNameElement.thisType,
|
||||
matchedType: matchedType,
|
||||
);
|
||||
return typeNode.type = typeNameElement.instantiate(
|
||||
typeArguments: typeArguments,
|
||||
nullabilitySuffix: NullabilitySuffix.none,
|
||||
);
|
||||
}
|
||||
} else if (typeNameElement is TypeAliasElement) {
|
||||
var typeParameters = typeNameElement.typeParameters;
|
||||
if (typeParameters.isNotEmpty) {
|
||||
var typeArguments = _inferTypeArguments(
|
||||
typeParameters: typeParameters,
|
||||
errorNode: typeNode,
|
||||
declaredType: typeNameElement.aliasedType,
|
||||
matchedType: matchedType,
|
||||
);
|
||||
return typeNode.type = typeNameElement.instantiate(
|
||||
typeArguments: typeArguments,
|
||||
nullabilitySuffix: NullabilitySuffix.none,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return typeNode.typeOrThrow;
|
||||
}
|
||||
|
||||
List<DartType> _inferTypeArguments({
|
||||
required List<TypeParameterElement> typeParameters,
|
||||
required AstNode errorNode,
|
||||
required DartType declaredType,
|
||||
required DartType matchedType,
|
||||
}) {
|
||||
var inferrer = GenericInferrer(
|
||||
resolverVisitor.typeSystem,
|
||||
typeParameters,
|
||||
errorReporter: resolverVisitor.errorReporter,
|
||||
errorNode: errorNode,
|
||||
genericMetadataIsEnabled: resolverVisitor.genericMetadataIsEnabled,
|
||||
);
|
||||
inferrer.constrainReturnType(declaredType, matchedType);
|
||||
return inferrer.partialInfer().map((typeArgument) {
|
||||
if (typeArgument is UnknownInferredType) {
|
||||
return _typeProvider.dynamicType;
|
||||
} else {
|
||||
return typeArgument;
|
||||
}
|
||||
}).toList();
|
||||
}
|
||||
}
|
|
@ -3787,7 +3787,7 @@ class AstBuilder extends StackListener {
|
|||
void handleExtractorPatternFields(
|
||||
int count, Token beginToken, Token endToken) {
|
||||
debugEvent("ExtractorPatternFields");
|
||||
var fields = popTypedList2<RecordPatternField>(count);
|
||||
var fields = popTypedList2<RecordPatternFieldImpl>(count);
|
||||
push(_ExtractorPatternFields(beginToken, endToken, fields));
|
||||
}
|
||||
|
||||
|
@ -5312,7 +5312,7 @@ class _ExtensionDeclarationBuilder extends _ClassLikeDeclarationBuilder {
|
|||
class _ExtractorPatternFields {
|
||||
final Token leftParenthesis;
|
||||
final Token rightParenthesis;
|
||||
final List<RecordPatternField> fields;
|
||||
final List<RecordPatternFieldImpl> fields;
|
||||
|
||||
_ExtractorPatternFields(
|
||||
this.leftParenthesis, this.rightParenthesis, this.fields);
|
||||
|
|
|
@ -41,6 +41,7 @@ import 'package:analyzer/src/dart/resolver/binary_expression_resolver.dart';
|
|||
import 'package:analyzer/src/dart/resolver/body_inference_context.dart';
|
||||
import 'package:analyzer/src/dart/resolver/constructor_reference_resolver.dart';
|
||||
import 'package:analyzer/src/dart/resolver/extension_member_resolver.dart';
|
||||
import 'package:analyzer/src/dart/resolver/extractor_pattern_resolver.dart';
|
||||
import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
|
||||
import 'package:analyzer/src/dart/resolver/for_resolver.dart';
|
||||
import 'package:analyzer/src/dart/resolver/function_expression_invocation_resolver.dart';
|
||||
|
@ -277,6 +278,9 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
|
|||
|
||||
late final AnnotationResolver _annotationResolver = AnnotationResolver(this);
|
||||
|
||||
late final ExtractorPatternResolver extractorPatternResolver =
|
||||
ExtractorPatternResolver(this);
|
||||
|
||||
final bool genericMetadataIsEnabled;
|
||||
|
||||
/// Stack for obtaining rewritten expressions. Prior to visiting an
|
||||
|
|
|
@ -14,122 +14,336 @@ main() {
|
|||
|
||||
@reflectiveTest
|
||||
class ExtractorPatternResolutionTest extends PatternsResolutionTest {
|
||||
test_identifier_noTypeArguments() async {
|
||||
test_generic_noTypeArguments_infer_interfaceType() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
class C {}
|
||||
|
||||
void f(x) {
|
||||
class A<T> {}
|
||||
class B<T> extends A<T> {}
|
||||
void f(A<int> x) {
|
||||
switch (x) {
|
||||
case C():
|
||||
case B():
|
||||
break;
|
||||
}
|
||||
}
|
||||
''');
|
||||
final node = findNode.switchPatternCase('case').pattern;
|
||||
assertParsedNodeText(node, r'''
|
||||
assertResolvedNodeText(node, r'''
|
||||
ExtractorPattern
|
||||
type: NamedType
|
||||
name: SimpleIdentifier
|
||||
token: C
|
||||
token: B
|
||||
staticElement: self::@class::B
|
||||
staticType: null
|
||||
type: B<int>
|
||||
leftParenthesis: (
|
||||
rightParenthesis: )
|
||||
''');
|
||||
}
|
||||
|
||||
test_identifier_withTypeArguments() async {
|
||||
test_generic_noTypeArguments_infer_interfaceType_viaTypeAlias() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
class C<T> {}
|
||||
|
||||
void f(x) {
|
||||
class A<T, U> {}
|
||||
class B<T, U> extends A<T, U> {}
|
||||
typedef L<T> = B<T, String>;
|
||||
void f(A<int, String> x) {
|
||||
switch (x) {
|
||||
case C<int>():
|
||||
case L():
|
||||
break;
|
||||
}
|
||||
}
|
||||
''');
|
||||
final node = findNode.switchPatternCase('case').pattern;
|
||||
assertParsedNodeText(node, r'''
|
||||
assertResolvedNodeText(node, r'''
|
||||
ExtractorPattern
|
||||
type: NamedType
|
||||
name: SimpleIdentifier
|
||||
token: C
|
||||
token: L
|
||||
staticElement: self::@typeAlias::L
|
||||
staticType: null
|
||||
type: B<int, String>
|
||||
alias: self::@typeAlias::L
|
||||
typeArguments
|
||||
int
|
||||
leftParenthesis: (
|
||||
rightParenthesis: )
|
||||
''');
|
||||
}
|
||||
|
||||
test_generic_withTypeArguments_hasName_variable_untyped() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
abstract class A<T> {
|
||||
T get foo;
|
||||
}
|
||||
|
||||
void f(x) {
|
||||
switch (x) {
|
||||
case A<int>(foo: var foo2):
|
||||
break;
|
||||
}
|
||||
}
|
||||
''');
|
||||
final node = findNode.switchPatternCase('case').pattern;
|
||||
assertResolvedNodeText(node, r'''
|
||||
ExtractorPattern
|
||||
type: NamedType
|
||||
name: SimpleIdentifier
|
||||
token: A
|
||||
staticElement: self::@class::A
|
||||
staticType: null
|
||||
typeArguments: TypeArgumentList
|
||||
leftBracket: <
|
||||
arguments
|
||||
NamedType
|
||||
name: SimpleIdentifier
|
||||
token: int
|
||||
staticElement: dart:core::@class::int
|
||||
staticType: null
|
||||
type: int
|
||||
rightBracket: >
|
||||
type: A<int>
|
||||
leftParenthesis: (
|
||||
fields
|
||||
RecordPatternField
|
||||
fieldName: RecordPatternFieldName
|
||||
name: foo
|
||||
colon: :
|
||||
pattern: VariablePattern
|
||||
keyword: var
|
||||
name: foo2
|
||||
declaredElement: hasImplicitType foo2@90
|
||||
type: int
|
||||
fieldElement: PropertyAccessorMember
|
||||
base: self::@class::A::@getter::foo
|
||||
substitution: {T: int}
|
||||
rightParenthesis: )
|
||||
''');
|
||||
}
|
||||
|
||||
test_prefixedIdentifier_noTypeArguments() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
class C {}
|
||||
''');
|
||||
|
||||
test_notGeneric_noTypeArguments_hasName_constant() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
import 'a.dart' as prefix;
|
||||
abstract class A {
|
||||
int get foo;
|
||||
}
|
||||
|
||||
void f(x) {
|
||||
switch (x) {
|
||||
case prefix.C():
|
||||
case A(foo: 0):
|
||||
break;
|
||||
}
|
||||
}
|
||||
''');
|
||||
|
||||
final node = findNode.switchPatternCase('case').pattern;
|
||||
assertParsedNodeText(node, r'''
|
||||
assertResolvedNodeText(node, r'''
|
||||
ExtractorPattern
|
||||
type: NamedType
|
||||
name: PrefixedIdentifier
|
||||
prefix: SimpleIdentifier
|
||||
token: prefix
|
||||
period: .
|
||||
identifier: SimpleIdentifier
|
||||
token: C
|
||||
leftParenthesis: (
|
||||
rightParenthesis: )
|
||||
''');
|
||||
}
|
||||
|
||||
test_prefixedIdentifier_withTypeArguments() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
class C<T> {}
|
||||
''');
|
||||
|
||||
await assertNoErrorsInCode(r'''
|
||||
import 'a.dart' as prefix;
|
||||
|
||||
void f(x) {
|
||||
switch (x) {
|
||||
case prefix.C<int>():
|
||||
break;
|
||||
}
|
||||
}
|
||||
''');
|
||||
|
||||
final node = findNode.switchPatternCase('case').pattern;
|
||||
assertParsedNodeText(node, r'''
|
||||
ExtractorPattern
|
||||
type: NamedType
|
||||
name: PrefixedIdentifier
|
||||
prefix: SimpleIdentifier
|
||||
token: prefix
|
||||
period: .
|
||||
identifier: SimpleIdentifier
|
||||
token: C
|
||||
typeArguments: TypeArgumentList
|
||||
leftBracket: <
|
||||
arguments
|
||||
NamedType
|
||||
name: SimpleIdentifier
|
||||
token: int
|
||||
rightBracket: >
|
||||
token: A
|
||||
staticElement: self::@class::A
|
||||
staticType: null
|
||||
type: A
|
||||
leftParenthesis: (
|
||||
fields
|
||||
RecordPatternField
|
||||
fieldName: RecordPatternFieldName
|
||||
name: foo
|
||||
colon: :
|
||||
pattern: ConstantPattern
|
||||
expression: IntegerLiteral
|
||||
literal: 0
|
||||
staticType: int
|
||||
fieldElement: self::@class::A::@getter::foo
|
||||
rightParenthesis: )
|
||||
''');
|
||||
}
|
||||
|
||||
test_notGeneric_noTypeArguments_hasName_extensionGetter() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
abstract class A {}
|
||||
|
||||
extension E on A {
|
||||
int get foo => 0;
|
||||
}
|
||||
|
||||
void f(x) {
|
||||
switch (x) {
|
||||
case A(foo: 0):
|
||||
break;
|
||||
}
|
||||
}
|
||||
''');
|
||||
final node = findNode.switchPatternCase('case').pattern;
|
||||
assertResolvedNodeText(node, r'''
|
||||
ExtractorPattern
|
||||
type: NamedType
|
||||
name: SimpleIdentifier
|
||||
token: A
|
||||
staticElement: self::@class::A
|
||||
staticType: null
|
||||
type: A
|
||||
leftParenthesis: (
|
||||
fields
|
||||
RecordPatternField
|
||||
fieldName: RecordPatternFieldName
|
||||
name: foo
|
||||
colon: :
|
||||
pattern: ConstantPattern
|
||||
expression: IntegerLiteral
|
||||
literal: 0
|
||||
staticType: int
|
||||
fieldElement: self::@extension::E::@getter::foo
|
||||
rightParenthesis: )
|
||||
''');
|
||||
}
|
||||
|
||||
test_notGeneric_noTypeArguments_hasName_variable_untyped() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
abstract class A {
|
||||
int get foo;
|
||||
}
|
||||
|
||||
void f(x) {
|
||||
switch (x) {
|
||||
case A(foo: var foo2):
|
||||
break;
|
||||
}
|
||||
}
|
||||
''');
|
||||
final node = findNode.switchPatternCase('case').pattern;
|
||||
assertResolvedNodeText(node, r'''
|
||||
ExtractorPattern
|
||||
type: NamedType
|
||||
name: SimpleIdentifier
|
||||
token: A
|
||||
staticElement: self::@class::A
|
||||
staticType: null
|
||||
type: A
|
||||
leftParenthesis: (
|
||||
fields
|
||||
RecordPatternField
|
||||
fieldName: RecordPatternFieldName
|
||||
name: foo
|
||||
colon: :
|
||||
pattern: VariablePattern
|
||||
keyword: var
|
||||
name: foo2
|
||||
declaredElement: hasImplicitType foo2@84
|
||||
type: int
|
||||
fieldElement: self::@class::A::@getter::foo
|
||||
rightParenthesis: )
|
||||
''');
|
||||
}
|
||||
|
||||
test_notGeneric_noTypeArguments_noName_variable() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
abstract class A {
|
||||
int get foo;
|
||||
}
|
||||
|
||||
void f(x) {
|
||||
switch (x) {
|
||||
case A(: var foo):
|
||||
break;
|
||||
}
|
||||
}
|
||||
''');
|
||||
final node = findNode.switchPatternCase('case').pattern;
|
||||
assertResolvedNodeText(node, r'''
|
||||
ExtractorPattern
|
||||
type: NamedType
|
||||
name: SimpleIdentifier
|
||||
token: A
|
||||
staticElement: self::@class::A
|
||||
staticType: null
|
||||
type: A
|
||||
leftParenthesis: (
|
||||
fields
|
||||
RecordPatternField
|
||||
fieldName: RecordPatternFieldName
|
||||
colon: :
|
||||
pattern: VariablePattern
|
||||
keyword: var
|
||||
name: foo
|
||||
declaredElement: hasImplicitType foo@81
|
||||
type: int
|
||||
fieldElement: self::@class::A::@getter::foo
|
||||
rightParenthesis: )
|
||||
''');
|
||||
}
|
||||
|
||||
test_notGeneric_noTypeArguments_noName_variable_nullCheck() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
abstract class A {
|
||||
int? get foo;
|
||||
}
|
||||
|
||||
void f(x) {
|
||||
switch (x) {
|
||||
case A(: var foo?):
|
||||
break;
|
||||
}
|
||||
}
|
||||
''');
|
||||
final node = findNode.switchPatternCase('case').pattern;
|
||||
assertResolvedNodeText(node, r'''
|
||||
ExtractorPattern
|
||||
type: NamedType
|
||||
name: SimpleIdentifier
|
||||
token: A
|
||||
staticElement: self::@class::A
|
||||
staticType: null
|
||||
type: A
|
||||
leftParenthesis: (
|
||||
fields
|
||||
RecordPatternField
|
||||
fieldName: RecordPatternFieldName
|
||||
colon: :
|
||||
pattern: PostfixPattern
|
||||
operand: VariablePattern
|
||||
keyword: var
|
||||
name: foo
|
||||
declaredElement: hasImplicitType foo@82
|
||||
type: int
|
||||
operator: ?
|
||||
fieldElement: self::@class::A::@getter::foo
|
||||
rightParenthesis: )
|
||||
''');
|
||||
}
|
||||
|
||||
test_notGeneric_noTypeArguments_noName_variable_parenthesis() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
abstract class A {
|
||||
int get foo;
|
||||
}
|
||||
|
||||
void f(x) {
|
||||
switch (x) {
|
||||
case A(: (var foo)):
|
||||
break;
|
||||
}
|
||||
}
|
||||
''');
|
||||
final node = findNode.switchPatternCase('case').pattern;
|
||||
assertResolvedNodeText(node, r'''
|
||||
ExtractorPattern
|
||||
type: NamedType
|
||||
name: SimpleIdentifier
|
||||
token: A
|
||||
staticElement: self::@class::A
|
||||
staticType: null
|
||||
type: A
|
||||
leftParenthesis: (
|
||||
fields
|
||||
RecordPatternField
|
||||
fieldName: RecordPatternFieldName
|
||||
colon: :
|
||||
pattern: ParenthesizedPattern
|
||||
leftParenthesis: (
|
||||
pattern: VariablePattern
|
||||
keyword: var
|
||||
name: foo
|
||||
declaredElement: hasImplicitType foo@82
|
||||
type: int
|
||||
rightParenthesis: )
|
||||
fieldElement: self::@class::A::@getter::foo
|
||||
rightParenthesis: )
|
||||
''');
|
||||
}
|
||||
|
|
|
@ -1086,6 +1086,7 @@ class ResolvedAstPrinter extends ThrowingAstVisitor<void> {
|
|||
_writeln('RecordPatternField');
|
||||
_withIndent(() {
|
||||
_writeNamedChildEntities(node);
|
||||
_writeElement('fieldElement', node.fieldElement);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue