CONSTANT_PATTERN_NEVER_MATCHES_VALUE_TYPE: allow int vs int, expand checks for generic types.

Change-Id: If360cdcba68b934d84fab816ef280598f8038624
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/285081
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2023-02-28 02:53:11 +00:00 committed by Commit Queue
parent 8041daf7b0
commit d1559a99b6
3 changed files with 153 additions and 5 deletions

View file

@ -23,6 +23,7 @@ import 'package:analyzer/src/dart/constant/has_type_parameter_reference.dart';
import 'package:analyzer/src/dart/constant/potentially_constant.dart';
import 'package:analyzer/src/dart/constant/value.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/least_greatest_closure.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/diagnostic/diagnostic_factory.dart';
@ -447,15 +448,23 @@ class ConstantVerifier extends RecursiveAstVisitor<void> {
/// `false`, taking into account the fact that [constantType] has primitive
/// equality.
bool _canBeEqual(DartType constantType, DartType valueType) {
if (constantType is InterfaceType && constantType.typeArguments.isEmpty) {
if (constantType is InterfaceType) {
if (valueType is InterfaceType) {
return valueType.typeArguments.isEmpty &&
_typeSystem.isSubtypeOf(constantType, valueType);
if (constantType.isDartCoreInt && valueType.isDartCoreDouble) {
return true;
}
final valueTypeGreatest = PatternGreatestClosureHelper(
topType: _typeSystem.objectQuestion,
bottomType: NeverTypeImpl.instance,
).eliminateToGreatest(valueType);
return _typeSystem.isSubtypeOf(constantType, valueTypeGreatest);
} else if (valueType is TypeParameterTypeImpl) {
final bound = valueType.promotedBound ?? valueType.element.bound;
if (bound != null && !hasTypeParameterReference(bound)) {
return _canBeEqual(constantType, bound);
}
} else if (valueType is FunctionType) {
return false;
}
}
// All other cases are not supported, so no warning.

View file

@ -94,3 +94,36 @@ class LeastGreatestClosureHelper extends ReplacementVisitor {
return super.visitTypeParameterType(type);
}
}
class PatternGreatestClosureHelper extends ReplacementVisitor {
final TypeImpl topType;
final TypeImpl bottomType;
bool _isCovariant = true;
PatternGreatestClosureHelper({
required this.topType,
required this.bottomType,
});
@override
void changeVariance() {
_isCovariant = !_isCovariant;
}
/// Returns a supertype of [type] for all values of type parameters.
DartType eliminateToGreatest(DartType type) {
_isCovariant = true;
return type.accept(this) ?? type;
}
@override
DartType? visitTypeParameterType(TypeParameterType type) {
final replacement = _isCovariant ? topType : bottomType;
return replacement.withNullability(
uniteNullabilities(
replacement.nullabilitySuffix,
type.nullabilitySuffix,
),
);
}
}

View file

@ -182,6 +182,102 @@ class B extends A {
]);
}
test_custom_primitiveEquality_generic_differentElement_constantIsSubtypeOfValue() async {
await assertNoErrorsInCode('''
void f(A<int> x) {
if (x case const B()) {}
}
class A<T> {
const A();
}
class B extends A<int> {
const B();
}
''');
}
test_custom_primitiveEquality_generic_differentElement_constantIsSupertypeOfValue() async {
await assertErrorsInCode('''
void f(B x) {
if (x case const A<int>()) {}
}
class A<T> {
const A();
}
class B extends A<int> {
const B();
}
''', [
error(WarningCode.CONSTANT_PATTERN_NEVER_MATCHES_VALUE_TYPE, 33, 8),
]);
}
test_custom_primitiveEquality_generic_sameElement_constantIsSameTypeAsValue() async {
await assertNoErrorsInCode('''
void f(A<int> x) {
if (x case const A<int>()) {}
}
class A<T> {
const A();
}
''');
}
test_custom_primitiveEquality_generic_sameElement_constantIsSubtypeOfValue() async {
await assertNoErrorsInCode('''
void f(A<num> x) {
if (x case const A<int>()) {}
}
class A<T> {
const A();
}
''');
}
test_custom_primitiveEquality_generic_sameElement_constantIsSupertypeOfValue() async {
await assertErrorsInCode('''
void f(A<int> x) {
if (x case const A<num>()) {}
}
class A<T> {
const A();
}
''', [
error(WarningCode.CONSTANT_PATTERN_NEVER_MATCHES_VALUE_TYPE, 38, 8),
]);
}
test_custom_primitiveEquality_generic_sameElement_typeParameter() async {
await assertNoErrorsInCode('''
void f<T>(A<T> x) {
if (x case const A<int>()) {}
}
class A<T> {
const A();
}
''');
}
test_custom_primitiveEquality_generic_sameElement_typeParameter_contravariant() async {
await assertNoErrorsInCode('''
void f<T>(A<void Function(T)> x) {
if (x case const A<void Function(int)>()) {}
}
class A<T> {
const A();
}
''');
}
test_int_bool() async {
await assertErrorsInCode('''
void f(bool x) {
@ -193,14 +289,24 @@ void f(bool x) {
}
test_int_double() async {
await assertErrorsInCode('''
await assertNoErrorsInCode('''
void f(double x) {
if (x case (zero)) {}
}
const zero = 0;
''');
}
test_int_functionType() async {
await assertErrorsInCode('''
void f(void Function() x) {
if (x case (0)) {}
}
class A {}
''', [
error(WarningCode.CONSTANT_PATTERN_NEVER_MATCHES_VALUE_TYPE, 33, 4),
error(WarningCode.CONSTANT_PATTERN_NEVER_MATCHES_VALUE_TYPE, 42, 1),
]);
}