Generate an error if a constant set contains two elements with the same value
Change-Id: Ie359a1ea639e9c928df32c727bcd2f075f92033f
Reviewed-on: https://dart-review.googlesource.com/c/88425
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 41df45e..7ffa3f6 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -585,6 +585,7 @@
StaticWarningCode.CONCRETE_CLASS_WITH_ABSTRACT_MEMBER,
StaticWarningCode.CONST_WITH_ABSTRACT_CLASS,
StaticWarningCode.EQUAL_KEYS_IN_MAP,
+ StaticWarningCode.EQUAL_VALUES_IN_CONST_SET,
StaticWarningCode.EXPORT_DUPLICATED_LIBRARY_NAMED,
StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS,
StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS_COULD_BE_NAMED,
diff --git a/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart b/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart
index eaba4b2..30e9486 100644
--- a/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart
+++ b/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart
@@ -180,10 +180,8 @@
if (keyResult != null) {
_reportErrorIfFromDeferredLibrary(key,
CompileTimeErrorCode.NON_CONSTANT_MAP_KEY_FROM_DEFERRED_LIBRARY);
- if (keys.contains(keyResult)) {
+ if (!keys.add(keyResult)) {
invalidKeys.add(key);
- } else {
- keys.add(keyResult);
}
DartType type = keyResult.type;
if (_implementsEqualsWhenNotAllowed(type)) {
@@ -203,10 +201,8 @@
DartObjectImpl result = key
.accept(new ConstantVisitor(_evaluationEngine, subErrorReporter));
if (result != null) {
- if (keys.contains(result)) {
+ if (!keys.add(result)) {
invalidKeys.add(key);
- } else {
- keys.add(result);
}
} else {
reportEqualKeys = false;
@@ -214,8 +210,7 @@
}
}
if (reportEqualKeys) {
- int length = invalidKeys.length;
- for (int i = 0; i < length; i++) {
+ for (int i = 0; i < invalidKeys.length; i++) {
_errorReporter.reportErrorForNode(
StaticWarningCode.EQUAL_KEYS_IN_MAP, invalidKeys[i]);
}
@@ -231,16 +226,20 @@
@override
void visitSetLiteral(SetLiteral node) {
super.visitSetLiteral(node);
+ HashSet<DartObject> elements = new HashSet<DartObject>();
+ List<Expression> invalidElements = new List<Expression>();
if (node.isConst) {
- DartObjectImpl result;
for (Expression element in node.elements) {
- result =
+ DartObjectImpl result =
_validate(element, CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT);
if (result != null) {
_reportErrorIfFromDeferredLibrary(
element,
CompileTimeErrorCode
.NON_CONSTANT_SET_ELEMENT_FROM_DEFERRED_LIBRARY);
+ if (!elements.add(result)) {
+ invalidElements.add(element);
+ }
DartType type = result.type;
if (_implementsEqualsWhenNotAllowed(type)) {
_errorReporter.reportErrorForNode(
@@ -250,6 +249,10 @@
}
}
}
+ for (var invalidElement in invalidElements) {
+ _errorReporter.reportErrorForNode(
+ StaticWarningCode.EQUAL_VALUES_IN_CONST_SET, invalidElement);
+ }
}
}
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index a3cff21..b6f9cc0 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -3661,6 +3661,14 @@
'EQUAL_KEYS_IN_MAP', "Two keys in a map literal can't be equal.");
/**
+ * It is a compile-time error if any two of the values in a constant set are
+ * equal according to `==`.
+ */
+ static const StaticWarningCode EQUAL_VALUES_IN_CONST_SET =
+ const StaticWarningCode('EQUAL_VALUES_IN_CONST_SET',
+ "Two values in a constant set can't be equal.");
+
+ /**
* 14.2 Exports: It is a static warning to export two different libraries with
* the same name.
*
diff --git a/pkg/analyzer/test/generated/static_warning_code_test.dart b/pkg/analyzer/test/generated/static_warning_code_test.dart
index 08e53a9..f5b8968 100644
--- a/pkg/analyzer/test/generated/static_warning_code_test.dart
+++ b/pkg/analyzer/test/generated/static_warning_code_test.dart
@@ -4,6 +4,7 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/error/error.dart';
+import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/source_io.dart';
import 'package:test/test.dart';
@@ -13,11 +14,54 @@
main() {
defineReflectiveSuite(() {
+ defineReflectiveTests(EqualValuesInConstSetTest);
defineReflectiveTests(StaticWarningCodeTest);
});
}
@reflectiveTest
+class EqualValuesInConstSetTest extends ResolverTestCase {
+ @override
+ List<String> get enabledExperiments => [EnableString.set_literals];
+
+ @override
+ bool get enableNewAnalysisDriver => true;
+
+ test_simpleValues() async {
+ Source source = addSource('var s = const {0, 1, 0};');
+ await computeAnalysisResult(source);
+ assertErrors(source, [StaticWarningCode.EQUAL_VALUES_IN_CONST_SET]);
+ verify([source]);
+ }
+
+ test_valuesWithEqualTypeParams() async {
+ Source source = addSource(r'''
+class A<T> {
+ const A();
+}
+var s = const {A<int>(), A<int>()};
+''');
+ await computeAnalysisResult(source);
+ assertErrors(source, [StaticWarningCode.EQUAL_VALUES_IN_CONST_SET]);
+ verify([source]);
+ }
+
+ test_valuesWithUnequalTypeParams() async {
+ // No error should be produced because A<int> and A<num> are different
+ // types.
+ Source source = addSource(r'''
+class A<T> {
+ const A();
+}
+const s = {A<int>(), A<num>()};
+''');
+ await computeAnalysisResult(source);
+ assertNoErrors(source);
+ verify([source]);
+ }
+}
+
+@reflectiveTest
class StaticWarningCodeTest extends ResolverTestCase {
fail_argumentTypeNotAssignable_tearOff_required() async {
Source source = addSource(r'''