[ffigen] Parameter names in function pointer fields (#537)

diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md
index 6dd3b69..2cad2a0 100644
--- a/pkgs/ffigen/CHANGELOG.md
+++ b/pkgs/ffigen/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 7.2.10
+
+- Generate parameter names in function pointer fields
+
 # 7.2.9
 
 - Detect LLVM installed using Scoop on Windows machines.
diff --git a/pkgs/ffigen/example/c_json/cjson_generated_bindings.dart b/pkgs/ffigen/example/c_json/cjson_generated_bindings.dart
index ad704b5..0571e62 100644
--- a/pkgs/ffigen/example/c_json/cjson_generated_bindings.dart
+++ b/pkgs/ffigen/example/c_json/cjson_generated_bindings.dart
@@ -1257,13 +1257,12 @@
 }
 
 class cJSON_Hooks extends ffi.Struct {
-  external ffi
-          .Pointer<ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Size)>>
+  external ffi.Pointer<
+          ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Size sz)>>
       malloc_fn;
 
-  external ffi
-          .Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>
-      free_fn;
+  external ffi.Pointer<
+      ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void> ptr)>> free_fn;
 }
 
 typedef cJSON_bool = ffi.Int;
diff --git a/pkgs/ffigen/example/libclang-example/generated_bindings.dart b/pkgs/ffigen/example/libclang-example/generated_bindings.dart
index e41aaf9..800c580 100644
--- a/pkgs/ffigen/example/libclang-example/generated_bindings.dart
+++ b/pkgs/ffigen/example/libclang-example/generated_bindings.dart
@@ -9748,7 +9748,9 @@
 /// The visitor should return one of the \c CXChildVisitResult values
 /// to direct clang_visitCursorChildren().
 typedef CXCursorVisitor = ffi.Pointer<
-    ffi.NativeFunction<ffi.Int32 Function(CXCursor, CXCursor, CXClientData)>>;
+    ffi.NativeFunction<
+        ffi.Int32 Function(
+            CXCursor cursor, CXCursor parent, CXClientData client_data)>>;
 
 /// Describes how the traversal of the children of a particular
 /// cursor should proceed after visiting a particular child cursor.
@@ -10425,8 +10427,11 @@
 /// the first element refers to the location that included 'included_file'.
 typedef CXInclusionVisitor = ffi.Pointer<
     ffi.NativeFunction<
-        ffi.Void Function(CXFile, ffi.Pointer<CXSourceLocation>,
-            ffi.UnsignedInt, CXClientData)>>;
+        ffi.Void Function(
+            CXFile included_file,
+            ffi.Pointer<CXSourceLocation> inclusion_stack,
+            ffi.UnsignedInt include_len,
+            CXClientData client_data)>>;
 typedef NativeClang_getInclusions = ffi.Void Function(
     CXTranslationUnit tu, CXInclusionVisitor visitor, CXClientData client_data);
 typedef DartClang_getInclusions = void Function(
@@ -10943,8 +10948,10 @@
   /// Called periodically to check whether indexing should be aborted.
   /// Should return 0 to continue, and non-zero to abort.
   external ffi.Pointer<
-      ffi.NativeFunction<
-          ffi.Int Function(CXClientData, ffi.Pointer<ffi.Void>)>> abortQuery;
+          ffi.NativeFunction<
+              ffi.Int Function(
+                  CXClientData client_data, ffi.Pointer<ffi.Void> reserved)>>
+      abortQuery;
 
   /// Called at the end of indexing; passes the complete diagnostic set.
   external ffi.Pointer<
@@ -10955,8 +10962,8 @@
 
   external ffi.Pointer<
       ffi.NativeFunction<
-          CXIdxClientFile Function(
-              CXClientData, CXFile, ffi.Pointer<ffi.Void>)>> enteredMainFile;
+          CXIdxClientFile Function(CXClientData client_data, CXFile mainFile,
+              ffi.Pointer<ffi.Void> reserved)>> enteredMainFile;
 
   /// Called when a file gets \#included/\#imported.
   external ffi.Pointer<
@@ -10979,9 +10986,10 @@
 
   /// Called at the beginning of indexing a translation unit.
   external ffi.Pointer<
-      ffi.NativeFunction<
-          CXIdxClientContainer Function(
-              CXClientData, ffi.Pointer<ffi.Void>)>> startedTranslationUnit;
+          ffi.NativeFunction<
+              CXIdxClientContainer Function(
+                  CXClientData client_data, ffi.Pointer<ffi.Void> reserved)>>
+      startedTranslationUnit;
 
   external ffi.Pointer<
           ffi.NativeFunction<
@@ -11095,8 +11103,9 @@
 ///
 /// The visitor should return one of the \c CXVisitorResult values
 /// to direct \c clang_Type_visitFields.
-typedef CXFieldVisitor = ffi
-    .Pointer<ffi.NativeFunction<ffi.Int32 Function(CXCursor, CXClientData)>>;
+typedef CXFieldVisitor = ffi.Pointer<
+    ffi.NativeFunction<
+        ffi.Int32 Function(CXCursor C, CXClientData client_data)>>;
 typedef NativeClang_Type_visitFields = ffi.UnsignedInt Function(
     CXType T, CXFieldVisitor visitor, CXClientData client_data);
 typedef DartClang_Type_visitFields = int Function(
diff --git a/pkgs/ffigen/lib/src/code_generator/func_type.dart b/pkgs/ffigen/lib/src/code_generator/func_type.dart
index 3025bd2..5402028 100644
--- a/pkgs/ffigen/lib/src/code_generator/func_type.dart
+++ b/pkgs/ffigen/lib/src/code_generator/func_type.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:ffigen/src/code_generator.dart';
+import 'package:ffigen/src/code_generator/utils.dart';
 
 import 'writer.dart';
 
@@ -54,6 +55,21 @@
       p.type.addDependencies(dependencies);
     }
   }
