diff --git a/bindgen-tests/tests/expectations/tests/opaque-template-instantiation-namespaced.rs b/bindgen-tests/tests/expectations/tests/opaque-template-instantiation-namespaced.rs
index 58644e053d..137927a302 100644
--- a/bindgen-tests/tests/expectations/tests/opaque-template-instantiation-namespaced.rs
+++ b/bindgen-tests/tests/expectations/tests/opaque-template-instantiation-namespaced.rs
@@ -86,16 +86,16 @@ pub mod root {
                 "Offset of field: ContainsOpaqueInstantiation::opaque",
             ][::std::mem::offset_of!(ContainsOpaqueInstantiation, opaque) - 0usize];
         };
+        #[allow(clippy::unnecessary_operation, clippy::identity_op)]
+        const _: () = {
+            [
+                "Size of template specialization: Template_open0_Foo_close0",
+            ][::std::mem::size_of::<root::zoidberg::Template<root::zoidberg::Foo>>()
+                - 1usize];
+            [
+                "Align of template specialization: Template_open0_Foo_close0",
+            ][::std::mem::align_of::<root::zoidberg::Template<root::zoidberg::Foo>>()
+                - 1usize];
+        };
     }
-    #[allow(clippy::unnecessary_operation, clippy::identity_op)]
-    const _: () = {
-        [
-            "Size of template specialization: Template_open0_Foo_close0",
-        ][::std::mem::size_of::<root::zoidberg::Template<root::zoidberg::Foo>>()
-            - 1usize];
-        [
-            "Align of template specialization: Template_open0_Foo_close0",
-        ][::std::mem::align_of::<root::zoidberg::Template<root::zoidberg::Foo>>()
-            - 1usize];
-    };
 }
diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs
index 998c4ca2da..0209763f4a 100644
--- a/bindgen/codegen/mod.rs
+++ b/bindgen/codegen/mod.rs
@@ -499,6 +499,36 @@ impl Item {
             return false;
         }
 
+        // Skip duplicate type aliases.
+        // If this item is a type alias or resolved ref, and its ultimate target
+        // has the same canonical path and will also be generated, skip this one.
+        if let ItemKind::Type(ty) = self.kind() {
+            match ty.kind() {
+                TypeKind::Alias(target_id) |
+                TypeKind::ResolvedTypeRef(target_id) => {
+                    let direct_target = ctx.resolve_item(*target_id);
+                    // Check if the direct target has the same path and will be generated.
+                    // If so, skip generating this alias/ref because the direct target
+                    // will either be generated or skipped in favour of its target.
+                    if direct_target.id() != self.id() &&
+                        direct_target.canonical_path(ctx) ==
+                            self.canonical_path(ctx) &&
+                        ctx.codegen_items().contains(&direct_target.id())
+                    {
+                        debug!(
+                            "<Item as CodeGenerator>::process_before_codegen: Skipping duplicate alias {:?} because its direct target {:?} has the same path and will be generated",
+                            self.id(),
+                            direct_target.id()
+                        );
+                        // Mark as seen so we don't process it again if reachable through another path
+                        result.set_seen(self.id());
+                        return false;
+                    }
+                }
+                _ => {}
+            }
+        }
+
         if !ctx.codegen_items().contains(&self.id()) {
             // TODO(emilio, #453): Figure out what to do when this happens
             // legitimately, we could track the opaque stuff and disable the
diff --git a/bindgen/ir/context.rs b/bindgen/ir/context.rs
index 99c75d63d8..7f52a5e43e 100644
--- a/bindgen/ir/context.rs
+++ b/bindgen/ir/context.rs
@@ -783,17 +783,23 @@ If you encounter an error missing from this list, please file an issue or a PR!"
         assert_ne!(item.id(), self.root_module);
         assert!(self.resolve_item_fallible(item.id()).is_none());
 
-        if let Some(ref mut parent) = self.items[item.parent_id().0] {
-            if let Some(module) = parent.as_module_mut() {
+        let mut ancestor_id = item.parent_id();
+        while let Some(ref mut ancestor_item) = self.items[ancestor_id.0] {
+            if let Some(module) = ancestor_item.as_module_mut() {
                 debug!(
-                    "add_item_to_module: adding {:?} as child of parent module {:?}",
+                    "add_item_to_module: adding {:?} as child of ancestor module {:?}",
                     item.id(),
-                    item.parent_id()
+                    ancestor_id
                 );
 
                 module.children_mut().insert(item.id());
                 return;
             }
+
+            ancestor_id = ancestor_item.parent_id();
+            if ancestor_id == ancestor_item.id() {
+                break;
+            }
         }
 
         debug!(
diff --git a/bindgen/ir/template.rs b/bindgen/ir/template.rs
index c48575a6b8..b9f68a4f46 100644
--- a/bindgen/ir/template.rs
+++ b/bindgen/ir/template.rs
@@ -224,6 +224,7 @@ impl TemplateInstantiation {
     /// Parse a `TemplateInstantiation` from a clang `Type`.
     pub(crate) fn from_ty(
         ty: &clang::Type,
+        parent_id: Option<ItemId>,
         ctx: &mut BindgenContext,
     ) -> Option<TemplateInstantiation> {
         use clang_sys::*;
@@ -237,13 +238,15 @@ impl TemplateInstantiation {
                 args.chain(canonical_args.skip(arg_count))
                     .filter(|t| t.kind() != CXType_Invalid)
                     .map(|t| {
-                        Item::from_ty_or_ref(t, t.declaration(), None, ctx)
+                        Item::from_ty_or_ref(t, t.declaration(), parent_id, ctx)
                     })
                     .collect()
             }
             None => args
                 .filter(|t| t.kind() != CXType_Invalid)
-                .map(|t| Item::from_ty_or_ref(t, t.declaration(), None, ctx))
+                .map(|t| {
+                    Item::from_ty_or_ref(t, t.declaration(), parent_id, ctx)
+                })
                 .collect(),
         });
 
diff --git a/bindgen/ir/ty.rs b/bindgen/ir/ty.rs
index 049dc980dd..6ccfe453e6 100644
--- a/bindgen/ir/ty.rs
+++ b/bindgen/ir/ty.rs
@@ -750,7 +750,7 @@ impl Type {
             (ty.template_args().is_some() && ty_kind != CXType_Typedef)
         {
             // This is a template instantiation.
-            match TemplateInstantiation::from_ty(ty, ctx) {
+            match TemplateInstantiation::from_ty(ty, parent_id, ctx) {
                 Some(inst) => TypeKind::TemplateInstantiation(inst),
                 None => TypeKind::Opaque,
             }
@@ -1071,7 +1071,7 @@ impl Type {
                 CXType_Typedef => {
                     let inner = cursor.typedef_type().expect("Not valid Type?");
                     let inner_id =
-                        Item::from_ty_or_ref(inner, location, None, ctx);
+                        Item::from_ty_or_ref(inner, location, parent_id, ctx);
                     if inner_id == potential_id {
                         warn!(
                             "Generating opaque type instead of self-referential \