[ffigen] Typedefs for Dart types (#625)

* WIP fix #386

* Fix tests

* fmt

* Update test expectations

* Update test expectations

* Fix analysis

* fmt

* Fix more tests

* WIP refactor

* Refactor `sameFfiDartAndCType` to not require a `Writer`

* Fix tests

* Fix analysis

* Update test expectations

* Format

* Fix typedef_test.dart

* Fix nits

* Fix tests

* Fmt

* Regenerate more test files

* Regen more tests

* Remove accidental commit
diff --git a/pkgs/ffigen/example/c_json/cjson_generated_bindings.dart b/pkgs/ffigen/example/c_json/cjson_generated_bindings.dart
index fa9e56e..38bc263 100644
--- a/pkgs/ffigen/example/c_json/cjson_generated_bindings.dart
+++ b/pkgs/ffigen/example/c_json/cjson_generated_bindings.dart
@@ -1268,6 +1268,7 @@
 }
 
 typedef cJSON_bool = ffi.Int;
+typedef DartcJSON_bool = int;
 
 const int CJSON_VERSION_MAJOR = 1;
 
diff --git a/pkgs/ffigen/example/libclang-example/generated_bindings.dart b/pkgs/ffigen/example/libclang-example/generated_bindings.dart
index a0ae7a2..7cf663e 100644
--- a/pkgs/ffigen/example/libclang-example/generated_bindings.dart
+++ b/pkgs/ffigen/example/libclang-example/generated_bindings.dart
@@ -9748,8 +9748,10 @@
 /// The visitor should return one of the \c CXChildVisitResult values
 /// to direct clang_visitCursorChildren().
 typedef CXCursorVisitor
