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 {