[ffigen] Allow extern inline functions to be generated. (#594)

diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md
index 1b70ff7..b4bdf28 100644
--- a/pkgs/ffigen/CHANGELOG.md
+++ b/pkgs/ffigen/CHANGELOG.md
@@ -1,6 +1,7 @@
 # 9.0.1
 
 - Fix doc comment missing on struct/union array fields.
+- Allow extern inline functions to be generated.
 
 # 9.0.0
 
diff --git a/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart b/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart
index a2217b4..4c5fba2 100644
--- a/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart
+++ b/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart
@@ -948,6 +948,24 @@
       _clang_Cursor_isAnonymousRecordDeclPtr
           .asFunction<int Function(CXCursor)>();
 
+  /// Returns the storage class for a function or variable declaration.
+  ///
+  /// If the passed in Cursor is not a function or variable declaration,
+  /// CX_SC_Invalid is returned else the storage class.
+  int clang_Cursor_getStorageClass(
+    CXCursor arg0,
+  ) {
+    return _clang_Cursor_getStorageClass(
+      arg0,
+    );
+  }
+
+  late final _clang_Cursor_getStorageClassPtr =
+      _lookup<ffi.NativeFunction<ffi.Int32 Function(CXCursor)>>(
+          'clang_Cursor_getStorageClass');
+  late final _clang_Cursor_getStorageClass =
+      _clang_Cursor_getStorageClassPtr.asFunction<int Function(CXCursor)>();
+
   /// Visit the children of a particular cursor.
   ///
   /// This function visits all the direct children of the given cursor,
@@ -2564,6 +2582,19 @@
   static const int CXTypeLayoutError_Undeduced = -6;
 }
 
+/// Represents the storage classes as declared in the source. CX_SC_Invalid
+/// was added for the case that the passed cursor in not a declaration.
+abstract class CX_StorageClass {
+  static const int CX_SC_Invalid = 0;
+  static const int CX_SC_None = 1;
+  static const int CX_SC_Extern = 2;
+  static const int CX_SC_Static = 3;
+  static const int CX_SC_PrivateExtern = 4;
+  static const int CX_SC_OpenCLWorkGroupLocal = 5;
+  static const int CX_SC_Auto = 6;
+  static const int CX_SC_Register = 7;
+}
+
 /// Describes how the traversal of the children of a particular
 /// cursor should proceed after visiting a particular child cursor.
 ///
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 552a0d4..f9d1121 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
@@ -36,8 +36,9 @@
 
     final rt = _getFunctionReturnType(cursor);
     final parameters = _getParameters(cursor, funcName);
-
-    if (clang.clang_Cursor_isFunctionInlined(cursor) != 0) {
+    if (clang.clang_Cursor_isFunctionInlined(cursor) != 0 &&
+        clang.clang_Cursor_getStorageClass(cursor) !=
+            clang_types.CX_StorageClass.CX_SC_Extern) {
       _logger.fine('---- Removed Function, reason: inline function: '
           '${cursor.completeStringRepr()}');
       _logger.warning(
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 c4a01b3..7475b09 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
@@ -103,6 +103,20 @@
       void Function(ffi.Pointer<shortHand>,
           ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>();
 
+  void externInlineFunc(
+    int a,
+  ) {
+    return _externInlineFunc(
+      a,
+    );
+  }
+
+  late final _externInlineFuncPtr =
+      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int)>>(
+          'externInlineFunc');
+  late final _externInlineFunc =
+      _externInlineFuncPtr.asFunction<void Function(int)>();
+
   int diffChars(
     int a,
     int b,
diff --git a/pkgs/ffigen/test/header_parser_tests/functions.h b/pkgs/ffigen/test/header_parser_tests/functions.h
index 28b8546..9cf6a3b 100644
--- a/pkgs/ffigen/test/header_parser_tests/functions.h
+++ b/pkgs/ffigen/test/header_parser_tests/functions.h
@@ -20,4 +20,7 @@
 // Should be skipped as inline functions are not supported.
 static inline void inlineFunc();
 
+// Not skipped since it is extern.
+extern inline void externInlineFunc(int a);
+
 char diffChars(unsigned char a, signed char b);
diff --git a/pkgs/ffigen/tool/libclang_config.yaml b/pkgs/ffigen/tool/libclang_config.yaml
index f0f9b3b..b948e5c 100644
--- a/pkgs/ffigen/tool/libclang_config.yaml
+++ b/pkgs/ffigen/tool/libclang_config.yaml
@@ -94,6 +94,7 @@
     - clang_getNumArgTypes
     - clang_getArgType
     - clang_isFunctionTypeVariadic
+    - clang_Cursor_getStorageClass
     - clang_getCursorResultType
     - clang_getEnumConstantDeclValue
     - clang_equalRanges