Version 2.17.0-54.0.dev

Merge commit '627326aec14f52f9fc9015ad22bd2810635e006d' into 'dev'
diff --git a/DEPS b/DEPS
index cfd0829..2d8c940 100644
--- a/DEPS
+++ b/DEPS
@@ -133,7 +133,7 @@
   "mime_rev": "c931f4bed87221beaece356494b43731445ce7b8",
   "mockito_rev": "d39ac507483b9891165e422ec98d9fb480037c8b",
   "oauth2_rev": "7cd3284049fe5badbec9f2bea2afc41d14c01057",
-  "package_config_rev": "fb736aa12316dd2d882b202a438a6946a4b4bea0",
+  "package_config_rev": "8731bf10b5375542792a32a0f7c8a6f370583d96",
   "path_rev": "baedce9d2ca11ea2cdf54395a74eb038087777a4",
   "pedantic_rev": "66f2f6c27581c7936482e83be80b27be2719901c",
   "platform_rev": "1ffad63428bbd1b3ecaa15926bacfb724023648c",
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
index 1c50b6a..5841315 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
@@ -79,6 +79,10 @@
           var request = new ExecuteDefinitionsPhaseRequest.deserialize(deserializer, zoneId);
           (await _executeDefinitionsPhase(request, sendRequest)).serialize(serializer);
           break;
+        case MessageType.executeTypesPhaseRequest:
+          var request = new ExecuteTypesPhaseRequest.deserialize(deserializer, zoneId);
+          (await _executeTypesPhase(request, sendRequest)).serialize(serializer);
+          break;
         case MessageType.response:
           var response = new SerializableResponse.deserialize(deserializer, zoneId);
           _responseCompleters.remove(response.requestId)!.complete(response);
@@ -135,6 +139,32 @@
   }
 }
 
+Future<SerializableResponse> _executeTypesPhase(
+    ExecuteTypesPhaseRequest request,
+    Future<Response> Function(Request request) sendRequest) async {
+  try {
+    Macro? instance = _macroInstances[request.macro];
+    if (instance == null) {
+      throw new StateError('Unrecognized macro instance \${request.macro}\\n'
+          'Known instances: \$_macroInstances)');
+    }
+
+    var result = await executeTypesMacro(instance, request.declaration);
+    return new SerializableResponse(
+        responseType: MessageType.macroExecutionResult,
+        response: result,
+        requestId: request.id,
+        serializationZoneId: request.serializationZoneId);
+  } catch (e, s) {
+    return new SerializableResponse(
+      responseType: MessageType.error,
+      error: e.toString(),
+      stackTrace: s.toString(),
+      requestId: request.id,
+      serializationZoneId: request.serializationZoneId);
+  }
+}
+
 Future<SerializableResponse> _executeDeclarationsPhase(
     ExecuteDeclarationsPhaseRequest request,
     Future<Response> Function(Request request) sendRequest) async {
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/builder_impls.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/builder_impls.dart
index 59a6376..577e7bf 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/builder_impls.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/builder_impls.dart
@@ -24,6 +24,13 @@
       : _augmentations = parentAugmentations ?? [];
 }
 