-    = ffi.Pointer<ffi.NativeFunction<CXCursorVisitor_function>>;
-typedef CXCursorVisitor_function = ffi.Int32 Function(
+    = ffi.Pointer<ffi.NativeFunction<CXCursorVisitorFunction>>;
+typedef CXCursorVisitorFunction = ffi.Int32 Function(
+    CXCursor cursor, CXCursor parent, CXClientData client_data);
+typedef DartCXCursorVisitorFunction = int Function(
     CXCursor cursor, CXCursor parent, CXClientData client_data);
 
 /// Describes how the traversal of the children of a particular
@@ -10426,12 +10428,17 @@
 /// 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<CXInclusionVisitor_function>>;
-typedef CXInclusionVisitor_function = ffi.Void Function(
+    = ffi.Pointer<ffi.NativeFunction<CXInclusionVisitorFunction>>;
+typedef CXInclusionVisitorFunction = ffi.Void Function(
     CXFile included_file,
     ffi.Pointer<CXSourceLocation> inclusion_stack,
     ffi.UnsignedInt include_len,
     CXClientData client_data);
+typedef DartCXInclusionVisitorFunction = void Function(
+    CXFile included_file,
+    ffi.Pointer<CXSourceLocation> inclusion_stack,
+    int include_len,
+    CXClientData client_data);
 typedef NativeClang_getInclusions = ffi.Void Function(
     CXTranslationUnit tu, CXInclusionVisitor visitor, CXClientData client_data);
 typedef DartClang_getInclusions = void Function(
@@ -11104,8 +11111,10 @@
 /// The visitor should return one of the \c CXVisitorResult values
 /// to direct \c clang_Type_visitFields.
 typedef CXFieldVisitor
-    = ffi.Pointer<ffi.NativeFunction<CXFieldVisitor_function>>;
-typedef CXFieldVisitor_function = ffi.Int32 Function(
+    = ffi.Pointer<ffi.NativeFunction<CXFieldVisitorFunction>>;
+typedef CXFieldVisitorFunction = ffi.Int32 Function(
+    CXCursor C, CXClientData client_data);
+typedef DartCXFieldVisitorFunction = int Function(
     CXCursor C, CXClientData client_data);
 typedef NativeClang_Type_visitFields = ffi.UnsignedInt Function(
     CXType T, CXFieldVisitor visitor, CXClientData client_data);
diff --git a/pkgs/ffigen/lib/src/code_generator/func.dart b/pkgs/ffigen/lib/src/code_generator/func.dart
index f8a8177..b3141ae 100644
--- a/pkgs/ffigen/lib/src/code_generator/func.dart
+++ b/pkgs/ffigen/lib/src/code_generator/func.dart
@@ -46,8 +46,7 @@
   late final String funcPointerName;
 
   /// Contains typealias for function type if [exposeFunctionTypedefs] is true.
-  Typealias? _exposedCFunctionTypealias;
-  Typealias? _exposedDartFunctionTypealias;
+  Typealias? _exposedFunctionTypealias;
 
   /// [originalName] is looked up in dynamic library, if not
   /// provided, takes the value of [name].
@@ -85,15 +84,10 @@
     // Get function name with first letter in upper case.
     final upperCaseName = name[0].toUpperCase() + name.substring(1);
     if (exposeFunctionTypedefs) {
-      _exposedCFunctionTypealias = Typealias(
-        name: 'Native$upperCaseName',
+      _exposedFunctionTypealias = Typealias(
+        name: upperCaseName,
         type: functionType,
-        isInternal: true,
-      );
-      _exposedDartFunctionTypealias = Typealias(
-        name: 'Dart$upperCaseName',
-        type: functionType,
-        useDartType: true,
+        genFfiDartType: true,
         isInternal: true,
       );
     }
@@ -115,12 +109,10 @@
       p.name = paramNamer.makeUnique(p.name);
     }
 
-    final cType = exposeFunctionTypedefs
-        ? _exposedCFunctionTypealias!.name
-        : functionType.getCType(w, writeArgumentNames: false);
-    final dartType = exposeFunctionTypedefs
-        ? _exposedDartFunctionTypealias!.name
-        : functionType.getFfiDartType(w, writeArgumentNames: false);
+    final cType = _exposedFunctionTypealias?.getCType(w) ??
+        functionType.getCType(w, writeArgumentNames: false);
+    final dartType = _exposedFunctionTypealias?.getFfiDartType(w) ??
+        functionType.getFfiDartType(w, writeArgumentNames: false);
 
     if (ffiNativeConfig.enabled) {
       final assetString = ffiNativeConfig.asset != null
@@ -180,8 +172,7 @@
     dependencies.add(this);
     functionType.addDependencies(dependencies);
     if (exposeFunctionTypedefs) {
-      _exposedCFunctionTypealias!.addDependencies(dependencies);
-      _exposedDartFunctionTypealias!.addDependencies(dependencies);
+      _exposedFunctionTypealias!.addDependencies(dependencies);
     }
   }
 }
diff --git a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
index 421a508..c79ccca 100644
--- a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
+++ b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
@@ -314,9 +314,11 @@
     }
   }
 
-  static bool _isInstanceType(Type type) =>
-      type is ObjCInstanceType ||
-      (type is ObjCNullable && type.child is ObjCInstanceType);
+  static bool _isInstanceType(Type type) {
+    if (type is ObjCInstanceType) return true;
+    final baseType = type.typealiasType;
+    return baseType is ObjCNullable && baseType.child is ObjCInstanceType;
+  }
 
   void addMethod(ObjCMethod method) {
     final oldMethod = methods[method.originalName];
@@ -400,27 +402,29 @@
   // passed to native as Pointer<ObjCObject>, but the user sees the Dart wrapper
   // class. These methods need to be kept in sync.
   bool _needsConverting(Type type) =>
-      type is ObjCInterface ||
-      type is ObjCBlock ||
-      type is ObjCObjectPointer ||
       type is ObjCInstanceType ||
-      type is ObjCNullable;
+      type.typealiasType is ObjCInterface ||
+      type.typealiasType is ObjCBlock ||
+      type.typealiasType is ObjCObjectPointer ||
+      type.typealiasType is ObjCNullable;
 
   String _getConvertedType(Type type, Writer w, String enclosingClass) {
     if (type is ObjCInstanceType) return enclosingClass;
-    if (type is ObjCNullable && type.child is ObjCInstanceType) {
+    final baseType = type.typealiasType;
+    if (baseType is ObjCNullable && baseType.child is ObjCInstanceType) {
       return '$enclosingClass?';
     }
     return type.getDartType(w);
   }
 
   String _doArgConversion(ObjCMethodParam arg) {
-    if (arg.type is ObjCNullable) {
+    final baseType = arg.type.typealiasType;
+    if (baseType is ObjCNullable) {
       return '${arg.name}?._id ?? ffi.nullptr';
-    } else if (arg.type is ObjCInterface ||
-        arg.type is ObjCObjectPointer ||
-        arg.type is ObjCInstanceType ||
-        arg.type is ObjCBlock) {
+    } else if (arg.type is ObjCInstanceType ||
+        baseType is ObjCInterface ||
+        baseType is ObjCObjectPointer ||
+        baseType is ObjCBlock) {
       return '${arg.name}._id';
     }
     return arg.name;
@@ -429,23 +433,25 @@
   String _doReturnConversion(Type type, String value, String enclosingClass,
       String library, bool isOwnedReturn) {
     var prefix = '';
-    if (type is ObjCNullable) {
+    var baseType = type.typealiasType;
+    if (baseType is ObjCNullable) {
       prefix = '$value.address == 0 ? null : ';
-      type = type.child;
+      type = baseType.child;
+      baseType = type.typealiasType;
     }
     final ownerFlags = 'retain: ${!isOwnedReturn}, release: true';
-    if (type is ObjCInterface) {
-      return '$prefix${type.name}._($value, $library, $ownerFlags)';
-    }
-    if (type is ObjCBlock) {
-      return '$prefix${type.name}._($value, $library)';
-    }
-    if (type is ObjCObjectPointer) {
-      return '${prefix}NSObject._($value, $library, $ownerFlags)';
-    }
     if (type is ObjCInstanceType) {
       return '$prefix$enclosingClass._($value, $library, $ownerFlags)';
     }
+    if (baseType is ObjCInterface) {
+      return '$prefix${baseType.name}._($value, $library, $ownerFlags)';
+    }
+    if (baseType is ObjCBlock) {
+      return '$prefix${baseType.name}._($value, $library)';
+    }
+    if (baseType is ObjCObjectPointer) {
+      return '${prefix}NSObject._($value, $library, $ownerFlags)';
+    }
     return prefix + value;
   }
 }
diff --git a/pkgs/ffigen/lib/src/code_generator/typealias.dart b/pkgs/ffigen/lib/src/code_generator/typealias.dart
index 5c5cc44..ec9430e 100644
--- a/pkgs/ffigen/lib/src/code_generator/typealias.dart
+++ b/pkgs/ffigen/lib/src/code_generator/typealias.dart
@@ -17,27 +17,28 @@
 /// ```
 class Typealias extends BindingType {
   final Type type;
-  final bool _useDartType;
+  String? _ffiDartAliasName;
+  String? _dartAliasName;
 
+  /// Creates a Typealias.
+  ///
+  /// If [genFfiDartType] is true, a binding is generated for the Ffi Dart type
+  /// in addition to the C type. See [Type.getFfiDartType].
   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 genFfiDartType = false,
     bool isInternal = false,
   }) {
     final funcType = _getFunctionTypeFromPointer(type);
     if (funcType != null) {
       type = PointerType(NativeFunc(Typealias._(
-        name: '${name}_function',
+        name: '${name}Function',
         type: funcType,
-        useDartType: useDartType,
+        genFfiDartType: genFfiDartType,
         isInternal: isInternal,
       )));
     }
@@ -49,7 +50,7 @@
         dartDoc: dartDoc,
         name: name,
         type: type,
-        useDartType: useDartType,
+        genFfiDartType: genFfiDartType,
         isInternal: isInternal,
       );
     }
@@ -59,7 +60,7 @@
       dartDoc: dartDoc,
       name: name,
       type: type,
-      useDartType: useDartType,
+      genFfiDartType: genFfiDartType,
       isInternal: isInternal,
     );
   }
@@ -70,12 +71,16 @@
     String? dartDoc,
     required String name,
     required this.type,
-    bool useDartType = false,
+    bool genFfiDartType = false,
     bool isInternal = false,
-  })  : _useDartType = useDartType,
+  })  : _ffiDartAliasName = genFfiDartType ? 'Dart$name' : null,
+        _dartAliasName =
+            (!genFfiDartType && type is! Typealias && !type.sameDartAndCType)
+                ? 'Dart$name'
+                : null,
         super(
           usr: usr,
-          name: name,
+          name: genFfiDartType ? 'Native$name' : name,
           dartDoc: dartDoc,
           originalName: originalName,
           isInternal: isInternal,
@@ -98,12 +103,24 @@
 
   @override
   BindingString toBindingString(Writer w) {
+    if (_ffiDartAliasName != null) {
+      _ffiDartAliasName = w.topLevelUniqueNamer.makeUnique(_ffiDartAliasName!);
+    }
+    if (_dartAliasName != null) {
+      _dartAliasName = w.topLevelUniqueNamer.makeUnique(_dartAliasName!);
+    }
+
     final sb = StringBuffer();
     if (dartDoc != null) {
       sb.write(makeDartDoc(dartDoc!));
     }
-    sb.write('typedef $name = ');
-    sb.write('${_useDartType ? type.getFfiDartType(w) : type.getCType(w)};\n');
+    sb.write('typedef $name = ${type.getCType(w)};\n');
+    if (_ffiDartAliasName != null) {
+      sb.write('typedef $_ffiDartAliasName = ${type.getFfiDartType(w)};\n');
+    }
+    if (_dartAliasName != null) {
+      sb.write('typedef $_dartAliasName = ${type.getDartType(w)};\n');
+    }
     return BindingString(
         type: BindingStringType.typeDef, string: sb.toString());
   }
@@ -119,9 +136,9 @@
 
   @override
   String getFfiDartType(Writer w) {
-    // Typealias cannot be used by name in Dart types unless both the C and Dart
-    // type of the underlying types are same.
-    if (type.sameFfiDartAndCType) {
+    if (_ffiDartAliasName != null) {
+      return _ffiDartAliasName!;
+    } else if (type.sameFfiDartAndCType) {
       return name;
     } else {
       return type.getFfiDartType(w);
@@ -129,6 +146,9 @@
   }
 
   @override
+  String getDartType(Writer w) => _dartAliasName ?? type.getDartType(w);
+
+  @override
   bool get sameFfiDartAndCType => type.sameFfiDartAndCType;
 
   @override
@@ -154,11 +174,7 @@
     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 genFfiDartType = false,
     bool isInternal = false,
   }) : super._(
           usr: usr,
@@ -166,7 +182,7 @@
           dartDoc: dartDoc,
           name: name,
           type: type,
-          useDartType: useDartType,
+          genFfiDartType: genFfiDartType,
           isInternal: isInternal,
         );
 }
