Resolve PatternVariableDeclarationStatement

Change-Id: Ied7bcde49729608028eb5b62102f756b565a858f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/272080
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
Konstantin Shcheglov 2022-11-24 21:03:07 +00:00 committed by Commit Queue
parent 8b12c0c6fb
commit 7759f7fe03
13 changed files with 662 additions and 16 deletions

View file

@ -21,6 +21,7 @@ import 'package:analyzer/src/fasta/token_utils.dart' as util show findPrevious;
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/source.dart' show LineInfo;
import 'package:analyzer/src/generated/utilities_dart.dart';
import 'package:collection/collection.dart';
import 'package:meta/meta.dart';
/// Two or more string literals that are implicitly concatenated because of
@ -1540,8 +1541,9 @@ class CastPatternImpl extends DartPatternImpl implements CastPattern {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitCastPattern(this);
@override
DartType computePatternSchema(ResolverVisitor resolverVisitor) =>
throw UnimplementedError('TODO(paulberry)');
DartType computePatternSchema(ResolverVisitor resolverVisitor) {
return resolverVisitor.analyzeCastPatternSchema();
}
@override
void resolvePattern(
@ -8085,8 +8087,13 @@ class ListPatternImpl extends DartPatternImpl implements ListPattern {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitListPattern(this);
@override
DartType computePatternSchema(ResolverVisitor resolverVisitor) =>
throw UnimplementedError('TODO(paulberry)');
DartType computePatternSchema(ResolverVisitor resolverVisitor) {
var elementType = typeArguments?.arguments.elementAtOrNull(0)?.typeOrThrow;
return resolverVisitor.analyzeListPatternSchema(
elementType: elementType,
elements: elements,
);
}
@override
void resolvePattern(
@ -9449,8 +9456,9 @@ class ObjectPatternImpl extends DartPatternImpl implements ObjectPattern {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitObjectPattern(this);
@override
DartType computePatternSchema(ResolverVisitor resolverVisitor) =>
throw UnimplementedError('TODO(paulberry)');
DartType computePatternSchema(ResolverVisitor resolverVisitor) {
return resolverVisitor.analyzeObjectPatternSchema(type.typeOrThrow);
}
@override
void resolvePattern(
@ -9644,8 +9652,9 @@ class ParenthesizedPatternImpl extends DartPatternImpl
visitor.visitParenthesizedPattern(this);
@override
DartType computePatternSchema(ResolverVisitor resolverVisitor) =>
throw UnimplementedError('TODO(paulberry)');
DartType computePatternSchema(ResolverVisitor resolverVisitor) {
return resolverVisitor.dispatchPatternSchema(pattern);
}
@override
void resolvePattern(
@ -10109,8 +10118,12 @@ class PostfixPatternImpl extends DartPatternImpl implements PostfixPattern {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitPostfixPattern(this);
@override
DartType computePatternSchema(ResolverVisitor resolverVisitor) =>
throw UnimplementedError('TODO(paulberry)');
DartType computePatternSchema(ResolverVisitor resolverVisitor) {
return resolverVisitor.analyzeNullCheckOrAssertPatternSchema(
operand,
isAssert: operator.type == TokenType.BANG,
);
}
@override
void resolvePattern(
@ -10607,8 +10620,11 @@ class RecordPatternImpl extends DartPatternImpl implements RecordPattern {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitRecordPattern(this);
@override
DartType computePatternSchema(ResolverVisitor resolverVisitor) =>
throw UnimplementedError('TODO(paulberry)');
DartType computePatternSchema(ResolverVisitor resolverVisitor) {
return resolverVisitor.analyzeRecordPatternSchema(
fields: resolverVisitor.buildSharedRecordPatternFields(fields),
);
}
@override
void resolvePattern(
@ -13635,8 +13651,9 @@ class VariablePatternImpl extends DartPatternImpl implements VariablePattern {
E? accept<E>(AstVisitor<E> visitor) => visitor.visitVariablePattern(this);
@override
DartType computePatternSchema(ResolverVisitor resolverVisitor) =>
throw UnimplementedError('TODO(paulberry)');
DartType computePatternSchema(ResolverVisitor resolverVisitor) {
return resolverVisitor.analyzeVariablePatternSchema(type?.typeOrThrow);
}
@override
void resolvePattern(

View file

@ -408,7 +408,7 @@ class TypeSystemOperations
@override
DartType glb(DartType type1, DartType type2) {
throw UnimplementedError('TODO(paulberry)');
return typeSystem.getGreatestLowerBound(type1, type2);
}
@override
@ -452,7 +452,7 @@ class TypeSystemOperations
@override
DartType makeNullable(DartType type) {
throw UnimplementedError('TODO(paulberry)');
return typeSystem.makeNullable(type);
}
@override

View file

@ -990,6 +990,14 @@ class ResolutionVisitor extends RecursiveAstVisitor<void> {
});
}
@override
void visitPatternVariableDeclarationStatement(
PatternVariableDeclarationStatement node) {
_patternVariables.casePatternStart();
super.visitPatternVariableDeclarationStatement(node);
_patternVariables.casePatternFinish();
}
@override
void visitPrefixedIdentifier(covariant PrefixedIdentifierImpl node) {
var newNode = _astRewriter.prefixedIdentifier(_nameScope, node);

View file

@ -2834,6 +2834,19 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
elementResolver.visitPartOfDirective(node);
}
@override
void visitPatternVariableDeclarationStatement(
PatternVariableDeclarationStatement node) {
checkUnreachableNode(node);
var declaration = node.declaration;
// TODO(scheglov) Support for `late` was removed.
analyzePatternVariableDeclarationStatement(
node, declaration.pattern, declaration.expression,
isFinal: declaration.keyword.keyword == Keyword.FINAL, isLate: false);
// node.visitChildren(this);
popRewrite(); // expression
}
@override
void visitPostfixExpression(PostfixExpression node, {DartType? contextType}) {
checkUnreachableNode(node);

View file

@ -45,6 +45,29 @@ class FindNode {
return nodes.single;
}
/// Returns the [PatternVariableDeclaration], there must be only one.
PatternVariableDeclaration get singlePatternVariableDeclaration {
var nodes = <PatternVariableDeclaration>[];
unit.accept(
FunctionAstVisitor(
patternVariableDeclaration: nodes.add,
),
);
return nodes.single;
}
/// Returns the [PatternVariableDeclarationStatement], there must be only one.
PatternVariableDeclarationStatement
get singlePatternVariableDeclarationStatement {
var nodes = <PatternVariableDeclarationStatement>[];
unit.accept(
FunctionAstVisitor(
patternVariableDeclarationStatement: nodes.add,
),
);
return nodes.single;
}
AdjacentStrings adjacentStrings(String search) {
return _node(search, (n) => n is AdjacentStrings);
}

View file

@ -15,6 +15,9 @@ class FunctionAstVisitor extends RecursiveAstVisitor<void> {
final void Function(IfStatement)? ifStatement;
final void Function(Label)? label;
final void Function(MethodInvocation)? methodInvocation;
final void Function(PatternVariableDeclaration)? patternVariableDeclaration;
final void Function(PatternVariableDeclarationStatement)?
patternVariableDeclarationStatement;
final void Function(SimpleIdentifier)? simpleIdentifier;
final void Function(SwitchExpressionCase)? switchExpressionCase;
final void Function(SwitchPatternCase)? switchPatternCase;
@ -28,6 +31,8 @@ class FunctionAstVisitor extends RecursiveAstVisitor<void> {
this.ifStatement,
this.label,
this.methodInvocation,
this.patternVariableDeclaration,
this.patternVariableDeclarationStatement,
this.simpleIdentifier,
this.switchExpressionCase,
this.switchPatternCase,
@ -88,6 +93,19 @@ class FunctionAstVisitor extends RecursiveAstVisitor<void> {
super.visitMethodInvocation(node);
}
@override
void visitPatternVariableDeclaration(PatternVariableDeclaration node) {
patternVariableDeclaration?.call(node);
super.visitPatternVariableDeclaration(node);
}
@override
void visitPatternVariableDeclarationStatement(
PatternVariableDeclarationStatement node) {
patternVariableDeclarationStatement?.call(node);
super.visitPatternVariableDeclarationStatement(node);
}
@override
void visitSimpleIdentifier(SimpleIdentifier node) {
if (simpleIdentifier != null) {

View file

@ -62,6 +62,39 @@ CastPattern
staticElement: dart:core::@class::int
staticType: null
type: int
''');
}
test_variableDeclaration() async {
await assertNoErrorsInCode(r'''
void f(x) {
var (a as int) = x;
}
''');
final node = findNode.singlePatternVariableDeclaration;
assertResolvedNodeText(node, r'''
PatternVariableDeclaration
keyword: var
pattern: ParenthesizedPattern
leftParenthesis: (
pattern: CastPattern
pattern: VariablePattern
name: a
declaredElement: hasImplicitType a@19
type: int
asToken: as
type: NamedType
name: SimpleIdentifier
token: int
staticElement: dart:core::@class::int
staticType: null
type: int
rightParenthesis: )
equals: =
expression: SimpleIdentifier
token: x
staticElement: self::@function::f::@parameter::x
staticType: dynamic
''');
}
}

View file

@ -383,6 +383,123 @@ ListPattern
type: int
rightBracket: ]
requiredType: List<int>
''');
}
test_variableDeclaration_inferredType() async {
await assertNoErrorsInCode(r'''
void f(List<int> x) {
var [a] = x;
}
''');
final node = findNode.singlePatternVariableDeclaration;
assertResolvedNodeText(node, r'''
PatternVariableDeclaration
keyword: var
pattern: ListPattern
leftBracket: [
elements
VariablePattern
name: a
declaredElement: hasImplicitType a@29
type: int
rightBracket: ]
requiredType: List<int>
equals: =
expression: SimpleIdentifier
token: x
staticElement: self::@function::f::@parameter::x
staticType: List<int>
''');
}
test_variableDeclaration_typeSchema_withTypeArguments() async {
await assertNoErrorsInCode(r'''
void f() {
var <int>[a] = g();
}
T g<T>() => throw 0;
''');
final node = findNode.singlePatternVariableDeclaration;
assertResolvedNodeText(node, r'''
PatternVariableDeclaration
keyword: var
pattern: ListPattern
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: SimpleIdentifier
token: int
staticElement: dart:core::@class::int
staticType: null
type: int
rightBracket: >
leftBracket: [
elements
VariablePattern
name: a
declaredElement: hasImplicitType a@23
type: int
rightBracket: ]
requiredType: List<int>
equals: =
expression: MethodInvocation
methodName: SimpleIdentifier
token: g
staticElement: self::@function::g
staticType: T Function<T>()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticInvokeType: List<int> Function()
staticType: List<int>
typeArgumentTypes
List<int>
''');
}
test_variableDeclaration_typeSchema_withVariableType() async {
await assertNoErrorsInCode(r'''
void f() {
var [int a] = g();
}
T g<T>() => throw 0;
''');
final node = findNode.singlePatternVariableDeclaration;
assertResolvedNodeText(node, r'''
PatternVariableDeclaration
keyword: var
pattern: ListPattern
leftBracket: [
elements
VariablePattern
type: NamedType
name: SimpleIdentifier
token: int
staticElement: dart:core::@class::int
staticType: null
type: int
name: a
declaredElement: a@22
type: int
rightBracket: ]
requiredType: List<int>
equals: =
expression: MethodInvocation
methodName: SimpleIdentifier
token: g
staticElement: self::@function::g
staticType: T Function<T>()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticInvokeType: List<int> Function()
staticType: List<int>
typeArgumentTypes
List<int>
''');
}
}

View file

@ -778,6 +778,177 @@ ObjectPattern
type: int
fieldElement: <null>
rightParenthesis: )
''');
}
test_variableDeclaration_inferredType() async {
await assertNoErrorsInCode(r'''
void f(A<int> x) {
var A(foo: a) = x;
}
class A<T> {
T get foo => throw 0;
}
''');
final node = findNode.singlePatternVariableDeclaration;
assertResolvedNodeText(node, r'''
PatternVariableDeclaration
keyword: var
pattern: ObjectPattern
type: NamedType
name: SimpleIdentifier
token: A
staticElement: self::@class::A
staticType: null
type: A<int>
leftParenthesis: (
fields
RecordPatternField
fieldName: RecordPatternFieldName
name: foo
colon: :
pattern: VariablePattern
name: a
declaredElement: hasImplicitType a@32
type: int
fieldElement: PropertyAccessorMember
base: self::@class::A::@getter::foo
substitution: {T: int}
rightParenthesis: )
equals: =
expression: SimpleIdentifier
token: x
staticElement: self::@function::f::@parameter::x
staticType: A<int>
''');
}
/// TODO(scheglov) Remove `new` (everywhere), implement rewrite.
test_variableDeclaration_typeSchema_withTypeArguments() async {
await assertNoErrorsInCode(r'''
void f() {
var A<int>(foo: a) = new A();
}
class A<T> {
T get foo => throw 0;
}
''');
final node = findNode.singlePatternVariableDeclaration;
assertResolvedNodeText(node, r'''
PatternVariableDeclaration
keyword: var
pattern: ObjectPattern
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
name: a
declaredElement: hasImplicitType a@29
type: int
fieldElement: PropertyAccessorMember
base: self::@class::A::@getter::foo
substitution: {T: int}
rightParenthesis: )
equals: =
expression: InstanceCreationExpression
keyword: new
constructorName: ConstructorName
type: NamedType
name: SimpleIdentifier
token: A
staticElement: self::@class::A
staticType: null
type: A<int>
staticElement: ConstructorMember
base: self::@class::A::@constructor::new
substitution: {T: int}
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticType: A<int>
''');
}
test_variableDeclaration_typeSchema_withVariableType() async {
// `int a` does not propagate up, we get `A<dynamic>`
await assertNoErrorsInCode(r'''
void f() {
var A(foo: int a) = new A();
}
class A<T> {
T get foo => throw 0;
}
''');
final node = findNode.singlePatternVariableDeclaration;
assertResolvedNodeText(node, r'''
PatternVariableDeclaration
keyword: var
pattern: ObjectPattern
type: NamedType
name: SimpleIdentifier
token: A
staticElement: self::@class::A
staticType: null
type: A<dynamic>
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: a
declaredElement: a@28
type: int
fieldElement: PropertyAccessorMember
base: self::@class::A::@getter::foo
substitution: {T: dynamic}
rightParenthesis: )
equals: =
expression: InstanceCreationExpression
keyword: new
constructorName: ConstructorName
type: NamedType
name: SimpleIdentifier
token: A
staticElement: self::@class::A
staticType: null
type: A<dynamic>
staticElement: ConstructorMember
base: self::@class::A::@constructor::new
substitution: {T: dynamic}
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticType: A<dynamic>
''');
}
}

View file

@ -0,0 +1,96 @@
// 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:test_reflective_loader/test_reflective_loader.dart';
import 'context_collection_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(PatternVariableDeclarationStatementResolutionTest);
});
}
@reflectiveTest
class PatternVariableDeclarationStatementResolutionTest
extends PatternsResolutionTest {
test_inferredType() async {
await assertNoErrorsInCode(r'''
void f((int, String) x) {
var (a, b) = x;
}
''');
final node = findNode.singlePatternVariableDeclarationStatement;
assertResolvedNodeText(node, r'''
PatternVariableDeclarationStatement
declaration: PatternVariableDeclaration
keyword: var
pattern: RecordPattern
leftParenthesis: (
fields
RecordPatternField
pattern: VariablePattern
name: a
declaredElement: hasImplicitType a@33
type: int
fieldElement: <null>
RecordPatternField
pattern: VariablePattern
name: b
declaredElement: hasImplicitType b@36
type: String
fieldElement: <null>
rightParenthesis: )
equals: =
expression: SimpleIdentifier
token: x
staticElement: self::@function::f::@parameter::x
staticType: (int, String)
semicolon: ;
''');
}
test_typeSchema_fromVariableType() async {
await assertNoErrorsInCode(r'''
void f() {
var (int a) = g();
}
T g<T>() => throw 0;
''');
final node = findNode.singlePatternVariableDeclarationStatement;
assertResolvedNodeText(node, r'''
PatternVariableDeclarationStatement
declaration: PatternVariableDeclaration
keyword: var
pattern: ParenthesizedPattern
leftParenthesis: (
pattern: VariablePattern
type: NamedType
name: SimpleIdentifier
token: int
staticElement: dart:core::@class::int
staticType: null
type: int
name: a
declaredElement: a@22
type: int
rightParenthesis: )
equals: =
expression: MethodInvocation
methodName: SimpleIdentifier
token: g
staticElement: self::@function::g
staticType: T Function<T>()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticInvokeType: int Function()
staticType: int
typeArgumentTypes
int
semicolon: ;
''');
}
}

View file

@ -53,6 +53,33 @@ PostfixPattern
''');
}
test_nullAssert_variableDeclaration() async {
await assertNoErrorsInCode(r'''
void f(int? x) {
var (a!) = x;
}
''');
final node = findNode.singlePatternVariableDeclaration;
assertResolvedNodeText(node, r'''
PatternVariableDeclaration
keyword: var
pattern: ParenthesizedPattern
leftParenthesis: (
pattern: PostfixPattern
operand: VariablePattern
name: a
declaredElement: hasImplicitType a@24
type: int
operator: !
rightParenthesis: )
equals: =
expression: SimpleIdentifier
token: x
staticElement: self::@function::f::@parameter::x
staticType: int?
''');
}
test_nullCheck_ifCase() async {
await assertNoErrorsInCode(r'''
void f(int? x) {
@ -89,6 +116,35 @@ PostfixPattern
declaredElement: hasImplicitType y@45
type: int
operator: ?
''');
}
/// TODO(scheglov) finish
@FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/50066')
test_nullCheck_variableDeclaration() async {
await assertNoErrorsInCode(r'''
void f(int? x) {
var (a?) = x;
}
''');
final node = findNode.singlePatternVariableDeclaration;
assertResolvedNodeText(node, r'''
PatternVariableDeclaration
keyword: var
pattern: ParenthesizedPattern
leftParenthesis: (
pattern: PostfixPattern
operand: VariablePattern
name: a
declaredElement: hasImplicitType a@24
type: int
operator: !
rightParenthesis: )
equals: =
expression: SimpleIdentifier
token: x
staticElement: self::@function::f::@parameter::x
staticType: int?
''');
}
}

View file

@ -646,6 +646,97 @@ RecordPattern
type: int
fieldElement: <null>
rightParenthesis: )
''');
}
test_variableDeclaration_inferredType() async {
await assertNoErrorsInCode(r'''
void f((int, String) x) {
var (a, b) = x;
}
''');
final node = findNode.singlePatternVariableDeclaration;
assertResolvedNodeText(node, r'''
PatternVariableDeclaration
keyword: var
pattern: RecordPattern
leftParenthesis: (
fields
RecordPatternField
pattern: VariablePattern
name: a
declaredElement: hasImplicitType a@33
type: int
fieldElement: <null>
RecordPatternField
pattern: VariablePattern
name: b
declaredElement: hasImplicitType b@36
type: String
fieldElement: <null>
rightParenthesis: )
equals: =
expression: SimpleIdentifier
token: x
staticElement: self::@function::f::@parameter::x
staticType: (int, String)
''');
}
test_variableDeclaration_typeSchema() async {
await assertNoErrorsInCode(r'''
void f() {
var (int a, String b) = g();
}
(T, U) g<T, U>() => throw 0;
''');
final node = findNode.singlePatternVariableDeclaration;
assertResolvedNodeText(node, r'''
PatternVariableDeclaration
keyword: var
pattern: RecordPattern
leftParenthesis: (
fields
RecordPatternField
pattern: VariablePattern
type: NamedType
name: SimpleIdentifier
token: int
staticElement: dart:core::@class::int
staticType: null
type: int
name: a
declaredElement: a@22
type: int
fieldElement: <null>
RecordPatternField
pattern: VariablePattern
type: NamedType
name: SimpleIdentifier
token: String
staticElement: dart:core::@class::String
staticType: null
type: String
name: b
declaredElement: b@32
type: String
fieldElement: <null>
rightParenthesis: )
equals: =
expression: MethodInvocation
methodName: SimpleIdentifier
token: g
staticElement: self::@function::g
staticType: (T, U) Function<T, U>()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticInvokeType: (int, String) Function()
staticType: (int, String)
typeArgumentTypes
int
String
''');
}
}

View file

@ -66,6 +66,8 @@ import 'object_pattern_test.dart' as object_pattern;
import 'optional_const_test.dart' as optional_const;
import 'parenthesized_pattern_test.dart' as parenthesized_pattern;
import 'part_test.dart' as part_;
import 'pattern_variable_declaration_statement_test.dart'
as pattern_variable_declaration_statement;
import 'postfix_expression_test.dart' as postfix_expression;
import 'postfix_pattern_test.dart' as postfix_pattern;
import 'prefix_element_test.dart' as prefix_element;
@ -149,6 +151,7 @@ main() {
optional_const.main();
parenthesized_pattern.main();
part_.main();
pattern_variable_declaration_statement.main();
postfix_expression.main();
postfix_pattern.main();
prefix_element.main();