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