diff --git a/pkgs/ffigen/test/collision_tests/decl_decl_collision_test.dart b/pkgs/ffigen/test/collision_tests/decl_decl_collision_test.dart
index 4529f29..1edcaeb 100644
--- a/pkgs/ffigen/test/collision_tests/decl_decl_collision_test.dart
+++ b/pkgs/ffigen/test/collision_tests/decl_decl_collision_test.dart
@@ -13,7 +13,7 @@
       logWarnings(Level.SEVERE);
     });
     test('declaration conflict', () {
-      final l1 = Library(name: 'Bindings', bindings: [
+      final library = Library(name: 'Bindings', bindings: [
         Struct(name: 'TestStruct'),
         Struct(name: 'TestStruct'),
         EnumClass(name: 'TestEnum'),
@@ -53,49 +53,13 @@
         Struct(name: 'ffi'),
         Func(name: 'ffi1', returnType: NativeType(SupportedNativeType.Void)),
       ]);
-      final l2 = Library(name: 'Bindings', bindings: [
-        Struct(name: 'TestStruct'),
-        Struct(name: 'TestStruct1'),
-        EnumClass(name: 'TestEnum'),
-        EnumClass(name: 'TestEnum1'),
-        Func(
-            name: 'testFunc',
-            originalName: 'testFunc',
-            returnType: NativeType(SupportedNativeType.Void)),
-        Func(
-            name: 'testFunc1',
-            originalName: 'testFunc',
-            returnType: NativeType(SupportedNativeType.Void)),
-        Constant(
-          originalName: 'Test_Macro',
-          name: 'Test_Macro',
-          rawType: 'int',
-          rawValue: '0',
-        ),
-        Constant(
-          originalName: 'Test_Macro',
-          name: 'Test_Macro1',
-          rawType: 'int',
-          rawValue: '0',
-        ),
-        Typealias(
-            name: 'testAlias', type: NativeType(SupportedNativeType.Void)),
-        Typealias(
-            name: 'testAlias1', type: NativeType(SupportedNativeType.Void)),
-        Struct(name: 'testCrossDecl', originalName: 'testCrossDecl'),
-        Func(
-            name: 'testCrossDecl1',
-            originalName: 'testCrossDecl',
-            returnType: NativeType(SupportedNativeType.Void)),
-        Constant(name: 'testCrossDecl2', rawValue: '0', rawType: 'int'),
-        EnumClass(name: 'testCrossDecl3'),
-        Typealias(
-            name: 'testCrossDecl4', type: NativeType(SupportedNativeType.Void)),
-        Struct(name: 'ffi'),
-        Func(name: 'ffi1', returnType: NativeType(SupportedNativeType.Void)),
+      matchLibraryWithExpected(
+          library, 'decl_decl_collision_test_output.dart', [
+        'test',
+        'collision_tests',
+        'expected_bindings',
+        '_expected_decl_decl_collision_bindings.dart',
       ]);
-
-      expect(l1.generate(), l2.generate());
     });
   });
 }
diff --git a/pkgs/ffigen/test/collision_tests/expected_bindings/_expected_decl_decl_collision_bindings.dart b/pkgs/ffigen/test/collision_tests/expected_bindings/_expected_decl_decl_collision_bindings.dart
new file mode 100644
index 0000000..32388b6
--- /dev/null
+++ b/pkgs/ffigen/test/collision_tests/expected_bindings/_expected_decl_decl_collision_bindings.dart
@@ -0,0 +1,80 @@
+// AUTO GENERATED FILE, DO NOT EDIT.
+//
+// Generated by `package:ffigen`.
+// ignore_for_file: type=lint
+import 'dart:ffi' as ffi;
+
+class Bindings {
+  /// Holds the symbol lookup function.
+  final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
+      _lookup;
+
+  /// The symbols are looked up in [dynamicLibrary].
+  Bindings(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup;
+
+  /// The symbols are looked up with [lookup].
+  Bindings.fromLookup(
+      ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
+          lookup)
+      : _lookup = lookup;
+
+  void testFunc() {
+    return _testFunc();
+  }
+
+  late final _testFuncPtr =
+      _lookup<ffi.NativeFunction<ffi.Void Function()>>('testFunc');
+  late final _testFunc = _testFuncPtr.asFunction<void Function()>();
+
+  void testFunc1() {
+    return _testFunc1();
+  }
+
+  late final _testFunc1Ptr =
+      _lookup<ffi.NativeFunction<ffi.Void Function()>>('testFunc');
+  late final _testFunc1 = _testFunc1Ptr.asFunction<void Function()>();
+
+  void testCrossDecl1() {
+    return _testCrossDecl1();
+  }
+
+  late final _testCrossDecl1Ptr =
+      _lookup<ffi.NativeFunction<ffi.Void Function()>>('testCrossDecl');
+  late final _testCrossDecl1 = _testCrossDecl1Ptr.asFunction<void Function()>();
+
+  void ffi1() {
+    return _ffi1();
+  }
+
+  late final _ffi1Ptr =
+      _lookup<ffi.NativeFunction<ffi.Void Function()>>('ffi1');
+  late final _ffi1 = _ffi1Ptr.asFunction<void Function()>();
+}
+
+final class TestStruct extends ffi.Opaque {}
+
+final class TestStruct1 extends ffi.Opaque {}
+
+abstract class TestEnum {}
+
+abstract class TestEnum1 {}
+
+const int Test_Macro = 0;
+
+const int Test_Macro1 = 0;
+
+typedef testAlias = ffi.Void;
+typedef DarttestAlias = void;
+typedef testAlias1 = ffi.Void;
+typedef DarttestAlias1 = void;
+
+final class testCrossDecl extends ffi.Opaque {}
+
+const int testCrossDecl2 = 0;
+
+abstract class testCrossDecl3 {}
+
+typedef testCrossDecl4 = ffi.Void;
+typedef DarttestCrossDecl = void;
+
+final class ffi extends ffi.Opaque {}
diff --git a/pkgs/ffigen/test/collision_tests/expected_bindings/_expected_reserved_keyword_collision_bindings.dart b/pkgs/ffigen/test/collision_tests/expected_bindings/_expected_reserved_keyword_collision_bindings.dart
new file mode 100644
index 0000000..c788b49
--- /dev/null
+++ b/pkgs/ffigen/test/collision_tests/expected_bindings/_expected_reserved_keyword_collision_bindings.dart
@@ -0,0 +1,61 @@
+// AUTO GENERATED FILE, DO NOT EDIT.
+//
+// Generated by `package:ffigen`.
+// ignore_for_file: type=lint
+import 'dart:ffi' as ffi;
+
+class Bindings {
+  /// Holds the symbol lookup function.
+  final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
+      _lookup;
+
+  /// The symbols are looked up in [dynamicLibrary].
+  Bindings(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup;
+
+  /// The symbols are looked up with [lookup].
+  Bindings.fromLookup(
+      ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
+          lookup)
+      : _lookup = lookup;
+
+  void show1() {
+    return _show1();
+  }
+
+  late final _show1Ptr =
+      _lookup<ffi.NativeFunction<ffi.Void Function()>>('show');
+  late final _show1 = _show1Ptr.asFunction<void Function()>();
+
+  void implements1(
+    int if1,
+    int abstract1,
+    int in1,
+  ) {
+    return _implements1(
+      if1,
+      abstract1,
+      in1,
+    );
+  }
+
+  late final _implements1Ptr =
+      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int, ffi.Int, ffi.Int)>>(
+          'implements');
+  late final _implements1 =
+      _implements1Ptr.asFunction<void Function(int, int, int)>();
+}
+
+final class abstract1 extends ffi.Opaque {}
+
+final class abstract2 extends ffi.Opaque {}
+
+final class if1 extends ffi.Opaque {}
+
+abstract class return1 {}
+
+abstract class export1 {}
+
+const int else1 = 0;
+
+typedef var1 = ffi.Void;
+typedef Dartvar = void;
diff --git a/pkgs/ffigen/test/collision_tests/reserved_keyword_collision_test.dart b/pkgs/ffigen/test/collision_tests/reserved_keyword_collision_test.dart
index c6873f2..88f353a 100644
--- a/pkgs/ffigen/test/collision_tests/reserved_keyword_collision_test.dart
+++ b/pkgs/ffigen/test/collision_tests/reserved_keyword_collision_test.dart
@@ -14,7 +14,7 @@
       logWarnings(Level.SEVERE);
     });
     test('reserved keyword collision', () {
-      final l1 = Library(name: 'Bindings', bindings: [
+      final library = Library(name: 'Bindings', bindings: [
         Struct(name: 'abstract'),
         Struct(name: 'abstract'),
         Struct(name: 'if'),
@@ -45,42 +45,13 @@
         ),
         Typealias(name: 'var', type: NativeType(SupportedNativeType.Void)),
       ]);
-      final l2 = Library(name: 'Bindings', bindings: [
-        Struct(name: 'abstract1'),
-        Struct(name: 'abstract2'),
-        Struct(name: 'if1'),
-        EnumClass(name: 'return1'),
-        EnumClass(name: 'export1'),
-        Func(
-            name: 'show1',
-            originalName: 'show',
-            returnType: NativeType(SupportedNativeType.Void)),
-        Func(
-            name: 'implements1',
-            originalName: 'implements',
-            parameters: [
-              Parameter(
-                type: intType,
-                name: 'if1',
-              ),
-              Parameter(
-                type: intType,
-                name: 'abstract1',
-              ),
-              Parameter(
-                type: intType,
-                name: 'in1',
-              ),
-            ],
-            returnType: NativeType(SupportedNativeType.Void)),
-        Constant(
-          name: 'else1',
-          rawType: 'int',
-          rawValue: '0',
-        ),
-        Typealias(name: 'var1', type: NativeType(SupportedNativeType.Void)),
+      matchLibraryWithExpected(
+          library, 'reserved_keyword_collision_test_output.dart', [
+        'test',
+        'collision_tests',
+        'expected_bindings',
+        '_expected_reserved_keyword_collision_bindings.dart',
       ]);
-      expect(l1.generate(), l2.generate());
     });
   });
 }
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart
index a6f4456..6106985 100644
--- a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart
+++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart
@@ -68,8 +68,9 @@
   late final _func4 = _func4Ptr.asFunction<void Function(Typedef1)>();
 }
 
