[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 <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Lasse Nielsen <lrn@google.com>
Commit-Queue: Kallen Tu <kallentu@google.com>
This commit is contained in:
Kallen Tu 2023-09-22 16:13:10 +00:00 committed by Commit Queue
parent 5f33a9ab80
commit 742ba5c20d
22 changed files with 649 additions and 586 deletions

View file

@ -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 {

View file

@ -130,23 +130,21 @@ class ConstantVerifier extends RecursiveAstVisitor<void> {
@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<void> {
node,
[matchedValueType, constantType],
);
return;
}
}
}
}
super.visitConstantPattern(node);
}
}
@ -210,8 +210,8 @@ class ConstantVerifier extends RecursiveAstVisitor<void> {
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<void> {
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> {
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<void> {
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<void> {
}
}
/// 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<AnalysisError> 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<void> {
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<void> {
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<void> {
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<void> {
} 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<void> {
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> {
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,

File diff suppressed because it is too large Load diff

View file

@ -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<Object>? arguments;
final List<Object> arguments;
/// Additional context messages for the error, including stack trace
/// information if the error occurs within a constructor.
final List<DiagnosticMessage> 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<DiagnosticMessage>? 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<Object>? arguments,
List<DiagnosticMessage>? 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<Object>? arguments,
List<DiagnosticMessage>? 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<Object>? arguments,
List<DiagnosticMessage>? contextMessages,
this.avoidReporting = false,
this.isUnresolved = false,
this.isRuntimeException = false})
: arguments = arguments ?? [],
contextMessages = contextMessages ?? [];
}
/// The state of an object representing a list.

View file

@ -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<AnalysisError>? additionalErrors;
/// Initialize a newly created annotation. The given [compilationUnit] is the
/// compilation unit in which the annotation appears.
ElementAnnotationImpl(this.compilationUnit);
@override
List<AnalysisError> get constantEvaluationErrors =>
evaluationResult?.errors ?? const <AnalysisError>[];
List<AnalysisError> 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 <AnalysisError>[];
}
@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");
}

View file

@ -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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
}
}
/// 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),
);
}

View file

@ -9171,7 +9171,7 @@ CompileTimeErrorCode:
entry even though it's a set literal:
```dart
const collection = <String>{[!'a' : 'b'!]};
var collection = <String>{[!'a' : 'b'!]};
```
#### Common fixes
@ -9181,7 +9181,7 @@ CompileTimeErrorCode:
another type argument:
```dart
const collection = <String, String>{'a' : 'b'};
var collection = <String, String>{'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 = <String>{'a', 'b'};
var collection = <String>{'a', 'b'};
```
MAP_KEY_TYPE_NOT_ASSIGNABLE:
problemMessage: "The element type '{0}' can't be assigned to the map key type '{1}'."

View file

@ -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);
}

View file

@ -1504,7 +1504,6 @@ class C<U> {
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<int>.();
// 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) {

View file

@ -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);
}

View file

@ -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<T> {
const C();
T? get t => null;
}
const x = const C().t;
''', [
error(CompileTimeErrorCode.CONST_EVAL_PROPERTY_ACCESS, 59, 11),
]);
}
}

View file

@ -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),
]);
}
}

View file

@ -84,20 +84,6 @@ const x = const A();
''');
}
test_nonStaticField_inGenericClass() async {
await assertErrorsInCode('''
class C<T> {
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 {

View file

@ -204,7 +204,6 @@ class A<U> {
''', [
error(CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS_FUNCTION_TEAROFF,
65, 1),
error(CompileTimeErrorCode.CONST_TYPE_PARAMETER, 65, 1),
]);
}
@ -220,7 +219,6 @@ class A<U> {
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<U> {
5),
error(CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS_FUNCTION_TEAROFF,
58, 1),
error(CompileTimeErrorCode.CONST_TYPE_PARAMETER, 58, 1),
]);
}

View file

@ -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),
]);
}

View file

@ -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 = <String>{[!'a' : 'b'!]};
var collection = <String>{[!'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 = <String, String>{'a' : 'b'};
var collection = <String, String>{'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 = <String>{'a', 'b'};
var collection = <String>{'a', 'b'};
{% endprettify %}
### map_key_type_not_assignable

View file

@ -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'.

View file

@ -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);

View file

@ -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'.

View file

@ -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() {

View file

@ -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'.

View file

@ -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);