[fasta] Add support for annotations on enum values
Fixes #33083
Bug: https://github.com/dart-lang/sdk/issues/33083
Change-Id: If278ee3c59489944e2e821ae48ba3cb363dbbdd1
Reviewed-on: https://dart-review.googlesource.com/56496
Commit-Queue: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Peter von der Ahé <ahe@google.com>
Reviewed-by: Dan Rubel <danrubel@google.com>
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
index e1d7ba7..5f91fd5 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -128,17 +128,6 @@
}
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Null> codeAnnotationOnEnumConstant = messageAnnotationOnEnumConstant;
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const MessageCode messageAnnotationOnEnumConstant = const MessageCode(
- "AnnotationOnEnumConstant",
- analyzerCode: "ANNOTATION_ON_ENUM_CONSTANT",
- dart2jsCode: "*fatal*",
- message: r"""Enum constants can't have annotations.""",
- tip: r"""Try removing the annotation.""");
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<
Message Function(
int
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_enum_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_enum_builder.dart
index 18cca9e..ef2a750 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_enum_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_enum_builder.dart
@@ -159,14 +159,15 @@
charEndOffset);
members["toString"] = toStringBuilder;
String className = name;
- for (int i = 0; i < constantNamesAndOffsetsAndDocs.length; i += 3) {
- String name = constantNamesAndOffsetsAndDocs[i];
- int charOffset = constantNamesAndOffsetsAndDocs[i + 1];
- String documentationComment = constantNamesAndOffsetsAndDocs[i + 2];
+ for (int i = 0; i < constantNamesAndOffsetsAndDocs.length; i += 4) {
+ List<MetadataBuilder> metadata = constantNamesAndOffsetsAndDocs[i];
+ String name = constantNamesAndOffsetsAndDocs[i + 1];
+ int charOffset = constantNamesAndOffsetsAndDocs[i + 2];
+ String documentationComment = constantNamesAndOffsetsAndDocs[i + 3];
if (members.containsKey(name)) {
parent.addCompileTimeError(templateDuplicatedName.withArguments(name),
charOffset, noLength, parent.fileUri);
- constantNamesAndOffsetsAndDocs[i] = null;
+ constantNamesAndOffsetsAndDocs[i + 1] = null;
continue;
}
if (name == className) {
@@ -175,11 +176,18 @@
charOffset,
noLength,
parent.fileUri);
- constantNamesAndOffsetsAndDocs[i] = null;
+ constantNamesAndOffsetsAndDocs[i + 1] = null;
continue;
}
- KernelFieldBuilder fieldBuilder = new KernelFieldBuilder(null, selfType,
- name, constMask | staticMask, parent, charOffset, null, true);
+ KernelFieldBuilder fieldBuilder = new KernelFieldBuilder(
+ metadata,
+ selfType,
+ name,
+ constMask | staticMask,
+ parent,
+ charOffset,
+ null,
+ true);
metadataCollector?.setDocumentationComment(
fieldBuilder.target, documentationComment);
members[name] = fieldBuilder;
@@ -235,8 +243,8 @@
toStringBuilder.body = new ReturnStatement(
new DirectPropertyGet(new ThisExpression(), nameField));
List<Expression> values = <Expression>[];
- for (int i = 0; i < constantNamesAndOffsetsAndDocs.length; i += 3) {
- String name = constantNamesAndOffsetsAndDocs[i];
+ for (int i = 0; i < constantNamesAndOffsetsAndDocs.length; i += 4) {
+ String name = constantNamesAndOffsetsAndDocs[i + 1];
if (name != null) {
KernelFieldBuilder builder = this[name];
values.add(new StaticGet(builder.build(libraryBuilder)));
@@ -273,8 +281,8 @@
..parent = constructor);
}
int index = 0;
- for (int i = 0; i < constantNamesAndOffsetsAndDocs.length; i += 3) {
- String constant = constantNamesAndOffsetsAndDocs[i];
+ for (int i = 0; i < constantNamesAndOffsetsAndDocs.length; i += 4) {
+ String constant = constantNamesAndOffsetsAndDocs[i + 1];
if (constant != null) {
KernelFieldBuilder field = this[constant];
field.build(libraryBuilder);
diff --git a/pkg/front_end/lib/src/fasta/parser/parser.dart b/pkg/front_end/lib/src/fasta/parser/parser.dart
index 05f512f..08e0144 100644
--- a/pkg/front_end/lib/src/fasta/parser/parser.dart
+++ b/pkg/front_end/lib/src/fasta/parser/parser.dart
@@ -1596,7 +1596,7 @@
/// ```
/// enumType:
- /// metadata 'enum' id '{' id [',' id]* [','] '}'
+ /// metadata 'enum' id '{' metadata id [',' metadata id]* [','] '}'
/// ;
/// ```
Token parseEnum(Token token) {
@@ -1618,10 +1618,6 @@
break;
}
token = parseMetadataStar(token);
- if (!identical(token.next, next)) {
- listener.handleRecoverableError(
- fasta.messageAnnotationOnEnumConstant, next, token);
- }
token = ensureIdentifier(token, IdentifierContext.enumValueDeclaration);
next = token.next;
count++;
diff --git a/pkg/front_end/lib/src/fasta/source/diet_listener.dart b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
index 4adca66..58f6ebc 100644
--- a/pkg/front_end/lib/src/fasta/source/diet_listener.dart
+++ b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
@@ -9,6 +9,7 @@
AsyncMarker,
Class,
Expression,
+ Field,
InterfaceType,
Library,
LibraryDependency,
@@ -33,8 +34,7 @@
import '../kernel/kernel_body_builder.dart' show KernelBodyBuilder;
-import '../parser.dart'
- show Assert, IdentifierContext, MemberKind, Parser, optional;
+import '../parser.dart' show Assert, MemberKind, Parser, optional;
import '../problems.dart' show internalProblem, unexpected;
@@ -274,15 +274,6 @@
}
@override
- void handleIdentifier(Token token, IdentifierContext context) {
- if (context == IdentifierContext.enumValueDeclaration) {
- // Discard the metadata.
- pop();
- }
- super.handleIdentifier(token, context);
- }
-
- @override
void handleQualified(Token period) {
debugEvent("handleQualified");
String suffix = pop();
@@ -643,11 +634,13 @@
void endEnum(Token enumKeyword, Token leftBrace, int count) {
debugEvent("Enum");
- discard(count); // values
+ List metadataAndValues = new List.filled(count * 2, null, growable: true);
+ popList(count * 2, metadataAndValues);
+
String name = pop();
Token metadata = pop();
- Builder enumBuilder = lookupBuilder(enumKeyword, null, name);
+ ClassBuilder enumBuilder = lookupBuilder(enumKeyword, null, name);
Class target = enumBuilder.target;
var metadataConstants = parseMetadata(enumBuilder, metadata);
if (metadataConstants != null) {
@@ -655,6 +648,17 @@
target.addAnnotation(metadataConstant);
}
}
+ for (int i = 0; i < metadataAndValues.length; i += 2) {
+ Token metadata = metadataAndValues[i];
+ String valueName = metadataAndValues[i + 1];
+ Builder builder = enumBuilder.scope.local[valueName];
+ if (metadata != null) {
+ Field field = builder.target;
+ for (var annotation in parseMetadata(builder, metadata)) {
+ field.addAnnotation(annotation);
+ }
+ }
+ }
checkEmpty(enumKeyword.charOffset);
}
diff --git a/pkg/front_end/lib/src/fasta/source/outline_builder.dart b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
index f2f3dfd..64f66b9 100644
--- a/pkg/front_end/lib/src/fasta/source/outline_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
@@ -276,8 +276,6 @@
@override
void handleIdentifier(Token token, IdentifierContext context) {
if (context == IdentifierContext.enumValueDeclaration) {
- // Discard the metadata.
- pop();
super.handleIdentifier(token, context);
push(token.charOffset);
String documentationComment = getDocumentationComment(token);
@@ -944,7 +942,7 @@
void endEnum(Token enumKeyword, Token leftBrace, int count) {
String documentationComment = getDocumentationComment(enumKeyword);
List<Object> constantNamesAndOffsets = popList(
- count * 3, new List<Object>.filled(count * 3, null, growable: true));
+ count * 4, new List<Object>.filled(count * 4, null, growable: true));
int charOffset = pop();
String name = pop();
List<MetadataBuilder> metadata = pop();
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 1e8e061..440aab5 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -1940,12 +1940,6 @@
ExpressionNotMetadata:
template: "This can't be used as metadata; metadata should be a reference to a compile-time constant variable, or a call to a constant constructor."
-AnnotationOnEnumConstant:
- template: "Enum constants can't have annotations."
- tip: "Try removing the annotation."
- analyzerCode: ANNOTATION_ON_ENUM_CONSTANT
- dart2jsCode: "*fatal*"
-
ExpectedAnInitializer:
template: "Expected an initializer."
analyzerCode: MISSING_INITIALIZER
diff --git a/pkg/front_end/testcases/annotation_on_enum_values.dart b/pkg/front_end/testcases/annotation_on_enum_values.dart
new file mode 100644
index 0000000..29dba81
--- /dev/null
+++ b/pkg/front_end/testcases/annotation_on_enum_values.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2018, 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.
+
+// This test checks that annotations on enum values are preserved by the
+// compiler.
+
+const int hest = 42;
+
+class Fisk<T> {
+ final T x;
+ const Fisk.fisk(this.x);
+}
+
+enum Foo {
+ @hest bar,
+ @Fisk.fisk(hest) baz,
+ cafebabe,
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/annotation_on_enum_values.dart.direct.expect b/pkg/front_end/testcases/annotation_on_enum_values.dart.direct.expect
new file mode 100644
index 0000000..246f4cb
--- /dev/null
+++ b/pkg/front_end/testcases/annotation_on_enum_values.dart.direct.expect
@@ -0,0 +1,27 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Fisk<T extends core::Object = dynamic> extends core::Object {
+ final field self::Fisk::T x;
+ const constructor fisk(self::Fisk::T x) → void
+ : self::Fisk::x = x, super core::Object::•()
+ ;
+}
+class Foo extends core::Object {
+ final field core::int index;
+ final field core::String _name;
+ static const field core::List<self::Foo> values = const <self::Foo>[self::Foo::bar, self::Foo::baz, self::Foo::cafebabe];
+ @self::hest
+ static const field self::Foo bar = const self::Foo::•(0, "Foo.bar");
+ @self::Fisk::fisk<dynamic>(self::hest)
+ static const field self::Foo baz = const self::Foo::•(1, "Foo.baz");
+ static const field self::Foo cafebabe = const self::Foo::•(2, "Foo.cafebabe");
+ const constructor •(core::int index, core::String _name) → void
+ : self::Foo::index = index, self::Foo::_name = _name, super core::Object::•()
+ ;
+ method toString() → core::String
+ return this.{=self::Foo::_name};
+}
+static const field core::int hest = 42;
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/annotation_on_enum_values.dart.direct.transformed.expect b/pkg/front_end/testcases/annotation_on_enum_values.dart.direct.transformed.expect
new file mode 100644
index 0000000..246f4cb
--- /dev/null
+++ b/pkg/front_end/testcases/annotation_on_enum_values.dart.direct.transformed.expect
@@ -0,0 +1,27 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Fisk<T extends core::Object = dynamic> extends core::Object {
+ final field self::Fisk::T x;
+ const constructor fisk(self::Fisk::T x) → void
+ : self::Fisk::x = x, super core::Object::•()
+ ;
+}
+class Foo extends core::Object {
+ final field core::int index;
+ final field core::String _name;
+ static const field core::List<self::Foo> values = const <self::Foo>[self::Foo::bar, self::Foo::baz, self::Foo::cafebabe];
+ @self::hest
+ static const field self::Foo bar = const self::Foo::•(0, "Foo.bar");
+ @self::Fisk::fisk<dynamic>(self::hest)
+ static const field self::Foo baz = const self::Foo::•(1, "Foo.baz");
+ static const field self::Foo cafebabe = const self::Foo::•(2, "Foo.cafebabe");
+ const constructor •(core::int index, core::String _name) → void
+ : self::Foo::index = index, self::Foo::_name = _name, super core::Object::•()
+ ;
+ method toString() → core::String
+ return this.{=self::Foo::_name};
+}
+static const field core::int hest = 42;
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/annotation_on_enum_values.dart.outline.expect b/pkg/front_end/testcases/annotation_on_enum_values.dart.outline.expect
new file mode 100644
index 0000000..02c5c32
--- /dev/null
+++ b/pkg/front_end/testcases/annotation_on_enum_values.dart.outline.expect
@@ -0,0 +1,25 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Fisk<T extends core::Object = dynamic> extends core::Object {
+ final field self::Fisk::T x;
+ const constructor fisk(self::Fisk::T x) → void
+ ;
+}
+class Foo extends core::Object {
+ final field core::int index;
+ final field core::String _name;
+ static const field core::List<self::Foo> values = const <self::Foo>[self::Foo::bar, self::Foo::baz, self::Foo::cafebabe];
+ static const field self::Foo bar = const self::Foo::•(0, "Foo.bar");
+ static const field self::Foo baz = const self::Foo::•(1, "Foo.baz");
+ static const field self::Foo cafebabe = const self::Foo::•(2, "Foo.cafebabe");
+ const constructor •(core::int index, core::String _name) → void
+ : self::Foo::index = index, self::Foo::_name = _name, super core::Object::•()
+ ;
+ method toString() → core::String
+ return this.{=self::Foo::_name};
+}
+static const field core::int hest;
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/annotation_on_enum_values.dart.strong.expect b/pkg/front_end/testcases/annotation_on_enum_values.dart.strong.expect
new file mode 100644
index 0000000..2c026ab
--- /dev/null
+++ b/pkg/front_end/testcases/annotation_on_enum_values.dart.strong.expect
@@ -0,0 +1,27 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Fisk<T extends core::Object = dynamic> extends core::Object {
+ final field self::Fisk::T x;
+ const constructor fisk(self::Fisk::T x) → void
+ : self::Fisk::x = x, super core::Object::•()
+ ;
+}
+class Foo extends core::Object {
+ final field core::int index;
+ final field core::String _name;
+ static const field core::List<self::Foo> values = const <self::Foo>[self::Foo::bar, self::Foo::baz, self::Foo::cafebabe];
+ @self::hest
+ static const field self::Foo bar = const self::Foo::•(0, "Foo.bar");
+ @self::Fisk::fisk<core::int>(self::hest)
+ static const field self::Foo baz = const self::Foo::•(1, "Foo.baz");
+ static const field self::Foo cafebabe = const self::Foo::•(2, "Foo.cafebabe");
+ const constructor •(core::int index, core::String _name) → void
+ : self::Foo::index = index, self::Foo::_name = _name, super core::Object::•()
+ ;
+ method toString() → core::String
+ return this.{=self::Foo::_name};
+}
+static const field core::int hest = 42;
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/annotation_on_enum_values.dart.strong.transformed.expect b/pkg/front_end/testcases/annotation_on_enum_values.dart.strong.transformed.expect
new file mode 100644
index 0000000..2c026ab
--- /dev/null
+++ b/pkg/front_end/testcases/annotation_on_enum_values.dart.strong.transformed.expect
@@ -0,0 +1,27 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Fisk<T extends core::Object = dynamic> extends core::Object {
+ final field self::Fisk::T x;
+ const constructor fisk(self::Fisk::T x) → void
+ : self::Fisk::x = x, super core::Object::•()
+ ;
+}
+class Foo extends core::Object {
+ final field core::int index;
+ final field core::String _name;
+ static const field core::List<self::Foo> values = const <self::Foo>[self::Foo::bar, self::Foo::baz, self::Foo::cafebabe];
+ @self::hest
+ static const field self::Foo bar = const self::Foo::•(0, "Foo.bar");
+ @self::Fisk::fisk<core::int>(self::hest)
+ static const field self::Foo baz = const self::Foo::•(1, "Foo.baz");
+ static const field self::Foo cafebabe = const self::Foo::•(2, "Foo.cafebabe");
+ const constructor •(core::int index, core::String _name) → void
+ : self::Foo::index = index, self::Foo::_name = _name, super core::Object::•()
+ ;
+ method toString() → core::String
+ return this.{=self::Foo::_name};
+}
+static const field core::int hest = 42;
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/ast_builder.status b/pkg/front_end/testcases/ast_builder.status
index 1301285..6e166ec 100644
--- a/pkg/front_end/testcases/ast_builder.status
+++ b/pkg/front_end/testcases/ast_builder.status
@@ -4,6 +4,7 @@
# Status file for the ast_builder_test.dart test suite. This is testing
# generating analyzer ASTs and connecting up type inference information.
+annotation_on_enum_values: Crash
annotation_top: Crash
argument_mismatch: Fail
bug32629: Fail