+
+  void addParameterNames(List<String> names) {
+    if (names.length != parameters.length) {
+      return;
+    }
+    final paramNamer = UniqueNamer({});
+    for (int i = 0; i < parameters.length; i++) {
+      final finalName = paramNamer.makeUnique(names[i]);
+      parameters[i] = Parameter(
+        type: parameters[i].type,
+        originalName: names[i],
+        name: finalName,
+      );
+    }
+  }
 }
 
 /// Represents a NativeFunction<Function>.
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart
index 5a7709c..a88f5fc 100644
--- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart
@@ -253,7 +253,7 @@
           parsed.maxChildAlignment = align;
         }
 
-        final mt = cursor.type().toCodeGenType();
+        final mt = cursor.toCodeGenType();
         if (mt is IncompleteArray) {
           // TODO(68): Structs with flexible Array Members are not supported.
           parsed.flexibleArrayMember = true;
@@ -271,7 +271,6 @@
         if (mt.baseType is UnimplementedType) {
           parsed.unimplementedMemberType = true;
         }
-
         parsed.compound.members.add(
           Member(
             dartDoc: getCursorDocComment(
@@ -295,7 +294,7 @@
         break;
       case clang_types.CXCursorKind.CXCursor_UnionDecl:
       case clang_types.CXCursorKind.CXCursor_StructDecl:
-        final mt = cursor.type().toCodeGenType();
+        final mt = cursor.toCodeGenType();
 
         // If the union/struct are anonymous, then we need to add them now,
         // otherwise they will be added in the next iteration.
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/function_type_param_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/function_type_param_parser.dart
new file mode 100644
index 0000000..739b3b1
--- /dev/null
+++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/function_type_param_parser.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:ffi';
+
+import '../clang_bindings/clang_bindings.dart';
+import '../data.dart';
+import '../utils.dart';
+
+/// This type holds the list of `ParmDecl` nodes of a function type declaration.
+class FunctionTypeParams {
+  final List<String> paramNames;
+  final Map<String, CXCursor> params;
+  FunctionTypeParams()
+      : paramNames = [],
+        params = {};
+}
+
+FunctionTypeParams? _params;
+
+int _functionPointerFieldVisitor(
+    CXCursor cursor, CXCursor parent, Pointer<Void> clientData) {
+  if (cursor.kind == CXCursorKind.CXCursor_ParmDecl) {
+    final spelling = cursor.spelling();
+    if (spelling.isNotEmpty) {
+      _params!.paramNames.add(spelling);
+      _params!.params[spelling] = cursor;
+      return CXChildVisitResult.CXChildVisit_Continue;
+    } else {
+      // A parameter's spelling is empty, do not continue further traversal.
+      _params!.paramNames.clear();
+      _params!.params.clear();
+      return CXChildVisitResult.CXChildVisit_Break;
+    }
+  }
+  // The cursor itself may be a pointer etc..
+  return CXChildVisitResult.CXChildVisit_Recurse;
+}
+
+/// Returns `ParmDecl` nodes of function pointer declaration
+/// directly or indirectly pointed to by [cursor].
+FunctionTypeParams parseFunctionPointerParamNames(CXCursor cursor) {
+  _params = FunctionTypeParams();
+  clang.clang_visitChildren(
+    cursor,
+    Pointer.fromFunction(
+        _functionPointerFieldVisitor, exceptional_visitor_return),
+    nullptr,
+  );
+  final result = _params;
+  _params = null;
+  return result!;
+}
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
index 848f60b..d2d3412 100644
--- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
@@ -127,5 +127,5 @@
 }
 
 Type _getParameterType(clang_types.CXCursor cursor) {
-  return cursor.type().toCodeGenType();
+  return cursor.toCodeGenType();
 }
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart
index 988a45e..85df348 100644
--- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart
@@ -40,7 +40,8 @@
   final typedefUsr = cursor.usr();
   if (shouldIncludeTypealias(typedefUsr, typedefName)) {
     final ct = clang.clang_getTypedefDeclUnderlyingType(cursor);
-    final s = getCodeGenType(ct, pointerReference: pointerReference);
+    final s = getCodeGenType(ct,
+        pointerReference: pointerReference, originalCursor: cursor);
 
     if (bindingsIndex.isSeenUnsupportedTypealias(typedefUsr)) {
       // Do not process unsupported typealiases again.
diff --git a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart
index d6b87f2..c6a0951 100644
--- a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart
+++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart
@@ -4,6 +4,7 @@
 
 /// Extracts code_gen Type from type.
 import 'package:ffigen/src/code_generator.dart';
+import 'package:ffigen/src/header_parser/sub_parsers/function_type_param_parser.dart';
 import 'package:ffigen/src/header_parser/sub_parsers/typedefdecl_parser.dart';
 import 'package:ffigen/src/strings.dart' as strings;
 import 'package:logging/logging.dart';
@@ -21,6 +22,8 @@
 final _logger = Logger('ffigen.header_parser.extractor');
 const _padding = '  ';
 
+const maxRecursionDepth = 5;
+
 /// Converts cxtype to a typestring code_generator can accept.
 Type getCodeGenType(
   clang_types.CXType cxtype, {
@@ -30,6 +33,10 @@
 
   /// Passed on if a value was marked as a pointer before this one.
   bool pointerReference = false,
+
+  /// Cursor of the declaration, currently this is useful only to extract
+  /// parameter names in function types.
+  clang_types.CXCursor? originalCursor,
 }) {
   _logger.fine('${_padding}getCodeGenType ${cxtype.completeStringRepr()}');
 
@@ -89,7 +96,11 @@
   switch (cxtype.kind) {
     case clang_types.CXTypeKind.CXType_Pointer:
       final pt = clang.clang_getPointeeType(cxtype);
-      final s = getCodeGenType(pt, pointerReference: true);
+      final s = getCodeGenType(
+        pt,
+        pointerReference: true,
+        originalCursor: originalCursor,
+      );
 
       // Replace Pointer<_Dart_Handle> with Handle.
       if (config.useDartHandle &&
@@ -101,10 +112,10 @@
       return PointerType(s);
     case clang_types.CXTypeKind.CXType_FunctionProto:
       // Primarily used for function pointers.
-      return _extractFromFunctionProto(cxtype);
+      return _extractFromFunctionProto(cxtype, cursor: originalCursor);
     case clang_types.CXTypeKind.CXType_FunctionNoProto:
       // Primarily used for function types with zero arguments.
-      return _extractFromFunctionProto(cxtype);
+      return _extractFromFunctionProto(cxtype, cursor: originalCursor);
     case clang_types.CXTypeKind
         .CXType_ConstantArray: // Primarily used for constant array in struct members.
       return ConstantArray(
@@ -295,7 +306,8 @@
 }
 
 // Used for function pointer arguments.
-Type _extractFromFunctionProto(clang_types.CXType cxtype) {
+Type _extractFromFunctionProto(clang_types.CXType cxtype,
+    {clang_types.CXCursor? cursor}) {
   final parameters = <Parameter>[];
   final totalArgs = clang.clang_getNumArgTypes(cxtype);
   for (var i = 0; i < totalArgs; i++) {
@@ -314,8 +326,43 @@
     );
   }
 
-  return NativeFunc(FunctionType(
+  final functionType = FunctionType(
     parameters: parameters,
     returnType: clang.clang_getResultType(cxtype).toCodeGenType(),
-  ));
+  );
+  _parseAndMergeParamNames(functionType, cursor, maxRecursionDepth);
+  return NativeFunc(functionType);
+}
+
+void _parseAndMergeParamNames(
+  FunctionType functionType,
+  clang_types.CXCursor? cursor,
+  int recursionDepth,
+) {
+  if (cursor == null) {
+    return;
+  }
+  if (recursionDepth == 0) {
+    final cursorRepr = cursor.completeStringRepr();
+    _logger.warning('Recursion depth exceeded when merging function parameters.'
+        ' Last cursor encountered was $cursorRepr');
+    return;
+  }
+
+  final paramsInfo = parseFunctionPointerParamNames(cursor);
+  functionType.addParameterNames(paramsInfo.paramNames);
+
+  for (final param in functionType.parameters) {
+    final paramRealType = param.type.typealiasType;
+    final paramBaseType = paramRealType.baseType.typealiasType;
+    if (paramBaseType is NativeFunc && param.name.isNotEmpty) {
+      final paramFunctionType = paramBaseType.type;
+      final paramCursor = paramsInfo.params[param.name];
+      _parseAndMergeParamNames(
+        paramFunctionType,
+        paramCursor,
+        recursionDepth - 1,
+      );
+    }
+  }
 }
diff --git a/pkgs/ffigen/lib/src/header_parser/utils.dart b/pkgs/ffigen/lib/src/header_parser/utils.dart
index 0b4770f..a674618 100644
--- a/pkgs/ffigen/lib/src/header_parser/utils.dart
+++ b/pkgs/ffigen/lib/src/header_parser/utils.dart
@@ -80,6 +80,11 @@
         .toStringAndDispose();
   }
 
+  /// Get code_gen [Type] representation of [clang_types.CXType].
+  Type toCodeGenType() {
+    return getCodeGenType(type(), originalCursor: this);
+  }
+
   /// for debug: returns [spelling] [kind] [kindSpelling] [type] [typeSpelling].
   String completeStringRepr() {
     final cxtype = type();
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_functions_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_functions_bindings.dart
index f9e8c1a..c4a01b3 100644
--- a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_functions_bindings.dart
+++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_functions_bindings.dart
@@ -138,4 +138,4 @@
 }
 
 typedef shortHand = ffi.NativeFunction<
-    ffi.Void Function(ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>;
+    ffi.Void Function(ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> b)>;
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart
index 7153ce4..6ef30ff 100644
--- a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart
+++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart
@@ -24,7 +24,8 @@
     ffi.Pointer<
             ffi.NativeFunction<
                 ffi.Void Function(
-                    ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>>
+                    ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>
+                        unnamed2)>>
         unnamed1,
   ) {
     return _func(
@@ -38,16 +39,15 @@
               ffi.Pointer<
                   ffi.NativeFunction<
                       ffi.Void Function(
-                          ffi.Pointer<
-                              ffi.NativeFunction<
-                                  ffi.Void Function()>>)>>)>>('func');
+                          ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>
+                              unnamed2)>>)>>('func');
   late final _func = _funcPtr.asFunction<
       void Function(
           ffi.Pointer<
               ffi.NativeFunction<
                   ffi.Void Function(
-                      ffi.Pointer<
-                          ffi.NativeFunction<ffi.Void Function()>>)>>)>();
+                      ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>
+                          unnamed2)>>)>();
 
   void funcWithNativeFunc(
     WithTypedefReturnType named,
@@ -68,7 +68,8 @@
   external ffi.Pointer<
       ffi.NativeFunction<
           ffi.Void Function(
-              ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>> unnamed1;
+              ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>
+                  unnamed2)>> unnamed1;
 }
 
 typedef WithTypedefReturnType
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_struct_fptr_fields_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_struct_fptr_fields_bindings.dart
new file mode 100644
index 0000000..b0490b2
--- /dev/null
+++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_struct_fptr_fields_bindings.dart
@@ -0,0 +1,72 @@
+// AUTO GENERATED FILE, DO NOT EDIT.
+//
+// Generated by `package:ffigen`.
+// ignore_for_file: type=lint
+import 'dart:ffi' as ffi;
+
+class S extends ffi.Struct {
+  external ffi.Pointer<ffi.NativeFunction<ffi.Int Function()>> func1;
+
+  external ffi
+          .Pointer<ffi.NativeFunction<ffi.Int Function(ffi.Int a, ffi.Int b)>>
+      comparator;
+
+  external ffi.Pointer<
+      ffi.NativeFunction<
+          ffi.Int Function(ffi.Double a, ffi.Float b, ffi.Pointer<ffi.Char> c,
+              ffi.Int d, ffi.LongLong e)>> veryManyArguments;
+
+  external ffi.Pointer<
+          ffi.NativeFunction<
+              ffi.Int Function(
+                  ffi.Int, ffi.Int, ffi.Int, ffi.Float, ffi.Pointer<ffi.Char>)>>
+      argsDontHaveNames;
+
+  external ArithmeticOperation operation;
+
+  external ffi.Pointer<
+          ffi.Pointer<
+              ffi.NativeFunction<
+                  ffi.Void Function(ffi.Pointer<ffi.Int> array, ffi.Int len)>>>
+      sortPtr;
+
+  external ffi.Pointer<
+      ffi.NativeFunction<
+          ffi.Void Function(
+              ffi.Pointer<ffi.Int> array,
+              ffi.Int len,
+              ffi.Pointer<ffi.NativeFunction<ffi.Int Function(ffi.Int x)>>
+                  evaluator)>> sortBy;
+
+  external ffi.Pointer<
+          ffi.NativeFunction<ffi.Void Function(ffi.Int, ffi.Int, ffi.Char)>>
+      improperlyDeclaredParams;
+
+  external ffi.Pointer<
+      ffi.NativeFunction<
+          ffi.Void Function(
+              ffi.Pointer<ffi.Int> array,
+              ffi.Pointer<ffi.NativeFunction<ffi.Int Function(ffi.Int x)>>
+                  primaryEvaluator,
+              ffi.Pointer<ffi.NativeFunction<ffi.Int Function(ffi.Int x)>>
+                  fallbackEvaluator)>> sortByWithFallback;
+
+  @ffi.Array.multi([2])
+  external ffi.Array<
+          ffi.Pointer<
+              ffi.NativeFunction<ffi.Void Function(ffi.Char, ffi.Char)>>>
+      manyFunctions;
+
+  external ffi.Pointer<
+      ffi.NativeFunction<
+          ffi.Pointer<ffi.NativeFunction<ffi.Int Function(ffi.Int, ffi.Int)>>
+              Function(ffi.Int, ffi.Int)>> functionReturningFunction;
+
+  external ffi.Pointer<
+      ffi.NativeFunction<
+          ffi.Pointer<ffi.NativeFunction<ffi.Int Function(ffi.Int, ffi.Int)>>
+              Function(ffi.Int, ffi.Int)>> functionReturningFunctionImproper;
+}
+
+typedef ArithmeticOperation
+    = ffi.Pointer<ffi.NativeFunction<ffi.Int Function(ffi.Int a, ffi.Int b)>>;
diff --git a/pkgs/ffigen/test/header_parser_tests/struct_fptr_fields.h b/pkgs/ffigen/test/header_parser_tests/struct_fptr_fields.h
new file mode 100644
index 0000000..e95d1ca
--- /dev/null
+++ b/pkgs/ffigen/test/header_parser_tests/struct_fptr_fields.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+typedef int (*ArithmeticOperation)(int a, int b);
+
+struct S
+{
+    // Function pointer field, but no parameters.
+    int (*func1)(void);
+    // Function pointer field with parameters.
+    int (*comparator)(int a, int b);
+    // Function pointer field with lot of parameters
+    int (*veryManyArguments)(double a, float b, char *c, int d, long long e);
+    // Function pointer field with parameters, but no names
+    int (*argsDontHaveNames)(int, int, int, float, char *);
+    // Function pointer through typedef
+    ArithmeticOperation operation;
+    // Pointer to function pointer
+    void (**sortPtr)(int *array, int len);
+    // Function pointer with a function pointer parameter
+    void (*sortBy)(int *array, int len, int (*evaluator)(int x));
+    // Function where few parameters are named. This should not
+    // produce parameters in output.
+    void (*improperlyDeclaredParams)(int a, int, char);
+    // Function pointer with 2 function pointer parameters
+    void (*sortByWithFallback)(int *array,
+                               int (*primaryEvaluator)(int x),
+                               int (*fallbackEvaluator)(int x));
+
+    // TODO(#545): Handle remaining cases of parsing param names
+    // ---
+    // Array of function pointers. Does not produce proper output right now.
+    void (*manyFunctions[2])(char a, char b);
+    // Function pointer returning function pointer. Does not produce valid output.
+    int (*(*functionReturningFunction)(int a, int b))(int c, int d);
+    // Function pointer returning function pointer. The return type has param
+    // names, but the function itself doesn't. This also shouldn't produce
+    // any parameters in output.
+    int (*(*functionReturningFunctionImproper)(int a, int b))(int, int);
+};
diff --git a/pkgs/ffigen/test/header_parser_tests/struct_fptr_fields_test.dart b/pkgs/ffigen/test/header_parser_tests/struct_fptr_fields_test.dart
new file mode 100644
index 0000000..765b8b0
--- /dev/null
+++ b/pkgs/ffigen/test/header_parser_tests/struct_fptr_fields_test.dart
@@ -0,0 +1,42 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:ffigen/src/code_generator.dart';
+import 'package:ffigen/src/config_provider.dart';
+import 'package:ffigen/src/header_parser.dart' as parser;
+import 'package:ffigen/src/strings.dart' as strings;
+import 'package:logging/logging.dart';
+import 'package:test/test.dart';
+import 'package:yaml/yaml.dart' as yaml;
+
+import '../test_utils.dart';
+
+late Library actual;
+void main() {
+  group('Function pointer parameters parsing test', () {
+    setUpAll(() {
+      logWarnings(Level.SEVERE);
+      actual = parser.parse(
+        Config.fromYaml(yaml.loadYaml('''
+${strings.name}: 'NativeLibrary'
+${strings.description}: 'Function pointer fields in structs Test'
+${strings.output}: 'unused'
+${strings.headers}:
+  ${strings.entryPoints}:
+    - 'test/header_parser_tests/struct_fptr_fields.h'
+        ''') as yaml.YamlMap),
+      );
+    });
+
+    test('Expected bindings', () {
+      matchLibraryWithExpected(
+          actual, 'header_parser_struct_fptr_fields_output.dart', [
+        'test',
+        'header_parser_tests',
+        'expected_bindings',
+        '_expected_struct_fptr_fields_bindings.dart',
+      ]);
+    });
+  });
+}
diff --git a/pkgs/ffigen/test/large_integration_tests/_expected_cjson_bindings.dart b/pkgs/ffigen/test/large_integration_tests/_expected_cjson_bindings.dart
index 98db26e..fe6b008 100644
--- a/pkgs/ffigen/test/large_integration_tests/_expected_cjson_bindings.dart
+++ b/pkgs/ffigen/test/large_integration_tests/_expected_cjson_bindings.dart
@@ -1237,13 +1237,12 @@
 }
 
 class cJSON_Hooks extends ffi.Struct {
-  external ffi
-          .Pointer<ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Size)>>
+  external ffi.Pointer<
+          ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Size sz)>>
       malloc_fn;
 
-  external ffi
-          .Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>
-      free_fn;
+  external ffi.Pointer<
+      ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void> ptr)>> free_fn;
 }
 
 typedef cJSON_bool = ffi.Int;