-typedef Typedef1 = ffi.Pointer<ffi.NativeFunction<Typedef1_function>>;
-typedef Typedef1_function = ffi.Void Function(ffi.Handle);
+typedef Typedef1 = ffi.Pointer<ffi.NativeFunction<Typedef1Function>>;
+typedef Typedef1Function = ffi.Void Function(ffi.Handle);
+typedef DartTypedef1Function = void Function(Object);
 
 final class Struct1 extends ffi.Opaque {}
 
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 ca4e5e6..b3063c4 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
@@ -73,16 +73,18 @@
 }
 
 typedef WithTypedefReturnType
-    = ffi.Pointer<ffi.NativeFunction<WithTypedefReturnType_function>>;
-typedef WithTypedefReturnType_function = InsideReturnType Function();
+    = ffi.Pointer<ffi.NativeFunction<WithTypedefReturnTypeFunction>>;
+typedef WithTypedefReturnTypeFunction = InsideReturnType Function();
 typedef InsideReturnType
-    = ffi.Pointer<ffi.NativeFunction<InsideReturnType_function>>;
-typedef InsideReturnType_function = ffi.Void Function();
+    = ffi.Pointer<ffi.NativeFunction<InsideReturnTypeFunction>>;
+typedef InsideReturnTypeFunction = ffi.Void Function();
+typedef DartInsideReturnTypeFunction = void Function();
 
 final class Struct2 extends ffi.Struct {
   external VoidFuncPointer constFuncPointer;
 }
 
 typedef VoidFuncPointer
