Extract _ConstLiteralVerifier and split it into literal / operation methods.
R=brianwilkerson@google.com
Change-Id: I4e3412625cd8c1350bdf7dbc32d92000bc53600e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/96986
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart b/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart
index 7234756..d2cbd86 100644
--- a/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart
+++ b/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart
@@ -2,8 +2,6 @@
// 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.
-import 'dart:collection';
-
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
@@ -146,21 +144,15 @@
if (node.isConst) {
InterfaceType nodeType = node.staticType;
DartType elementType = nodeType.typeArguments[0];
- // Dummy sets of keys to accommodate the fact that
- // `_validateCollectionElement` handles map literals.
- HashSet<DartObject> keys = new HashSet<DartObject>();
- List<Expression> invalidKeys = new List<Expression>();
+ var verifier = _ConstLiteralVerifier(
+ this,
+ isConst: true,
+ errorCode: CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT,
+ forList: true,
+ listElementType: elementType,
+ );
for (CollectionElement element in node.elements2) {
- bool isValid = _validateCollectionElement(element, true, keys,
- invalidKeys, CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT,
- forList: true, listElementType: elementType);
- if (isValid && element is Expression) {
- // TODO(brianwilkerson) Handle the other kinds of elements.
- _reportErrorIfFromDeferredLibrary(
- element,
- CompileTimeErrorCode
- .NON_CONSTANT_LIST_ELEMENT_FROM_DEFERRED_LIBRARY);
- }
+ verifier.verify(element);
}
}
}
@@ -175,50 +167,50 @@
void visitSetOrMapLiteral(SetOrMapLiteral node) {
super.visitSetOrMapLiteral(node);
bool isConst = node.isConst;
- HashSet<DartObject> keys = new HashSet<DartObject>();
- List<Expression> invalidKeys = new List<Expression>();
if (node.isSet) {
if (isConst) {
InterfaceType nodeType = node.staticType;
var elementType = nodeType.typeArguments[0];
- var uniqueValues = Set<DartObject>();
var duplicateElements = <Expression>[];
+ var verifier = _ConstLiteralVerifier(
+ this,
+ isConst: isConst,
+ errorCode: CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT,
+ forSet: true,
+ setElementType: elementType,
+ setUniqueValues: Set<DartObject>(),
+ setDuplicateElements: duplicateElements,
+ );
for (CollectionElement element in node.elements2) {
- _validateCollectionElement(element, isConst, keys, invalidKeys,
- CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT,
- forSet: true,
- setElementType: elementType,
- setElements: uniqueValues,
- setElementsDuplicate: duplicateElements);
+ verifier.verify(element);
}
- for (var invalidElement in duplicateElements) {
+ for (var duplicateElement in duplicateElements) {
_errorReporter.reportErrorForNode(
- StaticWarningCode.EQUAL_VALUES_IN_CONST_SET, invalidElement);
+ StaticWarningCode.EQUAL_VALUES_IN_CONST_SET,
+ duplicateElement,
+ );
}
}
} else if (node.isMap) {
bool reportEqualKeys = true;
+ var duplicateKeyElements = <Expression>[];
+ var verifier = _ConstLiteralVerifier(
+ this,
+ isConst: isConst,
+ errorCode: CompileTimeErrorCode.NON_CONSTANT_MAP_ELEMENT,
+ forMap: true,
+ mapUniqueKeys: Set<DartObject>(),
+ mapDuplicateKeyElements: duplicateKeyElements,
+ );
for (CollectionElement entry in node.elements2) {
- if (entry is MapLiteralEntry) {
- // TODO(mfairhurst): Change non-const error to a hint, and report
- // duplicates in constant evaluator instead.
- // TODO(mfairhurst): unify this with _validateCollectionElemet
- if (!_validateMapLiteralEntry(entry, isConst, keys, invalidKeys)) {
- reportEqualKeys = false;
- }
- } else {
- bool isValid = _validateCollectionElement(entry, isConst, keys,
- invalidKeys, CompileTimeErrorCode.NON_CONSTANT_MAP_ELEMENT,
- forMap: true);
- if (isValid) {
- // TODO(mfarihurst): handle deferred library checks
- }
- }
+ verifier.verify(entry);
}
if (reportEqualKeys) {
- for (int i = 0; i < invalidKeys.length; i++) {
+ for (var duplicateKeyElement in duplicateKeyElements) {
_errorReporter.reportErrorForNode(
- StaticWarningCode.EQUAL_KEYS_IN_MAP, invalidKeys[i]);
+ StaticWarningCode.EQUAL_KEYS_IN_MAP,
+ duplicateKeyElement,
+ );
}
}
}
@@ -437,178 +429,6 @@
return result;
}
- bool _validateCollectionElement(
- CollectionElement element,
- bool isConst,
- HashSet<DartObject> keys,
- List<Expression> invalidKeys,
- ErrorCode errorCode, {
- DartType listElementType,
- DartType mapKeyType,
- DartType mapValueType,
- DartType setElementType,
- Set<DartObject> setElements,
- List<CollectionElement> setElementsDuplicate,
- bool forList = false,
- bool forMap = false,
- bool forSet = false,
- }) {
- if (element is Expression) {
- if (!isConst) return true;
-
- var value = _validate(element, errorCode);
- if (value == null) return false;
-
- if (forList) {
- if (!_evaluationEngine.runtimeTypeMatch(value, listElementType)) {
- _errorReporter.reportErrorForNode(
- StaticWarningCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE,
- element,
- [value.type, listElementType],
- );
- return false;
- }
- }
-
- if (forSet) {
- if (!_evaluationEngine.runtimeTypeMatch(value, setElementType)) {
- _errorReporter.reportErrorForNode(
- StaticWarningCode.SET_ELEMENT_TYPE_NOT_ASSIGNABLE,
- element,
- [value.type, setElementType],
- );
- return false;
- }
-
- if (_implementsEqualsWhenNotAllowed(value.type)) {
- _errorReporter.reportErrorForNode(
- CompileTimeErrorCode.CONST_SET_ELEMENT_TYPE_IMPLEMENTS_EQUALS,
- element,
- [value.type.displayName]);
- return false;
- }
-
- _reportErrorIfFromDeferredLibrary(
- element,
- CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT_FROM_DEFERRED_LIBRARY,
- );
-
- if (!setElements.add(value)) {
- setElementsDuplicate.add(element);
- }
- }
-
- return true;
- } else if (element is ForElement) {
- if (isConst) {
- _errorReporter.reportErrorForNode(errorCode, element);
- return false;
- }
- return true;
- } else if (element is IfElement) {
- if (isConst) {
- DartObject conditionResult = _validate(element.condition, errorCode);
- bool conditionValue = conditionResult?.toBoolValue();
- if (conditionValue == null) {
- // The errors have already been reported.
- return false;
- } else if (conditionValue) {
- return _validateCollectionElement(
- element.thenElement,
- isConst,
- keys,
- invalidKeys,
- errorCode,
- forList: forList,
- forMap: forMap,
- forSet: forSet,
- listElementType: listElementType,
- mapKeyType: mapKeyType,
- mapValueType: mapValueType,
- setElementType: setElementType,
- setElements: setElements,
- setElementsDuplicate: setElementsDuplicate,
- ) !=
- null;
- } else if (element.elseElement != null) {
- return _validateCollectionElement(
- element.elseElement,
- isConst,
- keys,
- invalidKeys,
- errorCode,
- forList: forList,
- forMap: forMap,
- forSet: forSet,
- listElementType: listElementType,
- mapKeyType: mapKeyType,
- mapValueType: mapValueType,
- setElementType: setElementType,
- setElements: setElements,
- setElementsDuplicate: setElementsDuplicate,
- ) !=
- null;
- } else {
- return true;
- }
- }
- return true;
- } else if (element is MapLiteralEntry) {
- return _validateMapLiteralEntry(element, isConst, keys, invalidKeys);
- } else if (element is SpreadElement) {
- if (!isConst) return true;
-
- var value = _validate(element.expression, errorCode);
- if (value == null) return false;
-
- if (forList || forSet) {
- var listValue = value.toListValue();
- var setValue = value.toSetValue();
- if (listValue != null ||
- setValue != null ||
- value.isNull && _isNullableSpread(element)) {
- if (listValue != null) {
- var elementType = value.type.typeArguments[0];
- if (_implementsEqualsWhenNotAllowed(elementType)) {
- _errorReporter.reportErrorForNode(
- CompileTimeErrorCode.CONST_SET_ELEMENT_TYPE_IMPLEMENTS_EQUALS,
- element,
- [elementType.displayName],
- );
- }
- }
- return true;
- }
- _errorReporter.reportErrorForNode(
- CompileTimeErrorCode.CONST_SPREAD_EXPECTED_LIST_OR_SET,
- element.expression,
- );
- return false;
- }
-
- if (forMap) {
- if (value.toMapValue() != null ||
- value.isNull && _isNullableSpread(element)) {
- return true;
- }
- _errorReporter.reportErrorForNode(
- CompileTimeErrorCode.CONST_SPREAD_EXPECTED_MAP,
- element.expression,
- );
- return false;
- }
-
- return true;
- }
- throw new UnsupportedError(
- 'Unhandled type of collection element: ${element.runtimeType}');
- }
-
- static bool _isNullableSpread(SpreadElement element) {
- return element.spreadOperator.type ==
- TokenType.PERIOD_PERIOD_PERIOD_QUESTION;
- }
-
/// Validate that if the passed arguments are constant expressions.
///
/// @param argumentList the argument list to evaluate
@@ -757,52 +577,6 @@
_validateInitializerExpression(parameterElements, argument);
}
}
-
- bool _validateMapLiteralEntry(MapLiteralEntry entry, bool isConst,
- HashSet<DartObject> keys, List<Expression> invalidKeys) {
- Expression key = entry.key;
- if (isConst) {
- DartObjectImpl keyResult =
- _validate(key, CompileTimeErrorCode.NON_CONSTANT_MAP_KEY);
- Expression valueExpression = entry.value;
- DartObjectImpl valueResult = _validate(
- valueExpression, CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE);
- if (valueResult != null) {
- _reportErrorIfFromDeferredLibrary(valueExpression,
- CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE_FROM_DEFERRED_LIBRARY);
- }
- if (keyResult != null) {
- _reportErrorIfFromDeferredLibrary(key,
- CompileTimeErrorCode.NON_CONSTANT_MAP_KEY_FROM_DEFERRED_LIBRARY);
- if (!keys.add(keyResult)) {
- invalidKeys.add(key);
- }
- DartType type = keyResult.type;
- if (_implementsEqualsWhenNotAllowed(type)) {
- _errorReporter.reportErrorForNode(
- CompileTimeErrorCode
- .CONST_MAP_KEY_EXPRESSION_TYPE_IMPLEMENTS_EQUALS,
- key,
- [type.displayName]);
- }
- }
- } else {
- // Note: we throw the errors away because this isn't actually a const.
- AnalysisErrorListener errorListener = AnalysisErrorListener.NULL_LISTENER;
- ErrorReporter subErrorReporter =
- new ErrorReporter(errorListener, _errorReporter.source);
- DartObjectImpl result =
- key.accept(new ConstantVisitor(_evaluationEngine, subErrorReporter));
- if (result != null) {
- if (!keys.add(result)) {
- invalidKeys.add(key);
- }
- } else {
- return false;
- }
- }
- return true;
- }
}
class _ConstantVerifier_validateInitializerExpression extends ConstantVisitor {
@@ -864,3 +638,259 @@
return super.visitSimpleIdentifier(node);
}
}
+
+class _ConstLiteralVerifier {
+ final ConstantVerifier verifier;
+ final bool isConst;
+ final Set<DartObject> mapUniqueKeys;
+ final List<Expression> mapDuplicateKeyElements;
+ final ErrorCode errorCode;
+ final DartType listElementType;
+ final DartType mapKeyType;
+ final DartType mapValueType;
+ final DartType setElementType;
+ final Set<DartObject> setUniqueValues;
+ final List<CollectionElement> setDuplicateElements;
+ final bool forList;
+ final bool forMap;
+ final bool forSet;
+
+ _ConstLiteralVerifier(
+ this.verifier, {
+ this.isConst,
+ this.mapUniqueKeys,
+ this.mapDuplicateKeyElements,
+ this.errorCode,
+ this.listElementType,
+ this.mapKeyType,
+ this.mapValueType,
+ this.setElementType,
+ this.setUniqueValues,
+ this.setDuplicateElements,
+ this.forList = false,
+ this.forMap = false,
+ this.forSet = false,
+ });
+
+ bool verify(CollectionElement element) {
+ if (element is Expression) {
+ if (!isConst) return true;
+
+ var value = verifier._validate(element, errorCode);
+ if (value == null) return false;
+
+ if (forList) {
+ return _validateListExpression(element, value);
+ }
+
+ if (forSet) {
+ return _validateSetExpression(element, value);
+ }
+
+ return true;
+ } else if (element is ForElement) {
+ if (!isConst) return true;
+
+ verifier._errorReporter.reportErrorForNode(errorCode, element);
+ return false;
+ } else if (element is IfElement) {
+ if (!isConst) return true;
+
+ DartObject conditionResult =
+ verifier._validate(element.condition, errorCode);
+ bool conditionValue = conditionResult?.toBoolValue();
+ if (conditionValue == null) {
+ // The errors have already been reported.
+ return false;
+ } else if (conditionValue) {
+ return verify(element.thenElement);
+ } else if (element.elseElement != null) {
+ return verify(element.elseElement);
+ } else {
+ return true;
+ }
+ } else if (element is MapLiteralEntry) {
+ return _validateMapLiteralEntry(element);
+ } else if (element is SpreadElement) {
+ if (!isConst) return true;
+
+ var value = verifier._validate(element.expression, errorCode);
+ if (value == null) return false;
+
+ if (forList || forSet) {
+ return _validateListOrSetSpread(element, value);
+ }
+
+ if (forMap) {
+ return _validateMapSpread(element, value);
+ }
+
+ return true;
+ }
+ throw new UnsupportedError(
+ 'Unhandled type of collection element: ${element.runtimeType}',
+ );
+ }
+
+ bool _validateListExpression(Expression expression, DartObjectImpl value) {
+ if (!verifier._evaluationEngine.runtimeTypeMatch(value, listElementType)) {
+ verifier._errorReporter.reportErrorForNode(
+ StaticWarningCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE,
+ expression,
+ [value.type, listElementType],
+ );
+ return false;
+ }
+
+ verifier._reportErrorIfFromDeferredLibrary(
+ expression,
+ CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT_FROM_DEFERRED_LIBRARY,
+ );
+
+ return true;
+ }
+
+ bool _validateListOrSetSpread(SpreadElement element, DartObjectImpl value) {
+ var listValue = value.toListValue();
+ var setValue = value.toSetValue();
+
+ if (listValue == null && setValue == null) {
+ if (value.isNull && _isNullableSpread(element)) {
+ return true;
+ }
+ verifier._errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.CONST_SPREAD_EXPECTED_LIST_OR_SET,
+ element.expression,
+ );
+ return false;
+ }
+
+ if (listValue != null) {
+ var elementType = value.type.typeArguments[0];
+ if (verifier._implementsEqualsWhenNotAllowed(elementType)) {
+ verifier._errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.CONST_SET_ELEMENT_TYPE_IMPLEMENTS_EQUALS,
+ element,
+ [elementType],
+ );
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool _validateMapLiteralEntry(MapLiteralEntry entry) {
+ if (!forMap) return false;
+
+ var keyExpression = entry.key;
+ if (isConst) {
+ var keyValue = verifier._validate(
+ keyExpression,
+ CompileTimeErrorCode.NON_CONSTANT_MAP_KEY,
+ );
+
+ var valueExpression = entry.value;
+ var valueValue = verifier._validate(
+ valueExpression,
+ CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE,
+ );
+
+ if (keyValue != null) {
+ verifier._reportErrorIfFromDeferredLibrary(
+ keyExpression,
+ CompileTimeErrorCode.NON_CONSTANT_MAP_KEY_FROM_DEFERRED_LIBRARY,
+ );
+
+ if (!mapUniqueKeys.add(keyValue)) {
+ mapDuplicateKeyElements.add(keyExpression);
+ }
+
+ var keyType = keyValue.type;
+ if (verifier._implementsEqualsWhenNotAllowed(keyType)) {
+ verifier._errorReporter.reportErrorForNode(
+ CompileTimeErrorCode
+ .CONST_MAP_KEY_EXPRESSION_TYPE_IMPLEMENTS_EQUALS,
+ keyExpression,
+ [keyType],
+ );
+ }
+ }
+
+ if (valueValue != null) {
+ verifier._reportErrorIfFromDeferredLibrary(
+ valueExpression,
+ CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE_FROM_DEFERRED_LIBRARY,
+ );
+ }
+ } else {
+ // Note: we throw the errors away because this isn't actually a const.
+ var nullErrorReporter = new ErrorReporter(
+ AnalysisErrorListener.NULL_LISTENER,
+ verifier._errorReporter.source,
+ );
+ var keyValue = keyExpression.accept(
+ new ConstantVisitor(verifier._evaluationEngine, nullErrorReporter),
+ );
+
+ if (keyValue != null) {
+ if (!mapUniqueKeys.add(keyValue)) {
+ mapDuplicateKeyElements.add(keyExpression);
+ }
+ }
+
+ return true;
+ }
+ return true;
+ }
+
+ bool _validateMapSpread(SpreadElement element, DartObjectImpl value) {
+ if (value.toMapValue() != null ||
+ value.isNull && _isNullableSpread(element)) {
+ return true;
+ }
+
+ verifier._errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.CONST_SPREAD_EXPECTED_MAP,
+ element.expression,
+ );
+
+ return false;
+ }
+
+ bool _validateSetExpression(Expression expression, DartObjectImpl value) {
+ if (!verifier._evaluationEngine.runtimeTypeMatch(value, setElementType)) {
+ verifier._errorReporter.reportErrorForNode(
+ StaticWarningCode.SET_ELEMENT_TYPE_NOT_ASSIGNABLE,
+ expression,
+ [value.type, setElementType],
+ );
+ return false;
+ }
+
+ if (verifier._implementsEqualsWhenNotAllowed(value.type)) {
+ verifier._errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.CONST_SET_ELEMENT_TYPE_IMPLEMENTS_EQUALS,
+ expression,
+ [value.type],
+ );
+ return false;
+ }
+
+ verifier._reportErrorIfFromDeferredLibrary(
+ expression,
+ CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT_FROM_DEFERRED_LIBRARY,
+ );
+
+ if (!setUniqueValues.add(value)) {
+ setDuplicateElements.add(expression);
+ }
+
+ return true;
+ }
+
+ static bool _isNullableSpread(SpreadElement element) {
+ return element.spreadOperator.type ==
+ TokenType.PERIOD_PERIOD_PERIOD_QUESTION;
+ }
+}
diff --git a/pkg/analyzer/test/generated/compile_time_error_code_test.dart b/pkg/analyzer/test/generated/compile_time_error_code_test.dart
index e4340d6..959fd3e 100644
--- a/pkg/analyzer/test/generated/compile_time_error_code_test.dart
+++ b/pkg/analyzer/test/generated/compile_time_error_code_test.dart
@@ -457,13 +457,6 @@
verify([source]);
}
- test_mapKeyTypeNotAssignable_const() async {
- Source source = addSource("var v = const <String, int >{1 : 2};");
- await computeAnalysisResult(source);
- assertErrors(source, [StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE]);
- verify([source]);
- }
-
test_mapValueTypeNotAssignable_const() async {
Source source = addSource("var v = const <String, String>{'a' : 2};");
await computeAnalysisResult(source);