diff --git a/pkgs/ffigen/test/large_integration_tests/_expected_libclang_bindings.dart b/pkgs/ffigen/test/large_integration_tests/_expected_libclang_bindings.dart
index 47e997a..510e0b0 100644
--- a/pkgs/ffigen/test/large_integration_tests/_expected_libclang_bindings.dart
+++ b/pkgs/ffigen/test/large_integration_tests/_expected_libclang_bindings.dart
@@ -7431,7 +7431,9 @@
 
 /// Visitor invoked for each cursor found by a traversal.
 typedef CXCursorVisitor = ffi.Pointer<
-    ffi.NativeFunction<ffi.Int32 Function(CXCursor, CXCursor, CXClientData)>>;
+    ffi.NativeFunction<
+        ffi.Int32 Function(
+            CXCursor cursor, CXCursor parent, CXClientData client_data)>>;
 
 /// Opaque pointer representing client data that will be passed through to
 /// various callbacks and visitors.
@@ -7761,8 +7763,11 @@
 /// clang_getInclusions()).
 typedef CXInclusionVisitor = ffi.Pointer<
     ffi.NativeFunction<
-        ffi.Void Function(CXFile, ffi.Pointer<CXSourceLocation>,
-            ffi.UnsignedInt, CXClientData)>>;
+        ffi.Void Function(
+            CXFile included_file,
+            ffi.Pointer<CXSourceLocation> inclusion_stack,
+            ffi.UnsignedInt include_len,
+            CXClientData client_data)>>;
 
 abstract class CXEvalResultKind {
   static const int CXEval_Int = 1;
@@ -8124,8 +8129,10 @@
   /// Called periodically to check whether indexing should be aborted. Should
   /// return 0 to continue, and non-zero to abort.
   external ffi.Pointer<
-      ffi.NativeFunction<
-          ffi.Int Function(CXClientData, ffi.Pointer<ffi.Void>)>> abortQuery;
+          ffi.NativeFunction<
+              ffi.Int Function(
+                  CXClientData client_data, ffi.Pointer<ffi.Void> reserved)>>
+      abortQuery;
 
   /// Called at the end of indexing; passes the complete diagnostic set.
   external ffi.Pointer<
@@ -8136,8 +8143,8 @@
 
   external ffi.Pointer<
       ffi.NativeFunction<
-          CXIdxClientFile Function(
-              CXClientData, CXFile, ffi.Pointer<ffi.Void>)>> enteredMainFile;
+          CXIdxClientFile Function(CXClientData client_data, CXFile mainFile,
+              ffi.Pointer<ffi.Void> reserved)>> enteredMainFile;
 
   /// Called when a file gets #included/#imported.
   external ffi.Pointer<
@@ -8155,9 +8162,10 @@
 
   /// Called at the beginning of indexing a translation unit.
   external ffi.Pointer<
-      ffi.NativeFunction<
-          CXIdxClientContainer Function(
-              CXClientData, ffi.Pointer<ffi.Void>)>> startedTranslationUnit;
+          ffi.NativeFunction<
+              CXIdxClientContainer Function(
+                  CXClientData client_data, ffi.Pointer<ffi.Void> reserved)>>
+      startedTranslationUnit;
 
   external ffi.Pointer<
           ffi.NativeFunction<
@@ -8216,8 +8224,9 @@
 }
 
 /// Visitor invoked for each field found by a traversal.
