Updated parsing of typedef enclosed declarations (#83)

* added getCursorUSR_wrap method to wrapper, and bindings

* Updated code to use libclang's USR for identification

* Struct's typedef name will be used now (if it is only used in 1 typedef)

* parseXXX now returns seen binding instead of null

* added tests and removed obsolete code

* updated version, changelog, test_coverage.dart

* added PR ref to changelog

* minor changes
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ebc9c26..0c54f0a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 0.2.2
+- Fixed multiple generation/skipping of typedef enclosed declarations.
+- Typedef names are now given higher preference over inner names, See [#83](https://github.com/dart-lang/ffigen/pull/83).
+
 # 0.2.1+1
 - Added FAQ to readme.
 
diff --git a/lib/src/clang_library/wrapper.c b/lib/src/clang_library/wrapper.c
index c43c7f6..2482e36 100644
--- a/lib/src/clang_library/wrapper.c
+++ b/lib/src/clang_library/wrapper.c
@@ -358,4 +358,9 @@
     return clang_Cursor_isAnonymousRecordDecl(*cursor);
 }
 
+CXString *clang_getCursorUSR_wrap(CXCursor *cursor)
+{
+    return ptrToCXString(clang_getCursorUSR(*cursor));
+}
+
 // END ===== WRAPPER FUNCTIONS =====================
diff --git a/lib/src/clang_library/wrapper.def b/lib/src/clang_library/wrapper.def
index c82231f..5cb8a5c 100644
--- a/lib/src/clang_library/wrapper.def
+++ b/lib/src/clang_library/wrapper.def
@@ -358,3 +358,4 @@
 clang_Cursor_Evaluate_wrap
 clang_Cursor_isAnonymous_wrap
 clang_Cursor_isAnonymousRecordDecl_wrap
+clang_getCursorUSR_wrap
diff --git a/lib/src/code_generator/binding.dart b/lib/src/code_generator/binding.dart
index 5a10c06..b4267b8 100644
--- a/lib/src/code_generator/binding.dart
+++ b/lib/src/code_generator/binding.dart
@@ -12,13 +12,23 @@
 ///
 /// Do not extend directly, use [LookUpBinding] or [NoLookUpBinding].
 abstract class Binding {
+  /// Holds the Unified Symbol Resolution string obtained from libclang.
+  final String usr;
+
+  /// The name as it was in C.
   final String originalName;
 
+  /// Binding name to generate, may get changed to resolve name conflicts.
   String name;
 
   final String dartDoc;
 
-  Binding({@required this.originalName, @required this.name, this.dartDoc});
+  Binding({
+    @required this.usr,
+    @required this.originalName,
+    @required this.name,
+    this.dartDoc,
+  });
 
   /// Return typedef dependencies.
   List<Typedef> getTypedefDependencies(Writer w);
@@ -33,17 +43,29 @@
 /// Base class for bindings which look up symbols in dynamic library.
 abstract class LookUpBinding extends Binding {
   LookUpBinding({
-    @required String originalName,
+    String usr,
+    String originalName,
     @required String name,
     String dartDoc,
-  }) : super(originalName: originalName, name: name, dartDoc: dartDoc);
+  }) : super(
+          usr: usr ?? name,
+          originalName: originalName ?? name,
+          name: name,
+          dartDoc: dartDoc,
+        );
 }
 
 /// Base class for bindings which don't look up symbols in dynamic library.
 abstract class NoLookUpBinding extends Binding {
   NoLookUpBinding({
-    @required String originalName,
+    String usr,
+    String originalName,
     @required String name,
     String dartDoc,
-  }) : super(originalName: originalName, name: name, dartDoc: dartDoc);
+  }) : super(
+          usr: usr ?? name,
+          originalName: originalName ?? name,
+          name: name,
+          dartDoc: dartDoc,
+        );
 }
