Report duplicate set entries with a different error
It will take some time until the CFE will take on reporting this error (and
duplicate keys in maps).
For now: detect whether a map is used as the implementation of a set literal,
and if so report the error differently.
Change-Id: I9f657189c79a4532023f615aa91bf63602f27664
Reviewed-on: https://dart-review.googlesource.com/c/91102
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/common_elements.dart b/pkg/compiler/lib/src/common_elements.dart
index 9913a66..124cfce 100644
--- a/pkg/compiler/lib/src/common_elements.dart
+++ b/pkg/compiler/lib/src/common_elements.dart
@@ -61,6 +61,9 @@
/// The `Map` class defined in 'dart:core';
ClassEntity get mapClass;
+ /// The `Set` class defined in 'dart:core';
+ ClassEntity get unmodifiableSetClass;
+
/// The `Iterable` class defined in 'dart:core';
ClassEntity get iterableClass;
@@ -653,6 +656,11 @@
ClassEntity _mapClass;
ClassEntity get mapClass => _mapClass ??= _findClass(coreLibrary, 'Map');
+ /// The `_UnmodifiableSet` class defined in 'dart:collection';
+ ClassEntity _unmodifiableSetClass;
+ ClassEntity get unmodifiableSetClass => _unmodifiableSetClass ??=
+ _findClass(_env.lookupLibrary(Uris.dart_collection), '_UnmodifiableSet');
+
/// The `Iterable` class defined in 'dart:core';
ClassEntity _iterableClass;
ClassEntity get iterableClass =>
diff --git a/pkg/compiler/lib/src/constants/evaluation.dart b/pkg/compiler/lib/src/constants/evaluation.dart
index ff9355f..386a2ee 100644
--- a/pkg/compiler/lib/src/constants/evaluation.dart
+++ b/pkg/compiler/lib/src/constants/evaluation.dart
@@ -24,6 +24,12 @@
/// Type in the enclosing constructed
InterfaceType get enclosingConstructedType;
+ /// Whether the immediate parent is a set literal.
+ ///
+ /// Used to distinguish map-literal from set-literal errors. This will be
+ /// removed once the CFE reports errors on constants.
+ bool get immediateUnderSetLiteral;
+
/// Read environments string passed in using the '-Dname=value' option.
String readFromEnvironment(String name);
@@ -55,6 +61,8 @@
ConstantValue evaluateConstructor(ConstructorEntity constructor,
InterfaceType type, ConstantValue evaluate());
+ ConstantValue evaluateMapBody(ConstantValue evaluate());
+
ConstantValue evaluateField(FieldEntity field, ConstantValue evaluate());
/// `true` if assertions are enabled.
@@ -70,6 +78,7 @@
abstract class EvaluationEnvironmentBase implements EvaluationEnvironment {
Link<Spannable> _spannableStack = const Link<Spannable>();
InterfaceType enclosingConstructedType;
+ bool immediateUnderSetLiteral = false;
final Set<FieldEntity> _currentlyEvaluatedFields = new Set<FieldEntity>();
final bool constantRequired;
@@ -117,13 +126,26 @@
_spannableStack = _spannableStack.prepend(constructor);
var old = enclosingConstructedType;
enclosingConstructedType = type;
+ if (type.element == commonElements.unmodifiableSetClass) {
+ immediateUnderSetLiteral = true;
+ }
ConstantValue result = evaluate();
+ // All const set literals have as an immediate child a const map. The map
+ // evaluate method calls evaluateMapBody and reset this flag immediately.
+ // Because there are no other children, the flag is kept false.
+ assert(!immediateUnderSetLiteral);
enclosingConstructedType = old;
_spannableStack = _spannableStack.tail;
return result;
}
@override
+ ConstantValue evaluateMapBody(ConstantValue evaluate()) {
+ immediateUnderSetLiteral = false;
+ return evaluate();
+ }
+
+ @override
void reportError(
ConstantExpression expression, MessageKind kind, Map arguments) {
if (constantRequired) {
diff --git a/pkg/compiler/lib/src/constants/expressions.dart b/pkg/compiler/lib/src/constants/expressions.dart
index 60a18ca..82f640d 100644
--- a/pkg/compiler/lib/src/constants/expressions.dart
+++ b/pkg/compiler/lib/src/constants/expressions.dart
@@ -433,23 +433,32 @@
@override
ConstantValue evaluate(
EvaluationEnvironment environment, ConstantSystem constantSystem) {
- Map<ConstantValue, ConstantValue> map = <ConstantValue, ConstantValue>{};
- for (int i = 0; i < keys.length; i++) {
- ConstantValue key = keys[i].evaluate(environment, constantSystem);
- if (!key.isConstant) {
- return new NonConstantValue();
+ // TODO(sigmund): delete once the CFE provides these error messages.
+ bool isSetLiteral = environment.immediateUnderSetLiteral;
+ return environment.evaluateMapBody(() {
+ Map<ConstantValue, ConstantValue> map = <ConstantValue, ConstantValue>{};
+ for (int i = 0; i < keys.length; i++) {
+ ConstantValue key = keys[i].evaluate(environment, constantSystem);
+ if (!key.isConstant) {
+ return new NonConstantValue();
+ }
+ ConstantValue value = values[i].evaluate(environment, constantSystem);
+ if (!value.isConstant) {
+ return new NonConstantValue();
+ }
+ if (map.containsKey(key)) {
+ environment.reportError(
+ keys[i],
+ isSetLiteral
+ ? MessageKind.EQUAL_SET_ENTRY
+ : MessageKind.EQUAL_MAP_ENTRY_KEY,
+ {});
+ }
+ map[key] = value;
}
- ConstantValue value = values[i].evaluate(environment, constantSystem);
- if (!value.isConstant) {
- return new NonConstantValue();
- }
- if (map.containsKey(key)) {
- environment.reportError(keys[i], MessageKind.EQUAL_MAP_ENTRY_KEY, {});
- }
- map[key] = value;
- }
- return constantSystem.createMap(environment.commonElements, type,
- map.keys.toList(), map.values.toList());
+ return constantSystem.createMap(environment.commonElements, type,
+ map.keys.toList(), map.values.toList());
+ });
}
ConstantExpression apply(NormalizedArguments arguments) {
diff --git a/pkg/compiler/lib/src/diagnostics/messages.dart b/pkg/compiler/lib/src/diagnostics/messages.dart
index c8131bb..a3532c9 100644
--- a/pkg/compiler/lib/src/diagnostics/messages.dart
+++ b/pkg/compiler/lib/src/diagnostics/messages.dart
@@ -37,6 +37,7 @@
CYCLIC_COMPILE_TIME_CONSTANTS,
DIRECTLY_THROWING_NSM,
EQUAL_MAP_ENTRY_KEY,
+ EQUAL_SET_ENTRY,
EXTRANEOUS_MODIFIER,
EXTRANEOUS_MODIFIER_REPLACE,
FORIN_NOT_ASSIGNABLE,
@@ -433,6 +434,16 @@
}"""
]),
+ MessageKind.EQUAL_SET_ENTRY: const MessageTemplate(
+ MessageKind.EQUAL_SET_ENTRY, "An entry appears twice in the set.",
+ howToFix: "Try removing one of the entries.",
+ examples: const [
+ """
+main() {
+ var m = const {'foo', 'bar', 'foo'};
+}"""
+ ]),
+
MessageKind.COMPILER_CRASHED: const MessageTemplate(
MessageKind.COMPILER_CRASHED,
"The compiler crashed when compiling this element."),
diff --git a/tests/compiler/dart2js/model/constant_expression_evaluate_test.dart b/tests/compiler/dart2js/model/constant_expression_evaluate_test.dart
index 3b16a86..c35cadf 100644
--- a/tests/compiler/dart2js/model/constant_expression_evaluate_test.dart
+++ b/tests/compiler/dart2js/model/constant_expression_evaluate_test.dart
@@ -67,6 +67,8 @@
bool get checkCasts => true;
+ bool get immediateUnderSetLiteral => _environment.immediateUnderSetLiteral;
+
@override
String readFromEnvironment(String name) => env[name];
@@ -130,6 +132,11 @@
}
@override
+ ConstantValue evaluateMapBody(ConstantValue evaluate()) {
+ return _environment.evaluateMapBody(evaluate);
+ }
+
+ @override
bool get enableAssertions => true;
}