-    = ffi.Pointer<ffi.NativeFunction<VoidFuncPointer_function>>;
-typedef VoidFuncPointer_function = ffi.Void Function();
+    = ffi.Pointer<ffi.NativeFunction<VoidFuncPointerFunction>>;
+typedef VoidFuncPointerFunction = ffi.Void Function();
+typedef DartVoidFuncPointerFunction = void Function();
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
index 8dc7297..f5af81f 100644
--- 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
@@ -69,5 +69,6 @@
 }
 
 typedef ArithmeticOperation
-    = ffi.Pointer<ffi.NativeFunction<ArithmeticOperation_function>>;
-typedef ArithmeticOperation_function = ffi.Int Function(ffi.Int a, ffi.Int b);
+    = ffi.Pointer<ffi.NativeFunction<ArithmeticOperationFunction>>;
+typedef ArithmeticOperationFunction = ffi.Int Function(ffi.Int a, ffi.Int b);
+typedef DartArithmeticOperationFunction = int Function(int a, int b);
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_typedef_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_typedef_bindings.dart
index b0b171a..a33e9c8 100644
--- a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_typedef_bindings.dart
+++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_typedef_bindings.dart
@@ -92,8 +92,9 @@
 }
 
 typedef NamedFunctionProto
-    = ffi.Pointer<ffi.NativeFunction<NamedFunctionProto_function>>;
-typedef NamedFunctionProto_function = ffi.Void Function();
+    = ffi.Pointer<ffi.NativeFunction<NamedFunctionProtoFunction>>;
+typedef NamedFunctionProtoFunction = ffi.Void Function();
+typedef DartNamedFunctionProtoFunction = void Function();
 
 final class AnonymousStructInTypedef extends ffi.Opaque {}
 