-typedef CXFieldVisitor = ffi
-    .Pointer<ffi.NativeFunction<ffi.Int32 Function(CXCursor, CXClientData)>>;
+typedef CXFieldVisitor = ffi.Pointer<
+    ffi.NativeFunction<
+        ffi.Int32 Function(CXCursor C, CXClientData client_data)>>;
 
 const int CINDEX_VERSION_MAJOR = 0;
 
diff --git a/pkgs/ffigen/test/large_integration_tests/_expected_sqlite_bindings.dart b/pkgs/ffigen/test/large_integration_tests/_expected_sqlite_bindings.dart
index 62480fb..d3b48db 100644
--- a/pkgs/ffigen/test/large_integration_tests/_expected_sqlite_bindings.dart
+++ b/pkgs/ffigen/test/large_integration_tests/_expected_sqlite_bindings.dart
@@ -9585,7 +9585,8 @@
     ffi.Pointer<sqlite3> pBlocked,
     ffi.Pointer<
             ffi.NativeFunction<
-                ffi.Void Function(ffi.Pointer<ffi.Pointer<ffi.Void>>, ffi.Int)>>
+                ffi.Void Function(
+                    ffi.Pointer<ffi.Pointer<ffi.Void>> apArg, ffi.Int nArg)>>
         xNotify,
     ffi.Pointer<ffi.Void> pNotifyArg,
   ) {
@@ -9603,7 +9604,8 @@
               ffi.Pointer<
                   ffi.NativeFunction<
                       ffi.Void Function(
-                          ffi.Pointer<ffi.Pointer<ffi.Void>>, ffi.Int)>>,
+                          ffi.Pointer<ffi.Pointer<ffi.Void>> apArg,
+                          ffi.Int nArg)>>,
               ffi.Pointer<ffi.Void>)>>('sqlite3_unlock_notify');
   late final _sqlite3_unlock_notify = _sqlite3_unlock_notifyPtr.asFunction<
       int Function(
@@ -9611,7 +9613,7 @@
           ffi.Pointer<
               ffi.NativeFunction<
                   ffi.Void Function(
-                      ffi.Pointer<ffi.Pointer<ffi.Void>>, ffi.Int)>>,
+                      ffi.Pointer<ffi.Pointer<ffi.Void>> apArg, ffi.Int nArg)>>,
           ffi.Pointer<ffi.Void>)>();
 
   /// CAPI3REF: String Comparison
