Better typedefs for `NativeFunction`s (#621)

* Fix #614

* Update test expectations

* Hacky fix

* Refactor

* Fix analysis

* Fix analysis

* Fix analysis
diff --git a/CHANGELOG.md b/CHANGELOG.md
index efc5af3..d9d4b88 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,8 @@
 - Fix return_of_invalid_type analysis error for ObjCBlocks.
 - Fix crash in ObjC methods and blocks that return structs by value.
 - Fix ObjC methods returning instancetype having the wrong type in sublasses.
+- When generating typedefs for `Pointer<NativeFunction<Function>>`, also
+  generate a typedef for the `Function`.
 - Bump min SDK version to 3.2.0-114.0.dev.
 
 # 9.0.1
diff --git a/example/libclang-example/generated_bindings.dart b/example/libclang-example/generated_bindings.dart
index fe7f3c4..a0ae7a2 100644
--- a/example/libclang-example/generated_bindings.dart
+++ b/example/libclang-example/generated_bindings.dart
@@ -9747,10 +9747,10 @@
 ///
 /// The visitor should return one of the \c CXChildVisitResult values
 /// to direct clang_visitCursorChildren().
-typedef CXCursorVisitor = ffi.Pointer<
-    ffi.NativeFunction<
-        ffi.Int32 Function(
-            CXCursor cursor, CXCursor parent, CXClientData client_data)>>;
+typedef CXCursorVisitor
+    = ffi.Pointer<ffi.NativeFunction<CXCursorVisitor_function>>;
+typedef CXCursorVisitor_function = 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,13 +10425,13 @@
 /// the second and third arguments provide the inclusion stack.  The
 /// array is sorted in order of immediate inclusion.  For example,
 /// the first element refers to the location that included 'included_file'.
-typedef CXInclusionVisitor = ffi.Pointer<
-    ffi.NativeFunction<
-        ffi.Void Function(
-            CXFile included_file,
-            ffi.Pointer<CXSourceLocation> inclusion_stack,
-            ffi.UnsignedInt include_len,
-            CXClientData client_data)>>;
+typedef CXInclusionVisitor
+    = ffi.Pointer<ffi.NativeFunction<CXInclusionVisitor_function>>;
+typedef CXInclusionVisitor_function = 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(
@@ -11103,9 +11103,10 @@
 ///
 /// 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 C, CXClientData client_data)>>;
+typedef CXFieldVisitor
+    = ffi.Pointer<ffi.NativeFunction<CXFieldVisitor_function>>;
+typedef CXFieldVisitor_function = 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/lib/src/code_generator/func_type.dart b/lib/src/code_generator/func_type.dart
index d4c74f6..675c85b 100644
--- a/lib/src/code_generator/func_type.dart
+++ b/lib/src/code_generator/func_type.dart
@@ -113,26 +113,35 @@
 
 /// Represents a NativeFunction<Function>.
 class NativeFunc extends Type {
-  final FunctionType type;
+  // Either a FunctionType or a Typealias of a FunctionType.
+  final Type _type;
 
-  NativeFunc(this.type);
+  NativeFunc(this._type) {
+    assert(_type is FunctionType || _type is Typealias);
+  }
+
+  FunctionType get type {
+    if (_type is Typealias) {
+      return _type.typealiasType as FunctionType;
+    }
+    return _type as FunctionType;
+  }
 
   @override
   void addDependencies(Set<Binding> dependencies) {
-    type.addDependencies(dependencies);
+    _type.addDependencies(dependencies);
   }
 
   @override
   String getCType(Writer w) =>
-      '${w.ffiLibraryPrefix}.NativeFunction<${type.getCType(w)}>';
+      '${w.ffiLibraryPrefix}.NativeFunction<${_type.getCType(w)}>';
 
   @override
-  String getDartType(Writer w) =>
-      '${w.ffiLibraryPrefix}.NativeFunction<${type.getCType(w)}>';
+  String getDartType(Writer w) => getCType(w);
 
   @override
-  String toString() => 'NativeFunction<${type.toString()}>';
+  String toString() => 'NativeFunction<${_type.toString()}>';
 
   @override
-  String cacheKey() => 'NatFn(${type.cacheKey()})';
+  String cacheKey() => 'NatFn(${_type.cacheKey()})';
 }
diff --git a/lib/src/code_generator/typealias.dart b/lib/src/code_generator/typealias.dart
index 6a47f61..c7d95ca 100644
--- a/lib/src/code_generator/typealias.dart
+++ b/lib/src/code_generator/typealias.dart
@@ -18,16 +18,45 @@
   final Type type;
   final bool _useDartType;
 
-  Typealias({
+  factory Typealias({
+    String? usr,
+    String? originalName,
+    String? dartDoc,
+    required String name,
+    required Type type,
+
+    /// If true, the binding string uses Dart type instead of C type.
+    ///
+    /// E.g if C type is ffi.Void func(ffi.Int32), Dart type is void func(int).
+    bool useDartType = false,
+    bool isInternal = false,
+  }) {
+    final funcType = _getFunctionTypeFromPointer(type);
+    if (funcType != null) {
+      type = PointerType(NativeFunc(Typealias._(
+        name: '${name}_function',
+        type: funcType,
+        useDartType: useDartType,
+        isInternal: isInternal,
+      )));
+    }
+    return Typealias._(
+      usr: usr,
+      originalName: originalName,
+      dartDoc: dartDoc,
+      name: name,
+      type: type,
+      useDartType: useDartType,
+      isInternal: isInternal,
+    );
+  }
+
+  Typealias._({
     String? usr,
     String? originalName,
     String? dartDoc,
     required String name,
     required this.type,
-
-    /// If true, the binding string uses Dart type instead of C type.
-    ///
-    /// E.g if C type is ffi.Void func(ffi.Int32), Dart type is void func(int).
     bool useDartType = false,
     bool isInternal = false,
   })  : _useDartType = useDartType,
@@ -47,6 +76,13 @@
     type.addDependencies(dependencies);
   }
 
+  static FunctionType? _getFunctionTypeFromPointer(Type type) {
+    if (type is! PointerType) return null;
+    final pointee = type.child;
+    if (pointee is! NativeFunc) return null;
+    return pointee.type;
+  }
+
   @override
   BindingString toBindingString(Writer w) {
     final sb = StringBuffer();
diff --git a/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart b/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart
index 8455b7a..a6f4456 100644
--- a/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart
+++ b/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart
@@ -68,8 +68,8 @@
   late final _func4 = _func4Ptr.asFunction<void Function(Typedef1)>();
 }
 
-typedef Typedef1
-    = ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Handle)>>;
+typedef Typedef1 = ffi.Pointer<ffi.NativeFunction<Typedef1_function>>;
+typedef Typedef1_function = ffi.Void Function(ffi.Handle);
 
 final class Struct1 extends ffi.Opaque {}
 
diff --git a/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart b/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart
index f77a57b..ca4e5e6 100644
--- a/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart
+++ b/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart
@@ -73,11 +73,16 @@
 }
 
 typedef WithTypedefReturnType
-    = ffi.Pointer<ffi.NativeFunction<InsideReturnType Function()>>;
-typedef InsideReturnType = ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>;
+    = ffi.Pointer<ffi.NativeFunction<WithTypedefReturnType_function>>;
+typedef WithTypedefReturnType_function = InsideReturnType Function();
+typedef InsideReturnType
+    = ffi.Pointer<ffi.NativeFunction<InsideReturnType_function>>;
+typedef InsideReturnType_function = ffi.Void Function();
 
 final class Struct2 extends ffi.Struct {
   external VoidFuncPointer constFuncPointer;
 }
 
-typedef VoidFuncPointer = ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>;
+typedef VoidFuncPointer
+    = ffi.Pointer<ffi.NativeFunction<VoidFuncPointer_function>>;
+typedef VoidFuncPointer_function = ffi.Void Function();
diff --git a/test/header_parser_tests/expected_bindings/_expected_struct_fptr_fields_bindings.dart b/test/header_parser_tests/expected_bindings/_expected_struct_fptr_fields_bindings.dart
index 2a769db..8dc7297 100644
--- a/test/header_parser_tests/expected_bindings/_expected_struct_fptr_fields_bindings.dart
+++ b/test/header_parser_tests/expected_bindings/_expected_struct_fptr_fields_bindings.dart
@@ -69,4 +69,5 @@
 }
 
 typedef ArithmeticOperation