@@ -113,6 +114,7 @@
 }
 
 typedef NestingASpecifiedType = ffi.IntPtr;
+typedef DartNestingASpecifiedType = int;
 
 final class Struct2 extends ffi.Opaque {}
 
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 f4edb59..cc66670 100644
--- a/pkgs/ffigen/test/large_integration_tests/_expected_cjson_bindings.dart
+++ b/pkgs/ffigen/test/large_integration_tests/_expected_cjson_bindings.dart
@@ -1249,6 +1249,7 @@
 }
 
 typedef cJSON_bool = ffi.Int;
+typedef DartcJSON_bool = int;
 
 const int CJSON_VERSION_MAJOR = 1;
 
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 d30109c..3d27499 100644
--- a/pkgs/ffigen/test/large_integration_tests/_expected_libclang_bindings.dart
+++ b/pkgs/ffigen/test/large_integration_tests/_expected_libclang_bindings.dart
@@ -7431,8 +7431,10 @@
 
 /// Visitor invoked for each cursor found by a traversal.
 typedef CXCursorVisitor
-    = ffi.Pointer<ffi.NativeFunction<CXCursorVisitor_function>>;
-typedef CXCursorVisitor_function = ffi.Int32 Function(
+    = ffi.Pointer<ffi.NativeFunction<CXCursorVisitorFunction>>;
+typedef CXCursorVisitorFunction = ffi.Int32 Function(
+    CXCursor cursor, CXCursor parent, CXClientData client_data);
+typedef DartCXCursorVisitorFunction = int Function(
     CXCursor cursor, CXCursor parent, CXClientData client_data);
 
 /// Opaque pointer representing client data that will be passed through to
@@ -7762,12 +7764,17 @@
 /// Visitor invoked for each file in a translation unit (used with
 /// clang_getInclusions()).
 typedef CXInclusionVisitor
-    = ffi.Pointer<ffi.NativeFunction<CXInclusionVisitor_function>>;
-typedef CXInclusionVisitor_function = ffi.Void Function(
+    = ffi.Pointer<ffi.NativeFunction<CXInclusionVisitorFunction>>;
+typedef CXInclusionVisitorFunction = ffi.Void Function(
     CXFile included_file,
     ffi.Pointer<CXSourceLocation> inclusion_stack,
     ffi.UnsignedInt include_len,
     CXClientData client_data);
+typedef DartCXInclusionVisitorFunction = void Function(
+    CXFile included_file,
+    ffi.Pointer<CXSourceLocation> inclusion_stack,
+    int include_len,
+    CXClientData client_data);
 
 abstract class CXEvalResultKind {
   static const int CXEval_Int = 1;
@@ -8225,8 +8232,10 @@
 
 /// Visitor invoked for each field found by a traversal.
 typedef CXFieldVisitor
-    = ffi.Pointer<ffi.NativeFunction<CXFieldVisitor_function>>;
-typedef CXFieldVisitor_function = ffi.Int32 Function(
+    = ffi.Pointer<ffi.NativeFunction<CXFieldVisitorFunction>>;
+typedef CXFieldVisitorFunction = ffi.Int32 Function(
+    CXCursor C, CXClientData client_data);
+typedef DartCXFieldVisitorFunction = int 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 492da42..5c6de76 100644
--- a/pkgs/ffigen/test/large_integration_tests/_expected_sqlite_bindings.dart
+++ b/pkgs/ffigen/test/large_integration_tests/_expected_sqlite_bindings.dart
@@ -10854,9 +10854,11 @@
 
 typedef sqlite3_int64 = sqlite_int64;
 typedef sqlite_int64 = ffi.LongLong;
+typedef Dartsqlite_int64 = int;
 typedef sqlite3_syscall_ptr
-    = ffi.Pointer<ffi.NativeFunction<sqlite3_syscall_ptr_function>>;
-typedef sqlite3_syscall_ptr_function = ffi.Void Function();
+    = ffi.Pointer<ffi.NativeFunction<sqlite3_syscall_ptrFunction>>;
+typedef sqlite3_syscall_ptrFunction = ffi.Void Function();
+typedef Dartsqlite3_syscall_ptrFunction = void Function();
 
 final class sqlite3_mem_methods extends ffi.Struct {
   /// Memory allocation function
@@ -10899,6 +10901,7 @@
 
 typedef sqlite3_uint64 = sqlite_uint64;
 typedef sqlite_uint64 = ffi.UnsignedLongLong;
+typedef Dartsqlite_uint64 = int;
 
 final class sqlite3_stmt extends ffi.Opaque {}
 
@@ -11507,6 +11510,7 @@
 }
 
 typedef sqlite3_rtree_dbl = ffi.Double;
+typedef Dartsqlite3_rtree_dbl = double;
 
 /// A pointer to a structure of the following type is passed as the
 /// argument to scored geometry callback registered using
@@ -12010,13 +12014,19 @@
 }
 
 typedef fts5_extension_function
-    = ffi.Pointer<ffi.NativeFunction<fts5_extension_function_function>>;
-typedef fts5_extension_function_function = ffi.Void Function(
+    = ffi.Pointer<ffi.NativeFunction<fts5_extension_functionFunction>>;
+typedef fts5_extension_functionFunction = 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 Dartfts5_extension_functionFunction = void Function(
+    ffi.Pointer<Fts5ExtensionApi> pApi,
+    ffi.Pointer<Fts5Context> pFts,
+    ffi.Pointer<sqlite3_context> pCtx,
+    int nVal,
+    ffi.Pointer<ffi.Pointer<sqlite3_value>> apVal);
 
 const String SQLITE_VERSION = '3.32.3';
 
diff --git a/pkgs/ffigen/test/native_objc_test/typedef_config.yaml b/pkgs/ffigen/test/native_objc_test/typedef_config.yaml
new file mode 100644
index 0000000..f375bfc
--- /dev/null
+++ b/pkgs/ffigen/test/native_objc_test/typedef_config.yaml
@@ -0,0 +1,17 @@
+name: TypedefTestObjCLibrary
+description: 'Tests typedef'
+language: objc
+output: 'typedef_bindings.dart'
+exclude-all-by-default: true
+objc-interfaces:
+  include:
+    - SomeClass
+    - AnotherClass
+typedefs:
+  include:
+    - SomeClassPtr
+headers:
+  entry-points:
+    - 'typedef_test.m'
+preamble: |
+  // ignore_for_file: camel_case_types, non_constant_identifier_names, unused_element, unused_field
diff --git a/pkgs/ffigen/test/native_objc_test/typedef_test.dart b/pkgs/ffigen/test/native_objc_test/typedef_test.dart
new file mode 100644
index 0000000..7a47fdb
--- /dev/null
+++ b/pkgs/ffigen/test/native_objc_test/typedef_test.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2023, 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.
+
+// Objective C support is only available on mac.
+@TestOn('mac-os')
+
+import 'dart:ffi';
+import 'dart:io';
+
+import 'package:test/test.dart';
+import '../test_utils.dart';
+import 'typedef_bindings.dart';
+import 'util.dart';
+
+void main() {
+  late TypedefTestObjCLibrary lib;
+
+  group('typedef', () {
+    setUpAll(() {
+      logWarnings();
+      final dylib = File('test/native_objc_test/typedef_test.dylib');
+      verifySetupFile(dylib);
+      lib = TypedefTestObjCLibrary(DynamicLibrary.open(dylib.absolute.path));
+      generateBindingsForCoverage('typedef');
+    });
+
+    test('Regression test for #386', () {
+      // https://github.com/dart-lang/ffigen/issues/386
+      // Make sure that the typedef DartSomeClassPtr is for SomeClass.
+      final DartSomeClassPtr instance = SomeClass.new1(lib);
+      expect(instance.pointer, isNot(nullptr));
+    });
+  });
+}
diff --git a/pkgs/ffigen/test/native_objc_test/typedef_test.m b/pkgs/ffigen/test/native_objc_test/typedef_test.m
new file mode 100644
index 0000000..c5972ab
--- /dev/null
+++ b/pkgs/ffigen/test/native_objc_test/typedef_test.m
@@ -0,0 +1,20 @@
+// Copyright (c) 2023, 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 <Foundation/NSObject.h>
+
+@interface SomeClass : NSObject {}
+@end
+
+@implementation SomeClass
+@end
+
+typedef SomeClass* SomeClassPtr;
+
+@interface AnotherClass : NSObject {}
+@property SomeClassPtr property;
+@end
+
+@implementation AnotherClass
+@end
diff --git a/pkgs/ffigen/test/regen.dart b/pkgs/ffigen/test/regen.dart
index 1617eda..ec52964 100644
--- a/pkgs/ffigen/test/regen.dart
+++ b/pkgs/ffigen/test/regen.dart
@@ -8,7 +8,7 @@
 import 'package:args/args.dart';
 import 'package:ffigen/ffigen.dart';
 import 'package:logging/logging.dart';
-import './test_utils.dart';
+import 'test_utils.dart';
 
 const usage = r'''Regenerates the Dart FFI bindings used in tests and examples.
 
@@ -18,12 +18,14 @@
 $ dart run test/setup.dart && dart run test/regen.dart && dart test
 ''';
 
-Future<void> _regenConfig(File yamlConfig, File bindingOutput) async {
-  yamlConfig = yamlConfig.absolute;
-  bindingOutput = bindingOutput.absolute;
-  final config = testConfigFromPath(yamlConfig.path);
-  final library = parse(config);
-  library.generateFile(bindingOutput);
+void _regenConfig(String yamlConfigPath, String bindingOutputPath) {
+  final yamlConfig = File(yamlConfigPath).absolute;
+  final bindingOutput = File(bindingOutputPath).absolute;
+  withChDir(yamlConfig.path, () {
+    final config = testConfigFromPath(yamlConfig.path);
+    final library = parse(config);
+    library.generateFile(bindingOutput);
+  });
 }
 
 Future<void> main(List<String> args) async {
@@ -50,25 +52,12 @@
     print('${record.level.name}: ${record.time}: ${record.message}');
   });
 
-  final nativeTestConfig = File('test/native_test/config.yaml').absolute;
-  final nativeTestOut =
-      File('test/native_test/_expected_native_test_bindings.dart').absolute;
-  await withChDir(nativeTestConfig.path,
-      () => _regenConfig(nativeTestConfig, nativeTestOut));
-
-  final libclangConfig = File('example/libclang-example/config.yaml').absolute;
-  final libclangOut =
-      File('example/libclang-example/generated_bindings.dart').absolute;
-  await withChDir(
-      libclangConfig.path, () => _regenConfig(libclangConfig, libclangOut));
-
-  final simpleConfig = File('example/simple/config.yaml').absolute;
-  final simpleOut = File('example/simple/generated_bindings.dart').absolute;
-  await withChDir(
-      simpleConfig.path, () => _regenConfig(simpleConfig, simpleOut));
-
-  final cJsonConfig = File('example/c_json/config.yaml').absolute;
-  final cJsonOut =
-      File('example/c_json/cjson_generated_bindings.dart').absolute;
-  await withChDir(cJsonConfig.path, () => _regenConfig(cJsonConfig, cJsonOut));
+  _regenConfig('test/native_test/config.yaml',
+      'test/native_test/_expected_native_test_bindings.dart');
+  _regenConfig('example/libclang-example/config.yaml',
+      'example/libclang-example/generated_bindings.dart');
+  _regenConfig(
+      'example/simple/config.yaml', 'example/simple/generated_bindings.dart');
+  _regenConfig('example/c_json/config.yaml',
+      'example/c_json/cjson_generated_bindings.dart');
 }
diff --git a/pkgs/ffigen/test/test_utils.dart b/pkgs/ffigen/test/test_utils.dart
index 95434ff..9482b9c 100644
--- a/pkgs/ffigen/test/test_utils.dart
+++ b/pkgs/ffigen/test/test_utils.dart
@@ -47,7 +47,7 @@
 // Remove '\r' for Windows compatibility, then apply user's normalizer.
 String _normalizeGeneratedCode(
     String generated, String Function(String)? codeNormalizer) {
-  final noCR = generated.replaceAll('\r', '').replaceAll(RegExp(r'\n+'), '\n');
+  final noCR = generated.replaceAll('\r', '');
   if (codeNormalizer == null) return noCR;
   return codeNormalizer(noCR);
 }
@@ -103,7 +103,7 @@
         _normalizeGeneratedCode(file.readAsStringSync(), codeNormalizer);
     final expected = _normalizeGeneratedCode(
         File(path.joinAll(pathToExpected)).readAsStringSync(), codeNormalizer);
-    expect(actual, expected);
+    expect(actual.split('\n'), expected.split('\n'));
     if (file.existsSync()) {
       file.delete();
     }