@@ -10969,16 +10971,17 @@
       xBestIndex;
 
   external ffi.Pointer<
-          ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab>)>>
+          ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab> pVTab)>>
       xDisconnect;
 
   external ffi.Pointer<
-      ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab>)>> xDestroy;
+          ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab> pVTab)>>
+      xDestroy;
 
   external ffi.Pointer<
       ffi.NativeFunction<
-          ffi.Int Function(ffi.Pointer<sqlite3_vtab>,
-              ffi.Pointer<ffi.Pointer<sqlite3_vtab_cursor>>)>> xOpen;
+          ffi.Int Function(ffi.Pointer<sqlite3_vtab> pVTab,
+              ffi.Pointer<ffi.Pointer<sqlite3_vtab_cursor>> ppCursor)>> xOpen;
 
   external ffi.Pointer<
       ffi.NativeFunction<
@@ -11020,37 +11023,42 @@
               ffi.Pointer<sqlite3_int64>)>> xUpdate;
 
   external ffi.Pointer<
-      ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab>)>> xBegin;
+          ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab> pVTab)>>
+      xBegin;
 
   external ffi.Pointer<
-      ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab>)>> xSync;
+          ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab> pVTab)>>
+      xSync;
 
   external ffi.Pointer<
-      ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab>)>> xCommit;
+          ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab> pVTab)>>
+      xCommit;
 
   external ffi.Pointer<
