Skip to content
This repository has been archived by the owner on Nov 20, 2024. It is now read-only.

Report type_literal_in_constant_pattern more often. #4358

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 18 additions & 19 deletions lib/src/rules/type_literal_in_constant_pattern.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,30 @@ import '../analyzer.dart';
const _desc = r"Don't use constant patterns with type literals.";

const _details = r'''
Use `== TypeName` or `TypeName _` instead of type literals in patterns.
If you meant to test if the object has type `Foo`, instead write `Foo _`.

**BAD:**
```dart
void f(Type x) {
if (x case int) {
print('int');
void f(Object? x) {
if (x case num) {
print('int or double');
}
}
```

**GOOD:**
```dart
void f(Type x) {
if (x case == int) {
print('int');
void f(Object? x) {
if (x case num _) {
print('int or double');
}
}
```

If you do mean to test that the matched value (which you expect to have the
type `Type`) is equal to the type literal `Foo`, then this lint can be
silenced using `const (Foo)`.

**BAD:**
```dart
void f(Object? x) {
Expand All @@ -42,12 +46,11 @@ void f(Object? x) {
**GOOD:**
```dart
void f(Object? x) {
if (x case int _) {
if (x case const (int)) {
print('int');
}
}
```

''';

class TypeLiteralInConstantPattern extends LintRule {
Expand Down Expand Up @@ -86,18 +89,14 @@ class _Visitor extends SimpleAstVisitor {

@override
visitConstantPattern(ConstantPattern node) {
// `const (MyType)` is fine.
if (node.constKeyword != null) {
return;
}

var expressionType = node.expression.staticType;
if (expressionType != null && expressionType.isDartCoreType) {
var matchedValueType = node.matchedValueType;
if (matchedValueType != null) {
var typeSystem = context.typeSystem;
matchedValueType = typeSystem.resolveToBound(matchedValueType);
if (!matchedValueType.isDartCoreType) {
if (typeSystem.isSubtypeOf(expressionType, matchedValueType)) {
rule.reportLint(node);
}
}
}
rule.reportLint(node);
}
}
}
28 changes: 12 additions & 16 deletions test/rules/type_literal_in_constant_pattern_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,6 @@ void f(dynamic x) {
]);
}

test_constType_matchNum() async {
await assertDiagnostics(r'''
void f(num x) {
if (x case int) {}
}
''', [
error(WarningCode.CONSTANT_PATTERN_NEVER_MATCHES_VALUE_TYPE, 29, 3),
]);
}

test_constType_matchObject() async {
await assertDiagnostics(r'''
void f(Object x) {
Expand All @@ -77,11 +67,13 @@ void f(Object? x) {
}

test_constType_matchType() async {
await assertNoDiagnostics(r'''
await assertDiagnostics(r'''
void f(Type x) {
if (x case int) {}
}
''');
''', [
lint(30, 3),
]);
}

test_constType_matchType_explicitConst() async {
Expand All @@ -93,7 +85,7 @@ void f(Type x) {
}

test_constType_matchType_nested() async {
await assertNoDiagnostics(r'''
await assertDiagnostics(r'''
void f(A x) {
if (x case A(type: int)) {}
}
Expand All @@ -102,7 +94,9 @@ class A {
final Type type;
A(this.type);
}
''');
''', [
lint(35, 3),
]);
}

test_constType_matchTypeParameter_boundObjectNullable() async {
Expand All @@ -117,10 +111,12 @@ void f<T extends Object?>(T x) {

/// Nobody will write such code, but just in case.
test_constType_matchTypeParameter_boundType() async {
await assertNoDiagnostics(r'''
await assertDiagnostics(r'''
void f<T extends Type>(T x) {
if (x case int) {}
}
''');
''', [
lint(43, 3),
]);
}
}