Resolve RecordTypeAnnotation AST.
Change-Id: I5eac632ea3badf5dbce35fb842b17c9898bb02cb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/255818
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
index ee1c4b7..938d24f 100644
--- a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
@@ -23,6 +23,7 @@
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/element_walker.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
+import 'package:analyzer/src/summary2/types_builder.dart';
class ElementHolder {
final ElementImpl _element;
@@ -968,6 +969,12 @@
}
@override
+ void visitRecordTypeAnnotation(covariant RecordTypeAnnotationImpl node) {
+ node.visitChildren(this);
+ TypesBuilder.recordTypeAnnotation(node);
+ }
+
+ @override
void visitSimpleFormalParameter(covariant SimpleFormalParameterImpl node) {
ParameterElementImpl element;
if (node.parent is DefaultFormalParameter) {
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 26e9a4e9..3acde71 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -2216,6 +2216,32 @@
}
@override
+ void visitRecordTypeAnnotation(RecordTypeAnnotation node) {
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitRecordTypeAnnotationNamedField(
+ RecordTypeAnnotationNamedField node,
+ ) {
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitRecordTypeAnnotationNamedFields(
+ RecordTypeAnnotationNamedFields node,
+ ) {
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitRecordTypeAnnotationPositionalField(
+ RecordTypeAnnotationPositionalField node,
+ ) {
+ node.visitChildren(this);
+ }
+
+ @override
void visitRedirectingConstructorInvocation(
RedirectingConstructorInvocation node) {
//
diff --git a/pkg/analyzer/lib/src/summary2/types_builder.dart b/pkg/analyzer/lib/src/summary2/types_builder.dart
index 06c100c..1234ef7 100644
--- a/pkg/analyzer/lib/src/summary2/types_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/types_builder.dart
@@ -205,7 +205,7 @@
} else if (node is MixinDeclaration) {
_mixinDeclaration(node);
} else if (node is RecordTypeAnnotationImpl) {
- _recordTypeAnnotation(node);
+ recordTypeAnnotation(node);
} else if (node is SimpleFormalParameter) {
var element = node.declaredElement as ParameterElementImpl;
element.type = node.type?.type ?? _dynamicType;
@@ -327,33 +327,6 @@
}
}
- void _recordTypeAnnotation(RecordTypeAnnotationImpl node) {
- final positionalFields = node.positionalFields.map((field) {
- return RecordPositionalFieldElementImpl(
- name: field.name?.lexeme,
- nameOffset: -1,
- type: field.type.typeOrThrow,
- );
- }).toList();
-
- final namedFields = node.namedFields?.fields.map((field) {
- return RecordNamedFieldElementImpl(
- name: field.name.lexeme,
- nameOffset: -1,
- type: field.type.typeOrThrow,
- );
- }).toList();
-
- node.type = RecordElementImpl(
- positionalFields: positionalFields,
- namedFields: namedFields ?? const [],
- ).instantiate(
- nullabilitySuffix: node.question != null
- ? NullabilitySuffix.question
- : NullabilitySuffix.none,
- );
- }
-
void _superFormalParameter(SuperFormalParameter node) {
var element = node.declaredElement as SuperFormalParameterElementImpl;
var parameterList = node.parameters;
@@ -380,6 +353,33 @@
.toList();
}
+ static void recordTypeAnnotation(RecordTypeAnnotationImpl node) {
+ final positionalFields = node.positionalFields.map((field) {
+ return RecordPositionalFieldElementImpl(
+ name: field.name?.lexeme,
+ nameOffset: -1,
+ type: field.type.typeOrThrow,
+ );
+ }).toList();
+
+ final namedFields = node.namedFields?.fields.map((field) {
+ return RecordNamedFieldElementImpl(
+ name: field.name.lexeme,
+ nameOffset: -1,
+ type: field.type.typeOrThrow,
+ );
+ }).toList();
+
+ node.type = RecordElementImpl(
+ positionalFields: positionalFields,
+ namedFields: namedFields ?? const [],
+ ).instantiate(
+ nullabilitySuffix: node.question != null
+ ? NullabilitySuffix.question
+ : NullabilitySuffix.none,
+ );
+ }
+
/// The [FunctionType] to use when a function type is expected for a type
/// alias, but the actual provided type annotation is not a function type.
static FunctionTypeImpl _errorFunctionType() {
diff --git a/pkg/analyzer/test/src/dart/resolution/record_type_annotation_test.dart b/pkg/analyzer/test/src/dart/resolution/record_type_annotation_test.dart
new file mode 100644
index 0000000..a228671
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/resolution/record_type_annotation_test.dart
@@ -0,0 +1,376 @@
+// 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(RecordTypeAnnotationTest);
+ });
+}
+
+@reflectiveTest
+class RecordTypeAnnotationTest extends PubPackageResolutionTest {
+ test_class_method_formalParameter() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ void foo((int, String) a) {}
+}
+''');
+
+ final node = findNode.recordTypeAnnotation('(int');
+ assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+ leftParenthesis: (
+ positionalFields
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: int
+ staticElement: dart:core::@class::int
+ staticType: null
+ type: int
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: String
+ staticElement: dart:core::@class::String
+ staticType: null
+ type: String
+ rightParenthesis: )
+ type: (int, String)
+''');
+ }
+
+ test_class_method_returnType() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ (int, String) foo() => throw 0;
+}
+''');
+
+ final node = findNode.recordTypeAnnotation('(int');
+ assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+ leftParenthesis: (
+ positionalFields
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: int
+ staticElement: dart:core::@class::int
+ staticType: null
+ type: int
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: String
+ staticElement: dart:core::@class::String
+ staticType: null
+ type: String
+ rightParenthesis: )
+ type: (int, String)
+''');
+ }
+
+ test_localFunction_formalParameter() async {
+ await assertNoErrorsInCode(r'''
+void f() {
+ // ignore:unused_element
+ void g((int, String) a) {}
+}
+''');
+
+ final node = findNode.recordTypeAnnotation('(int');
+ assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+ leftParenthesis: (
+ positionalFields
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: int
+ staticElement: dart:core::@class::int
+ staticType: null
+ type: int
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: String
+ staticElement: dart:core::@class::String
+ staticType: null
+ type: String
+ rightParenthesis: )
+ type: (int, String)
+''');
+ }
+
+ test_localFunction_returnType() async {
+ await assertNoErrorsInCode(r'''
+void f() {
+ // ignore:unused_element
+ (int, String) g() => throw 0;
+}
+''');
+
+ final node = findNode.recordTypeAnnotation('(int');
+ assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+ leftParenthesis: (
+ positionalFields
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: int
+ staticElement: dart:core::@class::int
+ staticType: null
+ type: int
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: String
+ staticElement: dart:core::@class::String
+ staticType: null
+ type: String
+ rightParenthesis: )
+ type: (int, String)
+''');
+ }
+
+ test_localVariable_mixed() async {
+ await assertNoErrorsInCode(r'''
+void f() {
+ // ignore:unused_local_variable
+ (int, String, {bool f3}) x;
+}
+''');
+
+ final node = findNode.recordTypeAnnotation('(int');
+ assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+ leftParenthesis: (
+ positionalFields
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: int
+ staticElement: dart:core::@class::int
+ staticType: null
+ type: int
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: String
+ staticElement: dart:core::@class::String
+ staticType: null
+ type: String
+ namedFields: RecordTypeAnnotationNamedFields
+ leftBracket: {
+ fields
+ RecordTypeAnnotationNamedField
+ type: NamedType
+ name: SimpleIdentifier
+ token: bool
+ staticElement: dart:core::@class::bool
+ staticType: null
+ type: bool
+ name: f3
+ rightBracket: }
+ rightParenthesis: )
+ type: (int, String, {bool f3})
+''');
+ }
+
+ test_localVariable_named() async {
+ await assertNoErrorsInCode(r'''
+void f() {
+ // ignore:unused_local_variable
+ ({int f1, String f2}) x;
+}
+''');
+
+ final node = findNode.recordTypeAnnotation('({int');
+ assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+ leftParenthesis: (
+ namedFields: RecordTypeAnnotationNamedFields
+ leftBracket: {
+ fields
+ RecordTypeAnnotationNamedField
+ type: NamedType
+ name: SimpleIdentifier
+ token: int
+ staticElement: dart:core::@class::int
+ staticType: null
+ type: int
+ name: f1
+ RecordTypeAnnotationNamedField
+ type: NamedType
+ name: SimpleIdentifier
+ token: String
+ staticElement: dart:core::@class::String
+ staticType: null
+ type: String
+ name: f2
+ rightBracket: }
+ rightParenthesis: )
+ type: ({int f1, String f2})
+''');
+ }
+
+ test_localVariable_positional() async {
+ await assertNoErrorsInCode(r'''
+void f() {
+ // ignore:unused_local_variable
+ (int, String) x;
+}
+''');
+
+ final node = findNode.recordTypeAnnotation('(int');
+ assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+ leftParenthesis: (
+ positionalFields
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: int
+ staticElement: dart:core::@class::int
+ staticType: null
+ type: int
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: String
+ staticElement: dart:core::@class::String
+ staticType: null
+ type: String
+ rightParenthesis: )
+ type: (int, String)
+''');
+ }
+
+ test_topFunction_formalParameter() async {
+ await assertNoErrorsInCode(r'''
+void f((int, String) a) {}
+''');
+
+ final node = findNode.recordTypeAnnotation('(int');
+ assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+ leftParenthesis: (
+ positionalFields
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: int
+ staticElement: dart:core::@class::int
+ staticType: null
+ type: int
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: String
+ staticElement: dart:core::@class::String
+ staticType: null
+ type: String
+ rightParenthesis: )
+ type: (int, String)
+''');
+ }
+
+ test_topFunction_nullable() async {
+ await assertNoErrorsInCode(r'''
+(int, String)? f() => throw 0;
+''');
+
+ final node = findNode.recordTypeAnnotation('(int');
+ assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+ leftParenthesis: (
+ positionalFields
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: int
+ staticElement: dart:core::@class::int
+ staticType: null
+ type: int
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: String
+ staticElement: dart:core::@class::String
+ staticType: null
+ type: String
+ rightParenthesis: )
+ question: ?
+ type: (int, String)?
+''');
+ }
+
+ test_topFunction_returnType() async {
+ await assertNoErrorsInCode(r'''
+(int, String) f() => throw 0;
+''');
+
+ final node = findNode.recordTypeAnnotation('(int');
+ assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+ leftParenthesis: (
+ positionalFields
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: int
+ staticElement: dart:core::@class::int
+ staticType: null
+ type: int
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: String
+ staticElement: dart:core::@class::String
+ staticType: null
+ type: String
+ rightParenthesis: )
+ type: (int, String)
+''');
+ }
+
+ @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/49769')
+ test_typeArgument() async {
+ await assertNoErrorsInCode(r'''
+void f() {
+ // ignore:unused_local_variable
+ final x = <(int, String)>[];
+}
+''');
+
+ final node = findNode.recordTypeAnnotation('(int');
+ assertResolvedNodeText(node, r'''
+RecordTypeAnnotation
+ leftParenthesis: (
+ positionalFields
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: int
+ staticElement: dart:core::@class::int
+ staticType: null
+ type: int
+ RecordTypeAnnotationPositionalField
+ type: NamedType
+ name: SimpleIdentifier
+ token: String
+ staticElement: dart:core::@class::String
+ staticType: null
+ type: String
+ rightParenthesis: )
+ type: (int, String)
+''');
+ }
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/test_all.dart b/pkg/analyzer/test/src/dart/resolution/test_all.dart
index c6c8dca..346c51d 100644
--- a/pkg/analyzer/test/src/dart/resolution/test_all.dart
+++ b/pkg/analyzer/test/src/dart/resolution/test_all.dart
@@ -63,6 +63,7 @@
import 'prefixed_identifier_test.dart' as prefixed_identifier;
import 'property_access_test.dart' as property_access;
import 'record_literal_test.dart' as record_literal;
+import 'record_type_annotation_test.dart' as record_type_annotation;
import 'simple_identifier_test.dart' as simple_identifier;
import 'super_formal_parameter_test.dart' as super_formal_parameter;
import 'top_level_variable_test.dart' as top_level_variable;
@@ -131,6 +132,7 @@
prefixed_identifier.main();
property_access.main();
record_literal.main();
+ record_type_annotation.main();
simple_identifier.main();
super_formal_parameter.main();
top_level_variable.main();
diff --git a/pkg/analyzer/test/src/summary/resolved_ast_printer.dart b/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
index 4320431..84a97b1 100644
--- a/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
+++ b/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
@@ -975,7 +975,7 @@
_writeln('RecordTypeAnnotation');
_withIndent(() {
_writeNamedChildEntities(node);
- _writeType('staticType', node.type);
+ _writeType('type', node.type);
});
}
diff --git a/tests/language_2/record_type_test.dart b/tests/language_2/record_type_test.dart
index 643c6cb..2ab7375 100644
--- a/tests/language_2/record_type_test.dart
+++ b/tests/language_2/record_type_test.dart
@@ -7,7 +7,6 @@
main() {
(int, int) record1 = (1, 2);
//^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
// [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
// [cfe] This requires the experimental 'records' language feature to be enabled.
// ^
@@ -18,7 +17,6 @@
// [cfe] This expression has type 'void' and can't be used.
(int x, int y) record1Named = (1, 2);
//^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
// [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
// [cfe] This requires the experimental 'records' language feature to be enabled.
// ^
@@ -30,7 +28,6 @@
(int, int, ) record2 = (1, 2);
//^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
// [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
// [cfe] This requires the experimental 'records' language feature to be enabled.
// ^
@@ -42,7 +39,6 @@
(int x, int y, ) record2Named = (1, 2);
//^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
// [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
// [cfe] This requires the experimental 'records' language feature to be enabled.
// ^
@@ -54,7 +50,6 @@
(int, int, {int a, int b}) record3 = (1, 2, a: 3, b: 4);
//^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
// [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
// [cfe] This requires the experimental 'records' language feature to be enabled.
// ^
@@ -66,7 +61,6 @@
(int x, int y, {int a, int b}) record3Named = (1, 2, a: 3, b: 4);
//^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
// [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
// [cfe] This requires the experimental 'records' language feature to be enabled.
// ^
@@ -78,7 +72,6 @@
(int, int, {int a, int b, }) record4 = (1, 2, a: 3, b: 4);
//^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
// [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
// [cfe] This requires the experimental 'records' language feature to be enabled.
// ^
@@ -90,7 +83,6 @@
(int x, int y, {int a, int b, }) record4Named = (1, 2, a: 3, b: 4);
//^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
// [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
// [cfe] This requires the experimental 'records' language feature to be enabled.
// ^
@@ -113,15 +105,12 @@
// [cfe] This expression has type 'void' and can't be used.
(int, int) Function ((int, int) a) z1 = ((int, int) a) { return (42, 42); };
//^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
// [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
// [cfe] This requires the experimental 'records' language feature to be enabled.
// ^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
// [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
// [cfe] This requires the experimental 'records' language feature to be enabled.
// ^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
// [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
// [cfe] This requires the experimental 'records' language feature to be enabled.
// ^
@@ -131,11 +120,9 @@
(int, int) foo((int, {bool b}) inputRecord, int x) {
// [error column 1, length 1]
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
// [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
// [cfe] This requires the experimental 'records' language feature to be enabled.
// ^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
// [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
// [cfe] This requires the experimental 'records' language feature to be enabled.
if (inputRecord.b) return (42, 42);
@@ -155,7 +142,6 @@
class Bar {
(int, int) foo(int x) => (42, 42);
//^
-// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
// [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
// [cfe] This requires the experimental 'records' language feature to be enabled.
// ^