-          ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab>)>>
+          ffi.NativeFunction<ffi.Int Function(ffi.Pointer<sqlite3_vtab> pVTab)>>
       xRollback;
 
   external ffi.Pointer<
       ffi.NativeFunction<
           ffi.Int Function(
-              ffi.Pointer<sqlite3_vtab>,
-              ffi.Int,
-              ffi.Pointer<ffi.Char>,
+              ffi.Pointer<sqlite3_vtab> pVtab,
+              ffi.Int nArg,
+              ffi.Pointer<ffi.Char> zName,
               ffi.Pointer<
-                  ffi.Pointer<
-                      ffi.NativeFunction<
-                          ffi.Void Function(
-                              ffi.Pointer<sqlite3_context>,
-                              ffi.Int,
-                              ffi.Pointer<ffi.Pointer<sqlite3_value>>)>>>,
-              ffi.Pointer<ffi.Pointer<ffi.Void>>)>> xFindFunction;
+                      ffi.Pointer<
+                          ffi.NativeFunction<
+                              ffi.Void Function(
+                                  ffi.Pointer<sqlite3_context>,
+                                  ffi.Int,
+                                  ffi.Pointer<ffi.Pointer<sqlite3_value>>)>>>
+                  pxFunc,
+              ffi.Pointer<ffi.Pointer<ffi.Void>> ppArg)>> xFindFunction;
 
   external ffi.Pointer<