-    = ffi.Pointer<ffi.NativeFunction<ffi.Int Function(ffi.Int a, ffi.Int b)>>;
+    = ffi.Pointer<ffi.NativeFunction<ArithmeticOperation_function>>;
+typedef ArithmeticOperation_function = ffi.Int Function(ffi.Int a, ffi.Int b);
diff --git a/test/header_parser_tests/expected_bindings/_expected_typedef_bindings.dart b/test/header_parser_tests/expected_bindings/_expected_typedef_bindings.dart
index 885f641..b0b171a 100644
--- a/test/header_parser_tests/expected_bindings/_expected_typedef_bindings.dart
+++ b/test/header_parser_tests/expected_bindings/_expected_typedef_bindings.dart
@@ -92,7 +92,8 @@
 }
 
 typedef NamedFunctionProto
-    = ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>;
+    = ffi.Pointer<ffi.NativeFunction<NamedFunctionProto_function>>;
+typedef NamedFunctionProto_function = ffi.Void Function();
 
 final class AnonymousStructInTypedef extends ffi.Opaque {}
 
diff --git a/test/large_integration_tests/_expected_libclang_bindings.dart b/test/large_integration_tests/_expected_libclang_bindings.dart
index 2bc7688..d30109c 100644
--- a/test/large_integration_tests/_expected_libclang_bindings.dart
+++ b/test/large_integration_tests/_expected_libclang_bindings.dart
@@ -7430,10 +7430,10 @@
 }
 
 /// Visitor invoked for each cursor found by a traversal.
