[analyzer/ffi] `Native` static checks

Bug: https://github.com/dart-lang/sdk/issues/49803
Bug: https://github.com/dart-lang/sdk/issues/50097

Change-Id: Id5b52be88937bcf9245f98e71afa56f079f288f0
Cq-Include-Trybots: luci.dart.try:analyzer-linux-release-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/265085
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index a1c5f19..efea28a 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -1323,7 +1323,8 @@
     final element = this.element;
     return element is ConstructorElement &&
         element.ffiClass != null &&
-        element.enclosingElement.name == 'FfiNative';
+        (element.enclosingElement.name == 'Native' ||
+            element.enclosingElement.name == 'FfiNative');
   }
 
   bool get isPacked {
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
index 7c245f0..af298ba 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
@@ -827,6 +827,23 @@
   const FfiNative(this.nativeName, {this.isLeaf: false});
 }
 
+class Native<T> {
+  final String? symbol;
+  final String? asset;
+  final bool isLeaf;
+
+  const Native({
+    this.asset,
+    this.isLeaf: false,
+    this.symbol,
+  });
+}
+
+class Asset {
+  final String asset;
+  const Asset(this.asset);
+}
+
 class Abi {
   static const androidArm = _androidArm;
   static const androidArm64 = _androidArm64;
diff --git a/pkg/analyzer/test/src/diagnostics/ffi_native_test.dart b/pkg/analyzer/test/src/diagnostics/ffi_native_test.dart
index e3baa70..79cf773 100644
--- a/pkg/analyzer/test/src/diagnostics/ffi_native_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/ffi_native_test.dart
@@ -11,6 +11,7 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(FfiNativeTest);
+    defineReflectiveTests(NativeTest);
   });
 }
 
@@ -182,3 +183,160 @@
     ]);
   }
 }
+
+@reflectiveTest
+class NativeTest extends PubPackageResolutionTest {
+  test_annotation_Native_getters() async {
+    await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+class NativeFieldWrapperClass1 {}
+
+class Paragraph extends NativeFieldWrapperClass1 {
+  @Native<Double Function(Pointer<Void>)>(isLeaf: true)
+  external double get ideographicBaseline;
+
+  @Native<Void Function(Pointer<Void>, Double)>(isLeaf: true)
+  external set ideographicBaseline(double d);
+}
+''');
+  }
+
+  test_annotation_Native_noArguments() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+@Native
+external int foo();
+''', [
+      error(CompileTimeErrorCode.NO_ANNOTATION_CONSTRUCTOR_ARGUMENTS, 20, 7),
+    ]);
+  }
+
+  test_NativeCanUseHandles() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+@Native<Handle Function(Handle)>()
+external Object doesntMatter(Object);
+''', []);
+  }
+
+  test_NativeCanUseLeaf() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+@Native<Int8 Function(Int64)>(isLeaf:true)
+external int doesntMatter(int x);
+''', []);
+  }
+
+  test_NativeInstanceMethodsMustHaveReceiver() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+class K {
+  @Native<Void Function(Double)>()
+  external void doesntMatter(double x);
+}
+''', [
+      error(FfiCode.FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS_WITH_RECEIVER,
+          31, 72),
+    ]);
+  }
+
+  test_NativeLeafMustNotReturnHandle() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+@Native<Handle Function()>(isLeaf:true)
+external Object doesntMatter();
+''', [
+      error(FfiCode.LEAF_CALL_MUST_NOT_RETURN_HANDLE, 19, 71),
+    ]);
+  }
+
+  test_NativeLeafMustNotTakeHandles() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+@Native<Void Function(Handle)>(symbol: 'DoesntMatter', isLeaf:true)
+external void doesntMatter(Object o);
+''', [
+      error(FfiCode.LEAF_CALL_MUST_NOT_TAKE_HANDLE, 19, 105),
+    ]);
+  }
+
+  test_NativeNonFfiParameter() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+@Native<IntPtr Function(int)>()
+external int nonFfiParameter(int v);
+''', [
+      error(FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE, 19, 68),
+    ]);
+  }
+
+  test_NativeNonFfiReturnType() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+@Native<double Function(IntPtr)>()
+external double nonFfiReturnType(int v);
+''', [
+      error(FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE, 19, 75),
+    ]);
+  }
+
+  test_NativePointerParameter() async {
+    await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+@Native<Void Function(Pointer)>()
+external void free(Pointer pointer);
+''');
+  }
+
+  test_NativeTooFewParameters() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+@Native<Void Function(Double)>()
+external void doesntMatter(double x, double y);
+''', [
+      error(FfiCode.FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS, 19, 80),
+    ]);
+  }
+
+  test_NativeTooManyParameters() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+@Native<Void Function(Double, Double)>()
+external void doesntMatter(double x);
+''', [
+      error(FfiCode.FFI_NATIVE_UNEXPECTED_NUMBER_OF_PARAMETERS, 19, 78),
+    ]);
+  }
+
+  test_NativeVoidReturn() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+@Native<Handle Function(Uint32, Uint32, Handle)>()
+external void voidReturn(int width, int height, Object outImage);
+''', [
+      error(FfiCode.MUST_BE_A_SUBTYPE, 19, 116),
+    ]);
+  }
+
+  test_NativeWrongFfiParameter() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+@Native<IntPtr Function(Double)>()
+external int wrongFfiParameter(int v);
+''', [
+      error(FfiCode.MUST_BE_A_SUBTYPE, 19, 73),
+    ]);
+  }
+
+  test_NativeWrongFfiReturnType() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+@Native<IntPtr Function(IntPtr)>()
+external double wrongFfiReturnType(int v);
+''', [
+      error(FfiCode.MUST_BE_A_SUBTYPE, 19, 77),
+    ]);
+  }
+}