-      ffi.NativeFunction<
-          ffi.Int Function(
-              ffi.Pointer<sqlite3_vtab>, ffi.Pointer<ffi.Char>)>> xRename;
+          ffi.NativeFunction<
+              ffi.Int Function(
+                  ffi.Pointer<sqlite3_vtab> pVtab, ffi.Pointer<ffi.Char> zNew)>>
+      xRename;
 
   /// The methods above are in version 1 of the sqlite_module object. Those
   /// below are for version 2 and greater.
@@ -11344,9 +11352,9 @@
       xShutdown;
 
   external ffi.Pointer<
-          ffi.NativeFunction<
-              ffi.Pointer<sqlite3_pcache> Function(ffi.Int, ffi.Int, ffi.Int)>>
-      xCreate;
+      ffi.NativeFunction<
+          ffi.Pointer<sqlite3_pcache> Function(
+              ffi.Int szPage, ffi.Int szExtra, ffi.Int bPurgeable)>> xCreate;
 
   external ffi.Pointer<
       ffi.NativeFunction<
@@ -11401,7 +11409,8 @@
 
   external ffi.Pointer<
       ffi.NativeFunction<
-          ffi.Pointer<sqlite3_pcache> Function(ffi.Int, ffi.Int)>> xCreate;
+          ffi.Pointer<sqlite3_pcache> Function(
+              ffi.Int szPage, ffi.Int bPurgeable)>> xCreate;
 
   external ffi.Pointer<
       ffi.NativeFunction<