-typedef CXCursorVisitor = ffi.Pointer<
-    ffi.NativeFunction<
-        ffi.Int32 Function(
-            CXCursor cursor, CXCursor parent, CXClientData client_data)>>;
+typedef CXCursorVisitor
+    = ffi.Pointer<ffi.NativeFunction<CXCursorVisitor_function>>;
+typedef CXCursorVisitor_function = 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,13 +7761,13 @@
 
 /// Visitor invoked for each file in a translation unit (used with
 /// clang_getInclusions()).
-typedef CXInclusionVisitor = ffi.Pointer<
-    ffi.NativeFunction<
-        ffi.Void Function(
-            CXFile included_file,
-            ffi.Pointer<CXSourceLocation> inclusion_stack,
-            ffi.UnsignedInt include_len,
-            CXClientData client_data)>>;
+typedef CXInclusionVisitor
+    = ffi.Pointer<ffi.NativeFunction<CXInclusionVisitor_function>>;
+typedef CXInclusionVisitor_function = 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;
@@ -8224,9 +8224,10 @@
 }
 
 /// Visitor invoked for each field found by a traversal.
-typedef CXFieldVisitor = ffi.Pointer<
-    ffi
-    .NativeFunction<ffi.Int32 Function(CXCursor C, CXClientData client_data)>>;
+typedef CXFieldVisitor
+    = ffi.Pointer<ffi.NativeFunction<CXFieldVisitor_function>>;
+typedef CXFieldVisitor_function = ffi.Int32 Function(
+    CXCursor C, CXClientData client_data);
 
 const int CINDEX_VERSION_MAJOR = 0;
 
diff --git a/test/large_integration_tests/_expected_sqlite_bindings.dart b/test/large_integration_tests/_expected_sqlite_bindings.dart
index 984fad3..492da42 100644
--- a/test/large_integration_tests/_expected_sqlite_bindings.dart
+++ b/test/large_integration_tests/_expected_sqlite_bindings.dart
@@ -10855,7 +10855,8 @@
 typedef sqlite3_int64 = sqlite_int64;
 typedef sqlite_int64 = ffi.LongLong;
 typedef sqlite3_syscall_ptr
-    = ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>;
+    = ffi.Pointer<ffi.NativeFunction<sqlite3_syscall_ptr_function>>;
+typedef sqlite3_syscall_ptr_function = ffi.Void Function();
 
 final class sqlite3_mem_methods extends ffi.Struct {
   /// Memory allocation function
@@ -12008,14 +12009,14 @@
                   xDestroy)>> xCreateFunction;
 }
 
-typedef fts5_extension_function = ffi.Pointer<
-    ffi.NativeFunction<
-        ffi.Void Function(
-            ffi.Pointer<Fts5ExtensionApi> pApi,
-            ffi.Pointer<Fts5Context> pFts,
-            ffi.Pointer<sqlite3_context> pCtx,
-            ffi.Int nVal,
-            ffi.Pointer<ffi.Pointer<sqlite3_value>> apVal)>>;
+typedef fts5_extension_function
+    = ffi.Pointer<ffi.NativeFunction<fts5_extension_function_function>>;
+typedef fts5_extension_function_function = ffi.Void Function(
+    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';