Issue 51893. Report PATTERN_NEVER_MATCHES_VALUE_TYPE in some cases.

Bug: https://github.com/dart-lang/sdk/issues/51893
Change-Id: Ia7582df3942a077e81f0343e83359c303d152b1b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/292420
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2023-04-03 20:31:45 +00:00 committed by Commit Queue
parent bb85a2d183
commit db0dfec609
18 changed files with 1072 additions and 33 deletions

View file

@ -3038,6 +3038,8 @@ WarningCode.OVERRIDE_ON_NON_OVERRIDING_METHOD:
status: hasFix
WarningCode.OVERRIDE_ON_NON_OVERRIDING_SETTER:
status: hasFix
WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE:
status: noFix
WarningCode.RECEIVER_OF_TYPE_NEVER:
status: needsFix
notes: |-

View file

@ -831,7 +831,7 @@ Object f(Object? x) => switch (x) {
test_createChange_patternVariable_switchStatement_shared() async {
await indexTestUnit('''
void f(Object? x) {
switch (0) {
switch (x) {
case int test when test > 0:
case [int test] when test < 0:
test;
@ -847,7 +847,7 @@ void f(Object? x) {
// validate change
return assertSuccessfulRefactoring('''
void f(Object? x) {
switch (0) {
switch (x) {
case int newName when newName > 0:
case [int newName] when newName < 0:
newName;

View file

@ -1553,11 +1553,19 @@ class CastPatternImpl extends DartPatternImpl implements CastPattern {
SharedMatchContext context,
) {
type.accept(resolverVisitor);
final requiredType = type.typeOrThrow;
resolverVisitor.analyzeCastPattern(
context: context,
pattern: this,
innerPattern: pattern,
requiredType: type.typeOrThrow,
requiredType: requiredType,
);
resolverVisitor.checkPatternNeverMatchesValueType(
context: context,
pattern: this,
requiredType: requiredType,
);
}
@ -3664,10 +3672,15 @@ class DeclaredVariablePatternImpl extends VariablePatternImpl
ResolverVisitor resolverVisitor,
SharedMatchContext context,
) {
declaredElement!.type = resolverVisitor
.analyzeDeclaredVariablePattern(context, this, declaredElement!,
declaredElement!.name, type?.typeOrThrow)
.staticType;
final result = resolverVisitor.analyzeDeclaredVariablePattern(context, this,
declaredElement!, declaredElement!.name, type?.typeOrThrow);
declaredElement!.type = result.staticType;
resolverVisitor.checkPatternNeverMatchesValueType(
context: context,
pattern: this,
requiredType: result.staticType,
);
}
@override
@ -10004,11 +10017,17 @@ class ObjectPatternImpl extends DartPatternImpl implements ObjectPattern {
ResolverVisitor resolverVisitor,
SharedMatchContext context,
) {
resolverVisitor.analyzeObjectPattern(
final result = resolverVisitor.analyzeObjectPattern(
context,
this,
fields: resolverVisitor.buildSharedPatternFields(fields),
);
resolverVisitor.checkPatternNeverMatchesValueType(
context: context,
pattern: this,
requiredType: result.requiredType,
);
}
@override
@ -14297,11 +14316,20 @@ class WildcardPatternImpl extends DartPatternImpl implements WildcardPattern {
ResolverVisitor resolverVisitor,
SharedMatchContext context,
) {
final declaredType = type?.typeOrThrow;
resolverVisitor.analyzeWildcardPattern(
context: context,
node: this,
declaredType: type?.typeOrThrow,
declaredType: declaredType,
);
if (declaredType != null) {
resolverVisitor.checkPatternNeverMatchesValueType(
context: context,
pattern: this,
requiredType: declaredType,
);
}
}
@override

View file

@ -827,6 +827,24 @@ class ClassElementImpl extends ClassOrMixinElementImpl implements ClassElement {
super.methods = methods;
}
/// If the class is final, returns all its subtypes.
/// All these subtypes can be only in the same library.
List<InterfaceType>? get subtypesOfFinal {
if (isFinal) {
final result = <InterfaceType>[];
for (final element in library.topLevelElements) {
if (element is InterfaceElement && element != this) {
final elementThis = element.thisType;
if (elementThis.asInstanceOf(this) != null) {
result.add(elementThis);
}
}
}
return result;
}
return null;
}
@override
List<InterfaceType> get superclassConstraints => const [];

View file

@ -139,6 +139,108 @@ class TypeSystemImpl implements TypeSystem {
return ft.parameters.any((p) => predicate(p.type));
}
/// Checks if an instance of [left] could possibly also be an instance of
/// [right]. For example, an instance of `num` could be `int`, so
/// canBeSubtypeOf(`num`, `int`) would return `true`, even though `num` is
/// not a subtype of `int`. More generally, we check if there could be a
/// type that implements both [left] and [right], regardless of whether
/// [left] is a subtype of [right], or [right] is a subtype of [left].
bool canBeSubtypeOf(DartType left, DartType right) {
// If one is `Null`, then the other must be nullable.
final leftIsNullable = isPotentiallyNullable(left);
final rightIsNullable = isPotentiallyNullable(right);
if (left.isDartCoreNull) {
return rightIsNullable;
} else if (right.isDartCoreNull) {
return leftIsNullable;
}
// If none is `Null`, but both are nullable, they match at `Null`.
if (leftIsNullable && rightIsNullable) {
return true;
}
// Could be `void Function() vs. Object`.
// Could be `void Function() vs. Function`.
if (left is FunctionTypeImpl && right is InterfaceTypeImpl) {
return right.isDartCoreFunction || right.isDartCoreObject;
}
// Could be `Object vs. void Function()`.
// Could be `Function vs. void Function()`.
if (left is InterfaceTypeImpl && right is FunctionTypeImpl) {
return left.isDartCoreFunction || left.isDartCoreObject;
}
if (left is InterfaceTypeImpl && right is InterfaceTypeImpl) {
final leftElement = left.element;
final rightElement = right.element;
bool canBeSubtypeOfInterfaces(InterfaceType left, InterfaceType right) {
assert(left.element == right.element);
final leftArguments = left.typeArguments;
final rightArguments = right.typeArguments;
assert(leftArguments.length == rightArguments.length);
for (var i = 0; i < leftArguments.length; i++) {
if (!canBeSubtypeOf(leftArguments[i], rightArguments[i])) {
return false;
}
}
return true;
}
// If the left is enum, we know types of all its instances.
if (leftElement is EnumElementImpl) {
for (final constant in leftElement.constants) {
final constantType = constant.type;
if (isSubtypeOf(constantType, right)) {
return true;
}
}
return false;
}
if (leftElement == rightElement) {
return canBeSubtypeOfInterfaces(left, right);
}
if (leftElement is ClassElementImpl) {
// If the left is final, we know all subtypes, and can check them.
final finalSubtypes = leftElement.subtypesOfFinal;
if (finalSubtypes != null) {
for (final candidate in [left, ...finalSubtypes]) {
final asRight = candidate.asInstanceOf(rightElement);
if (asRight != null) {
if (_canBeEqualArguments(asRight, right)) {
return true;
}
}
}
return false;
}
}
if (rightElement is ClassElementImpl) {
// If the right is final, then it or one of its subtypes must
// implement the left.
final finalSubtypes = rightElement.subtypesOfFinal;
if (finalSubtypes != null) {
for (final candidate in [right, ...finalSubtypes]) {
final asLeft = candidate.asInstanceOf(leftElement);
if (asLeft != null) {
if (canBeSubtypeOfInterfaces(left, asLeft)) {
return true;
}
}
}
return false;
}
}
}
return true;
}
/// Returns [type] in which all promoted type variables have been replaced
/// with their unpromoted equivalents, and, if non-nullable by default,
/// replaces all legacy types with their non-nullable equivalents.
@ -1711,6 +1813,34 @@ class TypeSystemImpl implements TypeSystem {
this.strictInference = strictInference;
}
/// Optimistically estimates, if type arguments of [left] can be equal to
/// the type arguments of [right]. Both types must be instantiations of the
/// same element.
bool _canBeEqualArguments(InterfaceType left, InterfaceType right) {
assert(left.element == right.element);
final leftArguments = left.typeArguments;
final rightArguments = right.typeArguments;
assert(leftArguments.length == rightArguments.length);
for (var i = 0; i < leftArguments.length; i++) {
final leftArgument = leftArguments[i];
final rightArgument = rightArguments[i];
if (!_canBeEqualTo(leftArgument, rightArgument)) {
return false;
}
}
return true;
}
/// Optimistically estimates, if [left] can be equal to [right].
bool _canBeEqualTo(DartType left, DartType right) {
if (left is InterfaceType && right is InterfaceType) {
if (left.element != right.element) {
return false;
}
}
return true;
}
List<DartType> _defaultTypeArguments(
List<TypeParameterElement> typeParameters,
) {

View file

@ -30,10 +30,15 @@ class ListPatternResolver {
}
}
node.requiredType = resolverVisitor
.analyzeListPattern(context, node,
elementType: typeArguments?.arguments.first.typeOrThrow,
elements: node.elements)
.requiredType;
final result = resolverVisitor.analyzeListPattern(context, node,
elementType: typeArguments?.arguments.first.typeOrThrow,
elements: node.elements);
node.requiredType = result.requiredType;
resolverVisitor.checkPatternNeverMatchesValueType(
context: context,
pattern: node,
requiredType: result.requiredType,
);
}
}

View file

@ -6666,6 +6666,15 @@ class WarningCode extends AnalyzerErrorCode {
uniqueName: 'OVERRIDE_ON_NON_OVERRIDING_SETTER',
);
/// Parameters:
/// 0: the matched value type
/// 1: the required pattern type
static const WarningCode PATTERN_NEVER_MATCHES_VALUE_TYPE = WarningCode(
'PATTERN_NEVER_MATCHES_VALUE_TYPE',
"The matched value type '{0}' can never match the required type '{1}'.",
correctionMessage: "Try using a different pattern.",
);
/// It is not an error to call or tear-off a method, setter, or getter, or to
/// read or write a field, on a receiver of static type `Never`.
/// Implementations that provide feedback about dead or unreachable code are

View file

@ -971,6 +971,7 @@ const List<ErrorCode> errorCodeValues = [
WarningCode.OVERRIDE_ON_NON_OVERRIDING_GETTER,
WarningCode.OVERRIDE_ON_NON_OVERRIDING_METHOD,
WarningCode.OVERRIDE_ON_NON_OVERRIDING_SETTER,
WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE,
WarningCode.RECEIVER_OF_TYPE_NEVER,
WarningCode.RECORD_LITERAL_ONE_POSITIONAL_NO_TRAILING_COMMA,
WarningCode.REMOVED_LINT_USE,

View file

@ -657,6 +657,25 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
assert(_rewriteStack.isEmpty);
}
/// Reports an error if the [pattern] with the [requiredType] cannot
/// match the [DartPatternImpl.matchedValueType].
void checkPatternNeverMatchesValueType({
required SharedMatchContext context,
required DartPatternImpl pattern,
required DartType requiredType,
}) {
if (context.irrefutableContext == null) {
final matchedType = pattern.matchedValueType!;
if (!typeSystem.canBeSubtypeOf(matchedType, requiredType)) {
errorReporter.reportErrorForNode(
WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE,
pattern,
[matchedType, requiredType],
);
}
}
}
void checkReadOfNotAssignedLocalVariable(
SimpleIdentifier node,
Element? element,
@ -1562,12 +1581,19 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
}
}
node.requiredType = analyzeMapPattern(
final result = analyzeMapPattern(
context,
node,
typeArguments: typeArguments,
elements: node.elements,
).requiredType;
);
node.requiredType = result.requiredType;
checkPatternNeverMatchesValueType(
context: context,
pattern: node,
requiredType: result.requiredType,
);
}
@override

View file

@ -291,8 +291,7 @@ class ArgumentError extends Error {
static T checkNotNull<T>(T argument, [String, name]) => argument;
}
// In the SDK this is an abstract class.
class BigInt implements Comparable<BigInt> {
abstract final class BigInt implements Comparable<BigInt> {
int compareTo(BigInt other) => 0;
static BigInt parse(String source, {int? radix}) => throw 0;
}

View file

@ -3940,7 +3940,7 @@ CompileTimeErrorCode:
```dart
void f(Object o) {
switch (0) {
switch (o) {
case C(f: 1, [!f!]: 2):
return;
}
@ -23671,6 +23671,13 @@ WarningCode:
A setter with the override annotation does not override an existing setter.
No parameters.
PATTERN_NEVER_MATCHES_VALUE_TYPE:
problemMessage: "The matched value type '{0}' can never match the required type '{1}'."
correctionMessage: "Try using a different pattern."
comment: |-
Parameters:
0: the matched value type
1: the required pattern type
RECEIVER_OF_TYPE_NEVER:
problemMessage: "The receiver is of type 'Never', and will never complete with a value."
correctionMessage: Try checking for throw expressions or type errors in the receiver

View file

@ -2,7 +2,7 @@
// 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/dart/error/hint_codes.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'context_collection_resolution.dart';
@ -16,11 +16,13 @@ main() {
@reflectiveTest
class LogicalAndPatternResolutionTest extends PubPackageResolutionTest {
test_ifCase() async {
await assertNoErrorsInCode(r'''
await assertErrorsInCode(r'''
void f(x) {
if (x case int _ && double _) {}
}
''');
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 34, 8),
]);
final node = findNode.singleGuardedPattern.pattern;
assertResolvedNodeText(node, r'''
LogicalAndPattern
@ -48,14 +50,16 @@ LogicalAndPattern
}
test_switchCase() async {
await assertNoErrorsInCode(r'''
await assertErrorsInCode(r'''
void f(x) {
switch (x) {
case int _ && double _:
break;
}
}
''');
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 45, 8),
]);
final node = findNode.singleGuardedPattern.pattern;
assertResolvedNodeText(node, r'''
LogicalAndPattern

View file

@ -87,7 +87,9 @@ SwitchExpression
test_location_topLevel() async {
await assertNoErrorsInCode(r'''
final a = switch (null) {
num a = 0;
final b = switch (a) {
int(:var isEven) when isEven => 1,
_ => 0,
};
@ -98,9 +100,10 @@ final a = switch (null) {
SwitchExpression
switchKeyword: switch
leftParenthesis: (
expression: NullLiteral
literal: null
staticType: Null
expression: SimpleIdentifier
token: a
staticElement: self::@getter::a
staticType: num
rightParenthesis: )
leftBracket: {
cases
@ -121,17 +124,17 @@ SwitchExpression
pattern: DeclaredVariablePattern
keyword: var
name: isEven
declaredElement: hasImplicitType isEven@37
declaredElement: hasImplicitType isEven@46
type: bool
matchedValueType: bool
element: dart:core::@class::int::@getter::isEven
rightParenthesis: )
matchedValueType: Null
matchedValueType: num
whenClause: WhenClause
whenKeyword: when
expression: SimpleIdentifier
token: isEven
staticElement: isEven@37
staticElement: isEven@46
staticType: bool
arrow: =>
expression: IntegerLiteral
@ -141,7 +144,7 @@ SwitchExpression
guardedPattern: GuardedPattern
pattern: WildcardPattern
name: _
matchedValueType: Null
matchedValueType: num
arrow: =>
expression: IntegerLiteral
literal: 0

View file

@ -128,6 +128,7 @@ void f(bool x) {
}
''', [
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 19, 6),
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 41, 5),
]);
}

View file

@ -0,0 +1,802 @@
// 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(PatternNeverMatchesValueTypeTest);
});
}
@reflectiveTest
class PatternNeverMatchesValueTypeTest extends PubPackageResolutionTest {
test_functionType_interfaceType() async {
await assertErrorsInCode('''
void f(void Function() x) {
if (x case int _) {}
}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 41, 5),
]);
}
test_functionType_interfaceType_function() async {
await assertNoErrorsInCode('''
void f(void Function() x) {
if (x case Function _) {}
}
''');
}
test_functionType_interfaceType_object() async {
await assertNoErrorsInCode('''
void f(void Function() x) {
if (x case Object _) {}
}
''');
}
test_functionType_interfaceType_objectQuestion() async {
await assertNoErrorsInCode('''
void f(void Function() x) {
if (x case Object? _) {}
}
''');
}
test_functionTypeQuestion_interfaceType_object() async {
await assertNoErrorsInCode('''
void f(void Function()? x) {
if (x case Object _) {}
}
''');
}
test_functionTypeQuestion_interfaceType_objectQuestion() async {
await assertNoErrorsInCode('''
void f(void Function()? x) {
if (x case Object? _) {}
}
''');
}
test_interfaceType2_generic_argumentsMatch() async {
await assertNoErrorsInCode('''
void f(List<A> x) {
if (x case List<B> _) {}
}
final class A {}
final class B extends A {}
''');
}
test_interfaceType2_generic_argumentsNotMatch() async {
await assertErrorsInCode('''
void f(List<A> x) {
if (x case List<B> _) {}
}
final class A {}
final class B {}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 33, 9),
]);
}
test_interfaceType2_matchedFinal_hasSubtypes_noneImplementsRequired() async {
await assertErrorsInCode('''
void f(A x) {
if (x case R _) {}
}
final class A {}
final class A2 extends A {}
final class A3 implements A {}
class R {}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 27, 3),
]);
}
test_interfaceType2_matchedFinal_hasSubtypes_oneExtendsRequired() async {
await assertNoErrorsInCode('''
void f(A x) {
if (x case R _) {}
}
final class A {}
final class A2 extends R implements A {}
class R {}
''');
}
test_interfaceType2_matchedFinal_hasSubtypes_oneImplementsRequired() async {
await assertNoErrorsInCode('''
void f(A x) {
if (x case R _) {}
}
final class A {}
final class A2 extends A implements R {}
class R {}
''');
}
test_interfaceType2_matchedFinal_hasSubtypes_oneImplementsRequired2() async {
await assertNoErrorsInCode('''
void f(A x) {
if (x case R _) {}
}
final class A {}
final class A2 extends A {}
final class A3 extends A2 implements R {}
class R {}
''');
}
test_interfaceType2_matchedFinal_hasSubtypes_oneMixesRequired() async {
await assertNoErrorsInCode('''
void f(A x) {
if (x case R _) {}
}
final class A {}
final class A2 extends A with R {}
mixin class R {}
''');
}
test_interfaceType2_matchedFinal_it_implementsGenericRequired_isGeneric_argumentCouldMatch() async {
await assertNoErrorsInCode('''
void f<T>(A<T> x) {
if (x case R<int> _) {}
}
final class A<T> extends R<T> {}
class R<T> {}
''');
}
test_interfaceType2_matchedFinal_it_implementsGenericRequired_notGeneric_differentArguments() async {
await assertErrorsInCode('''
void f(A x) {
if (x case R<int> _) {}
}
final class A extends R<num> {}
class R<T> {}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 27, 8),
]);
}
test_interfaceType2_matchedFinal_itImplementsRequired() async {
await assertNoErrorsInCode('''
void f(A x) {
if (x case R _) {}
}
final class A implements R {}
class R {}
''');
}
test_interfaceType2_matchedFinal_itMixesRequired() async {
await assertNoErrorsInCode('''
void f(A x) {
if (x case R _) {}
}
final class A extends Object with R {}
mixin class R {}
''');
}
test_interfaceType2_matchedFinal_requiredUnrelated() async {
await assertErrorsInCode('''
void f(A x) {
if (x case R _) {}
}
final class A {}
class R {}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 27, 3),
]);
}
test_interfaceType2_matchedObject_requiredClass() async {
await assertNoErrorsInCode('''
void f(Object x) {
if (x case A _) {}
}
class A {}
''');
}
test_interfaceType2_matchedObject_requiredFinalClass() async {
await assertNoErrorsInCode('''
void f(Object x) {
if (x case int _) {}
}
''');
}
test_interfaceType2_matchedObjectQuestion_requiredClass() async {
await assertNoErrorsInCode('''
void f(Object? x) {
if (x case A _) {}
}
class A {}
''');
}
test_interfaceType2_matchedObjectQuestion_requiredFinalClass() async {
await assertNoErrorsInCode('''
void f(Object? x) {
if (x case int _) {}
}
''');
}
test_interfaceType2_requiredFinal_matchedSelf() async {
await assertNoErrorsInCode('''
void f(A x) {
if (x case A _) {}
}
final class A {}
''');
}
test_interfaceType2_requiredFinal_matchedSelf_generic_argumentsMatch() async {
await assertNoErrorsInCode('''
void f(A<B> x) {
if (x case A<C> _) {}
}
final class A<T> {}
final class B {}
final class C extends B {}
''');
}
test_interfaceType2_requiredFinal_matchedSelf_generic_argumentsNotMatch() async {
await assertErrorsInCode('''
void f(A<B> x) {
if (x case A<C> _) {}
}
final class A<T> {}
final class B {}
final class C {}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 30, 6),
]);
}
test_interfaceType2_requiredFinal_matchedSubtype() async {
await assertNoErrorsInCode('''
void f(B x) {
if (x case A _) {}
}
final class A {}
final class B extends A {}
''');
}
test_interfaceType2_requiredFinal_matchedSubtype_generic_argumentsNotMatch() async {
await assertErrorsInCode('''
void f(B x) {
if (x case A<D> _) {}
}
final class A<T> {}
final class B extends A<C> {}
final class C {}
final class D {}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 27, 6),
]);
}
test_interfaceType2_requiredFinal_matchedSupertype() async {
await assertNoErrorsInCode('''
void f(A x) {
if (x case B _) {}
}
class A {}
final class B extends A {}
''');
}
test_interfaceType2_requiredFinal_matchedSupertype_generic_argumentsMatch() async {
await assertNoErrorsInCode('''
void f(A<C> x) {
if (x case B _) {}
}
class A<T> {}
final class B extends A<C> {}
final class C {}
''');
}
test_interfaceType2_requiredFinal_matchedSupertype_generic_argumentsNotMatch() async {
await assertErrorsInCode('''
void f(A<D> x) {
if (x case B _) {}
}
class A<T> {}
final class B extends A<C> {}
final class C {}
final class D {}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 30, 3),
]);
}
test_interfaceType2_requiredFinal_matchedSupertype_generic_differentArguments() async {
await assertNoErrorsInCode('''
void f(A<num> x) {
if (x case B _) {}
}
class A<T> {}
final class B extends A<int> {}
''');
}
test_interfaceType2_requiredFinal_matchedUnrelated() async {
await assertErrorsInCode('''
void f(A x) {
if (x case B _) {}
}
class A {}
final class B {}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 27, 3),
]);
}
test_interfaceType_functionType() async {
await assertErrorsInCode('''
void f(int x) {
if (x case void Function() _) {}
}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 29, 17),
]);
}
test_interfaceType_functionType_function() async {
await assertNoErrorsInCode('''
void f(Function x) {
if (x case void Function() _) {}
}
''');
}
test_interfaceType_functionType_object() async {
await assertNoErrorsInCode('''
void f(Object x) {
if (x case void Function() _) {}
}
''');
}
test_matchedEnum_requiredDifferentEnum() async {
await assertErrorsInCode('''
void f(A x) {
if (x case R _) {}
}
enum A { v }
enum R { v }
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 27, 3),
]);
}
test_matchedEnum_requiredNotEnum() async {
await assertErrorsInCode('''
void f(A x) {
if (x case R _) {}
}
enum A { v }
class R {}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 27, 3),
]);
}
test_matchedEnum_requiredNotEnum_implemented() async {
await assertNoErrorsInCode('''
void f(A x) {
if (x case R _) {}
}
enum A implements R { v }
class R {}
''');
}
test_matchedEnum_requiredNotEnum_implemented_generic_rightTypeArguments() async {
await assertNoErrorsInCode('''
void f(A x) {
if (x case R<num> _) {}
}
enum A implements R<int> { v }
class R<T> {}
''');
}
test_matchedEnum_requiredNotEnum_implemented_generic_rightTypeArguments2() async {
await assertNoErrorsInCode('''
void f(A x) {
if (x case R<num> _) {}
}
enum A<T> implements R<T> {
v1<String>(),
v2<int>(),
}
class R<T> {}
''');
}
test_matchedEnum_requiredNotEnum_implemented_generic_wrongTypeArguments() async {
await assertErrorsInCode('''
void f(A x) {
if (x case R<int> _) {}
}
enum A implements R<num> { v }
class R<T> {}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 27, 8),
]);
}
test_matchedEnum_requiredNotEnum_implemented_generic_wrongTypeArguments2() async {
await assertErrorsInCode('''
void f(A x) {
if (x case R<String> _) {}
}
enum A<T> implements R<T> {
v1<int>(),
v2<double>(),
}
class R<T> {}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 27, 11),
]);
}
test_matchedEnum_requiredNotEnum_mixed() async {
await assertNoErrorsInCode('''
void f(A x) {
if (x case R _) {}
}
enum A with R { v }
mixin R {}
''');
}
test_matchedEnum_requiredSameEnum() async {
await assertNoErrorsInCode('''
void f(E? x) {
if (x case E _) {}
}
enum E { v }
''');
}
test_matchedEnum_requiredSameEnum_generic_hasValue() async {
await assertNoErrorsInCode('''
void f<T>(E<T>? x) {
if (x case E<num> _) {}
}
enum E<T> { v<int>() }
''');
}
test_matchedEnum_requiredSameEnum_generic_noValue() async {
await assertErrorsInCode('''
void f<T>(E<T>? x) {
if (x case E<String> _) {}
}
enum E<T> { v1<int>(), v2<double>() }
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 34, 11),
]);
}
test_matchedNull_requiredNotNullable() async {
await assertErrorsInCode('''
void f(Null x) {
if (x case A _) {}
}
class A {}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 30, 3),
]);
}
test_matchedNull_requiredNull() async {
await assertNoErrorsInCode('''
void f(Null x) {
if (x case Null _) {}
}
''');
}
test_matchedNull_requiredObject() async {
await assertErrorsInCode('''
void f(Null x) {
if (x case Object _) {}
}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 30, 8),
]);
}
test_refutable_pattern_castPattern_match() async {
await assertNoErrorsInCode('''
void f(num x) {
if (x case _ as int) {}
}
''');
}
test_refutable_pattern_castPattern_notMatch() async {
await assertErrorsInCode('''
void f(String x) {
if (x case _ as int) {}
}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 32, 8),
]);
}
test_refutable_pattern_declaredVariablePattern_match() async {
await assertErrorsInCode('''
void f(num x) {
if (x case int a) {}
}
''', [
error(WarningCode.UNUSED_LOCAL_VARIABLE, 33, 1),
]);
}
test_refutable_pattern_declaredVariablePattern_notMatch() async {
await assertErrorsInCode('''
void f(String x) {
if (x case int a) {}
}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 32, 5),
error(WarningCode.UNUSED_LOCAL_VARIABLE, 36, 1),
]);
}
test_refutable_pattern_listPattern_match() async {
await assertNoErrorsInCode('''
void f(List<num> x) {
if (x case <int>[]) {}
}
''');
}
test_refutable_pattern_listPattern_notMatch() async {
await assertErrorsInCode('''
void f(int x) {
if (x case <int>[]) {}
}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 29, 7),
]);
}
test_refutable_pattern_mapPattern_match() async {
await assertNoErrorsInCode('''
void f(Object? x) {
if (x case <int, String>{0: _}) {}
}
''');
}
test_refutable_pattern_mapPattern_notMatch() async {
await assertErrorsInCode('''
void f(int x) {
if (x case <int, String>{0: _}) {}
}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 29, 19),
]);
}
test_refutable_pattern_objectPattern_match() async {
await assertNoErrorsInCode('''
void f(num x) {
if (x case int()) {}
}
''');
}
test_refutable_pattern_objectPattern_notMatch() async {
await assertErrorsInCode('''
void f(String x) {
if (x case int()) {}
}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 32, 5),
]);
}
test_refutable_pattern_wildcard_match() async {
await assertNoErrorsInCode('''
void f(num x) {
if (x case int _) {}
}
''');
}
test_refutable_pattern_wildcard_notMatch() async {
await assertErrorsInCode('''
void f(String x) {
if (x case int _) {}
}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 32, 5),
]);
}
test_requiredNull_matchedNotNullable() async {
await assertErrorsInCode('''
void f(A x) {
if (x case Null _) {}
}
class A {}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 27, 6),
]);
}
test_requiredNull_matchedNotNullable_functionType() async {
await assertErrorsInCode('''
void f(void Function() x) {
if (x case Null _) {}
}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 41, 6),
]);
}
test_requiredNull_matchedNotNullable_interfaceType_object() async {
await assertErrorsInCode('''
void f(Object x) {
if (x case Null _) {}
}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 32, 6),
]);
}
test_requiredNull_matchedNotNullable_typeParameterType() async {
await assertErrorsInCode('''
void f<T extends num>(T x) {
if (x case Null _) {}
}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 42, 6),
]);
}
test_requiredNull_matchedNullable_dynamicType() async {
await assertNoErrorsInCode('''
void f(dynamic x) {
if (x case Null _) {}
}
''');
}
test_requiredNull_matchedNullable_functionType() async {
await assertNoErrorsInCode('''
void f(void Function()? x) {
if (x case Null _) {}
}
''');
}
test_requiredNull_matchedNullable_interfaceType() async {
await assertNoErrorsInCode('''
void f(A? x) {
if (x case Null _) {}
}
class A {}
''');
}
test_requiredNull_matchedNullable_typeParameterType_implicitBound() async {
await assertNoErrorsInCode('''
void f<T>(T x) {
if (x case Null _) {}
}
''');
}
/// TODO(scheglov) We should report that `B?` should be replaced with `B`.
test_requiredNullable_matchedNotNullable_match() async {
await assertNoErrorsInCode('''
void f(A x) {
if (x case B? _) {}
}
final class A {}
final class B extends A {}
''');
}
/// Check that nullable does not prevent reporting the warning.
test_requiredNullable_matchedNotNullable_notMatch() async {
await assertErrorsInCode('''
void f(A x) {
if (x case B? _) {}
}
final class A {}
final class B {}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 27, 4),
]);
}
test_requiredNullable_matchedNull() async {
await assertNoErrorsInCode('''
void f(Null x) {
if (x case A? _) {}
}
final class A {}
''');
}
/// They match only because both can be `Null`.
/// Otherwise, two unrelated final classes cannot match.
test_requiredNullable_matchedNullable() async {
await assertNoErrorsInCode('''
void f(A? x) {
if (x case B? _) {}
}
final class A {}
final class B {}
''');
}
}

View file

@ -637,6 +637,8 @@ import 'part_of_different_library_test.dart' as part_of_different_library;
import 'part_of_non_part_test.dart' as part_of_non_part;
import 'pattern_assignment_not_local_variable_test.dart'
as pattern_assignment_not_local_variable;
import 'pattern_never_matches_value_type_test.dart'
as pattern_never_matches_value_type;
import 'pattern_type_mismatch_in_irrefutable_context_test.dart'
as pattern_type_mismatch_in_irrefutable_context;
import 'pattern_variable_assignment_inside_guard_test.dart'
@ -1292,6 +1294,7 @@ main() {
part_of_different_library.main();
part_of_non_part.main();
pattern_assignment_not_local_variable.main();
pattern_never_matches_value_type.main();
pattern_type_mismatch_in_irrefutable_context.main();
pattern_variable_assignment_inside_guard.main();
positional_super_formal_parameter_with_positional_argument.main();

View file

@ -61,6 +61,7 @@ void f(Object a) {
}
}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 47, 10),
error(WarningCode.UNNECESSARY_QUESTION_MARK, 56, 1),
]);
}

View file

@ -4980,7 +4980,7 @@ matched twice in the same object pattern:
{% prettify dart tag=pre+code %}
void f(Object o) {
switch (0) {
switch (o) {
case C(f: 1, [!f!]: 2):
return;
}