diff --git a/lib/src/code_generator/constant.dart b/lib/src/code_generator/constant.dart
index b5521ad..41572ab 100644
--- a/lib/src/code_generator/constant.dart
+++ b/lib/src/code_generator/constant.dart
@@ -31,12 +31,18 @@
   final String rawValue;
 
   Constant({
+    String usr,
     String originalName,
     @required String name,
     String dartDoc,
     @required this.rawType,
     @required this.rawValue,
-  }) : super(originalName: originalName ?? name, name: name, dartDoc: dartDoc);
+  }) : super(
+          usr: usr,
+          originalName: originalName,
+          name: name,
+          dartDoc: dartDoc,
+        );
 
   @override
   BindingString toBindingString(Writer w) {
diff --git a/lib/src/code_generator/enum_class.dart b/lib/src/code_generator/enum_class.dart
index 1a383ce..9404c4b 100644
--- a/lib/src/code_generator/enum_class.dart
+++ b/lib/src/code_generator/enum_class.dart
@@ -28,12 +28,18 @@
   final List<EnumConstant> enumConstants;
 
   EnumClass({
+    String usr,
     String originalName,
     @required String name,
     String dartDoc,
     List<EnumConstant> enumConstants,
   })  : enumConstants = enumConstants ?? [],
-        super(originalName: originalName ?? name, name: name, dartDoc: dartDoc);
+        super(
+          usr: usr,
+          originalName: originalName,
+          name: name,
+          dartDoc: dartDoc,
+        );
 
   @override
   BindingString toBindingString(Writer w) {
diff --git a/lib/src/code_generator/func.dart b/lib/src/code_generator/func.dart
index 551dae9..d29abcb 100644
--- a/lib/src/code_generator/func.dart
+++ b/lib/src/code_generator/func.dart
@@ -36,6 +36,7 @@
   /// [originalName] is looked up in dynamic library, if not
   /// provided, takes the value of [name].
   Func({
+    String usr,
     @required String name,
     String originalName,
     String dartDoc,
@@ -43,7 +44,11 @@
     List<Parameter> parameters,
   })  : parameters = parameters ?? [],
         super(
-            originalName: originalName ?? name, name: name, dartDoc: dartDoc) {
+          usr: usr,
+          originalName: originalName,
+          name: name,
+          dartDoc: dartDoc,
+        ) {
     for (var i = 0; i < this.parameters.length; i++) {
       if (this.parameters[i].name == null ||
           this.parameters[i].name.trim() == '') {
diff --git a/lib/src/code_generator/global.dart b/lib/src/code_generator/global.dart
index d0e67cf..79478fa 100644
--- a/lib/src/code_generator/global.dart
+++ b/lib/src/code_generator/global.dart
@@ -25,11 +25,17 @@
   final Type type;
 
   Global({
+    String usr,
     String originalName,
     @required String name,
     @required this.type,
     String dartDoc,
-  }) : super(originalName: originalName ?? name, name: name, dartDoc: dartDoc);
+  }) : super(
+          usr: usr,
+          originalName: originalName,
+          name: name,
+          dartDoc: dartDoc,
+        );
 
   @override
   BindingString toBindingString(Writer w) {
diff --git a/lib/src/code_generator/struc.dart b/lib/src/code_generator/struc.dart
index f241fd7..8cd6df3 100644
--- a/lib/src/code_generator/struc.dart
+++ b/lib/src/code_generator/struc.dart
@@ -39,12 +39,18 @@
   List<Member> members;
 
   Struc({
+    String usr,
     String originalName,
     @required String name,
     String dartDoc,
     List<Member> members,
   })  : members = members ?? [],
-        super(originalName: originalName ?? name, name: name, dartDoc: dartDoc);
+        super(
+          usr: usr,
+          originalName: originalName,
+          name: name,
+          dartDoc: dartDoc,
+        );
 
   List<int> _getArrayDimensionLengths(Type type) {
     final array = <int>[];
diff --git a/lib/src/header_parser/clang_bindings/clang_bindings.dart b/lib/src/header_parser/clang_bindings/clang_bindings.dart
index 55c1903..82222c7 100644
--- a/lib/src/header_parser/clang_bindings/clang_bindings.dart
+++ b/lib/src/header_parser/clang_bindings/clang_bindings.dart
@@ -489,7 +489,7 @@
   /// instead of cxcursor by default.
   int clang_visitChildren_wrap(
     ffi.Pointer<CXCursor> parent,
-    ffi.Pointer<ffi.NativeFunction<ModifiedCXCursorVisitor_1>> _modifiedVisitor,
+    ffi.Pointer<ffi.NativeFunction<ModifiedCXCursorVisitor>> _modifiedVisitor,
     int uid,
   ) {
     _clang_visitChildren_wrap ??= _dylib.lookupFunction<
@@ -778,6 +778,19 @@
 
   _dart_clang_Cursor_isAnonymousRecordDecl_wrap
       _clang_Cursor_isAnonymousRecordDecl_wrap;
+
+  ffi.Pointer<CXString> clang_getCursorUSR_wrap(
+    ffi.Pointer<CXCursor> cursor,
+  ) {
+    _clang_getCursorUSR_wrap ??= _dylib.lookupFunction<
+        _c_clang_getCursorUSR_wrap,
+        _dart_clang_getCursorUSR_wrap>('clang_getCursorUSR_wrap');
+    return _clang_getCursorUSR_wrap(
+      cursor,
+    );
+  }
+
+  _dart_clang_getCursorUSR_wrap _clang_getCursorUSR_wrap;
 }
 
 /// A character string.
@@ -2451,7 +2464,7 @@
   int opts,
 );
 
-typedef ModifiedCXCursorVisitor_1 = ffi.Int32 Function(
+typedef ModifiedCXCursorVisitor = ffi.Int32 Function(
   ffi.Pointer<CXCursor>,
   ffi.Pointer<CXCursor>,
   ffi.Pointer<ffi.Void>,
@@ -2459,13 +2472,13 @@
 
 typedef _c_clang_visitChildren_wrap = ffi.Uint32 Function(
   ffi.Pointer<CXCursor> parent,
-  ffi.Pointer<ffi.NativeFunction<ModifiedCXCursorVisitor_1>> _modifiedVisitor,
+  ffi.Pointer<ffi.NativeFunction<ModifiedCXCursorVisitor>> _modifiedVisitor,
   ffi.Int64 uid,
 );
 
 typedef _dart_clang_visitChildren_wrap = int Function(
   ffi.Pointer<CXCursor> parent,
-  ffi.Pointer<ffi.NativeFunction<ModifiedCXCursorVisitor_1>> _modifiedVisitor,
+  ffi.Pointer<ffi.NativeFunction<ModifiedCXCursorVisitor>> _modifiedVisitor,
   int uid,
 );
 
@@ -2641,3 +2654,11 @@
 typedef _dart_clang_Cursor_isAnonymousRecordDecl_wrap = int Function(
   ffi.Pointer<CXCursor> cursor,
 );
+
+typedef _c_clang_getCursorUSR_wrap = ffi.Pointer<CXString> Function(
+  ffi.Pointer<CXCursor> cursor,
+);
+
+typedef _dart_clang_getCursorUSR_wrap = ffi.Pointer<CXString> Function(
+  ffi.Pointer<CXCursor> cursor,
+);
diff --git a/lib/src/header_parser/data.dart b/lib/src/header_parser/data.dart
index 6c21338..c307a26 100644
--- a/lib/src/header_parser/data.dart
+++ b/lib/src/header_parser/data.dart
@@ -37,8 +37,8 @@
 final uid = Isolate.current.controlPort.nativePort;
 
 /// Saved macros, Key: prefixedName, Value originalName.
-Map<String, String> get savedMacros => _savedMacros;
-Map<String, String> _savedMacros;
+Map<String, Macro> get savedMacros => _savedMacros;
+Map<String, Macro> _savedMacros;
 
 /// Saved unnamed EnumConstants.
 List<Constant> get unnamedEnumConstants => _unnamedEnumConstants;
diff --git a/lib/src/header_parser/includer.dart b/lib/src/header_parser/includer.dart
index a4766e1..5c534a7 100644
--- a/lib/src/header_parser/includer.dart
+++ b/lib/src/header_parser/includer.dart
@@ -7,8 +7,8 @@
 /// Utility functions to check whether a binding should be parsed or not
 /// based on filters.
 
-bool shouldIncludeStruct(String name) {
-  if (bindingsIndex.isSeenStruct(name) || name == '') {
+bool shouldIncludeStruct(String usr, String name) {
+  if (bindingsIndex.isSeenStruct(usr) || name == '') {
     return false;
   } else if (config.structDecl == null ||
       config.structDecl.shouldInclude(name)) {
@@ -18,8 +18,8 @@
   }
 }
 
-bool shouldIncludeFunc(String name) {
-  if (bindingsIndex.isSeenFunc(name) || name == '') {
+bool shouldIncludeFunc(String usr, String name) {
+  if (bindingsIndex.isSeenFunc(usr) || name == '') {
     return false;
   } else if (config.functionDecl == null ||
       config.functionDecl.shouldInclude(name)) {
@@ -29,8 +29,8 @@
   }
 }
 
-bool shouldIncludeEnumClass(String name) {
-  if (bindingsIndex.isSeenEnumClass(name) || name == '') {
+bool shouldIncludeEnumClass(String usr, String name) {
+  if (bindingsIndex.isSeenEnumClass(usr) || name == '') {
     return false;
   } else if (config.enumClassDecl == null ||
       config.enumClassDecl.shouldInclude(name)) {
@@ -40,8 +40,8 @@
   }
 }
 
-bool shouldIncludeMacro(String name) {
-  if (bindingsIndex.isSeenMacro(name) || name == '') {
+bool shouldIncludeMacro(String usr, String name) {
+  if (bindingsIndex.isSeenMacro(usr) || name == '') {
     return false;
   } else if (config.macroDecl == null || config.macroDecl.shouldInclude(name)) {
     return true;
diff --git a/lib/src/header_parser/parser.dart b/lib/src/header_parser/parser.dart
index 1771574..c5698b0 100644
--- a/lib/src/header_parser/parser.dart
+++ b/lib/src/header_parser/parser.dart
@@ -82,8 +82,8 @@
     cmdLen = config.compilerOpts.length;
   }
 
-  // Contains all bindings.
-  final bindings = <Binding>[];
+  // Contains all bindings. A set ensures we never have duplicates.
+  final bindings = <Binding>{};
 
   // Log all headers for user.
   _logger.info('Input Headers: ${config.headers.entryPoints}');
@@ -130,5 +130,5 @@
     clangCmdArgs.dispose(config.compilerOpts.length);
   }
   clang.clang_disposeIndex(index);
-  return bindings;
+  return bindings.toList();
 }
diff --git a/lib/src/header_parser/sub_parsers/enumdecl_parser.dart b/lib/src/header_parser/sub_parsers/enumdecl_parser.dart
index 3460e28..50248a6 100644
--- a/lib/src/header_parser/sub_parsers/enumdecl_parser.dart
+++ b/lib/src/header_parser/sub_parsers/enumdecl_parser.dart
@@ -28,10 +28,11 @@
 EnumClass parseEnumDeclaration(
   Pointer<clang_types.CXCursor> cursor, {
 
-  /// Optionally provide name to use (useful in case struct is inside a typedef).
+  /// Optionally provide name to use (useful in case enum is inside a typedef).
   String name,
 }) {
   _stack.push(_ParsedEnum());
+  final enumUsr = cursor.usr();
   final enumName = name ?? cursor.spelling();
   if (enumName == '') {
     // Save this unnamed enum if it is anonymous (therefore not in a typedef).
@@ -42,17 +43,24 @@
     } else {
       _logger.fine('Unnamed enum inside a typedef.');
     }
-  } else if (shouldIncludeEnumClass(enumName) &&
-      !bindingsIndex.isSeenEnumClass(enumName)) {
+  } else if (shouldIncludeEnumClass(enumUsr, enumName)) {
     _logger.fine('++++ Adding Enum: ${cursor.completeStringRepr()}');
     _stack.top.enumClass = EnumClass(
+      usr: enumUsr,
       dartDoc: getCursorDocComment(cursor),
       originalName: enumName,
       name: config.enumClassDecl.renameUsingConfig(enumName),
     );
-    bindingsIndex.addEnumClassToSeen(enumName, _stack.top.enumClass);
+    bindingsIndex.addEnumClassToSeen(enumUsr, _stack.top.enumClass);
     _addEnumConstant(cursor);
   }
+  if (bindingsIndex.isSeenEnumClass(enumUsr)) {
+    _stack.top.enumClass = bindingsIndex.getSeenEnumClass(enumUsr);
+
+    // If enum is seen, update it's name.
+    _stack.top.enumClass.name =
+        config.enumClassDecl.renameUsingConfig(enumName);
+  }
 
   return _stack.pop().enumClass;
 }
diff --git a/lib/src/header_parser/sub_parsers/functiondecl_parser.dart b/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
index 9137c73..78d52b4 100644
--- a/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
+++ b/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
@@ -31,8 +31,9 @@
   _stack.top.structByValueParameter = false;
   _stack.top.unimplementedParameterType = false;
 
+  final funcUsr = cursor.usr();
   final funcName = cursor.spelling();
-  if (shouldIncludeFunc(funcName) && !bindingsIndex.isSeenFunc(funcName)) {
+  if (shouldIncludeFunc(funcUsr, funcName)) {
     _logger.fine('++++ Adding Function: ${cursor.completeStringRepr()}');
 
     final rt = _getFunctionReturnType(cursor);
@@ -65,12 +66,15 @@
         cursor,
         nesting.length + commentPrefix.length,
       ),
+      usr: funcUsr,
       name: config.functionDecl.renameUsingConfig(funcName),
       originalName: funcName,
       returnType: rt,
       parameters: parameters,
     );
-    bindingsIndex.addFuncToSeen(funcName, _stack.top.func);
+    bindingsIndex.addFuncToSeen(funcUsr, _stack.top.func);
+  } else if (bindingsIndex.isSeenFunc(funcUsr)) {
+    _stack.top.func = bindingsIndex.getSeenFunc(funcUsr);
   }
 
   return _stack.pop().func;
diff --git a/lib/src/header_parser/sub_parsers/macro_parser.dart b/lib/src/header_parser/sub_parsers/macro_parser.dart
index dd35b78..ad9df56 100644
--- a/lib/src/header_parser/sub_parsers/macro_parser.dart
+++ b/lib/src/header_parser/sub_parsers/macro_parser.dart
@@ -20,25 +20,25 @@
 
 /// Adds a macro definition to be parsed later.
 void saveMacroDefinition(Pointer<clang_types.CXCursor> cursor) {
+  final macroUsr = cursor.usr();
   final originalMacroName = cursor.spelling();
-  if (shouldIncludeMacro(originalMacroName) &&
-      !bindingsIndex.isSeenMacro(originalMacroName) &&
-      clang.clang_Cursor_isMacroBuiltin_wrap(cursor) == 0 &&
-      clang.clang_Cursor_isMacroFunctionLike_wrap(cursor) == 0) {
+  if (clang.clang_Cursor_isMacroBuiltin_wrap(cursor) == 0 &&
+      clang.clang_Cursor_isMacroFunctionLike_wrap(cursor) == 0 &&
+      shouldIncludeMacro(macroUsr, originalMacroName)) {
     // Parse macro only if it's not builtin or function-like.
     _logger.fine(
         "++++ Saved Macro '$originalMacroName' for later : ${cursor.completeStringRepr()}");
     final prefixedName = config.macroDecl.renameUsingConfig(originalMacroName);
-    bindingsIndex.addMacroToSeen(originalMacroName, prefixedName);
-    _saveMacro(prefixedName, originalMacroName);
+    bindingsIndex.addMacroToSeen(macroUsr, prefixedName);
+    _saveMacro(prefixedName, macroUsr, originalMacroName);
   }
 }
 
 /// Saves a macro to be parsed later.
 ///
 /// Macros are parsed later in [parseSavedMacros()].
-void _saveMacro(String name, String originalName) {
-  savedMacros[name] = originalName;
+void _saveMacro(String name, String usr, String originalName) {
+  savedMacros[name] = Macro(usr, originalName);
 }
 
 List<Constant> _bindings;
@@ -114,7 +114,8 @@
       switch (k) {
         case clang_types.CXEvalResultKind.CXEval_Int:
           constant = Constant(
-            originalName: savedMacros[macroName],
+            usr: savedMacros[macroName].usr,
+            originalName: savedMacros[macroName].originalName,
             name: macroName,
             rawType: 'int',
             rawValue: clang.clang_EvalResult_getAsLongLong(e).toString(),
@@ -122,7 +123,8 @@
           break;
         case clang_types.CXEvalResultKind.CXEval_Float:
           constant = Constant(
-            originalName: savedMacros[macroName],
+            usr: savedMacros[macroName].usr,
+            originalName: savedMacros[macroName].originalName,
             name: macroName,
             rawType: 'double',
             rawValue: clang.clang_EvalResult_getAsDouble(e).toString(),
@@ -135,7 +137,8 @@
           // Escape ' character, because our strings are enclosed with '.
           value = value.replaceAll("'", r"\'");
           constant = Constant(
-            originalName: savedMacros[macroName],
+            usr: savedMacros[macroName].usr,
+            originalName: savedMacros[macroName].originalName,
             name: macroName,
             rawType: 'String',
             rawValue: "'${value}'",
@@ -204,7 +207,8 @@
   for (final prefixedMacroName in savedMacros.keys) {
     // Write macro.
     final macroVarName = MacroVariableString.encode(prefixedMacroName);
-    sb.writeln('auto ${macroVarName} = ${savedMacros[prefixedMacroName]};');
+    sb.writeln(
+        'auto ${macroVarName} = ${savedMacros[prefixedMacroName].originalName};');
     // Add to _macroVarNames.
     _macroVarNames.add(macroVarName);
   }
diff --git a/lib/src/header_parser/sub_parsers/structdecl_parser.dart b/lib/src/header_parser/sub_parsers/structdecl_parser.dart
index 3b62123..6ae4c80 100644
--- a/lib/src/header_parser/sub_parsers/structdecl_parser.dart
+++ b/lib/src/header_parser/sub_parsers/structdecl_parser.dart
@@ -38,25 +38,34 @@
   bool ignoreFilter = false,
 }) {
   _stack.push(_ParsedStruc());
+
+  final structUsr = cursor.usr();
   final structName = name ?? cursor.spelling();
 
   if (structName.isEmpty) {
     _logger.finest('unnamed structure or typedef structure declaration');
-  } else if ((ignoreFilter || shouldIncludeStruct(structName)) &&
-      (!bindingsIndex.isSeenStruct(structName))) {
+  } else if ((ignoreFilter || shouldIncludeStruct(structUsr, structName)) &&
+      (!bindingsIndex.isSeenStruct(structUsr))) {
     _logger.fine(
         '++++ Adding Structure: structName: ${structName}, ${cursor.completeStringRepr()}');
     _stack.top.struc = Struc(
+      usr: structUsr,
       originalName: structName,
       name: config.structDecl.renameUsingConfig(structName),
       dartDoc: getCursorDocComment(cursor),
     );
     // Adding to seen here to stop recursion if a struct has itself as a
     // member, members are updated later.
-    bindingsIndex.addStructToSeen(structName, _stack.top.struc);
+    bindingsIndex.addStructToSeen(structUsr, _stack.top.struc);
     _setStructMembers(cursor);
   }
 
+  if (bindingsIndex.isSeenStruct(structUsr)) {
+    _stack.top.struc = bindingsIndex.getSeenStruct(structUsr);
+
+    // If struct is seen, update it's name.
+    _stack.top.struc.name = config.structDecl.renameUsingConfig(structName);
+  }
   return _stack.pop().struc;
 }
 
diff --git a/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart b/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart
index b5039c4..713fae0 100644
--- a/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart
+++ b/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart
@@ -56,6 +56,7 @@
 void _addUnNamedEnumConstant(Pointer<clang_types.CXCursor> cursor) {
   unnamedEnumConstants.add(
     Constant(
+      usr: cursor.usr(),
       originalName: cursor.spelling(),
       name: config.enumClassDecl.renameMemberUsingConfig(
         '', // Un-named enum constants have an empty declaration name.
diff --git a/lib/src/header_parser/translation_unit_parser.dart b/lib/src/header_parser/translation_unit_parser.dart
index 4c1f36c..f576fae 100644
--- a/lib/src/header_parser/translation_unit_parser.dart
+++ b/lib/src/header_parser/translation_unit_parser.dart
@@ -19,12 +19,12 @@
 
 var _logger = Logger('ffigen.header_parser.translation_unit_parser');
 
-List<Binding> _bindings;
+Set<Binding> _bindings;
 
 /// Parses the translation unit and returns the generated bindings.
-List<Binding> parseTranslationUnit(
+Set<Binding> parseTranslationUnit(
     Pointer<clang_types.CXCursor> translationUnitCursor) {
-  _bindings = [];
+  _bindings = {};
   final resultCode = clang.clang_visitChildren_wrap(
     translationUnitCursor,
     Pointer.fromFunction(
@@ -77,9 +77,10 @@
   return clang_types.CXChildVisitResult.CXChildVisit_Continue;
 }
 
-/// Adds to binding if not null.
+/// Adds to binding if unseen and not null.
 void addToBindings(Binding b) {
   if (b != null) {
+    // This is a set, and hence will not have duplicates.
     _bindings.add(b);
   }
 }
diff --git a/lib/src/header_parser/type_extractor/extractor.dart b/lib/src/header_parser/type_extractor/extractor.dart
index d73e029..a198420 100644
--- a/lib/src/header_parser/type_extractor/extractor.dart
+++ b/lib/src/header_parser/type_extractor/extractor.dart
@@ -51,7 +51,7 @@
       et.dispose();
       return s;
     case clang_types.CXTypeKind.CXType_Record:
-      return _extractfromRecord(cxtype);
+      return _extractfromRecord(cxtype, parentName);
     case clang_types.CXTypeKind.CXType_Enum:
       return Type.nativeType(
         enumNativeType,
@@ -87,7 +87,7 @@
   }
 }
 
-Type _extractfromRecord(Pointer<clang_types.CXType> cxtype) {
+Type _extractfromRecord(Pointer<clang_types.CXType> cxtype, String parentName) {
   Type type;
 
   final cursor = clang.clang_getTypeDeclaration_wrap(cxtype);
@@ -96,26 +96,21 @@
   switch (clang.clang_getCursorKind_wrap(cursor)) {
     case clang_types.CXCursorKind.CXCursor_StructDecl:
       final cxtype = cursor.type();
-      var structName = cursor.spelling();
-      if (structName == '') {
-        // Incase of anonymous structs defined inside a typedef.
-        structName = cxtype.spelling();
-      }
+      final structUsr = cursor.usr();
 
-      final fixedStructName = config.structDecl.renameUsingConfig(structName);
+      // Name of typedef (parentName) is used if available.
+      final structName = parentName ?? cursor.spelling();
 
       // Also add a struct binding, if its unseen.
       // TODO(23): Check if we should auto add struct.
-      if (bindingsIndex.isSeenStruct(structName)) {
-        type = Type.struct(bindingsIndex.getSeenStruct(structName));
+      if (bindingsIndex.isSeenStruct(structUsr)) {
+        type = Type.struct(bindingsIndex.getSeenStruct(structUsr));
       } else {
         final struc = parseStructDeclaration(cursor,
-            name: fixedStructName, ignoreFilter: true);
+            name: structName, ignoreFilter: true);
         type = Type.struct(struc);
         // Add to bindings.
         addToBindings(struc);
-        // Add to seen.
-        bindingsIndex.addStructToSeen(structName, struc);
       }
 
       cxtype.dispose();
diff --git a/lib/src/header_parser/utils.dart b/lib/src/header_parser/utils.dart
index 54fcc8b..d578b00 100644
--- a/lib/src/header_parser/utils.dart
+++ b/lib/src/header_parser/utils.dart
@@ -57,6 +57,10 @@
 }
 
 extension CXCursorExt on Pointer<clang_types.CXCursor> {
+  String usr() {
+    return clang.clang_getCursorUSR_wrap(this).toStringAndDispose();
+  }
+
   /// Returns the kind int from [clang_types.CXCursorKind].
   int kind() {
     return clang.clang_getCursorKind_wrap(this);
@@ -78,7 +82,7 @@
   String completeStringRepr() {
     final cxtype = type();
     final s =
-        '(Cursor) spelling: ${spelling()}, kind: ${kind()}, kindSpelling: ${kindSpelling()}, type: ${cxtype.kind()}, typeSpelling: ${cxtype.spelling()}';
+        '(Cursor) spelling: ${spelling()}, kind: ${kind()}, kindSpelling: ${kindSpelling()}, type: ${cxtype.kind()}, typeSpelling: ${cxtype.spelling()}, usr: ${usr()}';
     cxtype.dispose();
     return s;
   }
@@ -323,9 +327,16 @@
   }
 }
 
+class Macro {
+  final String usr;
+  final String originalName;
+
+  Macro(this.usr, this.originalName);
+}
+
 /// Tracks if a binding is 'seen' or not.
 class BindingsIndex {
-  // Stores binding names already seen. Map key is same as their original name.
+  // Tracks if bindings are already seen, Map key is USR obtained from libclang.
   final Map<String, Struc> _structs = {};
   final Map<String, Func> _functions = {};
   final Map<String, EnumClass> _enumClass = {};
@@ -333,52 +344,52 @@
   // Stores only named typedefC used in NativeFunc.
   final Map<String, Typedef> _functionTypedefs = {};
 
-  bool isSeenStruct(String originalName) {
-    return _structs.containsKey(originalName);
+  bool isSeenStruct(String usr) {
+    return _structs.containsKey(usr);
   }
 
-  void addStructToSeen(String originalName, Struc struc) {
-    _structs[originalName] = struc;
+  void addStructToSeen(String usr, Struc struc) {
+    _structs[usr] = struc;
   }
 
-  Struc getSeenStruct(String originalName) {
-    return _structs[originalName];
+  Struc getSeenStruct(String usr) {
+    return _structs[usr];
   }
 
-  bool isSeenFunc(String originalName) {
-    return _functions.containsKey(originalName);
+  bool isSeenFunc(String usr) {
+    return _functions.containsKey(usr);
   }
 
-  void addFuncToSeen(String originalName, Func func) {
-    _functions[originalName] = func;
+  void addFuncToSeen(String usr, Func func) {
+    _functions[usr] = func;
   }
 
-  Func getSeenFunc(String originalName) {
-    return _functions[originalName];
+  Func getSeenFunc(String usr) {
+    return _functions[usr];
   }
 
-  bool isSeenEnumClass(String originalName) {
-    return _enumClass.containsKey(originalName);
+  bool isSeenEnumClass(String usr) {
+    return _enumClass.containsKey(usr);
   }
 
-  void addEnumClassToSeen(String originalName, EnumClass enumClass) {
-    _enumClass[originalName] = enumClass;
+  void addEnumClassToSeen(String usr, EnumClass enumClass) {
+    _enumClass[usr] = enumClass;
   }
 
-  EnumClass getSeenEnumClass(String originalName) {
-    return _enumClass[originalName];
+  EnumClass getSeenEnumClass(String usr) {
+    return _enumClass[usr];
   }
 
-  bool isSeenMacro(String originalName) {
-    return _macros.containsKey(originalName);
+  bool isSeenMacro(String usr) {
+    return _macros.containsKey(usr);
   }
 
-  void addMacroToSeen(String originalName, String macro) {
-    _macros[originalName] = macro;
+  void addMacroToSeen(String usr, String macro) {
+    _macros[usr] = macro;
   }
 
-  String getSeenMacro(String originalName) {
-    return _macros[originalName];
+  String getSeenMacro(String usr) {
+    return _macros[usr];
   }
 
   bool isSeenFunctionTypedef(String originalName) {
diff --git a/lib/src/strings.dart b/lib/src/strings.dart
index e055347..726e2f5 100644
--- a/lib/src/strings.dart
+++ b/lib/src/strings.dart
@@ -7,7 +7,7 @@
     as clang;
 
 // This version must be updated whenever we update the libclang wrapper.
-const dylibVersion = 'v2';
+const dylibVersion = 'v3';
 
 /// Name of the dynamic library file according to current platform.
 String get dylibFileName {
diff --git a/pubspec.yaml b/pubspec.yaml
index 1820277..13ee6db 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -3,7 +3,7 @@
 # BSD-style license that can be found in the LICENSE file.
 
 name: ffigen
-version: 0.2.1+1
+version: 0.2.2
 homepage: https://github.com/dart-lang/ffigen
 description: Experimental generator for FFI bindings, using LibClang to parse C/C++ header files.
 
diff --git a/test/header_parser_tests/typedef.h b/test/header_parser_tests/typedef.h
index f9aa357..3e9d996 100644
--- a/test/header_parser_tests/typedef.h
+++ b/test/header_parser_tests/typedef.h
@@ -11,3 +11,43 @@
 };
 
 extern NamedFunctionProto func1(NamedFunctionProto named, void (*unnamed)(int));
+
+typedef struct
+{
+
+} AnonymousStructInTypedef;
+// These typerefs do not affect the name of AnonymousStructInTypedef.
+typedef AnonymousStructInTypedef Typeref1;
+typedef AnonymousStructInTypedef Typeref2;
+
+// Name from global namespace is used.
+typedef struct _NamedStructInTypedef
+{
+
+} NamedStructInTypedef;
+
+// Both these names must be exlucded or this struct will be generated.
+typedef struct _ExcludedStruct
+{
+
+} ExcludedStruct;
+typedef ExcludedStruct NTyperef1;
+
+// Because `struct _ExcludedStruct` is excluded, the type name used
+// in this function (the first function) will be used.
+// Therefore, _ExcludedStruct will be generated as NTyperef1.
+void func2(NTyperef1 *);
+
+typedef enum
+{
+
+} AnonymousEnumInTypedef;
+// These typerefs do not affect the name of AnonymousEnumInTypedef.
+typedef AnonymousEnumInTypedef Typeref1;
+typedef AnonymousEnumInTypedef Typeref2;
+
+// Name from global namespace is used.
+typedef enum _NamedEnumInTypedef
+{
+
+} NamedEnumInTypedef;
diff --git a/test/header_parser_tests/typedef_test.dart b/test/header_parser_tests/typedef_test.dart
index 8480acd..7f7646c 100644
--- a/test/header_parser_tests/typedef_test.dart
+++ b/test/header_parser_tests/typedef_test.dart
@@ -27,6 +27,10 @@
 ${strings.headers}:
   ${strings.entryPoints}:
     - 'test/header_parser_tests/typedef.h'
+${strings.structs}:
+  ${strings.exclude}:
+    - ExcludedStruct
+    - _ExcludedStruct
         ''') as yaml.YamlMap),
       );
     });
@@ -43,6 +47,8 @@
     typedefType: TypedefType.C,
     returnType: Type.nativeType(SupportedNativeType.Void),
   );
+
+  final excludedNtyperef = Struc(name: 'NTyperef1');
   return Library(
     name: 'Bindings',
     bindings: [
@@ -81,6 +87,18 @@
         ],
         returnType: Type.pointer(Type.nativeFunc(namedTypedef)),
       ),
+      Struc(name: 'AnonymousStructInTypedef'),
+      Struc(name: 'NamedStructInTypedef'),
+      excludedNtyperef,
+      Func(
+        name: 'func2',
+        returnType: Type.nativeType(SupportedNativeType.Void),
+        parameters: [
+          Parameter(type: Type.pointer(Type.struct(excludedNtyperef)))
+        ],
+      ),
+      EnumClass(name: 'AnonymousEnumInTypedef'),
+      EnumClass(name: 'NamedEnumInTypedef'),
     ],
   );
 }
diff --git a/test/test_coverage.dart b/test/test_coverage.dart
index 812105f..a35f0f7 100644
--- a/test/test_coverage.dart
+++ b/test/test_coverage.dart
@@ -21,6 +21,8 @@
     as header_parser_tests_macros_test;
 import 'header_parser_tests/nested_parsing_test.dart'
     as header_parser_tests_nested_parsing_test;
+import 'header_parser_tests/typedef_test.dart'
+    as header_parser_tests_typedef_test;
 import 'header_parser_tests/unnamed_enums_test.dart'
     as header_parser_tests_unnamed_enums_test;
 import 'large_integration_tests/large_test.dart'
@@ -39,6 +41,7 @@
   header_parser_tests_macros_test.main();
   header_parser_tests_function_n_struct_test.main();
   header_parser_tests_nested_parsing_test.main();
+  header_parser_tests_typedef_test.main();
   header_parser_tests_unnamed_enums_test.main();
   native_test_native_test.main();
   rename_tests_rename_test.main();
diff --git a/tool/libclang_config.yaml b/tool/libclang_config.yaml
index 1f93991..c6baf25 100644
--- a/tool/libclang_config.yaml
+++ b/tool/libclang_config.yaml
@@ -99,3 +99,4 @@
     - clang_Cursor_Evaluate_wrap
     - clang_Cursor_isAnonymous_wrap
     - clang_Cursor_isAnonymousRecordDecl_wrap
+    - clang_getCursorUSR_wrap