[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),
+ ]);
+ }
+}