@@ -11952,48 +11961,48 @@
 
   /// Create a new tokenizer
   external ffi.Pointer<
-          ffi.NativeFunction<
-              ffi.Int Function(
-                  ffi.Pointer<fts5_api>,
-                  ffi.Pointer<ffi.Char>,
-                  ffi.Pointer<ffi.Void>,
-                  ffi.Pointer<fts5_tokenizer>,
-                  ffi.Pointer<
+      ffi.NativeFunction<
+          ffi.Int Function(
+              ffi.Pointer<fts5_api> pApi,
+              ffi.Pointer<ffi.Char> zName,
+              ffi.Pointer<ffi.Void> pContext,
+              ffi.Pointer<fts5_tokenizer> pTokenizer,
+              ffi.Pointer<
                       ffi.NativeFunction<
-                          ffi.Void Function(ffi.Pointer<ffi.Void>)>>)>>
-      xCreateTokenizer;
+                          ffi.Void Function(ffi.Pointer<ffi.Void>)>>
+                  xDestroy)>> xCreateTokenizer;
 
   /// Find an existing tokenizer
   external ffi.Pointer<
       ffi.NativeFunction<
           ffi.Int Function(
-              ffi.Pointer<fts5_api>,
-              ffi.Pointer<ffi.Char>,
-              ffi.Pointer<ffi.Pointer<ffi.Void>>,
-              ffi.Pointer<fts5_tokenizer>)>> xFindTokenizer;
+              ffi.Pointer<fts5_api> pApi,
+              ffi.Pointer<ffi.Char> zName,
+              ffi.Pointer<ffi.Pointer<ffi.Void>> ppContext,
+              ffi.Pointer<fts5_tokenizer> pTokenizer)>> xFindTokenizer;
 
   /// Create a new auxiliary function
   external ffi.Pointer<
-          ffi.NativeFunction<
-              ffi.Int Function(
-                  ffi.Pointer<fts5_api>,
-                  ffi.Pointer<ffi.Char>,
-                  ffi.Pointer<ffi.Void>,
-                  fts5_extension_function,
-                  ffi.Pointer<
+      ffi.NativeFunction<
+          ffi.Int Function(
+              ffi.Pointer<fts5_api> pApi,
+              ffi.Pointer<ffi.Char> zName,
+              ffi.Pointer<ffi.Void> pContext,
+              fts5_extension_function xFunction,
+              ffi.Pointer<
                       ffi.NativeFunction<
-                          ffi.Void Function(ffi.Pointer<ffi.Void>)>>)>>
-      xCreateFunction;
+                          ffi.Void Function(ffi.Pointer<ffi.Void>)>>
+                  xDestroy)>> xCreateFunction;
 }
 
 typedef fts5_extension_function = ffi.Pointer<
     ffi.NativeFunction<
         ffi.Void Function(
-            ffi.Pointer<Fts5ExtensionApi>,
-            ffi.Pointer<Fts5Context>,
-            ffi.Pointer<sqlite3_context>,
-            ffi.Int,
-            ffi.Pointer<ffi.Pointer<sqlite3_value>>)>>;
+            ffi.Pointer<Fts5ExtensionApi> pApi,
+            ffi.Pointer<Fts5Context> pFts,
+            ffi.Pointer<sqlite3_context> pCtx,
+            ffi.Int nVal,
+            ffi.Pointer<ffi.Pointer<sqlite3_value>> apVal)>>;
 
 const String SQLITE_VERSION = '3.32.3';