Skip to content

Commit 9ec1a84

Browse files
kallentuCommit Queue
authored andcommitted
[analyzer] Produce CONST_EVAL_EXTENSION_METHOD diagnostic when using extension methods in const contexts.
More specific error code. Semi-related to work in https://dart-review.googlesource.com/c/sdk/+/301505. Change-Id: I1233e9e31389d387a9c777fe83cdd30a224bd00e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/310760 Reviewed-by: Brian Wilkerson <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Kallen Tu <[email protected]>
1 parent 09f6d8b commit 9ec1a84

File tree

11 files changed

+97
-44
lines changed

11 files changed

+97
-44
lines changed

pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,8 @@ CompileTimeErrorCode.CONST_DEFERRED_CLASS:
380380
status: needsFix
381381
notes: |-
382382
Remove the `deferred` keyword from the import.
383+
CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD:
384+
status: needsEvaluation
383385
CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT:
384386
status: needsEvaluation
385387
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION:

pkg/analyzer/lib/src/dart/constant/constant_verifier.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,8 @@ class ConstantVerifier extends RecursiveAstVisitor<void> {
552552
AnalysisError data = errors[i];
553553
ErrorCode dataErrorCode = data.errorCode;
554554
if (identical(dataErrorCode,
555+
CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD) ||
556+
identical(dataErrorCode,
555557
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION) ||
556558
identical(
557559
dataErrorCode, CompileTimeErrorCode.CONST_EVAL_THROWS_IDBZE) ||

pkg/analyzer/lib/src/dart/constant/evaluation.dart

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -593,8 +593,11 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
593593
@override
594594
Constant? visitBinaryExpression(BinaryExpression node) {
595595
if (node.staticElement?.enclosingElement is ExtensionElement) {
596-
_error(node, null);
597-
return null;
596+
// TODO(kallentu): Don't report error here.
597+
_errorReporter.reportErrorForNode(
598+
CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD, node);
599+
return InvalidConstant(
600+
node, CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD);
598601
}
599602

600603
TokenType operatorType = node.operator.type;
@@ -1084,8 +1087,11 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
10841087
return null;
10851088
}
10861089
if (node.staticElement?.enclosingElement is ExtensionElement) {
1087-
_error(node, null);
1088-
return null;
1090+
// TODO(kallentu): Don't report error here.
1091+
_errorReporter.reportErrorForNode(
1092+
CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD, node);
1093+
return InvalidConstant(
1094+
node, CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD);
10891095
}
10901096
if (node.operator.type == TokenType.BANG) {
10911097
return _dartObjectComputer.logicalNot(node, operand);

pkg/analyzer/lib/src/error/codes.g.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,12 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
795795
hasPublishedDocs: true,
796796
);
797797

798+
static const CompileTimeErrorCode CONST_EVAL_EXTENSION_METHOD =
799+
CompileTimeErrorCode(
800+
'CONST_EVAL_EXTENSION_METHOD',
801+
"Extension methods can't be used in constant expressions.",
802+
);
803+
798804
static const CompileTimeErrorCode CONST_EVAL_FOR_ELEMENT =
799805
CompileTimeErrorCode(
800806
'CONST_EVAL_FOR_ELEMENT',

pkg/analyzer/lib/src/error/error_code_values.g.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ const List<ErrorCode> errorCodeValues = [
109109
CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER,
110110
CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_FINAL_FIELD,
111111
CompileTimeErrorCode.CONST_DEFERRED_CLASS,
112+
CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD,
112113
CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT,
113114
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION,
114115
CompileTimeErrorCode.CONST_EVAL_THROWS_IDBZE,

pkg/analyzer/lib/src/lint/linter.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -904,6 +904,7 @@ class _ConstantAnalysisErrorListener extends AnalysisErrorListener {
904904
switch (errorCode) {
905905
case CompileTimeErrorCode
906906
.CONST_CONSTRUCTOR_WITH_FIELD_INITIALIZED_BY_NON_CONST:
907+
case CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD:
907908
case CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL:
908909
case CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_INT:
909910
case CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING:

pkg/analyzer/messages.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2568,6 +2568,8 @@ CompileTimeErrorCode:
25682568

25692569
const json2 = convert.JsonCodec();
25702570
```
2571+
CONST_EVAL_EXTENSION_METHOD:
2572+
problemMessage: "Extension methods can't be used in constant expressions."
25712573
CONST_EVAL_FOR_ELEMENT:
25722574
problemMessage: "Constant expressions don't support 'for' elements."
25732575
correctionMessage: "Try replacing the 'for' element with a spread, or removing 'const'."

pkg/analyzer/test/generated/constant_test.dart

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -368,26 +368,6 @@ const [for (var i = 0; i < 4; i++) i]
368368
await _assertValueInt(-42, "-42");
369369
}
370370

371-
test_negated_object_hasExtension() async {
372-
await assertErrorsInCode('''
373-
extension on Object {
374-
int operator -() => 0;
375-
}
376-
377-
const Object v1 = 1;
378-
const v2 = -v1;
379-
''', [
380-
error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 82,
381-
3),
382-
]);
383-
384-
final v2 = findElement.topVar('v2');
385-
final evaluationResult = v2.evaluationResult;
386-
assertDartObjectText(evaluationResult.value, r'''
387-
<null>
388-
''');
389-
}
390-
391371
/// Even though it is an error to specify a default value for a required
392372
/// parameter, we still can evaluate it.
393373
test_normalParameter_requiredNamed_hasDefault() async {
@@ -577,26 +557,6 @@ E<String>
577557
await _assertValueInt(5, "2 + 3");
578558
}
579559

580-
test_plus_object_hasExtension() async {
581-
await assertErrorsInCode('''
582-
extension on Object {
583-
int operator +(Object other) => 0;
584-
}
585-
586-
const Object v1 = 0;
587-
const v2 = v1 + v1;
588-
''', [
589-
error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 94,
590-
7),
591-
]);
592-
593-
final v2 = findElement.topVar('v2');
594-
final evaluationResult = v2.evaluationResult;
595-
assertDartObjectText(evaluationResult.value, r'''
596-
<null>
597-
''');
598-
}
599-
600560
test_plus_string_string() async {
601561
await _assertValueString("ab", "'a' + 'b'");
602562
}

pkg/analyzer/test/src/dart/constant/evaluation_test.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,20 @@ const c = identical(int, int);
483483
);
484484
}
485485

486+
test_visitBinaryExpression_extensionMethod() async {
487+
await assertErrorsInCode('''
488+
extension on Object {
489+
int operator +(Object other) => 0;
490+
}
491+
492+
const Object v1 = 0;
493+
const v2 = v1 + v1;
494+
''', [
495+
error(CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD, 94, 7),
496+
]);
497+
_assertNull('v2');
498+
}
499+
486500
test_visitBinaryExpression_gtGtGt_negative_fewerBits() async {
487501
await resolveTestCode('''
488502
const c = 0xFFFFFFFF >>> 8;
@@ -1383,6 +1397,20 @@ const void Function(int) h = self.g;
13831397
_assertTypeArguments(result, ['int']);
13841398
}
13851399

1400+
test_visitPrefixExpression_extensionMethod() async {
1401+
await assertErrorsInCode('''
1402+
extension on Object {
1403+
int operator -() => 0;
1404+
}
1405+
1406+
const Object v1 = 1;
1407+
const v2 = -v1;
1408+
''', [
1409+
error(CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD, 82, 3),
1410+
]);
1411+
_assertNull('v2');
1412+
}
1413+
13861414
test_visitPropertyAccess_genericFunction_instantiated() async {
13871415
await resolveTestCode('''
13881416
import '' as self;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/src/error/codes.dart';
6+
import 'package:test_reflective_loader/test_reflective_loader.dart';
7+
8+
import '../dart/resolution/context_collection_resolution.dart';
9+
10+
main() {
11+
defineReflectiveSuite(() {
12+
defineReflectiveTests(ConstEvalExtensionMethodTest);
13+
});
14+
}
15+
16+
@reflectiveTest
17+
class ConstEvalExtensionMethodTest extends PubPackageResolutionTest {
18+
test_binaryExpression() async {
19+
await assertErrorsInCode('''
20+
extension on Object {
21+
int operator +(Object other) => 0;
22+
}
23+
24+
const Object v1 = 0;
25+
const v2 = v1 + v1;
26+
''', [
27+
error(CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD, 94, 7),
28+
]);
29+
}
30+
31+
test_prefixExpression() async {
32+
await assertErrorsInCode('''
33+
extension on Object {
34+
int operator -() => 0;
35+
}
36+
37+
const Object v1 = 1;
38+
const v2 = -v1;
39+
''', [
40+
error(CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD, 82, 3),
41+
]);
42+
}
43+
}

0 commit comments

Comments
 (0)