From 7272aadca415ef9688967ee1ebec53cd0734b524 Mon Sep 17 00:00:00 2001 From: Brian Wilkerson Date: Mon, 24 Jan 2022 20:02:27 +0000 Subject: [PATCH] Add diagnostic documentation for most of the ffi diagnostics Change-Id: I37ba1852dea9c7e53966050eafaed9bb61cffbb8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/229580 Reviewed-by: Konstantin Shcheglov Commit-Queue: Brian Wilkerson --- .../lib/src/dart/error/ffi_code.g.dart | 1316 ++++++++++++++- .../lib/src/test_utilities/mock_packages.dart | 27 + .../lib/src/test_utilities/mock_sdk.dart | 2 + pkg/analyzer/messages.yaml | 1332 ++++++++++++++- .../context_collection_resolution.dart | 9 + .../test/verify_diagnostics_test.dart | 2 +- pkg/analyzer/tool/diagnostics/diagnostics.md | 1452 +++++++++++++++++ 7 files changed, 4105 insertions(+), 35 deletions(-) diff --git a/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart b/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart index fc3a932b780..873531ab8f0 100644 --- a/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart +++ b/pkg/analyzer/lib/src/dart/error/ffi_code.g.dart @@ -64,9 +64,43 @@ class FfiCode extends AnalyzerErrorCode { /** * No parameters. */ + // #### Description + // + // The analyzer produces this diagnostic when a field that's declared in a + // subclass of `Struct` and has the type `Pointer` also has an annotation + // associated with it. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the field `p`, which + // has the type `Pointer` and is declared in a subclass of `Struct`, has the + // annotation `@Double()`: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // [!@Double()!] + // external Pointer p; + // } + // ``` + // + // #### Common fixes + // + // Remove the annotations from the field: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // external Pointer p; + // } + // ``` static const FfiCode ANNOTATION_ON_POINTER_FIELD = FfiCode( 'ANNOTATION_ON_POINTER_FIELD', - "Fields in a struct class whose type is 'Pointer' should not have any " + "Fields in a struct class whose type is 'Pointer' shouldn't have any " "annotations.", correctionMessage: "Try removing the annotation.", ); @@ -75,6 +109,56 @@ class FfiCode extends AnalyzerErrorCode { * Parameters: * 0: the name of the argument */ + // #### Description + // + // The analyzer produces this diagnostic when an invocation of either + // `Pointer.asFunction` or `DynamicLibrary.lookupFunction` has an `isLeaf` + // argument whose value isn't a constant expression. + // + // The analyzer also produces this diagnostic when the value of the + // `exceptionalReturn` argument of `Pointer.fromFunction`. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the value of the + // `isLeaf` argument is a parameter, and hence isn't a constant: + // + // ```dart + // import 'dart:ffi'; + // + // int Function(int) fromPointer( + // Pointer> p, bool isLeaf) { + // return p.asFunction(isLeaf: [!isLeaf!]); + // } + // ``` + // + // #### Common fixes + // + // If there's a suitable constant that can be used, then replace the argument + // with a constant: + // + // ```dart + // import 'dart:ffi'; + // + // const isLeaf = false; + // + // int Function(int) fromPointer(Pointer> p) { + // return p.asFunction(isLeaf: isLeaf); + // } + // ``` + // + // If there isn't a suitable constant, then replace the argument with a + // boolean literal: + // + // ```dart + // import 'dart:ffi'; + // + // int Function(int) fromPointer(Pointer> p) { + // return p.asFunction(isLeaf: true); + // } + // ``` static const FfiCode ARGUMENT_MUST_BE_A_CONSTANT = FfiCode( 'ARGUMENT_MUST_BE_A_CONSTANT', "Argument '{0}' must be a constant.", @@ -84,6 +168,52 @@ class FfiCode extends AnalyzerErrorCode { /** * No parameters. */ + // #### Description + // + // The analyzer produces this diagnostic when a subclass of either `Struct` + // or `Union` is instantiated using a generative constructor. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the class `C` is being + // instantiated using a generative constructor: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // @Int32() + // external int a; + // } + // + // void f() { + // [!C!](); + // } + // ``` + // + // #### Common fixes + // + // If you need to allocate the structure described by the class, then use the + // `ffi` package to do so: + // + // ```dart + // import 'dart:ffi'; + // import 'package:ffi/ffi.dart'; + // + // class C extends Struct { + // @Int32() + // external int a; + // } + // + // void f() { + // final pointer = calloc.allocate(4); + // final c = pointer.ref; + // print(c); + // calloc.free(pointer); + // } + // ``` static const FfiCode CREATION_OF_STRUCT_OR_UNION = FfiCode( 'CREATION_OF_STRUCT_OR_UNION', "Subclasses of 'Struct' and 'Union' are backed by native memory, and can't " @@ -97,9 +227,56 @@ class FfiCode extends AnalyzerErrorCode { * 0: the name of the subclass * 1: the name of the superclass */ + // #### Description + // + // The analyzer produces this diagnostic when a subclass of `Struct` or + // `Union` doesn't have any fields. Having an empty `Struct` or `Union` + // isn't supported. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the class `C`, which + // extends `Struct`, doesn't declare any fields: + // + // ```dart + // import 'dart:ffi'; + // + // class [!C!] extends Struct {} + // ``` + // + // #### Common fixes + // + // If the class is intended to be a struct, then declare one or more fields: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // @Int32() + // external int x; + // } + // ``` + // + // If the class is intended to be used as a type argument to `Pointer`, then + // make it a subclass of `Opaque`: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Opaque {} + // ``` + // + // If the class isn't intended to be a struct, then remove or change the + // extends clause: + // + // ```dart + // class C {} + // ``` static const FfiCode EMPTY_STRUCT = FfiCode( 'EMPTY_STRUCT', - "The class '{0}' can’t be empty because it's a subclass of '{1}'.", + "The class '{0}' can't be empty because it's a subclass of '{1}'.", correctionMessage: "Try adding a field to '{0}' or use a different superclass.", ); @@ -107,6 +284,40 @@ class FfiCode extends AnalyzerErrorCode { /** * No parameters. */ + // #### Description + // + // The analyzer produces this diagnostic when a field in a subclass of + // `Struct` has more than one annotation describing the native type of the + // field. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the field `x` has two + // annotations describing the native type of the field: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // @Int32() + // [!@Int16()!] + // external int x; + // } + // ``` + // + // #### Common fixes + // + // Remove all but one of the annotations: + // + // ```dart + // import 'dart:ffi'; + // class C extends Struct { + // @Int32() + // external int x; + // } + // ``` static const FfiCode EXTRA_ANNOTATION_ON_STRUCT_FIELD = FfiCode( 'EXTRA_ANNOTATION_ON_STRUCT_FIELD', "Fields in a struct class must have exactly one annotation indicating the " @@ -117,6 +328,41 @@ class FfiCode extends AnalyzerErrorCode { /** * No parameters. */ + // #### Description + // + // The analyzer produces this diagnostic when a field in a subclass of + // `Struct` has more than one annotation describing the size of the native + // array. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the field `a0` has two + // annotations that specify the size of the native array: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // @Array(4) + // [!@Array(8)!] + // external Array a0; + // } + // ``` + // + // #### Common fixes + // + // Remove all but one of the annotations: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // @Array(8) + // external Array a0; + // } + // ``` static const FfiCode EXTRA_SIZE_ANNOTATION_CARRAY = FfiCode( 'EXTRA_SIZE_ANNOTATION_CARRAY', "'Array's must have exactly one 'Array' annotation.", @@ -174,6 +420,45 @@ class FfiCode extends AnalyzerErrorCode { /** * No parameters. */ + // #### Description + // + // The analyzer produces this diagnostic when a constructor in a subclass of + // either `Struct` or `Union` has one or more field initializers. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the class `C` has a + // constructor with an initializer for the field `f`: + // + // ```dart + // // @dart = 2.9 + // import 'dart:ffi'; + // + // class C extends Struct { + // @Int32() + // int f; + // + // C() : [!f = 0!]; + // } + // ``` + // + // #### Common fixes + // + // Remove the field initializer: + // + // ```dart + // // @dart = 2.9 + // import 'dart:ffi'; + // + // class C extends Struct { + // @Int32() + // int f; + // + // C(); + // } + // ``` static const FfiCode FIELD_INITIALIZER_IN_STRUCT = FfiCode( 'FIELD_INITIALIZER_IN_STRUCT', "Constructors in subclasses of 'Struct' and 'Union' can't have field " @@ -185,6 +470,39 @@ class FfiCode extends AnalyzerErrorCode { /** * No parameters. */ + // #### Description + // + // The analyzer produces this diagnostic when a field in a subclass of + // `Struct` has an initializer. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the field `p` has an + // initializer: + // + // ```dart + // // @dart = 2.9 + // import 'dart:ffi'; + // + // class C extends Struct { + // Pointer [!p!] = nullptr; + // } + // ``` + // + // #### Common fixes + // + // Remove the initializer: + // + // ```dart + // // @dart = 2.9 + // import 'dart:ffi'; + // + // class C extends Struct { + // Pointer p; + // } + // ``` static const FfiCode FIELD_IN_STRUCT_WITH_INITIALIZER = FfiCode( 'FIELD_IN_STRUCT_WITH_INITIALIZER', "Fields in subclasses of 'Struct' and 'Union' can't have initializers.", @@ -195,6 +513,39 @@ class FfiCode extends AnalyzerErrorCode { /** * No parameters. */ + // #### Description + // + // The analyzer produces this diagnostic when a field in a subclass of either + // `Struct` or `Union` isn't marked as being `external`. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the field `a` isn't + // marked as being `external`: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // @Int16() + // int [!a!]; + // } + // ``` + // + // #### Common fixes + // + // Add the required `external` modifier: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // @Int16() + // external int a; + // } + // ``` static const FfiCode FIELD_MUST_BE_EXTERNAL_IN_STRUCT = FfiCode( 'FIELD_MUST_BE_EXTERNAL_IN_STRUCT', "Fields of 'Struct' and 'Union' subclasses must be marked external.", @@ -205,20 +556,93 @@ class FfiCode extends AnalyzerErrorCode { * Parameters: * 0: the name of the struct class */ + // #### Description + // + // The analyzer produces this diagnostic when a subclass of either `Struct` + // or `Union` has a type parameter. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the class `S` defines + // the type parameter `T`: + // + // ```dart + // import 'dart:ffi'; + // + // class [!S!] extends Struct { + // external Pointer notEmpty; + // } + // ``` + // + // #### Common fixes + // + // Remove the type parameters from the class: + // + // ```dart + // import 'dart:ffi'; + // + // class S extends Struct { + // external Pointer notEmpty; + // } + // ``` static const FfiCode GENERIC_STRUCT_SUBCLASS = FfiCode( 'GENERIC_STRUCT_SUBCLASS', - "The class '{0}' can't extend 'Struct' or 'Union' because it is generic.", + "The class '{0}' can't extend 'Struct' or 'Union' because '{0}' is " + "generic.", correctionMessage: "Try removing the type parameters from '{0}'.", ); /** * No parameters. */ + // #### Description + // + // The analyzer produces this diagnostic when an invocation of the method + // `Pointer.fromFunction` has a second argument (the exceptional return + // value) and the type to be returned from the invocation is either `void`, + // `Handle` or `Pointer`. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because a second argument is + // provided when the return type of `f` is `void`: + // + // ```dart + // import 'dart:ffi'; + // + // typedef T = Void Function(Int8); + // + // void f(int i) {} + // + // void g() { + // Pointer.fromFunction(f, [!42!]); + // } + // ``` + // + // #### Common fixes + // + // Remove the exception value: + // + // ```dart + // import 'dart:ffi'; + // + // typedef T = Void Function(Int8); + // + // void f(int i) {} + // + // void g() { + // Pointer.fromFunction(f); + // } + // ``` static const FfiCode INVALID_EXCEPTION_VALUE = FfiCode( 'INVALID_EXCEPTION_VALUE', - "The method 'Pointer.fromFunction' must not have an exceptional return " - "value (the second argument) when the return type of the function is " - "either 'void', 'Handle' or 'Pointer'.", + "The method 'Pointer.fromFunction' can't have an exceptional return value " + "(the second argument) when the return type of the function is either " + "'void', 'Handle' or 'Pointer'.", correctionMessage: "Try removing the exceptional return value.", ); @@ -226,6 +650,46 @@ class FfiCode extends AnalyzerErrorCode { * Parameters: * 0: the type of the field */ + // #### Description + // + // The analyzer produces this diagnostic when a field in a subclass of + // `Struct` has a type other than `int`, `double`, `Array`, `Pointer`, or + // subtype of `Struct` or `Union`. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the field `str` has + // the type `String`, which isn't one of the allowed types for fields in a + // subclass of `Struct`: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // external [!String!] s; + // + // @Int32() + // external int i; + // } + // ``` + // + // #### Common fixes + // + // Use one of the allowed types for the field: + // + // ```dart + // import 'dart:ffi'; + // import 'package:ffi/ffi.dart'; + // + // class C extends Struct { + // external Pointer s; + // + // @Int32() + // external int i; + // } + // ``` static const FfiCode INVALID_FIELD_TYPE_IN_STRUCT = FfiCode( 'INVALID_FIELD_TYPE_IN_STRUCT', "Fields in struct classes can't have the type '{0}'. They can only be " @@ -239,27 +703,167 @@ class FfiCode extends AnalyzerErrorCode { /** * No parameters. */ + // #### Description + // + // The analyzer produces this diagnostic when the value of the `isLeaf` + // argument in an invocation of either `Pointer.asFunction` or + // `DynamicLibrary.lookupFunction` is `true` and the function that would be + // returned would have a return type of `Handle`. + // + // The analyzer also produces this diagnostic when the value of the `isLeaf` + // argument in an `FfiNative` annotation is `true` and the type argument on + // the annotation is a function type whose return type is `Handle`. + // + // In all of these cases, leaf calls are only supported for the types `bool`, + // `int`, `float`, `double`, and, as a return type `void`. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the function `p` + // returns a `Handle`, but the `isLeaf` argument is `true`: + // + // ```dart + // import 'dart:ffi'; + // + // void f(Pointer> p) { + // [!p.asFunction(isLeaf: true)!]; + // } + // ``` + // + // #### Common fixes + // + // If the function returns a handle, then remove the `isLeaf` argument: + // + // ```dart + // import 'dart:ffi'; + // + // void f(Pointer> p) { + // p.asFunction(); + // } + // ``` + // + // If the function returns one of the supported types, then correct the type + // information: + // + // ```dart + // import 'dart:ffi'; + // + // void f(Pointer> p) { + // p.asFunction(isLeaf: true); + // } + // ``` static const FfiCode LEAF_CALL_MUST_NOT_RETURN_HANDLE = FfiCode( 'LEAF_CALL_MUST_NOT_RETURN_HANDLE', - "FFI leaf call must not return a Handle.", + "FFI leaf call can't return a 'Handle'.", correctionMessage: "Try changing the return type to primitive or struct.", ); /** * No parameters. */ + // #### Description + // + // The analyzer produces this diagnostic when the value of the `isLeaf` + // argument in an invocation of either `Pointer.asFunction` or + // `DynamicLibrary.lookupFunction` is `true` and the function that would be + // returned would have a parameter of type `Handle`. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the function `p` has a + // parameter of type `Handle`, but the `isLeaf` argument is `true`: + // + // ```dart + // import 'dart:ffi'; + // + // void f(Pointer> p) { + // [!p.asFunction(isLeaf: true)!]; + // } + // ``` + // + // #### Common fixes + // + // If the function has at least one parameter of type `Handle`, then remove + // the `isLeaf` argument: + // + // ```dart + // import 'dart:ffi'; + // + // void f(Pointer> p) { + // p.asFunction(); + // } + // ``` + // + // If none of the function's parameters are `Handle`s, then correct the type + // information: + // + // ```dart + // import 'dart:ffi'; + // + // void f(Pointer> p) { + // p.asFunction(isLeaf: true); + // } + // ``` static const FfiCode LEAF_CALL_MUST_NOT_TAKE_HANDLE = FfiCode( 'LEAF_CALL_MUST_NOT_TAKE_HANDLE', - "FFI leaf call must not take arguments of type Handle.", + "FFI leaf call can't take arguments of type 'Handle'.", correctionMessage: "Try changing the argument type to primitive or struct.", ); /** * No parameters. */ + // #### Description + // + // The analyzer produces this diagnostic when the annotation on a field in a + // subclass of `Struct` or `Union` doesn't match the Dart type of the field. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the annotation + // `Double` doesn't match the Dart type `int`: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // [!@Double()!] + // external int x; + // } + // ``` + // + // #### Common fixes + // + // If the type of the field is correct, then change the annotation to match: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // @Int32() + // external int x; + // } + // ``` + // + // If the annotation is correct, then change the type of the field to match: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // @Double() + // external double x; + // } + // ``` static const FfiCode MISMATCHED_ANNOTATION_ON_STRUCT_FIELD = FfiCode( 'MISMATCHED_ANNOTATION_ON_STRUCT_FIELD', - "The annotation does not match the declared type of the field.", + "The annotation doesn't match the declared type of the field.", correctionMessage: "Try using a different annotation or changing the declared type to " "match.", @@ -268,6 +872,41 @@ class FfiCode extends AnalyzerErrorCode { /** * No parameters. */ + // #### Description + // + // The analyzer produces this diagnostic when a field in a subclass of + // `Struct` or `Union` whose type requires an annotation doesn't have one. + // The Dart types `int`, `double`, and `Array` are used to represent multiple + // C types, and the annotation specifies which of the compatible C types the + // field represents. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the field `x` doesn't + // have an annotation indicating the underlying width of the integer value: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // external [!int!] x; + // } + // ``` + // + // #### Common fixes + // + // Add an appropriate annotation to the field: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // @Int64() + // external int x; + // } + // ``` static const FfiCode MISSING_ANNOTATION_ON_STRUCT_FIELD = FfiCode( 'MISSING_ANNOTATION_ON_STRUCT_FIELD', "Fields in a struct class must either have the type 'Pointer' or an " @@ -278,11 +917,49 @@ class FfiCode extends AnalyzerErrorCode { /** * No parameters. */ + // #### Description + // + // The analyzer produces this diagnostic when an invocation of the method + // `Pointer.fromFunction` doesn't have a second argument (the exceptional + // return value) when the type to be returned from the invocation is neither + // `void`, `Handle`, nor `Pointer`. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the type returned by + // `f` is expected to be an 8-bit integer but the call to `fromFunction` + // doesn't include an exceptional return argument: + // + // ```dart + // import 'dart:ffi'; + // + // int f(int i) => i * 2; + // + // void g() { + // Pointer.[!fromFunction!](f); + // } + // ``` + // + // #### Common fixes + // + // Add an exceptional return type: + // + // ```dart + // import 'dart:ffi'; + // + // int f(int i) => i * 2; + // + // void g() { + // Pointer.fromFunction(f, 0); + // } + // ``` static const FfiCode MISSING_EXCEPTION_VALUE = FfiCode( 'MISSING_EXCEPTION_VALUE', "The method 'Pointer.fromFunction' must have an exceptional return value " "(the second argument) when the return type of the function is neither " - "'void', 'Handle' or 'Pointer'.", + "'void', 'Handle', nor 'Pointer'.", correctionMessage: "Try adding an exceptional return value.", ); @@ -290,6 +967,46 @@ class FfiCode extends AnalyzerErrorCode { * Parameters: * 0: the type of the field */ + // #### Description + // + // The analyzer produces this diagnostic when a field in a subclass of + // `Struct` or `Union` doesn't have a type annotation. Every field must have + // an explicit type, and the type must either be `int`, `double`, `Pointer`, + // or a subclass of either `Struct` or `Union`. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the field `str` + // doesn't have a type annotation: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // external var [!str!]; + // + // @Int32() + // external int i; + // } + // ``` + // + // #### Common fixes + // + // Explicitly specify the type of the field: + // + // ```dart + // import 'dart:ffi'; + // import 'package:ffi/ffi.dart'; + // + // class C extends Struct { + // external Pointer str; + // + // @Int32() + // external int i; + // } + // ``` static const FfiCode MISSING_FIELD_TYPE_IN_STRUCT = FfiCode( 'MISSING_FIELD_TYPE_IN_STRUCT', "Fields in struct classes must have an explicitly declared type of 'int', " @@ -300,10 +1017,45 @@ class FfiCode extends AnalyzerErrorCode { /** * No parameters. */ + // #### Description + // + // The analyzer produces this diagnostic when a field in a subclass of either + // `Struct` or `Union` has a type of `Array` but doesn't have a single + // `Array` annotation indicating the dimensions of the array. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the field `a0` doesn't + // have an `Array` annotation: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // external [!Array!] a0; + // } + // ``` + // + // #### Common fixes + // + // Ensure that there's exactly one `Array` annotation on the field: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // @Array(8) + // external Array a0; + // } + // ``` static const FfiCode MISSING_SIZE_ANNOTATION_CARRAY = FfiCode( 'MISSING_SIZE_ANNOTATION_CARRAY', - "'Array's must have exactly one 'Array' annotation.", - correctionMessage: "Try adding a 'Array' annotation.", + "Fields of type 'Array' must have exactly one 'Array' annotation.", + correctionMessage: + "Try adding an 'Array' annotation, or removing all but one of the " + "annotations.", ); /** @@ -311,6 +1063,47 @@ class FfiCode extends AnalyzerErrorCode { * 0: the type that should be a valid dart:ffi native type. * 1: the name of the function whose invocation depends on this relationship */ + // #### Description + // + // The analyzer produces this diagnostic when an invocation of either + // `Pointer.fromFunction` or `DynamicLibrary.lookupFunction` has a type + // argument(whether explicit or inferred) that isn't a native function type. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the type `T` can be + // any subclass of `Function` but the type argument for `fromFunction` is + // required to be a native function type: + // + // ```dart + // import 'dart:ffi'; + // + // int f(int i) => i * 2; + // + // class C { + // void g() { + // Pointer.fromFunction<[!T!]>(f, 0); + // } + // } + // ``` + // + // #### Common fixes + // + // Use a native function type as the type argument to the invocation: + // + // ```dart + // import 'dart:ffi'; + // + // int f(int i) => i * 2; + // + // class C { + // void g() { + // Pointer.fromFunction(f, 0); + // } + // } + // ``` static const FfiCode MUST_BE_A_NATIVE_FUNCTION_TYPE = FfiCode( 'MUST_BE_A_NATIVE_FUNCTION_TYPE', "The type '{0}' given to '{1}' must be a valid 'dart:ffi' native function " @@ -325,6 +1118,64 @@ class FfiCode extends AnalyzerErrorCode { * 1: the supertype that the subtype is compared to * 2: the name of the function whose invocation depends on this relationship */ + // #### Description + // + // The analyzer produces this diagnostic in two cases: + // - In an invocation of `Pointer.fromFunction` where the type argument + // (whether explicit or inferred) isn't a supertype of the type of the + // function passed as the first argument to the method. + // - In an invocation of `DynamicLibrary.lookupFunction` where the first type + // argument isn't a supertype of the second type argument. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the type of the + // function `f` (`String Function(int)`) isn't a subtype of the type + // argument `T` (`Int8 Function(Int8)`): + // + // ```dart + // import 'dart:ffi'; + // + // typedef T = Int8 Function(Int8); + // + // double f(double i) => i; + // + // void g() { + // Pointer.fromFunction([!f!], 5.0); + // } + // ``` + // + // #### Common fixes + // + // If the function is correct, then change the type argument to match: + // + // ```dart + // import 'dart:ffi'; + // + // typedef T = Float Function(Float); + // + // double f(double i) => i; + // + // void g() { + // Pointer.fromFunction(f, 5.0); + // } + // ``` + // + // If the type argument is correct, then change the function to match: + // + // ```dart + // import 'dart:ffi'; + // + // typedef T = Int8 Function(Int8); + // + // int f(int i) => i; + // + // void g() { + // Pointer.fromFunction(f, 5); + // } + // ``` static const FfiCode MUST_BE_A_SUBTYPE = FfiCode( 'MUST_BE_A_SUBTYPE', "The type '{0}' must be a subtype of '{1}' for '{2}'.", @@ -335,10 +1186,50 @@ class FfiCode extends AnalyzerErrorCode { * Parameters: * 0: the name of the function, method, or constructor having type arguments */ + // #### Description + // + // The analyzer produces this diagnostic when the type arguments to a method + // are required to be known at compile time, but a type parameter, whose + // value can't be known at compile time, is used as a type argument. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the type argument to + // `Pointer.asFunction` must be known at compile time, but the type parameter + // `R`, which isn't known at compile time, is being used as the type + // argument: + // + // ```dart + // import 'dart:ffi'; + // + // typedef T = int Function(int); + // + // class C { + // void m(Pointer> p) { + // p.asFunction<[!R!]>(); + // } + // } + // ``` + // + // #### Common fixes + // + // Remove any uses of type parameters: + // + // ```dart + // import 'dart:ffi'; + // + // class C { + // void m(Pointer> p) { + // p.asFunction(); + // } + // } + // ``` static const FfiCode NON_CONSTANT_TYPE_ARGUMENT = FfiCode( 'NON_CONSTANT_TYPE_ARGUMENT', - "The type arguments to '{0}' must be compile time constants but type " - "parameters are not constants.", + "The type arguments to '{0}' must be known at compile time, so they can't " + "be type parameters.", correctionMessage: "Try changing the type argument to be a constant type.", ); @@ -346,10 +1237,53 @@ class FfiCode extends AnalyzerErrorCode { * Parameters: * 0: the type that should be a valid dart:ffi native type. */ + // #### Description + // + // The analyzer produces this diagnostic when the method `asFunction` is + // invoked on a pointer to a native function, but the signature of the native + // function isn't a valid C function signature. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because function signature + // associated with the pointer `p` (`FNative`) isn't a valid C function + // signature: + // + // ```dart + // import 'dart:ffi'; + // + // typedef FNative = int Function(int); + // typedef F = int Function(int); + // + // class C { + // void f(Pointer> p) { + // p.asFunction<[!F!]>(); + // } + // } + // ``` + // + // #### Common fixes + // + // Make the `NativeFunction` signature a valid C signature: + // + // ```dart + // import 'dart:ffi'; + // + // typedef FNative = Int8 Function(Int8); + // typedef F = int Function(int); + // + // class C { + // void f(Pointer> p) { + // p.asFunction(); + // } + // } + // ``` static const FfiCode NON_NATIVE_FUNCTION_TYPE_ARGUMENT_TO_POINTER = FfiCode( 'NON_NATIVE_FUNCTION_TYPE_ARGUMENT_TO_POINTER', - "The type argument for the pointer '{0}' must be a valid 'NativeFunction' " - "in order to use 'asFunction'.", + "Can't invoke 'asFunction' because the function signature '{0}' for the " + "pointer isn't a valid C function signature.", correctionMessage: "Try changing the function argument in 'NativeFunction' to only use " "NativeTypes.", @@ -358,6 +1292,39 @@ class FfiCode extends AnalyzerErrorCode { /** * No parameters. */ + // #### Description + // + // The analyzer produces this diagnostic when a dimension given in an `Array` + // annotation is less than or equal to zero (`0`). + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because an array dimension of + // `-1` was provided: + // + // ```dart + // import 'dart:ffi'; + // + // class MyStruct extends Struct { + // @Array([!-8!]) + // external Array a0; + // } + // ``` + // + // #### Common fixes + // + // Change the dimension to be a positive integer: + // + // ```dart + // import 'dart:ffi'; + // + // class MyStruct extends Struct { + // @Array(8) + // external Array a0; + // } + // ``` static const FfiCode NON_POSITIVE_ARRAY_DIMENSION = FfiCode( 'NON_POSITIVE_ARRAY_DIMENSION', "Array dimensions must be positive numbers.", @@ -368,11 +1335,46 @@ class FfiCode extends AnalyzerErrorCode { * Parameters: * 0: the type of the field */ + // #### Description + // + // The analyzer produces this diagnostic when the type argument for the class + // `Array` isn't one of the valid types: either a native integer, `Float`, + // `Double`, `Pointer`, or subtype of `Struct`, `Union`, or + // `AbiSpecificInteger`. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the type argument to + // `Array` is `Void`, and `Void` isn't one of the valid types: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // @Array(8) + // external Array<[!Void!]> a0; + // } + // ``` + // + // #### Common fixes + // + // Change the type argument to one of the valid types: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // @Array(8) + // external Array a0; + // } + // ``` static const FfiCode NON_SIZED_TYPE_ARGUMENT = FfiCode( 'NON_SIZED_TYPE_ARGUMENT', - "Type arguments to '{0}' can't have the type '{1}'. They can only be " - "declared as native integer, 'Float', 'Double', 'Pointer', or subtype " - "of 'Struct', 'Union', or 'AbiSpecificInteger'.", + "The type '{1}' isn't a valid type argument for '{0}'. The type argument " + "must be a native integer, 'Float', 'Double', 'Pointer', or subtype of " + "'Struct', 'Union', or 'AbiSpecificInteger'.", correctionMessage: "Try using a native integer, 'Float', 'Double', 'Pointer', or subtype " "of 'Struct', 'Union', or 'AbiSpecificInteger'.", @@ -381,6 +1383,40 @@ class FfiCode extends AnalyzerErrorCode { /** * No parameters. */ + // #### Description + // + // The analyzer produces this diagnostic when a subclass of `Struct` has more + // than one `Packed` annotation. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the class `C`, which + // is a subclass of `Struct`, has two `Packed` annotations: + // + // ```dart + // import 'dart:ffi'; + // + // @Packed(1) + // [!@Packed(1)!] + // class C extends Struct { + // external Pointer notEmpty; + // } + // ``` + // + // #### Common fixes + // + // Remove all but one of the annotations: + // + // ```dart + // import 'dart:ffi'; + // + // @Packed(1) + // class C extends Struct { + // external Pointer notEmpty; + // } + // ``` static const FfiCode PACKED_ANNOTATION = FfiCode( 'PACKED_ANNOTATION', "Structs must have at most one 'Packed' annotation.", @@ -390,6 +1426,39 @@ class FfiCode extends AnalyzerErrorCode { /** * No parameters. */ + // #### Description + // + // The analyzer produces this diagnostic when the argument to the `Packed` + // annotation isn't one of the allowed values: 1, 2, 4, 8, or 16. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the argument to the + // `Packed` annotation (`3`) isn't one of the allowed values: + // + // ```dart + // import 'dart:ffi'; + // + // @Packed([!3!]) + // class C extends Struct { + // external Pointer notEmpty; + // } + // ``` + // + // #### Common fixes + // + // Change the alignment to be one of the allowed values: + // + // ```dart + // import 'dart:ffi'; + // + // @Packed(4) + // class C extends Struct { + // external Pointer notEmpty; + // } + // ``` static const FfiCode PACKED_ANNOTATION_ALIGNMENT = FfiCode( 'PACKED_ANNOTATION_ALIGNMENT', "Only packing to 1, 2, 4, 8, and 16 bytes is supported.", @@ -402,10 +1471,86 @@ class FfiCode extends AnalyzerErrorCode { * 0: the name of the outer struct * 1: the name of the struct being nested */ + // #### Description + // + // The analyzer produces this diagnostic when a subclass of `Struct` that is + // annotated as being `Packed` declares a field whose type is also a subclass + // of `Struct` and the field's type is either not packed or is packed less + // tightly. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the class `Outer`, + // which is a subclass of `Struct` and is packed on 1-byte boundaries, + // declared a field whose type (`Inner`) is packed on 8-byte boundaries: + // + // ```dart + // import 'dart:ffi'; + // + // @Packed(8) + // class Inner extends Struct { + // external Pointer notEmpty; + // } + // + // @Packed(1) + // class Outer extends Struct { + // external Pointer notEmpty; + // + // external [!Inner!] nestedLooselyPacked; + // } + // ``` + // + // #### Common fixes + // + // If the inner struct should be packed more tightly, then change the + // argument to the inner struct's `Packed` annotation: + // + // ```dart + // import 'dart:ffi'; + // + // @Packed(1) + // class Inner extends Struct { + // external Pointer notEmpty; + // } + // + // @Packed(1) + // class Outer extends Struct { + // external Pointer notEmpty; + // + // external Inner nestedLooselyPacked; + // } + // ``` + // + // If the outer struct should be packed less tightly, then change the + // argument to the outer struct's `Packed` annotation: + // + // ```dart + // import 'dart:ffi'; + // + // @Packed(8) + // class Inner extends Struct { + // external Pointer notEmpty; + // } + // + // @Packed(8) + // class Outer extends Struct { + // external Pointer notEmpty; + // + // external Inner nestedLooselyPacked; + // } + // ``` + // + // If the inner struct doesn't have an annotation and should be packed, then + // add an annotation. + // + // If the inner struct doesn't have an annotation and the outer struct + // shouldn't be packed, then remove its annotation. static const FfiCode PACKED_NESTING_NON_PACKED = FfiCode( 'PACKED_NESTING_NON_PACKED', "Nesting the non-packed or less tightly packed struct '{0}' in a packed " - "struct '{1}' is not supported.", + "struct '{1}' isn't supported.", correctionMessage: "Try packing the nested struct or packing the nested struct more " "tightly.", @@ -414,6 +1559,53 @@ class FfiCode extends AnalyzerErrorCode { /** * No parameters. */ + // #### Description + // + // The analyzer produces this diagnostic when the number of dimensions + // specified in an `Array` annotation doesn't match the number of nested + // arrays specified by the type of a field. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the field `a0` has a + // type with three nested arrays, but only two dimensions are given in the + // `Array` annotation: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // [!@Array(8, 8)!] + // external Array>> a0; + // } + // ``` + // + // #### Common fixes + // + // If the type of the field is correct, then fix the annotation to have the + // required number of dimensions: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // @Array(8, 8, 4) + // external Array>> a0; + // } + // ``` + // + // If the type of the field is wrong, then fix the type of the field: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // @Array(8, 8) + // external Array> a0; + // } + // ``` static const FfiCode SIZE_ANNOTATION_DIMENSIONS = FfiCode( 'SIZE_ANNOTATION_DIMENSIONS', "'Array's must have an 'Array' annotation that matches the dimensions.", @@ -425,6 +1617,46 @@ class FfiCode extends AnalyzerErrorCode { * 0: the name of the subclass * 1: the name of the class being extended, implemented, or mixed in */ + // #### Description + // + // The analyzer produces this diagnostic when a class extends any FFI class + // other than `Struct` or `Union`, or implements or mixes in any FFI class. + // `Struct` and `Union` are the only FFI classes that can be subtyped, and + // then only by extending them. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the class `C` extends + // `Double`: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends [!Double!] {} + // ``` + // + // #### Common fixes + // + // If the class should extend either `Struct` or `Union`, then change the + // declaration of the class: + // + // ```dart + // import 'dart:ffi'; + // + // class C extends Struct { + // @Int32() + // external int i; + // } + // ``` + // + // If the class shouldn't extend either `Struct` or `Union`, then remove any + // references to FFI classes: + // + // ```dart + // class C {} + // ``` static const FfiCode SUBTYPE_OF_FFI_CLASS_IN_EXTENDS = FfiCode( 'SUBTYPE_OF_FFI_CLASS', "The class '{0}' can't extend '{1}'.", @@ -461,6 +1693,50 @@ class FfiCode extends AnalyzerErrorCode { * 0: the name of the subclass * 1: the name of the class being extended, implemented, or mixed in */ + // #### Description + // + // The analyzer produces this diagnostic when a class extends, implements, or + // mixes in a class that extends either `Struct` or `Union`. Classes can only + // extend either `Struct` or `Union` directly. + // + // For more information about FFI, see [C interop using dart:ffi][]. + // + // #### Example + // + // The following code produces this diagnostic because the class `C` extends + // `S`, and `S` extends `Struct`: + // + // ```dart + // import 'dart:ffi'; + // + // class S extends Struct { + // external Pointer f; + // } + // + // class C extends [!S!] { + // external Pointer g; + // } + // ``` + // + // #### Common fixes + // + // If you're trying to define a struct or union that shares some fields + // declared by a different struct or union, then extend `Struct` or `Union` + // directly and copy the shared fields: + // + // ```dart + // import 'dart:ffi'; + // + // class S extends Struct { + // external Pointer f; + // } + // + // class C extends Struct { + // external Pointer f; + // + // external Pointer g; + // } + // ``` static const FfiCode SUBTYPE_OF_STRUCT_CLASS_IN_EXTENDS = FfiCode( 'SUBTYPE_OF_STRUCT_CLASS', "The class '{0}' can't extend '{1}' because '{1}' is a subtype of " diff --git a/pkg/analyzer/lib/src/test_utilities/mock_packages.dart b/pkg/analyzer/lib/src/test_utilities/mock_packages.dart index 84ee9ef6a65..9c4b72b28d6 100644 --- a/pkg/analyzer/lib/src/test_utilities/mock_packages.dart +++ b/pkg/analyzer/lib/src/test_utilities/mock_packages.dart @@ -6,6 +6,33 @@ import 'package:analyzer/file_system/file_system.dart'; /// Helper for creating mock packages. class MockPackages { + /// Create a fake 'ffi' package that can be used by tests. + static void addFfiPackageFiles(Folder rootFolder) { + var libFolder = rootFolder.getChildAssumingFolder('lib'); + libFolder.getChildAssumingFile('ffi.dart').writeAsStringSync(r''' +import 'dart:ffi'; + +const Allocator calloc = _CallocAllocator(); + +abstract class Allocator { + Pointer allocate(int byteCount, {int? alignment}); + + void free(Pointer pointer); +} + +class Utf8 extends Opaque {} + +class _CallocAllocator implements Allocator { + @override + Pointer allocate(int byteCount, {int? alignment}) + => throw ''; + + @override + void free(Pointer pointer) => throw ''; +} +'''); + } + /// Create a fake 'js' package that can be used by tests. static void addJsPackageFiles(Folder rootFolder) { var libFolder = rootFolder.getChildAssumingFolder('lib'); diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart index e373a74b8d5..84784c3b405 100644 --- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart +++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart @@ -662,6 +662,8 @@ class NativeType { class Handle extends NativeType {} +abstract class Opaque extends NativeType {} + class Void extends NativeType {} class Int8 extends NativeType { diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml index 1e292ccadf4..f3dcc6da50f 100644 --- a/pkg/analyzer/messages.yaml +++ b/pkg/analyzer/messages.yaml @@ -13822,34 +13822,286 @@ FfiCode: correctionMessage: Try changing the value to 'Int8', 'Int16', 'Int32', 'Int64', 'Uint8', 'Uint16', 'UInt32', or 'Uint64'. comment: No parameters. ANNOTATION_ON_POINTER_FIELD: - problemMessage: "Fields in a struct class whose type is 'Pointer' should not have any annotations." + problemMessage: "Fields in a struct class whose type is 'Pointer' shouldn't have any annotations." correctionMessage: Try removing the annotation. comment: No parameters. + documentation: |- + #### Description + + The analyzer produces this diagnostic when a field that's declared in a + subclass of `Struct` and has the type `Pointer` also has an annotation + associated with it. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the field `p`, which + has the type `Pointer` and is declared in a subclass of `Struct`, has the + annotation `@Double()`: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + [!@Double()!] + external Pointer p; + } + ``` + + #### Common fixes + + Remove the annotations from the field: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + external Pointer p; + } + ``` ARGUMENT_MUST_BE_A_CONSTANT: problemMessage: "Argument '{0}' must be a constant." correctionMessage: Try replacing the value with a literal or const. comment: |- Parameters: 0: the name of the argument + documentation: |- + #### Description + + The analyzer produces this diagnostic when an invocation of either + `Pointer.asFunction` or `DynamicLibrary.lookupFunction` has an `isLeaf` + argument whose value isn't a constant expression. + + The analyzer also produces this diagnostic when the value of the + `exceptionalReturn` argument of `Pointer.fromFunction`. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the value of the + `isLeaf` argument is a parameter, and hence isn't a constant: + + ```dart + import 'dart:ffi'; + + int Function(int) fromPointer( + Pointer> p, bool isLeaf) { + return p.asFunction(isLeaf: [!isLeaf!]); + } + ``` + + #### Common fixes + + If there's a suitable constant that can be used, then replace the argument + with a constant: + + ```dart + import 'dart:ffi'; + + const isLeaf = false; + + int Function(int) fromPointer(Pointer> p) { + return p.asFunction(isLeaf: isLeaf); + } + ``` + + If there isn't a suitable constant, then replace the argument with a + boolean literal: + + ```dart + import 'dart:ffi'; + + int Function(int) fromPointer(Pointer> p) { + return p.asFunction(isLeaf: true); + } + ``` CREATION_OF_STRUCT_OR_UNION: problemMessage: "Subclasses of 'Struct' and 'Union' are backed by native memory, and can't be instantiated by a generative constructor." correctionMessage: "Try allocating it via allocation, or load from a 'Pointer'." comment: No parameters. + documentation: |- + #### Description + + The analyzer produces this diagnostic when a subclass of either `Struct` + or `Union` is instantiated using a generative constructor. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the class `C` is being + instantiated using a generative constructor: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + @Int32() + external int a; + } + + void f() { + [!C!](); + } + ``` + + #### Common fixes + + If you need to allocate the structure described by the class, then use the + `ffi` package to do so: + + ```dart + import 'dart:ffi'; + import 'package:ffi/ffi.dart'; + + class C extends Struct { + @Int32() + external int a; + } + + void f() { + final pointer = calloc.allocate(4); + final c = pointer.ref; + print(c); + calloc.free(pointer); + } + ``` EMPTY_STRUCT: - problemMessage: "The class '{0}' can’t be empty because it's a subclass of '{1}'." + problemMessage: "The class '{0}' can't be empty because it's a subclass of '{1}'." correctionMessage: "Try adding a field to '{0}' or use a different superclass." comment: |- Parameters: 0: the name of the subclass 1: the name of the superclass + documentation: |- + #### Description + + The analyzer produces this diagnostic when a subclass of `Struct` or + `Union` doesn't have any fields. Having an empty `Struct` or `Union` + isn't supported. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the class `C`, which + extends `Struct`, doesn't declare any fields: + + ```dart + import 'dart:ffi'; + + class [!C!] extends Struct {} + ``` + + #### Common fixes + + If the class is intended to be a struct, then declare one or more fields: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + @Int32() + external int x; + } + ``` + + If the class is intended to be used as a type argument to `Pointer`, then + make it a subclass of `Opaque`: + + ```dart + import 'dart:ffi'; + + class C extends Opaque {} + ``` + + If the class isn't intended to be a struct, then remove or change the + extends clause: + + ```dart + class C {} + ``` EXTRA_ANNOTATION_ON_STRUCT_FIELD: problemMessage: Fields in a struct class must have exactly one annotation indicating the native type. correctionMessage: Try removing the extra annotation. comment: No parameters. + documentation: |- + #### Description + + The analyzer produces this diagnostic when a field in a subclass of + `Struct` has more than one annotation describing the native type of the + field. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the field `x` has two + annotations describing the native type of the field: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + @Int32() + [!@Int16()!] + external int x; + } + ``` + + #### Common fixes + + Remove all but one of the annotations: + + ```dart + import 'dart:ffi'; + class C extends Struct { + @Int32() + external int x; + } + ``` EXTRA_SIZE_ANNOTATION_CARRAY: problemMessage: "'Array's must have exactly one 'Array' annotation." correctionMessage: Try removing the extra annotation. comment: No parameters. + documentation: |- + #### Description + + The analyzer produces this diagnostic when a field in a subclass of + `Struct` has more than one annotation describing the size of the native + array. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the field `a0` has two + annotations that specify the size of the native array: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + @Array(4) + [!@Array(8)!] + external Array a0; + } + ``` + + #### Common fixes + + Remove all but one of the annotations: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + @Array(8) + external Array a0; + } + ``` FFI_NATIVE_MUST_BE_EXTERNAL: problemMessage: FfiNative functions must be declared external. correctionMessage: Add the `external` keyword to the function. @@ -13876,60 +14128,576 @@ FfiCode: problemMessage: "Constructors in subclasses of 'Struct' and 'Union' can't have field initializers." correctionMessage: Try removing the field initializer and marking the field as external. comment: No parameters. + documentation: |- + #### Description + + The analyzer produces this diagnostic when a constructor in a subclass of + either `Struct` or `Union` has one or more field initializers. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the class `C` has a + constructor with an initializer for the field `f`: + + ```dart + // @dart = 2.9 + import 'dart:ffi'; + + class C extends Struct { + @Int32() + int f; + + C() : [!f = 0!]; + } + ``` + + #### Common fixes + + Remove the field initializer: + + ```dart + // @dart = 2.9 + import 'dart:ffi'; + + class C extends Struct { + @Int32() + int f; + + C(); + } + ``` FIELD_IN_STRUCT_WITH_INITIALIZER: problemMessage: "Fields in subclasses of 'Struct' and 'Union' can't have initializers." correctionMessage: Try removing the initializer and marking the field as external. comment: No parameters. + documentation: |- + #### Description + + The analyzer produces this diagnostic when a field in a subclass of + `Struct` has an initializer. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the field `p` has an + initializer: + + ```dart + // @dart = 2.9 + import 'dart:ffi'; + + class C extends Struct { + Pointer [!p!] = nullptr; + } + ``` + + #### Common fixes + + Remove the initializer: + + ```dart + // @dart = 2.9 + import 'dart:ffi'; + + class C extends Struct { + Pointer p; + } + ``` FIELD_MUST_BE_EXTERNAL_IN_STRUCT: problemMessage: "Fields of 'Struct' and 'Union' subclasses must be marked external." correctionMessage: "Try adding the 'external' modifier." comment: No parameters. + documentation: |- + #### Description + + The analyzer produces this diagnostic when a field in a subclass of either + `Struct` or `Union` isn't marked as being `external`. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the field `a` isn't + marked as being `external`: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + @Int16() + int [!a!]; + } + ``` + + #### Common fixes + + Add the required `external` modifier: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + @Int16() + external int a; + } + ``` GENERIC_STRUCT_SUBCLASS: - problemMessage: "The class '{0}' can't extend 'Struct' or 'Union' because it is generic." + problemMessage: "The class '{0}' can't extend 'Struct' or 'Union' because '{0}' is generic." correctionMessage: "Try removing the type parameters from '{0}'." comment: |- Parameters: 0: the name of the struct class + documentation: |- + #### Description + + The analyzer produces this diagnostic when a subclass of either `Struct` + or `Union` has a type parameter. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the class `S` defines + the type parameter `T`: + + ```dart + import 'dart:ffi'; + + class [!S!] extends Struct { + external Pointer notEmpty; + } + ``` + + #### Common fixes + + Remove the type parameters from the class: + + ```dart + import 'dart:ffi'; + + class S extends Struct { + external Pointer notEmpty; + } + ``` INVALID_EXCEPTION_VALUE: - problemMessage: "The method 'Pointer.fromFunction' must not have an exceptional return value (the second argument) when the return type of the function is either 'void', 'Handle' or 'Pointer'." + problemMessage: "The method 'Pointer.fromFunction' can't have an exceptional return value (the second argument) when the return type of the function is either 'void', 'Handle' or 'Pointer'." correctionMessage: Try removing the exceptional return value. comment: No parameters. + documentation: |- + #### Description + + The analyzer produces this diagnostic when an invocation of the method + `Pointer.fromFunction` has a second argument (the exceptional return + value) and the type to be returned from the invocation is either `void`, + `Handle` or `Pointer`. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because a second argument is + provided when the return type of `f` is `void`: + + ```dart + import 'dart:ffi'; + + typedef T = Void Function(Int8); + + void f(int i) {} + + void g() { + Pointer.fromFunction(f, [!42!]); + } + ``` + + #### Common fixes + + Remove the exception value: + + ```dart + import 'dart:ffi'; + + typedef T = Void Function(Int8); + + void f(int i) {} + + void g() { + Pointer.fromFunction(f); + } + ``` INVALID_FIELD_TYPE_IN_STRUCT: problemMessage: "Fields in struct classes can't have the type '{0}'. They can only be declared as 'int', 'double', 'Array', 'Pointer', or subtype of 'Struct' or 'Union'." correctionMessage: "Try using 'int', 'double', 'Array', 'Pointer', or subtype of 'Struct' or 'Union'." comment: |- Parameters: 0: the type of the field + documentation: |- + #### Description + + The analyzer produces this diagnostic when a field in a subclass of + `Struct` has a type other than `int`, `double`, `Array`, `Pointer`, or + subtype of `Struct` or `Union`. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the field `str` has + the type `String`, which isn't one of the allowed types for fields in a + subclass of `Struct`: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + external [!String!] s; + + @Int32() + external int i; + } + ``` + + #### Common fixes + + Use one of the allowed types for the field: + + ```dart + import 'dart:ffi'; + import 'package:ffi/ffi.dart'; + + class C extends Struct { + external Pointer s; + + @Int32() + external int i; + } + ``` LEAF_CALL_MUST_NOT_RETURN_HANDLE: - problemMessage: FFI leaf call must not return a Handle. + problemMessage: "FFI leaf call can't return a 'Handle'." correctionMessage: Try changing the return type to primitive or struct. comment: No parameters. + documentation: |- + #### Description + + The analyzer produces this diagnostic when the value of the `isLeaf` + argument in an invocation of either `Pointer.asFunction` or + `DynamicLibrary.lookupFunction` is `true` and the function that would be + returned would have a return type of `Handle`. + + The analyzer also produces this diagnostic when the value of the `isLeaf` + argument in an `FfiNative` annotation is `true` and the type argument on + the annotation is a function type whose return type is `Handle`. + + In all of these cases, leaf calls are only supported for the types `bool`, + `int`, `float`, `double`, and, as a return type `void`. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the function `p` + returns a `Handle`, but the `isLeaf` argument is `true`: + + ```dart + import 'dart:ffi'; + + void f(Pointer> p) { + [!p.asFunction(isLeaf: true)!]; + } + ``` + + #### Common fixes + + If the function returns a handle, then remove the `isLeaf` argument: + + ```dart + import 'dart:ffi'; + + void f(Pointer> p) { + p.asFunction(); + } + ``` + + If the function returns one of the supported types, then correct the type + information: + + ```dart + import 'dart:ffi'; + + void f(Pointer> p) { + p.asFunction(isLeaf: true); + } + ``` LEAF_CALL_MUST_NOT_TAKE_HANDLE: - problemMessage: FFI leaf call must not take arguments of type Handle. + problemMessage: "FFI leaf call can't take arguments of type 'Handle'." correctionMessage: Try changing the argument type to primitive or struct. comment: No parameters. + documentation: |- + #### Description + + The analyzer produces this diagnostic when the value of the `isLeaf` + argument in an invocation of either `Pointer.asFunction` or + `DynamicLibrary.lookupFunction` is `true` and the function that would be + returned would have a parameter of type `Handle`. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the function `p` has a + parameter of type `Handle`, but the `isLeaf` argument is `true`: + + ```dart + import 'dart:ffi'; + + void f(Pointer> p) { + [!p.asFunction(isLeaf: true)!]; + } + ``` + + #### Common fixes + + If the function has at least one parameter of type `Handle`, then remove + the `isLeaf` argument: + + ```dart + import 'dart:ffi'; + + void f(Pointer> p) { + p.asFunction(); + } + ``` + + If none of the function's parameters are `Handle`s, then correct the type + information: + + ```dart + import 'dart:ffi'; + + void f(Pointer> p) { + p.asFunction(isLeaf: true); + } + ``` MISMATCHED_ANNOTATION_ON_STRUCT_FIELD: - problemMessage: The annotation does not match the declared type of the field. + problemMessage: "The annotation doesn't match the declared type of the field." correctionMessage: Try using a different annotation or changing the declared type to match. comment: No parameters. + documentation: |- + #### Description + + The analyzer produces this diagnostic when the annotation on a field in a + subclass of `Struct` or `Union` doesn't match the Dart type of the field. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the annotation + `Double` doesn't match the Dart type `int`: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + [!@Double()!] + external int x; + } + ``` + + #### Common fixes + + If the type of the field is correct, then change the annotation to match: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + @Int32() + external int x; + } + ``` + + If the annotation is correct, then change the type of the field to match: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + @Double() + external double x; + } + ``` MISSING_ANNOTATION_ON_STRUCT_FIELD: problemMessage: "Fields in a struct class must either have the type 'Pointer' or an annotation indicating the native type." correctionMessage: Try adding an annotation. comment: No parameters. + documentation: |- + #### Description + + The analyzer produces this diagnostic when a field in a subclass of + `Struct` or `Union` whose type requires an annotation doesn't have one. + The Dart types `int`, `double`, and `Array` are used to represent multiple + C types, and the annotation specifies which of the compatible C types the + field represents. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the field `x` doesn't + have an annotation indicating the underlying width of the integer value: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + external [!int!] x; + } + ``` + + #### Common fixes + + Add an appropriate annotation to the field: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + @Int64() + external int x; + } + ``` MISSING_EXCEPTION_VALUE: - problemMessage: "The method 'Pointer.fromFunction' must have an exceptional return value (the second argument) when the return type of the function is neither 'void', 'Handle' or 'Pointer'." + problemMessage: "The method 'Pointer.fromFunction' must have an exceptional return value (the second argument) when the return type of the function is neither 'void', 'Handle', nor 'Pointer'." correctionMessage: Try adding an exceptional return value. comment: No parameters. + documentation: |- + #### Description + + The analyzer produces this diagnostic when an invocation of the method + `Pointer.fromFunction` doesn't have a second argument (the exceptional + return value) when the type to be returned from the invocation is neither + `void`, `Handle`, nor `Pointer`. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the type returned by + `f` is expected to be an 8-bit integer but the call to `fromFunction` + doesn't include an exceptional return argument: + + ```dart + import 'dart:ffi'; + + int f(int i) => i * 2; + + void g() { + Pointer.[!fromFunction!](f); + } + ``` + + #### Common fixes + + Add an exceptional return type: + + ```dart + import 'dart:ffi'; + + int f(int i) => i * 2; + + void g() { + Pointer.fromFunction(f, 0); + } + ``` MISSING_FIELD_TYPE_IN_STRUCT: problemMessage: "Fields in struct classes must have an explicitly declared type of 'int', 'double' or 'Pointer'." correctionMessage: "Try using 'int', 'double' or 'Pointer'." comment: |- Parameters: 0: the type of the field + documentation: |- + #### Description + + The analyzer produces this diagnostic when a field in a subclass of + `Struct` or `Union` doesn't have a type annotation. Every field must have + an explicit type, and the type must either be `int`, `double`, `Pointer`, + or a subclass of either `Struct` or `Union`. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the field `str` + doesn't have a type annotation: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + external var [!str!]; + + @Int32() + external int i; + } + ``` + + #### Common fixes + + Explicitly specify the type of the field: + + ```dart + import 'dart:ffi'; + import 'package:ffi/ffi.dart'; + + class C extends Struct { + external Pointer str; + + @Int32() + external int i; + } + ``` MISSING_SIZE_ANNOTATION_CARRAY: - problemMessage: "'Array's must have exactly one 'Array' annotation." - correctionMessage: "Try adding a 'Array' annotation." + problemMessage: "Fields of type 'Array' must have exactly one 'Array' annotation." + correctionMessage: "Try adding an 'Array' annotation, or removing all but one of the annotations." comment: No parameters. + documentation: |- + #### Description + + The analyzer produces this diagnostic when a field in a subclass of either + `Struct` or `Union` has a type of `Array` but doesn't have a single + `Array` annotation indicating the dimensions of the array. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the field `a0` doesn't + have an `Array` annotation: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + external [!Array!] a0; + } + ``` + + #### Common fixes + + Ensure that there's exactly one `Array` annotation on the field: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + @Array(8) + external Array a0; + } + ``` MUST_BE_A_NATIVE_FUNCTION_TYPE: problemMessage: "The type '{0}' given to '{1}' must be a valid 'dart:ffi' native function type." correctionMessage: "Try changing the type to only use members for 'dart:ffi'." @@ -13937,6 +14705,48 @@ FfiCode: Parameters: 0: the type that should be a valid dart:ffi native type. 1: the name of the function whose invocation depends on this relationship + documentation: |- + #### Description + + The analyzer produces this diagnostic when an invocation of either + `Pointer.fromFunction` or `DynamicLibrary.lookupFunction` has a type + argument(whether explicit or inferred) that isn't a native function type. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the type `T` can be + any subclass of `Function` but the type argument for `fromFunction` is + required to be a native function type: + + ```dart + import 'dart:ffi'; + + int f(int i) => i * 2; + + class C { + void g() { + Pointer.fromFunction<[!T!]>(f, 0); + } + } + ``` + + #### Common fixes + + Use a native function type as the type argument to the invocation: + + ```dart + import 'dart:ffi'; + + int f(int i) => i * 2; + + class C { + void g() { + Pointer.fromFunction(f, 0); + } + } + ``` MUST_BE_A_SUBTYPE: problemMessage: "The type '{0}' must be a subtype of '{1}' for '{2}'." correctionMessage: Try changing one or both of the type arguments. @@ -13945,47 +14755,455 @@ FfiCode: 0: the type that should be a subtype 1: the supertype that the subtype is compared to 2: the name of the function whose invocation depends on this relationship + documentation: |- + #### Description + + The analyzer produces this diagnostic in two cases: + - In an invocation of `Pointer.fromFunction` where the type argument + (whether explicit or inferred) isn't a supertype of the type of the + function passed as the first argument to the method. + - In an invocation of `DynamicLibrary.lookupFunction` where the first type + argument isn't a supertype of the second type argument. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the type of the + function `f` (`String Function(int)`) isn't a subtype of the type + argument `T` (`Int8 Function(Int8)`): + + ```dart + import 'dart:ffi'; + + typedef T = Int8 Function(Int8); + + double f(double i) => i; + + void g() { + Pointer.fromFunction([!f!], 5.0); + } + ``` + + #### Common fixes + + If the function is correct, then change the type argument to match: + + ```dart + import 'dart:ffi'; + + typedef T = Float Function(Float); + + double f(double i) => i; + + void g() { + Pointer.fromFunction(f, 5.0); + } + ``` + + If the type argument is correct, then change the function to match: + + ```dart + import 'dart:ffi'; + + typedef T = Int8 Function(Int8); + + int f(int i) => i; + + void g() { + Pointer.fromFunction(f, 5); + } + ``` NON_CONSTANT_TYPE_ARGUMENT: - problemMessage: "The type arguments to '{0}' must be compile time constants but type parameters are not constants." + problemMessage: "The type arguments to '{0}' must be known at compile time, so they can't be type parameters." correctionMessage: Try changing the type argument to be a constant type. comment: |- Parameters: 0: the name of the function, method, or constructor having type arguments + documentation: |- + #### Description + + The analyzer produces this diagnostic when the type arguments to a method + are required to be known at compile time, but a type parameter, whose + value can't be known at compile time, is used as a type argument. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the type argument to + `Pointer.asFunction` must be known at compile time, but the type parameter + `R`, which isn't known at compile time, is being used as the type + argument: + + ```dart + import 'dart:ffi'; + + typedef T = int Function(int); + + class C { + void m(Pointer> p) { + p.asFunction<[!R!]>(); + } + } + ``` + + #### Common fixes + + Remove any uses of type parameters: + + ```dart + import 'dart:ffi'; + + class C { + void m(Pointer> p) { + p.asFunction(); + } + } + ``` NON_NATIVE_FUNCTION_TYPE_ARGUMENT_TO_POINTER: - problemMessage: "The type argument for the pointer '{0}' must be a valid 'NativeFunction' in order to use 'asFunction'." + problemMessage: "Can't invoke 'asFunction' because the function signature '{0}' for the pointer isn't a valid C function signature." correctionMessage: "Try changing the function argument in 'NativeFunction' to only use NativeTypes." comment: |- Parameters: 0: the type that should be a valid dart:ffi native type. + documentation: |- + #### Description + + The analyzer produces this diagnostic when the method `asFunction` is + invoked on a pointer to a native function, but the signature of the native + function isn't a valid C function signature. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because function signature + associated with the pointer `p` (`FNative`) isn't a valid C function + signature: + + ```dart + import 'dart:ffi'; + + typedef FNative = int Function(int); + typedef F = int Function(int); + + class C { + void f(Pointer> p) { + p.asFunction<[!F!]>(); + } + } + ``` + + #### Common fixes + + Make the `NativeFunction` signature a valid C signature: + + ```dart + import 'dart:ffi'; + + typedef FNative = Int8 Function(Int8); + typedef F = int Function(int); + + class C { + void f(Pointer> p) { + p.asFunction(); + } + } + ``` NON_POSITIVE_ARRAY_DIMENSION: problemMessage: Array dimensions must be positive numbers. correctionMessage: Try changing the input to a positive number. comment: No parameters. + documentation: |- + #### Description + + The analyzer produces this diagnostic when a dimension given in an `Array` + annotation is less than or equal to zero (`0`). + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because an array dimension of + `-1` was provided: + + ```dart + import 'dart:ffi'; + + class MyStruct extends Struct { + @Array([!-8!]) + external Array a0; + } + ``` + + #### Common fixes + + Change the dimension to be a positive integer: + + ```dart + import 'dart:ffi'; + + class MyStruct extends Struct { + @Array(8) + external Array a0; + } + ``` NON_SIZED_TYPE_ARGUMENT: - problemMessage: "Type arguments to '{0}' can't have the type '{1}'. They can only be declared as native integer, 'Float', 'Double', 'Pointer', or subtype of 'Struct', 'Union', or 'AbiSpecificInteger'." + problemMessage: "The type '{1}' isn't a valid type argument for '{0}'. The type argument must be a native integer, 'Float', 'Double', 'Pointer', or subtype of 'Struct', 'Union', or 'AbiSpecificInteger'." correctionMessage: "Try using a native integer, 'Float', 'Double', 'Pointer', or subtype of 'Struct', 'Union', or 'AbiSpecificInteger'." comment: |- Parameters: 0: the type of the field + documentation: |- + #### Description + + The analyzer produces this diagnostic when the type argument for the class + `Array` isn't one of the valid types: either a native integer, `Float`, + `Double`, `Pointer`, or subtype of `Struct`, `Union`, or + `AbiSpecificInteger`. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the type argument to + `Array` is `Void`, and `Void` isn't one of the valid types: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + @Array(8) + external Array<[!Void!]> a0; + } + ``` + + #### Common fixes + + Change the type argument to one of the valid types: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + @Array(8) + external Array a0; + } + ``` PACKED_ANNOTATION: problemMessage: "Structs must have at most one 'Packed' annotation." correctionMessage: "Try removing extra 'Packed' annotations." comment: No parameters. + documentation: |- + #### Description + + The analyzer produces this diagnostic when a subclass of `Struct` has more + than one `Packed` annotation. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the class `C`, which + is a subclass of `Struct`, has two `Packed` annotations: + + ```dart + import 'dart:ffi'; + + @Packed(1) + [!@Packed(1)!] + class C extends Struct { + external Pointer notEmpty; + } + ``` + + #### Common fixes + + Remove all but one of the annotations: + + ```dart + import 'dart:ffi'; + + @Packed(1) + class C extends Struct { + external Pointer notEmpty; + } + ``` PACKED_ANNOTATION_ALIGNMENT: problemMessage: Only packing to 1, 2, 4, 8, and 16 bytes is supported. correctionMessage: "Try changing the 'Packed' annotation alignment to 1, 2, 4, 8, or 16." comment: No parameters. + documentation: |- + #### Description + + The analyzer produces this diagnostic when the argument to the `Packed` + annotation isn't one of the allowed values: 1, 2, 4, 8, or 16. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the argument to the + `Packed` annotation (`3`) isn't one of the allowed values: + + ```dart + import 'dart:ffi'; + + @Packed([!3!]) + class C extends Struct { + external Pointer notEmpty; + } + ``` + + #### Common fixes + + Change the alignment to be one of the allowed values: + + ```dart + import 'dart:ffi'; + + @Packed(4) + class C extends Struct { + external Pointer notEmpty; + } + ``` PACKED_NESTING_NON_PACKED: - problemMessage: "Nesting the non-packed or less tightly packed struct '{0}' in a packed struct '{1}' is not supported." + problemMessage: "Nesting the non-packed or less tightly packed struct '{0}' in a packed struct '{1}' isn't supported." correctionMessage: Try packing the nested struct or packing the nested struct more tightly. comment: |- Parameters: 0: the name of the outer struct 1: the name of the struct being nested + documentation: |- + #### Description + + The analyzer produces this diagnostic when a subclass of `Struct` that is + annotated as being `Packed` declares a field whose type is also a subclass + of `Struct` and the field's type is either not packed or is packed less + tightly. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the class `Outer`, + which is a subclass of `Struct` and is packed on 1-byte boundaries, + declared a field whose type (`Inner`) is packed on 8-byte boundaries: + + ```dart + import 'dart:ffi'; + + @Packed(8) + class Inner extends Struct { + external Pointer notEmpty; + } + + @Packed(1) + class Outer extends Struct { + external Pointer notEmpty; + + external [!Inner!] nestedLooselyPacked; + } + ``` + + #### Common fixes + + If the inner struct should be packed more tightly, then change the + argument to the inner struct's `Packed` annotation: + + ```dart + import 'dart:ffi'; + + @Packed(1) + class Inner extends Struct { + external Pointer notEmpty; + } + + @Packed(1) + class Outer extends Struct { + external Pointer notEmpty; + + external Inner nestedLooselyPacked; + } + ``` + + If the outer struct should be packed less tightly, then change the + argument to the outer struct's `Packed` annotation: + + ```dart + import 'dart:ffi'; + + @Packed(8) + class Inner extends Struct { + external Pointer notEmpty; + } + + @Packed(8) + class Outer extends Struct { + external Pointer notEmpty; + + external Inner nestedLooselyPacked; + } + ``` + + If the inner struct doesn't have an annotation and should be packed, then + add an annotation. + + If the inner struct doesn't have an annotation and the outer struct + shouldn't be packed, then remove its annotation. SIZE_ANNOTATION_DIMENSIONS: problemMessage: "'Array's must have an 'Array' annotation that matches the dimensions." correctionMessage: "Try adjusting the arguments in the 'Array' annotation." comment: No parameters. + documentation: |- + #### Description + + The analyzer produces this diagnostic when the number of dimensions + specified in an `Array` annotation doesn't match the number of nested + arrays specified by the type of a field. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the field `a0` has a + type with three nested arrays, but only two dimensions are given in the + `Array` annotation: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + [!@Array(8, 8)!] + external Array>> a0; + } + ``` + + #### Common fixes + + If the type of the field is correct, then fix the annotation to have the + required number of dimensions: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + @Array(8, 8, 4) + external Array>> a0; + } + ``` + + If the type of the field is wrong, then fix the type of the field: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + @Array(8, 8) + external Array> a0; + } + ``` SUBTYPE_OF_FFI_CLASS_IN_EXTENDS: sharedName: SUBTYPE_OF_FFI_CLASS problemMessage: "The class '{0}' can't extend '{1}'." @@ -13994,6 +15212,47 @@ FfiCode: Parameters: 0: the name of the subclass 1: the name of the class being extended, implemented, or mixed in + documentation: |- + #### Description + + The analyzer produces this diagnostic when a class extends any FFI class + other than `Struct` or `Union`, or implements or mixes in any FFI class. + `Struct` and `Union` are the only FFI classes that can be subtyped, and + then only by extending them. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the class `C` extends + `Double`: + + ```dart + import 'dart:ffi'; + + class C extends [!Double!] {} + ``` + + #### Common fixes + + If the class should extend either `Struct` or `Union`, then change the + declaration of the class: + + ```dart + import 'dart:ffi'; + + class C extends Struct { + @Int32() + external int i; + } + ``` + + If the class shouldn't extend either `Struct` or `Union`, then remove any + references to FFI classes: + + ```dart + class C {} + ``` SUBTYPE_OF_FFI_CLASS_IN_IMPLEMENTS: sharedName: SUBTYPE_OF_FFI_CLASS problemMessage: "The class '{0}' can't implement '{1}'." @@ -14018,6 +15277,51 @@ FfiCode: Parameters: 0: the name of the subclass 1: the name of the class being extended, implemented, or mixed in + documentation: |- + #### Description + + The analyzer produces this diagnostic when a class extends, implements, or + mixes in a class that extends either `Struct` or `Union`. Classes can only + extend either `Struct` or `Union` directly. + + For more information about FFI, see [C interop using dart:ffi][]. + + #### Example + + The following code produces this diagnostic because the class `C` extends + `S`, and `S` extends `Struct`: + + ```dart + import 'dart:ffi'; + + class S extends Struct { + external Pointer f; + } + + class C extends [!S!] { + external Pointer g; + } + ``` + + #### Common fixes + + If you're trying to define a struct or union that shares some fields + declared by a different struct or union, then extend `Struct` or `Union` + directly and copy the shared fields: + + ```dart + import 'dart:ffi'; + + class S extends Struct { + external Pointer f; + } + + class C extends Struct { + external Pointer f; + + external Pointer g; + } + ``` SUBTYPE_OF_STRUCT_CLASS_IN_IMPLEMENTS: sharedName: SUBTYPE_OF_STRUCT_CLASS problemMessage: "The class '{0}' can't implement '{1}' because '{1}' is a subtype of 'Struct', 'Union', or 'AbiSpecificInteger'." diff --git a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart index 89c6d3f3ed7..3f5a183b185 100644 --- a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart +++ b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart @@ -313,6 +313,7 @@ class PubPackageResolutionTest extends ContextResolutionTest { void writeTestPackageConfig( PackageConfigFileBuilder config, { String? languageVersion, + bool ffi = false, bool js = false, bool meta = false, }) { @@ -324,6 +325,14 @@ class PubPackageResolutionTest extends ContextResolutionTest { languageVersion: languageVersion ?? testPackageLanguageVersion, ); + if (ffi) { + var ffiPath = '/packages/ffi'; + MockPackages.addFfiPackageFiles( + getFolder(ffiPath), + ); + config.add(name: 'ffi', rootPath: ffiPath); + } + if (js) { var jsPath = '/packages/js'; MockPackages.addJsPackageFiles( diff --git a/pkg/analyzer/test/verify_diagnostics_test.dart b/pkg/analyzer/test/verify_diagnostics_test.dart index d5ee1bf808c..873f700a478 100644 --- a/pkg/analyzer/test/verify_diagnostics_test.dart +++ b/pkg/analyzer/test/verify_diagnostics_test.dart @@ -451,6 +451,6 @@ class _SnippetTest extends PubPackageResolutionTest { ); } } - writeTestPackageConfig(packageConfigBuilder, meta: true); + writeTestPackageConfig(packageConfigBuilder, ffi: true, meta: true); } } diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md index 282257c3274..c8454b0f1c1 100644 --- a/pkg/analyzer/tool/diagnostics/diagnostics.md +++ b/pkg/analyzer/tool/diagnostics/diagnostics.md @@ -649,6 +649,101 @@ union(a, b) { } {% endprettify %} +### annotation_on_pointer_field + +_Fields in a struct class whose type is 'Pointer' shouldn't have any +annotations._ + +#### Description + +The analyzer produces this diagnostic when a field that's declared in a +subclass of `Struct` and has the type `Pointer` also has an annotation +associated with it. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the field `p`, which +has the type `Pointer` and is declared in a subclass of `Struct`, has the +annotation `@Double()`: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + [!@Double()!] + external Pointer p; +} +{% endprettify %} + +#### Common fixes + +Remove the annotations from the field: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + external Pointer p; +} +{% endprettify %} + +### argument_must_be_a_constant + +_Argument '{0}' must be a constant._ + +#### Description + +The analyzer produces this diagnostic when an invocation of either +`Pointer.asFunction` or `DynamicLibrary.lookupFunction` has an `isLeaf` +argument whose value isn't a constant expression. + +The analyzer also produces this diagnostic when the value of the +`exceptionalReturn` argument of `Pointer.fromFunction`. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the value of the +`isLeaf` argument is a parameter, and hence isn't a constant: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +int Function(int) fromPointer( + Pointer> p, bool isLeaf) { + return p.asFunction(isLeaf: [!isLeaf!]); +} +{% endprettify %} + +#### Common fixes + +If there's a suitable constant that can be used, then replace the argument +with a constant: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +const isLeaf = false; + +int Function(int) fromPointer(Pointer> p) { + return p.asFunction(isLeaf: isLeaf); +} +{% endprettify %} + +If there isn't a suitable constant, then replace the argument with a +boolean literal: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +int Function(int) fromPointer(Pointer> p) { + return p.asFunction(isLeaf: true); +} +{% endprettify %} + ### argument_type_not_assignable _The argument type '{0}' can't be assigned to the parameter type '{1}'._ @@ -2696,6 +2791,58 @@ class C { C newC() => C(); {% endprettify %} +### creation_of_struct_or_union + +_Subclasses of 'Struct' and 'Union' are backed by native memory, and can't be +instantiated by a generative constructor._ + +#### Description + +The analyzer produces this diagnostic when a subclass of either `Struct` +or `Union` is instantiated using a generative constructor. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the class `C` is being +instantiated using a generative constructor: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + @Int32() + external int a; +} + +void f() { + [!C!](); +} +{% endprettify %} + +#### Common fixes + +If you need to allocate the structure described by the class, then use the +`ffi` package to do so: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +class C extends Struct { + @Int32() + external int a; +} + +void f() { + final pointer = calloc.allocate(4); + final c = pointer.ref; + print(c); + calloc.free(pointer); +} +{% endprettify %} + ### creation_with_non_type _The name '{0}' isn't a class._ @@ -3777,6 +3924,58 @@ import 'dart:math' show min; var x = min(2, min(0, 1)); {% endprettify %} +### empty_struct + +_The class '{0}' can't be empty because it's a subclass of '{1}'._ + +#### Description + +The analyzer produces this diagnostic when a subclass of `Struct` or +`Union` doesn't have any fields. Having an empty `Struct` or `Union` +isn't supported. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the class `C`, which +extends `Struct`, doesn't declare any fields: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class [!C!] extends Struct {} +{% endprettify %} + +#### Common fixes + +If the class is intended to be a struct, then declare one or more fields: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + @Int32() + external int x; +} +{% endprettify %} + +If the class is intended to be used as a type argument to `Pointer`, then +make it a subclass of `Opaque`: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Opaque {} +{% endprettify %} + +If the class isn't intended to be a struct, then remove or change the +extends clause: + +{% prettify dart tag=pre+code %} +class C {} +{% endprettify %} + ### equal_elements_in_const_set _Two elements in a constant set literal can't be equal._ @@ -4548,6 +4747,46 @@ f() { If there are multiple cascaded accesses, you'll need to duplicate the extension override for each one. +### extra_annotation_on_struct_field + +_Fields in a struct class must have exactly one annotation indicating the native +type._ + +#### Description + +The analyzer produces this diagnostic when a field in a subclass of +`Struct` has more than one annotation describing the native type of the +field. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the field `x` has two +annotations describing the native type of the field: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + @Int32() + [!@Int16()!] + external int x; +} +{% endprettify %} + +#### Common fixes + +Remove all but one of the annotations: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; +class C extends Struct { + @Int32() + external int x; +} +{% endprettify %} + ### extra_positional_arguments _Too many positional arguments: {0} expected, but {1} found._ @@ -4625,6 +4864,46 @@ void g() { } {% endprettify %} +### extra_size_annotation_carray + +_'Array's must have exactly one 'Array' annotation._ + +#### Description + +The analyzer produces this diagnostic when a field in a subclass of +`Struct` has more than one annotation describing the size of the native +array. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the field `a0` has two +annotations that specify the size of the native array: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + @Array(4) + [!@Array(8)!] + external Array a0; +} +{% endprettify %} + +#### Common fixes + +Remove all but one of the annotations: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + @Array(8) + external Array a0; +} +{% endprettify %} + ### field_initialized_by_multiple_initializers _The field '{0}' can't be initialized twice in the same constructor._ @@ -4800,6 +5079,51 @@ class C { } {% endprettify %} +### field_initializer_in_struct + +_Constructors in subclasses of 'Struct' and 'Union' can't have field +initializers._ + +#### Description + +The analyzer produces this diagnostic when a constructor in a subclass of +either `Struct` or `Union` has one or more field initializers. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the class `C` has a +constructor with an initializer for the field `f`: + +{% prettify dart tag=pre+code %} +// @dart = 2.9 +import 'dart:ffi'; + +class C extends Struct { + @Int32() + int f; + + C() : [!f = 0!]; +} +{% endprettify %} + +#### Common fixes + +Remove the field initializer: + +{% prettify dart tag=pre+code %} +// @dart = 2.9 +import 'dart:ffi'; + +class C extends Struct { + @Int32() + int f; + + C(); +} +{% endprettify %} + ### field_initializer_not_assignable _The initializer type '{0}' can't be assigned to the field type '{1}' in a const @@ -4981,6 +5305,82 @@ class C { } {% endprettify %} +### field_in_struct_with_initializer + +_Fields in subclasses of 'Struct' and 'Union' can't have initializers._ + +#### Description + +The analyzer produces this diagnostic when a field in a subclass of +`Struct` has an initializer. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the field `p` has an +initializer: + +{% prettify dart tag=pre+code %} +// @dart = 2.9 +import 'dart:ffi'; + +class C extends Struct { + Pointer [!p!] = nullptr; +} +{% endprettify %} + +#### Common fixes + +Remove the initializer: + +{% prettify dart tag=pre+code %} +// @dart = 2.9 +import 'dart:ffi'; + +class C extends Struct { + Pointer p; +} +{% endprettify %} + +### field_must_be_external_in_struct + +_Fields of 'Struct' and 'Union' subclasses must be marked external._ + +#### Description + +The analyzer produces this diagnostic when a field in a subclass of either +`Struct` or `Union` isn't marked as being `external`. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the field `a` isn't +marked as being `external`: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + @Int16() + int [!a!]; +} +{% endprettify %} + +#### Common fixes + +Add the required `external` modifier: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + @Int16() + external int a; +} +{% endprettify %} + ### final_initialized_in_declaration_and_constructor _'{0}' is final and was given a value when it was declared, so it can't be set @@ -5369,6 +5769,42 @@ void f(dynamic list) { } {% endprettify %} +### generic_struct_subclass + +_The class '{0}' can't extend 'Struct' or 'Union' because '{0}' is generic._ + +#### Description + +The analyzer produces this diagnostic when a subclass of either `Struct` +or `Union` has a type parameter. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the class `S` defines +the type parameter `T`: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class [!S!] extends Struct { + external Pointer notEmpty; +} +{% endprettify %} + +#### Common fixes + +Remove the type parameters from the class: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class S extends Struct { + external Pointer notEmpty; +} +{% endprettify %} + ### getter_not_subtype_setter_types _The return type of getter '{0}' is '{1}' which isn't a subtype of the type @@ -6565,6 +7001,53 @@ dependencies: version: ^1.4.0 ``` +### invalid_exception_value + +_The method 'Pointer.fromFunction' can't have an exceptional return value (the +second argument) when the return type of the function is either 'void', 'Handle' or 'Pointer'._ + +#### Description + +The analyzer produces this diagnostic when an invocation of the method +`Pointer.fromFunction` has a second argument (the exceptional return +value) and the type to be returned from the invocation is either `void`, +`Handle` or `Pointer`. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because a second argument is +provided when the return type of `f` is `void`: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +typedef T = Void Function(Int8); + +void f(int i) {} + +void g() { + Pointer.fromFunction(f, [!42!]); +} +{% endprettify %} + +#### Common fixes + +Remove the exception value: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +typedef T = Void Function(Int8); + +void f(int i) {} + +void g() { + Pointer.fromFunction(f); +} +{% endprettify %} + ### invalid_extension_argument_count _Extension overrides must have exactly one argument: the value of 'this' in the @@ -6677,6 +7160,52 @@ class C { } {% endprettify %} +### invalid_field_type_in_struct + +_Fields in struct classes can't have the type '{0}'. They can only be declared +as 'int', 'double', 'Array', 'Pointer', or subtype of 'Struct' or 'Union'._ + +#### Description + +The analyzer produces this diagnostic when a field in a subclass of +`Struct` has a type other than `int`, `double`, `Array`, `Pointer`, or +subtype of `Struct` or `Union`. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the field `str` has +the type `String`, which isn't one of the allowed types for fields in a +subclass of `Struct`: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + external [!String!] s; + + @Int32() + external int i; +} +{% endprettify %} + +#### Common fixes + +Use one of the allowed types for the field: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +class C extends Struct { + external Pointer s; + + @Int32() + external int i; +} +{% endprettify %} + ### invalid_implementation_override _'{1}.{0}' ('{2}') isn't a valid concrete implementation of '{3}.{0}' ('{4}')._ @@ -7748,6 +8277,112 @@ int f() { } {% endprettify %} +### leaf_call_must_not_return_handle + +_FFI leaf call can't return a 'Handle'._ + +#### Description + +The analyzer produces this diagnostic when the value of the `isLeaf` +argument in an invocation of either `Pointer.asFunction` or +`DynamicLibrary.lookupFunction` is `true` and the function that would be +returned would have a return type of `Handle`. + +The analyzer also produces this diagnostic when the value of the `isLeaf` +argument in an `FfiNative` annotation is `true` and the type argument on +the annotation is a function type whose return type is `Handle`. + +In all of these cases, leaf calls are only supported for the types `bool`, +`int`, `float`, `double`, and, as a return type `void`. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the function `p` +returns a `Handle`, but the `isLeaf` argument is `true`: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +void f(Pointer> p) { + [!p.asFunction(isLeaf: true)!]; +} +{% endprettify %} + +#### Common fixes + +If the function returns a handle, then remove the `isLeaf` argument: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +void f(Pointer> p) { + p.asFunction(); +} +{% endprettify %} + +If the function returns one of the supported types, then correct the type +information: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +void f(Pointer> p) { + p.asFunction(isLeaf: true); +} +{% endprettify %} + +### leaf_call_must_not_take_handle + +_FFI leaf call can't take arguments of type 'Handle'._ + +#### Description + +The analyzer produces this diagnostic when the value of the `isLeaf` +argument in an invocation of either `Pointer.asFunction` or +`DynamicLibrary.lookupFunction` is `true` and the function that would be +returned would have a parameter of type `Handle`. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the function `p` has a +parameter of type `Handle`, but the `isLeaf` argument is `true`: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +void f(Pointer> p) { + [!p.asFunction(isLeaf: true)!]; +} +{% endprettify %} + +#### Common fixes + +If the function has at least one parameter of type `Handle`, then remove +the `isLeaf` argument: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +void f(Pointer> p) { + p.asFunction(); +} +{% endprettify %} + +If none of the function's parameters are `Handle`s, then correct the type +information: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +void f(Pointer> p) { + p.asFunction(isLeaf: true); +} +{% endprettify %} + ### list_element_type_not_assignable _The element type '{0}' can't be assigned to the list type '{1}'._ @@ -8027,6 +8662,96 @@ If the type of the value is correct, then change the value type of the map: var m = {'a' : 2}; {% endprettify %} +### mismatched_annotation_on_struct_field + +_The annotation doesn't match the declared type of the field._ + +#### Description + +The analyzer produces this diagnostic when the annotation on a field in a +subclass of `Struct` or `Union` doesn't match the Dart type of the field. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the annotation +`Double` doesn't match the Dart type `int`: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + [!@Double()!] + external int x; +} +{% endprettify %} + +#### Common fixes + +If the type of the field is correct, then change the annotation to match: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + @Int32() + external int x; +} +{% endprettify %} + +If the annotation is correct, then change the type of the field to match: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + @Double() + external double x; +} +{% endprettify %} + +### missing_annotation_on_struct_field + +_Fields in a struct class must either have the type 'Pointer' or an annotation +indicating the native type._ + +#### Description + +The analyzer produces this diagnostic when a field in a subclass of +`Struct` or `Union` whose type requires an annotation doesn't have one. +The Dart types `int`, `double`, and `Array` are used to represent multiple +C types, and the annotation specifies which of the compatible C types the +field represents. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the field `x` doesn't +have an annotation indicating the underlying width of the integer value: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + external [!int!] x; +} +{% endprettify %} + +#### Common fixes + +Add an appropriate annotation to the field: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + @Int64() + external int x; +} +{% endprettify %} + ### missing_dart_library _Required library '{0}' is missing._ @@ -8158,6 +8883,96 @@ void f(E e) { } {% endprettify %} +### missing_exception_value + +_The method 'Pointer.fromFunction' must have an exceptional return value (the +second argument) when the return type of the function is neither 'void', 'Handle', nor 'Pointer'._ + +#### Description + +The analyzer produces this diagnostic when an invocation of the method +`Pointer.fromFunction` doesn't have a second argument (the exceptional +return value) when the type to be returned from the invocation is neither +`void`, `Handle`, nor `Pointer`. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the type returned by +`f` is expected to be an 8-bit integer but the call to `fromFunction` +doesn't include an exceptional return argument: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +int f(int i) => i * 2; + +void g() { + Pointer.[!fromFunction!](f); +} +{% endprettify %} + +#### Common fixes + +Add an exceptional return type: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +int f(int i) => i * 2; + +void g() { + Pointer.fromFunction(f, 0); +} +{% endprettify %} + +### missing_field_type_in_struct + +_Fields in struct classes must have an explicitly declared type of 'int', +'double' or 'Pointer'._ + +#### Description + +The analyzer produces this diagnostic when a field in a subclass of +`Struct` or `Union` doesn't have a type annotation. Every field must have +an explicit type, and the type must either be `int`, `double`, `Pointer`, +or a subclass of either `Struct` or `Union`. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the field `str` +doesn't have a type annotation: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + external var [!str!]; + + @Int32() + external int i; +} +{% endprettify %} + +#### Common fixes + +Explicitly specify the type of the field: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +class C extends Struct { + external Pointer str; + + @Int32() + external int i; +} +{% endprettify %} + ### missing_name _The 'name' field is required but missing._ @@ -8289,6 +9104,44 @@ int [!f!](int x) { Add a `return` statement that makes the return value explicit, even if `null` is the appropriate value. +### missing_size_annotation_carray + +_Fields of type 'Array' must have exactly one 'Array' annotation._ + +#### Description + +The analyzer produces this diagnostic when a field in a subclass of either +`Struct` or `Union` has a type of `Array` but doesn't have a single +`Array` annotation indicating the dimensions of the array. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the field `a0` doesn't +have an `Array` annotation: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + external [!Array!] a0; +} +{% endprettify %} + +#### Common fixes + +Ensure that there's exactly one `Array` annotation on the field: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + @Array(8) + external Array a0; +} +{% endprettify %} + ### mixin_application_concrete_super_invoked_member_type _The super-invoked member '{0}' has the type '{1}', and the concrete member in @@ -8820,6 +9673,115 @@ class B extends A { } {% endprettify %} +### must_be_a_native_function_type + +_The type '{0}' given to '{1}' must be a valid 'dart:ffi' native function type._ + +#### Description + +The analyzer produces this diagnostic when an invocation of either +`Pointer.fromFunction` or `DynamicLibrary.lookupFunction` has a type +argument(whether explicit or inferred) that isn't a native function type. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the type `T` can be +any subclass of `Function` but the type argument for `fromFunction` is +required to be a native function type: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +int f(int i) => i * 2; + +class C { + void g() { + Pointer.fromFunction<[!T!]>(f, 0); + } +} +{% endprettify %} + +#### Common fixes + +Use a native function type as the type argument to the invocation: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +int f(int i) => i * 2; + +class C { + void g() { + Pointer.fromFunction(f, 0); + } +} +{% endprettify %} + +### must_be_a_subtype + +_The type '{0}' must be a subtype of '{1}' for '{2}'._ + +#### Description + +The analyzer produces this diagnostic in two cases: +- In an invocation of `Pointer.fromFunction` where the type argument + (whether explicit or inferred) isn't a supertype of the type of the + function passed as the first argument to the method. +- In an invocation of `DynamicLibrary.lookupFunction` where the first type + argument isn't a supertype of the second type argument. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the type of the +function `f` (`String Function(int)`) isn't a subtype of the type +argument `T` (`Int8 Function(Int8)`): + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +typedef T = Int8 Function(Int8); + +double f(double i) => i; + +void g() { + Pointer.fromFunction([!f!], 5.0); +} +{% endprettify %} + +#### Common fixes + +If the function is correct, then change the type argument to match: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +typedef T = Float Function(Float); + +double f(double i) => i; + +void g() { + Pointer.fromFunction(f, 5.0); +} +{% endprettify %} + +If the type argument is correct, then change the function to match: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +typedef T = Int8 Function(Int8); + +int f(int i) => i; + +void g() { + Pointer.fromFunction(f, 5); +} +{% endprettify %} + ### must_be_immutable _This class (or a class that this class inherits from) is marked as @@ -9640,6 +10602,52 @@ var i = 0; var s = {i}; {% endprettify %} +### non_constant_type_argument + +_The type arguments to '{0}' must be known at compile time, so they can't be +type parameters._ + +#### Description + +The analyzer produces this diagnostic when the type arguments to a method +are required to be known at compile time, but a type parameter, whose +value can't be known at compile time, is used as a type argument. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the type argument to +`Pointer.asFunction` must be known at compile time, but the type parameter +`R`, which isn't known at compile time, is being used as the type +argument: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +typedef T = int Function(int); + +class C { + void m(Pointer> p) { + p.asFunction<[!R!]>(); + } +} +{% endprettify %} + +#### Common fixes + +Remove any uses of type parameters: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C { + void m(Pointer> p) { + p.asFunction(); + } +} +{% endprettify %} + ### non_const_call_to_literal_constructor _This instance creation must be 'const', because the {0} constructor is marked @@ -9728,6 +10736,134 @@ class B extends A { If the generative constructor is the unnamed constructor, and if there are no arguments being passed to it, then you can remove the super invocation. +### non_native_function_type_argument_to_pointer + +_Can't invoke 'asFunction' because the function signature '{0}' for the pointer +isn't a valid C function signature._ + +#### Description + +The analyzer produces this diagnostic when the method `asFunction` is +invoked on a pointer to a native function, but the signature of the native +function isn't a valid C function signature. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because function signature +associated with the pointer `p` (`FNative`) isn't a valid C function +signature: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +typedef FNative = int Function(int); +typedef F = int Function(int); + +class C { + void f(Pointer> p) { + p.asFunction<[!F!]>(); + } +} +{% endprettify %} + +#### Common fixes + +Make the `NativeFunction` signature a valid C signature: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +typedef FNative = Int8 Function(Int8); +typedef F = int Function(int); + +class C { + void f(Pointer> p) { + p.asFunction(); + } +} +{% endprettify %} + +### non_positive_array_dimension + +_Array dimensions must be positive numbers._ + +#### Description + +The analyzer produces this diagnostic when a dimension given in an `Array` +annotation is less than or equal to zero (`0`). + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because an array dimension of +`-1` was provided: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class MyStruct extends Struct { + @Array([!-8!]) + external Array a0; +} +{% endprettify %} + +#### Common fixes + +Change the dimension to be a positive integer: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class MyStruct extends Struct { + @Array(8) + external Array a0; +} +{% endprettify %} + +### non_sized_type_argument + +_The type '{1}' isn't a valid type argument for '{0}'. The type argument must be +a native integer, 'Float', 'Double', 'Pointer', or subtype of 'Struct', 'Union', or 'AbiSpecificInteger'._ + +#### Description + +The analyzer produces this diagnostic when the type argument for the class +`Array` isn't one of the valid types: either a native integer, `Float`, +`Double`, `Pointer`, or subtype of `Struct`, `Union`, or +`AbiSpecificInteger`. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the type argument to +`Array` is `Void`, and `Void` isn't one of the valid types: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + @Array(8) + external Array<[!Void!]> a0; +} +{% endprettify %} + +#### Common fixes + +Change the type argument to one of the valid types: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + @Array(8) + external Array a0; +} +{% endprettify %} + ### non_sync_factory _Factory bodies can't use 'async', 'async*', or 'sync*'._ @@ -10720,6 +11856,165 @@ superclass, then consider removing the member from the subclass. If the member can't be removed, then remove the annotation. +### packed_annotation + +_Structs must have at most one 'Packed' annotation._ + +#### Description + +The analyzer produces this diagnostic when a subclass of `Struct` has more +than one `Packed` annotation. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the class `C`, which +is a subclass of `Struct`, has two `Packed` annotations: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +@Packed(1) +[!@Packed(1)!] +class C extends Struct { + external Pointer notEmpty; +} +{% endprettify %} + +#### Common fixes + +Remove all but one of the annotations: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +@Packed(1) +class C extends Struct { + external Pointer notEmpty; +} +{% endprettify %} + +### packed_annotation_alignment + +_Only packing to 1, 2, 4, 8, and 16 bytes is supported._ + +#### Description + +The analyzer produces this diagnostic when the argument to the `Packed` +annotation isn't one of the allowed values: 1, 2, 4, 8, or 16. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the argument to the +`Packed` annotation (`3`) isn't one of the allowed values: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +@Packed([!3!]) +class C extends Struct { + external Pointer notEmpty; +} +{% endprettify %} + +#### Common fixes + +Change the alignment to be one of the allowed values: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +@Packed(4) +class C extends Struct { + external Pointer notEmpty; +} +{% endprettify %} + +### packed_nesting_non_packed + +_Nesting the non-packed or less tightly packed struct '{0}' in a packed struct +'{1}' isn't supported._ + +#### Description + +The analyzer produces this diagnostic when a subclass of `Struct` that is +annotated as being `Packed` declares a field whose type is also a subclass +of `Struct` and the field's type is either not packed or is packed less +tightly. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the class `Outer`, +which is a subclass of `Struct` and is packed on 1-byte boundaries, +declared a field whose type (`Inner`) is packed on 8-byte boundaries: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +@Packed(8) +class Inner extends Struct { + external Pointer notEmpty; +} + +@Packed(1) +class Outer extends Struct { + external Pointer notEmpty; + + external [!Inner!] nestedLooselyPacked; +} +{% endprettify %} + +#### Common fixes + +If the inner struct should be packed more tightly, then change the +argument to the inner struct's `Packed` annotation: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +@Packed(1) +class Inner extends Struct { + external Pointer notEmpty; +} + +@Packed(1) +class Outer extends Struct { + external Pointer notEmpty; + + external Inner nestedLooselyPacked; +} +{% endprettify %} + +If the outer struct should be packed less tightly, then change the +argument to the outer struct's `Packed` annotation: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +@Packed(8) +class Inner extends Struct { + external Pointer notEmpty; +} + +@Packed(8) +class Outer extends Struct { + external Pointer notEmpty; + + external Inner nestedLooselyPacked; +} +{% endprettify %} + +If the inner struct doesn't have an annotation and should be packed, then +add an annotation. + +If the inner struct doesn't have an annotation and the outer struct +shouldn't be packed, then remove its annotation. + ### part_of_different_library _Expected this library to be part of '{0}', not '{1}'._ @@ -12561,6 +13856,58 @@ import 'dart:convert' as convert; var y = convert.json.encode(x.min(0, 1)); {% endprettify %} +### size_annotation_dimensions + +_'Array's must have an 'Array' annotation that matches the dimensions._ + +#### Description + +The analyzer produces this diagnostic when the number of dimensions +specified in an `Array` annotation doesn't match the number of nested +arrays specified by the type of a field. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the field `a0` has a +type with three nested arrays, but only two dimensions are given in the +`Array` annotation: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + [!@Array(8, 8)!] + external Array>> a0; +} +{% endprettify %} + +#### Common fixes + +If the type of the field is correct, then fix the annotation to have the +required number of dimensions: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + @Array(8, 8, 4) + external Array>> a0; +} +{% endprettify %} + +If the type of the field is wrong, then fix the type of the field: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + @Array(8, 8) + external Array> a0; +} +{% endprettify %} + ### static_access_to_instance_member _Instance member '{0}' can't be accessed using static access._ @@ -12724,6 +14071,55 @@ type, and possibly the whole clause: class B {} {% endprettify %} +### subtype_of_ffi_class + +_The class '{0}' can't extend '{1}'._ + +_The class '{0}' can't implement '{1}'._ + +_The class '{0}' can't mix in '{1}'._ + +#### Description + +The analyzer produces this diagnostic when a class extends any FFI class +other than `Struct` or `Union`, or implements or mixes in any FFI class. +`Struct` and `Union` are the only FFI classes that can be subtyped, and +then only by extending them. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the class `C` extends +`Double`: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends [!Double!] {} +{% endprettify %} + +#### Common fixes + +If the class should extend either `Struct` or `Union`, then change the +declaration of the class: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class C extends Struct { + @Int32() + external int i; +} +{% endprettify %} + +If the class shouldn't extend either `Struct` or `Union`, then remove any +references to FFI classes: + +{% prettify dart tag=pre+code %} +class C {} +{% endprettify %} + ### subtype_of_sealed_class _The class '{0}' shouldn't be extended, mixed in, or implemented because it's @@ -12775,6 +14171,62 @@ If the class needs to be a subtype of the sealed class, then either change the sealed class so that it's no longer sealed or move the subclass into the same package as the sealed class. +### subtype_of_struct_class + +_The class '{0}' can't extend '{1}' because '{1}' is a subtype of 'Struct', +'Union', or 'AbiSpecificInteger'._ + +_The class '{0}' can't implement '{1}' because '{1}' is a subtype of 'Struct', +'Union', or 'AbiSpecificInteger'._ + +_The class '{0}' can't mix in '{1}' because '{1}' is a subtype of 'Struct', +'Union', or 'AbiSpecificInteger'._ + +#### Description + +The analyzer produces this diagnostic when a class extends, implements, or +mixes in a class that extends either `Struct` or `Union`. Classes can only +extend either `Struct` or `Union` directly. + +For more information about FFI, see [C interop using dart:ffi][]. + +#### Example + +The following code produces this diagnostic because the class `C` extends +`S`, and `S` extends `Struct`: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class S extends Struct { + external Pointer f; +} + +class C extends [!S!] { + external Pointer g; +} +{% endprettify %} + +#### Common fixes + +If you're trying to define a struct or union that shares some fields +declared by a different struct or union, then extend `Struct` or `Union` +directly and copy the shared fields: + +{% prettify dart tag=pre+code %} +import 'dart:ffi'; + +class S extends Struct { + external Pointer f; +} + +class C extends Struct { + external Pointer f; + + external Pointer g; +} +{% endprettify %} + ### supertype_expands_to_type_parameter _A type alias that expands to a type parameter can't be implemented._