Report CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION

Change-Id: Idb6d0963515457e1748362dbda1441d5820a040a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/273521
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2022-12-02 22:56:57 +00:00 committed by Commit Queue
parent 4275d595b6
commit 48d0d95edb
15 changed files with 635 additions and 48 deletions

View file

@ -336,6 +336,8 @@ CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR:
status: needsEvaluation
CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT:
status: needsEvaluation
CompileTimeErrorCode.CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION:
status: needsEvaluation
CompileTimeErrorCode.CONTINUE_LABEL_ON_SWITCH:
status: needsEvaluation
CompileTimeErrorCode.COULD_NOT_INFER:

View file

@ -4302,6 +4302,8 @@ abstract class ExpressionImpl extends AstNodeImpl
var parent = child.parent;
if (parent is ConstantContextForExpressionImpl) {
return true;
} else if (parent is ConstantPatternImpl) {
return true;
} else if (parent is EnumConstantArguments) {
return true;
} else if (parent is TypedLiteralImpl && parent.constKeyword != null) {

View file

@ -99,6 +99,16 @@ class ConstantVerifier extends RecursiveAstVisitor<void> {
}
}
@override
void visitConstantPattern(ConstantPattern node) {
_validate(
node.expression,
CompileTimeErrorCode.CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION,
);
super.visitConstantPattern(node);
}
@override
void visitConstructorDeclaration(ConstructorDeclaration node) {
var constKeyword = node.constKeyword;

View file

@ -22,6 +22,11 @@ class ConstantExpressionsDependenciesFinder extends RecursiveAstVisitor {
HashSet<ConstantEvaluationTarget> dependencies =
HashSet<ConstantEvaluationTarget>();
@override
visitConstantPattern(ConstantPattern node) {
_find(node.expression);
}
@override
void visitInstanceCreationExpression(InstanceCreationExpression node) {
if (node.isConst) {

View file

@ -620,6 +620,14 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
uniqueName: 'CONFLICTING_TYPE_VARIABLE_AND_MIXIN',
);
/// No parameters.
static const CompileTimeErrorCode
CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION = CompileTimeErrorCode(
'CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION',
"The expression of a constant pattern must be a valid constant.",
correctionMessage: "Try making the expression a valid constant.",
);
/// 16.12.2 Const: It is a compile-time error if evaluation of a constant
/// object results in an uncaught exception being thrown.
///

View file

@ -88,6 +88,7 @@ const List<ErrorCode> errorCodeValues = [
CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MEMBER_EXTENSION,
CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MEMBER_MIXIN,
CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MIXIN,
CompileTimeErrorCode.CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION,
CompileTimeErrorCode.CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH,
CompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH,
CompileTimeErrorCode.CONST_CONSTRUCTOR_THROWS_EXCEPTION,

View file

@ -2878,6 +2878,10 @@ CompileTimeErrorCode:
Parameters:
0: the name of the type
CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION:
problemMessage: The expression of a constant pattern must be a valid constant.
correctionMessage: Try making the expression a valid constant.
comment: No parameters.
CONTINUE_LABEL_ON_SWITCH:
problemMessage: A `continue` label resolves to a `switch` statement, but the label must be on a loop or a switch member.
hasPublishedDocs: true

View file

@ -298,7 +298,6 @@ class C {
assertInContext("C()}", true);
}
@FailingTest(reason: 'not yet implemented')
test_inConstantContext_instanceCreation_switch_true() {
parse('''
f(v) {
@ -479,7 +478,6 @@ final x = const (0, [1]);
assertInContext('[1]', true);
}
@FailingTest(reason: 'not yet implemented')
test_inConstantContext_listLiteral_switch_true() {
parse('''
f(v) {
@ -607,7 +605,6 @@ f() {
assertInContext("{'d", true);
}
@FailingTest(reason: 'not yet implemented')
test_inConstantContext_mapLiteral_switch_true() {
parse('''
f(v) {

View file

@ -40,9 +40,10 @@ CastPattern
test_switchCase() async {
await assertNoErrorsInCode(r'''
void f(x, y) {
void f(x) {
const a = 0;
switch (x) {
case y as int:
case a as int:
break;
}
}
@ -52,9 +53,9 @@ void f(x, y) {
CastPattern
pattern: ConstantPattern
expression: SimpleIdentifier
token: y
staticElement: self::@function::f::@parameter::y
staticType: dynamic
token: a
staticElement: a@20
staticType: int
asToken: as
type: NamedType
name: SimpleIdentifier

View file

@ -199,8 +199,12 @@ IfElement
test_rewrite_caseClause_pattern() async {
await assertNoErrorsInCode(r'''
void f(Object x, int Function() a) {
[if (x case const a()) 0];
void f(Object x) {
[if (x case const A()) 0];
}
class A {
const A();
}
''');
@ -218,17 +222,19 @@ IfElement
guardedPattern: GuardedPattern
pattern: ConstantPattern
const: const
expression: FunctionExpressionInvocation
function: SimpleIdentifier
token: a
staticElement: self::@function::f::@parameter::a
staticType: int Function()
expression: InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: SimpleIdentifier
token: A
staticElement: self::@class::A
staticType: null
type: A
staticElement: self::@class::A::@constructor::new
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticElement: <null>
staticInvokeType: int Function()
staticType: int
staticType: A
rightParenthesis: )
thenElement: IntegerLiteral
literal: 0

View file

@ -927,8 +927,12 @@ IfStatement
test_rewrite_caseClause_pattern() async {
await assertNoErrorsInCode(r'''
void f(x, int Function() a) {
if (x case const a()) {}
void f(x) {
if (x case const A()) {}
}
class A {
const A();
}
''');
@ -946,17 +950,19 @@ IfStatement
guardedPattern: GuardedPattern
pattern: ConstantPattern
const: const
expression: FunctionExpressionInvocation
function: SimpleIdentifier
token: a
staticElement: self::@function::f::@parameter::a
staticType: int Function()
expression: InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: SimpleIdentifier
token: A
staticElement: self::@class::A
staticType: null
type: A
staticElement: self::@class::A::@constructor::new
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticElement: <null>
staticInvokeType: int Function()
staticType: int
staticType: A
rightParenthesis: )
thenStatement: Block
leftBracket: {

View file

@ -94,12 +94,16 @@ SwitchExpressionCase
test_rewrite_case_pattern() async {
await assertNoErrorsInCode(r'''
void f(Object? x, int Function() a) {
void f(Object? x) {
(switch (x) {
const a() => 0,
const A() => 0,
_ => 1,
});
}
class A {
const A();
}
''');
final node = findNode.switchExpressionCase('=> 0');
@ -108,17 +112,19 @@ SwitchExpressionCase
guardedPattern: GuardedPattern
pattern: ConstantPattern
const: const
expression: FunctionExpressionInvocation
function: SimpleIdentifier
token: a
staticElement: self::@function::f::@parameter::a
staticType: int Function()
expression: InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: SimpleIdentifier
token: A
staticElement: self::@class::A
staticType: null
type: A
staticElement: self::@class::A::@constructor::new
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticElement: <null>
staticInvokeType: int Function()
staticType: int
staticType: A
arrow: =>
expression: IntegerLiteral
literal: 0

View file

@ -134,12 +134,16 @@ SwitchStatement
test_rewrite_pattern() async {
await assertNoErrorsInCode(r'''
void f(Object? x, int Function() a) {
void f(Object? x) {
switch (x) {
case const a():
case const A():
break;
}
}
class A {
const A();
}
''');
final node = findNode.switchStatement('switch');
@ -159,17 +163,19 @@ SwitchStatement
guardedPattern: GuardedPattern
pattern: ConstantPattern
const: const
expression: FunctionExpressionInvocation
function: SimpleIdentifier
token: a
staticElement: self::@function::f::@parameter::a
staticType: int Function()
expression: InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: SimpleIdentifier
token: A
staticElement: self::@class::A
staticType: null
type: A
staticElement: self::@class::A::@constructor::new
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticElement: <null>
staticInvokeType: int Function()
staticType: int
staticType: A
colon: :
statements
BreakStatement

View file

@ -0,0 +1,530 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/src/error/codes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../dart/resolution/context_collection_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ConstantPatternWithNonConstantExpressionTest);
});
}
@reflectiveTest
class ConstantPatternWithNonConstantExpressionTest
extends PubPackageResolutionTest {
test_boolLiteral() async {
await assertNoErrorsInCode(r'''
void f(x) {
if (x case true) {}
}
''');
var node = findNode.singleGuardedPattern;
assertResolvedNodeText(node, r'''
GuardedPattern
pattern: ConstantPattern
expression: BooleanLiteral
literal: true
staticType: bool
''');
}
test_class_field_const() async {
await assertNoErrorsInCode(r'''
class A {
static const a = 0;
}
void f(x) {
if (x case A.a) {}
}
''');
var node = findNode.singleGuardedPattern;
assertResolvedNodeText(node, r'''
GuardedPattern
pattern: ConstantPattern
expression: PrefixedIdentifier
prefix: SimpleIdentifier
token: A
staticElement: self::@class::A
staticType: null
period: .
identifier: SimpleIdentifier
token: a
staticElement: self::@class::A::@getter::a
staticType: int
staticElement: self::@class::A::@getter::a
staticType: int
''');
}
test_class_field_notConst() async {
await assertErrorsInCode(r'''
class A {
static final a = 0;
}
void f(x) {
if (x case A.a) {}
}
''', [
error(CompileTimeErrorCode.CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION,
60, 3),
]);
}
test_doubleLiteral() async {
await assertNoErrorsInCode(r'''
void f(x) {
if (x case 1.2) {}
}
''');
var node = findNode.singleGuardedPattern;
assertResolvedNodeText(node, r'''
GuardedPattern
pattern: ConstantPattern
expression: DoubleLiteral
literal: 1.2
staticType: double
''');
}
test_importPredix_class_field_const() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
static const a = 0;
}
''');
await assertNoErrorsInCode(r'''
import 'a.dart' as prefix;
void f(x) {
if (x case prefix.A.a) {}
}
''');
var node = findNode.singleGuardedPattern;
assertResolvedNodeText(node, r'''
GuardedPattern
pattern: ConstantPattern
expression: PropertyAccess
target: PrefixedIdentifier
prefix: SimpleIdentifier
token: prefix
staticElement: self::@prefix::prefix
staticType: null
period: .
identifier: SimpleIdentifier
token: A
staticElement: package:test/a.dart::@class::A
staticType: null
staticElement: package:test/a.dart::@class::A
staticType: null
operator: .
propertyName: SimpleIdentifier
token: a
staticElement: package:test/a.dart::@class::A::@getter::a
staticType: int
staticType: int
''');
}
test_importPredix_class_field_notConst() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
static const a = 0;
}
''');
await assertNoErrorsInCode(r'''
import 'a.dart' as prefix;
void f(x) {
if (x case prefix.A.a) {}
}
''');
var node = findNode.singleGuardedPattern;
assertResolvedNodeText(node, r'''
GuardedPattern
pattern: ConstantPattern
expression: PropertyAccess
target: PrefixedIdentifier
prefix: SimpleIdentifier
token: prefix
staticElement: self::@prefix::prefix
staticType: null
period: .
identifier: SimpleIdentifier
token: A
staticElement: package:test/a.dart::@class::A
staticType: null
staticElement: package:test/a.dart::@class::A
staticType: null
operator: .
propertyName: SimpleIdentifier
token: a
staticElement: package:test/a.dart::@class::A::@getter::a
staticType: int
staticType: int
''');
}
test_instanceCreation_const() async {
await assertNoErrorsInCode(r'''
class A {
const A();
}
void f(x) {
if (x case const A()) {}
}
''');
var node = findNode.singleGuardedPattern;
assertResolvedNodeText(node, r'''
GuardedPattern
pattern: ConstantPattern
const: const
expression: InstanceCreationExpression
constructorName: ConstructorName
type: NamedType
name: SimpleIdentifier
token: A
staticElement: self::@class::A
staticType: null
type: A
staticElement: self::@class::A::@constructor::new
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticType: A
''');
}
test_intLiteral() async {
await assertNoErrorsInCode(r'''
void f(x) {
if (x case 0) {}
}
''');
var node = findNode.singleGuardedPattern;
assertResolvedNodeText(node, r'''
GuardedPattern
pattern: ConstantPattern
expression: IntegerLiteral
literal: 0
staticType: int
''');
}
test_listLiteral_element_intLiteral() async {
await assertNoErrorsInCode(r'''
void f(x) {
if (x case const [0]) {}
}
''');
var node = findNode.singleGuardedPattern;
assertResolvedNodeText(node, r'''
GuardedPattern
pattern: ConstantPattern
const: const
expression: ListLiteral
leftBracket: [
elements
IntegerLiteral
literal: 0
staticType: int
rightBracket: ]
staticType: List<int>
''');
}
test_listLiteral_element_localVariable_const() async {
await assertNoErrorsInCode(r'''
void f(x) {
const a = 0;
if (x case const [a]) {}
}
''');
var node = findNode.singleGuardedPattern;
assertResolvedNodeText(node, r'''
GuardedPattern
pattern: ConstantPattern
const: const
expression: ListLiteral
leftBracket: [
elements
SimpleIdentifier
token: a
staticElement: a@20
staticType: int
rightBracket: ]
staticType: List<int>
''');
}
test_listLiteral_element_localVariable_notConst() async {
await assertErrorsInCode(r'''
void f(x) {
final a = 0;
if (x case const [a]) {}
}
''', [
error(CompileTimeErrorCode.CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION,
47, 1),
error(CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT, 47, 1),
]);
}
test_localVariable_const() async {
await assertNoErrorsInCode(r'''
void f(x) {
const a = 0;
if (x case a) {}
}
''');
var node = findNode.singleGuardedPattern;
assertResolvedNodeText(node, r'''
GuardedPattern
pattern: ConstantPattern
expression: SimpleIdentifier
token: a
staticElement: a@20
staticType: int
''');
}
test_localVariable_notConst() async {
await assertErrorsInCode(r'''
void f(x) {
var a = 0;
if (x case a) {}
}
''', [
error(CompileTimeErrorCode.CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION,
38, 1),
]);
}
test_mapLiteral_entries_intLiteral_intLiteral() async {
await assertNoErrorsInCode(r'''
void f(x) {
if (x case const {0: 1}) {}
}
''');
var node = findNode.singleGuardedPattern;
assertResolvedNodeText(node, r'''
GuardedPattern
pattern: ConstantPattern
const: const
expression: SetOrMapLiteral
leftBracket: {
elements
SetOrMapLiteral
key: IntegerLiteral
literal: 0
staticType: int
separator: :
value: IntegerLiteral
literal: 1
staticType: int
rightBracket: }
isMap: true
staticType: Map<int, int>
''');
}
test_mapLiteral_entries_key_localVariable_const() async {
await assertNoErrorsInCode(r'''
void f(x) {
const a = 0;
if (x case const {a: 1}) {}
}
''');
var node = findNode.singleGuardedPattern;
assertResolvedNodeText(node, r'''
GuardedPattern
pattern: ConstantPattern
const: const
expression: SetOrMapLiteral
leftBracket: {
elements
SetOrMapLiteral
key: SimpleIdentifier
token: a
staticElement: a@20
staticType: int
separator: :
value: IntegerLiteral
literal: 1
staticType: int
rightBracket: }
isMap: true
staticType: Map<int, int>
''');
}
test_mapLiteral_entries_key_localVariable_notConst() async {
await assertErrorsInCode(r'''
void f(x) {
final a = 0;
if (x case const {a: 1}) {}
}
''', [
error(CompileTimeErrorCode.CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION,
47, 1),
error(CompileTimeErrorCode.NON_CONSTANT_MAP_KEY, 47, 1),
]);
}
test_mapLiteral_entries_value_localVariable_const() async {
await assertNoErrorsInCode(r'''
void f(x) {
const a = 0;
if (x case const {0: a}) {}
}
''');
var node = findNode.singleGuardedPattern;
assertResolvedNodeText(node, r'''
GuardedPattern
pattern: ConstantPattern
const: const
expression: SetOrMapLiteral
leftBracket: {
elements
SetOrMapLiteral
key: IntegerLiteral
literal: 0
staticType: int
separator: :
value: SimpleIdentifier
token: a
staticElement: a@20
staticType: int
rightBracket: }
isMap: true
staticType: Map<int, int>
''');
}
test_mapLiteral_entries_value_localVariable_notConst() async {
await assertErrorsInCode(r'''
void f(x) {
final a = 0;
if (x case const {0: a}) {}
}
''', [
error(CompileTimeErrorCode.CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION,
50, 1),
error(CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE, 50, 1),
]);
}
test_setLiteral_element_intLiteral() async {
await assertNoErrorsInCode(r'''
void f(x) {
if (x case const {0}) {}
}
''');
var node = findNode.singleGuardedPattern;
assertResolvedNodeText(node, r'''
GuardedPattern
pattern: ConstantPattern
const: const
expression: SetOrMapLiteral
leftBracket: {
elements
IntegerLiteral
literal: 0
staticType: int
rightBracket: }
isMap: false
staticType: Set<int>
''');
}
test_setLiteral_element_localVariable_const() async {
await assertNoErrorsInCode(r'''
void f(x) {
const a = 0;
if (x case const {a}) {}
}
''');
var node = findNode.singleGuardedPattern;
assertResolvedNodeText(node, r'''
GuardedPattern
pattern: ConstantPattern
const: const
expression: SetOrMapLiteral
leftBracket: {
elements
SimpleIdentifier
token: a
staticElement: a@20
staticType: int
rightBracket: }
isMap: false
staticType: Set<int>
''');
}
test_topLevelVariable_const() async {
await assertNoErrorsInCode(r'''
const a = 0;
void f(x) {
if (x case a) {}
}
''');
var node = findNode.singleGuardedPattern;
assertResolvedNodeText(node, r'''
GuardedPattern
pattern: ConstantPattern
expression: SimpleIdentifier
token: a
staticElement: self::@getter::a
staticType: int
''');
}
test_topLevelVariable_notConst() async {
await assertErrorsInCode(r'''
final a = 0;
void f(x) {
if (x case a) {}
}
''', [
error(CompileTimeErrorCode.CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION,
39, 1),
]);
var node = findNode.singleGuardedPattern;
assertResolvedNodeText(node, r'''
GuardedPattern
pattern: ConstantPattern
expression: SimpleIdentifier
token: a
staticElement: self::@getter::a
staticType: int
''');
}
}

View file

@ -126,6 +126,8 @@ import 'const_with_non_type_test.dart' as const_with_non_type;
import 'const_with_type_parameters_test.dart' as const_with_type_parameters;
import 'const_with_undefined_constructor_test.dart'
as const_with_undefined_constructor;
import 'constant_pattern_with_non_constant_expression_test.dart'
as constant_pattern_with_non_constant_expression;
import 'could_not_infer_test.dart' as could_not_infer;
import 'creation_of_struct_or_union_test.dart' as creation_of_struct_or_union;
import 'dead_code_test.dart' as dead_code;
@ -901,6 +903,7 @@ main() {
const_with_non_type.main();
const_with_type_parameters.main();
const_with_undefined_constructor.main();
constant_pattern_with_non_constant_expression.main();
could_not_infer.main();
creation_of_struct_or_union.main();
dead_code.main();