+class TypeBuilderImpl extends TypeBuilderBase implements TypeBuilder {
+  @override
+  void declareType(DeclarationCode typeDeclaration) {
+    _augmentations.add(typeDeclaration);
+  }
+}
+
 /// Base class for all [DeclarationBuilder]s.
 class DeclarationBuilderBase extends TypeBuilderBase
     implements ClassIntrospector, TypeResolver {
@@ -66,10 +73,8 @@
 class DeclarationBuilderImpl extends DeclarationBuilderBase
     implements DeclarationBuilder {
   DeclarationBuilderImpl(
-      ClassIntrospector classIntrospector, TypeResolver typeResolver,
-      {List<DeclarationCode>? parentAugmentations})
-      : super(classIntrospector, typeResolver,
-            parentAugmentations: parentAugmentations);
+      ClassIntrospector classIntrospector, TypeResolver typeResolver)
+      : super(classIntrospector, typeResolver);
 
   @override
   void declareInLibrary(DeclarationCode declaration) {
@@ -82,10 +87,8 @@
   final TypeAnnotation definingClass;
 
   ClassMemberDeclarationBuilderImpl(this.definingClass,
-      ClassIntrospector classIntrospector, TypeResolver typeResolver,
-      {List<DeclarationCode>? parentAugmentations})
-      : super(classIntrospector, typeResolver,
-            parentAugmentations: parentAugmentations);
+      ClassIntrospector classIntrospector, TypeResolver typeResolver)
+      : super(classIntrospector, typeResolver);
 
   @override
   void declareInClass(DeclarationCode declaration) {
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/execute_macro.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/execute_macro.dart
index f94470d..4448b1e 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/execute_macro.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/execute_macro.dart
@@ -6,6 +6,38 @@
 import 'package:_fe_analyzer_shared/src/macros/executor_shared/builder_impls.dart';
 import 'package:_fe_analyzer_shared/src/macros/api.dart';
 
+/// Runs [macro] in the types phase and returns a  [MacroExecutionResult].
+Future<MacroExecutionResult> executeTypesMacro(
+    Macro macro, Declaration declaration) async {
+  TypeBuilderImpl builder = new TypeBuilderImpl();
+  if (declaration is FunctionDeclaration) {
+    if (macro is ConstructorTypesMacro &&
+        declaration is ConstructorDeclaration) {
+      await macro.buildTypesForConstructor(declaration, builder);
+      return builder.result;
+    } else if (macro is MethodTypesMacro && declaration is MethodDeclaration) {
+      await macro.buildTypesForMethod(declaration, builder);
+      return builder.result;
+    } else if (macro is FunctionTypesMacro) {
+      await macro.buildTypesForFunction(declaration, builder);
+      return builder.result;
+    }
+  } else if (declaration is VariableDeclaration) {
+    if (macro is FieldTypesMacro && declaration is FieldDeclaration) {
+      await macro.buildTypesForField(declaration, builder);
+      return builder.result;
+    } else if (macro is VariableTypesMacro) {
+      await macro.buildTypesForVariable(declaration, builder);
+      return builder.result;
+    }
+  } else if (macro is ClassTypesMacro && declaration is ClassDeclaration) {
+    await macro.buildTypesForClass(declaration, builder);
+    return builder.result;
+  }
+  throw new UnsupportedError('Unsupported macro type or invalid declaration:\n'
+      'macro: $macro\ndeclaration: $declaration');
+}
+
 /// Runs [macro] in the declaration phase and returns a  [MacroExecutionResult].
 Future<MacroExecutionResult> executeDeclarationsMacro(
     Macro macro,
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart
index 3eae69c..00b3787 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart
@@ -237,6 +237,32 @@
   }
 }
 
+/// A request to execute a macro on a particular declaration in the types phase.
+class ExecuteTypesPhaseRequest extends Request {
+  final MacroInstanceIdentifier macro;
+  final DeclarationImpl declaration;
+
+  ExecuteTypesPhaseRequest(this.macro, this.declaration,
+      {required int serializationZoneId})
+      : super(serializationZoneId: serializationZoneId);
+
+  /// When deserializing we have already consumed the message type, so we don't
+  /// consume it again.
+  ExecuteTypesPhaseRequest.deserialize(
+      Deserializer deserializer, int serializationZoneId)
+      : macro = new MacroInstanceIdentifierImpl.deserialize(deserializer),
+        declaration = RemoteInstance.deserialize(deserializer),
+        super.deserialize(deserializer, serializationZoneId);
+
+  void serialize(Serializer serializer) {
+    serializer.addNum(MessageType.executeTypesPhaseRequest.index);
+    macro.serialize(serializer);
+    declaration.serialize(serializer);
+
+    super.serialize(serializer);
+  }
+}
+
 /// A request to execute a macro on a particular declaration in the definition
 /// phase.
 class ExecuteDeclarationsPhaseRequest extends Request {
@@ -664,6 +690,7 @@
   error,
   executeDeclarationsPhaseRequest,
   executeDefinitionsPhaseRequest,
+  executeTypesPhaseRequest,
   instantiateMacroRequest,
   isExactlyTypeRequest,
   isSubtypeOfRequest,
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/isolated_executor/isolated_executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/isolated_executor/isolated_executor.dart
index 9194c7f..210469e 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/isolated_executor/isolated_executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/isolated_executor/isolated_executor.dart
@@ -420,10 +420,9 @@
 
   @override
   Future<MacroExecutionResult> executeTypesPhase(
-      MacroInstanceIdentifier macro, Declaration declaration) {
-    // TODO: implement executeTypesPhase
-    throw new UnimplementedError();
-  }
+          MacroInstanceIdentifier macro, DeclarationImpl declaration) =>
+      _sendRequest((zoneId) => new ExecuteTypesPhaseRequest(macro, declaration,
+          serializationZoneId: zoneId));
 
   @override
   Future<MacroInstanceIdentifier> instantiateMacro(
diff --git a/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart b/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart
index 81aa97f..ce7d844 100644
--- a/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart
@@ -212,6 +212,35 @@
     var testTypeDeclarationResolver =
         TestTypeDeclarationResolver({myClassStaticType: myClass});
 
+    group('in the types phase', () {
+      test('on methods', () async {
+        var result = await executor.executeTypesPhase(instanceId, myMethod);
+        expect(result.augmentations.single.debugString().toString(),
+            equalsIgnoringWhitespace('class GeneratedByMyMethod {}'));
+      });
+
+      test('on constructors', () async {
+        var result =
+            await executor.executeTypesPhase(instanceId, myConstructor);
+        expect(result.augmentations.single.debugString().toString(),
+            equalsIgnoringWhitespace('class GeneratedByMyConstructor {}'));
+      });
+
+      test('on fields', () async {
+        var result = await executor.executeTypesPhase(instanceId, myField);
+        expect(result.augmentations.single.debugString().toString(),
+            equalsIgnoringWhitespace('class GeneratedByMyField {}'));
+      });
+
+      test('on classes', () async {
+        var result = await executor.executeTypesPhase(instanceId, myClass);
+        expect(
+            result.augmentations.single.debugString().toString(),
+            equalsIgnoringWhitespace(
+                'class MyClassBuilder implements Builder<MyClass> {}'));
+      });
+    });
+
     group('in the declaration phase', () {
       test('on methods', () async {
         var result = await executor.executeDeclarationsPhase(
diff --git a/pkg/_fe_analyzer_shared/test/macros/isolated_executor/simple_macro.dart b/pkg/_fe_analyzer_shared/test/macros/isolated_executor/simple_macro.dart
index b5681aa..222e1cd 100644
--- a/pkg/_fe_analyzer_shared/test/macros/isolated_executor/simple_macro.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/isolated_executor/simple_macro.dart
@@ -14,16 +14,22 @@
 /// to validate the introspection APIs work as expected.
 class SimpleMacro
     implements
+        ClassTypesMacro,
         ClassDeclarationsMacro,
         ClassDefinitionMacro,
+        ConstructorTypesMacro,
         ConstructorDeclarationsMacro,
         ConstructorDefinitionMacro,
+        FieldTypesMacro,
         FieldDeclarationsMacro,
         FieldDefinitionMacro,
+        FunctionTypesMacro,
         FunctionDeclarationsMacro,
         FunctionDefinitionMacro,
+        MethodTypesMacro,
         MethodDeclarationsMacro,
         MethodDefinitionMacro,
+        VariableTypesMacro,
         VariableDeclarationsMacro,
         VariableDefinitionMacro {
   final int? x;
@@ -236,6 +242,77 @@
       initializer: variable.initializer,
     );
   }
+
+  @override
+  FutureOr<void> buildTypesForClass(
+      ClassDeclaration clazz, TypeBuilder builder) {
+    List<Object> _buildTypeParam(
+        TypeParameterDeclaration typeParam, bool isFirst) {
+      return [
+        if (!isFirst) ', ',
+        typeParam.name,
+        if (typeParam.bounds != null) ...[
+          ' extends ',
+          typeParam.bounds!,
+        ]
+      ];
+    }
+
+    builder.declareType(DeclarationCode.fromParts([
+      'class ${clazz.name}Builder',
+      if (clazz.typeParameters.isNotEmpty) ...[
+        '<',
+        ..._buildTypeParam(clazz.typeParameters.first, true),
+        for (var typeParam in clazz.typeParameters.skip(1))
+          ..._buildTypeParam(typeParam, false),
+        '>',
+      ],
+      ' implements Builder<',
+      clazz.type,
+      if (clazz.typeParameters.isNotEmpty) ...[
+        '<',
+        clazz.typeParameters.first,
+        for (var typeParam in clazz.typeParameters) ', ${typeParam.name}',
+        '>',
+      ],
+      '> {}'
+    ]));
+  }
+
+  @override
+  FutureOr<void> buildTypesForConstructor(
+      ConstructorDeclaration constructor, TypeBuilder builder) {
+    builder.declareType(DeclarationCode.fromString(
+        'class GeneratedBy${constructor.name.capitalize()} {}'));
+  }
+
+  @override
+  FutureOr<void> buildTypesForField(
+      FieldDeclaration field, TypeBuilder builder) {
+    builder.declareType(DeclarationCode.fromString(
+        'class GeneratedBy${field.name.capitalize()} {}'));
+  }
+
+  @override
+  FutureOr<void> buildTypesForFunction(
+      FunctionDeclaration function, TypeBuilder builder) {
+    builder.declareType(DeclarationCode.fromString(
+        'class GeneratedBy${function.name.capitalize()} {}'));
+  }
+
+  @override
+  FutureOr<void> buildTypesForMethod(
+      MethodDeclaration method, TypeBuilder builder) {
+    builder.declareType(DeclarationCode.fromString(
+        'class GeneratedBy${method.name.capitalize()} {}'));
+  }
+
+  @override
+  FutureOr<void> buildTypesForVariable(
+      VariableDeclaration variable, TypeBuilder builder) {
+    builder.declareType(DeclarationCode.fromString(
+        'class GeneratedBy${variable.name.capitalize()} {}'));
+  }
 }
 
 FunctionBodyCode _buildFunctionAugmentation(FunctionDeclaration function) =>
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 fc3a932..873531a 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 @@
   /**
    * 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<Int8> p;
+  // }
+  // ```
+  //
+  // #### Common fixes
+  //
+  // Remove the annotations from the field:
+  //
+  // ```dart
+  // import 'dart:ffi';
+  //
+  // class C extends Struct {
+  //   external Pointer<Int8> 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 @@
    * 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<NativeFunction<Int8 Function(Int8)>> 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<NativeFunction<Int8 Function(Int8)>> 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<NativeFunction<Int8 Function(Int8)>> 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 @@
   /**
    * 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<C>(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 @@
    * 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 @@
   /**
    * 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 @@
   /**
    * 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<Uint8> a0;
+  // }
+  // ```
+  //
+  // #### Common fixes
+  //
+  // Remove all but one of the annotations:
+  //
+  // ```dart
+  // import 'dart:ffi';
+  //
+  // class C extends Struct {
+  //   @Array(8)
+  //   external Array<Uint8> 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 @@
   /**
    * 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 @@
   /**
    * 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 @@
   /**
    * 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 @@
    * 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!]<T> 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<T>(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<T>(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 @@
    * 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<Utf8> 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 @@
   /**
    * 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<NativeFunction<Handle Function()>> p) {
+  //   [!p.asFunction<Object Function()>(isLeaf: true)!];
+  // }
+  // ```
+  //
+  // #### Common fixes
+  //
+  // If the function returns a handle, then remove the `isLeaf` argument:
+  //
+  // ```dart
+  // import 'dart:ffi';
+  //
+  // void f(Pointer<NativeFunction<Handle Function()>> p) {
+  //   p.asFunction<Object Function()>();
+  // }
+  // ```
+  //
+  // If the function returns one of the supported types, then correct the type
+  // information:
+  //
+  // ```dart
+  // import 'dart:ffi';
+  //
+  // void f(Pointer<NativeFunction<Int32 Function()>> p) {
+  //   p.asFunction<int Function()>(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<NativeFunction<Void Function(Handle)>> p) {
+  //   [!p.asFunction<void Function(Object)>(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<NativeFunction<Void Function(Handle)>> p) {
+  //   p.asFunction<void Function(Object)>();
+  // }
+  // ```
+  //
+  // If none of the function's parameters are `Handle`s, then correct the type
+  // information:
+  //
+  // ```dart
+  // import 'dart:ffi';
+  //
+  // void f(Pointer<NativeFunction<Void Function(Int8)>> p) {
+  //   p.asFunction<void Function(int)>(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 @@
   /**
    * 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 @@
   /**
    * 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!]<Int8 Function(Int8)>(f);
+  // }
+  // ```
+  //
+  // #### Common fixes
+  //
+  // Add an exceptional return type:
+  //
+  // ```dart
+  // import 'dart:ffi';
+  //
+  // int f(int i) => i * 2;
+  //
+  // void g() {
+  //   Pointer.fromFunction<Int8 Function(Int8)>(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 @@
    * 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<Utf8> 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 @@
   /**
    * 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<Uint8>!] 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<Uint8> 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 @@
    * 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<T extends Function> {
+  //   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<T extends Function> {
+  //   void g() {
+  //     Pointer.fromFunction<Int32 Function(Int32)>(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 @@
    * 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<T>([!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<T>(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<T>(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 @@
    * 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<R extends T> {
+  //   void m(Pointer<NativeFunction<T>> p) {
+  //     p.asFunction<[!R!]>();
+  //   }
+  // }
+  // ```
+  //
+  // #### Common fixes
+  //
+  // Remove any uses of type parameters:
+  //
+  // ```dart
+  // import 'dart:ffi';
+  //
+  // class C {
+  //   void m(Pointer<NativeFunction<Int64 Function(Int64)>> p) {
+  //     p.asFunction<int Function(int)>();
+  //   }
+  // }
+  // ```
   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 @@
    * 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<NativeFunction<FNative>> 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<NativeFunction<FNative>> p) {
+  //     p.asFunction<F>();
+  //   }
+  // }
+  // ```
   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 @@
   /**
    * 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<Uint8> a0;
+  // }
+  // ```
+  //
+  // #### Common fixes
+  //
+  // Change the dimension to be a positive integer:
+  //
+  // ```dart
+  // import 'dart:ffi';
+  //
+  // class MyStruct extends Struct {
+  //   @Array(8)
+  //   external Array<Uint8> a0;
+  // }
+  // ```
   static const FfiCode NON_POSITIVE_ARRAY_DIMENSION = FfiCode(
     'NON_POSITIVE_ARRAY_DIMENSION',
     "Array dimensions must be positive numbers.",
@@ -368,11 +1335,46 @@
    * 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<Uint8> 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 @@
   /**
    * 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<Uint8> notEmpty;
+  // }
+  // ```
+  //
+  // #### Common fixes
+  //
+  // Remove all but one of the annotations:
+  //
+  // ```dart
+  // import 'dart:ffi';
+  //
+  // @Packed(1)
+  // class C extends Struct {
+  //   external Pointer<Uint8> notEmpty;
+  // }
+  // ```
   static const FfiCode PACKED_ANNOTATION = FfiCode(
     'PACKED_ANNOTATION',
     "Structs must have at most one 'Packed' annotation.",
@@ -390,6 +1426,39 @@
   /**
    * 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<Uint8> 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<Uint8> 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 @@
    * 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<Uint8> notEmpty;
+  // }
+  //
+  // @Packed(1)
+  // class Outer extends Struct {
+  //   external Pointer<Uint8> 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<Uint8> notEmpty;
+  // }
+  //
+  // @Packed(1)
+  // class Outer extends Struct {
+  //   external Pointer<Uint8> 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<Uint8> notEmpty;
+  // }
+  //
+  // @Packed(8)
+  // class Outer extends Struct {
+  //   external Pointer<Uint8> 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 @@
   /**
    * 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<Array<Array<Uint8>>> 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<Array<Array<Uint8>>> 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<Array<Uint8>> 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 @@
    * 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 @@
    * 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 84ee9ef..9c4b72b 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 @@
 
 /// 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<T> allocate<T extends NativeType>(int byteCount, {int? alignment});
+
+  void free(Pointer pointer);
+}
+
+class Utf8 extends Opaque {}
+
+class _CallocAllocator implements Allocator {
+  @override
+  Pointer<T> allocate<T extends NativeType>(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 e373a74..84784c3 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 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 1e292cc..f3dcc6d 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -13822,34 +13822,286 @@
     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<Int8> p;
+      }
+      ```
+
+      #### Common fixes
+
+      Remove the annotations from the field:
+
+      ```dart
+      import 'dart:ffi';
+
+      class C extends Struct {
+        external Pointer<Int8> 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<NativeFunction<Int8 Function(Int8)>> 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<NativeFunction<Int8 Function(Int8)>> 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<NativeFunction<Int8 Function(Int8)>> 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<C>(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<Uint8> a0;
+      }
+      ```
+
+      #### Common fixes
+
+      Remove all but one of the annotations:
+
+      ```dart
+      import 'dart:ffi';
+
+      class C extends Struct {
+        @Array(8)
+        external Array<Uint8> 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 @@
     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!]<T> 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<T>(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<T>(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<Utf8> 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<NativeFunction<Handle Function()>> p) {
+        [!p.asFunction<Object Function()>(isLeaf: true)!];
+      }
+      ```
+
+      #### Common fixes
+
+      If the function returns a handle, then remove the `isLeaf` argument:
+
+      ```dart
+      import 'dart:ffi';
+
+      void f(Pointer<NativeFunction<Handle Function()>> p) {
+        p.asFunction<Object Function()>();
+      }
+      ```
+
+      If the function returns one of the supported types, then correct the type
+      information:
+
+      ```dart
+      import 'dart:ffi';
+
+      void f(Pointer<NativeFunction<Int32 Function()>> p) {
+        p.asFunction<int Function()>(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<NativeFunction<Void Function(Handle)>> p) {
+        [!p.asFunction<void Function(Object)>(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<NativeFunction<Void Function(Handle)>> p) {
+        p.asFunction<void Function(Object)>();
+      }
+      ```
+
+      If none of the function's parameters are `Handle`s, then correct the type
+      information:
+
+      ```dart
+      import 'dart:ffi';
+
+      void f(Pointer<NativeFunction<Void Function(Int8)>> p) {
+        p.asFunction<void Function(int)>(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!]<Int8 Function(Int8)>(f);
+      }
+      ```
+
+      #### Common fixes
+
+      Add an exceptional return type:
+
+      ```dart
+      import 'dart:ffi';
+
+      int f(int i) => i * 2;
+
+      void g() {
+        Pointer.fromFunction<Int8 Function(Int8)>(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<Utf8> 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<Uint8>!] 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<Uint8> 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 @@
       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<T extends Function> {
+        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<T extends Function> {
+        void g() {
+          Pointer.fromFunction<Int32 Function(Int32)>(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 @@
       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<T>([!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<T>(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<T>(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<R extends T> {
+        void m(Pointer<NativeFunction<T>> p) {
+          p.asFunction<[!R!]>();
+        }
+      }
+      ```
+
+      #### Common fixes
+
+      Remove any uses of type parameters:
+
+      ```dart
+      import 'dart:ffi';
+
+      class C {
+        void m(Pointer<NativeFunction<Int64 Function(Int64)>> p) {
+          p.asFunction<int Function(int)>();
+        }
+      }
+      ```
   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<NativeFunction<FNative>> 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<NativeFunction<FNative>> p) {
+          p.asFunction<F>();
+        }
+      }
+      ```
   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<Uint8> a0;
+      }
+      ```
+
+      #### Common fixes
+
+      Change the dimension to be a positive integer:
+
+      ```dart
+      import 'dart:ffi';
+
+      class MyStruct extends Struct {
+        @Array(8)
+        external Array<Uint8> 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<Uint8> 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<Uint8> notEmpty;
+      }
+      ```
+
+      #### Common fixes
+
+      Remove all but one of the annotations:
+
+      ```dart
+      import 'dart:ffi';
+
+      @Packed(1)
+      class C extends Struct {
+        external Pointer<Uint8> 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<Uint8> 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<Uint8> 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<Uint8> notEmpty;
+      }
+
+      @Packed(1)
+      class Outer extends Struct {
+        external Pointer<Uint8> 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<Uint8> notEmpty;
+      }
+
+      @Packed(1)
+      class Outer extends Struct {
+        external Pointer<Uint8> 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<Uint8> notEmpty;
+      }
+
+      @Packed(8)
+      class Outer extends Struct {
+        external Pointer<Uint8> 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<Array<Array<Uint8>>> 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<Array<Array<Uint8>>> 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<Array<Uint8>> a0;
+      }
+      ```
   SUBTYPE_OF_FFI_CLASS_IN_EXTENDS:
     sharedName: SUBTYPE_OF_FFI_CLASS
     problemMessage: "The class '{0}' can't extend '{1}'."
@@ -13994,6 +15212,47 @@
       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 @@
       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 89c6d3f..3f5a183 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 @@
   void writeTestPackageConfig(
     PackageConfigFileBuilder config, {
     String? languageVersion,
+    bool ffi = false,
     bool js = false,
     bool meta = false,
   }) {
@@ -324,6 +325,14 @@
       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 d5ee1bf..873f700 100644
--- a/pkg/analyzer/test/verify_diagnostics_test.dart
+++ b/pkg/analyzer/test/verify_diagnostics_test.dart
@@ -451,6 +451,6 @@
         );
       }
     }
-    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 282257c..c8454b0 100644
--- a/pkg/analyzer/tool/diagnostics/diagnostics.md
+++ b/pkg/analyzer/tool/diagnostics/diagnostics.md
@@ -649,6 +649,101 @@
 }
 {% 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<Int8> p;
+}
+{% endprettify %}
+
+#### Common fixes
+
+Remove the annotations from the field:
+
+{% prettify dart tag=pre+code %}
+import 'dart:ffi';
+
+class C extends Struct {
+  external Pointer<Int8> 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<NativeFunction<Int8 Function(Int8)>> 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<NativeFunction<Int8 Function(Int8)>> 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<NativeFunction<Int8 Function(Int8)>> 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 @@
 C<T> newC<T>() => C<T>();
 {% 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<C>(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 @@
 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 @@
 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 @@
 }
 {% 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<Uint8> 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<Uint8> a0;
+}
+{% endprettify %}
+
 ### field_initialized_by_multiple_initializers
 
 _The field '{0}' can't be initialized twice in the same constructor._
@@ -4800,6 +5079,51 @@
 }
 {% 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 @@
 }
 {% 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 @@
 }
 {% 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!]<T> 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 @@
     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<T>(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<T>(f);
+}
+{% endprettify %}
+
 ### invalid_extension_argument_count
 
 _Extension overrides must have exactly one argument: the value of 'this' in the
@@ -6677,6 +7160,52 @@
 }
 {% 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<Utf8> 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 @@
 }
 {% 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<NativeFunction<Handle Function()>> p) {
+  [!p.asFunction<Object Function()>(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<NativeFunction<Handle Function()>> p) {
+  p.asFunction<Object Function()>();
+}
+{% 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<NativeFunction<Int32 Function()>> p) {
+  p.asFunction<int Function()>(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<NativeFunction<Void Function(Handle)>> p) {
+  [!p.asFunction<void Function(Object)>(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<NativeFunction<Void Function(Handle)>> p) {
+  p.asFunction<void Function(Object)>();
+}
+{% 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<NativeFunction<Void Function(Int8)>> p) {
+  p.asFunction<void Function(int)>(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 @@
 var m = <String, int>{'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 @@
 }
 {% 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!]<Int8 Function(Int8)>(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<Int8 Function(Int8)>(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<Utf8> str;
+
+  @Int32()
+  external int i;
+}
+{% endprettify %}
+
 ### missing_name
 
 _The 'name' field is required but missing._
@@ -8289,6 +9104,44 @@
 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<Uint8>!] 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<Uint8> 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 @@
 }
 {% 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<T extends Function> {
+  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<T extends Function> {
+  void g() {
+    Pointer.fromFunction<Int32 Function(Int32)>(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<T>([!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<T>(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<T>(f, 5);
+}
+{% endprettify %}
+
 ### must_be_immutable
 
 _This class (or a class that this class inherits from) is marked as
@@ -9640,6 +10602,52 @@
 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<R extends T> {
+  void m(Pointer<NativeFunction<T>> 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<NativeFunction<Int64 Function(Int64)>> p) {
+    p.asFunction<int Function(int)>();
+  }
+}
+{% endprettify %}
+
 ### non_const_call_to_literal_constructor
 
 _This instance creation must be 'const', because the {0} constructor is marked
@@ -9728,6 +10736,134 @@
 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<NativeFunction<FNative>> 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<NativeFunction<FNative>> p) {
+    p.asFunction<F>();
+  }
+}
+{% 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<Uint8> 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<Uint8> 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<Uint8> a0;
+}
+{% endprettify %}
+
 ### non_sync_factory
 
 _Factory bodies can't use 'async', 'async*', or 'sync*'._
@@ -10720,6 +11856,165 @@
 
 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<Uint8> 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<Uint8> 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<Uint8> 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<Uint8> 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<Uint8> notEmpty;
+}
+
+@Packed(1)
+class Outer extends Struct {
+  external Pointer<Uint8> 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<Uint8> notEmpty;
+}
+
+@Packed(1)
+class Outer extends Struct {
+  external Pointer<Uint8> 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<Uint8> notEmpty;
+}
+
+@Packed(8)
+class Outer extends Struct {
+  external Pointer<Uint8> 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 @@
 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<Array<Array<Uint8>>> 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<Array<Array<Uint8>>> 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<Array<Uint8>> a0;
+}
+{% endprettify %}
+
 ### static_access_to_instance_member
 
 _Instance member '{0}' can't be accessed using static access._
@@ -12724,6 +14071,55 @@
 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 @@
 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._
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 1f0baa6..9e8ab9a 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -4129,10 +4129,22 @@
     }
   }
   if (FLAG_verify_acquired_data) {
-    if (external) {
-      ASSERT(!T->heap()->Contains(reinterpret_cast<uword>(data_tmp)));
-    } else {
-      ASSERT(T->heap()->Contains(reinterpret_cast<uword>(data_tmp)));
+    {
+      NoSafepointScope no_safepoint(T);
+      bool sweep_in_progress;
+      {
+        PageSpace* old_space = T->heap()->old_space();
+        MonitorLocker ml(old_space->tasks_lock());
+        sweep_in_progress = (old_space->phase() == PageSpace::kSweepingLarge) ||
+                            (old_space->phase() == PageSpace::kSweepingRegular);
+      }
+      if (!sweep_in_progress) {
+        if (external) {
+          ASSERT(!T->heap()->Contains(reinterpret_cast<uword>(data_tmp)));
+        } else {
+          ASSERT(T->heap()->Contains(reinterpret_cast<uword>(data_tmp)));
+        }
+      }
     }
     const Object& obj = Object::Handle(Z, Api::UnwrapHandle(object));
     WeakTable* table = I->group()->api_state()->acquired_table();
diff --git a/runtime/vm/heap/pages.cc b/runtime/vm/heap/pages.cc
index 39b1603..476ad6b 100644
--- a/runtime/vm/heap/pages.cc
+++ b/runtime/vm/heap/pages.cc
@@ -1247,16 +1247,39 @@
 
   bool has_reservation = MarkReservation();
 
+  {
+    // Move pages to sweeper work lists.
+    MutexLocker ml(&pages_lock_);
+    ASSERT(sweep_large_ == nullptr);
+    sweep_large_ = large_pages_;
+    large_pages_ = large_pages_tail_ = nullptr;
+    ASSERT(sweep_regular_ == nullptr);
+    if (!compact) {
+      sweep_regular_ = pages_;
+      pages_ = pages_tail_ = nullptr;
+    }
+  }
+
+  bool can_verify;
   if (compact) {
     SweepLarge();
     Compact(thread);
     set_phase(kDone);
+    can_verify = true;
   } else if (FLAG_concurrent_sweep && has_reservation) {
     ConcurrentSweep(isolate_group);
+    can_verify = false;
   } else {
     SweepLarge();
-    Sweep();
+    Sweep(/*exclusive*/ true);
     set_phase(kDone);
+    can_verify = true;
+  }
+
+  if (FLAG_verify_after_gc && can_verify) {
+    OS::PrintErr("Verifying after sweeping...");
+    heap_->VerifyGC(kForbidMarked);
+    OS::PrintErr(" done.\n");
   }
 
   TryReserveForOOM();
@@ -1294,65 +1317,82 @@
   TIMELINE_FUNCTION_GC_DURATION(Thread::Current(), "SweepLarge");
 
   GCSweeper sweeper;
-  OldPage* prev_page = nullptr;
-  OldPage* page = large_pages_;
-  while (page != nullptr) {
-    OldPage* next_page = page->next();
-    const intptr_t words_to_end = sweeper.SweepLargePage(page);
+  MutexLocker ml(&pages_lock_);
+  while (sweep_large_ != nullptr) {
+    OldPage* page = sweep_large_;
+    sweep_large_ = page->next();
+    page->set_next(nullptr);
+    ASSERT(page->type() == OldPage::kData);
+
+    ml.Unlock();
+    intptr_t words_to_end = sweeper.SweepLargePage(page);
+    intptr_t size;
     if (words_to_end == 0) {
-      FreeLargePage(page, prev_page);
+      size = page->memory_->size();
+      page->Deallocate();
     } else {
       TruncateLargePage(page, words_to_end << kWordSizeLog2);
-      prev_page = page;
     }
-    // Advance to the next page.
-    page = next_page;
+    ml.Lock();
+
+    if (words_to_end == 0) {
+      IncreaseCapacityInWordsLocked(-(size >> kWordSizeLog2));
+    } else {
+      AddLargePageLocked(page);
+    }
   }
 }
 
-void PageSpace::Sweep() {
+void PageSpace::Sweep(bool exclusive) {
   TIMELINE_FUNCTION_GC_DURATION(Thread::Current(), "Sweep");
 
   GCSweeper sweeper;
 
   intptr_t shard = 0;
   const intptr_t num_shards = Utils::Maximum(FLAG_scavenger_tasks, 1);
-  for (intptr_t i = 0; i < num_shards; i++) {
-    DataFreeList(i)->mutex()->Lock();
-  }
-
-  OldPage* prev_page = nullptr;
-  OldPage* page = pages_;
-  while (page != nullptr) {
-    OldPage* next_page = page->next();
-    ASSERT(page->type() == OldPage::kData);
-    shard = (shard + 1) % num_shards;
-    bool page_in_use =
-        sweeper.SweepPage(page, DataFreeList(shard), true /*is_locked*/);
-    if (page_in_use) {
-      prev_page = page;
-    } else {
-      FreePage(page, prev_page);
+  if (exclusive) {
+    for (intptr_t i = 0; i < num_shards; i++) {
+      DataFreeList(i)->mutex()->Lock();
     }
-    // Advance to the next page.
-    page = next_page;
   }
 
-  for (intptr_t i = 0; i < num_shards; i++) {
-    DataFreeList(i)->mutex()->Unlock();
+  MutexLocker ml(&pages_lock_);
+  while (sweep_regular_ != nullptr) {
+    OldPage* page = sweep_regular_;
+    sweep_regular_ = page->next();
+    page->set_next(nullptr);
+    ASSERT(page->type() == OldPage::kData);
+
+    ml.Unlock();
+    // Cycle through the shards round-robin so that free space is roughly
+    // evenly distributed among the freelists and so roughly evenly available
+    // to each scavenger worker.
+    shard = (shard + 1) % num_shards;
+    bool page_in_use = sweeper.SweepPage(page, DataFreeList(shard), exclusive);
+    intptr_t size;
+    if (!page_in_use) {
+      size = page->memory_->size();
+      page->Deallocate();
+    }
+    ml.Lock();
+
+    if (page_in_use) {
+      AddPageLocked(page);
+    } else {
+      IncreaseCapacityInWordsLocked(-(size >> kWordSizeLog2));
+    }
   }
 
-  if (FLAG_verify_after_gc) {
-    OS::PrintErr("Verifying after sweeping...");
-    heap_->VerifyGC(kForbidMarked);
-    OS::PrintErr(" done.\n");
+  if (exclusive) {
+    for (intptr_t i = 0; i < num_shards; i++) {
+      DataFreeList(i)->mutex()->Unlock();
+    }
   }
 }
 
 void PageSpace::ConcurrentSweep(IsolateGroup* isolate_group) {
   // Start the concurrent sweeper task now.
-  GCSweeper::SweepConcurrent(isolate_group, pages_, pages_tail_, large_pages_,
-                             large_pages_tail_, &freelists_[OldPage::kData]);
+  GCSweeper::SweepConcurrent(isolate_group);
 }
 
 void PageSpace::Compact(Thread* thread) {
diff --git a/runtime/vm/heap/pages.h b/runtime/vm/heap/pages.h
index b8e209b..11a4cc2 100644
--- a/runtime/vm/heap/pages.h
+++ b/runtime/vm/heap/pages.h
@@ -576,7 +576,7 @@
                             int64_t pre_wait_for_sweepers,
                             int64_t pre_safe_point);
   void SweepLarge();
-  void Sweep();
+  void Sweep(bool exclusive);
   void ConcurrentSweep(IsolateGroup* isolate_group);
   void Compact(Thread* thread);
 
@@ -614,6 +614,8 @@
   OldPage* large_pages_ = nullptr;
   OldPage* large_pages_tail_ = nullptr;
   OldPage* image_pages_ = nullptr;
+  OldPage* sweep_regular_ = nullptr;
+  OldPage* sweep_large_ = nullptr;
 
   // Various sizes being tracked for this generation.
   intptr_t max_capacity_in_words_;
diff --git a/runtime/vm/heap/sweeper.cc b/runtime/vm/heap/sweeper.cc
index d814521..11900bb 100644
--- a/runtime/vm/heap/sweeper.cc
+++ b/runtime/vm/heap/sweeper.cc
@@ -109,127 +109,54 @@
 
 class ConcurrentSweeperTask : public ThreadPool::Task {
  public:
-  ConcurrentSweeperTask(IsolateGroup* isolate_group,
-                        PageSpace* old_space,
-                        OldPage* first,
-                        OldPage* last,
-                        OldPage* large_first,
-                        OldPage* large_last)
-      : task_isolate_group_(isolate_group),
-        old_space_(old_space),
-        first_(first),
-        last_(last),
-        large_first_(large_first),
-        large_last_(large_last) {
-    ASSERT(task_isolate_group_ != NULL);
-    ASSERT(first_ != NULL);
-    ASSERT(old_space_ != NULL);
-    ASSERT(last_ != NULL);
-    MonitorLocker ml(old_space_->tasks_lock());
-    old_space_->set_tasks(old_space_->tasks() + 1);
-    old_space_->set_phase(PageSpace::kSweepingLarge);
+  explicit ConcurrentSweeperTask(IsolateGroup* isolate_group)
+      : isolate_group_(isolate_group) {
+    ASSERT(isolate_group != nullptr);
+    PageSpace* old_space = isolate_group->heap()->old_space();
+    MonitorLocker ml(old_space->tasks_lock());
+    old_space->set_tasks(old_space->tasks() + 1);
+    old_space->set_phase(PageSpace::kSweepingLarge);
   }
 
   virtual void Run() {
     bool result = Thread::EnterIsolateGroupAsHelper(
-        task_isolate_group_, Thread::kSweeperTask, /*bypass_safepoint=*/true);
+        isolate_group_, Thread::kSweeperTask, /*bypass_safepoint=*/true);
     ASSERT(result);
+    PageSpace* old_space = isolate_group_->heap()->old_space();
     {
       Thread* thread = Thread::Current();
       ASSERT(thread->BypassSafepoints());  // Or we should be checking in.
       TIMELINE_FUNCTION_GC_DURATION(thread, "ConcurrentSweep");
-      GCSweeper sweeper;
 
-      OldPage* page = large_first_;
-      OldPage* prev_page = NULL;
-      while (page != NULL) {
-        OldPage* next_page;
-        if (page == large_last_) {
-          // Don't access page->next(), which would be a race with mutator
-          // allocating new pages.
-          next_page = NULL;
-        } else {
-          next_page = page->next();
-        }
-        ASSERT(page->type() == OldPage::kData);
-        const intptr_t words_to_end = sweeper.SweepLargePage(page);
-        if (words_to_end == 0) {
-          old_space_->FreeLargePage(page, prev_page);
-        } else {
-          old_space_->TruncateLargePage(page, words_to_end << kWordSizeLog2);
-          prev_page = page;
-        }
-        page = next_page;
-      }
+      old_space->SweepLarge();
 
       {
-        MonitorLocker ml(old_space_->tasks_lock());
-        ASSERT(old_space_->phase() == PageSpace::kSweepingLarge);
-        old_space_->set_phase(PageSpace::kSweepingRegular);
+        MonitorLocker ml(old_space->tasks_lock());
+        ASSERT(old_space->phase() == PageSpace::kSweepingLarge);
+        old_space->set_phase(PageSpace::kSweepingRegular);
         ml.NotifyAll();
       }
 
-      intptr_t shard = 0;
-      const intptr_t num_shards = Utils::Maximum(FLAG_scavenger_tasks, 1);
-      page = first_;
-      prev_page = NULL;
-      while (page != NULL) {
-        OldPage* next_page;
-        if (page == last_) {
-          // Don't access page->next(), which would be a race with mutator
-          // allocating new pages.
-          next_page = NULL;
-        } else {
-          next_page = page->next();
-        }
-        ASSERT(page->type() == OldPage::kData);
-        shard = (shard + 1) % num_shards;
-        bool page_in_use =
-            sweeper.SweepPage(page, old_space_->DataFreeList(shard), false);
-        if (page_in_use) {
-          prev_page = page;
-        } else {
-          old_space_->FreePage(page, prev_page);
-        }
-        {
-          // Notify the mutator thread that we have added elements to the free
-          // list or that more capacity is available.
-          MonitorLocker ml(old_space_->tasks_lock());
-          ml.Notify();
-        }
-        page = next_page;
-      }
+      old_space->Sweep(/*exclusive*/ false);
     }
     // Exit isolate cleanly *before* notifying it, to avoid shutdown race.
     Thread::ExitIsolateGroupAsHelper(/*bypass_safepoint=*/true);
     // This sweeper task is done. Notify the original isolate.
     {
-      MonitorLocker ml(old_space_->tasks_lock());
-      old_space_->set_tasks(old_space_->tasks() - 1);
-      ASSERT(old_space_->phase() == PageSpace::kSweepingRegular);
-      old_space_->set_phase(PageSpace::kDone);
+      MonitorLocker ml(old_space->tasks_lock());
+      old_space->set_tasks(old_space->tasks() - 1);
+      ASSERT(old_space->phase() == PageSpace::kSweepingRegular);
+      old_space->set_phase(PageSpace::kDone);
       ml.NotifyAll();
     }
   }
 
  private:
-  IsolateGroup* task_isolate_group_;
-  PageSpace* old_space_;
-  OldPage* first_;
-  OldPage* last_;
-  OldPage* large_first_;
-  OldPage* large_last_;
+  IsolateGroup* isolate_group_;
 };
 
-void GCSweeper::SweepConcurrent(IsolateGroup* isolate_group,
-                                OldPage* first,
-                                OldPage* last,
-                                OldPage* large_first,
-                                OldPage* large_last,
-                                FreeList* freelist) {
-  bool result = Dart::thread_pool()->Run<ConcurrentSweeperTask>(
-      isolate_group, isolate_group->heap()->old_space(), first, last,
-      large_first, large_last);
+void GCSweeper::SweepConcurrent(IsolateGroup* isolate_group) {
+  bool result = Dart::thread_pool()->Run<ConcurrentSweeperTask>(isolate_group);
   ASSERT(result);
 }
 
diff --git a/runtime/vm/heap/sweeper.h b/runtime/vm/heap/sweeper.h
index ca9b6c4..8b781d4 100644
--- a/runtime/vm/heap/sweeper.h
+++ b/runtime/vm/heap/sweeper.h
@@ -34,13 +34,8 @@
   // last marked object.
   intptr_t SweepLargePage(OldPage* page);
 
-  // Sweep the regular sized data pages between first and last inclusive.
-  static void SweepConcurrent(IsolateGroup* isolate_group,
-                              OldPage* first,
-                              OldPage* last,
-                              OldPage* large_first,
-                              OldPage* large_last,
-                              FreeList* freelist);
+  // Sweep the large and regular sized data pages.
+  static void SweepConcurrent(IsolateGroup* isolate_group);
 };
 
 }  // namespace dart
diff --git a/tools/VERSION b/tools/VERSION
index 97705ad..3a2798d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 53
+PRERELEASE 54
 PRERELEASE_PATCH 0
\ No newline at end of file