|  | // Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file | 
|  | // 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:_fe_analyzer_shared/src/exhaustiveness/dart_template_buffer.dart'; | 
|  | import 'package:_fe_analyzer_shared/src/exhaustiveness/exhaustive.dart'; | 
|  | import 'package:_fe_analyzer_shared/src/exhaustiveness/space.dart'; | 
|  | import 'package:analyzer/dart/analysis/declared_variables.dart'; | 
|  | import 'package:analyzer/dart/analysis/features.dart'; | 
|  | import 'package:analyzer/dart/ast/ast.dart'; | 
|  | import 'package:analyzer/dart/ast/token.dart'; | 
|  | import 'package:analyzer/dart/ast/visitor.dart'; | 
|  | import 'package:analyzer/dart/constant/value.dart'; | 
|  | import 'package:analyzer/dart/element/element.dart'; | 
|  | import 'package:analyzer/dart/element/nullability_suffix.dart'; | 
|  | import 'package:analyzer/dart/element/type.dart'; | 
|  | import 'package:analyzer/dart/element/type_provider.dart'; | 
|  | import 'package:analyzer/error/error.dart'; | 
|  | import 'package:analyzer/error/listener.dart'; | 
|  | import 'package:analyzer/src/dart/ast/ast.dart'; | 
|  | import 'package:analyzer/src/dart/ast/extensions.dart'; | 
|  | import 'package:analyzer/src/dart/constant/evaluation.dart'; | 
|  | import 'package:analyzer/src/dart/constant/has_type_parameter_reference.dart'; | 
|  | import 'package:analyzer/src/dart/constant/potentially_constant.dart'; | 
|  | import 'package:analyzer/src/dart/constant/value.dart'; | 
|  | import 'package:analyzer/src/dart/element/element.dart'; | 
|  | import 'package:analyzer/src/dart/element/least_greatest_closure.dart'; | 
|  | import 'package:analyzer/src/dart/element/type.dart'; | 
|  | import 'package:analyzer/src/dart/element/type_system.dart'; | 
|  | import 'package:analyzer/src/diagnostic/diagnostic_factory.dart'; | 
|  | import 'package:analyzer/src/error/codes.dart'; | 
|  | import 'package:analyzer/src/generated/exhaustiveness.dart'; | 
|  | import 'package:analyzer/src/utilities/extensions/ast.dart'; | 
|  |  | 
|  | /// Instances of the class `ConstantVerifier` traverse an AST structure looking | 
|  | /// for additional errors and warnings not covered by the parser and resolver. | 
|  | /// In particular, it looks for errors and warnings related to constant | 
|  | /// expressions. | 
|  | class ConstantVerifier extends RecursiveAstVisitor<void> { | 
|  | /// The error reporter by which errors will be reported. | 
|  | final ErrorReporter _errorReporter; | 
|  |  | 
|  | /// The type operations. | 
|  | final TypeSystemImpl _typeSystem; | 
|  |  | 
|  | /// The type provider used to access the known types. | 
|  | final TypeProvider _typeProvider; | 
|  |  | 
|  | /// The current library that is being analyzed. | 
|  | final LibraryElementImpl _currentLibrary; | 
|  |  | 
|  | final ConstantEvaluationEngine _evaluationEngine; | 
|  |  | 
|  | final DiagnosticFactory _diagnosticFactory = DiagnosticFactory(); | 
|  |  | 
|  | /// Cache used for checking exhaustiveness. | 
|  | final AnalyzerExhaustivenessCache _exhaustivenessCache; | 
|  |  | 
|  | /// Cache of constant values used for exhaustiveness checking. | 
|  | /// | 
|  | /// When verifying a switch statement/expression the constant values of the | 
|  | /// contained [ConstantPattern]s are cached here. The cache is released once | 
|  | /// the exhaustiveness of the switch has been checked. | 
|  | Map<ConstantPattern, DartObjectImpl>? _constantPatternValues; | 
|  |  | 
|  | Map<Expression, DartObjectImpl>? _mapPatternKeyValues; | 
|  |  | 
|  | final ExhaustivenessDataForTesting? exhaustivenessDataForTesting; | 
|  |  | 
|  | /// Initialize a newly created constant verifier. | 
|  | ConstantVerifier(ErrorReporter errorReporter, | 
|  | LibraryElementImpl currentLibrary, DeclaredVariables declaredVariables, | 
|  | {bool retainDataForTesting = false}) | 
|  | : this._( | 
|  | errorReporter, | 
|  | currentLibrary, | 
|  | currentLibrary.typeSystem, | 
|  | currentLibrary.typeProvider, | 
|  | declaredVariables, | 
|  | AnalyzerExhaustivenessCache( | 
|  | currentLibrary.typeSystem, currentLibrary), | 
|  | retainDataForTesting: retainDataForTesting, | 
|  | ); | 
|  |  | 
|  | ConstantVerifier._( | 
|  | this._errorReporter, | 
|  | this._currentLibrary, | 
|  | this._typeSystem, | 
|  | this._typeProvider, | 
|  | DeclaredVariables declaredVariables, | 
|  | this._exhaustivenessCache, { | 
|  | required bool retainDataForTesting, | 
|  | })  : _evaluationEngine = ConstantEvaluationEngine( | 
|  | declaredVariables: declaredVariables, | 
|  | configuration: ConstantEvaluationConfiguration(), | 
|  | ), | 
|  | exhaustivenessDataForTesting = retainDataForTesting | 
|  | ? ExhaustivenessDataForTesting(_exhaustivenessCache) | 
|  | : null; | 
|  |  | 
|  | @override | 
|  | void visitAnnotation(Annotation node) { | 
|  | super.visitAnnotation(node); | 
|  | // check annotation creation | 
|  | var element = node.element; | 
|  | if (element is ConstructorElement) { | 
|  | // should be 'const' constructor | 
|  | if (!element.isConst) { | 
|  | _errorReporter.atNode( | 
|  | node, | 
|  | CompileTimeErrorCode.NON_CONSTANT_ANNOTATION_CONSTRUCTOR, | 
|  | ); | 
|  | return; | 
|  | } | 
|  | // should have arguments | 
|  | var argumentList = node.arguments; | 
|  | if (argumentList == null) { | 
|  | _errorReporter.atNode( | 
|  | node, | 
|  | CompileTimeErrorCode.NO_ANNOTATION_CONSTRUCTOR_ARGUMENTS, | 
|  | ); | 
|  | return; | 
|  | } | 
|  | // arguments should be constants | 
|  | _validateConstantArguments(argumentList); | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitConstantPattern(ConstantPattern node) { | 
|  | var expression = node.expression.unParenthesized; | 
|  | if (expression.typeOrThrow is InvalidType) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | var value = _evaluateAndReportError( | 
|  | expression, | 
|  | CompileTimeErrorCode.CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION, | 
|  | ); | 
|  | if (value is DartObjectImpl) { | 
|  | if (_currentLibrary.featureSet.isEnabled(Feature.patterns)) { | 
|  | _constantPatternValues?[node] = value; | 
|  | if (value.hasPrimitiveEquality(_currentLibrary.featureSet)) { | 
|  | var constantType = value.type; | 
|  | var matchedValueType = node.matchedValueType; | 
|  | matchedValueType = matchedValueType?.extensionTypeErasure; | 
|  | if (matchedValueType != null) { | 
|  | if (!_canBeEqual(constantType, matchedValueType)) { | 
|  | _errorReporter.atNode( | 
|  | node, | 
|  | WarningCode.CONSTANT_PATTERN_NEVER_MATCHES_VALUE_TYPE, | 
|  | arguments: [matchedValueType, constantType], | 
|  | ); | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | super.visitConstantPattern(node); | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitConstructorDeclaration(ConstructorDeclaration node) { | 
|  | var constKeyword = node.constKeyword; | 
|  | if (constKeyword != null) { | 
|  | // Check and report cycles. | 
|  | // Factory cycles are reported in elsewhere in | 
|  | // [ErrorVerifier._checkForRecursiveFactoryRedirect]. | 
|  | var element = node.declaredElement; | 
|  | if (element is ConstructorElementImpl && | 
|  | !element.isCycleFree && | 
|  | !element.isFactory) { | 
|  | _errorReporter.atNode( | 
|  | node.returnType, | 
|  | CompileTimeErrorCode.RECURSIVE_CONSTANT_CONSTRUCTOR, | 
|  | ); | 
|  | } | 
|  |  | 
|  | _validateConstructorInitializers(node); | 
|  | if (node.factoryKeyword == null) { | 
|  | _validateFieldInitializers( | 
|  | node.parent.classMembers, | 
|  | constKeyword, | 
|  | isEnumDeclaration: node.parent is EnumDeclaration, | 
|  | ); | 
|  | } | 
|  | } | 
|  | _validateDefaultValues(node.parameters); | 
|  | super.visitConstructorDeclaration(node); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitConstructorReference(ConstructorReference node) { | 
|  | super.visitConstructorReference(node); | 
|  | if (node.inConstantContext || node.inConstantExpression) { | 
|  | _checkForConstWithTypeParameters(node.constructorName.type, | 
|  | CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS_CONSTRUCTOR_TEAROFF); | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | visitEnumConstantDeclaration(EnumConstantDeclaration node) { | 
|  | super.visitEnumConstantDeclaration(node); | 
|  |  | 
|  | var argumentList = node.arguments?.argumentList; | 
|  | if (argumentList != null) { | 
|  | _validateConstantArguments(argumentList); | 
|  | } | 
|  |  | 
|  | var element = node.declaredElement as ConstFieldElementImpl; | 
|  | var result = element.evaluationResult; | 
|  | if (result is InvalidConstant) { | 
|  | _reportError(result, null); | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitFunctionExpression(FunctionExpression node) { | 
|  | super.visitFunctionExpression(node); | 
|  | _validateDefaultValues(node.parameters); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitFunctionReference(FunctionReference node) { | 
|  | super.visitFunctionReference(node); | 
|  | if (node.inConstantContext || node.inConstantExpression) { | 
|  | var typeArguments = node.typeArguments; | 
|  | if (typeArguments == null) { | 
|  | return; | 
|  | } | 
|  | for (var typeArgument in typeArguments.arguments) { | 
|  | _checkForConstWithTypeParameters(typeArgument, | 
|  | CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS_FUNCTION_TEAROFF); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitGenericFunctionType(GenericFunctionType node) { | 
|  | // TODO(srawlins): Also check interface types (TypeName?). | 
|  | super.visitGenericFunctionType(node); | 
|  | var parent = node.parent; | 
|  | if ((parent is AsExpression || parent is IsExpression) && | 
|  | (parent as Expression).inConstantContext) { | 
|  | _checkForConstWithTypeParameters( | 
|  | node, CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS); | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitInstanceCreationExpression(InstanceCreationExpression node) { | 
|  | if (node.isConst) { | 
|  | var namedType = node.constructorName.type; | 
|  | _checkForConstWithTypeParameters( | 
|  | namedType, CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS); | 
|  |  | 
|  | // We need to evaluate the constant to see if any errors occur during its | 
|  | // evaluation. | 
|  | var constructor = node.constructorName.staticElement; | 
|  | if (constructor != null) { | 
|  | var constantVisitor = | 
|  | ConstantVisitor(_evaluationEngine, _currentLibrary, _errorReporter); | 
|  | var result = _evaluationEngine.evaluateAndFormatErrorsInConstructorCall( | 
|  | _currentLibrary, | 
|  | node, | 
|  | constructor.returnType.typeArguments, | 
|  | node.argumentList.arguments, | 
|  | constructor, | 
|  | constantVisitor); | 
|  | switch (result) { | 
|  | case InvalidConstant(): | 
|  | if (!result.avoidReporting) { | 
|  | _errorReporter.atOffset( | 
|  | offset: result.offset, | 
|  | length: result.length, | 
|  | errorCode: result.errorCode, | 
|  | arguments: result.arguments, | 
|  | contextMessages: result.contextMessages, | 
|  | ); | 
|  | } | 
|  | case DartObjectImpl(): | 
|  | // Check for further errors in individual arguments. | 
|  | node.argumentList.accept(this); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | super.visitInstanceCreationExpression(node); | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitListLiteral(ListLiteral node) { | 
|  | super.visitListLiteral(node); | 
|  | if (node.isConst) { | 
|  | var nodeType = node.staticType as InterfaceType; | 
|  | var elementType = nodeType.typeArguments[0]; | 
|  | var verifier = _ConstLiteralVerifier( | 
|  | this, | 
|  | errorCode: CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT, | 
|  | listElementType: elementType, | 
|  | ); | 
|  | for (var element in node.elements) { | 
|  | verifier.verify(element); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitMapPattern(MapPattern node) { | 
|  | node.typeArguments?.accept(this); | 
|  |  | 
|  | var featureSet = _currentLibrary.featureSet; | 
|  | var uniqueKeys = HashMap<DartObjectImpl, Expression>( | 
|  | hashCode: (_) => 0, | 
|  | equals: (a, b) { | 
|  | if (a.isIdentical2(_typeSystem, b).toBoolValue() == true) { | 
|  | return true; | 
|  | } | 
|  | if (a.hasPrimitiveEquality(featureSet) && | 
|  | b.hasPrimitiveEquality(featureSet)) { | 
|  | return a == b; | 
|  | } | 
|  | return false; | 
|  | }, | 
|  | ); | 
|  | var duplicateKeys = <Expression, Expression>{}; | 
|  | for (var element in node.elements) { | 
|  | element.accept(this); | 
|  | if (element is MapPatternEntry) { | 
|  | var key = element.key; | 
|  | var keyValue = _evaluateAndReportError( | 
|  | key, | 
|  | CompileTimeErrorCode.NON_CONSTANT_MAP_PATTERN_KEY, | 
|  | ); | 
|  | if (keyValue is DartObjectImpl) { | 
|  | _mapPatternKeyValues?[key] = keyValue; | 
|  | var existingKey = uniqueKeys[keyValue]; | 
|  | if (existingKey != null) { | 
|  | duplicateKeys[key] = existingKey; | 
|  | } else { | 
|  | uniqueKeys[keyValue] = key; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | for (var duplicateEntry in duplicateKeys.entries) { | 
|  | _errorReporter.reportError( | 
|  | _diagnosticFactory.equalKeysInMapPattern( | 
|  | _errorReporter.source, | 
|  | duplicateEntry.key, | 
|  | duplicateEntry.value, | 
|  | ), | 
|  | ); | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitMethodDeclaration(MethodDeclaration node) { | 
|  | super.visitMethodDeclaration(node); | 
|  | _validateDefaultValues(node.parameters); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitRecordLiteral(RecordLiteral node) { | 
|  | super.visitRecordLiteral(node); | 
|  |  | 
|  | if (node.isConst) { | 
|  | for (var field in node.fields) { | 
|  | _evaluateAndReportError( | 
|  | field, | 
|  | CompileTimeErrorCode.NON_CONSTANT_RECORD_FIELD, | 
|  | ); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitRelationalPattern(RelationalPattern node) { | 
|  | super.visitRelationalPattern(node); | 
|  |  | 
|  | _evaluateAndReportError( | 
|  | node.operand, | 
|  | CompileTimeErrorCode.NON_CONSTANT_RELATIONAL_PATTERN_EXPRESSION, | 
|  | ); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitSetOrMapLiteral(SetOrMapLiteral node) { | 
|  | super.visitSetOrMapLiteral(node); | 
|  | if (node.isSet) { | 
|  | if (node.isConst) { | 
|  | var nodeType = node.staticType as InterfaceType; | 
|  | var elementType = nodeType.typeArguments[0]; | 
|  | var config = _SetVerifierConfig(elementType: elementType); | 
|  | var verifier = _ConstLiteralVerifier( | 
|  | this, | 
|  | errorCode: CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT, | 
|  | setConfig: config, | 
|  | ); | 
|  | for (CollectionElement element in node.elements) { | 
|  | verifier.verify(element); | 
|  | } | 
|  | for (var duplicateEntry in config.duplicateElements.entries) { | 
|  | _errorReporter.reportError(_diagnosticFactory.equalElementsInConstSet( | 
|  | _errorReporter.source, duplicateEntry.key, duplicateEntry.value)); | 
|  | } | 
|  | } | 
|  | } else if (node.isMap) { | 
|  | if (node.isConst) { | 
|  | var nodeType = node.staticType as InterfaceType; | 
|  | var keyType = nodeType.typeArguments[0]; | 
|  | var valueType = nodeType.typeArguments[1]; | 
|  | var config = _MapVerifierConfig( | 
|  | keyType: keyType, | 
|  | valueType: valueType, | 
|  | ); | 
|  | var verifier = _ConstLiteralVerifier( | 
|  | this, | 
|  | errorCode: CompileTimeErrorCode.NON_CONSTANT_MAP_ELEMENT, | 
|  | mapConfig: config, | 
|  | ); | 
|  | for (var entry in node.elements) { | 
|  | verifier.verify(entry); | 
|  | } | 
|  | for (var duplicateEntry in config.duplicateKeys.entries) { | 
|  | _errorReporter.reportError(_diagnosticFactory.equalKeysInConstMap( | 
|  | _errorReporter.source, duplicateEntry.key, duplicateEntry.value)); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitSwitchExpression(SwitchExpression node) { | 
|  | _withConstantPatternValues((mapPatternKeyValues, constantPatternValues) { | 
|  | super.visitSwitchExpression(node); | 
|  | _validateSwitchExhaustiveness( | 
|  | node: node, | 
|  | switchKeyword: node.switchKeyword, | 
|  | scrutinee: node.expression, | 
|  | caseNodes: node.cases, | 
|  | mapPatternKeyValues: mapPatternKeyValues, | 
|  | constantPatternValues: constantPatternValues, | 
|  | mustBeExhaustive: true, | 
|  | isSwitchExpression: true, | 
|  | ); | 
|  | }); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitSwitchStatement(SwitchStatement node) { | 
|  | _withConstantPatternValues((mapPatternKeyValues, constantPatternValues) { | 
|  | super.visitSwitchStatement(node); | 
|  | if (_currentLibrary.featureSet.isEnabled(Feature.patterns)) { | 
|  | _validateSwitchExhaustiveness( | 
|  | node: node, | 
|  | switchKeyword: node.switchKeyword, | 
|  | scrutinee: node.expression, | 
|  | caseNodes: node.members, | 
|  | mapPatternKeyValues: mapPatternKeyValues, | 
|  | constantPatternValues: constantPatternValues, | 
|  | mustBeExhaustive: | 
|  | _typeSystem.isAlwaysExhaustive(node.expression.typeOrThrow), | 
|  | isSwitchExpression: false, | 
|  | ); | 
|  | } else { | 
|  | _validateSwitchStatement_nullSafety(node); | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitVariableDeclaration(VariableDeclaration node) { | 
|  | super.visitVariableDeclaration(node); | 
|  | var initializer = node.initializer; | 
|  | if (initializer != null && (node.isConst || node.isFinal)) { | 
|  | var element = node.declaredElement as VariableElementImpl; | 
|  | if (element is FieldElement && !element.isStatic) { | 
|  | var enclosingElement = element.enclosingElement; | 
|  | if (enclosingElement is ClassElementImpl && | 
|  | !enclosingElement.hasGenerativeConstConstructor) { | 
|  | // TODO(kallentu): Evaluate if we need to do this check for inline | 
|  | // classes. | 
|  | // | 
|  | // We report errors in the class fields only if there's a generative | 
|  | // const constructor in the class. | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | var result = element.evaluationResult; | 
|  | if (result == null) { | 
|  | // Variables marked "const" should have had their values computed by | 
|  | // ConstantValueComputer.  Other variables will only have had their | 
|  | // values computed if the value was needed (e.g. final variables in a | 
|  | // class containing const constructors). | 
|  | assert(!node.isConst); | 
|  | return; | 
|  | } | 
|  | if (result is InvalidConstant) { | 
|  | if (node.isConst) { | 
|  | _reportError(result, | 
|  | CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE); | 
|  | } else { | 
|  | _reportError(result, null); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Returns `false` if we can prove that `constant == value` always returns | 
|  | /// `false`, taking into account the fact that [constantType] has primitive | 
|  | /// equality. | 
|  | bool _canBeEqual(DartType constantType, DartType valueType) { | 
|  | if (constantType is InterfaceType) { | 
|  | if (valueType is InterfaceType) { | 
|  | if (constantType.isDartCoreInt && valueType.isDartCoreDouble) { | 
|  | return true; | 
|  | } | 
|  | var valueTypeGreatest = PatternGreatestClosureHelper( | 
|  | topType: _typeSystem.objectQuestion, | 
|  | bottomType: NeverTypeImpl.instance, | 
|  | ).eliminateToGreatest(valueType); | 
|  | return _typeSystem.isSubtypeOf(constantType, valueTypeGreatest); | 
|  | } else if (valueType is TypeParameterTypeImpl) { | 
|  | var bound = valueType.promotedBound ?? valueType.element.bound; | 
|  | if (bound != null && !hasTypeParameterReference(bound)) { | 
|  | var lowestBound = | 
|  | valueType.nullabilitySuffix == NullabilitySuffix.question | 
|  | ? _typeSystem.makeNullable(bound) | 
|  | : bound; | 
|  | return _canBeEqual(constantType, lowestBound); | 
|  | } | 
|  | } else if (valueType is FunctionType) { | 
|  | if (constantType.isDartCoreNull) { | 
|  | return _typeSystem.isNullable(valueType); | 
|  | } | 
|  | return false; | 
|  | } | 
|  | } | 
|  | // All other cases are not supported, so no warning. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /// Verify that the given [type] does not reference any type parameters which | 
|  | /// are declared outside [type]. | 
|  | /// | 
|  | /// A generic function type is allowed to reference its own type parameter(s). | 
|  | /// | 
|  | /// See [CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS]. | 
|  | void _checkForConstWithTypeParameters( | 
|  | TypeAnnotation type, ErrorCode errorCode, | 
|  | {Set<TypeParameterElement>? allowedTypeParameters}) { | 
|  | allowedTypeParameters = {...?allowedTypeParameters}; | 
|  | if (type is NamedType) { | 
|  | // Should not be a type parameter. | 
|  | if (type.element is TypeParameterElement && | 
|  | !allowedTypeParameters.contains(type.element)) { | 
|  | _errorReporter.atNode( | 
|  | type, | 
|  | errorCode, | 
|  | ); | 
|  | return; | 
|  | } | 
|  | // Check type arguments. | 
|  | var typeArguments = type.typeArguments; | 
|  | if (typeArguments != null) { | 
|  | for (var argument in typeArguments.arguments) { | 
|  | _checkForConstWithTypeParameters(argument, errorCode, | 
|  | allowedTypeParameters: allowedTypeParameters); | 
|  | } | 
|  | } | 
|  | } else if (type is GenericFunctionType) { | 
|  | var typeParameters = type.typeParameters; | 
|  | if (typeParameters != null) { | 
|  | allowedTypeParameters.addAll(typeParameters.typeParameters | 
|  | .map((tp) => tp.declaredElement) | 
|  | .nonNulls); | 
|  | for (var typeParameter in typeParameters.typeParameters) { | 
|  | var bound = typeParameter.bound; | 
|  | if (bound != null) { | 
|  | _checkForConstWithTypeParameters(bound, errorCode, | 
|  | allowedTypeParameters: allowedTypeParameters); | 
|  | } | 
|  | } | 
|  | } | 
|  | var returnType = type.returnType; | 
|  | if (returnType != null) { | 
|  | _checkForConstWithTypeParameters(returnType, errorCode, | 
|  | allowedTypeParameters: allowedTypeParameters); | 
|  | } | 
|  | for (var parameter in type.parameters.parameters) { | 
|  | // In a generic function type, [parameter] can only be a | 
|  | // [SimpleFormalParameter]. | 
|  | if (parameter is SimpleFormalParameter) { | 
|  | var parameterType = parameter.type; | 
|  | if (parameterType != null) { | 
|  | _checkForConstWithTypeParameters(parameterType, errorCode, | 
|  | allowedTypeParameters: allowedTypeParameters); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Evaluates [expression] and reports any evaluation error. | 
|  | /// | 
|  | /// Returns the compile time constant of [expression], or an [InvalidConstant] | 
|  | /// if an error was found during evaluation. If an [InvalidConstant] was | 
|  | /// found, the error will be reported and [errorCode] will be the default | 
|  | /// error code to be reported. | 
|  | Constant _evaluateAndReportError(Expression expression, ErrorCode errorCode) { | 
|  | var errorListener = RecordingErrorListener(); | 
|  | var subErrorReporter = ErrorReporter( | 
|  | errorListener, | 
|  | _errorReporter.source, | 
|  | ); | 
|  | var constantVisitor = | 
|  | ConstantVisitor(_evaluationEngine, _currentLibrary, subErrorReporter); | 
|  | var result = constantVisitor.evaluateConstant(expression); | 
|  | if (result is InvalidConstant) { | 
|  | _reportError(result, errorCode); | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /// Reports an error to the [_errorReporter]. | 
|  | /// | 
|  | /// If the [error] isn't found in the list, use the given [defaultErrorCode] | 
|  | /// instead. | 
|  | void _reportError(InvalidConstant error, ErrorCode? defaultErrorCode) { | 
|  | if (error.avoidReporting) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // TODO(kallentu): Create a set of errors so this method is readable. But | 
|  | // also maybe turn this into a deny list instead of an allow list. | 
|  | // | 
|  | // These error codes are more specific than the [defaultErrorCode] so they | 
|  | // will overwrite and replace the default when we report the error. | 
|  | ErrorCode errorCode = error.errorCode; | 
|  | if (identical( | 
|  | errorCode, CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD) || | 
|  | identical( | 
|  | errorCode, CompileTimeErrorCode.CONST_EVAL_EXTENSION_TYPE_METHOD) || | 
|  | identical(errorCode, CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT) || | 
|  | identical( | 
|  | errorCode, CompileTimeErrorCode.CONST_EVAL_METHOD_INVOCATION) || | 
|  | identical(errorCode, CompileTimeErrorCode.CONST_EVAL_PROPERTY_ACCESS) || | 
|  | identical( | 
|  | errorCode, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION) || | 
|  | identical(errorCode, CompileTimeErrorCode.CONST_EVAL_THROWS_IDBZE) || | 
|  | identical( | 
|  | errorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING) || | 
|  | identical(errorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL) || | 
|  | identical(errorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_INT) || | 
|  | identical(errorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_INT) || | 
|  | identical(errorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM) || | 
|  | identical(errorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM_STRING) || | 
|  | identical(errorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_STRING) || | 
|  | identical( | 
|  | errorCode, CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT) || | 
|  | identical(errorCode, | 
|  | CompileTimeErrorCode.CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH) || | 
|  | identical(errorCode, | 
|  | CompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH) || | 
|  | identical(errorCode, CompileTimeErrorCode.CONST_TYPE_PARAMETER) || | 
|  | identical(errorCode, | 
|  | CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS_FUNCTION_TEAROFF) || | 
|  | identical(errorCode, | 
|  | CompileTimeErrorCode.CONST_SPREAD_EXPECTED_LIST_OR_SET) || | 
|  | identical(errorCode, CompileTimeErrorCode.CONST_SPREAD_EXPECTED_MAP) || | 
|  | identical(errorCode, CompileTimeErrorCode.EXPRESSION_IN_MAP) || | 
|  | identical(errorCode, CompileTimeErrorCode.VARIABLE_TYPE_MISMATCH) || | 
|  | identical(errorCode, CompileTimeErrorCode.NON_BOOL_CONDITION) || | 
|  | identical( | 
|  | errorCode, | 
|  | CompileTimeErrorCode | 
|  | .NON_CONSTANT_DEFAULT_VALUE_FROM_DEFERRED_LIBRARY) || | 
|  | identical(errorCode, | 
|  | CompileTimeErrorCode.NON_CONSTANT_MAP_KEY_FROM_DEFERRED_LIBRARY) || | 
|  | identical( | 
|  | errorCode, | 
|  | CompileTimeErrorCode | 
|  | .NON_CONSTANT_MAP_VALUE_FROM_DEFERRED_LIBRARY) || | 
|  | identical(errorCode, | 
|  | CompileTimeErrorCode.SET_ELEMENT_FROM_DEFERRED_LIBRARY) || | 
|  | identical(errorCode, | 
|  | CompileTimeErrorCode.SPREAD_EXPRESSION_FROM_DEFERRED_LIBRARY) || | 
|  | identical( | 
|  | errorCode, | 
|  | CompileTimeErrorCode | 
|  | .NON_CONSTANT_CASE_EXPRESSION_FROM_DEFERRED_LIBRARY) || | 
|  | identical( | 
|  | errorCode, | 
|  | CompileTimeErrorCode | 
|  | .INVALID_ANNOTATION_CONSTANT_VALUE_FROM_DEFERRED_LIBRARY) || | 
|  | identical(errorCode, | 
|  | CompileTimeErrorCode.IF_ELEMENT_CONDITION_FROM_DEFERRED_LIBRARY) || | 
|  | identical( | 
|  | errorCode, | 
|  | CompileTimeErrorCode | 
|  | .CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE_FROM_DEFERRED_LIBRARY) || | 
|  | identical( | 
|  | errorCode, | 
|  | CompileTimeErrorCode | 
|  | .NON_CONSTANT_LIST_ELEMENT_FROM_DEFERRED_LIBRARY) || | 
|  | identical( | 
|  | errorCode, | 
|  | CompileTimeErrorCode | 
|  | .NON_CONSTANT_RECORD_FIELD_FROM_DEFERRED_LIBRARY) || | 
|  | identical( | 
|  | errorCode, | 
|  | CompileTimeErrorCode | 
|  | .CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE_FROM_DEFERRED_LIBRARY) || | 
|  | identical(errorCode, | 
|  | CompileTimeErrorCode.PATTERN_CONSTANT_FROM_DEFERRED_LIBRARY) || | 
|  | identical(errorCode, | 
|  | CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION) || | 
|  | identical( | 
|  | errorCode, | 
|  | CompileTimeErrorCode | 
|  | .WRONG_NUMBER_OF_TYPE_ARGUMENTS_ANONYMOUS_FUNCTION)) { | 
|  | _errorReporter.reportError( | 
|  | AnalysisError.tmp( | 
|  | source: _errorReporter.source, | 
|  | offset: error.offset, | 
|  | length: error.length, | 
|  | errorCode: error.errorCode, | 
|  | arguments: error.arguments, | 
|  | contextMessages: error.contextMessages, | 
|  | ), | 
|  | ); | 
|  | } else if (defaultErrorCode != null) { | 
|  | _errorReporter.reportError( | 
|  | AnalysisError.tmp( | 
|  | source: _errorReporter.source, | 
|  | offset: error.offset, | 
|  | length: error.length, | 
|  | errorCode: defaultErrorCode, | 
|  | ), | 
|  | ); | 
|  | } | 
|  | } | 
|  |  | 
|  | void _reportNotPotentialConstants(AstNode node) { | 
|  | var notPotentiallyConstants = getNotPotentiallyConstants( | 
|  | node, | 
|  | featureSet: _currentLibrary.featureSet, | 
|  | ); | 
|  | if (notPotentiallyConstants.isEmpty) return; | 
|  |  | 
|  | for (var notConst in notPotentiallyConstants) { | 
|  | _errorReporter.atNode( | 
|  | notConst, | 
|  | CompileTimeErrorCode.INVALID_CONSTANT, | 
|  | ); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Validates that all arguments in the [argumentList] are potentially | 
|  | /// constant expressions. | 
|  | void _reportNotPotentialConstantsArguments(ArgumentList argumentList) { | 
|  | for (Expression argument in argumentList.arguments) { | 
|  | _reportNotPotentialConstants(argument); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Check if the object [obj] matches the type [type] according to runtime | 
|  | /// type checking rules. | 
|  | bool _runtimeTypeMatch(DartObjectImpl obj, DartType type) { | 
|  | return _currentLibrary.typeSystem.runtimeTypeMatch(obj, type); | 
|  | } | 
|  |  | 
|  | /// Validates that the arguments in [argumentList] are constant expressions. | 
|  | void _validateConstantArguments(ArgumentList argumentList) { | 
|  | for (Expression argument in argumentList.arguments) { | 
|  | Expression realArgument = | 
|  | argument is NamedExpression ? argument.expression : argument; | 
|  | _evaluateAndReportError( | 
|  | realArgument, CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Validates that the expressions of the initializers of the given constant | 
|  | /// [constructor] are constant expressions. | 
|  | void _validateConstructorInitializers(ConstructorDeclaration constructor) { | 
|  | NodeList<ConstructorInitializer> initializers = constructor.initializers; | 
|  | for (ConstructorInitializer initializer in initializers) { | 
|  | if (initializer is AssertInitializer) { | 
|  | _reportNotPotentialConstants(initializer.condition); | 
|  | var message = initializer.message; | 
|  | if (message != null) { | 
|  | _reportNotPotentialConstants(message); | 
|  | } | 
|  | } else if (initializer is ConstructorFieldInitializer) { | 
|  | _reportNotPotentialConstants(initializer.expression); | 
|  | } else if (initializer is RedirectingConstructorInvocation) { | 
|  | _reportNotPotentialConstantsArguments(initializer.argumentList); | 
|  | } else if (initializer is SuperConstructorInvocation) { | 
|  | _reportNotPotentialConstantsArguments(initializer.argumentList); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Validates that the default value associated with each of the parameters in | 
|  | /// [parameters] is a constant expression. | 
|  | void _validateDefaultValues(FormalParameterList? parameters) { | 
|  | if (parameters == null) { | 
|  | return; | 
|  | } | 
|  | for (FormalParameter parameter in parameters.parameters) { | 
|  | if (parameter is DefaultFormalParameter) { | 
|  | var defaultValue = parameter.defaultValue; | 
|  | Constant? result; | 
|  | if (defaultValue == null) { | 
|  | result = DartObjectImpl( | 
|  | _typeSystem, | 
|  | _typeProvider.nullType, | 
|  | NullState.NULL_STATE, | 
|  | ); | 
|  | } else if (defaultValue.typeOrThrow is InvalidType) { | 
|  | // We have already reported an error. | 
|  | } else { | 
|  | result = _evaluateAndReportError( | 
|  | defaultValue, CompileTimeErrorCode.NON_CONSTANT_DEFAULT_VALUE); | 
|  | } | 
|  | VariableElementImpl element = | 
|  | parameter.declaredElement as VariableElementImpl; | 
|  | element.evaluationResult = result; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Validates that the expressions of any field initializers in | 
|  | /// [members] are all compile-time constants. Since this is only | 
|  | /// required if the class has a constant constructor, the error is reported at | 
|  | /// [constKeyword], the const keyword on such a constant constructor. | 
|  | void _validateFieldInitializers( | 
|  | List<ClassMember> members, | 
|  | Token constKeyword, { | 
|  | required bool isEnumDeclaration, | 
|  | }) { | 
|  | for (ClassMember member in members) { | 
|  | if (member is FieldDeclaration && !member.isStatic) { | 
|  | for (VariableDeclaration variableDeclaration | 
|  | in member.fields.variables) { | 
|  | if (isEnumDeclaration && | 
|  | variableDeclaration.name.lexeme == 'values') { | 
|  | continue; | 
|  | } | 
|  | var initializer = variableDeclaration.initializer; | 
|  | if (initializer != null) { | 
|  | // Ignore any errors produced during validation--if the constant | 
|  | // can't be evaluated we'll just report a single error. | 
|  | AnalysisErrorListener errorListener = | 
|  | AnalysisErrorListener.NULL_LISTENER; | 
|  | ErrorReporter subErrorReporter = ErrorReporter( | 
|  | errorListener, | 
|  | _errorReporter.source, | 
|  | ); | 
|  | var result = initializer.accept(ConstantVisitor( | 
|  | _evaluationEngine, _currentLibrary, subErrorReporter)); | 
|  | // TODO(kallentu): Report the specific error we got from the | 
|  | // evaluator to make it clear to the user what's wrong. | 
|  | if (result is! DartObjectImpl) { | 
|  | _errorReporter.atToken( | 
|  | constKeyword, | 
|  | CompileTimeErrorCode | 
|  | .CONST_CONSTRUCTOR_WITH_FIELD_INITIALIZED_BY_NON_CONST, | 
|  | arguments: [variableDeclaration.name.lexeme], | 
|  | ); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void _validateSwitchExhaustiveness({ | 
|  | required AstNode node, | 
|  | required Token switchKeyword, | 
|  | required Expression scrutinee, | 
|  | required List<AstNode> caseNodes, | 
|  | required Map<Expression, DartObjectImpl> mapPatternKeyValues, | 
|  | required Map<ConstantPattern, DartObjectImpl> constantPatternValues, | 
|  | required bool mustBeExhaustive, | 
|  | required bool isSwitchExpression, | 
|  | }) { | 
|  | var scrutineeType = scrutinee.typeOrThrow; | 
|  | var scrutineeTypeEx = _exhaustivenessCache.getStaticType(scrutineeType); | 
|  |  | 
|  | var caseNodesWithSpace = <AstNode>[]; | 
|  | var caseSpaces = <Space>[]; | 
|  | var hasDefault = false; | 
|  |  | 
|  | var patternConverter = PatternConverter( | 
|  | languageVersion: _currentLibrary.languageVersion.effective, | 
|  | featureSet: _currentLibrary.featureSet, | 
|  | cache: _exhaustivenessCache, | 
|  | mapPatternKeyValues: mapPatternKeyValues, | 
|  | constantPatternValues: constantPatternValues, | 
|  | ); | 
|  | patternConverter.hasInvalidType = scrutineeType is InvalidType; | 
|  |  | 
|  | // Build spaces for cases. | 
|  | for (var caseNode in caseNodes) { | 
|  | GuardedPattern? guardedPattern; | 
|  | if (caseNode is SwitchCase) { | 
|  | // Should not happen, ignore. | 
|  | } else if (caseNode is SwitchDefault) { | 
|  | hasDefault = true; | 
|  | } else if (caseNode is SwitchExpressionCase) { | 
|  | guardedPattern = caseNode.guardedPattern; | 
|  | } else if (caseNode is SwitchPatternCase) { | 
|  | guardedPattern = caseNode.guardedPattern; | 
|  | } else { | 
|  | throw UnimplementedError('(${caseNode.runtimeType}) $caseNode'); | 
|  | } | 
|  |  | 
|  | if (guardedPattern != null) { | 
|  | Space space = patternConverter.createRootSpace( | 
|  | scrutineeTypeEx, guardedPattern.pattern, | 
|  | hasGuard: guardedPattern.whenClause != null); | 
|  | caseNodesWithSpace.add(caseNode); | 
|  | caseSpaces.add(space); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Prepare for recording data for testing. | 
|  | var exhaustivenessDataForTesting = this.exhaustivenessDataForTesting; | 
|  |  | 
|  | // Compute and report errors. | 
|  | var errors = patternConverter.hasInvalidType | 
|  | ? const <ExhaustivenessError>[] | 
|  | : reportErrors(_exhaustivenessCache, scrutineeTypeEx, caseSpaces, | 
|  | computeUnreachable: true); | 
|  |  | 
|  | var reportNonExhaustive = mustBeExhaustive && !hasDefault; | 
|  | for (var error in errors) { | 
|  | if (error is UnreachableCaseError) { | 
|  | var caseNode = caseNodesWithSpace[error.index]; | 
|  | Token errorToken; | 
|  | if (caseNode is SwitchExpressionCase) { | 
|  | errorToken = caseNode.arrow; | 
|  | } else if (caseNode is SwitchPatternCase) { | 
|  | errorToken = caseNode.keyword; | 
|  | } else { | 
|  | throw UnimplementedError('(${caseNode.runtimeType}) $caseNode'); | 
|  | } | 
|  | _errorReporter.atToken( | 
|  | errorToken, | 
|  | WarningCode.UNREACHABLE_SWITCH_CASE, | 
|  | ); | 
|  | } else if (error is NonExhaustiveError && reportNonExhaustive) { | 
|  | var errorBuffer = SimpleDartBuffer(); | 
|  | error.witnesses.first.toDart(errorBuffer, forCorrection: false); | 
|  | var correctionTextBuffer = SimpleDartBuffer(); | 
|  | error.witnesses.first.toDart(correctionTextBuffer, forCorrection: true); | 
|  |  | 
|  | var correctionData = <List<MissingPatternPart>>[]; | 
|  | for (var witness in error.witnesses) { | 
|  | var correctionDataBuffer = AnalyzerDartTemplateBuffer(); | 
|  | witness.toDart(correctionDataBuffer, forCorrection: true); | 
|  | if (correctionDataBuffer.isComplete) { | 
|  | correctionData.add(correctionDataBuffer.parts); | 
|  | } | 
|  | } | 
|  | _errorReporter.atToken( | 
|  | switchKeyword, | 
|  | isSwitchExpression | 
|  | ? CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH_EXPRESSION | 
|  | : CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH_STATEMENT, | 
|  | arguments: [ | 
|  | scrutineeType, | 
|  | errorBuffer.toString(), | 
|  | correctionTextBuffer.toString(), | 
|  | ], | 
|  | data: correctionData.isNotEmpty ? correctionData : null, | 
|  | ); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Record data for testing. | 
|  | if (exhaustivenessDataForTesting != null) { | 
|  | for (var i = 0; i < caseSpaces.length; i++) { | 
|  | var caseNode = caseNodesWithSpace[i]; | 
|  | exhaustivenessDataForTesting.caseSpaces[caseNode] = caseSpaces[i]; | 
|  | } | 
|  | exhaustivenessDataForTesting.switchScrutineeType[node] = scrutineeTypeEx; | 
|  | exhaustivenessDataForTesting.switchCases[node] = caseSpaces; | 
|  | for (var error in errors) { | 
|  | if (error is UnreachableCaseError) { | 
|  | exhaustivenessDataForTesting.errors[caseNodesWithSpace[error.index]] = | 
|  | error; | 
|  | } else if (reportNonExhaustive) { | 
|  | exhaustivenessDataForTesting.errors[node] = error; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void _validateSwitchStatement_nullSafety(SwitchStatement node) { | 
|  | void validateExpression(Expression expression) { | 
|  | var expressionValue = _evaluateAndReportError( | 
|  | expression, | 
|  | CompileTimeErrorCode.NON_CONSTANT_CASE_EXPRESSION, | 
|  | ); | 
|  | if (expressionValue is! DartObjectImpl) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | var featureSet = _currentLibrary.featureSet; | 
|  | if (!featureSet.isEnabled(Feature.patterns)) { | 
|  | var expressionType = expressionValue.type; | 
|  | if (!expressionValue.hasPrimitiveEquality(featureSet)) { | 
|  | _errorReporter.atNode( | 
|  | expression, | 
|  | CompileTimeErrorCode.CASE_EXPRESSION_TYPE_IMPLEMENTS_EQUALS, | 
|  | arguments: [expressionType], | 
|  | ); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | for (var switchMember in node.members) { | 
|  | if (switchMember is SwitchCase) { | 
|  | validateExpression(switchMember.expression); | 
|  | } else if (switchMember is SwitchPatternCase) { | 
|  | if (_currentLibrary.featureSet.isEnabled(Feature.patterns)) { | 
|  | switchMember.accept(this); | 
|  | } else { | 
|  | var pattern = switchMember.guardedPattern.pattern; | 
|  | if (pattern is ConstantPattern) { | 
|  | validateExpression(pattern.expression.unParenthesized); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Runs [f] with new [_constantPatternValues]. | 
|  | void _withConstantPatternValues( | 
|  | void Function(Map<Expression, DartObjectImpl> mapPatternKeyValues, | 
|  | Map<ConstantPattern, DartObjectImpl> constantPatternValues) | 
|  | f, | 
|  | ) { | 
|  | var previousMapKeyValues = _mapPatternKeyValues; | 
|  | var previousConstantPatternValues = _constantPatternValues; | 
|  | var mapKeyValues = _mapPatternKeyValues = {}; | 
|  | var constantValues = _constantPatternValues = {}; | 
|  | f(mapKeyValues, constantValues); | 
|  | _mapPatternKeyValues = previousMapKeyValues; | 
|  | _constantPatternValues = previousConstantPatternValues; | 
|  | } | 
|  | } | 
|  |  | 
|  | class _ConstLiteralVerifier { | 
|  | final ConstantVerifier verifier; | 
|  | final ErrorCode errorCode; | 
|  | final DartType? listElementType; | 
|  | final _SetVerifierConfig? setConfig; | 
|  | final _MapVerifierConfig? mapConfig; | 
|  |  | 
|  | _ConstLiteralVerifier( | 
|  | this.verifier, { | 
|  | required this.errorCode, | 
|  | this.listElementType, | 
|  | this.mapConfig, | 
|  | this.setConfig, | 
|  | }); | 
|  |  | 
|  | bool verify(CollectionElement element) { | 
|  | if (element is Expression) { | 
|  | var value = verifier._evaluateAndReportError(element, errorCode); | 
|  | if (value is! DartObjectImpl) return false; | 
|  |  | 
|  | var listElementType = this.listElementType; | 
|  | if (listElementType != null) { | 
|  | return _validateListExpression(listElementType, element, value); | 
|  | } | 
|  |  | 
|  | var setConfig = this.setConfig; | 
|  | if (setConfig != null) { | 
|  | return _validateSetExpression(setConfig, element, value); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } else if (element is ForElement) { | 
|  | verifier._errorReporter.atNode( | 
|  | element, | 
|  | CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT, | 
|  | ); | 
|  | return false; | 
|  | } else if (element is IfElement) { | 
|  | var conditionConstant = | 
|  | verifier._evaluateAndReportError(element.expression, errorCode); | 
|  | if (conditionConstant is! DartObjectImpl) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // The errors have already been reported. | 
|  | if (!conditionConstant.isBool) return false; | 
|  |  | 
|  | var conditionValue = conditionConstant.toBoolValue(); | 
|  |  | 
|  | var thenValid = true; | 
|  | var elseValid = true; | 
|  | var thenElement = element.thenElement; | 
|  | var elseElement = element.elseElement; | 
|  |  | 
|  | if (conditionValue == null) { | 
|  | thenValid = _reportNotPotentialConstants(thenElement); | 
|  | if (elseElement != null) { | 
|  | elseValid = _reportNotPotentialConstants(elseElement); | 
|  | } | 
|  | return thenValid && elseValid; | 
|  | } | 
|  |  | 
|  | // Only validate the relevant branch as per `conditionValue`. This | 
|  | // avoids issues like duplicate values showing up in a const set, when | 
|  | // they occur in each branch, like `{if (x) ...[1] else [1, 2]}`. | 
|  | if (conditionValue) { | 
|  | thenValid = verify(thenElement); | 
|  | if (elseElement != null) { | 
|  | elseValid = _reportNotPotentialConstants(elseElement); | 
|  | } | 
|  | } else { | 
|  | thenValid = _reportNotPotentialConstants(thenElement); | 
|  | if (elseElement != null) { | 
|  | elseValid = verify(elseElement); | 
|  | } | 
|  | } | 
|  |  | 
|  | return thenValid && elseValid; | 
|  | } else if (element is MapLiteralEntry) { | 
|  | return _validateMapLiteralEntry(element); | 
|  | } else if (element is SpreadElement) { | 
|  | var value = | 
|  | verifier._evaluateAndReportError(element.expression, errorCode); | 
|  | if (value is! DartObjectImpl) return false; | 
|  |  | 
|  | if (listElementType != null || setConfig != null) { | 
|  | return _validateListOrSetSpread(element, value); | 
|  | } | 
|  |  | 
|  | var mapConfig = this.mapConfig; | 
|  | if (mapConfig != null) { | 
|  | return _validateMapSpread(mapConfig, element, value); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  | throw UnsupportedError( | 
|  | 'Unhandled type of collection element: ${element.runtimeType}', | 
|  | ); | 
|  | } | 
|  |  | 
|  | /// Returns whether the [node] is a potential constant. | 
|  | bool _reportNotPotentialConstants(AstNode node) { | 
|  | var notPotentiallyConstants = getNotPotentiallyConstants( | 
|  | node, | 
|  | featureSet: verifier._currentLibrary.featureSet, | 
|  | ); | 
|  | if (notPotentiallyConstants.isEmpty) return true; | 
|  |  | 
|  | for (var notConst in notPotentiallyConstants) { | 
|  | CompileTimeErrorCode errorCode; | 
|  | if (listElementType != null) { | 
|  | errorCode = CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT; | 
|  | } else if (mapConfig != null) { | 
|  | errorCode = CompileTimeErrorCode.NON_CONSTANT_MAP_ELEMENT; | 
|  | for (AstNode? parent = notConst; | 
|  | parent != null; | 
|  | parent = parent.parent) { | 
|  | if (parent is MapLiteralEntry) { | 
|  | if (parent.key == notConst) { | 
|  | errorCode = CompileTimeErrorCode.NON_CONSTANT_MAP_KEY; | 
|  | } else { | 
|  | errorCode = CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE; | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | } else if (setConfig != null) { | 
|  | errorCode = CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT; | 
|  | } else { | 
|  | throw UnimplementedError(); | 
|  | } | 
|  | verifier._errorReporter.atNode( | 
|  | notConst, | 
|  | errorCode, | 
|  | ); | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool _validateListExpression( | 
|  | DartType listElementType, Expression expression, DartObjectImpl value) { | 
|  | if (!verifier._runtimeTypeMatch(value, listElementType)) { | 
|  | verifier._errorReporter.atNode( | 
|  | expression, | 
|  | CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE, | 
|  | arguments: [value.type, listElementType], | 
|  | ); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool _validateListOrSetSpread(SpreadElement element, DartObjectImpl value) { | 
|  | var listValue = value.toListValue(); | 
|  | var setValue = value.toSetValue(); | 
|  | var iterableValue = listValue ?? setValue; | 
|  |  | 
|  | if (iterableValue == null) { | 
|  | if (value.isNull && element.isNullAware) { | 
|  | return true; | 
|  | } | 
|  | // TODO(kallentu): Consolidate this with | 
|  | // [ConstantVisitor._addElementsToList] and the other similar | 
|  | // _addElementsTo methods.. | 
|  | verifier._errorReporter.atNode( | 
|  | element.expression, | 
|  | CompileTimeErrorCode.CONST_SPREAD_EXPECTED_LIST_OR_SET, | 
|  | ); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | var setConfig = this.setConfig; | 
|  | if (setConfig == null) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (listValue != null) { | 
|  | var featureSet = verifier._currentLibrary.featureSet; | 
|  | if (!listValue.every((e) => e.hasPrimitiveEquality(featureSet))) { | 
|  | verifier._errorReporter.atNode( | 
|  | element, | 
|  | CompileTimeErrorCode.CONST_SET_ELEMENT_NOT_PRIMITIVE_EQUALITY, | 
|  | arguments: [value.type], | 
|  | ); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (var item in iterableValue) { | 
|  | Expression expression = element.expression; | 
|  | var existingValue = setConfig.uniqueValues[item]; | 
|  | if (existingValue != null) { | 
|  | setConfig.duplicateElements[expression] = existingValue; | 
|  | } else { | 
|  | setConfig.uniqueValues[item] = expression; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool _validateMapLiteralEntry(MapLiteralEntry entry) { | 
|  | var config = mapConfig; | 
|  | if (config == null) return false; | 
|  |  | 
|  | var keyExpression = entry.key; | 
|  | var valueExpression = entry.value; | 
|  |  | 
|  | var keyValue = verifier._evaluateAndReportError( | 
|  | keyExpression, | 
|  | CompileTimeErrorCode.NON_CONSTANT_MAP_KEY, | 
|  | ); | 
|  | var valueValue = verifier._evaluateAndReportError( | 
|  | valueExpression, | 
|  | CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE, | 
|  | ); | 
|  |  | 
|  | if (keyValue is DartObjectImpl) { | 
|  | var keyType = keyValue.type; | 
|  |  | 
|  | if (!verifier._runtimeTypeMatch(keyValue, config.keyType)) { | 
|  | verifier._errorReporter.atNode( | 
|  | keyExpression, | 
|  | CompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE, | 
|  | arguments: [keyType, config.keyType], | 
|  | ); | 
|  | } | 
|  |  | 
|  | var featureSet = verifier._currentLibrary.featureSet; | 
|  | if (!keyValue.hasPrimitiveEquality(featureSet)) { | 
|  | verifier._errorReporter.atNode( | 
|  | keyExpression, | 
|  | CompileTimeErrorCode.CONST_MAP_KEY_NOT_PRIMITIVE_EQUALITY, | 
|  | arguments: [keyType], | 
|  | ); | 
|  | } | 
|  |  | 
|  | var existingKey = config.uniqueKeys[keyValue]; | 
|  | if (existingKey != null) { | 
|  | config.duplicateKeys[keyExpression] = existingKey; | 
|  | } else { | 
|  | config.uniqueKeys[keyValue] = keyExpression; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (valueValue is DartObjectImpl) { | 
|  | if (!verifier._runtimeTypeMatch(valueValue, config.valueType)) { | 
|  | verifier._errorReporter.atNode( | 
|  | valueExpression, | 
|  | CompileTimeErrorCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE, | 
|  | arguments: [valueValue.type, config.valueType], | 
|  | ); | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool _validateMapSpread( | 
|  | _MapVerifierConfig config, | 
|  | SpreadElement element, | 
|  | DartObjectImpl value, | 
|  | ) { | 
|  | if (value.isNull && element.isNullAware) { | 
|  | return true; | 
|  | } | 
|  | var map = value.toMapValue(); | 
|  | if (map != null) { | 
|  | // TODO(brianwilkerson): Figure out how to improve the error messages. They | 
|  | //  currently point to the whole spread expression, but the key and/or | 
|  | //  value being referenced might not be located there (if it's referenced | 
|  | //  through a const variable). | 
|  | for (var keyValue in map.keys) { | 
|  | var existingKey = config.uniqueKeys[keyValue]; | 
|  | if (existingKey != null) { | 
|  | config.duplicateKeys[element.expression] = existingKey; | 
|  | } else { | 
|  | config.uniqueKeys[keyValue] = element.expression; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  | verifier._errorReporter.atNode( | 
|  | element.expression, | 
|  | CompileTimeErrorCode.CONST_SPREAD_EXPECTED_MAP, | 
|  | ); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool _validateSetExpression( | 
|  | _SetVerifierConfig config, | 
|  | Expression expression, | 
|  | DartObjectImpl value, | 
|  | ) { | 
|  | if (!verifier._runtimeTypeMatch(value, config.elementType)) { | 
|  | verifier._errorReporter.atNode( | 
|  | expression, | 
|  | CompileTimeErrorCode.SET_ELEMENT_TYPE_NOT_ASSIGNABLE, | 
|  | arguments: [value.type, config.elementType], | 
|  | ); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | var featureSet = verifier._currentLibrary.featureSet; | 
|  | if (!value.hasPrimitiveEquality(featureSet)) { | 
|  | verifier._errorReporter.atNode( | 
|  | expression, | 
|  | CompileTimeErrorCode.CONST_SET_ELEMENT_NOT_PRIMITIVE_EQUALITY, | 
|  | arguments: [value.type], | 
|  | ); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | var existingValue = config.uniqueValues[value]; | 
|  | if (existingValue != null) { | 
|  | config.duplicateElements[expression] = existingValue; | 
|  | } else { | 
|  | config.uniqueValues[value] = expression; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | class _MapVerifierConfig { | 
|  | final DartType keyType; | 
|  | final DartType valueType; | 
|  | final Map<DartObject, Expression> uniqueKeys = {}; | 
|  | final Map<Expression, Expression> duplicateKeys = {}; | 
|  |  | 
|  | _MapVerifierConfig({ | 
|  | required this.keyType, | 
|  | required this.valueType, | 
|  | }); | 
|  | } | 
|  |  | 
|  | class _SetVerifierConfig { | 
|  | final DartType elementType; | 
|  | final Map<DartObject, Expression> uniqueValues = {}; | 
|  | final Map<Expression, Expression> duplicateElements = {}; | 
|  |  | 
|  | _SetVerifierConfig({ | 
|  | required this.elementType, | 
|  | }); | 
|  | } | 
|  |  | 
|  | extension on Expression { | 
|  | /// Returns whether `this` is found in a constant expression. | 
|  | /// | 
|  | /// This does not check whether `this` is found in a constant context. | 
|  | bool get inConstantExpression { | 
|  | AstNode child = this; | 
|  | var parent = child.parent; | 
|  | while (parent != null) { | 
|  | if (parent is DefaultFormalParameter && child == parent.defaultValue) { | 
|  | // A parameter default value does not constitute a constant context, but | 
|  | // must be a constant expression. | 
|  | return true; | 
|  | } else if (parent is VariableDeclaration && child == parent.initializer) { | 
|  | var declarationList = parent.parent; | 
|  | if (declarationList is VariableDeclarationList) { | 
|  | var declarationListParent = declarationList.parent; | 
|  | if (declarationListParent is FieldDeclaration && | 
|  | !declarationListParent.isStatic) { | 
|  | var container = declarationListParent.parent; | 
|  | if (container is ClassDeclaration) { | 
|  | var enclosingClass = container.declaredElement; | 
|  | if (enclosingClass is ClassElementImpl) { | 
|  | // A field initializer of a class with at least one generative | 
|  | // const constructor does not constitute a constant context, but | 
|  | // must be a constant expression. | 
|  | return enclosingClass.hasGenerativeConstConstructor; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } else { | 
|  | child = parent; | 
|  | parent = child.parent; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  | } |