Extension type. Report CONST_EVAL_EXTENSION_TYPE_METHOD if the operator is declared by extension type.

Change-Id: I26a04332939d2a09b898835dc82cdc75cf23fb33
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/347388
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2024-01-22 21:11:47 +00:00 committed by Commit Queue
parent a3c115f9b0
commit 7965219df2
7 changed files with 141 additions and 10 deletions

View file

@ -415,6 +415,8 @@ CompileTimeErrorCode.CONST_EVAL_ASSERTION_FAILURE:
status: needsEvaluation
CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD:
status: needsEvaluation
CompileTimeErrorCode.CONST_EVAL_EXTENSION_TYPE_METHOD:
status: needsEvaluation
CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT:
status: needsEvaluation
CompileTimeErrorCode.CONST_EVAL_METHOD_INVOCATION:

View file

@ -622,6 +622,8 @@ class ConstantVerifier extends RecursiveAstVisitor<void> {
ErrorCode errorCode = error.errorCode;
if (identical(
errorCode, CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD) ||
identical(
errorCode, CompileTimeErrorCode.CONST_EVAL_EXTENSION_TYPE_METHOD) ||
identical(errorCode, CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT) ||
identical(
errorCode, CompileTimeErrorCode.CONST_EVAL_METHOD_INVOCATION) ||

View file

@ -645,9 +645,19 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
@override
Constant visitBinaryExpression(BinaryExpression node) {
if (node.staticElement?.enclosingElement is ExtensionElement) {
return InvalidConstant.forEntity(
node, CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD);
var operatorElement = node.staticElement;
var operatorContainer = operatorElement?.enclosingElement;
switch (operatorContainer) {
case ExtensionElement():
return InvalidConstant.forEntity(
node,
CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD,
);
case ExtensionTypeElement():
return InvalidConstant.forEntity(
node,
CompileTimeErrorCode.CONST_EVAL_EXTENSION_TYPE_METHOD,
);
}
TokenType operatorType = node.operator.type;
@ -1148,14 +1158,25 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
@override
Constant visitPrefixExpression(PrefixExpression node) {
var operatorElement = node.staticElement;
var operatorContainer = operatorElement?.enclosingElement;
switch (operatorContainer) {
case ExtensionElement():
return InvalidConstant.forEntity(
node,
CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD,
);
case ExtensionTypeElement():
return InvalidConstant.forEntity(
node,
CompileTimeErrorCode.CONST_EVAL_EXTENSION_TYPE_METHOD,
);
}
var operand = evaluateConstant(node.operand);
if (operand is! DartObjectImpl) {
return operand;
}
if (node.staticElement?.enclosingElement is ExtensionElement) {
return InvalidConstant.forEntity(
node, CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD);
}
if (node.operator.type == TokenType.BANG) {
return _dartObjectComputer.logicalNot(node, operand);
} else if (node.operator.type == TokenType.TILDE) {
@ -1621,9 +1642,19 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
/// an error, and `null` otherwise.
Constant? _evaluatePropertyAccess(DartObjectImpl targetResult,
SimpleIdentifier identifier, AstNode errorNode) {
if (identifier.staticElement?.enclosingElement is ExtensionElement) {
return InvalidConstant.forEntity(
errorNode, CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD);
var propertyElement = identifier.staticElement;
var propertyContainer = propertyElement?.enclosingElement;
switch (propertyContainer) {
case ExtensionElement():
return InvalidConstant.forEntity(
errorNode,
CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD,
);
case ExtensionTypeElement():
return InvalidConstant.forEntity(
errorNode,
CompileTimeErrorCode.CONST_EVAL_EXTENSION_TYPE_METHOD,
);
}
var targetType = targetResult.type;

View file

@ -904,6 +904,12 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
"Extension methods can't be used in constant expressions.",
);
static const CompileTimeErrorCode CONST_EVAL_EXTENSION_TYPE_METHOD =
CompileTimeErrorCode(
'CONST_EVAL_EXTENSION_TYPE_METHOD',
"Extension type methods can't be used in constant expressions.",
);
static const CompileTimeErrorCode CONST_EVAL_FOR_ELEMENT =
CompileTimeErrorCode(
'CONST_EVAL_FOR_ELEMENT',

View file

@ -122,6 +122,7 @@ const List<ErrorCode> errorCodeValues = [
CompileTimeErrorCode.CONST_DEFERRED_CLASS,
CompileTimeErrorCode.CONST_EVAL_ASSERTION_FAILURE,
CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD,
CompileTimeErrorCode.CONST_EVAL_EXTENSION_TYPE_METHOD,
CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT,
CompileTimeErrorCode.CONST_EVAL_METHOD_INVOCATION,
CompileTimeErrorCode.CONST_EVAL_PROPERTY_ACCESS,

View file

@ -2705,6 +2705,8 @@ CompileTimeErrorCode:
problemMessage: "The assertion in this constant expression failed."
CONST_EVAL_EXTENSION_METHOD:
problemMessage: "Extension methods can't be used in constant expressions."
CONST_EVAL_EXTENSION_TYPE_METHOD:
problemMessage: "Extension type methods can't be used in constant expressions."
CONST_EVAL_FOR_ELEMENT:
problemMessage: "Constant expressions don't support 'for' elements."
correctionMessage: "Try replacing the 'for' element with a spread, or removing 'const'."

View file

@ -977,6 +977,35 @@ const v2 = v1 + v1;
_assertNull(result);
}
test_visitBinaryExpression_extensionType() async {
await assertErrorsInCode('''
extension type const A(int it) {
int operator +(Object other) => 0;
}
const v1 = A(1);
const v2 = v1 + 2;
''', [
error(CompileTimeErrorCode.CONST_EVAL_EXTENSION_TYPE_METHOD, 101, 6),
]);
final result = _topLevelVar('v2');
_assertNull(result);
}
test_visitBinaryExpression_extensionType_implementsInt() async {
await assertNoErrorsInCode('''
extension type const A(int it) implements int {}
const v1 = A(1);
const v2 = v1 + 2;
''');
final result = _topLevelVar('v2');
assertDartObjectText(result, r'''
int 3
variable: self::@variable::v2
''');
}
test_visitBinaryExpression_gt_int_int() async {
await assertNoErrorsInCode('''
const c = 2 > 3;
@ -2522,6 +2551,35 @@ const v2 = -v1;
_assertNull(result);
}
test_visitPrefixExpression_extensionType() async {
await assertErrorsInCode('''
extension type const A(int it) {
int operator -() => 0;
}
const v1 = A(1);
const v2 = -v1;
''', [
error(CompileTimeErrorCode.CONST_EVAL_EXTENSION_TYPE_METHOD, 89, 3),
]);
final result = _topLevelVar('v2');
_assertNull(result);
}
test_visitPrefixExpression_extensionType_implementsInt() async {
await assertNoErrorsInCode('''
extension type const A(int it) implements int {}
const v1 = A(1);
const v2 = -v1;
''');
final result = _topLevelVar('v2');
assertDartObjectText(result, r'''
int -1
variable: self::@variable::v2
''');
}
test_visitPrefixExpression_logicalNot() async {
await assertNoErrorsInCode('''
const c = !true;
@ -4235,6 +4293,35 @@ const b = B('');
]);
}
test_visitPropertyAccess_length_extensionType() async {
await assertErrorsInCode('''
extension type const A(String it) {
int get length => 0;
}
const v1 = A('');
const v2 = v1.length;
''', [
error(CompileTimeErrorCode.CONST_EVAL_EXTENSION_TYPE_METHOD, 91, 9),
]);
final result = _topLevelVar('v2');
_assertNull(result);
}
test_visitPropertyAccess_length_extensionType_implementsString() async {
await assertNoErrorsInCode('''
extension type const A(String it) implements String {}
const v1 = A('abc');
const v2 = v1.length;
''');
final result = _topLevelVar('v2');
assertDartObjectText(result, r'''
int 3
variable: self::@variable::v2
''');
}
test_visitPropertyAccess_length_unresolvedType() async {
await assertErrorsInCode('''
class B {