Report RELATIONAL_PATTERN_ARGUMENT_TYPE_NOT_ASSIGNABLE instead.

Bug: https://github.com/dart-lang/sdk/issues/51679
Change-Id: Ia0655afa328dbc20ee7ee0fb62675a0f26f9be19
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/287940
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Konstantin Shcheglov 2023-03-10 01:56:23 +00:00 committed by Commit Queue
parent d0024e9931
commit 74fb9e190f
12 changed files with 233 additions and 188 deletions

View file

@ -1653,9 +1653,10 @@ mixin TypeAnalyzer<
Type argumentType =
isEquality ? operations.promoteToNonNull(operandType) : operandType;
if (!operations.isAssignableTo(argumentType, operator.parameterType)) {
argumentTypeNotAssignableError = errors.argumentTypeNotAssignable(
argument: operand,
argumentType: argumentType,
argumentTypeNotAssignableError =
errors.relationalPatternOperandTypeNotAssignable(
pattern: node,
operandType: argumentType,
parameterType: operator.parameterType,
);
}
@ -2511,14 +2512,6 @@ abstract class TypeAnalyzerErrors<
Type extends Object,
Pattern extends Node,
Error> implements TypeAnalyzerErrorsBase {
/// Called if [argument] has type [argumentType], which is not assignable
/// to [parameterType].
Error argumentTypeNotAssignable({
required Expression argument,
required Type argumentType,
required Type parameterType,
});
/// Called if pattern support is disabled and a case constant's static type
/// doesn't properly match the scrutinee's static type.
Error caseExpressionTypeMismatch(
@ -2627,6 +2620,14 @@ abstract class TypeAnalyzerErrors<
Error refutablePatternInIrrefutableContext(
{required Node pattern, required Node context});
/// Called if the operand of the [pattern] has the type [operandType], which
/// is not assignable to [parameterType] of the invoked relational operator.
Error relationalPatternOperandTypeNotAssignable({
required Pattern pattern,
required Type operandType,
required Type parameterType,
});
/// Called if the [returnType] of the invoked relational operator is not
/// assignable to `bool`.
Error relationalPatternOperatorReturnTypeNotAssignableToBool({

View file

@ -2991,19 +2991,6 @@ class _MiniAstErrors
/// highlight the point of failure.
StackTrace? _assertInErrorRecoveryStack;
@override
void argumentTypeNotAssignable({
required Expression argument,
required Type argumentType,
required Type parameterType,
}) {
_recordError('argumentTypeNotAssignable', {
'argument': argument,
'argumentType': argumentType,
'parameterType': parameterType,
});
}
@override
void assertInErrorRecovery() {
if (_accumulatedErrors.isEmpty) {
@ -3181,6 +3168,19 @@ class _MiniAstErrors
{'pattern': pattern, 'context': context});
}
@override
void relationalPatternOperandTypeNotAssignable({
required Pattern pattern,
required Type operandType,
required Type parameterType,
}) {
_recordError('relationalPatternOperandTypeNotAssignable', {
'pattern': pattern,
'operandType': operandType,
'parameterType': parameterType,
});
}
@override
void relationalPatternOperatorReturnTypeNotAssignableToBool({
required Pattern pattern,

View file

@ -3633,13 +3633,13 @@ main() {
h.run([
ifCase(
expr('int').checkContext('?'),
relationalPattern('>', expr('String')..errorId = 'OPERAND'),
relationalPattern('>', expr('String'))..errorId = 'PATTERN',
[],
).checkIr('ifCase(expr(int), >(expr(String), '
'matchedType: int), variables(), true, block(), noop)')
], expectedErrors: {
'argumentTypeNotAssignable(argument: OPERAND, '
'argumentType: String, parameterType: num)'
'relationalPatternOperandTypeNotAssignable(pattern: PATTERN, '
'operandType: String, parameterType: num)'
});
});
test('return type is not assignable to bool', () {

View file

@ -1017,6 +1017,8 @@ CompileTimeErrorCode.REFERENCED_BEFORE_DECLARATION:
status: needsEvaluation
CompileTimeErrorCode.REFUTABLE_PATTERN_IN_IRREFUTABLE_CONTEXT:
status: noFix
CompileTimeErrorCode.RELATIONAL_PATTERN_OPERAND_TYPE_NOT_ASSIGNABLE:
status: noFix
CompileTimeErrorCode.RELATIONAL_PATTERN_OPERATOR_RETURN_TYPE_NOT_ASSIGNABLE_TO_BOOL:
status: noFix
CompileTimeErrorCode.REST_ELEMENT_NOT_LAST_IN_MAP_PATTERN:

View file

@ -28,19 +28,6 @@ class SharedTypeAnalyzerErrors
SharedTypeAnalyzerErrors(this._errorReporter);
@override
void argumentTypeNotAssignable({
required Expression argument,
required DartType argumentType,
required DartType parameterType,
}) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE,
argument,
[argumentType, parameterType],
);
}
@override
void assertInErrorRecovery() {}
@ -233,6 +220,19 @@ class SharedTypeAnalyzerErrors
);
}
@override
void relationalPatternOperandTypeNotAssignable({
required covariant RelationalPatternImpl pattern,
required DartType operandType,
required DartType parameterType,
}) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.RELATIONAL_PATTERN_OPERAND_TYPE_NOT_ASSIGNABLE,
pattern.operand,
[operandType, parameterType, pattern.operator.lexeme],
);
}
@override
void relationalPatternOperatorReturnTypeNotAssignableToBool({
required covariant RelationalPatternImpl pattern,

View file

@ -4265,6 +4265,17 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
"instead.",
);
/// Parameters:
/// 0: the operand type
/// 1: the parameter type of the invoked operator
/// 2: the name of the invoked operator
static const CompileTimeErrorCode
RELATIONAL_PATTERN_OPERAND_TYPE_NOT_ASSIGNABLE = CompileTimeErrorCode(
'RELATIONAL_PATTERN_OPERAND_TYPE_NOT_ASSIGNABLE',
"The constant expression type '{0}' is not assignable to the parameter "
"type '{1}' of the '{2}' operator.",
);
static const CompileTimeErrorCode
RELATIONAL_PATTERN_OPERATOR_RETURN_TYPE_NOT_ASSIGNABLE_TO_BOOL =
CompileTimeErrorCode(

View file

@ -432,6 +432,7 @@ const List<ErrorCode> errorCodeValues = [
CompileTimeErrorCode.REDIRECT_TO_TYPE_ALIAS_EXPANDS_TO_TYPE_PARAMETER,
CompileTimeErrorCode.REFERENCED_BEFORE_DECLARATION,
CompileTimeErrorCode.REFUTABLE_PATTERN_IN_IRREFUTABLE_CONTEXT,
CompileTimeErrorCode.RELATIONAL_PATTERN_OPERAND_TYPE_NOT_ASSIGNABLE,
CompileTimeErrorCode
.RELATIONAL_PATTERN_OPERATOR_RETURN_TYPE_NOT_ASSIGNABLE_TO_BOOL,
CompileTimeErrorCode.REST_ELEMENT_NOT_LAST_IN_MAP_PATTERN,

View file

@ -12464,6 +12464,13 @@ CompileTimeErrorCode:
REFUTABLE_PATTERN_IN_IRREFUTABLE_CONTEXT:
problemMessage: Refutable patterns can't be used in an irrefutable context.
correctionMessage: Try using an if-case, a 'switch' statement, or a 'switch' expression instead.
RELATIONAL_PATTERN_OPERAND_TYPE_NOT_ASSIGNABLE:
problemMessage: "The constant expression type '{0}' is not assignable to the parameter type '{1}' of the '{2}' operator."
comment: |-
Parameters:
0: the operand type
1: the parameter type of the invoked operator
2: the name of the invoked operator
RELATIONAL_PATTERN_OPERATOR_RETURN_TYPE_NOT_ASSIGNABLE_TO_BOOL:
problemMessage: The return type of operators used in relational patterns must be assignable to 'bool'.
correctionMessage: Try updating the operator declaration to return 'bool'.

View file

@ -217,135 +217,6 @@ void g() {
error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 44, 12),
]);
}
test_relationalPattern_bangEq_matchedValueNullable() async {
await assertNoErrorsInCode(r'''
class A {}
void f(A? x) {
switch (x) {
case != null:
break;
}
}
''');
}
test_relationalPattern_bangEq_operandNull() async {
await assertNoErrorsInCode(r'''
class A {}
void f(A x) {
switch (x) {
case != null:
break;
}
}
''');
}
test_relationalPattern_bangEq_operandNullable() async {
await assertNoErrorsInCode(r'''
class A {}
const int? y = 0;
void f(A x) {
switch (x) {
case != y:
break;
}
}
''');
}
test_relationalPattern_eqEq() async {
await assertNoErrorsInCode(r'''
class A {}
void f(A x) {
switch (x) {
case == 0:
break;
}
}
''');
}
test_relationalPattern_eqEq_covariantParameterType() async {
await assertErrorsInCode(r'''
class A {
bool operator ==(covariant A other) => true;
}
void f(A x) {
switch (x) {
case == 0:
break;
}
}
''', [
error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 101, 1),
]);
}
test_relationalPattern_eqEq_matchedValueNullable() async {
await assertNoErrorsInCode(r'''
class A {}
void f(A? x) {
switch (x) {
case == null:
break;
}
}
''');
}
test_relationalPattern_eqEq_operandNull() async {
await assertNoErrorsInCode(r'''
class A {}
void f(A x) {
switch (x) {
case == null:
break;
}
}
''');
}
test_relationalPattern_eqEq_operandNullable() async {
await assertNoErrorsInCode(r'''
class A {}
const int? y = 0;
void f(A x) {
switch (x) {
case == y:
break;
}
}
''');
}
test_relationalPattern_greaterThan() async {
await assertErrorsInCode(r'''
class A {
bool operator >(A other) => true;
}
void f(A x) {
switch (x) {
case > 0:
break;
}
}
''', [
error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 89, 1),
]);
}
}
mixin ArgumentTypeNotAssignableTestCases on PubPackageResolutionTest {

View file

@ -0,0 +1,149 @@
// Copyright (c) 2023, 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(RelationalPatternArgumentTypeNotAssignableTest);
});
}
@reflectiveTest
class RelationalPatternArgumentTypeNotAssignableTest
extends PubPackageResolutionTest {
test_bangEq_matchedValueNullable() async {
await assertNoErrorsInCode(r'''
class A {}
void f(A? x) {
switch (x) {
case != null:
break;
}
}
''');
}
test_bangEq_operandNull() async {
await assertNoErrorsInCode(r'''
class A {}
void f(A x) {
switch (x) {
case != null:
break;
}
}
''');
}
test_bangEq_operandNullable() async {
await assertNoErrorsInCode(r'''
class A {}
const int? y = 0;
void f(A x) {
switch (x) {
case != y:
break;
}
}
''');
}
test_eqEq() async {
await assertNoErrorsInCode(r'''
class A {}
void f(A x) {
switch (x) {
case == 0:
break;
}
}
''');
}
test_eqEq_covariantParameterType() async {
await assertErrorsInCode(r'''
class A {
bool operator ==(covariant A other) => true;
}
void f(A x) {
switch (x) {
case == 0:
break;
}
}
''', [
error(CompileTimeErrorCode.RELATIONAL_PATTERN_OPERAND_TYPE_NOT_ASSIGNABLE,
101, 1),
]);
}
test_eqEq_matchedValueNullable() async {
await assertNoErrorsInCode(r'''
class A {}
void f(A? x) {
switch (x) {
case == null:
break;
}
}
''');
}
test_eqEq_operandNull() async {
await assertNoErrorsInCode(r'''
class A {}
void f(A x) {
switch (x) {
case == null:
break;
}
}
''');
}
test_eqEq_operandNullable() async {
await assertNoErrorsInCode(r'''
class A {}
const int? y = 0;
void f(A x) {
switch (x) {
case == y:
break;
}
}
''');
}
test_greaterThan() async {
await assertErrorsInCode(r'''
class A {
bool operator >(A other) => true;
}
void f(A x) {
switch (x) {
case > 0:
break;
}
}
''', [
error(CompileTimeErrorCode.RELATIONAL_PATTERN_OPERAND_TYPE_NOT_ASSIGNABLE,
89, 1),
]);
}
}

