Impl of INVALID_LITERAL_ANNOTATION

Bug: https://github.com/dart-lang/sdk/issues/34259
Change-Id: Ia5ca627a89dcb3f0fcd5ce74048df8fdf9dd9c9c
Reviewed-on: https://dart-review.googlesource.com/c/88283
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/dart/element/element.dart b/pkg/analyzer/lib/dart/element/element.dart
index 87e6f0f..547cb82 100644
--- a/pkg/analyzer/lib/dart/element/element.dart
+++ b/pkg/analyzer/lib/dart/element/element.dart
@@ -742,6 +742,10 @@
   /// subclasses as being immutable.
   bool get isImmutable;
 
+  /// Return `true` if this annotation marks the associated constructor as
+  /// being literal.
+  bool get isLiteral;
+
   /// Return `true` if this annotation marks the associated member as running
   /// a single test.
   bool get isIsTest;
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index c8510e5..41df45e 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -293,6 +293,7 @@
   HintCode.INVALID_FACTORY_METHOD_DECL,
   HintCode.INVALID_FACTORY_METHOD_IMPL,
   HintCode.INVALID_IMMUTABLE_ANNOTATION,
+  HintCode.INVALID_LITERAL_ANNOTATION,
   HintCode.INVALID_REQUIRED_PARAM,
   HintCode.INVALID_SEALED_ANNOTATION,
   HintCode.INVALID_USE_OF_PROTECTED_MEMBER,
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 87f0d4d..5490be9 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -2437,6 +2437,10 @@
   /// as being immutable.
   static String _IMMUTABLE_VARIABLE_NAME = "immutable";
 
+  /// The name of the top-level variable used to mark a constructor as being
+  /// literal.
+  static String _LITERAL_VARIABLE_NAME = "literal";
+
   /// The name of the top-level variable used to mark a function as running
   /// a single test.
   static String _IS_TEST_VARIABLE_NAME = "isTest";
@@ -2563,6 +2567,12 @@
       element.library?.name == _META_LIB_NAME;
 
   @override
+  bool get isLiteral =>
+      element is PropertyAccessorElement &&
+      element.name == _LITERAL_VARIABLE_NAME &&
+      element.library?.name == _META_LIB_NAME;
+
+  @override
   bool get isIsTest =>
       element is PropertyAccessorElement &&
       element.name == _IS_TEST_VARIABLE_NAME &&
diff --git a/pkg/analyzer/lib/src/dart/error/hint_codes.dart b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
index 281600e..07700ec 100644
--- a/pkg/analyzer/lib/src/dart/error/hint_codes.dart
+++ b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
@@ -214,6 +214,14 @@
       "Only classes can be annotated as being immutable.");
 
   /**
+   * This hint is generated anywhere a @literal annotation is associated with
+   * anything other than a const constructor.
+   */
+  static const HintCode INVALID_LITERAL_ANNOTATION = const HintCode(
+      'INVALID_LITERAL_ANNOTATION',
+      "Only const constructors can be annotated as being literal.");
+
+  /**
    * This hint is generated anywhere where `@required` annotates a non named
    * parameter or a named parameter with default value.
    *
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 00ccf82..3696609 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -320,16 +320,21 @@
             .reportErrorForNode(HintCode.INVALID_FACTORY_ANNOTATION, node, []);
       }
     } else if (element?.isImmutable == true) {
-      AstNode parent = node.parent;
       if (parent is! ClassOrMixinDeclaration && parent is! ClassTypeAlias) {
         _errorReporter.reportErrorForNode(
             HintCode.INVALID_IMMUTABLE_ANNOTATION, node, []);
       }
-    }
-    if (node.elementAnnotation?.isSealed == true &&
-        !(parent is ClassDeclaration || parent is ClassTypeAlias)) {
-      _errorReporter.reportErrorForNode(
-          HintCode.INVALID_SEALED_ANNOTATION, node.parent, [node.element.name]);
+    } else if (element?.isLiteral == true) {
+      if (parent is! ConstructorDeclaration ||
+          (parent as ConstructorDeclaration).constKeyword == null) {
+        _errorReporter
+            .reportErrorForNode(HintCode.INVALID_LITERAL_ANNOTATION, node, []);
+      }
+    } else if (element?.isSealed == true) {
+      if (!(parent is ClassDeclaration || parent is ClassTypeAlias)) {
+        _errorReporter.reportErrorForNode(HintCode.INVALID_SEALED_ANNOTATION,
+            node.parent, [node.element.name]);
+      }
     }
     super.visitAnnotation(node);
   }
diff --git a/pkg/analyzer/test/generated/hint_code_test.dart b/pkg/analyzer/test/generated/hint_code_test.dart
index 83b3265..40e7b67 100644
--- a/pkg/analyzer/test/generated/hint_code_test.dart
+++ b/pkg/analyzer/test/generated/hint_code_test.dart
@@ -1723,6 +1723,32 @@
     verify([source]);
   }
 
+  test_invalidLiteralAnnotation_nonConstConstructor() async {
+    Source source = addSource(r'''
+import 'package:meta/meta.dart';
+class A {
+  @literal
+  A() {}
+}
+''');
+    await computeAnalysisResult(source);
+    assertErrors(source, [HintCode.INVALID_LITERAL_ANNOTATION]);
+    verify([source]);
+  }
+
+  test_invalidLiteralAnnotation_nonConstructor() async {
+    Source source = addSource(r'''
+import 'package:meta/meta.dart';
+class A {
+  @literal
+  void m() {}
+}
+''');
+    await computeAnalysisResult(source);
+    assertErrors(source, [HintCode.INVALID_LITERAL_ANNOTATION]);
+    verify([source]);
+  }
+
   test_invalidRequiredParam_on_named_parameter_with_default() async {
     Source source = addNamedSource('/lib1.dart', r'''
 import 'package:meta/meta.dart';
diff --git a/pkg/analyzer/test/generated/non_hint_code_test.dart b/pkg/analyzer/test/generated/non_hint_code_test.dart
index 9f1b7f6..c6a3028 100644
--- a/pkg/analyzer/test/generated/non_hint_code_test.dart
+++ b/pkg/analyzer/test/generated/non_hint_code_test.dart
@@ -26,10 +26,14 @@
 library meta;
 
 const _AlwaysThrows alwaysThrows = const _AlwaysThrows();
+const _Literal literal = const _Literal();
 
 class _AlwaysThrows {
   const _AlwaysThrows();
 }
+class _Literal {
+  const _Literal();
+}
 '''
       ]
     ]);
@@ -684,6 +688,19 @@
     verify([source]);
   }
 
+  test_invalidLiteralAnnotation_constConstructor() async {
+    Source source = addSource(r'''
+import 'package:meta/meta.dart';
+class A {
+  @literal
+  const A();
+}
+''');
+    await computeAnalysisResult(source);
+    assertNoErrors(source);
+    verify([source]);
+  }
+
   test_overrideOnNonOverridingField_inInterface() async {
     Source source = addSource(r'''
 class A {