Report PATTERN_NEVER_MATCHES_VALUE_TYPE for RecordType(s).

Change-Id: I9ade865ae8cd86f97b41d862c30876a31ee32b51
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/294620
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/type_system.dart b/pkg/analyzer/lib/src/dart/element/type_system.dart
index 0da7997..73c0839 100644
--- a/pkg/analyzer/lib/src/dart/element/type_system.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_system.dart
@@ -238,6 +238,51 @@
       }
     }
 
+    if (left is RecordType) {
+      if (right is FunctionType) {
+        return false;
+      }
+      if (right is InterfaceType) {
+        return right.isDartCoreObject || right.isDartCoreRecord;
+      }
+    }
+
+    if (right is RecordType) {
+      if (left is FunctionType) {
+        return false;
+      }
+      if (left is InterfaceType) {
+        return left.isDartCoreObject || left.isDartCoreRecord;
+      }
+    }
+
+    if (left is RecordType && right is RecordType) {
+      if (left.positionalFields.length != right.positionalFields.length) {
+        return false;
+      }
+      for (var i = 0; i < left.positionalFields.length; i++) {
+        final leftField = left.positionalFields[i];
+        final rightField = right.positionalFields[i];
+        if (!canBeSubtypeOf(leftField.type, rightField.type)) {
+          return false;
+        }
+      }
+
+      if (left.namedFields.length != right.namedFields.length) {
+        return false;
+      }
+      for (var i = 0; i < left.namedFields.length; i++) {
+        final leftField = left.namedFields[i];
+        final rightField = right.namedFields[i];
+        if (leftField.name != rightField.name) {
+          return false;
+        }
+        if (!canBeSubtypeOf(leftField.type, rightField.type)) {
+          return false;
+        }
+      }
+    }
+
     return true;
   }
 
diff --git a/pkg/analyzer/test/src/diagnostics/pattern_never_matches_value_type_test.dart b/pkg/analyzer/test/src/diagnostics/pattern_never_matches_value_type_test.dart
index 3d3fd96..70a4496 100644
--- a/pkg/analyzer/test/src/diagnostics/pattern_never_matches_value_type_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/pattern_never_matches_value_type_test.dart
@@ -49,6 +49,16 @@
 ''');
   }
 
+  test_functionType_recordType() async {
+    await assertErrorsInCode('''
+void f(void Function() x) {
+  if (x case (int,) _) {}
+}
+''', [
+      error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 41, 8),
+    ]);
+  }
+
   test_functionTypeQuestion_interfaceType_object() async {
     await assertNoErrorsInCode('''
 void f(void Function()? x) {
@@ -398,6 +408,34 @@
 ''');
   }
 
+  test_interfaceType_recordType() async {
+    await assertErrorsInCode('''
+void f(A x) {
+  if (x case (A,) _) {}
+}
+
+class A {}
+''', [
+      error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 27, 6),
+    ]);
+  }
+
+  test_interfaceType_recordType_object() async {
+    await assertNoErrorsInCode('''
+void f(Object x) {
+  if (x case (int,) _) {}
+}
+''');
+  }
+
+  test_interfaceType_recordType_record() async {
+    await assertNoErrorsInCode('''
+void f(Record x) {
+  if (x case (int,) _) {}
+}
+''');
+  }
+
   test_matchedEnum_requiredDifferentEnum() async {
     await assertErrorsInCode('''
 void f(A x) {
@@ -564,6 +602,111 @@
     ]);
   }
 
+  test_recordType2_named_differentCount() async {
+    await assertErrorsInCode('''
+void f(({int f1,}) x) {
+  if (x case ({int f1, int f2,}) _) {}
+}
+''', [
+      error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 37, 21),
+    ]);
+  }
+
+  test_recordType2_named_differentNames() async {
+    await assertErrorsInCode('''
+void f(({int a, int b}) x) {
+  if (x case ({int f1, int f2,}) _) {}
+}
+''', [
+      error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 42, 21),
+    ]);
+  }
+
+  test_recordType2_named_unrelated() async {
+    await assertErrorsInCode('''
+void f(({A f1,}) x) {
+  if (x case ({R f1,}) _) {}
+}
+
+final class A {}
+class R {}
+''', [
+      error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 35, 11),
+    ]);
+  }
+
+  test_recordType2_positional_canMatch() async {
+    await assertNoErrorsInCode('''
+void f((A,) x) {
+  if (x case (B,) _) {}
+}
+
+class A {}
+class B {}
+''');
+  }
+
+  test_recordType2_positional_differentCount() async {
+    await assertErrorsInCode('''
+void f((int,) x) {
+  if (x case (int, String) _) {}
+}
+''', [
+      error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 32, 15),
+    ]);
+  }
+
+  test_recordType2_positional_unrelated() async {
+    await assertErrorsInCode('''
+void f((A,) x) {
+  if (x case (R,) _) {}
+}
+
+final class A {}
+class R {}
+''', [
+      error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 30, 6),
+    ]);
+  }
+
+  test_recordType_functionType() async {
+    await assertErrorsInCode('''
+void f((int,) x) {
+  if (x case void Function() _) {}
+}
+''', [
+      error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 32, 17),
+    ]);
+  }
+
+  test_recordType_interfaceType() async {
+    await assertErrorsInCode('''
+void f((A,) x) {
+  if (x case A _) {}
+}
+
+class A {}
+''', [
+      error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 30, 3),
+    ]);
+  }
+
+  test_recordType_interfaceType_object() async {
+    await assertNoErrorsInCode('''
+void f((int,)? x) {
+  if (x case Object _) {}
+}
+''');
+  }
+
+  test_recordType_interfaceType_record() async {
+    await assertNoErrorsInCode('''
+void f((int,)? x) {
+  if (x case Record _) {}
+}
+''');
+  }
+
   test_refutable_pattern_castPattern_match() async {
     await assertNoErrorsInCode('''
 void f(num x) {