View file

@ -691,6 +691,8 @@ import 'referenced_before_declaration_test.dart'
as referenced_before_declaration;
import 'refutable_pattern_in_irrefutable_context_test.dart'
as refutable_pattern_in_irrefutable_context;
import 'relational_pattern_operand_type_not_assignable_test.dart'
as relational_pattern_operand_type_not_assignable;
import 'relational_pattern_operator_return_type_not_assignable_to_bool_test.dart'
as relational_pattern_operator_return_type_not_assignable_to_bool;
import 'removed_lint_use_test.dart' as removed_lint_in_ignore;
@ -1324,6 +1326,7 @@ main() {
redirect_to_type_alias_expands_to_type_parameter.main();
referenced_before_declaration.main();
refutable_pattern_in_irrefutable_context.main();
relational_pattern_operand_type_not_assignable.main();
relational_pattern_operator_return_type_not_assignable_to_bool.main();
removed_lint_in_ignore.main();
replaced_lint_in_ignore.main();

View file

@ -36,19 +36,6 @@ class SharedTypeAnalyzerErrors
required this.coreTypes,
required this.isNonNullableByDefault});
@override
InvalidExpression argumentTypeNotAssignable({
required Expression argument,
required DartType argumentType,
required DartType parameterType,
}) {
return helper.buildProblem(
templateArgumentTypeNotAssignable.withArguments(
argumentType, parameterType, isNonNullableByDefault),
argument.fileOffset,
noLength);
}
@override
void assertInErrorRecovery() {
// TODO(paulberry): figure out how to do this.
@ -164,12 +151,6 @@ class SharedTypeAnalyzerErrors
messageNonBoolCondition, node.fileOffset, noLength);
}
@override
void patternDoesNotAllowLate({required TreeNode pattern}) {
// TODO(johnniwinther): Is late even supported by the grammar or parser?
throw new UnimplementedError('TODO(paulberry)');
}
@override
InvalidExpression nonExhaustiveSwitch(
{required TreeNode node, required DartType scrutineeType}) {
@ -188,6 +169,12 @@ class SharedTypeAnalyzerErrors
noLength);
}
@override
void patternDoesNotAllowLate({required TreeNode pattern}) {
// TODO(johnniwinther): Is late even supported by the grammar or parser?
throw new UnimplementedError('TODO(paulberry)');
}
@override
InvalidExpression patternForInExpressionIsNotIterable({
required TreeNode node,
@ -217,6 +204,19 @@ class SharedTypeAnalyzerErrors
pattern.fileOffset, noLength);
}
@override
InvalidExpression relationalPatternOperandTypeNotAssignable({
required covariant RelationalPattern pattern,
required DartType operandType,
required DartType parameterType,
}) {
return helper.buildProblem(
templateArgumentTypeNotAssignable.withArguments(
operandType, parameterType, isNonNullableByDefault),
pattern.expression.fileOffset,
noLength);
}
@override
InvalidExpression relationalPatternOperatorReturnTypeNotAssignableToBool({
required Pattern pattern,