mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 12:58:05 +00:00
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:
parent
bb85a2d183
commit
db0dfec609
|
@ -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: |-
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 [];
|
||||
|
||||
|
|
|
@ -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,
|
||||
) {
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -128,6 +128,7 @@ void f(bool x) {
|
|||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 19, 6),
|
||||
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 41, 5),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {}
|
||||
''');
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -61,6 +61,7 @@ void f(Object a) {
|
|||
}
|
||||
}
|
||||
''', [
|
||||
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 47, 10),
|
||||
error(WarningCode.UNNECESSARY_QUESTION_MARK, 56, 1),
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue