From 742ba5c20dfecfb20cb717e299a422b806097757 Mon Sep 17 00:00:00 2001 From: Kallen Tu Date: Fri, 22 Sep 2023 16:13:10 +0000 Subject: [PATCH] [analyzer] Change the const evaluation result of variables to be Constant. We rely on the result of the `ConstantVisitor` to indicate whether we have an error or a valid constant value. This CL changes `evaluationResult` to be a `Constant` and changes error reporting to occur at a POE for evaluating a constant. Last few chunks of cleaning up the constant evaluator, woo! Change-Id: Icd41a4fcbab0626df36c6a83cd60ecbb59c2dcf0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/324573 Reviewed-by: Brian Wilkerson Reviewed-by: Konstantin Shcheglov Reviewed-by: Lasse Nielsen Commit-Queue: Kallen Tu --- .../lib/src/status/tree_writer.dart | 17 +- .../src/dart/constant/constant_verifier.dart | 324 +++++----- .../lib/src/dart/constant/evaluation.dart | 585 ++++++++---------- pkg/analyzer/lib/src/dart/constant/value.dart | 103 ++- .../lib/src/dart/element/element.dart | 57 +- .../lib/src/error/dead_code_verifier.dart | 39 +- pkg/analyzer/messages.yaml | 6 +- .../test/src/dart/analysis/driver_test.dart | 18 +- .../src/dart/constant/evaluation_test.dart | 28 +- .../src/dart/resolution/constant_test.dart | 2 +- .../const_eval_property_access_test.dart | 15 +- .../const_eval_type_string_test.dart | 2 - ...tialized_with_non_constant_value_test.dart | 14 - .../const_with_type_parameters_test.dart | 3 - ...ern_with_non_constant_expression_test.dart | 3 - pkg/analyzer/tool/diagnostics/diagnostics.md | 6 +- ..._functions_instance_fields_error_test.dart | 2 +- .../const_functions_instance_fields_test.dart | 2 +- tests/language/method/not_found_test.dart | 2 + .../language/string/interpolation1_test.dart | 1 - tests/language_2/method/not_found_test.dart | 2 + .../string/interpolation1_test.dart | 4 +- 22 files changed, 649 insertions(+), 586 deletions(-) diff --git a/pkg/analysis_server/lib/src/status/tree_writer.dart b/pkg/analysis_server/lib/src/status/tree_writer.dart index a1fe2482237..a385faec504 100644 --- a/pkg/analysis_server/lib/src/status/tree_writer.dart +++ b/pkg/analysis_server/lib/src/status/tree_writer.dart @@ -6,6 +6,7 @@ import 'dart:convert'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/exception/exception.dart'; +import 'package:analyzer/src/dart/constant/value.dart'; import 'package:analyzer/src/dart/element/element.dart'; import 'package:analyzer/src/generated/source.dart'; @@ -67,13 +68,15 @@ mixin TreeWriter { var buffer = StringBuffer(); buffer.write(_toString(value.element)); var result = value.evaluationResult; - if (result == null) { - buffer.write(': no result'); - } else { - buffer.write(': value = '); - buffer.write(result.value); - buffer.write('; errors = '); - buffer.write(result.errors); + switch (result) { + case null: + buffer.write(': no result'); + case DartObjectImpl(): + buffer.write(': value = '); + buffer.write(result); + case InvalidConstant(): + buffer.write('; errors = '); + buffer.writeAll(value.constantEvaluationErrors, ', '); } return buffer.toString(); } else { diff --git a/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart b/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart index 3a79fd79747..bc5df59281b 100644 --- a/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart +++ b/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart @@ -130,23 +130,21 @@ class ConstantVerifier extends RecursiveAstVisitor { @override void visitConstantPattern(ConstantPattern node) { - super.visitConstantPattern(node); - var expression = node.expression.unParenthesized; if (expression.typeOrThrow is InvalidType) { return; } - DartObjectImpl? value = _validate( + var value = _evaluateAndReportError( expression, CompileTimeErrorCode.CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION, ); - if (value != null) { + if (value is DartObjectImpl) { if (_currentLibrary.featureSet.isEnabled(Feature.patterns)) { _constantPatternValues?[node] = value; if (value.hasPrimitiveEquality(_currentLibrary.featureSet)) { - final constantType = value.type; - final matchedValueType = node.matchedValueType; + var constantType = value.type; + var matchedValueType = node.matchedValueType; if (matchedValueType != null) { if (!_canBeEqual(constantType, matchedValueType)) { _errorReporter.reportErrorForNode( @@ -154,10 +152,12 @@ class ConstantVerifier extends RecursiveAstVisitor { node, [matchedValueType, constantType], ); + return; } } } } + super.visitConstantPattern(node); } } @@ -210,8 +210,8 @@ class ConstantVerifier extends RecursiveAstVisitor { var element = node.declaredElement as ConstFieldElementImpl; var result = element.evaluationResult; - if (result != null) { - _reportErrors(result.errors, null); + if (result is InvalidConstant) { + _reportError(result, null); } } @@ -320,11 +320,11 @@ class ConstantVerifier extends RecursiveAstVisitor { element.accept(this); if (element is MapPatternEntry) { var key = element.key; - var keyValue = _validate( + var keyValue = _evaluateAndReportError( key, CompileTimeErrorCode.NON_CONSTANT_MAP_PATTERN_KEY, ); - if (keyValue != null) { + if (keyValue is DartObjectImpl) { _mapPatternKeyValues?[key] = keyValue; var existingKey = uniqueKeys[keyValue]; if (existingKey != null) { @@ -357,7 +357,7 @@ class ConstantVerifier extends RecursiveAstVisitor { void visitRelationalPattern(RelationalPattern node) { super.visitRelationalPattern(node); - _validate( + _evaluateAndReportError( node.operand, CompileTimeErrorCode.NON_CONSTANT_RELATIONAL_PATTERN_EXPRESSION, ); @@ -483,11 +483,13 @@ class ConstantVerifier extends RecursiveAstVisitor { assert(!node.isConst); return; } - if (node.isConst) { - _reportErrors(result.errors, - CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE); - } else { - _reportErrors(result.errors, null); + if (result is InvalidConstant) { + if (node.isConst) { + _reportError(result, + CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE); + } else { + _reportError(result, null); + } } } } @@ -571,104 +573,137 @@ class ConstantVerifier extends RecursiveAstVisitor { } } - /// Report any errors in the given list. Except for special cases, use the - /// given error code rather than the one reported in the error. + /// Evaluates [expression] and reports any evaluation error. /// - /// @param errors the errors that need to be reported - /// @param errorCode the error code to be used - void _reportErrors(List errors, ErrorCode? errorCode) { - int length = errors.length; - for (int i = 0; i < length; i++) { - AnalysisError data = errors[i]; - ErrorCode dataErrorCode = data.errorCode; - if (identical(dataErrorCode, - CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD) || - identical(dataErrorCode, - CompileTimeErrorCode.CONST_EVAL_METHOD_INVOCATION) || - identical(dataErrorCode, - CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION) || - identical( - dataErrorCode, CompileTimeErrorCode.CONST_EVAL_THROWS_IDBZE) || - identical(dataErrorCode, - CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING) || - identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL) || - identical( - dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_INT) || - identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_INT) || - identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM) || - identical( - dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_STRING) || - identical(dataErrorCode, - CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT) || - identical(dataErrorCode, - CompileTimeErrorCode.CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH) || - identical(dataErrorCode, - CompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH) || - identical(dataErrorCode, CompileTimeErrorCode.CONST_TYPE_PARAMETER) || - identical( - dataErrorCode, CompileTimeErrorCode.CONST_SPREAD_EXPECTED_MAP) || - identical(dataErrorCode, - CompileTimeErrorCode.CONST_SPREAD_EXPECTED_LIST_OR_SET) || - identical( - dataErrorCode, - CompileTimeErrorCode - .CONST_WITH_TYPE_PARAMETERS_FUNCTION_TEAROFF) || - identical( - dataErrorCode, CompileTimeErrorCode.VARIABLE_TYPE_MISMATCH) || - identical(dataErrorCode, CompileTimeErrorCode.NON_BOOL_CONDITION) || - identical( - dataErrorCode, - CompileTimeErrorCode - .NON_CONSTANT_DEFAULT_VALUE_FROM_DEFERRED_LIBRARY) || - identical( - dataErrorCode, - CompileTimeErrorCode - .NON_CONSTANT_MAP_KEY_FROM_DEFERRED_LIBRARY) || - identical( - dataErrorCode, - CompileTimeErrorCode - .NON_CONSTANT_MAP_VALUE_FROM_DEFERRED_LIBRARY) || - identical(dataErrorCode, - CompileTimeErrorCode.SET_ELEMENT_FROM_DEFERRED_LIBRARY) || - identical(dataErrorCode, - CompileTimeErrorCode.SPREAD_EXPRESSION_FROM_DEFERRED_LIBRARY) || - identical( - dataErrorCode, - CompileTimeErrorCode - .NON_CONSTANT_CASE_EXPRESSION_FROM_DEFERRED_LIBRARY) || - identical( - dataErrorCode, - CompileTimeErrorCode - .INVALID_ANNOTATION_CONSTANT_VALUE_FROM_DEFERRED_LIBRARY) || - identical( - dataErrorCode, - CompileTimeErrorCode - .IF_ELEMENT_CONDITION_FROM_DEFERRED_LIBRARY) || - identical( - dataErrorCode, - CompileTimeErrorCode - .CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE_FROM_DEFERRED_LIBRARY) || - identical( - dataErrorCode, - CompileTimeErrorCode - .NON_CONSTANT_LIST_ELEMENT_FROM_DEFERRED_LIBRARY) || - identical( - dataErrorCode, - CompileTimeErrorCode - .CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE_FROM_DEFERRED_LIBRARY) || - identical(dataErrorCode, - CompileTimeErrorCode.PATTERN_CONSTANT_FROM_DEFERRED_LIBRARY)) { - _errorReporter.reportError(data); - } else if (errorCode != null) { - _errorReporter.reportError( - AnalysisError.tmp( - source: data.source, - offset: data.offset, - length: data.length, - errorCode: errorCode, - ), - ); - } + /// 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, + isNonNullableByDefault: _currentLibrary.isNonNullableByDefault, + ); + 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_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_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 + .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, + ), + ); } } @@ -701,28 +736,6 @@ class ConstantVerifier extends RecursiveAstVisitor { return _currentLibrary.typeSystem.runtimeTypeMatch(obj, type); } - /// Validate that the given expression is a compile time constant. Return the - /// value of the compile time constant, or `null` if the expression is not a - /// compile time constant. - /// - /// @param expression the expression to be validated - /// @param errorCode the error code to be used if the expression is not a - /// compile time constant - /// @return the value of the compile time constant - DartObjectImpl? _validate(Expression expression, ErrorCode errorCode) { - RecordingErrorListener errorListener = RecordingErrorListener(); - ErrorReporter subErrorReporter = ErrorReporter( - errorListener, - _errorReporter.source, - isNonNullableByDefault: _currentLibrary.isNonNullableByDefault, - ); - var result = expression.accept( - ConstantVisitor(_evaluationEngine, _currentLibrary, subErrorReporter)); - _reportErrors(errorListener.errors, errorCode); - // TODO(kallentu): Evaluate whether we want to change the return type. - return result is DartObjectImpl ? result : null; - } - /// Validate that if the passed arguments are constant expressions. /// /// @param argumentList the argument list to evaluate @@ -730,7 +743,7 @@ class ConstantVerifier extends RecursiveAstVisitor { for (Expression argument in argumentList.arguments) { Expression realArgument = argument is NamedExpression ? argument.expression : argument; - _validate( + _evaluateAndReportError( realArgument, CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT); } } @@ -767,7 +780,7 @@ class ConstantVerifier extends RecursiveAstVisitor { for (FormalParameter parameter in parameters.parameters) { if (parameter is DefaultFormalParameter) { var defaultValue = parameter.defaultValue; - DartObjectImpl? result; + Constant? result; if (defaultValue == null) { result = DartObjectImpl( _typeSystem, @@ -777,12 +790,12 @@ class ConstantVerifier extends RecursiveAstVisitor { } else if (defaultValue.typeOrThrow is InvalidType) { // We have already reported an error. } else { - result = _validate( + result = _evaluateAndReportError( defaultValue, CompileTimeErrorCode.NON_CONSTANT_DEFAULT_VALUE); } VariableElementImpl element = parameter.declaredElement as VariableElementImpl; - element.evaluationResult = EvaluationResultImpl(result); + element.evaluationResult = result; } } } @@ -958,11 +971,11 @@ class ConstantVerifier extends RecursiveAstVisitor { if (switchMember is SwitchCase) { Expression expression = switchMember.expression; - var expressionValue = _validate( + var expressionValue = _evaluateAndReportError( expression, CompileTimeErrorCode.NON_CONSTANT_CASE_EXPRESSION, ); - if (expressionValue == null) { + if (expressionValue is! DartObjectImpl) { continue; } firstValue ??= expressionValue; @@ -1004,11 +1017,11 @@ class ConstantVerifier extends RecursiveAstVisitor { void _validateSwitchStatement_nullSafety(SwitchStatement node) { void validateExpression(Expression expression) { - var expressionValue = _validate( + var expressionValue = _evaluateAndReportError( expression, CompileTimeErrorCode.NON_CONSTANT_CASE_EXPRESSION, ); - if (expressionValue == null) { + if (expressionValue is! DartObjectImpl) { return; } @@ -1074,8 +1087,8 @@ class _ConstLiteralVerifier { bool verify(CollectionElement element) { if (element is Expression) { - var value = verifier._validate(element, errorCode); - if (value == null) return false; + var value = verifier._evaluateAndReportError(element, errorCode); + if (value is! DartObjectImpl) return false; final listElementType = this.listElementType; if (listElementType != null) { @@ -1093,8 +1106,12 @@ class _ConstLiteralVerifier { CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT, element); return false; } else if (element is IfElement) { - var conditionValue = verifier._validate(element.expression, errorCode); - var conditionBool = conditionValue?.toBoolValue(); + var conditionValue = + verifier._evaluateAndReportError(element.expression, errorCode); + if (conditionValue is! DartObjectImpl) { + return false; + } + var conditionBool = conditionValue.toBoolValue(); // The errors have already been reported. if (conditionBool == null) return false; @@ -1119,8 +1136,9 @@ class _ConstLiteralVerifier { } else if (element is MapLiteralEntry) { return _validateMapLiteralEntry(element); } else if (element is SpreadElement) { - var value = verifier._validate(element.expression, errorCode); - if (value == null) return false; + var value = + verifier._evaluateAndReportError(element.expression, errorCode); + if (value is! DartObjectImpl) return false; if (listElementType != null || setConfig != null) { return _validateListOrSetSpread(element, value); @@ -1245,16 +1263,16 @@ class _ConstLiteralVerifier { var keyExpression = entry.key; var valueExpression = entry.value; - var keyValue = verifier._validate( + var keyValue = verifier._evaluateAndReportError( keyExpression, CompileTimeErrorCode.NON_CONSTANT_MAP_KEY, ); - var valueValue = verifier._validate( + var valueValue = verifier._evaluateAndReportError( valueExpression, CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE, ); - if (keyValue != null) { + if (keyValue is DartObjectImpl) { var keyType = keyValue.type; if (!verifier._runtimeTypeMatch(keyValue, config.keyType)) { @@ -1282,7 +1300,7 @@ class _ConstLiteralVerifier { } } - if (valueValue != null) { + if (valueValue is DartObjectImpl) { if (!verifier._runtimeTypeMatch(valueValue, config.valueType)) { verifier._errorReporter.reportErrorForNode( CompileTimeErrorCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE, diff --git a/pkg/analyzer/lib/src/dart/constant/evaluation.dart b/pkg/analyzer/lib/src/dart/constant/evaluation.dart index fca15a1304b..010006669e5 100644 --- a/pkg/analyzer/lib/src/dart/constant/evaluation.dart +++ b/pkg/analyzer/lib/src/dart/constant/evaluation.dart @@ -98,70 +98,69 @@ class ConstantEvaluationEngine { if (constant is ConstVariableElement) { var defaultValue = constant.constantInitializer; if (defaultValue != null) { - RecordingErrorListener errorListener = RecordingErrorListener(); - ErrorReporter errorReporter = ErrorReporter( + var errorListener = RecordingErrorListener(); + var errorReporter = ErrorReporter( errorListener, constant.source!, isNonNullableByDefault: library.isNonNullableByDefault, ); - // TODO(kallentu): Remove unwrapping of Constant. - var dartConstant = defaultValue - .accept(ConstantVisitor(this, library, errorReporter)); - var dartObject = dartConstant is DartObjectImpl ? dartConstant : null; - constant.evaluationResult = - EvaluationResultImpl(dartObject, errorListener.errors); + var constantVisitor = ConstantVisitor(this, library, errorReporter); + var dartConstant = constantVisitor.evaluateConstant(defaultValue); + constant.evaluationResult = dartConstant; } else { - constant.evaluationResult = EvaluationResultImpl( - _nullObject(library), - ); + constant.evaluationResult = _nullObject(library); } } } else if (constant is VariableElementImpl) { var constantInitializer = constant.constantInitializer; if (constantInitializer != null) { - RecordingErrorListener errorListener = RecordingErrorListener(); - ErrorReporter errorReporter = ErrorReporter( + var errorListener = RecordingErrorListener(); + var errorReporter = ErrorReporter( errorListener, constant.source!, isNonNullableByDefault: library.isNonNullableByDefault, ); - // TODO(kallentu): Remove unwrapping of Constant. - var dartConstant = constantInitializer - .accept(ConstantVisitor(this, library, errorReporter)); - var dartObject = dartConstant is DartObjectImpl ? dartConstant : null; - // Only check the type for truly const declarations (don't check final - // fields with initializers, since their types may be generic. The type - // of the final field will be checked later, when the constructor is - // invoked). - if (dartObject != null && constant.isConst) { - if (!library.typeSystem.runtimeTypeMatch(dartObject, constant.type)) { - // If the static types are mismatched, an error would have already - // been reported. - if (library.typeSystem.isAssignableTo( - constantInitializer.typeOrThrow, constant.type)) { - errorReporter.reportErrorForNode( - CompileTimeErrorCode.VARIABLE_TYPE_MISMATCH, - constantInitializer, - [dartObject.type, constant.type]); + var constantVisitor = ConstantVisitor(this, library, errorReporter); + var dartConstant = + constantVisitor.evaluateConstant(constantInitializer); + if (dartConstant is DartObjectImpl) { + // Only check the type for truly const declarations (don't check final + // fields with initializers, since their types may be generic. The type + // of the final field will be checked later, when the constructor is + // invoked). + if (constant.isConst) { + if (!library.typeSystem + .runtimeTypeMatch(dartConstant, constant.type)) { + // If the static types are mismatched, an error would have already + // been reported. + if (library.typeSystem.isAssignableTo( + constantInitializer.typeOrThrow, constant.type)) { + errorReporter.reportErrorForNode( + CompileTimeErrorCode.VARIABLE_TYPE_MISMATCH, + constantInitializer, + [dartConstant.type, constant.type]); + constant.evaluationResult = InvalidConstant.forEntity( + constantInitializer, + CompileTimeErrorCode.VARIABLE_TYPE_MISMATCH, + arguments: [dartConstant.type, constant.type]); + return; + } } + + // Associate with the variable. + dartConstant = DartObjectImpl.forVariable(dartConstant, constant); } - // Associate with the variable. - dartObject = DartObjectImpl.forVariable(dartObject, constant); - } - - if (dartObject != null) { var enumConstant = _enumConstant(constant); if (enumConstant != null) { - dartObject.updateEnumConstant( + dartConstant.updateEnumConstant( index: enumConstant.index, name: enumConstant.name, ); } } - constant.evaluationResult = - EvaluationResultImpl(dartObject, errorListener.errors); + constant.evaluationResult = dartConstant; } } else if (constant is ConstructorElementImpl) { if (constant.isConst) { @@ -185,19 +184,18 @@ class ConstantEvaluationEngine { // This could happen in the event that the annotation refers to a // non-constant. The error is detected elsewhere, so just silently // ignore it here. - constant.evaluationResult = EvaluationResultImpl(null); + constant.evaluationResult = null; } } else if (element is ConstructorElement && element.isConst && constNode.arguments != null) { - RecordingErrorListener errorListener = RecordingErrorListener(); - ErrorReporter errorReporter = ErrorReporter( + var errorListener = RecordingErrorListener(); + var errorReporter = ErrorReporter( errorListener, constant.source, isNonNullableByDefault: library.isNonNullableByDefault, ); - ConstantVisitor constantVisitor = - ConstantVisitor(this, library, errorReporter); + var constantVisitor = ConstantVisitor(this, library, errorReporter); final result = evaluateAndReportErrorsInConstructorCall( library, constNode, @@ -206,16 +204,13 @@ class ConstantEvaluationEngine { element, constantVisitor, errorReporter); - // TODO(kallentu): Report the InvalidConstant returned from the - // constructor call. - final evaluationConstant = result is DartObjectImpl ? result : null; - constant.evaluationResult = - EvaluationResultImpl(evaluationConstant, errorListener.errors); + constant.evaluationResult = result; + constant.additionalErrors = errorListener.errors; } else { // This may happen for invalid code (e.g. failing to pass arguments // to an annotation which references a const constructor). The error // is detected elsewhere, so just silently ignore it here. - constant.evaluationResult = EvaluationResultImpl(null); + constant.evaluationResult = null; } } else if (constant is VariableElement) { // constant is a VariableElement but not a VariableElementImpl. This can @@ -380,22 +375,26 @@ class ConstantEvaluationEngine { formatList(result.errorCode.problemMessage, result.arguments); final contextMessage = DiagnosticMessageImpl( filePath: library.source.fullName, - length: result.entity.length, + length: result.length, message: "The exception is '$formattedMessage' and occurs here.", - offset: result.entity.offset, + offset: result.offset, url: null, ); final errorNode = configuration.errorNode(node); - result = InvalidConstant( + result = InvalidConstant.forEntity( errorNode, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, arguments: [], contextMessages: [...result.contextMessages, contextMessage]); } + if (result.avoidReporting) { + return result; + } + errorReporter.reportErrorForOffset( result.errorCode, - result.entity.offset, - result.entity.length, + result.offset, + result.length, result.arguments, result.contextMessages, ); @@ -448,7 +447,8 @@ class ConstantEvaluationEngine { errorReporter.reportErrorForElement( CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT, constant, []); (constant as VariableElementImpl).evaluationResult = - EvaluationResultImpl(null, errorListener.errors); + InvalidConstant.forElement( + constant, CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT); } else if (constant is ConstructorElement) { // We don't report cycle errors on constructor declarations here since // there is nowhere to put the error information. @@ -605,6 +605,25 @@ class ConstantVisitor extends UnifyingAstVisitor { /// provider. TypeProvider get _typeProvider => _library.typeProvider; + /// Evaluates the expression of [node] using this [ConstantVisitor]. + /// + /// Returns the resulting constant value, which can be an [InvalidConstant] + /// if the expression fails to evaluate to a constant value. + /// + /// The [ConstantVisitor] can't return any `null` values even though + /// [UnifyingAstVisitor] allows it. If we encounter an unexpected `null` + /// value, we will return an [InvalidConstant] instead. + Constant evaluateConstant(AstNode node) { + var result = node.accept(this); + if (result == null) { + // Should never reach this. + throw UnsupportedError( + 'The constant evaluator returned an unexpected null value.', + ); + } + return result; + } + @override Constant visitAdjacentStrings(AdjacentStrings node) { return _concatenateNodes(node, node.strings); @@ -612,11 +631,11 @@ class ConstantVisitor extends UnifyingAstVisitor { @override Constant visitAsExpression(AsExpression node) { - var expression = _getConstant(node.expression); + var expression = evaluateConstant(node.expression); if (expression is! DartObjectImpl) { return expression; } - var type = _getConstant(node.type); + var type = evaluateConstant(node.type); if (type is! DartObjectImpl) { return type; } @@ -629,19 +648,19 @@ class ConstantVisitor extends UnifyingAstVisitor { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode( CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD, node); - return InvalidConstant( + return InvalidConstant.forEntity( node, CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD); } TokenType operatorType = node.operator.type; - var leftResult = _getConstant(node.leftOperand); + var leftResult = evaluateConstant(node.leftOperand); if (leftResult is! DartObjectImpl) { return leftResult; } // Used for the [DartObjectComputer], which will handle any exceptions. DartObjectImpl rightOperandComputer() { - var constant = _getConstant(node.rightOperand); + var constant = evaluateConstant(node.rightOperand); switch (constant) { case DartObjectImpl(): return constant; @@ -678,11 +697,11 @@ class ConstantVisitor extends UnifyingAstVisitor { } } return _dartObjectComputer.lazyQuestionQuestion( - node, leftResult, () => _getConstant(node.rightOperand)); + node, leftResult, () => evaluateConstant(node.rightOperand)); } // Evaluate eager operators. - var rightResult = _getConstant(node.rightOperand); + var rightResult = evaluateConstant(node.rightOperand); if (rightResult is! DartObjectImpl) { return rightResult; } @@ -744,7 +763,7 @@ class ConstantVisitor extends UnifyingAstVisitor { @override Constant visitConditionalExpression(ConditionalExpression node) { var condition = node.condition; - var conditionConstant = _getConstant(condition); + var conditionConstant = evaluateConstant(condition); if (conditionConstant is! DartObjectImpl) { return conditionConstant; } @@ -753,7 +772,7 @@ class ConstantVisitor extends UnifyingAstVisitor { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode( CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL, condition); - return InvalidConstant( + return InvalidConstant.forEntity( condition, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL); } conditionConstant = _dartObjectComputer.applyBooleanConversion( @@ -768,19 +787,19 @@ class ConstantVisitor extends UnifyingAstVisitor { if (error is InvalidConstant) { return error; } - return _getConstant(node.thenExpression); + return evaluateConstant(node.thenExpression); } else if (conditionResultBool == false) { var error = _reportNotPotentialConstants(node.thenExpression); if (error is InvalidConstant) { return error; } - return _getConstant(node.elseExpression); + return evaluateConstant(node.elseExpression); } else { - var thenConstant = _getConstant(node.thenExpression); + var thenConstant = evaluateConstant(node.thenExpression); if (thenConstant is InvalidConstant) { return thenConstant; } - var elseConstant = _getConstant(node.elseExpression); + var elseConstant = evaluateConstant(node.elseExpression); if (elseConstant is InvalidConstant) { return elseConstant; } @@ -795,7 +814,8 @@ class ConstantVisitor extends UnifyingAstVisitor { Constant visitConstructorReference(ConstructorReference node) { var constructorFunctionType = node.typeOrThrow; if (constructorFunctionType is! FunctionType) { - return InvalidConstant(node, CompileTimeErrorCode.INVALID_CONSTANT); + return InvalidConstant.forEntity( + node, CompileTimeErrorCode.INVALID_CONSTANT); } var classType = constructorFunctionType.returnType as InterfaceType; var typeArguments = classType.typeArguments; @@ -817,7 +837,8 @@ class ConstantVisitor extends UnifyingAstVisitor { final constructorElement = node.constructorName.staticElement?.declaration .ifTypeOrNull(); if (constructorElement == null) { - return InvalidConstant(node, CompileTimeErrorCode.INVALID_CONSTANT); + return InvalidConstant.forEntity( + node, CompileTimeErrorCode.INVALID_CONSTANT); } return DartObjectImpl( @@ -839,7 +860,7 @@ class ConstantVisitor extends UnifyingAstVisitor { @override Constant visitFunctionReference(FunctionReference node) { - var functionResult = _getConstant(node.function); + var functionResult = evaluateConstant(node.function); if (functionResult is! DartObjectImpl) { return functionResult; } @@ -863,7 +884,7 @@ class ConstantVisitor extends UnifyingAstVisitor { _errorReporter.reportErrorForNode( CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS_FUNCTION_TEAROFF, node); - return InvalidConstant(node, + return InvalidConstant.forEntity(node, CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS_FUNCTION_TEAROFF); } } @@ -876,7 +897,7 @@ class ConstantVisitor extends UnifyingAstVisitor { var typeArguments = []; for (var typeArgument in typeArgumentList.arguments) { - var typeArgumentConstant = _getConstant(typeArgument); + var typeArgumentConstant = evaluateConstant(typeArgument); switch (typeArgumentConstant) { case InvalidConstant( errorCode: CompileTimeErrorCode.CONST_TYPE_PARAMETER @@ -887,14 +908,14 @@ class ConstantVisitor extends UnifyingAstVisitor { _errorReporter.reportErrorForNode( CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS_FUNCTION_TEAROFF, typeArgument); - return InvalidConstant(typeArgument, + return InvalidConstant.forEntity(typeArgument, CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS_FUNCTION_TEAROFF); case InvalidConstant(): return typeArgumentConstant; case DartObjectImpl(): var typeArgumentType = typeArgumentConstant.toTypeValue(); if (typeArgumentType == null) { - return InvalidConstant( + return InvalidConstant.forEntity( typeArgument, CompileTimeErrorCode.INVALID_CONSTANT); } // TODO(srawlins): Test type alias types (`typedef i = int`) used as @@ -904,7 +925,7 @@ class ConstantVisitor extends UnifyingAstVisitor { } } return _dartObjectComputer.typeInstantiate( - functionResult, typeArguments, node.function); + functionResult, typeArguments, node.function, typeArgumentList); } @override @@ -929,7 +950,8 @@ class ConstantVisitor extends UnifyingAstVisitor { // Couldn't resolve the constructor so we can't compute a value. No // problem - the error has already been reported. // TODO(kallentu): Use a better error code for this. - return InvalidConstant(node, CompileTimeErrorCode.INVALID_CONSTANT); + return InvalidConstant.forEntity( + node, CompileTimeErrorCode.INVALID_CONSTANT); } // TODO(kallentu): Avoid reporting errors in this visitor. @@ -963,7 +985,7 @@ class ConstantVisitor extends UnifyingAstVisitor { @override Constant visitInterpolationExpression(InterpolationExpression node) { - var result = _getConstant(node.expression); + var result = evaluateConstant(node.expression); if (result is! DartObjectImpl) { return result; } @@ -972,7 +994,7 @@ class ConstantVisitor extends UnifyingAstVisitor { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode( CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING, node); - return InvalidConstant( + return InvalidConstant.forEntity( node, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING); } return _dartObjectComputer.performToString(node, result); @@ -989,11 +1011,11 @@ class ConstantVisitor extends UnifyingAstVisitor { @override Constant visitIsExpression(IsExpression node) { - var expression = _getConstant(node.expression); + var expression = evaluateConstant(node.expression); if (expression is! DartObjectImpl) { return expression; } - var type = _getConstant(node.type); + var type = evaluateConstant(node.type); if (type is! DartObjectImpl) { return type; } @@ -1006,7 +1028,7 @@ class ConstantVisitor extends UnifyingAstVisitor { // TODO(kallentu): Don't report the error here. _errorReporter.reportErrorForNode( CompileTimeErrorCode.MISSING_CONST_IN_LIST_LITERAL, node); - return InvalidConstant( + return InvalidConstant.forEntity( node, CompileTimeErrorCode.MISSING_CONST_IN_LIST_LITERAL); } var elements = []; @@ -1043,11 +1065,11 @@ class ConstantVisitor extends UnifyingAstVisitor { if (enclosingElement is CompilationUnitElement) { LibraryElement library = enclosingElement.library; if (library.isDartCore) { - var leftArgument = _getConstant(arguments[0]); + var leftArgument = evaluateConstant(arguments[0]); if (leftArgument is! DartObjectImpl) { return leftArgument; } - var rightArgument = _getConstant(arguments[1]); + var rightArgument = evaluateConstant(arguments[1]); if (rightArgument is! DartObjectImpl) { return rightArgument; } @@ -1066,20 +1088,21 @@ class ConstantVisitor extends UnifyingAstVisitor { // before. Move this error reporting elsewhere. _errorReporter.reportErrorForNode( CompileTimeErrorCode.INVALID_CONSTANT, node); - return InvalidConstant(node, CompileTimeErrorCode.INVALID_CONSTANT, + return InvalidConstant.forEntity( + node, CompileTimeErrorCode.INVALID_CONSTANT, isUnresolved: true); } // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode( CompileTimeErrorCode.CONST_EVAL_METHOD_INVOCATION, node); - return InvalidConstant( + return InvalidConstant.forEntity( node, CompileTimeErrorCode.CONST_EVAL_METHOD_INVOCATION); } @override Constant visitNamedExpression(NamedExpression node) => - _getConstant(node.expression); + evaluateConstant(node.expression); @override Constant visitNamedType(NamedType node) { @@ -1090,10 +1113,12 @@ class ConstantVisitor extends UnifyingAstVisitor { // TODO(kallentu): Don't report error here _errorReporter.reportErrorForNode( CompileTimeErrorCode.CONST_TYPE_PARAMETER, node); - return InvalidConstant(node, CompileTimeErrorCode.CONST_TYPE_PARAMETER); + return InvalidConstant.forEntity( + node, CompileTimeErrorCode.CONST_TYPE_PARAMETER); } else if (node.isDeferred) { return _getDeferredLibraryError(node, node.name2) ?? - InvalidConstant(node, CompileTimeErrorCode.INVALID_CONSTANT); + InvalidConstant.forEntity( + node, CompileTimeErrorCode.INVALID_CONSTANT); } if (_substitution != null) { @@ -1124,7 +1149,7 @@ class ConstantVisitor extends UnifyingAstVisitor { @override Constant visitParenthesizedExpression(ParenthesizedExpression node) => - _getConstant(node.expression); + evaluateConstant(node.expression); @override Constant visitPrefixedIdentifier(PrefixedIdentifier node) { @@ -1135,10 +1160,11 @@ class ConstantVisitor extends UnifyingAstVisitor { if (prefixElement is PrefixElement) { if (node.isDeferred) { return _getDeferredLibraryError(node, node.identifier) ?? - InvalidConstant(node, CompileTimeErrorCode.INVALID_CONSTANT); + InvalidConstant.forEntity( + node, CompileTimeErrorCode.INVALID_CONSTANT); } } else if (prefixElement is! ExtensionElement) { - var prefixResult = _getConstant(prefixNode); + var prefixResult = evaluateConstant(prefixNode); if (prefixResult is! DartObjectImpl) { return prefixResult; } @@ -1164,7 +1190,7 @@ class ConstantVisitor extends UnifyingAstVisitor { @override Constant visitPrefixExpression(PrefixExpression node) { - var operand = _getConstant(node.operand); + var operand = evaluateConstant(node.operand); if (operand is! DartObjectImpl) { return operand; } @@ -1172,7 +1198,7 @@ class ConstantVisitor extends UnifyingAstVisitor { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode( CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD, node); - return InvalidConstant( + return InvalidConstant.forEntity( node, CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD); } if (node.operator.type == TokenType.BANG) { @@ -1193,7 +1219,7 @@ class ConstantVisitor extends UnifyingAstVisitor { Constant visitPropertyAccess(PropertyAccess node) { var target = node.target; if (target != null) { - var prefixResult = _getConstant(target); + var prefixResult = evaluateConstant(target); if (prefixResult is! DartObjectImpl) { return prefixResult; } @@ -1219,13 +1245,13 @@ class ConstantVisitor extends UnifyingAstVisitor { for (var field in node.fields) { if (field is NamedExpression) { var name = field.name.label.name; - var value = _getConstant(field.expression); + var value = evaluateConstant(field.expression); if (value is! DartObjectImpl) { return value; } namedFields[name] = value; } else { - var value = _getConstant(field); + var value = evaluateConstant(field); if (value is! DartObjectImpl) { return value; } @@ -1257,23 +1283,19 @@ class ConstantVisitor extends UnifyingAstVisitor { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode( CompileTimeErrorCode.MISSING_CONST_IN_MAP_LITERAL, node); - return InvalidConstant( + return InvalidConstant.forEntity( node, CompileTimeErrorCode.MISSING_CONST_IN_MAP_LITERAL); } Map map = {}; for (CollectionElement element in node.elements) { var result = _addElementsToMap(map, element); if (result is InvalidConstant) { - if (result.errorCode == - CompileTimeErrorCode.CONST_SPREAD_EXPECTED_MAP && - node.isMap) { - // Only report the error if we know this is a non-ambiguous map. - // TODO(kallentu): Don't report error here and avoid reporting this - // error in this case. - _errorReporter.reportErrorForOffset( - CompileTimeErrorCode.CONST_SPREAD_EXPECTED_MAP, - result.entity.offset, - result.entity.length); + if (!node.isMap) { + // We don't report the error if we know this is an ambiguous map or + // set. [CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_BOTH] + // or [CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_EITHER] is + // already reported elsewhere. + result.avoidReporting = true; } return result; } @@ -1295,7 +1317,7 @@ class ConstantVisitor extends UnifyingAstVisitor { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode( CompileTimeErrorCode.MISSING_CONST_IN_SET_LITERAL, node); - return InvalidConstant( + return InvalidConstant.forEntity( node, CompileTimeErrorCode.MISSING_CONST_IN_SET_LITERAL); } Set set = {}; @@ -1362,7 +1384,7 @@ class ConstantVisitor extends UnifyingAstVisitor { } @override - Constant visitTypeLiteral(TypeLiteral node) => _getConstant(node.type); + Constant visitTypeLiteral(TypeLiteral node) => evaluateConstant(node.type); /// Add the entries produced by evaluating the given collection [element] to /// the given [list]. Return an [InvalidConstant] if the evaluation of one or @@ -1371,7 +1393,7 @@ class ConstantVisitor extends UnifyingAstVisitor { List list, CollectionElement element) { switch (element) { case Expression(): - var expression = _getConstant(element); + var expression = evaluateConstant(element); switch (expression) { case InvalidConstant(): return expression; @@ -1383,10 +1405,10 @@ class ConstantVisitor extends UnifyingAstVisitor { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode( CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT, element); - return InvalidConstant( + return InvalidConstant.forEntity( element, CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT); case IfElement(): - var condition = _getConstant(element.expression); + var condition = evaluateConstant(element.expression); switch (condition) { case InvalidConstant(): return condition; @@ -1396,7 +1418,7 @@ class ConstantVisitor extends UnifyingAstVisitor { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode( CompileTimeErrorCode.NON_BOOL_CONDITION, element.expression); - return InvalidConstant( + return InvalidConstant.forEntity( element.expression, CompileTimeErrorCode.NON_BOOL_CONDITION); } else if (conditionValue) { return _addElementsToList(list, element.thenElement); @@ -1407,10 +1429,10 @@ class ConstantVisitor extends UnifyingAstVisitor { return null; } case MapLiteralEntry(): - return InvalidConstant( + return InvalidConstant.forEntity( element, CompileTimeErrorCode.MAP_ENTRY_NOT_IN_MAP); case SpreadElement(): - var spread = _getConstant(element.expression); + var spread = evaluateConstant(element.expression); switch (spread) { case InvalidConstant(): return spread; @@ -1425,7 +1447,7 @@ class ConstantVisitor extends UnifyingAstVisitor { _errorReporter.reportErrorForNode( CompileTimeErrorCode.CONST_SPREAD_EXPECTED_LIST_OR_SET, element.expression); - return InvalidConstant(element.expression, + return InvalidConstant.forEntity(element.expression, CompileTimeErrorCode.CONST_SPREAD_EXPECTED_LIST_OR_SET); } list.addAll(listValue); @@ -1441,15 +1463,16 @@ class ConstantVisitor extends UnifyingAstVisitor { Map map, CollectionElement element) { switch (element) { case Expression(): - return InvalidConstant(element, CompileTimeErrorCode.EXPRESSION_IN_MAP); + return InvalidConstant.forEntity( + element, CompileTimeErrorCode.EXPRESSION_IN_MAP); case ForElement(): // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode( CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT, element); - return InvalidConstant( + return InvalidConstant.forEntity( element, CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT); case IfElement(): - var condition = _getConstant(element.expression); + var condition = evaluateConstant(element.expression); switch (condition) { case InvalidConstant(): return condition; @@ -1459,7 +1482,7 @@ class ConstantVisitor extends UnifyingAstVisitor { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode( CompileTimeErrorCode.NON_BOOL_CONDITION, element.expression); - return InvalidConstant( + return InvalidConstant.forEntity( element.expression, CompileTimeErrorCode.NON_BOOL_CONDITION); } else if (conditionValue) { return _addElementsToMap(map, element.thenElement); @@ -1470,8 +1493,8 @@ class ConstantVisitor extends UnifyingAstVisitor { return null; } case MapLiteralEntry(): - var keyResult = _getConstant(element.key); - var valueResult = _getConstant(element.value); + var keyResult = evaluateConstant(element.key); + var valueResult = evaluateConstant(element.value); switch (keyResult) { case InvalidConstant(): return keyResult; @@ -1485,7 +1508,7 @@ class ConstantVisitor extends UnifyingAstVisitor { } return null; case SpreadElement(): - var spread = _getConstant(element.expression); + var spread = evaluateConstant(element.expression); switch (spread) { case InvalidConstant(): return spread; @@ -1496,7 +1519,7 @@ class ConstantVisitor extends UnifyingAstVisitor { } var mapValue = spread.toMapValue(); if (mapValue == null) { - return InvalidConstant(element.expression, + return InvalidConstant.forEntity(element.expression, CompileTimeErrorCode.CONST_SPREAD_EXPECTED_MAP); } map.addAll(mapValue); @@ -1512,7 +1535,7 @@ class ConstantVisitor extends UnifyingAstVisitor { Set set, CollectionElement element) { switch (element) { case Expression(): - var expression = _getConstant(element); + var expression = evaluateConstant(element); switch (expression) { case InvalidConstant(): return expression; @@ -1524,10 +1547,10 @@ class ConstantVisitor extends UnifyingAstVisitor { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode( CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT, element); - return InvalidConstant( + return InvalidConstant.forEntity( element, CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT); case IfElement(): - var condition = _getConstant(element.expression); + var condition = evaluateConstant(element.expression); switch (condition) { case InvalidConstant(): return condition; @@ -1537,7 +1560,7 @@ class ConstantVisitor extends UnifyingAstVisitor { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode( CompileTimeErrorCode.NON_BOOL_CONDITION, element.expression); - return InvalidConstant( + return InvalidConstant.forEntity( element.expression, CompileTimeErrorCode.NON_BOOL_CONDITION); } else if (conditionValue) { return _addElementsToSet(set, element.thenElement); @@ -1548,10 +1571,10 @@ class ConstantVisitor extends UnifyingAstVisitor { return null; } case MapLiteralEntry(): - return InvalidConstant( + return InvalidConstant.forEntity( element, CompileTimeErrorCode.MAP_ENTRY_NOT_IN_MAP); case SpreadElement(): - var spread = _getConstant(element.expression); + var spread = evaluateConstant(element.expression); switch (spread) { case InvalidConstant(): return spread; @@ -1566,7 +1589,7 @@ class ConstantVisitor extends UnifyingAstVisitor { _errorReporter.reportErrorForNode( CompileTimeErrorCode.CONST_SPREAD_EXPECTED_LIST_OR_SET, element.expression); - return InvalidConstant(element.expression, + return InvalidConstant.forEntity(element.expression, CompileTimeErrorCode.CONST_SPREAD_EXPECTED_LIST_OR_SET); } set.addAll(setValue); @@ -1581,7 +1604,7 @@ class ConstantVisitor extends UnifyingAstVisitor { Constant _concatenateNodes(Expression node, List astNodes) { Constant? result; for (AstNode astNode in astNodes) { - var constant = _getConstant(astNode); + var constant = evaluateConstant(astNode); if (constant is! DartObjectImpl) { return constant; } @@ -1637,7 +1660,7 @@ class ConstantVisitor extends UnifyingAstVisitor { if (identifier.staticElement?.enclosingElement is ExtensionElement) { _errorReporter.reportErrorForNode( CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD, errorNode); - return InvalidConstant( + return InvalidConstant.forEntity( errorNode, CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD); } @@ -1662,7 +1685,7 @@ class ConstantVisitor extends UnifyingAstVisitor { identifier.name, targetType.getDisplayString(withNullability: _isNonNullableByDefault) ]); - return InvalidConstant( + return InvalidConstant.forEntity( errorNode, CompileTimeErrorCode.CONST_EVAL_PROPERTY_ACCESS, arguments: [ identifier.name, @@ -1670,20 +1693,6 @@ class ConstantVisitor extends UnifyingAstVisitor { ]); } - /// Return a [Constant], evaluated by the [ConstantVisitor]. - /// - /// The [ConstantVisitor] shouldn't return any `null` values even though - /// [UnifyingAstVisitor] allows it. If we encounter an unexpected `null` - /// value, we will return an [InvalidConstant] instead. - Constant _getConstant(AstNode node) { - var result = node.accept(this); - if (result == null) { - // Should never reach this. - return InvalidConstant(node, CompileTimeErrorCode.INVALID_CONSTANT); - } - return result; - } - /// Returns a [Constant] based on the [element] provided. /// /// The [errorNode] is the node to be used if an error needs to be reported, @@ -1711,7 +1720,7 @@ class ConstantVisitor extends UnifyingAstVisitor { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode( CompileTimeErrorCode.CONST_TYPE_PARAMETER, expression); - return InvalidConstant( + return InvalidConstant.forEntity( expression, CompileTimeErrorCode.CONST_TYPE_PARAMETER); } @@ -1721,31 +1730,31 @@ class ConstantVisitor extends UnifyingAstVisitor { // and errors for other constant expressions. In either case we have // already computed values of all dependencies first (or detect a cycle), // so the value has already been computed and we can just return it. - var result = variableElement.evaluationResult; + var evaluationResult = variableElement.evaluationResult; var isConstField = variableElement is FieldElement && (variableElement.isConst || variableElement.isFinal); if (isConstField || variableElement.isConst) { - // The constant value isn't computed yet, or there is an error while - // computing. We will mark it and determine whether or not to continue - // the evaluation upstream. - if (result == null) { - _error(errorNode2, null); - return InvalidConstant.genericError(errorNode, isUnresolved: true); + switch (evaluationResult) { + case null: + // The constant value isn't computed yet, or there is an error while + // computing. We will mark it and determine whether or not to continue + // the evaluation upstream. + _error(errorNode2, null); + return InvalidConstant.genericError(errorNode, isUnresolved: true); + case DartObjectImpl(): + if (identifier == null) { + return InvalidConstant.forEntity( + errorNode, CompileTimeErrorCode.INVALID_CONSTANT); + } + return _instantiateFunctionTypeForSimpleIdentifier( + identifier, evaluationResult); + case InvalidConstant(): + // TODO(kallentu): Investigate and fix the test failures that occur if + // we remove `avoidReporting` + return InvalidConstant.forEntity( + errorNode, CompileTimeErrorCode.INVALID_CONSTANT, + isUnresolved: true, avoidReporting: true); } - - var value = result.value; - if (value == null) { - // TODO(kallentu): Investigate and fix the test failures that occur if - // we report errors here. - return InvalidConstant( - errorNode, CompileTimeErrorCode.INVALID_CONSTANT, - isUnresolved: true); - } else if (identifier == null) { - return InvalidConstant( - errorNode, CompileTimeErrorCode.INVALID_CONSTANT); - } - - return _instantiateFunctionTypeForSimpleIdentifier(identifier, value); } } else if (variableElement is ConstructorElementImpl && expression != null) { @@ -1762,7 +1771,7 @@ class ConstantVisitor extends UnifyingAstVisitor { FunctionState(variableElement), ); if (identifier == null) { - return InvalidConstant( + return InvalidConstant.forEntity( errorNode, CompileTimeErrorCode.INVALID_CONSTANT); } return _instantiateFunctionTypeForSimpleIdentifier(identifier, rawType); @@ -1819,7 +1828,7 @@ class ConstantVisitor extends UnifyingAstVisitor { } _errorReporter.reportErrorForNode( CompileTimeErrorCode.CONST_TYPE_PARAMETER, errorNode2); - return InvalidConstant( + return InvalidConstant.forEntity( errorNode2, CompileTimeErrorCode.CONST_TYPE_PARAMETER); } } @@ -1894,7 +1903,7 @@ class ConstantVisitor extends UnifyingAstVisitor { errorTarget.offset, errorTarget.length, ); - return InvalidConstant(errorTarget, errorCode); + return InvalidConstant.forEntity(errorTarget, errorCode); } return null; } @@ -1967,16 +1976,28 @@ class ConstantVisitor extends UnifyingAstVisitor { CompileTimeErrorCode.INVALID_CONSTANT, notPotentiallyConstants.first, ); - return InvalidConstant( + return InvalidConstant.forEntity( notPotentiallyConstants.first, CompileTimeErrorCode.INVALID_CONSTANT); } /// Return the value of the given [expression], or a representation of a fake /// constant to continue the evaluation if the expression is unresolved. Constant _valueOf(Expression expression, DartType defaultType) { - var expressionValue = _getConstant(expression); + var expressionValue = evaluateConstant(expression); switch (expressionValue) { + // TODO(kallentu): g3 relies on reporting errors found here, but also + // being able to continue the evaluation with populating fields. Fix the + // interaction with g3 more elegantly. case InvalidConstant(isUnresolved: true): + if (!expressionValue.avoidReporting) { + _errorReporter.reportErrorForOffset( + expressionValue.errorCode, + expressionValue.offset, + expressionValue.length, + expressionValue.arguments, + expressionValue.contextMessages, + ); + } return ConstantEvaluationEngine._unresolvedObject( _library, defaultType); case Constant(): @@ -2003,7 +2024,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2017,7 +2038,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2027,7 +2048,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2038,7 +2059,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2049,7 +2070,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2060,7 +2081,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2071,7 +2092,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2082,7 +2103,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2093,7 +2114,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2104,7 +2125,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2115,7 +2136,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2126,7 +2147,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2137,7 +2158,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant( + return InvalidConstant.forEntity( node, exception.errorCode, isRuntimeException: exception.isRuntimeException, @@ -2152,7 +2173,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2163,7 +2184,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2174,7 +2195,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2193,7 +2214,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2204,7 +2225,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2214,7 +2235,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2225,7 +2246,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2236,7 +2257,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2246,7 +2267,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2257,7 +2278,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2267,7 +2288,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2278,7 +2299,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2289,7 +2310,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2300,7 +2321,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2310,7 +2331,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2321,7 +2342,7 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } @@ -2329,17 +2350,31 @@ class DartObjectComputer { DartObjectImpl function, List typeArguments, Expression node, + TypeArgumentList typeArgumentsErrorNode, ) { var rawType = function.type; if (rawType is FunctionType) { if (typeArguments.length != rawType.typeFormals.length) { - return InvalidConstant( - node, CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION); + if (node is SimpleIdentifier) { + return InvalidConstant.forEntity(typeArgumentsErrorNode, + CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION, + arguments: [ + node.name, + rawType.typeFormals.length, + typeArguments.length + ]); + } + return InvalidConstant.forEntity( + typeArgumentsErrorNode, + CompileTimeErrorCode + .WRONG_NUMBER_OF_TYPE_ARGUMENTS_ANONYMOUS_FUNCTION, + arguments: [rawType.typeFormals.length, typeArguments.length]); } var type = rawType.instantiate(typeArguments); return function.typeInstantiate(_typeSystem, type, typeArguments); } else { - return InvalidConstant(node, CompileTimeErrorCode.INVALID_CONSTANT); + return InvalidConstant.forEntity( + node, CompileTimeErrorCode.INVALID_CONSTANT); } } @@ -2354,88 +2389,11 @@ class DartObjectComputer { } on EvaluationException catch (exception) { // TODO(kallentu): Don't report error here. _errorReporter.reportErrorForNode(exception.errorCode, node); - return InvalidConstant(node, exception.errorCode); + return InvalidConstant.forEntity(node, exception.errorCode); } } } -/// The result of attempting to evaluate an expression. -class EvaluationResult { - // TODO(brianwilkerson) Merge with EvaluationResultImpl - /// The value of the expression. - final DartObject? value; - - /// The errors that should be reported for the expression(s) that were - /// evaluated. - final List? _errors; - - /// Initialize a newly created result object with the given [value] and set of - /// [_errors]. Clients should use one of the factory methods: [forErrors] and - /// [forValue]. - EvaluationResult(this.value, this._errors); - - /// Return a list containing the errors that should be reported for the - /// expression(s) that were evaluated. If there are no such errors, the list - /// will be empty. The list can be empty even if the expression is not a valid - /// compile time constant if the errors would have been reported by other - /// parts of the analysis engine. - List get errors => _errors ?? AnalysisError.NO_ERRORS; - - /// Return `true` if the expression is a compile-time constant expression that - /// would not throw an exception when evaluated. - bool get isValid => _errors == null; - - /// Return an evaluation result representing the result of evaluating an - /// expression that is not a compile-time constant because of the given - /// [errors]. - static EvaluationResult forErrors(List errors) => - EvaluationResult(null, errors); - - /// Return an evaluation result representing the result of evaluating an - /// expression that is a compile-time constant that evaluates to the given - /// [value]. - static EvaluationResult forValue(DartObject value) => - EvaluationResult(value, null); -} - -/// The result of attempting to evaluate a expression. -class EvaluationResultImpl { - /// The errors encountered while trying to evaluate the compile time constant. - /// These errors may or may not have prevented the expression from being a - /// valid compile time constant. - final List _errors; - - /// The value of the expression, or `null` if the value couldn't be computed - /// due to errors. - final DartObjectImpl? value; - - EvaluationResultImpl( - this.value, [ - this._errors = const [], - ]); - - List get errors => _errors; - - bool equalValues(TypeProvider typeProvider, EvaluationResultImpl result) { - if (value != null) { - if (result.value == null) { - return false; - } - return value == result.value; - } else { - return false; - } - } - - @override - String toString() { - if (value == null) { - return "error"; - } - return value.toString(); - } -} - class _EnumConstant { final int index; final String name; @@ -2602,7 +2560,7 @@ class _InstanceCreationEvaluator { var argumentCount = arguments.length; if (_constructor.name == "fromEnvironment") { if (!_checkFromEnvironmentArguments(arguments, definingType)) { - return InvalidConstant( + return InvalidConstant.forEntity( _errorNode, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); } String? variableName = @@ -2634,7 +2592,7 @@ class _InstanceCreationEvaluator { definingClass == typeProvider.symbolElement && argumentCount == 1) { if (!_checkSymbolArguments(arguments, isNullSafe: isNullSafe)) { - return InvalidConstant( + return InvalidConstant.forEntity( _errorNode, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); } return DartObjectImpl( @@ -2726,12 +2684,12 @@ class _InstanceCreationEvaluator { if ((field.isFinal || field.isConst) && !field.isStatic && field is ConstFieldElementImpl) { - var fieldValue = field.evaluationResult?.value; + var fieldValue = field.evaluationResult; // It is possible that the evaluation result is null. // This happens for example when we have duplicate fields. // `class Test {final x = 1; final x = 2; const Test();}` - if (fieldValue == null) { + if (fieldValue == null || fieldValue is! DartObjectImpl) { continue; } // Match the value and the type. @@ -2739,7 +2697,7 @@ class _InstanceCreationEvaluator { if (!typeSystem.runtimeTypeMatch(fieldValue, fieldType)) { var isRuntimeException = hasTypeParameterReference(field.type); var errorNode = field.constantInitializer ?? _errorNode; - return InvalidConstant(errorNode, + return InvalidConstant.forEntity(errorNode, CompileTimeErrorCode.CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH, arguments: [fieldValue.type, field.name, fieldType], isRuntimeException: isRuntimeException); @@ -2805,13 +2763,13 @@ class _InstanceCreationEvaluator { if (initializer is ConstructorFieldInitializer) { var initializerExpression = initializer.expression; var evaluationResult = - _initializerVisitor._getConstant(initializerExpression); + _initializerVisitor.evaluateConstant(initializerExpression); switch (evaluationResult) { case DartObjectImpl(): var fieldName = initializer.fieldName.name; if (_fieldMap.containsKey(fieldName)) { return _InitializersEvaluationResult( - InvalidConstant(_errorNode, + InvalidConstant.forEntity(_errorNode, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION), evaluationIsComplete: true); } @@ -2828,7 +2786,7 @@ class _InstanceCreationEvaluator { var errorNode = isRuntimeException ? initializerExpression : _errorNode; return _InitializersEvaluationResult( - InvalidConstant( + InvalidConstant.forEntity( errorNode, CompileTimeErrorCode .CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH, @@ -2847,15 +2805,15 @@ class _InstanceCreationEvaluator { if (evaluationResult.contextMessages.isEmpty) { evaluationResult.contextMessages.add(DiagnosticMessageImpl( filePath: _constructor.source.fullName, - length: evaluationResult.entity.length, + length: evaluationResult.length, message: "The error is in the field initializer of " "'${_constructor.displayName}', and occurs here.", - offset: evaluationResult.entity.offset, + offset: evaluationResult.offset, url: null, )); } return _InitializersEvaluationResult( - InvalidConstant.forEntity(evaluationResult, _errorNode), + InvalidConstant.copyWithEntity(evaluationResult, _errorNode), evaluationIsComplete: true); case InvalidConstant(): return _InitializersEvaluationResult(evaluationResult, @@ -2889,13 +2847,13 @@ class _InstanceCreationEvaluator { } } else if (initializer is AssertInitializer) { var condition = initializer.condition; - var evaluationResult = _initializerVisitor._getConstant(condition); + var evaluationResult = _initializerVisitor.evaluateConstant(condition); switch (evaluationResult) { case DartObjectImpl(): if (!evaluationResult.isBool || evaluationResult.toBoolValue() == false) { return _InitializersEvaluationResult( - InvalidConstant(initializer, + InvalidConstant.forEntity(initializer, CompileTimeErrorCode.CONST_EVAL_ASSERTION_FAILURE, isRuntimeException: true), evaluationIsComplete: true); @@ -2906,15 +2864,15 @@ class _InstanceCreationEvaluator { if (evaluationResult.contextMessages.isEmpty) { evaluationResult.contextMessages.add(DiagnosticMessageImpl( filePath: _constructor.source.fullName, - length: evaluationResult.entity.length, + length: evaluationResult.length, message: "The error is in the assert initializer of " "'${_constructor.displayName}', and occurs here.", - offset: evaluationResult.entity.offset, + offset: evaluationResult.offset, url: null, )); } return _InitializersEvaluationResult( - InvalidConstant.forEntity(evaluationResult, _errorNode), + InvalidConstant.copyWithEntity(evaluationResult, _errorNode), evaluationIsComplete: true); case InvalidConstant(): return _InitializersEvaluationResult(evaluationResult, @@ -2966,8 +2924,8 @@ class _InstanceCreationEvaluator { if (evaluationResult == null) { // No default was provided, so the default value is null. argumentValue = ConstantEvaluationEngine._nullObject(_library); - } else if (evaluationResult.value != null) { - argumentValue = evaluationResult.value; + } else if (evaluationResult is DartObjectImpl) { + argumentValue = evaluationResult; } } if (argumentValue != null) { @@ -2979,7 +2937,7 @@ class _InstanceCreationEvaluator { var isEvaluationException = errorTarget is Expression && _library.typeSystem .isAssignableTo(errorTarget.typeOrThrow, parameter.type); - return InvalidConstant(errorTarget, + return InvalidConstant.forEntity(errorTarget, CompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH, arguments: [argumentValue.type, parameter.type], isRuntimeException: isEvaluationException); @@ -2994,14 +2952,14 @@ class _InstanceCreationEvaluator { // the field. if (!argumentValue.isInvalid && !typeSystem.runtimeTypeMatch(argumentValue, fieldType)) { - return InvalidConstant(errorTarget, + return InvalidConstant.forEntity(errorTarget, CompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH, arguments: [argumentValue.type, fieldType]); } } var fieldName = field.name; if (_fieldMap.containsKey(fieldName)) { - return InvalidConstant( + return InvalidConstant.forEntity( _errorNode, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); } _fieldMap[fieldName] = argumentValue; @@ -3058,17 +3016,17 @@ class _InstanceCreationEvaluator { if (evaluationResult.contextMessages.isEmpty) { evaluationResult.contextMessages.add(DiagnosticMessageImpl( filePath: _constructor.source.fullName, - length: evaluationResult.entity.length, + length: evaluationResult.length, message: "The error is in the super constructor invocation " "of '${_constructor.displayName}', and occurs here.", - offset: evaluationResult.entity.offset, + offset: evaluationResult.offset, url: null, )); } else { evaluationResult.contextMessages.add( _stackTraceContextMessage(superConstructor, _constructor)); } - return InvalidConstant.forEntity(evaluationResult, _errorNode); + return InvalidConstant.copyWithEntity(evaluationResult, _errorNode); case InvalidConstant(): evaluationResult.contextMessages .add(_stackTraceContextMessage(superConstructor, _constructor)); @@ -3153,11 +3111,12 @@ class _InstanceCreationEvaluator { if (node is InstanceCreationExpression) { var newKeyword = node.keyword; if (newKeyword != null) { - return InvalidConstant( + return InvalidConstant.forEntity( newKeyword, CompileTimeErrorCode.CONST_WITH_NON_CONST); } } - return InvalidConstant(node, CompileTimeErrorCode.CONST_WITH_NON_CONST); + return InvalidConstant.forEntity( + node, CompileTimeErrorCode.CONST_WITH_NON_CONST); } if (!(constructor.declaration as ConstructorElementImpl).isCycleFree) { diff --git a/pkg/analyzer/lib/src/dart/constant/value.dart b/pkg/analyzer/lib/src/dart/constant/value.dart index dac6ce854fd..b6f8e436e25 100644 --- a/pkg/analyzer/lib/src/dart/constant/value.dart +++ b/pkg/analyzer/lib/src/dart/constant/value.dart @@ -2352,49 +2352,94 @@ class IntState extends NumState { /// An invalid constant that contains diagnostic information. class InvalidConstant implements Constant { - /// The entity that a constant evaluator error is reported at. - final SyntacticEntity entity; + /// The length of the entity that the evaluation error is reported at. + final int length; - /// The error code that is reported at the location of the [entity]. + /// The offset of the entity that the evaluation error is reported at. + final int offset; + + /// The error code that is being reported. final ErrorCode errorCode; /// The arguments required to complete the message. - final List? arguments; + final List arguments; /// Additional context messages for the error, including stack trace /// information if the error occurs within a constructor. final List contextMessages; - /// Return `true` if the error was an exception thrown during constant - /// evaluation. + /// Whether to omit reporting this error. + /// + /// If set to `true`, error reporting will ignore this invalid constant. + /// Defaults to `false`. + /// + /// The `ConstantVisitor` can change this to `true` when there's already an + /// error reported and this invalid constant would be an unnecessary follow-on + /// error. + bool avoidReporting; + + /// Whether this error was an exception thrown during constant evaluation. /// /// In [ConstantEvaluationEngine.evaluateAndReportErrorsInConstructorCall], /// we report this with a [CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION] /// and a context message pointing to where the exception was thrown. final bool isRuntimeException; - /// Return `true` if the constant evaluation encounters an unresolved - /// expression. + /// Whether the constant evaluation encounters an unresolved expression. final bool isUnresolved; - InvalidConstant(this.entity, this.errorCode, - {this.arguments, - List? contextMessages, - this.isUnresolved = false, - this.isRuntimeException = false}) - : contextMessages = contextMessages ?? []; - /// Creates a duplicate instance of [other], with a different [entity]. - factory InvalidConstant.forEntity( + factory InvalidConstant.copyWithEntity( InvalidConstant other, SyntacticEntity entity) { - return InvalidConstant(entity, other.errorCode, - arguments: other.arguments, - contextMessages: other.contextMessages, - isUnresolved: other.isUnresolved, - isRuntimeException: other.isRuntimeException); + return InvalidConstant.forEntity( + entity, + other.errorCode, + arguments: other.arguments, + contextMessages: other.contextMessages, + avoidReporting: other.avoidReporting, + isUnresolved: other.isUnresolved, + isRuntimeException: other.isRuntimeException, + ); } - /// Returns a generic error depending on the [node] provided. + /// Creates a constant evaluation error associated with an [element]. + InvalidConstant.forElement(Element element, ErrorCode errorCode, + {List? arguments, + List? contextMessages, + bool avoidReporting = false, + bool isUnresolved = false, + bool isRuntimeException = false}) + : this._( + element.nameLength, + element.nameOffset, + errorCode, + arguments: arguments, + contextMessages: contextMessages, + avoidReporting: avoidReporting, + isUnresolved: isUnresolved, + isRuntimeException: isRuntimeException, + ); + + /// Creates a constant evaluation error associated with a token or node + /// [entity]. + InvalidConstant.forEntity(SyntacticEntity entity, ErrorCode errorCode, + {List? arguments, + List? contextMessages, + bool avoidReporting = false, + bool isUnresolved = false, + bool isRuntimeException = false}) + : this._( + entity.length, + entity.offset, + errorCode, + arguments: arguments, + contextMessages: contextMessages, + avoidReporting: avoidReporting, + isUnresolved: isUnresolved, + isRuntimeException: isRuntimeException, + ); + + /// Creates a generic error depending on the [node] provided. factory InvalidConstant.genericError(AstNode node, {bool isUnresolved = false}) { final parent = node.parent; @@ -2402,13 +2447,23 @@ class InvalidConstant implements Constant { if (parent is ArgumentList && parent2 is InstanceCreationExpression && parent2.isConst) { - return InvalidConstant( + return InvalidConstant.forEntity( node, CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT, isUnresolved: isUnresolved); } - return InvalidConstant(node, CompileTimeErrorCode.INVALID_CONSTANT, + return InvalidConstant.forEntity( + node, CompileTimeErrorCode.INVALID_CONSTANT, isUnresolved: isUnresolved); } + + InvalidConstant._(this.length, this.offset, this.errorCode, + {List? arguments, + List? contextMessages, + this.avoidReporting = false, + this.isUnresolved = false, + this.isRuntimeException = false}) + : arguments = arguments ?? [], + contextMessages = contextMessages ?? []; } /// The state of an object representing a list. diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart index 84c7722cd72..ad002340d54 100644 --- a/pkg/analyzer/lib/src/dart/element/element.dart +++ b/pkg/analyzer/lib/src/dart/element/element.dart @@ -25,6 +25,7 @@ import 'package:analyzer/src/dart/ast/ast.dart'; import 'package:analyzer/src/dart/ast/token.dart'; import 'package:analyzer/src/dart/constant/compute.dart'; import 'package:analyzer/src/dart/constant/evaluation.dart'; +import 'package:analyzer/src/dart/constant/value.dart'; import 'package:analyzer/src/dart/element/display_string_builder.dart'; import 'package:analyzer/src/dart/element/member.dart'; import 'package:analyzer/src/dart/element/name_union.dart'; @@ -1282,11 +1283,11 @@ mixin ConstVariableElement implements ElementImpl, ConstantEvaluationTarget { /// initializers. Expression? constantInitializer; - EvaluationResultImpl? _evaluationResult; + Constant? _evaluationResult; - EvaluationResultImpl? get evaluationResult => _evaluationResult; + Constant? get evaluationResult => _evaluationResult; - set evaluationResult(EvaluationResultImpl? evaluationResult) { + set evaluationResult(Constant? evaluationResult) { _evaluationResult = evaluationResult; } @@ -1315,7 +1316,11 @@ mixin ConstVariableElement implements ElementImpl, ConstantEvaluationTarget { configuration: ConstantEvaluationConfiguration(), ); } - return evaluationResult?.value; + + if (evaluationResult case DartObjectImpl result) { + return result; + } + return null; } } @@ -1378,7 +1383,7 @@ class DefaultSuperFormalParameterElementImpl } @override - EvaluationResultImpl? get evaluationResult { + Constant? get evaluationResult { if (constantInitializer != null) { return super.evaluationResult; } @@ -1663,15 +1668,41 @@ class ElementAnnotationImpl implements ElementAnnotation { /// The result of evaluating this annotation as a compile-time constant /// expression, or `null` if the compilation unit containing the variable has /// not been resolved. - EvaluationResultImpl? evaluationResult; + Constant? evaluationResult; + + /// Any additional errors, other than [evaluationResult] being an + /// [InvalidConstant], that came from evaluating the constant expression, + /// or `null` if the compilation unit containing the variable has + /// not been resolved. + /// + /// TODO(kallentu): Remove this field once we fix up g3's dependency on + /// annotations having a valid result as well as unresolved errors. + List? additionalErrors; /// Initialize a newly created annotation. The given [compilationUnit] is the /// compilation unit in which the annotation appears. ElementAnnotationImpl(this.compilationUnit); @override - List get constantEvaluationErrors => - evaluationResult?.errors ?? const []; + List get constantEvaluationErrors { + final evaluationResult = this.evaluationResult; + final additionalErrors = this.additionalErrors; + if (evaluationResult is InvalidConstant) { + // When we have an [InvalidConstant], we don't report the additional + // errors because this result contains the most relevant error. + return [ + AnalysisError.tmp( + source: source, + offset: evaluationResult.offset, + length: evaluationResult.length, + errorCode: evaluationResult.errorCode, + arguments: evaluationResult.arguments, + contextMessages: evaluationResult.contextMessages, + ) + ]; + } + return additionalErrors ?? const []; + } @override AnalysisContext get context => compilationUnit.library.context; @@ -1824,7 +1855,11 @@ class ElementAnnotationImpl implements ElementAnnotation { configuration: ConstantEvaluationConfiguration(), ); } - return evaluationResult?.value; + + if (evaluationResult case DartObjectImpl result) { + return result; + } + return null; } @override @@ -6876,11 +6911,11 @@ abstract class VariableElementImpl extends ElementImpl /// compile-time constant expression, or `null` if this variable is not a /// 'const' variable, if it does not have an initializer, or if the /// compilation unit containing the variable has not been resolved. - EvaluationResultImpl? get evaluationResult => null; + Constant? get evaluationResult => null; /// Set the result of evaluating this variable's initializer as a compile-time /// constant expression to the given [result]. - set evaluationResult(EvaluationResultImpl? result) { + set evaluationResult(Constant? result) { throw StateError("Invalid attempt to set a compile-time constant result"); } diff --git a/pkg/analyzer/lib/src/error/dead_code_verifier.dart b/pkg/analyzer/lib/src/error/dead_code_verifier.dart index abb808957a6..01d00f326bb 100644 --- a/pkg/analyzer/lib/src/error/dead_code_verifier.dart +++ b/pkg/analyzer/lib/src/error/dead_code_verifier.dart @@ -11,7 +11,6 @@ import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/error/error.dart'; import 'package:analyzer/error/listener.dart'; import 'package:analyzer/source/source_range.dart'; -import 'package:analyzer/src/dart/constant/evaluation.dart'; import 'package:analyzer/src/dart/constant/value.dart'; import 'package:analyzer/src/dart/element/type_system.dart'; import 'package:analyzer/src/dart/resolver/exit_detector.dart'; @@ -160,9 +159,9 @@ class LegacyDeadCodeVerifier extends RecursiveAstVisitor { if (isAmpAmp || isBarBar) { Expression lhsCondition = node.leftOperand; if (!_isDebugConstant(lhsCondition)) { - var lhsResult = _getConstantBooleanValue(lhsCondition); + var lhsResult = _constantBooleanValue(lhsCondition); if (lhsResult != null) { - var value = lhsResult.value?.toBoolValue(); + var value = lhsResult.toBoolValue(); // Report error on "else" block: true || !e! // or on "if" block: false && !e! if (value == true && isBarBar || value == false && isAmpAmp) { @@ -213,9 +212,9 @@ class LegacyDeadCodeVerifier extends RecursiveAstVisitor { Expression conditionExpression = node.condition; conditionExpression.accept(this); if (!_isDebugConstant(conditionExpression)) { - var result = _getConstantBooleanValue(conditionExpression); + var result = _constantBooleanValue(conditionExpression); if (result != null) { - if (result.value?.toBoolValue() == true) { + if (result.toBoolValue() == true) { // Report error on "else" block: true ? 1 : !2! _errorReporter.reportErrorForNode( WarningCode.DEAD_CODE, node.elseExpression); @@ -238,9 +237,9 @@ class LegacyDeadCodeVerifier extends RecursiveAstVisitor { Expression conditionExpression = node.expression; conditionExpression.accept(this); if (!_isDebugConstant(conditionExpression)) { - var result = _getConstantBooleanValue(conditionExpression); + var result = _constantBooleanValue(conditionExpression); if (result != null) { - if (result.value?.toBoolValue() == true) { + if (result.toBoolValue() == true) { // Report error on else block: if(true) {} else {!} var elseElement = node.elseElement; if (elseElement != null) { @@ -266,9 +265,9 @@ class LegacyDeadCodeVerifier extends RecursiveAstVisitor { Expression conditionExpression = node.expression; conditionExpression.accept(this); if (!_isDebugConstant(conditionExpression)) { - var result = _getConstantBooleanValue(conditionExpression); + var result = _constantBooleanValue(conditionExpression); if (result != null) { - if (result.value?.toBoolValue() == true) { + if (result.toBoolValue() == true) { // Report error on else block: if(true) {} else {!} var elseStatement = node.elseStatement; if (elseStatement != null) { @@ -334,9 +333,9 @@ class LegacyDeadCodeVerifier extends RecursiveAstVisitor { Expression conditionExpression = node.condition; conditionExpression.accept(this); if (!_isDebugConstant(conditionExpression)) { - var result = _getConstantBooleanValue(conditionExpression); + var result = _constantBooleanValue(conditionExpression); if (result != null) { - if (result.value?.toBoolValue() == false) { + if (result.toBoolValue() == false) { // Report error on while block: while (false) {!} _errorReporter.reportErrorForNode(WarningCode.DEAD_CODE, node.body); return; @@ -387,17 +386,15 @@ class LegacyDeadCodeVerifier extends RecursiveAstVisitor { } } - /// Given some [expression], return [ValidResult.RESULT_TRUE] if it is `true`, - /// [ValidResult.RESULT_FALSE] if it is `false`, or `null` if the expression - /// is not a constant boolean value. - EvaluationResultImpl? _getConstantBooleanValue(Expression expression) { + /// A boolean [DartObjectImpl] from evaluating [expression]. + /// + /// Is `null` if [expression] does not evaluate to a boolean value. + DartObjectImpl? _constantBooleanValue(Expression expression) { if (expression is BooleanLiteral) { - return EvaluationResultImpl( - DartObjectImpl( - _typeSystem, - _typeSystem.typeProvider.boolType, - BoolState.from(expression.value), - ), + return DartObjectImpl( + _typeSystem, + _typeSystem.typeProvider.boolType, + BoolState.from(expression.value), ); } diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml index 8cb5e60a2ea..350a8b0fec2 100644 --- a/pkg/analyzer/messages.yaml +++ b/pkg/analyzer/messages.yaml @@ -9171,7 +9171,7 @@ CompileTimeErrorCode: entry even though it's a set literal: ```dart - const collection = {[!'a' : 'b'!]}; + var collection = {[!'a' : 'b'!]}; ``` #### Common fixes @@ -9181,7 +9181,7 @@ CompileTimeErrorCode: another type argument: ```dart - const collection = {'a' : 'b'}; + var collection = {'a' : 'b'}; ``` In other cases, you might need to change the explicit type from `Set` to @@ -9192,7 +9192,7 @@ CompileTimeErrorCode: included in the set: ```dart - const collection = {'a', 'b'}; + var collection = {'a', 'b'}; ``` MAP_KEY_TYPE_NOT_ASSIGNABLE: problemMessage: "The element type '{0}' can't be assigned to the map key type '{1}'." diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart index 008080e6494..571fccc848b 100644 --- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart +++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart @@ -15,7 +15,7 @@ import 'package:analyzer/src/dart/analysis/info_declaration_store.dart'; import 'package:analyzer/src/dart/analysis/performance_logger.dart'; import 'package:analyzer/src/dart/analysis/status.dart'; import 'package:analyzer/src/dart/ast/extensions.dart'; -import 'package:analyzer/src/dart/constant/evaluation.dart'; +import 'package:analyzer/src/dart/constant/value.dart'; import 'package:analyzer/src/dart/element/element.dart'; import 'package:analyzer/src/dart/sdk/sdk.dart'; import 'package:analyzer/src/error/codes.dart'; @@ -1351,9 +1351,8 @@ class C {} var result = await driver.getResultValid(testFile); var atD = AstFinder.getClass(result.unit, 'C').metadata[0]; var atDI = atD.elementAnnotation as ElementAnnotationImpl; - var value = atDI.evaluationResult!.value; // That is illegal. - expect(value, isNull); + expect(atDI.evaluationResult, isNull); } test_const_annotation_withArgs() async { @@ -1368,12 +1367,11 @@ class D { var result = await driver.getResultValid(testFile); var atD = AstFinder.getClass(result.unit, 'C').metadata[0]; var atDI = atD.elementAnnotation as ElementAnnotationImpl; - var value = atDI.evaluationResult!.value!; + var value = atDI.evaluationResult as DartObjectImpl; expect(value.type, isNotNull); assertType(value.type, 'D'); expect(value.fields!.keys, ['value']); expect(value.getField('value')!.toIntValue(), 1); - expect(atDI.evaluationResult!.errors, isEmpty); } test_const_annotation_withoutArgs() async { @@ -3736,11 +3734,11 @@ var v = 0 assertType(variable.declaredElement!.type, expected); } - void _expectCircularityError(EvaluationResultImpl evaluationResult) { - expect(evaluationResult, isNotNull); - expect(evaluationResult.value, isNull); - expect(evaluationResult.errors, hasLength(1)); - expect(evaluationResult.errors[0].errorCode, + void _expectCircularityError(Constant evaluationResult) { + if (evaluationResult is! InvalidConstant) { + fail('No error found when we expected a circularity error.'); + } + expect(evaluationResult.errorCode, CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT); } diff --git a/pkg/analyzer/test/src/dart/constant/evaluation_test.dart b/pkg/analyzer/test/src/dart/constant/evaluation_test.dart index fd63214bda3..2a684a63910 100644 --- a/pkg/analyzer/test/src/dart/constant/evaluation_test.dart +++ b/pkg/analyzer/test/src/dart/constant/evaluation_test.dart @@ -1504,7 +1504,6 @@ class C { error(WarningCode.UNUSED_LOCAL_VARIABLE, 55, 1), error(CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS_FUNCTION_TEAROFF, 61, 1), - error(CompileTimeErrorCode.CONST_TYPE_PARAMETER, 61, 1), ]); } @@ -1791,6 +1790,8 @@ const x = C.(); // reported. error(CompileTimeErrorCode.CLASS_INSTANTIATION_ACCESS_TO_UNKNOWN_MEMBER, 45, 8), + error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 45, + 8), error(ParserErrorCode.MISSING_IDENTIFIER, 52, 1), ]); } @@ -2428,6 +2429,15 @@ const res = {...?i}; ]); } + test_visitSetOrMapLiteral_ambiguous_expression() async { + await assertErrorsInCode(r''' +const m = {1: 1}; +const res = {...m, 2}; +''', [ + error(CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_BOTH, 30, 9), + ]); + } + test_visitSetOrMapLiteral_ambiguous_inList() async { await assertErrorsInCode(r''' const l = []; @@ -3191,8 +3201,6 @@ const c = true && a; ''', [ error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 27, 9), - error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 35, - 1), ]); } @@ -3361,8 +3369,6 @@ const c = false || a; ''', [ error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 27, 10), - error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 36, - 1), ]); } @@ -3946,8 +3952,6 @@ const y = B(x); ), error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 72, 1), error(CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT, 72, 1), - error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 72, - 1), ]); } @@ -4104,10 +4108,14 @@ class ConstantVisitorTestSupport extends PubPackageResolutionTest { DartObjectImpl? _evaluationResult(ConstVariableElement element) { final evaluationResult = element.evaluationResult; - if (evaluationResult == null) { - fail('Not evaluated: ${element.name}'); + switch (evaluationResult) { + case null: + fail('Not evaluated: ${element.name}'); + case InvalidConstant(): + return null; + case DartObjectImpl(): + return evaluationResult; } - return evaluationResult.value; } DartObjectImpl? _field(String variableName) { diff --git a/pkg/analyzer/test/src/dart/resolution/constant_test.dart b/pkg/analyzer/test/src/dart/resolution/constant_test.dart index bfcf3eed91e..26e6b0527c4 100644 --- a/pkg/analyzer/test/src/dart/resolution/constant_test.dart +++ b/pkg/analyzer/test/src/dart/resolution/constant_test.dart @@ -397,7 +397,7 @@ class B extends A { assertErrorsInResolvedUnit(result, []); var bElement = FindElement(result.unit).field('b') as ConstVariableElement; - var bValue = bElement.evaluationResult!.value!; + var bValue = bElement.evaluationResult as DartObjectImpl; var superFields = bValue.getField(GenericState.SUPERCLASS_FIELD); expect(superFields!.getField('f1')!.toBoolValue(), false); } diff --git a/pkg/analyzer/test/src/diagnostics/const_eval_property_access_test.dart b/pkg/analyzer/test/src/diagnostics/const_eval_property_access_test.dart index 2b194265ee5..5d248a17e56 100644 --- a/pkg/analyzer/test/src/diagnostics/const_eval_property_access_test.dart +++ b/pkg/analyzer/test/src/diagnostics/const_eval_property_access_test.dart @@ -38,8 +38,6 @@ const a = const A(); "The error is in the field initializer of 'A', and occurs here."), ], ), - error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 29, - 9), ]); } @@ -65,4 +63,17 @@ class RequiresNonEmptyList { ), ]); } + + test_nonStaticField_inGenericClass() async { + await assertErrorsInCode(''' +class C { + const C(); + T? get t => null; +} + +const x = const C().t; +''', [ + error(CompileTimeErrorCode.CONST_EVAL_PROPERTY_ACCESS, 59, 11), + ]); + } } diff --git a/pkg/analyzer/test/src/diagnostics/const_eval_type_string_test.dart b/pkg/analyzer/test/src/diagnostics/const_eval_type_string_test.dart index 3a8dfd8c304..ec7805a8128 100644 --- a/pkg/analyzer/test/src/diagnostics/const_eval_type_string_test.dart +++ b/pkg/analyzer/test/src/diagnostics/const_eval_type_string_test.dart @@ -37,8 +37,6 @@ const y = B(x); ), error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 72, 1), error(CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT, 72, 1), - error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 72, - 1), ]); } } diff --git a/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart b/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart index 857df32db1e..ab9c4ec3c17 100644 --- a/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart +++ b/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart @@ -84,20 +84,6 @@ const x = const A(); '''); } - test_nonStaticField_inGenericClass() async { - await assertErrorsInCode(''' -class C { - const C(); - T? get t => null; -} - -const x = const C().t; -''', [ - error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 59, - 11), - ]); - } - test_propertyExtraction_targetNotConst() async { await assertErrorsInCode(r''' class A { diff --git a/pkg/analyzer/test/src/diagnostics/const_with_type_parameters_test.dart b/pkg/analyzer/test/src/diagnostics/const_with_type_parameters_test.dart index 8a46a5a2653..d54e6ce4c5c 100644 --- a/pkg/analyzer/test/src/diagnostics/const_with_type_parameters_test.dart +++ b/pkg/analyzer/test/src/diagnostics/const_with_type_parameters_test.dart @@ -204,7 +204,6 @@ class A { ''', [ error(CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS_FUNCTION_TEAROFF, 65, 1), - error(CompileTimeErrorCode.CONST_TYPE_PARAMETER, 65, 1), ]); } @@ -220,7 +219,6 @@ class A { error(HintCode.UNUSED_LOCAL_VARIABLE, 54, 1), error(CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS_FUNCTION_TEAROFF, 60, 1), - error(CompileTimeErrorCode.CONST_TYPE_PARAMETER, 60, 1), ]); } @@ -239,7 +237,6 @@ class A { 5), error(CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS_FUNCTION_TEAROFF, 58, 1), - error(CompileTimeErrorCode.CONST_TYPE_PARAMETER, 58, 1), ]); } diff --git a/pkg/analyzer/test/src/diagnostics/constant_pattern_with_non_constant_expression_test.dart b/pkg/analyzer/test/src/diagnostics/constant_pattern_with_non_constant_expression_test.dart index c82f043c13f..f6039b75960 100644 --- a/pkg/analyzer/test/src/diagnostics/constant_pattern_with_non_constant_expression_test.dart +++ b/pkg/analyzer/test/src/diagnostics/constant_pattern_with_non_constant_expression_test.dart @@ -290,7 +290,6 @@ void f(x) { ''', [ error(CompileTimeErrorCode.CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION, 47, 1), - error(CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT, 47, 1), ]); } @@ -397,7 +396,6 @@ void f(x) { ''', [ error(CompileTimeErrorCode.CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION, 47, 1), - error(CompileTimeErrorCode.NON_CONSTANT_MAP_KEY, 47, 1), ]); } @@ -442,7 +440,6 @@ void f(x) { ''', [ error(CompileTimeErrorCode.CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION, 50, 1), - error(CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE, 50, 1), ]); } diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md index 8eccb110749..d783450206a 100644 --- a/pkg/analyzer/tool/diagnostics/diagnostics.md +++ b/pkg/analyzer/tool/diagnostics/diagnostics.md @@ -11586,7 +11586,7 @@ The following code produces this diagnostic because the literal has a map entry even though it's a set literal: {% prettify dart tag=pre+code %} -const collection = {[!'a' : 'b'!]}; +var collection = {[!'a' : 'b'!]}; {% endprettify %} #### Common fixes @@ -11596,7 +11596,7 @@ that it is a map. In the previous example, you could do this by adding another type argument: {% prettify dart tag=pre+code %} -const collection = {'a' : 'b'}; +var collection = {'a' : 'b'}; {% endprettify %} In other cases, you might need to change the explicit type from `Set` to @@ -11607,7 +11607,7 @@ possibly by replacing the colon with a comma if both values should be included in the set: {% prettify dart tag=pre+code %} -const collection = {'a', 'b'}; +var collection = {'a', 'b'}; {% endprettify %} ### map_key_type_not_assignable diff --git a/tests/language/const_functions/const_functions_instance_fields_error_test.dart b/tests/language/const_functions/const_functions_instance_fields_error_test.dart index cb069d522b5..24e74df1a6f 100644 --- a/tests/language/const_functions/const_functions_instance_fields_error_test.dart +++ b/tests/language/const_functions/const_functions_instance_fields_error_test.dart @@ -35,7 +35,7 @@ int fn2() { const var3 = const A(1).x; // ^^^^^^^^^^^^ -// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE +// [analyzer] COMPILE_TIME_ERROR.CONST_EVAL_PROPERTY_ACCESS // ^ // [analyzer] COMPILE_TIME_ERROR.UNDEFINED_GETTER // [cfe] The getter 'x' isn't defined for the class 'A'. diff --git a/tests/language/const_functions/const_functions_instance_fields_test.dart b/tests/language/const_functions/const_functions_instance_fields_test.dart index 1e4c9d0ab90..5fb5c794b89 100644 --- a/tests/language/const_functions/const_functions_instance_fields_test.dart +++ b/tests/language/const_functions/const_functions_instance_fields_test.dart @@ -29,7 +29,7 @@ int fn2() { const var3 = const A(1).y; // ^^^^^^^^^^^^ -// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE +// [analyzer] COMPILE_TIME_ERROR.CONST_EVAL_PROPERTY_ACCESS class B extends A { const B(int x) : super(x); diff --git a/tests/language/method/not_found_test.dart b/tests/language/method/not_found_test.dart index de1cd84d1c2..0f28c1d5f25 100644 --- a/tests/language/method/not_found_test.dart +++ b/tests/language/method/not_found_test.dart @@ -9,6 +9,8 @@ class A { //^^^^ // [analyzer] COMPILE_TIME_ERROR.CONCRETE_CLASS_WITH_ABSTRACT_MEMBER static const field = const B(); + // ^^^^^^^^^ + // [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE // ^ // [analyzer] COMPILE_TIME_ERROR.CREATION_WITH_NON_TYPE // [cfe] Can't access 'this' in a field initializer to read 'B'. diff --git a/tests/language/string/interpolation1_test.dart b/tests/language/string/interpolation1_test.dart index 9cf9206f5ed..6fc72a34b9e 100644 --- a/tests/language/string/interpolation1_test.dart +++ b/tests/language/string/interpolation1_test.dart @@ -14,7 +14,6 @@ class StringInterpolation1NegativeTest { static const DOLLAR = const A("$"); // ^ // [analyzer] SYNTACTIC_ERROR.MISSING_IDENTIFIER - // [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE // [analyzer] COMPILE_TIME_ERROR.INVALID_CONSTANT // [cfe] A '$' has special meaning inside a string, and must be followed by an identifier or an expression in curly braces ({}). static testMain() { diff --git a/tests/language_2/method/not_found_test.dart b/tests/language_2/method/not_found_test.dart index fa06dadc547..32337be6adb 100644 --- a/tests/language_2/method/not_found_test.dart +++ b/tests/language_2/method/not_found_test.dart @@ -11,6 +11,8 @@ class A { //^^^^ // [analyzer] COMPILE_TIME_ERROR.CONCRETE_CLASS_WITH_ABSTRACT_MEMBER static const field = const B(); + // ^^^^^^^^^ + // [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE // ^ // [analyzer] COMPILE_TIME_ERROR.CREATION_WITH_NON_TYPE // [cfe] Can't access 'this' in a field initializer to read 'B'. diff --git a/tests/language_2/string/interpolation1_test.dart b/tests/language_2/string/interpolation1_test.dart index 5aa1af41336..35f6a1ea624 100644 --- a/tests/language_2/string/interpolation1_test.dart +++ b/tests/language_2/string/interpolation1_test.dart @@ -14,11 +14,9 @@ class A { class StringInterpolation1NegativeTest { // Dollar not followed by "{" or identifier. static const DOLLAR = const A("$"); - // [error line 16, column 35, length 0] - // [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE - // [analyzer] COMPILE_TIME_ERROR.INVALID_CONSTANT // ^ // [analyzer] SYNTACTIC_ERROR.MISSING_IDENTIFIER + // [analyzer] COMPILE_TIME_ERROR.INVALID_CONSTANT // [cfe] A '$' has special meaning inside a string, and must be followed by an identifier or an expression in curly braces ({}). static testMain() { print(DOLLAR);