Repository: mozilla/cbindgen (language: rust)

Issue:
Introduce language backend
As discussed in #846 this introduces the notion of language backend in order to facilitate the addition of new supported languages to cbindgen

Notable changes: 
 - new `LanguageBackend` trait containing functions to write headers and footers in a source file 
 - 2 `LanguageBackend` impls : `CLikeLanguageBackend` and `CythonLanguageBackend`
 - the `Source` trait is now generic on a LanguageBackend and gets the language backend instead of a config as an argument ( both backend impls, end up wrapping a config)
 - The implementations of `Source` for the various structs of the `ir` have been moved to the corresponding backend module as there is now one implementation of `Source` per `LanguageBackend` for each relevant `ir` struct. 
 - The implementations of `Source` for both `LanguageBackend`s come from the existing impls and have been cleaned to contain only the parts relevant to the language of the Backend for which the impl is for

All tests continue to pass without any modification \o/ (those were quite useful during the refacto)


Discussion / hints:

Fix patch:
diff --git a/src/bindgen/bindings.rs b/src/bindgen/bindings.rs
index 80793db4b..029cfc66b 100644
--- a/src/bindgen/bindings.rs
+++ b/src/bindgen/bindings.rs
@@ -13,9 +13,12 @@ use std::rc::Rc;
 
 use crate::bindgen::config::{Config, Language};
 use crate::bindgen::ir::{
-    Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct, Typedef,
+    Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct, Type, Typedef,
 };
-use crate::bindgen::writer::{Source, SourceWriter};
+use crate::bindgen::language_backend::{
+    CLikeLanguageBackend, CythonLanguageBackend, LanguageBackend,
+};
+use crate::bindgen::writer::SourceWriter;
 
 /// A bindings header that can be written.
 pub struct Bindings {
@@ -25,21 +28,15 @@ pub struct Bindings {
     struct_map: ItemMap<Struct>,
     typedef_map: ItemMap<Typedef>,
     struct_fileds_memo: RefCell<HashMap<BindgenPath, Rc<Vec<String>>>>,
-    globals: Vec<Static>,
-    constants: Vec<Constant>,
-    items: Vec<ItemContainer>,
-    functions: Vec<Function>,
+    pub globals: Vec<Static>,
+    pub constants: Vec<Constant>,
+    pub items: Vec<ItemContainer>,
+    pub functions: Vec<Function>,
     source_files: Vec<path::PathBuf>,
     /// Bindings are generated by a recursive call to cbindgen
     /// and shouldn't do anything when written anywhere.
     noop: bool,
-    package_version: String,
-}
-
-#[derive(PartialEq, Eq)]
-enum NamespaceOperation {
-    Open,
-    Close,
+    pub package_version: String,
 }
 
 impl Bindings {
@@ -80,8 +77,6 @@ impl Bindings {
 
     /// Peels through typedefs to allow resolving structs.
     fn resolved_struct_path<'a>(&self, path: &'a BindgenPath) -> Cow<'a, BindgenPath> {
-        use crate::bindgen::ir::Type;
-
         let mut resolved_path = Cow::Borrowed(path);
         loop {
             let mut found = None;
@@ -207,360 +202,28 @@ impl Bindings {
         }
     }
 
-    pub fn write_headers<F: Write>(&self, out: &mut SourceWriter<F>) {
-        if self.noop {
-            return;
-        }
-
-        if self.config.package_version {
-            out.new_line_if_not_start();
-            match self.config.language {
-                Language::C | Language::Cxx => {
-                    write!(out, "/* Package version: {} */", self.package_version);
-                }
-                Language::Cython => {
-                    write!(out, "''' Package version: {} '''", self.package_version);
-                }
-            }
-
-            out.new_line();
-        }
-
-        if let Some(ref f) = self.config.header {
-            out.new_line_if_not_start();
-            write!(out, "{}", f);
-            out.new_line();
-        }
-        if let Some(f) = self.config.include_guard() {
-            out.new_line_if_not_start();
-            write!(out, "#ifndef {}", f);
-            out.new_line();
-            write!(out, "#define {}", f);
-            out.new_line();
-        }
-        if self.config.pragma_once && self.config.language != Language::Cython {
-            out.new_line_if_not_start();
-            write!(out, "#pragma once");
-            out.new_line();
-        }
-        if self.config.include_version {
-            out.new_line_if_not_start();
-            match self.config.language {
-                Language::C | Language::Cxx => {
-                    write!(
-                        out,
-                        "/* Generated with cbindgen:{} */",
-                        crate::bindgen::config::VERSION
-                    );
-                }
-                Language::Cython => {
-                    write!(
-                        out,
-                        "''' Generated with cbindgen:{} '''",
-                        crate::bindgen::config::VERSION
-                    );
-                }
-            }
-
-            out.new_line();
-        }
-        if let Some(ref f) = self.config.autogen_warning {
-            out.new_line_if_not_start();
-            write!(out, "{}", f);
-            out.new_line();
-        }
-
-        if self.config.no_includes
-            && self.config.sys_includes().is_empty()
-            && self.config.includes().is_empty()
-            && (self.config.cython.cimports.is_empty() || self.config.language != Language::Cython)
-            && self.config.after_includes.is_none()
-        {
-            return;
-        }
-
-        out.new_line_if_not_start();
-
-        if !self.config.no_includes {
-            match self.config.language {
-                Language::C => {
-                    out.write("#include <stdarg.h>");
-                    out.new_line();
-                    out.write("#include <stdbool.h>");
-                    out.new_line();
-                    if self.config.usize_is_size_t {
-                        out.write("#include <stddef.h>");
-                        out.new_line();
-                    }
-                    out.write("#include <stdint.h>");
-                    out.new_line();
-                    out.write("#include <stdlib.h>");
-                    out.new_line();
-                }
-                Language::Cxx => {
-                    out.write("#include <cstdarg>");
-                    out.new_line();
-                    if self.config.usize_is_size_t {
-                        out.write("#include <cstddef>");
-                        out.new_line();
-                    }
-                    out.write("#include <cstdint>");
-                    out.new_line();
-                    out.write("#include <cstdlib>");
-                    out.new_line();
-                    out.write("#include <ostream>");
-                    out.new_line();
-                    out.write("#include <new>");
-                    out.new_line();
-                    if self.config.enumeration.cast_assert_name.is_none()
-                        && (self.config.enumeration.derive_mut_casts
-                            || self.config.enumeration.derive_const_casts)
-                    {
-                        out.write("#include <cassert>");
-                        out.new_line();
-                    }
-                }
-                Language::Cython => {
-                    out.write(
-                        "from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t",
-                    );
-                    out.new_line();
-                    out.write(
-                        "from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t",
-                    );
-                    out.new_line();
-                    out.write("cdef extern from *");
-                    out.open_brace();
-                    out.write("ctypedef bint bool");
-                    out.new_line();
-                    out.write("ctypedef struct va_list");
-                    out.new_line();
-                    out.close_brace(false);
-                }
+    pub fn write<F: Write>(&self, file: F) {
+        match self.config.language {
+            Language::Cxx | Language::C => {
+                self.write_with_backend(file, &mut CLikeLanguageBackend::new(&self.config))
             }
-        }
-
-        for include in self.config.sys_includes() {
-            write!(out, "#include <{}>", include);
-            out.new_line();
-        }
-
-        for include in self.config.includes() {
-            write!(out, "#include \"{}\"", include);
-            out.new_line();
-        }
-
-        if self.config.language == Language::Cython {
-            for (module, names) in &self.config.cython.cimports {
-                write!(out, "from {} cimport {}", module, names.join(", "));
-                out.new_line();
+            Language::Cython => {
+                self.write_with_backend(file, &mut CythonLanguageBackend::new(&self.config))
             }
         }
-
-        if let Some(ref line) = self.config.after_includes {
-            write!(out, "{}", line);
-            out.new_line();
-        }
     }
 
-    pub fn write<F: Write>(&self, file: F) {
+    fn write_with_backend<F: Write, LB: LanguageBackend>(
+        &self,
+        file: F,
+        language_backend: &mut LB,
+    ) {
         if self.noop {
             return;
         }
 
         let mut out = SourceWriter::new(file, self);
 
-        self.write_headers(&mut out);
-
-        self.open_namespaces(&mut out);
-
-        for constant in &self.constants {
-            if constant.uses_only_primitive_types() {
-                out.new_line_if_not_start();
-                constant.write(&self.config, &mut out, None);
-                out.new_line();
-            }
-        }
-
-        for item in &self.items {
-            if item
-                .deref()
-                .annotations()
-                .bool("no-export")
-                .unwrap_or(false)
-            {
-                continue;
-            }
-
-            out.new_line_if_not_start();
-            match *item {
-                ItemContainer::Constant(..) => unreachable!(),
-                ItemContainer::Static(..) => unreachable!(),
-                ItemContainer::Enum(ref x) => x.write(&self.config, &mut out),
-                ItemContainer::Struct(ref x) => x.write(&self.config, &mut out),
-                ItemContainer::Union(ref x) => x.write(&self.config, &mut out),
-                ItemContainer::OpaqueItem(ref x) => x.write(&self.config, &mut out),
-                ItemContainer::Typedef(ref x) => x.write(&self.config, &mut out),
-            }
-            out.new_line();
-        }
-
-        for constant in &self.constants {
-            if !constant.uses_only_primitive_types() {
-                out.new_line_if_not_start();
-                constant.write(&self.config, &mut out, None);
-                out.new_line();
-            }
-        }
-
-        if !self.functions.is_empty() || !self.globals.is_empty() {
-            if self.config.cpp_compatible_c() {
-                out.new_line_if_not_start();
-                out.write("#ifdef __cplusplus");
-            }
-
-            if self.config.language == Language::Cxx {
-                if let Some(ref using_namespaces) = self.config.using_namespaces {
-                    for namespace in using_namespaces {
-                        out.new_line();
-                        write!(out, "using namespace {};", namespace);
-                    }
-                    out.new_line();
-                }
-            }
-
-            if self.config.language == Language::Cxx || self.config.cpp_compatible_c() {
-                out.new_line();
-                out.write("extern \"C\" {");
-                out.new_line();
-            }
-
-            if self.config.cpp_compatible_c() {
-                out.write("#endif // __cplusplus");
-                out.new_line();
-            }
-
-            for global in &self.globals {
-                out.new_line_if_not_start();
-                global.write(&self.config, &mut out);
-                out.new_line();
-            }
-
-            for function in &self.functions {
-                out.new_line_if_not_start();
-                function.write(&self.config, &mut out);
-                out.new_line();
-            }
-
-            if self.config.cpp_compatible_c() {
-                out.new_line();
-                out.write("#ifdef __cplusplus");
-            }
-
-            if self.config.language == Language::Cxx || self.config.cpp_compatible_c() {
-                out.new_line();
-                out.write("} // extern \"C\"");
-                out.new_line();
-            }
-
-            if self.config.cpp_compatible_c() {
-                out.write("#endif // __cplusplus");
-                out.new_line();
-            }
-        }
-
-        if self.config.language == Language::Cython
-            && self.globals.is_empty()
-            && self.constants.is_empty()
-            && self.items.is_empty()
-            && self.functions.is_empty()
-        {
-            out.write("pass");
-        }
-
-        self.close_namespaces(&mut out);
-
-        if let Some(f) = self.config.include_guard() {
-            out.new_line_if_not_start();
-            if self.config.language == Language::C {
-                write!(out, "#endif /* {} */", f);
-            } else {
-                write!(out, "#endif // {}", f);
-            }
-            out.new_line();
-        }
-        if let Some(ref f) = self.config.trailer {
-            out.new_line_if_not_start();
-            write!(out, "{}", f);
-            if !f.ends_with('\n') {
-                out.new_line();
-            }
-        }
-    }
-
-    fn all_namespaces(&self) -> Vec<&str> {
-        if self.config.language != Language::Cxx && !self.config.cpp_compatible_c() {
-            return vec![];
-        }
-        let mut ret = vec![];
-        if let Some(ref namespace) = self.config.namespace {
-            ret.push(&**namespace);
-        }
-        if let Some(ref namespaces) = self.config.namespaces {
-            for namespace in namespaces {
-                ret.push(&**namespace);
-            }
-        }
-        ret
-    }
-
-    fn open_close_namespaces<F: Write>(&self, op: NamespaceOperation, out: &mut SourceWriter<F>) {
-        if self.config.language == Language::Cython {
-            if op == NamespaceOperation::Open {
-                out.new_line();
-                let header = self.config.cython.header.as_deref().unwrap_or("*");
-                write!(out, "cdef extern from {}", header);
-                out.open_brace();
-            } else {
-                out.close_brace(false);
-            }
-            return;
-        }
-
-        let mut namespaces = self.all_namespaces();
-        if namespaces.is_empty() {
-            return;
-        }
-
-        if op == NamespaceOperation::Close {
-            namespaces.reverse();
-        }
-
-        if self.config.cpp_compatible_c() {
-            out.new_line_if_not_start();
-            out.write("#ifdef __cplusplus");
-        }
-
-        for namespace in namespaces {
-            out.new_line();
-            match op {
-                NamespaceOperation::Open => write!(out, "namespace {} {{", namespace),
-                NamespaceOperation::Close => write!(out, "}} // namespace {}", namespace),
-            }
-        }
-
-        out.new_line();
-        if self.config.cpp_compatible_c() {
-            out.write("#endif // __cplusplus");
-            out.new_line();
-        }
-    }
-
-    pub(crate) fn open_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) {
-        self.open_close_namespaces(NamespaceOperation::Open, out);
-    }
-
-    pub(crate) fn close_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) {
-        self.open_close_namespaces(NamespaceOperation::Close, out);
+        language_backend.write_bindings(&mut out, self);
     }
 }
diff --git a/src/bindgen/cdecl.rs b/src/bindgen/cdecl.rs
index a9c5a0fcf..fc96042cc 100644
--- a/src/bindgen/cdecl.rs
+++ b/src/bindgen/cdecl.rs
@@ -7,6 +7,7 @@ use std::io::Write;
 use crate::bindgen::config::Layout;
 use crate::bindgen::declarationtyperesolver::DeclarationType;
 use crate::bindgen::ir::{ConstExpr, Function, GenericArgument, Type};
+use crate::bindgen::language_backend::LanguageBackend;
 use crate::bindgen::writer::{ListType, SourceWriter};
 use crate::bindgen::{Config, Language};
 
@@ -183,7 +184,7 @@ impl CDecl {
                 });
                 self.declarators.push(CDeclarator::Func {
                     args,
-                    layout: config.function.args.clone(),
+                    layout: config.function.args,
                     never_return: *never_return,
                 });
                 self.build_type(ret, false, config);
@@ -191,7 +192,13 @@ impl CDecl {
         }
     }
 
-    fn write<F: Write>(&self, out: &mut SourceWriter<F>, ident: Option<&str>, config: &Config) {
+    fn write<F: Write, LB: LanguageBackend>(
+        &self,
+        language_backend: &mut LB,
+        out: &mut SourceWriter<F>,
+        ident: Option<&str>,
+        config: &Config,
+    ) {
         // Write the type-specifier and type-qualifier first
         if !self.type_qualifers.is_empty() {
             write!(out, "{} ", self.type_qualifers);
@@ -207,7 +214,15 @@ impl CDecl {
 
         if !self.type_generic_args.is_empty() {
             out.write("<");
-            out.write_horizontal_source_list(&self.type_generic_args, ListType::Join(", "));
+            out.write_horizontal_source_list(
+                language_backend,
+                &self.type_generic_args,
+                ListType::Join(", "),
+                |language_backend, out, g| match *g {
+                    GenericArgument::Type(ref ty) => language_backend.write_type(out, ty),
+                    GenericArgument::Const(ref expr) => write!(out, "{}", expr.as_str()),
+                },
+            );
             out.write(">");
         }
 
@@ -289,7 +304,8 @@ impl CDecl {
                         out.write("void");
                     }
 
-                    fn write_vertical<F: Write>(
+                    fn write_vertical<F: Write, LB: LanguageBackend>(
+                        language_backend: &mut LB,
                         out: &mut SourceWriter<F>,
                         config: &Config,
                         args: &[(Option<String>, CDecl)],
@@ -305,12 +321,13 @@ impl CDecl {
                             // Convert &Option<String> to Option<&str>
                             let arg_ident = arg_ident.as_ref().map(|x| x.as_ref());
 
-                            arg_ty.write(out, arg_ident, config);
+                            arg_ty.write(language_backend, out, arg_ident, config);
                         }
                         out.pop_tab();
                     }
 
-                    fn write_horizontal<F: Write>(
+                    fn write_horizontal<F: Write, LB: LanguageBackend>(
+                        language_backend: &mut LB,
                         out: &mut SourceWriter<F>,
                         config: &Config,
                         args: &[(Option<String>, CDecl)],
@@ -323,19 +340,19 @@ impl CDecl {
                             // Convert &Option<String> to Option<&str>
                             let arg_ident = arg_ident.as_ref().map(|x| x.as_ref());
 
-                            arg_ty.write(out, arg_ident, config);
+                            arg_ty.write(language_backend, out, arg_ident, config);
                         }
                     }
 
                     match layout {
-                        Layout::Vertical => write_vertical(out, config, args),
-                        Layout::Horizontal => write_horizontal(out, config, args),
+                        Layout::Vertical => write_vertical(language_backend, out, config, args),
+                        Layout::Horizontal => write_horizontal(language_backend, out, config, args),
                         Layout::Auto => {
                             if !out.try_write(
-                                |out| write_horizontal(out, config, args),
+                                |out| write_horizontal(language_backend, out, config, args),
                                 config.line_length,
                             ) {
-                                write_vertical(out, config, args)
+                                write_vertical(language_backend, out, config, args)
                             }
                         }
                     }
@@ -354,19 +371,31 @@ impl CDecl {
     }
 }
 
-pub fn write_func<F: Write>(
+pub fn write_func<F: Write, LB: LanguageBackend>(
+    language_backend: &mut LB,
     out: &mut SourceWriter<F>,
     f: &Function,
     layout: Layout,
     config: &Config,
 ) {
-    CDecl::from_func(f, layout, config).write(out, Some(f.path().name()), config);
+    CDecl::from_func(f, layout, config).write(language_backend, out, Some(f.path().name()), config);
 }
 
-pub fn write_field<F: Write>(out: &mut SourceWriter<F>, t: &Type, ident: &str, config: &Config) {
-    CDecl::from_type(t, config).write(out, Some(ident), config);
+pub fn write_field<F: Write, LB: LanguageBackend>(
+    language_backend: &mut LB,
+    out: &mut SourceWriter<F>,
+    t: &Type,
+    ident: &str,
+    config: &Config,
+) {
+    CDecl::from_type(t, config).write(language_backend, out, Some(ident), config);
 }
 
-pub fn write_type<F: Write>(out: &mut SourceWriter<F>, t: &Type, config: &Config) {
-    CDecl::from_type(t, config).write(out, None, config);
+pub fn write_type<F: Write, LB: LanguageBackend>(
+    language_backend: &mut LB,
+    out: &mut SourceWriter<F>,
+    t: &Type,
+    config: &Config,
+) {
+    CDecl::from_type(t, config).write(language_backend, out, None, config);
 }
diff --git a/src/bindgen/config.rs b/src/bindgen/config.rs
index dc5fa4635..c28c2aa67 100644
--- a/src/bindgen/config.rs
+++ b/src/bindgen/config.rs
@@ -134,7 +134,7 @@ impl FromStr for Braces {
 deserialize_enum_str!(Braces);
 
 /// A type of layout to use when generating long lines of code.
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub enum Layout {
     Horizontal,
     Vertical,
diff --git a/src/bindgen/ir/constant.rs b/src/bindgen/ir/constant.rs
index 51e59b2d9..d884b6e9d 100644
--- a/src/bindgen/ir/constant.rs
+++ b/src/bindgen/ir/constant.rs
@@ -16,8 +16,9 @@ use crate::bindgen::ir::{
     AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path,
     Struct, ToCondition, Type,
 };
+use crate::bindgen::language_backend::LanguageBackend;
 use crate::bindgen::library::Library;
-use crate::bindgen::writer::{Source, SourceWriter};
+use crate::bindgen::writer::SourceWriter;
 use crate::bindgen::Bindings;
 
 fn member_to_ident(member: &syn::Member) -> String {
@@ -28,7 +29,7 @@ fn member_to_ident(member: &syn::Member) -> String {
 }
 
 // TODO: Maybe add support to more std associated constants.
-fn to_known_assoc_constant(associated_to: &Path, name: &str) -> Option<String> {
+pub(crate) fn to_known_assoc_constant(associated_to: &Path, name: &str) -> Option<String> {
     use crate::bindgen::ir::{IntKind, PrimitiveType};
 
     if name != "MAX" && name != "MIN" {
@@ -472,107 +473,6 @@ impl Literal {
             _ => Err(format!("Unsupported expression. {:?}", *expr)),
         }
     }
-
-    pub(crate) fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
-        match self {
-            Literal::Expr(v) => match (&**v, config.language) {
-                ("true", Language::Cython) => write!(out, "True"),
-                ("false", Language::Cython) => write!(out, "False"),
-                (v, _) => write!(out, "{}", v),
-            },
-            Literal::Path {
-                ref associated_to,
-                ref name,
-            } => {
-                if let Some((ref path, ref export_name)) = associated_to {
-                    if let Some(known) = to_known_assoc_constant(path, name) {
-                        return write!(out, "{}", known);
-                    }
-                    let path_separator = match config.language {
-                        Language::Cython | Language::C => "_",
-                        Language::Cxx => {
-                            if config.structure.associated_constants_in_body {
-                                "::"
-                            } else {
-                                "_"
-                            }
-                        }
-                    };
-                    write!(out, "{}{}", export_name, path_separator)
-                }
-                write!(out, "{}", name)
-            }
-            Literal::FieldAccess {
-                ref base,
-                ref field,
-            } => {
-                write!(out, "(");
-                base.write(config, out);
-                write!(out, ").{}", field);
-            }
-            Literal::PostfixUnaryOp { op, ref value } => {
-                write!(out, "{}", op);
-                value.write(config, out);
-            }
-            Literal::BinOp {
-                ref left,
-                op,
-                ref right,
-            } => {
-                write!(out, "(");
-                left.write(config, out);
-                write!(out, " {} ", op);
-                right.write(config, out);
-                write!(out, ")");
-            }
-            Literal::Cast { ref ty, ref value } => {
-                out.write(if config.language == Language::Cython {
-                    "<"
-                } else {
-                    "("
-                });
-                ty.write(config, out);
-                out.write(if config.language == Language::Cython {
-                    ">"
-                } else {
-                    ")"
-                });
-                value.write(config, out);
-            }
-            Literal::Struct {
-                export_name,
-                fields,
-                path,
-            } => {
-                match config.language {
-                    Language::C => write!(out, "({})", export_name),
-                    Language::Cxx => write!(out, "{}", export_name),
-                    Language::Cython => write!(out, "<{}>", export_name),
-                }
-
-                write!(out, "{{ ");
-                let mut is_first_field = true;
-                // In C++, same order as defined is required.
-                let ordered_fields = out.bindings().struct_field_names(path);
-                for ordered_key in ordered_fields.iter() {
-                    if let Some(lit) = fields.get(ordered_key) {
-                        if !is_first_field {
-                            write!(out, ", ");
-                        } else {
-                            is_first_field = false;
-                        }
-                        match config.language {
-                            Language::Cxx => write!(out, "/* .{} = */ ", ordered_key),
-                            Language::C => write!(out, ".{} = ", ordered_key),
-                            Language::Cython => {}
-                        }
-                        lit.write(config, out);
-                    }
-                }
-                write!(out, " }}");
-            }
-        }
-    }
 }
 
 #[derive(Debug, Clone)]
@@ -692,9 +592,10 @@ impl Item for Constant {
 }
 
 impl Constant {
-    pub fn write_declaration<F: Write>(
+    pub fn write_declaration<F: Write, LB: LanguageBackend>(
         &self,
         config: &Config,
+        language_backend: &mut LB,
         out: &mut SourceWriter<F>,
         associated_to_struct: &Struct,
     ) {
@@ -709,13 +610,14 @@ impl Constant {
         } else {
             out.write("static const ");
         }
-        self.ty.write(config, out);
+        language_backend.write_type(out, &self.ty);
         write!(out, " {};", self.export_name())
     }
 
-    pub fn write<F: Write>(
+    pub fn write<F: Write, LB: LanguageBackend>(
         &self,
         config: &Config,
+        language_backend: &mut LB,
         out: &mut SourceWriter<F>,
         associated_to_struct: Option<&Struct>,
     ) {
@@ -770,7 +672,7 @@ impl Constant {
             _ => &self.value,
         };
 
-        self.documentation.write(config, out);
+        language_backend.write_documentation(out, &self.documentation);
 
         let allow_constexpr = config.constant.allow_constexpr && self.value.can_be_constexpr();
         match config.language {
@@ -789,22 +691,22 @@ impl Constant {
                     out.write("const ");
                 }
 
-                self.ty.write(config, out);
+                language_backend.write_type(out, &self.ty);
                 write!(out, " {} = ", name);
-                value.write(config, out);
+                language_backend.write_literal(out, value);
                 write!(out, ";");
             }
             Language::Cxx | Language::C => {
                 write!(out, "#define {} ", name);
-                value.write(config, out);
+                language_backend.write_literal(out, value);
             }
             Language::Cython => {
                 out.write("const ");
-                self.ty.write(config, out);
+                language_backend.write_type(out, &self.ty);
                 // For extern Cython declarations the initializer is ignored,
                 // but still useful as documentation, so we write it as a comment.
                 write!(out, " {} # = ", name);
-                value.write(config, out);
+                language_backend.write_literal(out, value);
             }
         }
 
diff --git a/src/bindgen/ir/documentation.rs b/src/bindgen/ir/documentation.rs
index 6822c0eaf..e6dba9794 100644
--- a/src/bindgen/ir/documentation.rs
+++ b/src/bindgen/ir/documentation.rs
@@ -2,11 +2,7 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use std::io::Write;
-
-use crate::bindgen::config::{Config, DocumentationLength, DocumentationStyle, Language};
 use crate::bindgen::utilities::SynAttributeHelpers;
-use crate::bindgen::writer::{Source, SourceWriter};
 
 #[derive(Debug, Clone)]
 pub struct Documentation {
@@ -36,76 +32,3 @@ impl Documentation {
         }
     }
 }
-
-impl Source for Documentation {
-    fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
-        if self.doc_comment.is_empty() || !config.documentation {
-            return;
-        }
-
-        let end = match config.documentation_length {
-            DocumentationLength::Short => 1,
-            DocumentationLength::Full => self.doc_comment.len(),
-        };
-
-        // Cython uses Python-style comments, so `documentation_style` is not relevant.
-        if config.language == Language::Cython {
-            for line in &self.doc_comment[..end] {
-                write!(out, "#{}", line);
-                out.new_line();
-            }
-            return;
-        }
-
-        let style = match config.documentation_style {
-            DocumentationStyle::Auto if config.language == Language::C => DocumentationStyle::Doxy,
-            DocumentationStyle::Auto if config.language == Language::Cxx => DocumentationStyle::Cxx,
-            DocumentationStyle::Auto => DocumentationStyle::C, // Fallback if `Language` gets extended.
-            other => other,
-        };
-
-        // Following these documents for style conventions:
-        // https://en.wikibooks.org/wiki/C++_Programming/Code/Style_Conventions/Comments
-        // https://www.cs.cmu.edu/~410/doc/doxygen.html
-        match style {
-            DocumentationStyle::C => {
-                out.write("/*");
-                out.new_line();
-            }
-
-            DocumentationStyle::Doxy => {
-                out.write("/**");
-                out.new_line();
-            }
-
-            _ => (),
-        }
-
-        for line in &self.doc_comment[..end] {
-            match style {
-                DocumentationStyle::C => out.write(""),
-                DocumentationStyle::Doxy => out.write(" *"),
-                DocumentationStyle::C99 => out.write("//"),
-                DocumentationStyle::Cxx => out.write("///"),
-                DocumentationStyle::Auto => unreachable!(), // Auto case should always be covered
-            }
-
-            write!(out, "{}", line);
-            out.new_line();
-        }
-
-        match style {
-            DocumentationStyle::C => {
-                out.write(" */");
-                out.new_line();
-            }
-
-            DocumentationStyle::Doxy => {
-                out.write(" */");
-                out.new_line();
-            }
-
-            _ => (),
-        }
-    }
-}
diff --git a/src/bindgen/ir/enumeration.rs b/src/bindgen/ir/enumeration.rs
index 11139a0fb..d98cae4ff 100644
--- a/src/bindgen/ir/enumeration.rs
+++ b/src/bindgen/ir/enumeration.rs
@@ -14,12 +14,13 @@ use crate::bindgen::ir::{
     GenericArgument, GenericParams, GenericPath, Item, ItemContainer, Literal, Path, Repr,
     ReprStyle, Struct, ToCondition, Type,
 };
+use crate::bindgen::language_backend::LanguageBackend;
 use crate::bindgen::library::Library;
 use crate::bindgen::mangle;
 use crate::bindgen::monomorph::Monomorphs;
 use crate::bindgen::rename::{IdentifierType, RenameRule};
 use crate::bindgen::reserved;
-use crate::bindgen::writer::{ListType, Source, SourceWriter};
+use crate::bindgen::writer::{ListType, SourceWriter};
 
 #[allow(clippy::large_enum_variant)]
 #[derive(Debug, Clone)]
@@ -46,7 +47,7 @@ impl VariantBody {
         Self::Empty(AnnotationSet::new())
     }
 
-    fn annotations(&self) -> &AnnotationSet {
+    pub fn annotations(&self) -> &AnnotationSet {
         match *self {
             Self::Empty(ref anno) => anno,
             Self::Body { ref body, .. } => &body.annotations,
@@ -292,40 +293,6 @@ impl EnumVariant {
     }
 }
 
-impl Source for EnumVariant {
-    fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
-        let condition = self.cfg.to_condition(config);
-        // Cython doesn't support conditional enum variants.
-        if config.language != Language::Cython {
-            condition.write_before(config, out);
-        }
-        self.documentation.write(config, out);
-        write!(out, "{}", self.export_name);
-
-        if let Some(note) = self
-            .body
-            .annotations()
-            .deprecated_note(config, DeprecatedNoteKind::EnumVariant)
-        {
-            write!(out, " {}", note);
-        }
-
-        if let Some(discriminant) = &self.discriminant {
-            if config.language == Language::Cython {
-                // For extern Cython declarations the enumerator value is ignored,
-                // but still useful as documentation, so we write it as a comment.
-                out.write(" #")
-            }
-            out.write(" = ");
-            discriminant.write(config, out);
-        }
-        out.write(",");
-        if config.language != Language::Cython {
-            condition.write_after(config, out);
-        }
-    }
-}
-
 #[derive(Debug, Clone)]
 pub struct Enum {
     pub path: Path,
@@ -341,12 +308,12 @@ pub struct Enum {
 
 impl Enum {
     /// Name of the generated tag enum.
-    fn tag_name(&self) -> &str {
+    pub(crate) fn tag_name(&self) -> &str {
         self.tag.as_deref().unwrap_or_else(|| self.export_name())
     }
 
     /// Enum with data turns into a union of structs with each struct having its own tag field.
-    fn inline_tag_field(repr: &Repr) -> bool {
+    pub(crate) fn inline_tag_field(repr: &Repr) -> bool {
         repr.style != ReprStyle::C
     }
 
@@ -663,99 +630,23 @@ impl Item for Enum {
     }
 }
 
-impl Source for Enum {
-    fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
-        let size = self.repr.ty.map(|ty| ty.to_primitive().to_repr_c(config));
-        let has_data = self.tag.is_some();
-        let inline_tag_field = Self::inline_tag_field(&self.repr);
-        let tag_name = self.tag_name();
-
-        let condition = self.cfg.to_condition(config);
-        condition.write_before(config, out);
-
-        self.documentation.write(config, out);
-        self.generic_params.write(config, out);
-
-        // If the enum has data, we need to emit a struct or union for the data
-        // and enum for the tag. C++ supports nested type definitions, so we open
-        // the struct or union here and define the tag enum inside it (*).
-        if has_data && config.language == Language::Cxx {
-            self.open_struct_or_union(config, out, inline_tag_field);
-        }
-
-        // Emit the tag enum and everything related to it.
-        self.write_tag_enum(config, out, size, has_data, tag_name);
-
-        // If the enum has data, we need to emit structs for the variants and gather them together.
-        if has_data {
-            self.write_variant_defs(config, out);
-            out.new_line();
-            out.new_line();
-
-            // Open the struct or union for the data (**), gathering all the variants with data
-            // together, unless it's C++, then we have already opened that struct/union at (*) and
-            // are currently inside it.
-            if config.language != Language::Cxx {
-                self.open_struct_or_union(config, out, inline_tag_field);
-            }
-
-            // Emit tag field that is separate from all variants.
-            self.write_tag_field(config, out, size, inline_tag_field, tag_name);
-            out.new_line();
-
-            // Open union of all variants with data, only in the non-inline tag scenario.
-            // Cython extern declarations don't manage layouts, layouts are defined entierly by the
-            // corresponding C code. So we can inline the unnamed union into the struct and get the
-            // same observable result. Moreother we have to do it because Cython doesn't support
-            // unnamed unions.
-            if !inline_tag_field && config.language != Language::Cython {
-                out.write("union");
-                out.open_brace();
-            }
-
-            // Emit fields for all variants with data.
-            self.write_variant_fields(config, out, inline_tag_field);
-
-            // Close union of all variants with data, only in the non-inline tag scenario.
-            // See the comment about Cython on `open_brace`.
-            if !inline_tag_field && config.language != Language::Cython {
-                out.close_brace(true);
-            }
-
-            // Emit convenience methods for the struct or enum for the data.
-            self.write_derived_functions_data(config, out, tag_name);
-
-            // Emit the post_body section, if relevant.
-            if let Some(body) = config.export.post_body(&self.path) {
-                out.new_line();
-                out.write_raw_block(body);
-            }
-
-            // Close the struct or union opened either at (*) or at (**).
-            if config.language == Language::C && config.style.generate_typedef() {
-                out.close_brace(false);
-                write!(out, " {};", self.export_name);
-            } else {
-                out.close_brace(true);
-            }
-        }
-
-        condition.write_after(config, out);
-    }
-}
-
 impl Enum {
     /// Emit the tag enum and convenience methods for it.
     /// For enums with data this is only a part of the output,
     /// but for enums without data it's the whole output (modulo doc comments etc.).
-    fn write_tag_enum<F: Write>(
+    pub(crate) fn write_tag_enum<
+        F: Write,
+        LB: LanguageBackend,
+        WV: Fn(&mut LB, &mut SourceWriter<F>, &EnumVariant),
+    >(
         &self,
         config: &Config,
+        language_backend: &mut LB,
         out: &mut SourceWriter<F>,
         size: Option<&str>,
-        has_data: bool,
-        tag_name: &str,
+        write_variant: WV,
     ) {
+        let tag_name = self.tag_name();
         // Open the tag enum.
         match config.language {
             Language::C => {
@@ -838,7 +729,7 @@ impl Enum {
             if i != 0 {
                 out.new_line()
             }
-            variant.write(config, out);
+            write_variant(language_backend, out, variant);
         }
 
         // Close the tag enum.
@@ -870,11 +761,11 @@ impl Enum {
         }
 
         // Emit convenience methods for the tag enum.
-        self.write_derived_functions_enum(config, out, has_data, tag_name);
+        self.write_derived_functions_enum(config, language_backend, out);
     }
 
     /// The code here mirrors the beginning of `Struct::write` and `Union::write`.
-    fn open_struct_or_union<F: Write>(
+    pub(crate) fn open_struct_or_union<F: Write>(
         &self,
         config: &Config,
         out: &mut SourceWriter<F>,
@@ -915,7 +806,12 @@ impl Enum {
     }
 
     /// Emit struct definitions for variants having data.
-    fn write_variant_defs<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+    pub(crate) fn write_variant_defs<F: Write, LB: LanguageBackend>(
+        &self,
+        config: &Config,
+        language_backend: &mut LB, // TODO probably need only one of Config/LanguageBackend
+        out: &mut SourceWriter<F>,
+    ) {
         for variant in &self.variants {
             if let VariantBody::Body {
                 ref body,
@@ -930,7 +826,7 @@ impl Enum {
                 if config.language != Language::Cython {
                     condition.write_before(config, out);
                 }
-                body.write(config, out);
+                language_backend.write_struct(out, body);
                 if config.language != Language::Cython {
                     condition.write_after(config, out);
                 }
@@ -942,7 +838,7 @@ impl Enum {
     /// For non-inline tag scenario this is *the* tag field, and it does not exist in the variants.
     /// For the inline tag scenario this is just a convenience and another way
     /// to refer to the same tag that exist in all the variants.
-    fn write_tag_field<F: Write>(
+    pub(crate) fn write_tag_field<F: Write>(
         &self,
         config: &Config,
         out: &mut SourceWriter<F>,
@@ -971,11 +867,17 @@ impl Enum {
     }
 
     /// Emit fields for all variants with data.
-    fn write_variant_fields<F: Write>(
+    pub(crate) fn write_variant_fields<
+        F: Write,
+        LB: LanguageBackend,
+        WF: Fn(&mut LB, &mut SourceWriter<F>, &Field),
+    >(
         &self,
         config: &Config,
+        language_backend: &mut LB,
         out: &mut SourceWriter<F>,
         inline_tag_field: bool,
+        write_field: WF,
     ) {
         let mut first = true;
         for variant in &self.variants {
@@ -1006,7 +908,12 @@ impl Enum {
                     }
                     let start_field =
                         usize::from(inline_tag_field && config.language == Language::Cython);
-                    out.write_vertical_source_list(&body.fields[start_field..], ListType::Cap(";"));
+                    out.write_vertical_source_list(
+                        language_backend,
+                        &body.fields[start_field..],
+                        ListType::Cap(";"),
+                        &write_field,
+                    );
                     if config.language != Language::Cython {
                         out.close_brace(true);
                     }
@@ -1023,13 +930,14 @@ impl Enum {
     }
 
     // Emit convenience methods for enums themselves.
-    fn write_derived_functions_enum<F: Write>(
+    fn write_derived_functions_enum<F: Write, LB: LanguageBackend>(
         &self,
         config: &Config,
+        language_backend: &mut LB,
         out: &mut SourceWriter<F>,
-        has_data: bool,
-        tag_name: &str,
     ) {
+        let has_data = self.tag.is_some();
+        let tag_name = self.tag_name();
         if config.language != Language::Cxx {
             return;
         }
@@ -1096,7 +1004,12 @@ impl Enum {
                     )
                 })
                 .collect();
-            out.write_vertical_source_list(&vec[..], ListType::Join(""));
+            out.write_vertical_source_list(
+                language_backend,
+                &vec[..],
+                ListType::Join(""),
+                |_, out, s| write!(out, "{}", s),
+            );
             out.close_brace(false);
             out.new_line();
 
@@ -1157,7 +1070,12 @@ impl Enum {
                         }
                     })
                     .collect();
-                out.write_vertical_source_list(&vec[..], ListType::Join(""));
+                out.write_vertical_source_list(
+                    language_backend,
+                    &vec[..],
+                    ListType::Join(""),
+                    |_, out, s| write!(out, "{}", s),
+                );
                 out.close_brace(false);
                 out.new_line();
 
@@ -1168,11 +1086,17 @@ impl Enum {
     }
 
     // Emit convenience methods for structs or unions produced for enums with data.
-    fn write_derived_functions_data<F: Write>(
+    pub(crate) fn write_derived_functions_data<
+        F: Write,
+        LB: LanguageBackend,
+        WF: Fn(&mut LB, &mut SourceWriter<F>, &Field),
+    >(
         &self,
         config: &Config,
+        language_backend: &mut LB,
         out: &mut SourceWriter<F>,
         tag_name: &str,
+        write_field: WF,
     ) {
         if config.language != Language::Cxx {
             return;
@@ -1224,7 +1148,12 @@ impl Enum {
                             )
                         })
                         .collect();
-                    out.write_vertical_source_list(&vec[..], ListType::Join(","));
+                    out.write_vertical_source_list(
+                        language_backend,
+                        &vec[..],
+                        ListType::Join(","),
+                        &write_field,
+                    );
                 }
 
                 write!(out, ")");
@@ -1248,13 +1177,13 @@ impl Enum {
                                 write!(out, "for (int i = 0; i < {}; i++)", length.as_str());
                                 out.open_brace();
                                 write!(out, "::new (&result.{}.{}[i]) (", variant_name, field.name);
-                                ty.write(config, out);
+                                language_backend.write_type(out, ty);
                                 write!(out, ")({}[i]);", arg_renamer(&field.name));
                                 out.close_brace(false);
                             }
                             ref ty => {
                                 write!(out, "::new (&result.{}.{}) (", variant_name, field.name);
-                                ty.write(config, out);
+                                language_backend.write_type(out, ty);
                                 write!(out, ")({});", arg_renamer(&field.name));
                             }
                         }
@@ -1316,7 +1245,7 @@ impl Enum {
                             is_ref: true,
                             is_nullable: false,
                         };
-                        return_type.write(config, out);
+                        language_backend.write_type(out, &return_type);
                     } else if const_casts {
                         write!(out, "const {}&", body.export_name());
                     } else {
diff --git a/src/bindgen/ir/field.rs b/src/bindgen/ir/field.rs
index 6e132bfaf..73019eee0 100644
--- a/src/bindgen/ir/field.rs
+++ b/src/bindgen/ir/field.rs
@@ -1,12 +1,7 @@
-use std::io::Write;
-
 use syn::ext::IdentExt;
 
-use crate::bindgen::cdecl;
-use crate::bindgen::config::{Config, Language};
-use crate::bindgen::ir::{AnnotationSet, Cfg, ConditionWrite};
-use crate::bindgen::ir::{Documentation, Path, ToCondition, Type};
-use crate::bindgen::writer::{Source, SourceWriter};
+use crate::bindgen::ir::{AnnotationSet, Cfg};
+use crate::bindgen::ir::{Documentation, Path, Type};
 
 #[derive(Debug, Clone)]
 pub struct Field {
@@ -48,33 +43,3 @@ impl Field {
         })
     }
 }
-
-impl Source for Field {
-    fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
-        // Cython doesn't support conditional fields.
-        let condition = self.cfg.to_condition(config);
-        if config.language != Language::Cython {
-            condition.write_before(config, out);
-        }
-
-        self.documentation.write(config, out);
-        cdecl::write_field(out, &self.ty, &self.name, config);
-        // Cython extern declarations don't manage layouts, layouts are defined entierly by the
-        // corresponding C code. So we can omit bitfield sizes which are not supported by Cython.
-        if config.language != Language::Cython {
-            if let Some(bitfield) = self.annotations.atom("bitfield") {
-                write!(out, ": {}", bitfield.unwrap_or_default());
-            }
-        }
-
-        if config.language != Language::Cython {
-            condition.write_after(config, out);
-            // FIXME(#634): `write_vertical_source_list` should support
-            // configuring list elements natively. For now we print a newline
-            // here to avoid printing `#endif;` with semicolon.
-            if condition.is_some() {
-                out.new_line();
-            }
-        }
-    }
-}
diff --git a/src/bindgen/ir/function.rs b/src/bindgen/ir/function.rs
index 8c65f2f9e..579c1cb9f 100644
--- a/src/bindgen/ir/function.rs
+++ b/src/bindgen/ir/function.rs
@@ -3,24 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use std::collections::HashMap;
-use std::io::Write;
 
 use syn::ext::IdentExt;
 
-use crate::bindgen::cdecl;
-use crate::bindgen::config::{Config, Language, Layout};
+use crate::bindgen::config::{Config, Language};
 use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
 use crate::bindgen::dependencies::Dependencies;
-use crate::bindgen::ir::{
-    AnnotationSet, Cfg, ConditionWrite, DeprecatedNoteKind, Documentation, GenericPath, Path,
-    ToCondition, Type,
-};
+use crate::bindgen::ir::{AnnotationSet, Cfg, Documentation, GenericPath, Path, Type};
 use crate::bindgen::library::Library;
 use crate::bindgen::monomorph::Monomorphs;
 use crate::bindgen::rename::{IdentifierType, RenameRule};
 use crate::bindgen::reserved;
 use crate::bindgen::utilities::IterHelpers;
-use crate::bindgen::writer::{Source, SourceWriter};
 
 #[derive(Debug, Clone)]
 pub struct FunctionArgument {
@@ -220,116 +214,6 @@ impl Function {
     }
 }
 
-impl Source for Function {
-    fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
-        fn write_1<W: Write>(func: &Function, config: &Config, out: &mut SourceWriter<W>) {
-            let prefix = config.function.prefix(&func.annotations);
-            let postfix = config.function.postfix(&func.annotations);
-
-            let condition = func.cfg.to_condition(config);
-            condition.write_before(config, out);
-
-            func.documentation.write(config, out);
-
-            if func.extern_decl {
-                out.write("extern ");
-            } else {
-                if let Some(ref prefix) = prefix {
-                    write!(out, "{} ", prefix);
-                }
-                if func.annotations.must_use(config) {
-                    if let Some(ref anno) = config.function.must_use {
-                        write!(out, "{} ", anno);
-                    }
-                }
-                if let Some(note) = func
-                    .annotations
-                    .deprecated_note(config, DeprecatedNoteKind::Function)
-                {
-                    write!(out, "{} ", note);
-                }
-            }
-            cdecl::write_func(out, func, Layout::Horizontal, config);
-
-            if !func.extern_decl {
-                if let Some(ref postfix) = postfix {
-                    write!(out, " {}", postfix);
-                }
-            }
-
-            if let Some(ref swift_name_macro) = config.function.swift_name_macro {
-                if let Some(swift_name) = func.swift_name(config) {
-                    write!(out, " {}({})", swift_name_macro, swift_name);
-                }
-            }
-
-            out.write(";");
-
-            condition.write_after(config, out);
-        }
-
-        fn write_2<W: Write>(func: &Function, config: &Config, out: &mut SourceWriter<W>) {
-            let prefix = config.function.prefix(&func.annotations);
-            let postfix = config.function.postfix(&func.annotations);
-
-            let condition = func.cfg.to_condition(config);
-
-            condition.write_before(config, out);
-
-            func.documentation.write(config, out);
-
-            if func.extern_decl {
-                out.write("extern ");
-            } else {
-                if let Some(ref prefix) = prefix {
-                    write!(out, "{}", prefix);
-                    out.new_line();
-                }
-                if func.annotations.must_use(config) {
-                    if let Some(ref anno) = config.function.must_use {
-                        write!(out, "{}", anno);
-                        out.new_line();
-                    }
-                }
-                if let Some(note) = func
-                    .annotations
-                    .deprecated_note(config, DeprecatedNoteKind::Function)
-                {
-                    write!(out, "{}", note);
-                    out.new_line();
-                }
-            }
-            cdecl::write_func(out, func, Layout::Vertical, config);
-            if !func.extern_decl {
-                if let Some(ref postfix) = postfix {
-                    out.new_line();
-                    write!(out, "{}", postfix);
-                }
-            }
-
-            if let Some(ref swift_name_macro) = config.function.swift_name_macro {
-                if let Some(swift_name) = func.swift_name(config) {
-                    write!(out, " {}({})", swift_name_macro, swift_name);
-                }
-            }
-
-            out.write(";");
-
-            condition.write_after(config, out);
-        }
-
-        match config.function.args {
-            Layout::Horizontal => write_1(self, config, out),
-            Layout::Vertical => write_2(self, config, out),
-            Layout::Auto => {
-                if !out.try_write(|out| write_1(self, config, out), config.line_length) {
-                    write_2(self, config, out)
-                }
-            }
-        }
-    }
-}
-
 trait SynFnArgHelpers {
     fn as_argument(&self) -> Result<Option<FunctionArgument>, String>;
 }
diff --git a/src/bindgen/ir/generic_path.rs b/src/bindgen/ir/generic_path.rs
index ef14890ab..60bd3f944 100644
--- a/src/bindgen/ir/generic_path.rs
+++ b/src/bindgen/ir/generic_path.rs
@@ -7,8 +7,9 @@ use crate::bindgen::cdecl;
 use crate::bindgen::config::{Config, Language};
 use crate::bindgen::declarationtyperesolver::{DeclarationType, DeclarationTypeResolver};
 use crate::bindgen::ir::{ConstExpr, Path, Type};
+use crate::bindgen::language_backend::LanguageBackend;
 use crate::bindgen::utilities::IterHelpers;
-use crate::bindgen::writer::{Source, SourceWriter};
+use crate::bindgen::writer::SourceWriter;
 
 #[derive(Debug, Clone)]
 pub enum GenericParamType {
@@ -94,8 +95,9 @@ impl GenericParams {
             .collect()
     }
 
-    fn write_internal<F: Write>(
+    pub(crate) fn write_internal<F: Write, LB: LanguageBackend>(
         &self,
+        language_backend: &mut LB,
         config: &Config,
         out: &mut SourceWriter<F>,
         with_default: bool,
@@ -114,7 +116,7 @@ impl GenericParams {
                         }
                     }
                     GenericParamType::Const(ref ty) => {
-                        cdecl::write_field(out, ty, item.name.name(), config);
+                        cdecl::write_field(language_backend, out, ty, item.name.name(), config);
                         if with_default {
                             write!(out, " = 0");
                         }
@@ -126,8 +128,13 @@ impl GenericParams {
         }
     }
 
-    pub fn write_with_default<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
-        self.write_internal(config, out, true);
+    pub fn write_with_default<F: Write, LB: LanguageBackend>(
+        &self,
+        language_backend: &mut LB,
+        config: &Config,
+        out: &mut SourceWriter<F>,
+    ) {
+        self.write_internal(language_backend, config, out, true);
     }
 }
 
@@ -139,12 +146,6 @@ impl Deref for GenericParams {
     }
 }
 
-impl Source for GenericParams {
-    fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
-        self.write_internal(config, out, false);
-    }
-}
-
 /// A (non-lifetime) argument passed to a generic, either a type or a constant expression.
 ///
 /// Note: Both arguments in a type like `Array<T, N>` are represented as
@@ -185,15 +186,6 @@ impl GenericArgument {
     }
 }
 
-impl Source for GenericArgument {
-    fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
-        match *self {
-            GenericArgument::Type(ref ty) => ty.write(config, out),
-            GenericArgument::Const(ref expr) => expr.write(config, out),
-        }
-    }
-}
-
 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub struct GenericPath {
     path: Path,
diff --git a/src/bindgen/ir/global.rs b/src/bindgen/ir/global.rs
index d476947d7..0d42bbc4f 100644
--- a/src/bindgen/ir/global.rs
+++ b/src/bindgen/ir/global.rs
@@ -2,15 +2,11 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use std::io::Write;
-
-use crate::bindgen::cdecl;
 use crate::bindgen::config::Config;
 use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
 use crate::bindgen::dependencies::Dependencies;
 use crate::bindgen::ir::{AnnotationSet, Cfg, Documentation, Item, ItemContainer, Path, Type};
 use crate::bindgen::library::Library;
-use crate::bindgen::writer::{Source, SourceWriter};
 
 #[derive(Debug, Clone)]
 pub struct Static {
@@ -107,16 +103,3 @@ impl Item for Static {
         self.ty.add_dependencies(library, out);
     }
 }
-
-impl Source for Static {
-    fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
-        self.documentation.write(config, out);
-        out.write("extern ");
-        if let Type::Ptr { is_const: true, .. } = self.ty {
-        } else if !self.mutable {
-            out.write("const ");
-        }
-        cdecl::write_field(out, &self.ty, &self.export_name, config);
-        out.write(";");
-    }
-}
diff --git a/src/bindgen/ir/opaque.rs b/src/bindgen/ir/opaque.rs
index 4451d4a16..b14a1c3ba 100644
--- a/src/bindgen/ir/opaque.rs
+++ b/src/bindgen/ir/opaque.rs
@@ -2,19 +2,15 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use std::io::Write;
-
-use crate::bindgen::config::{Config, Language};
+use crate::bindgen::config::Config;
 use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
 use crate::bindgen::dependencies::Dependencies;
 use crate::bindgen::ir::{
-    AnnotationSet, Cfg, ConditionWrite, Documentation, GenericArgument, GenericParams, Item,
-    ItemContainer, Path, ToCondition,
+    AnnotationSet, Cfg, Documentation, GenericArgument, GenericParams, Item, ItemContainer, Path,
 };
 use crate::bindgen::library::Library;
 use crate::bindgen::mangle;
 use crate::bindgen::monomorph::Monomorphs;
-use crate::bindgen::writer::{Source, SourceWriter};
 
 #[derive(Debug, Clone)]
 pub struct OpaqueItem {
@@ -136,41 +132,3 @@ impl Item for OpaqueItem {
         out.insert_opaque(self, monomorph, generic_values.to_owned());
     }
 }
-
-impl Source for OpaqueItem {
-    fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
-        let condition = self.cfg.to_condition(config);
-        condition.write_before(config, out);
-
-        self.documentation.write(config, out);
-
-        self.generic_params.write_with_default(config, out);
-
-        match config.language {
-            Language::C if config.style.generate_typedef() => {
-                write!(
-                    out,
-                    "typedef struct {} {};",
-                    self.export_name(),
-                    self.export_name()
-                );
-            }
-            Language::C | Language::Cxx => {
-                write!(out, "struct {};", self.export_name());
-            }
-            Language::Cython => {
-                write!(
-                    out,
-                    "{}struct {}",
-                    config.style.cython_def(),
-                    self.export_name()
-                );
-                out.open_brace();
-                out.write("pass");
-                out.close_brace(false);
-            }
-        }
-
-        condition.write_after(config, out);
-    }
-}
diff --git a/src/bindgen/ir/structure.rs b/src/bindgen/ir/structure.rs
index 2eefa953d..1f1995b73 100644
--- a/src/bindgen/ir/structure.rs
+++ b/src/bindgen/ir/structure.rs
@@ -10,9 +10,8 @@ use crate::bindgen::config::{Config, Language, LayoutConfig};
 use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
 use crate::bindgen::dependencies::Dependencies;
 use crate::bindgen::ir::{
-    AnnotationSet, Cfg, ConditionWrite, Constant, DeprecatedNoteKind, Documentation, Field,
-    GenericArgument, GenericParams, Item, ItemContainer, Path, Repr, ReprAlign, ReprStyle,
-    ToCondition, Type, Typedef,
+    AnnotationSet, Cfg, Constant, Documentation, Field, GenericArgument, GenericParams, Item,
+    ItemContainer, Path, Repr, ReprAlign, ReprStyle, Type,
 };
 use crate::bindgen::library::Library;
 use crate::bindgen::mangle;
@@ -20,7 +19,7 @@ use crate::bindgen::monomorph::Monomorphs;
 use crate::bindgen::rename::{IdentifierType, RenameRule};
 use crate::bindgen::reserved;
 use crate::bindgen::utilities::IterHelpers;
-use crate::bindgen::writer::{ListType, Source, SourceWriter};
+use crate::bindgen::writer::SourceWriter;
 
 #[derive(Debug, Clone)]
 pub struct Struct {
@@ -203,7 +202,7 @@ impl Struct {
         )
     }
 
-    fn emit_bitflags_binop<F: Write>(
+    pub(crate) fn emit_bitflags_binop<F: Write>(
         &self,
         constexpr_prefix: &str,
         operator: char,
@@ -375,343 +374,3 @@ impl Item for Struct {
         out.insert_struct(library, self, monomorph, generic_values.to_owned());
     }
 }
-
-impl Source for Struct {
-    fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
-        if self.is_transparent {
-            let typedef = Typedef {
-                path: self.path.clone(),
-                export_name: self.export_name.to_owned(),
-                generic_params: self.generic_params.clone(),
-                aliased: self.fields[0].ty.clone(),
-                cfg: self.cfg.clone(),
-                annotations: self.annotations.clone(),
-                documentation: self.documentation.clone(),
-            };
-            typedef.write(config, out);
-            for constant in &self.associated_constants {
-                out.new_line();
-                constant.write(config, out, Some(self));
-            }
-            return;
-        }
-
-        let condition = self.cfg.to_condition(config);
-        condition.write_before(config, out);
-
-        self.documentation.write(config, out);
-
-        if !self.is_enum_variant_body {
-            self.generic_params.write(config, out);
-        }
-
-        // The following results in
-        // C++ or C with Tag as style:
-        //   struct Name {
-        // C with Type only style:
-        //   typedef struct {
-        // C with Both as style:
-        //   typedef struct Name {
-        match config.language {
-            Language::C if config.style.generate_typedef() => out.write("typedef "),
-            Language::C | Language::Cxx => {}
-            Language::Cython => out.write(config.style.cython_def()),
-        }
-
-        // Cython extern declarations don't manage layouts, layouts are defined entierly by the
-        // corresponding C code. So this `packed` is only for documentation, and missing
-        // `aligned(n)` is also not a problem.
-        if config.language == Language::Cython {
-            if let Some(align) = self.alignment {
-                match align {
-                    ReprAlign::Packed => out.write("packed "),
-                    ReprAlign::Align(_) => {} // Not supported
-                }
-            }
-        }
-
-        out.write("struct");
-
-        if config.language != Language::Cython {
-            if let Some(align) = self.alignment {
-                match align {
-                    ReprAlign::Packed => {
-                        if let Some(ref anno) = config.layout.packed {
-                            write!(out, " {}", anno);
-                        }
-                    }
-                    ReprAlign::Align(n) => {
-                        if let Some(ref anno) = config.layout.aligned_n {
-                            write!(out, " {}({})", anno, n);
-                        }
-                    }
-                }
-            }
-        }
-
-        if self.annotations.must_use(config) {
-            if let Some(ref anno) = config.structure.must_use {
-                write!(out, " {}", anno);
-            }
-        }
-        if let Some(note) = self
-            .annotations
-            .deprecated_note(config, DeprecatedNoteKind::Struct)
-        {
-            write!(out, " {}", note);
-        }
-
-        if config.language != Language::C || config.style.generate_tag() {
-            write!(out, " {}", self.export_name());
-        }
-
-        out.open_brace();
-
-        // Emit the pre_body section, if relevant
-        if let Some(body) = config.export.pre_body(&self.path) {
-            out.write_raw_block(body);
-            out.new_line();
-        }
-
-        out.write_vertical_source_list(&self.fields, ListType::Cap(";"));
-        if config.language == Language::Cython && self.fields.is_empty() {
-            out.write("pass");
-        }
-
-        if config.language == Language::Cxx {
-            let mut wrote_start_newline = false;
-
-            if config.structure.derive_constructor(&self.annotations) && !self.fields.is_empty() {
-                if !wrote_start_newline {
-                    wrote_start_newline = true;
-                    out.new_line();
-                }
-
-                out.new_line();
-
-                let arg_renamer = |name: &str| {
-                    config
-                        .function
-                        .rename_args
-                        .apply(name, IdentifierType::FunctionArg)
-                        .into_owned()
-                };
-                write!(out, "{}(", self.export_name());
-                let vec: Vec<_> = self
-                    .fields
-                    .iter()
-                    .map(|field| {
-                        Field::from_name_and_type(
-                            // const-ref args to constructor
-                            format!("const& {}", arg_renamer(&field.name)),
-                            field.ty.clone(),
-                        )
-                    })
-                    .collect();
-                out.write_vertical_source_list(&vec[..], ListType::Join(","));
-                write!(out, ")");
-                out.new_line();
-                write!(out, "  : ");
-                let vec: Vec<_> = self
-                    .fields
-                    .iter()
-                    .map(|field| format!("{}({})", field.name, arg_renamer(&field.name)))
-                    .collect();
-                out.write_vertical_source_list(&vec[..], ListType::Join(","));
-                out.new_line();
-                write!(out, "{{}}");
-                out.new_line();
-            }
-
-            let other = config
-                .function
-                .rename_args
-                .apply("other", IdentifierType::FunctionArg);
-
-            if self
-                .annotations
-                .bool("internal-derive-bitflags")
-                .unwrap_or(false)
-            {
-                assert_eq!(self.fields.len(), 1);
-                let bits = &self.fields[0].name;
-                if !wrote_start_newline {
-                    wrote_start_newline = true;
-                    out.new_line();
-                }
-                let constexpr_prefix = if config.constant.allow_constexpr {
-                    "constexpr "
-                } else {
-                    ""
-                };
-
-                out.new_line();
-                write!(out, "{}explicit operator bool() const", constexpr_prefix);
-                out.open_brace();
-                write!(out, "return !!{bits};");
-                out.close_brace(false);
-
-                out.new_line();
-                write!(
-                    out,
-                    "{}{} operator~() const",
-                    constexpr_prefix,
-                    self.export_name()
-                );
-                out.open_brace();
-                write!(
-                    out,
-                    "return {} {{ static_cast<decltype({bits})>(~{bits}) }};",
-                    self.export_name()
-                );
-                out.close_brace(false);
-                self.emit_bitflags_binop(constexpr_prefix, '|', &other, out);
-                self.emit_bitflags_binop(constexpr_prefix, '&', &other, out);
-                self.emit_bitflags_binop(constexpr_prefix, '^', &other, out);
-            }
-
-            // Generate a serializer function that allows dumping this struct
-            // to an std::ostream. It's defined as a friend function inside the
-            // struct definition, and doesn't need the `inline` keyword even
-            // though it's implemented right in the generated header file.
-            if config.structure.derive_ostream(&self.annotations) {
-                if !wrote_start_newline {
-                    wrote_start_newline = true;
-                    out.new_line();
-                }
-
-                out.new_line();
-                let stream = config
-                    .function
-                    .rename_args
-                    .apply("stream", IdentifierType::FunctionArg);
-                let instance = config
-                    .function
-                    .rename_args
-                    .apply("instance", IdentifierType::FunctionArg);
-                write!(
-                    out,
-                    "friend std::ostream& operator<<(std::ostream& {}, const {}& {})",
-                    stream,
-                    self.export_name(),
-                    instance,
-                );
-                out.open_brace();
-                write!(out, "return {} << \"{{ \"", stream);
-                let vec: Vec<_> = self
-                    .fields
-                    .iter()
-                    .map(|x| format!(" << \"{}=\" << {}.{}", x.name, instance, x.name))
-                    .collect();
-                out.write_vertical_source_list(&vec[..], ListType::Join(" << \", \""));
-                out.write(" << \" }\";");
-                out.close_brace(false);
-            }
-
-            let skip_fields = self.has_tag_field as usize;
-
-            macro_rules! emit_op {
-                ($op_name:expr, $op:expr, $conjuc:expr) => {{
-                    if !wrote_start_newline {
-                        #[allow(unused_assignments)]
-                        {
-                            wrote_start_newline = true;
-                        }
-                        out.new_line();
-                    }
-
-                    out.new_line();
-
-                    if let Some(Some(attrs)) =
-                        self.annotations.atom(concat!($op_name, "-attributes"))
-                    {
-                        write!(out, "{} ", attrs);
-                    }
-
-                    write!(
-                        out,
-                        "bool operator{}(const {}& {}) const",
-                        $op,
-                        self.export_name(),
-                        other
-                    );
-                    out.open_brace();
-                    out.write("return ");
-                    let vec: Vec<_> = self
-                        .fields
-                        .iter()
-                        .skip(skip_fields)
-                        .map(|field| format!("{} {} {}.{}", field.name, $op, other, field.name))
-                        .collect();
-                    out.write_vertical_source_list(
-                        &vec[..],
-                        ListType::Join(&format!(" {}", $conjuc)),
-                    );
-                    out.write(";");
-                    out.close_brace(false);
-                }};
-            }
-
-            if config.structure.derive_eq(&self.annotations) && self.can_derive_eq() {
-                emit_op!("eq", "==", "&&");
-            }
-            if config.structure.derive_neq(&self.annotations) && self.can_derive_eq() {
-                emit_op!("neq", "!=", "||");
-            }
-            if config.structure.derive_lt(&self.annotations)
-                && self.fields.len() == 1
-                && self.fields[0].ty.can_cmp_order()
-            {
-                emit_op!("lt", "<", "&&");
-            }
-            if config.structure.derive_lte(&self.annotations)
-                && self.fields.len() == 1
-                && self.fields[0].ty.can_cmp_order()
-            {
-                emit_op!("lte", "<=", "&&");
-            }
-            if config.structure.derive_gt(&self.annotations)
-                && self.fields.len() == 1
-                && self.fields[0].ty.can_cmp_order()
-            {
-                emit_op!("gt", ">", "&&");
-            }
-            if config.structure.derive_gte(&self.annotations)
-                && self.fields.len() == 1
-                && self.fields[0].ty.can_cmp_order()
-            {
-                emit_op!("gte", ">=", "&&");
-            }
-        }
-
-        // Emit the post_body section, if relevant
-        if let Some(body) = config.export.post_body(&self.path) {
-            out.new_line();
-            out.write_raw_block(body);
-        }
-
-        if config.language == Language::Cxx
-            && config.structure.associated_constants_in_body
-            && config.constant.allow_static_const
-        {
-            for constant in &self.associated_constants {
-                out.new_line();
-                constant.write_declaration(config, out, self);
-            }
-        }
-
-        if config.language == Language::C && config.style.generate_typedef() {
-            out.close_brace(false);
-            write!(out, " {};", self.export_name());
-        } else {
-            out.close_brace(true);
-        }
-
-        for constant in &self.associated_constants {
-            out.new_line();
-            constant.write(config, out, Some(self));
-        }
-
-        condition.write_after(config, out);
-    }
-}
diff --git a/src/bindgen/ir/ty.rs b/src/bindgen/ir/ty.rs
index 5a31fb646..a3d846674 100644
--- a/src/bindgen/ir/ty.rs
+++ b/src/bindgen/ir/ty.rs
@@ -3,11 +3,9 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use std::borrow::Cow;
-use std::io::Write;
 
 use syn::ext::IdentExt;
 
-use crate::bindgen::cdecl;
 use crate::bindgen::config::{Config, Language};
 use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
 use crate::bindgen::dependencies::Dependencies;
@@ -15,7 +13,6 @@ use crate::bindgen::ir::{GenericArgument, GenericParams, GenericPath, Path};
 use crate::bindgen::library::Library;
 use crate::bindgen::monomorph::Monomorphs;
 use crate::bindgen::utilities::IterHelpers;
-use crate::bindgen::writer::{Source, SourceWriter};
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub enum PrimitiveType {
@@ -380,12 +377,6 @@ impl ConstExpr {
     }
 }
 
-impl Source for ConstExpr {
-    fn write<F: Write>(&self, _config: &Config, out: &mut SourceWriter<F>) {
-        write!(out, "{}", self.as_str());
-    }
-}
-
 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub enum Type {
     Ptr {
@@ -1003,15 +994,3 @@ impl Type {
         }
     }
 }
-
-impl Source for String {
-    fn write<F: Write>(&self, _config: &Config, out: &mut SourceWriter<F>) {
-        write!(out, "{}", self);
-    }
-}
-
-impl Source for Type {
-    fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
-        cdecl::write_type(out, self, config);
-    }
-}
diff --git a/src/bindgen/ir/typedef.rs b/src/bindgen/ir/typedef.rs
index 626732e2a..dbfdc5ba8 100644
--- a/src/bindgen/ir/typedef.rs
+++ b/src/bindgen/ir/typedef.rs
@@ -3,21 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use std::collections::HashMap;
-use std::io::Write;
 
 use syn::ext::IdentExt;
 
-use crate::bindgen::config::{Config, Language};
+use crate::bindgen::config::Config;
 use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
 use crate::bindgen::dependencies::Dependencies;
 use crate::bindgen::ir::{
-    AnnotationSet, Cfg, ConditionWrite, Documentation, Field, GenericArgument, GenericParams, Item,
-    ItemContainer, Path, ToCondition, Type,
+    AnnotationSet, Cfg, Documentation, GenericArgument, GenericParams, Item, ItemContainer, Path,
+    Type,
 };
 use crate::bindgen::library::Library;
 use crate::bindgen::mangle;
 use crate::bindgen::monomorph::Monomorphs;
-use crate::bindgen::writer::{Source, SourceWriter};
 
 /// A type alias that is represented as a C typedef
 #[derive(Debug, Clone)]
@@ -135,11 +133,6 @@ impl Item for Typedef {
         ItemContainer::Typedef(self.clone())
     }
 
-    fn rename_for_config(&mut self, config: &Config) {
-        config.export.rename(&mut self.export_name);
-        self.aliased.rename_for_config(config, &self.generic_params);
-    }
-
     fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
         resolver.add_none(&self.path);
     }
@@ -148,6 +141,11 @@ impl Item for Typedef {
         self.aliased.resolve_declaration_types(resolver);
     }
 
+    fn rename_for_config(&mut self, config: &Config) {
+        config.export.rename(&mut self.export_name);
+        self.aliased.rename_for_config(config, &self.generic_params);
+    }
+
     fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
         self.aliased
             .add_dependencies_ignoring_generics(&self.generic_params, library, out);
@@ -179,30 +177,3 @@ impl Item for Typedef {
         out.insert_typedef(library, self, monomorph, generic_values.to_owned());
     }
 }
-
-impl Source for Typedef {
-    fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
-        let condition = self.cfg.to_condition(config);
-        condition.write_before(config, out);
-
-        self.documentation.write(config, out);
-
-        self.generic_params.write(config, out);
-
-        match config.language {
-            Language::Cxx => {
-                write!(out, "using {} = ", self.export_name());
-                self.aliased.write(config, out);
-            }
-            Language::C | Language::Cython => {
-                write!(out, "{} ", config.language.typedef());
-                Field::from_name_and_type(self.export_name().to_owned(), self.aliased.clone())
-                    .write(config, out);
-            }
-        }
-
-        out.write(";");
-
-        condition.write_after(config, out);
-    }
-}
diff --git a/src/bindgen/ir/union.rs b/src/bindgen/ir/union.rs
index 07bd16c58..32db72648 100644
--- a/src/bindgen/ir/union.rs
+++ b/src/bindgen/ir/union.rs
@@ -2,23 +2,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use std::io::Write;
-
 use syn::ext::IdentExt;
 
-use crate::bindgen::config::{Config, Language, LayoutConfig};
+use crate::bindgen::config::{Config, LayoutConfig};
 use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
 use crate::bindgen::dependencies::Dependencies;
 use crate::bindgen::ir::{
-    AnnotationSet, Cfg, ConditionWrite, Documentation, Field, GenericArgument, GenericParams, Item,
-    ItemContainer, Path, Repr, ReprAlign, ReprStyle, ToCondition,
+    AnnotationSet, Cfg, Documentation, Field, GenericArgument, GenericParams, Item, ItemContainer,
+    Path, Repr, ReprAlign, ReprStyle,
 };
 use crate::bindgen::library::Library;
 use crate::bindgen::mangle;
 use crate::bindgen::monomorph::Monomorphs;
 use crate::bindgen::rename::{IdentifierType, RenameRule};
 use crate::bindgen::utilities::IterHelpers;
-use crate::bindgen::writer::{ListType, Source, SourceWriter};
 
 #[derive(Debug, Clone)]
 pub struct Union {
@@ -258,79 +255,3 @@ impl Item for Union {
         out.insert_union(library, self, monomorph, generic_values.to_owned());
     }
 }
-
-impl Source for Union {
-    fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
-        let condition = self.cfg.to_condition(config);
-        condition.write_before(config, out);
-
-        self.documentation.write(config, out);
-
-        self.generic_params.write(config, out);
-
-        // The following results in
-        // C++ or C with Tag as style:
-        //   union Name {
-        // C with Type only style:
-        //   typedef union {
-        // C with Both as style:
-        //   typedef union Name {
-        match config.language {
-            Language::C if config.style.generate_typedef() => out.write("typedef "),
-            Language::C | Language::Cxx => {}
-            Language::Cython => out.write(config.style.cython_def()),
-        }
-
-        out.write("union");
-
-        // Cython supports `packed` on structs (see comments there), but not on unions.
-        if config.language != Language::Cython {
-            if let Some(align) = self.alignment {
-                match align {
-                    ReprAlign::Packed => {
-                        if let Some(ref anno) = config.layout.packed {
-                            write!(out, " {}", anno);
-                        }
-                    }
-                    ReprAlign::Align(n) => {
-                        if let Some(ref anno) = config.layout.aligned_n {
-                            write!(out, " {}({})", anno, n);
-                        }
-                    }
-                }
-            }
-        }
-
-        if config.language != Language::C || config.style.generate_tag() {
-            write!(out, " {}", self.export_name);
-        }
-
-        out.open_brace();
-
-        // Emit the pre_body section, if relevant
-        if let Some(body) = config.export.pre_body(&self.path) {
-            out.write_raw_block(body);
-            out.new_line();
-        }
-
-        out.write_vertical_source_list(&self.fields, ListType::Cap(";"));
-        if config.language == Language::Cython && self.fields.is_empty() {
-            out.write("pass");
-        }
-
-        // Emit the post_body section, if relevant
-        if let Some(body) = config.export.post_body(&self.path) {
-            out.new_line();
-            out.write_raw_block(body);
-        }
-
-        if config.language == Language::C && config.style.generate_typedef() {
-            out.close_brace(false);
-            write!(out, " {};", self.export_name);
-        } else {
-            out.close_brace(true);
-        }
-
-        condition.write_after(config, out);
-    }
-}
diff --git a/src/bindgen/language_backend/clike.rs b/src/bindgen/language_backend/clike.rs
new file mode 100644
index 000000000..c33d6079c
--- /dev/null
+++ b/src/bindgen/language_backend/clike.rs
@@ -0,0 +1,997 @@
+use crate::bindgen::ir::{
+    to_known_assoc_constant, ConditionWrite, DeprecatedNoteKind, Documentation, Enum, EnumVariant,
+    Field, GenericParams, Item, Literal, OpaqueItem, ReprAlign, Static, Struct, ToCondition, Type,
+    Typedef, Union,
+};
+use crate::bindgen::language_backend::LanguageBackend;
+use crate::bindgen::rename::IdentifierType;
+use crate::bindgen::writer::{ListType, SourceWriter};
+use crate::bindgen::{cdecl, Bindings, Config, Language};
+use crate::bindgen::{DocumentationLength, DocumentationStyle};
+use std::io::Write;
+
+pub struct CLikeLanguageBackend<'a> {
+    config: &'a Config,
+}
+
+impl<'a> CLikeLanguageBackend<'a> {
+    pub fn new(config: &'a Config) -> Self {
+        Self { config }
+    }
+
+    fn write_enum_variant<W: Write>(&mut self, out: &mut SourceWriter<W>, u: &EnumVariant) {
+        let condition = u.cfg.to_condition(self.config);
+
+        condition.write_before(self.config, out);
+
+        self.write_documentation(out, &u.documentation);
+        write!(out, "{}", u.export_name);
+        if let Some(note) = u
+            .body
+            .annotations()
+            .deprecated_note(self.config, DeprecatedNoteKind::EnumVariant)
+        {
+            write!(out, " {}", note);
+        }
+        if let Some(discriminant) = &u.discriminant {
+            out.write(" = ");
+
+            self.write_literal(out, discriminant);
+        }
+        out.write(",");
+        condition.write_after(self.config, out);
+    }
+
+    fn write_field<W: Write>(&mut self, out: &mut SourceWriter<W>, f: &Field) {
+        let condition = f.cfg.to_condition(self.config);
+        condition.write_before(self.config, out);
+
+        self.write_documentation(out, &f.documentation);
+        cdecl::write_field(self, out, &f.ty, &f.name, self.config);
+
+        if let Some(bitfield) = f.annotations.atom("bitfield") {
+            write!(out, ": {}", bitfield.unwrap_or_default());
+        }
+
+        condition.write_after(self.config, out);
+        // FIXME(#634): `write_vertical_source_list` should support
+        // configuring list elements natively. For now we print a newline
+        // here to avoid printing `#endif;` with semicolon.
+        if condition.is_some() {
+            out.new_line();
+        }
+    }
+
+    fn write_generic_param<W: Write>(&mut self, out: &mut SourceWriter<W>, g: &GenericParams) {
+        g.write_internal(self, self.config, out, false);
+    }
+
+    fn open_close_namespaces<W: Write>(&mut self, out: &mut SourceWriter<W>, open: bool) {
+        let mut namespaces =
+            if self.config.language != Language::Cxx && !self.config.cpp_compatible_c() {
+                vec![]
+            } else {
+                let mut ret = vec![];
+                if let Some(ref namespace) = self.config.namespace {
+                    ret.push(&**namespace);
+                }
+                if let Some(ref namespaces) = self.config.namespaces {
+                    for namespace in namespaces {
+                        ret.push(&**namespace);
+                    }
+                }
+                ret
+            };
+
+        if namespaces.is_empty() {
+            return;
+        }
+
+        if !open {
+            namespaces.reverse();
+        }
+
+        if self.config.cpp_compatible_c() {
+            out.new_line_if_not_start();
+            out.write("#ifdef __cplusplus");
+        }
+
+        for namespace in namespaces {
+            out.new_line();
+            if open {
+                write!(out, "namespace {} {{", namespace)
+            } else {
+                write!(out, "}} // namespace {}", namespace)
+            }
+        }
+
+        out.new_line();
+        if self.config.cpp_compatible_c() {
+            out.write("#endif // __cplusplus");
+            out.new_line();
+        }
+    }
+
+    fn generate_typedef(&self) -> bool {
+        self.config.language == Language::C && self.config.style.generate_typedef()
+    }
+
+    fn write_derived_cpp_ops<W: Write>(&mut self, out: &mut SourceWriter<W>, s: &Struct) {
+        let mut wrote_start_newline = false;
+
+        if self.config.structure.derive_constructor(&s.annotations) && !s.fields.is_empty() {
+            if !wrote_start_newline {
+                wrote_start_newline = true;
+                out.new_line();
+            }
+
+            out.new_line();
+
+            let renamed_fields: Vec<_> = s
+                .fields
+                .iter()
+                .map(|field| {
+                    self.config
+                        .function
+                        .rename_args
+                        .apply(&field.name, IdentifierType::FunctionArg)
+                        .into_owned()
+                })
+                .collect();
+            write!(out, "{}(", s.export_name());
+            let vec: Vec<_> = s
+                .fields
+                .iter()
+                .zip(&renamed_fields)
+                .map(|(field, renamed)| {
+                    Field::from_name_and_type(
+                        // const-ref args to constructor
+                        format!("const& {}", renamed),
+                        field.ty.clone(),
+                    )
+                })
+                .collect();
+            out.write_vertical_source_list(self, &vec[..], ListType::Join(","), Self::write_field);
+            write!(out, ")");
+            out.new_line();
+            write!(out, "  : ");
+            let vec: Vec<_> = s
+                .fields
+                .iter()
+                .zip(&renamed_fields)
+                .map(|(field, renamed)| format!("{}({})", field.name, renamed))
+                .collect();
+            out.write_vertical_source_list(self, &vec[..], ListType::Join(","), |_, out, s| {
+                write!(out, "{}", s)
+            });
+            out.new_line();
+            write!(out, "{{}}");
+            out.new_line();
+        }
+
+        let other = self
+            .config
+            .function
+            .rename_args
+            .apply("other", IdentifierType::FunctionArg);
+
+        if s.annotations
+            .bool("internal-derive-bitflags")
+            .unwrap_or(false)
+        {
+            assert_eq!(s.fields.len(), 1);
+            let bits = &s.fields[0].name;
+            if !wrote_start_newline {
+                wrote_start_newline = true;
+                out.new_line();
+            }
+            let constexpr_prefix = if self.config.constant.allow_constexpr {
+                "constexpr "
+            } else {
+                ""
+            };
+
+            out.new_line();
+            write!(out, "{}explicit operator bool() const", constexpr_prefix);
+            out.open_brace();
+            write!(out, "return !!{bits};");
+            out.close_brace(false);
+
+            out.new_line();
+            write!(
+                out,
+                "{}{} operator~() const",
+                constexpr_prefix,
+                s.export_name()
+            );
+            out.open_brace();
+            write!(
+                out,
+                "return {} {{ static_cast<decltype({bits})>(~{bits}) }};",
+                s.export_name()
+            );
+            out.close_brace(false);
+            s.emit_bitflags_binop(constexpr_prefix, '|', &other, out);
+            s.emit_bitflags_binop(constexpr_prefix, '&', &other, out);
+            s.emit_bitflags_binop(constexpr_prefix, '^', &other, out);
+        }
+
+        // Generate a serializer function that allows dumping this struct
+        // to an std::ostream. It's defined as a friend function inside the
+        // struct definition, and doesn't need the `inline` keyword even
+        // though it's implemented right in the generated header file.
+        if self.config.structure.derive_ostream(&s.annotations) {
+            if !wrote_start_newline {
+                wrote_start_newline = true;
+                out.new_line();
+            }
+
+            out.new_line();
+            let stream = self
+                .config
+                .function
+                .rename_args
+                .apply("stream", IdentifierType::FunctionArg);
+            let instance = self
+                .config
+                .function
+                .rename_args
+                .apply("instance", IdentifierType::FunctionArg);
+            write!(
+                out,
+                "friend std::ostream& operator<<(std::ostream& {}, const {}& {})",
+                stream,
+                s.export_name(),
+                instance,
+            );
+            out.open_brace();
+            write!(out, "return {} << \"{{ \"", stream);
+            let vec: Vec<_> = s
+                .fields
+                .iter()
+                .map(|x| format!(" << \"{}=\" << {}.{}", x.name, instance, x.name))
+                .collect();
+            out.write_vertical_source_list(
+                self,
+                &vec[..],
+                ListType::Join(" << \", \""),
+                |_, out, s| write!(out, "{}", s),
+            );
+            out.write(" << \" }\";");
+            out.close_brace(false);
+        }
+
+        let skip_fields = s.has_tag_field as usize;
+
+        macro_rules! emit_op {
+            ($op_name:expr, $op:expr, $conjuc:expr) => {{
+                if !wrote_start_newline {
+                    #[allow(unused_assignments)]
+                    {
+                        wrote_start_newline = true;
+                    }
+                    out.new_line();
+                }
+
+                out.new_line();
+
+                if let Some(Some(attrs)) = s.annotations.atom(concat!($op_name, "-attributes")) {
+                    write!(out, "{} ", attrs);
+                }
+
+                write!(
+                    out,
+                    "bool operator{}(const {}& {}) const",
+                    $op,
+                    s.export_name(),
+                    other
+                );
+                out.open_brace();
+                out.write("return ");
+                let vec: Vec<_> = s
+                    .fields
+                    .iter()
+                    .skip(skip_fields)
+                    .map(|field| format!("{} {} {}.{}", field.name, $op, other, field.name))
+                    .collect();
+                out.write_vertical_source_list(
+                    self,
+                    &vec[..],
+                    ListType::Join(&format!(" {}", $conjuc)),
+                    |_, out, s| write!(out, "{}", s),
+                );
+                out.write(";");
+                out.close_brace(false);
+            }};
+        }
+
+        if self.config.structure.derive_eq(&s.annotations) && s.can_derive_eq() {
+            emit_op!("eq", "==", "&&");
+        }
+        if self.config.structure.derive_neq(&s.annotations) && s.can_derive_eq() {
+            emit_op!("neq", "!=", "||");
+        }
+        if self.config.structure.derive_lt(&s.annotations)
+            && s.fields.len() == 1
+            && s.fields[0].ty.can_cmp_order()
+        {
+            emit_op!("lt", "<", "&&");
+        }
+        if self.config.structure.derive_lte(&s.annotations)
+            && s.fields.len() == 1
+            && s.fields[0].ty.can_cmp_order()
+        {
+            emit_op!("lte", "<=", "&&");
+        }
+        if self.config.structure.derive_gt(&s.annotations)
+            && s.fields.len() == 1
+            && s.fields[0].ty.can_cmp_order()
+        {
+            emit_op!("gt", ">", "&&");
+        }
+        if self.config.structure.derive_gte(&s.annotations)
+            && s.fields.len() == 1
+            && s.fields[0].ty.can_cmp_order()
+        {
+            emit_op!("gte", ">=", "&&");
+        }
+    }
+}
+
+impl LanguageBackend for CLikeLanguageBackend<'_> {
+    fn write_headers<W: Write>(&self, out: &mut SourceWriter<W>, package_version: &str) {
+        if self.config.package_version {
+            write!(out, "/* Package version: {} */", package_version);
+            out.new_line();
+        }
+        if let Some(ref f) = self.config.header {
+            out.new_line_if_not_start();
+            write!(out, "{}", f);
+            out.new_line();
+        }
+        if let Some(f) = self.config.include_guard() {
+            out.new_line_if_not_start();
+            write!(out, "#ifndef {}", f);
+            out.new_line();
+            write!(out, "#define {}", f);
+            out.new_line();
+        }
+        if self.config.pragma_once {
+            out.new_line_if_not_start();
+            write!(out, "#pragma once");
+            out.new_line();
+        }
+        if self.config.include_version {
+            out.new_line_if_not_start();
+            write!(
+                out,
+                "/* Generated with cbindgen:{} */",
+                crate::bindgen::config::VERSION
+            );
+            out.new_line();
+        }
+        if let Some(ref f) = self.config.autogen_warning {
+            out.new_line_if_not_start();
+            write!(out, "{}", f);
+            out.new_line();
+        }
+
+        if self.config.no_includes
+            && self.config.sys_includes().is_empty()
+            && self.config.includes().is_empty()
+            && self.config.after_includes.is_none()
+        {
+            return;
+        }
+
+        out.new_line_if_not_start();
+
+        if !self.config.no_includes {
+            match self.config.language {
+                Language::C => {
+                    out.write("#include <stdarg.h>");
+                    out.new_line();
+                    out.write("#include <stdbool.h>");
+                    out.new_line();
+                    if self.config.usize_is_size_t {
+                        out.write("#include <stddef.h>");
+                        out.new_line();
+                    }
+                    out.write("#include <stdint.h>");
+                    out.new_line();
+                    out.write("#include <stdlib.h>");
+                    out.new_line();
+                }
+                Language::Cxx => {
+                    out.write("#include <cstdarg>");
+                    out.new_line();
+                    if self.config.usize_is_size_t {
+                        out.write("#include <cstddef>");
+                        out.new_line();
+                    }
+                    out.write("#include <cstdint>");
+                    out.new_line();
+                    out.write("#include <cstdlib>");
+                    out.new_line();
+                    out.write("#include <ostream>");
+                    out.new_line();
+                    out.write("#include <new>");
+                    out.new_line();
+                    if self.config.enumeration.cast_assert_name.is_none()
+                        && (self.config.enumeration.derive_mut_casts
+                            || self.config.enumeration.derive_const_casts)
+                    {
+                        out.write("#include <cassert>");
+                        out.new_line();
+                    }
+                }
+                _ => {}
+            }
+        }
+
+        for include in self.config.sys_includes() {
+            write!(out, "#include <{}>", include);
+            out.new_line();
+        }
+
+        for include in self.config.includes() {
+            write!(out, "#include \"{}\"", include);
+            out.new_line();
+        }
+
+        if let Some(ref line) = self.config.after_includes {
+            write!(out, "{}", line);
+            out.new_line();
+        }
+    }
+
+    fn open_namespaces<W: Write>(&mut self, out: &mut SourceWriter<W>) {
+        self.open_close_namespaces(out, true);
+    }
+
+    fn close_namespaces<W: Write>(&mut self, out: &mut SourceWriter<W>) {
+        self.open_close_namespaces(out, false)
+    }
+
+    fn write_footers<W: Write>(&mut self, out: &mut SourceWriter<W>) {
+        if let Some(f) = self.config.include_guard() {
+            out.new_line_if_not_start();
+            if self.config.language == Language::C {
+                write!(out, "#endif /* {} */", f);
+            } else {
+                write!(out, "#endif // {}", f);
+            }
+            out.new_line();
+        }
+    }
+
+    fn write_enum<W: Write>(&mut self, out: &mut SourceWriter<W>, e: &Enum) {
+        let size = e.repr.ty.map(|ty| ty.to_primitive().to_repr_c(self.config));
+        let has_data = e.tag.is_some();
+        let inline_tag_field = Enum::inline_tag_field(&e.repr);
+        let tag_name = e.tag_name();
+
+        let condition = e.cfg.to_condition(self.config);
+        condition.write_before(self.config, out);
+
+        self.write_documentation(out, &e.documentation);
+        self.write_generic_param(out, &e.generic_params);
+
+        // If the enum has data, we need to emit a struct or union for the data
+        // and enum for the tag. C++ supports nested type definitions, so we open
+        // the struct or union here and define the tag enum inside it (*).
+        if has_data && self.config.language == Language::Cxx {
+            e.open_struct_or_union(self.config, out, inline_tag_field);
+        }
+
+        // Emit the tag enum and everything related to it.
+        e.write_tag_enum(self.config, self, out, size, Self::write_enum_variant);
+
+        // If the enum has data, we need to emit structs for the variants and gather them together.
+        if has_data {
+            e.write_variant_defs(self.config, self, out);
+            out.new_line();
+            out.new_line();
+
+            // Open the struct or union for the data (**), gathering all the variants with data
+            // together, unless it's C++, then we have already opened that struct/union at (*) and
+            // are currently inside it.
+            if self.config.language != Language::Cxx {
+                e.open_struct_or_union(self.config, out, inline_tag_field);
+            }
+
+            // Emit tag field that is separate from all variants.
+            e.write_tag_field(self.config, out, size, inline_tag_field, tag_name);
+            out.new_line();
+
+            // Open union of all variants with data, only in the non-inline tag scenario.
+            if !inline_tag_field {
+                out.write("union");
+                out.open_brace();
+            }
+
+            // Emit fields for all variants with data.
+            e.write_variant_fields(self.config, self, out, inline_tag_field, Self::write_field);
+
+            // Close union of all variants with data, only in the non-inline tag scenario.
+            if !inline_tag_field {
+                out.close_brace(true);
+            }
+
+            // Emit convenience methods for the struct or enum for the data.
+            e.write_derived_functions_data(self.config, self, out, tag_name, Self::write_field);
+
+            // Emit the post_body section, if relevant.
+            if let Some(body) = self.config.export.post_body(&e.path) {
+                out.new_line();
+                out.write_raw_block(body);
+            }
+
+            // Close the struct or union opened either at (*) or at (**).
+            if self.generate_typedef() {
+                out.close_brace(false);
+                write!(out, " {};", e.export_name);
+            } else {
+                out.close_brace(true);
+            }
+        }
+
+        condition.write_after(self.config, out);
+    }
+
+    fn write_struct<W: Write>(&mut self, out: &mut SourceWriter<W>, s: &Struct) {
+        if s.is_transparent {
+            let typedef = Typedef {
+                path: s.path.clone(),
+                export_name: s.export_name.to_owned(),
+                generic_params: s.generic_params.clone(),
+                aliased: s.fields[0].ty.clone(),
+                cfg: s.cfg.clone(),
+                annotations: s.annotations.clone(),
+                documentation: s.documentation.clone(),
+            };
+            self.write_type_def(out, &typedef);
+            for constant in &s.associated_constants {
+                out.new_line();
+                constant.write(self.config, self, out, Some(s));
+            }
+            return;
+        }
+
+        let condition = s.cfg.to_condition(self.config);
+        condition.write_before(self.config, out);
+
+        self.write_documentation(out, &s.documentation);
+
+        if !s.is_enum_variant_body {
+            self.write_generic_param(out, &s.generic_params);
+        }
+
+        // The following results in
+        // C++ or C with Tag as style:
+        //   struct Name {
+        // C with Type only style:
+        //   typedef struct {
+        // C with Both as style:
+        //   typedef struct Name {
+        if self.generate_typedef() {
+            out.write("typedef ");
+        }
+
+        out.write("struct");
+
+        if let Some(align) = s.alignment {
+            match align {
+                ReprAlign::Packed => {
+                    if let Some(ref anno) = self.config.layout.packed {
+                        write!(out, " {}", anno);
+                    }
+                }
+                ReprAlign::Align(n) => {
+                    if let Some(ref anno) = self.config.layout.aligned_n {
+                        write!(out, " {}({})", anno, n);
+                    }
+                }
+            }
+        }
+
+        if s.annotations.must_use(self.config) {
+            if let Some(ref anno) = self.config.structure.must_use {
+                write!(out, " {}", anno);
+            }
+        }
+
+        if let Some(note) = s
+            .annotations
+            .deprecated_note(self.config, DeprecatedNoteKind::Struct)
+        {
+            write!(out, " {}", note);
+        }
+
+        if self.config.language != Language::C || self.config.style.generate_tag() {
+            write!(out, " {}", s.export_name());
+        }
+
+        out.open_brace();
+
+        // Emit the pre_body section, if relevant
+        if let Some(body) = self.config.export.pre_body(&s.path) {
+            out.write_raw_block(body);
+            out.new_line();
+        }
+
+        out.write_vertical_source_list(self, &s.fields, ListType::Cap(";"), Self::write_field);
+
+        if self.config.language == Language::Cxx {
+            self.write_derived_cpp_ops(out, s);
+        }
+
+        // Emit the post_body section, if relevant
+        if let Some(body) = self.config.export.post_body(&s.path) {
+            out.new_line();
+            out.write_raw_block(body);
+        }
+
+        if self.config.language == Language::Cxx
+            && self.config.structure.associated_constants_in_body
+            && self.config.constant.allow_static_const
+        {
+            for constant in &s.associated_constants {
+                out.new_line();
+                constant.write_declaration(self.config, self, out, s);
+            }
+        }
+
+        if self.generate_typedef() {
+            out.close_brace(false);
+            write!(out, " {};", s.export_name());
+        } else {
+            out.close_brace(true);
+        }
+
+        for constant in &s.associated_constants {
+            out.new_line();
+            constant.write(self.config, self, out, Some(s));
+        }
+
+        condition.write_after(self.config, out);
+    }
+
+    fn write_union<W: Write>(&mut self, out: &mut SourceWriter<W>, u: &Union) {
+        let condition = u.cfg.to_condition(self.config);
+        condition.write_before(self.config, out);
+
+        self.write_documentation(out, &u.documentation);
+
+        self.write_generic_param(out, &u.generic_params);
+
+        // The following results in
+        // C++ or C with Tag as style:
+        //   union Name {
+        // C with Type only style:
+        //   typedef union {
+        // C with Both as style:
+        //   typedef union Name {
+        if self.generate_typedef() {
+            out.write("typedef ");
+        }
+
+        out.write("union");
+
+        if let Some(align) = u.alignment {
+            match align {
+                ReprAlign::Packed => {
+                    if let Some(ref anno) = self.config.layout.packed {
+                        write!(out, " {}", anno);
+                    }
+                }
+                ReprAlign::Align(n) => {
+                    if let Some(ref anno) = self.config.layout.aligned_n {
+                        write!(out, " {}({})", anno, n);
+                    }
+                }
+            }
+        }
+
+        if self.config.language != Language::C || self.config.style.generate_tag() {
+            write!(out, " {}", u.export_name);
+        }
+
+        out.open_brace();
+
+        // Emit the pre_body section, if relevant
+        if let Some(body) = self.config.export.pre_body(&u.path) {
+            out.write_raw_block(body);
+            out.new_line();
+        }
+
+        out.write_vertical_source_list(self, &u.fields, ListType::Cap(";"), Self::write_field);
+
+        // Emit the post_body section, if relevant
+        if let Some(body) = self.config.export.post_body(&u.path) {
+            out.new_line();
+            out.write_raw_block(body);
+        }
+
+        if self.generate_typedef() {
+            out.close_brace(false);
+            write!(out, " {};", u.export_name);
+        } else {
+            out.close_brace(true);
+        }
+
+        condition.write_after(self.config, out);
+    }
+
+    fn write_opaque_item<W: Write>(&mut self, out: &mut SourceWriter<W>, o: &OpaqueItem) {
+        let condition = o.cfg.to_condition(self.config);
+        condition.write_before(self.config, out);
+
+        self.write_documentation(out, &o.documentation);
+
+        o.generic_params.write_with_default(self, self.config, out);
+
+        if self.generate_typedef() {
+            write!(
+                out,
+                "typedef struct {} {};",
+                o.export_name(),
+                o.export_name()
+            );
+        } else {
+            write!(out, "struct {};", o.export_name());
+        }
+
+        condition.write_after(self.config, out);
+    }
+
+    fn write_type_def<W: Write>(&mut self, out: &mut SourceWriter<W>, t: &Typedef) {
+        let condition = t.cfg.to_condition(self.config);
+        condition.write_before(self.config, out);
+
+        self.write_documentation(out, &t.documentation);
+
+        self.write_generic_param(out, &t.generic_params);
+
+        if self.config.language == Language::Cxx {
+            write!(out, "using {} = ", t.export_name());
+            self.write_type(out, &t.aliased);
+        } else {
+            write!(out, "{} ", self.config.language.typedef());
+            self.write_field(
+                out,
+                &Field::from_name_and_type(t.export_name().to_owned(), t.aliased.clone()),
+            );
+        }
+
+        out.write(";");
+
+        condition.write_after(self.config, out);
+    }
+
+    fn write_static<W: Write>(&mut self, out: &mut SourceWriter<W>, s: &Static) {
+        self.write_documentation(out, &s.documentation);
+        out.write("extern ");
+        if let Type::Ptr { is_const: true, .. } = s.ty {
+        } else if !s.mutable {
+            out.write("const ");
+        }
+        cdecl::write_field(self, out, &s.ty, &s.export_name, self.config);
+        out.write(";");
+    }
+
+    fn write_type<W: Write>(&mut self, out: &mut SourceWriter<W>, t: &Type) {
+        cdecl::write_type(self, out, t, self.config);
+    }
+
+    fn write_documentation<W: Write>(&mut self, out: &mut SourceWriter<W>, d: &Documentation) {
+        if d.doc_comment.is_empty() || !self.config.documentation {
+            return;
+        }
+
+        let end = match self.config.documentation_length {
+            DocumentationLength::Short => 1,
+            DocumentationLength::Full => d.doc_comment.len(),
+        };
+
+        let style = match self.config.documentation_style {
+            DocumentationStyle::Auto if self.config.language == Language::C => {
+                DocumentationStyle::Doxy
+            }
+            DocumentationStyle::Auto if self.config.language == Language::Cxx => {
+                DocumentationStyle::Cxx
+            }
+            DocumentationStyle::Auto => DocumentationStyle::C, // Fallback if `Language` gets extended.
+            other => other,
+        };
+
+        // Following these documents for style conventions:
+        // https://en.wikibooks.org/wiki/C++_Programming/Code/Style_Conventions/Comments
+        // https://www.cs.cmu.edu/~410/doc/doxygen.html
+        match style {
+            DocumentationStyle::C => {
+                out.write("/*");
+                out.new_line();
+            }
+
+            DocumentationStyle::Doxy => {
+                out.write("/**");
+                out.new_line();
+            }
+
+            _ => (),
+        }
+
+        for line in &d.doc_comment[..end] {
+            match style {
+                DocumentationStyle::C => out.write(""),
+                DocumentationStyle::Doxy => out.write(" *"),
+                DocumentationStyle::C99 => out.write("//"),
+                DocumentationStyle::Cxx => out.write("///"),
+                DocumentationStyle::Auto => unreachable!(), // Auto case should always be covered
+            }
+
+            write!(out, "{}", line);
+            out.new_line();
+        }
+
+        match style {
+            DocumentationStyle::C => {
+                out.write(" */");
+                out.new_line();
+            }
+
+            DocumentationStyle::Doxy => {
+                out.write(" */");
+                out.new_line();
+            }
+
+            _ => (),
+        }
+    }
+
+    fn write_literal<W: Write>(&mut self, out: &mut SourceWriter<W>, l: &Literal) {
+        match l {
+            Literal::Expr(v) => write!(out, "{}", v),
+            Literal::Path {
+                ref associated_to,
+                ref name,
+            } => {
+                if let Some((ref path, ref export_name)) = associated_to {
+                    if let Some(known) = to_known_assoc_constant(path, name) {
+                        return write!(out, "{}", known);
+                    }
+                    let path_separator = if self.config.language == Language::C {
+                        "_"
+                    } else if self.config.structure.associated_constants_in_body {
+                        "::"
+                    } else {
+                        "_"
+                    };
+                    write!(out, "{}{}", export_name, path_separator)
+                }
+                write!(out, "{}", name)
+            }
+            Literal::FieldAccess {
+                ref base,
+                ref field,
+            } => {
+                write!(out, "(");
+                self.write_literal(out, base);
+                write!(out, ").{}", field);
+            }
+            Literal::PostfixUnaryOp { op, ref value } => {
+                write!(out, "{}", op);
+                self.write_literal(out, value);
+            }
+            Literal::BinOp {
+                ref left,
+                op,
+                ref right,
+            } => {
+                write!(out, "(");
+                self.write_literal(out, left);
+                write!(out, " {} ", op);
+                self.write_literal(out, right);
+                write!(out, ")");
+            }
+            Literal::Cast { ref ty, ref value } => {
+                out.write("(");
+                self.write_type(out, ty);
+                out.write(")");
+                self.write_literal(out, value);
+            }
+            Literal::Struct {
+                export_name,
+                fields,
+                path,
+            } => {
+                if self.config.language == Language::C {
+                    write!(out, "({})", export_name);
+                } else {
+                    write!(out, "{}", export_name);
+                }
+
+                write!(out, "{{ ");
+                let mut is_first_field = true;
+                // In C++, same order as defined is required.
+                let ordered_fields = out.bindings().struct_field_names(path);
+                for ordered_key in ordered_fields.iter() {
+                    if let Some(lit) = fields.get(ordered_key) {
+                        if !is_first_field {
+                            write!(out, ", ");
+                        }
+                        is_first_field = false;
+                        if self.config.language == Language::Cxx {
+                            // TODO: Some C++ versions (c++20?) now support designated
+                            // initializers, consider generating them.
+                            write!(out, "/* .{} = */ ", ordered_key);
+                        } else {
+                            write!(out, ".{} = ", ordered_key);
+                        }
+                        self.write_literal(out, lit);
+                    }
+                }
+                write!(out, " }}");
+            }
+        }
+    }
+
+    fn write_globals<W: Write>(&mut self, out: &mut SourceWriter<W>, b: &Bindings) {
+        // Override default method to open various blocs containing both globals and functions
+        // these blocks are closed in [`write_functions`] that is also overridden
+        if !b.functions.is_empty() || !b.globals.is_empty() {
+            if b.config.cpp_compatible_c() {
+                out.new_line_if_not_start();
+                out.write("#ifdef __cplusplus");
+            }
+
+            if b.config.language == Language::Cxx {
+                if let Some(ref using_namespaces) = b.config.using_namespaces {
+                    for namespace in using_namespaces {
+                        out.new_line();
+                        write!(out, "using namespace {};", namespace);
+                    }
+                    out.new_line();
+                }
+            }
+
+            if b.config.language == Language::Cxx || b.config.cpp_compatible_c() {
+                out.new_line();
+                out.write("extern \"C\" {");
+                out.new_line();
+            }
+
+            if b.config.cpp_compatible_c() {
+                out.write("#endif // __cplusplus");
+                out.new_line();
+            }
+
+            self.write_globals_default(out, b);
+        }
+    }
+
+    fn write_functions<W: Write>(&mut self, out: &mut SourceWriter<W>, b: &Bindings) {
+        // Override default method to close various blocks containing both globals and functions
+        // these blocks are opened in [`write_globals`] that is also overridden
+        if !b.functions.is_empty() || !b.globals.is_empty() {
+            self.write_functions_default(out, b);
+
+            if b.config.cpp_compatible_c() {
+                out.new_line();
+                out.write("#ifdef __cplusplus");
+            }
+
+            if b.config.language == Language::Cxx || b.config.cpp_compatible_c() {
+                out.new_line();
+                out.write("} // extern \"C\"");
+                out.new_line();
+            }
+
+            if b.config.cpp_compatible_c() {
+                out.write("#endif // __cplusplus");
+                out.new_line();
+            }
+        }
+    }
+}
diff --git a/src/bindgen/language_backend/cython.rs b/src/bindgen/language_backend/cython.rs
new file mode 100644
index 000000000..d768c596d
--- /dev/null
+++ b/src/bindgen/language_backend/cython.rs
@@ -0,0 +1,434 @@
+use crate::bindgen::ir::{
+    to_known_assoc_constant, ConditionWrite, DeprecatedNoteKind, Documentation, Enum, EnumVariant,
+    Field, Item, Literal, OpaqueItem, ReprAlign, Static, Struct, ToCondition, Type, Typedef, Union,
+};
+use crate::bindgen::language_backend::LanguageBackend;
+use crate::bindgen::writer::{ListType, SourceWriter};
+use crate::bindgen::DocumentationLength;
+use crate::bindgen::{cdecl, Bindings, Config};
+use std::io::Write;
+
+pub struct CythonLanguageBackend<'a> {
+    config: &'a Config,
+}
+
+impl<'a> CythonLanguageBackend<'a> {
+    pub fn new(config: &'a Config) -> Self {
+        Self { config }
+    }
+
+    fn write_enum_variant<W: Write>(&mut self, out: &mut SourceWriter<W>, u: &EnumVariant) {
+        self.write_documentation(out, &u.documentation);
+        write!(out, "{}", u.export_name);
+        if let Some(discriminant) = &u.discriminant {
+            // For extern Cython declarations the enumerator value is ignored,
+            // but still useful as documentation, so we write it as a comment.
+            out.write(" # = ");
+            self.write_literal(out, discriminant);
+        }
+        out.write(",");
+    }
+
+    fn write_field<W: Write>(&mut self, out: &mut SourceWriter<W>, f: &Field) {
+        // Cython doesn't support conditional fields.
+        // let condition = f.cfg.to_condition(self.config);
+
+        self.write_documentation(out, &f.documentation);
+        cdecl::write_field(self, out, &f.ty, &f.name, self.config);
+
+        // Cython extern declarations don't manage layouts, layouts are defined entierly by the
+        // corresponding C code. So we can omit bitfield sizes which are not supported by Cython.
+    }
+}
+
+impl LanguageBackend for CythonLanguageBackend<'_> {
+    fn write_headers<W: Write>(&self, out: &mut SourceWriter<W>, package_version: &str) {
+        if self.config.package_version {
+            write!(out, "''' Package version: {} '''", package_version);
+            out.new_line();
+        }
+        if let Some(ref f) = self.config.header {
+            out.new_line_if_not_start();
+            write!(out, "{}", f);
+            out.new_line();
+        }
+
+        if self.config.include_version {
+            out.new_line_if_not_start();
+            write!(
+                out,
+                "/* Generated with cbindgen:{} */",
+                crate::bindgen::config::VERSION
+            );
+            out.new_line();
+        }
+        if let Some(ref f) = &self.config.autogen_warning {
+            out.new_line_if_not_start();
+            write!(out, "{}", f);
+            out.new_line();
+        }
+
+        if self.config.no_includes
+            && self.config.sys_includes().is_empty()
+            && self.config.includes().is_empty()
+            && (self.config.cython.cimports.is_empty())
+            && self.config.after_includes.is_none()
+        {
+            return;
+        }
+
+        out.new_line_if_not_start();
+
+        if !&self.config.no_includes {
+            out.write("from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t");
+            out.new_line();
+            out.write("from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t");
+            out.new_line();
+            out.write("cdef extern from *");
+            out.open_brace();
+            out.write("ctypedef bint bool");
+            out.new_line();
+            out.write("ctypedef struct va_list");
+            out.new_line();
+            out.close_brace(false);
+        }
+
+        for (module, names) in &self.config.cython.cimports {
+            write!(out, "from {} cimport {}", module, names.join(", "));
+            out.new_line();
+        }
+
+        if let Some(ref line) = &self.config.after_includes {
+            write!(out, "{}", line);
+            out.new_line();
+        }
+    }
+
+    fn open_namespaces<W: Write>(&mut self, out: &mut SourceWriter<W>) {
+        out.new_line();
+        let header = &self.config.cython.header.as_deref().unwrap_or("*");
+        write!(out, "cdef extern from {}", header);
+        out.open_brace();
+    }
+
+    fn close_namespaces<W: Write>(&mut self, out: &mut SourceWriter<W>) {
+        out.close_brace(false);
+    }
+
+    fn write_footers<W: Write>(&mut self, _out: &mut SourceWriter<W>) {}
+
+    fn write_enum<W: Write>(&mut self, out: &mut SourceWriter<W>, e: &Enum) {
+        let size = e.repr.ty.map(|ty| ty.to_primitive().to_repr_c(self.config));
+        let has_data = e.tag.is_some();
+        let inline_tag_field = Enum::inline_tag_field(&e.repr);
+        let tag_name = e.tag_name();
+
+        let condition = e.cfg.to_condition(self.config);
+        condition.write_before(self.config, out);
+
+        self.write_documentation(out, &e.documentation);
+
+        // Emit the tag enum and everything related to it.
+        e.write_tag_enum(self.config, self, out, size, Self::write_enum_variant);
+
+        // If the enum has data, we need to emit structs for the variants and gather them together.
+        if has_data {
+            e.write_variant_defs(self.config, self, out);
+            out.new_line();
+            out.new_line();
+
+            e.open_struct_or_union(self.config, out, inline_tag_field);
+
+            // Emit tag field that is separate from all variants.
+            e.write_tag_field(self.config, out, size, inline_tag_field, tag_name);
+            out.new_line();
+
+            // Emit fields for all variants with data.
+            e.write_variant_fields(self.config, self, out, inline_tag_field, Self::write_field);
+
+            // Emit the post_body section, if relevant.
+            if let Some(body) = &self.config.export.post_body(&e.path) {
+                out.new_line();
+                out.write_raw_block(body);
+            }
+
+            out.close_brace(true);
+        }
+
+        condition.write_after(self.config, out);
+    }
+
+    fn write_struct<W: Write>(&mut self, out: &mut SourceWriter<W>, s: &Struct) {
+        if s.is_transparent {
+            let typedef = Typedef {
+                path: s.path.clone(),
+                export_name: s.export_name.to_owned(),
+                generic_params: s.generic_params.clone(),
+                aliased: s.fields[0].ty.clone(),
+                cfg: s.cfg.clone(),
+                annotations: s.annotations.clone(),
+                documentation: s.documentation.clone(),
+            };
+            self.write_type_def(out, &typedef);
+            for constant in &s.associated_constants {
+                out.new_line();
+                constant.write(self.config, self, out, Some(s));
+            }
+            return;
+        }
+
+        let condition = s.cfg.to_condition(self.config);
+        condition.write_before(self.config, out);
+
+        self.write_documentation(out, &s.documentation);
+
+        out.write(self.config.style.cython_def());
+
+        // Cython extern declarations don't manage layouts, layouts are defined entierly by the
+        // corresponding C code. So this `packed` is only for documentation, and missing
+        // `aligned(n)` is also not a problem.
+        if let Some(align) = s.alignment {
+            match align {
+                ReprAlign::Packed => out.write("packed "),
+                ReprAlign::Align(_) => {} // Not supported
+            }
+        }
+
+        out.write("struct");
+
+        if s.annotations.must_use(self.config) {
+            if let Some(ref anno) = &self.config.structure.must_use {
+                write!(out, " {}", anno);
+            }
+        }
+
+        if let Some(note) = s
+            .annotations
+            .deprecated_note(self.config, DeprecatedNoteKind::Struct)
+        {
+            write!(out, " {}", note);
+        }
+
+        write!(out, " {}", s.export_name());
+
+        out.open_brace();
+
+        // Emit the pre_body section, if relevant
+        if let Some(body) = &self.config.export.pre_body(&s.path) {
+            out.write_raw_block(body);
+            out.new_line();
+        }
+
+        out.write_vertical_source_list(self, &s.fields, ListType::Cap(";"), Self::write_field);
+        if s.fields.is_empty() {
+            out.write("pass");
+        }
+
+        // Emit the post_body section, if relevant
+        if let Some(body) = &self.config.export.post_body(&s.path) {
+            out.new_line();
+            out.write_raw_block(body);
+        }
+        out.close_brace(true);
+
+        for constant in &s.associated_constants {
+            out.new_line();
+            constant.write(self.config, self, out, Some(s));
+        }
+
+        condition.write_after(self.config, out);
+    }
+
+    fn write_union<W: Write>(&mut self, out: &mut SourceWriter<W>, u: &Union) {
+        let condition = u.cfg.to_condition(self.config);
+        condition.write_before(self.config, out);
+
+        self.write_documentation(out, &u.documentation);
+
+        out.write(self.config.style.cython_def());
+
+        out.write("union");
+
+        write!(out, " {}", u.export_name);
+
+        out.open_brace();
+
+        // Emit the pre_body section, if relevant
+        if let Some(body) = &self.config.export.pre_body(&u.path) {
+            out.write_raw_block(body);
+            out.new_line();
+        }
+
+        out.write_vertical_source_list(self, &u.fields, ListType::Cap(";"), Self::write_field);
+        if u.fields.is_empty() {
+            out.write("pass");
+        }
+
+        // Emit the post_body section, if relevant
+        if let Some(body) = &self.config.export.post_body(&u.path) {
+            out.new_line();
+            out.write_raw_block(body);
+        }
+
+        out.close_brace(true);
+
+        condition.write_after(self.config, out);
+    }
+
+    fn write_opaque_item<W: Write>(&mut self, out: &mut SourceWriter<W>, o: &OpaqueItem) {
+        let condition = o.cfg.to_condition(self.config);
+        condition.write_before(self.config, out);
+
+        self.write_documentation(out, &o.documentation);
+
+        o.generic_params.write_with_default(self, self.config, out);
+
+        write!(
+            out,
+            "{}struct {}",
+            &self.config.style.cython_def(),
+            o.export_name()
+        );
+        out.open_brace();
+        out.write("pass");
+        out.close_brace(false);
+
+        condition.write_after(self.config, out);
+    }
+
+    fn write_type_def<W: Write>(&mut self, out: &mut SourceWriter<W>, t: &Typedef) {
+        let condition = t.cfg.to_condition(self.config);
+        condition.write_before(self.config, out);
+
+        self.write_documentation(out, &t.documentation);
+
+        write!(out, "{} ", &self.config.language.typedef());
+
+        self.write_field(
+            out,
+            &Field::from_name_and_type(t.export_name().to_owned(), t.aliased.clone()),
+        );
+
+        out.write(";");
+
+        condition.write_after(self.config, out);
+    }
+
+    fn write_static<W: Write>(&mut self, out: &mut SourceWriter<W>, s: &Static) {
+        self.write_documentation(out, &s.documentation);
+        out.write("extern ");
+        if let Type::Ptr { is_const: true, .. } = s.ty {
+        } else if !s.mutable {
+            out.write("const ");
+        }
+        cdecl::write_field(self, out, &s.ty, &s.export_name, self.config);
+        out.write(";");
+    }
+
+    fn write_type<W: Write>(&mut self, out: &mut SourceWriter<W>, t: &Type) {
+        cdecl::write_type(self, out, t, self.config);
+    }
+
+    fn write_documentation<W: Write>(&mut self, out: &mut SourceWriter<W>, d: &Documentation) {
+        if d.doc_comment.is_empty() || !&self.config.documentation {
+            return;
+        }
+
+        let end = match &self.config.documentation_length {
+            DocumentationLength::Short => 1,
+            DocumentationLength::Full => d.doc_comment.len(),
+        };
+
+        // Cython uses Python-style comments, so `documentation_style` is not relevant.
+        for line in &d.doc_comment[..end] {
+            write!(out, "#{}", line);
+            out.new_line();
+        }
+    }
+
+    fn write_literal<W: Write>(&mut self, out: &mut SourceWriter<W>, l: &Literal) {
+        match l {
+            Literal::Expr(v) => match &**v {
+                "true" => write!(out, "True"),
+                "false" => write!(out, "False"),
+                v => write!(out, "{}", v),
+            },
+            Literal::Path {
+                ref associated_to,
+                ref name,
+            } => {
+                if let Some((ref path, ref export_name)) = associated_to {
+                    if let Some(known) = to_known_assoc_constant(path, name) {
+                        return write!(out, "{}", known);
+                    }
+                    write!(out, "{}_", export_name)
+                }
+                write!(out, "{}", name)
+            }
+            Literal::FieldAccess {
+                ref base,
+                ref field,
+            } => {
+                write!(out, "(");
+                self.write_literal(out, base);
+                write!(out, ").{}", field);
+            }
+            Literal::PostfixUnaryOp { op, ref value } => {
+                write!(out, "{}", op);
+                self.write_literal(out, value);
+            }
+            Literal::BinOp {
+                ref left,
+                op,
+                ref right,
+            } => {
+                write!(out, "(");
+                self.write_literal(out, left);
+                write!(out, " {} ", op);
+                self.write_literal(out, right);
+                write!(out, ")");
+            }
+            Literal::Cast { ref ty, ref value } => {
+                out.write("<");
+                self.write_type(out, ty);
+                out.write(">");
+                self.write_literal(out, value);
+            }
+            Literal::Struct {
+                export_name,
+                fields,
+                path,
+            } => {
+                write!(out, "<{}>", export_name);
+
+                write!(out, "{{ ");
+                let mut is_first_field = true;
+                // In C++, same order as defined is required.
+                let ordered_fields = out.bindings().struct_field_names(path);
+                for ordered_key in ordered_fields.iter() {
+                    if let Some(lit) = fields.get(ordered_key) {
+                        if !is_first_field {
+                            write!(out, ", ");
+                        } else {
+                            is_first_field = false;
+                        }
+                        self.write_literal(out, lit);
+                    }
+                }
+                write!(out, " }}");
+            }
+        }
+    }
+
+    fn write_functions<W: Write>(&mut self, out: &mut SourceWriter<W>, b: &Bindings) {
+        self.write_functions_default(out, b);
+
+        if b.globals.is_empty()
+            && b.constants.is_empty()
+            && b.items.is_empty()
+            && b.functions.is_empty()
+        {
+            out.write("pass");
+        }
+    }
+}
diff --git a/src/bindgen/language_backend/mod.rs b/src/bindgen/language_backend/mod.rs
new file mode 100644
index 000000000..adb7d8507
--- /dev/null
+++ b/src/bindgen/language_backend/mod.rs
@@ -0,0 +1,210 @@
+use crate::bindgen::ir::{
+    cfg::ConditionWrite, DeprecatedNoteKind, Documentation, Enum, Function, ItemContainer, Literal,
+    OpaqueItem, Static, Struct, ToCondition, Type, Typedef, Union,
+};
+use crate::bindgen::writer::SourceWriter;
+use crate::bindgen::{cdecl, Bindings, Layout};
+use crate::Config;
+
+use std::io::Write;
+
+mod clike;
+mod cython;
+
+pub use clike::CLikeLanguageBackend;
+pub use cython::CythonLanguageBackend;
+
+pub trait LanguageBackend: Sized {
+    fn open_namespaces<W: Write>(&mut self, out: &mut SourceWriter<W>);
+    fn close_namespaces<W: Write>(&mut self, out: &mut SourceWriter<W>);
+    fn write_headers<W: Write>(&self, out: &mut SourceWriter<W>, package_version: &str);
+    fn write_footers<W: Write>(&mut self, out: &mut SourceWriter<W>);
+    fn write_enum<W: Write>(&mut self, out: &mut SourceWriter<W>, e: &Enum);
+    fn write_struct<W: Write>(&mut self, out: &mut SourceWriter<W>, s: &Struct);
+    fn write_union<W: Write>(&mut self, out: &mut SourceWriter<W>, u: &Union);
+    fn write_opaque_item<W: Write>(&mut self, out: &mut SourceWriter<W>, o: &OpaqueItem);
+    fn write_type_def<W: Write>(&mut self, out: &mut SourceWriter<W>, t: &Typedef);
+    fn write_static<W: Write>(&mut self, out: &mut SourceWriter<W>, s: &Static);
+
+    fn write_function<W: Write>(
+        &mut self,
+        config: &Config,
+        out: &mut SourceWriter<W>,
+        f: &Function,
+    ) {
+        match config.function.args {
+            Layout::Horizontal => {
+                self.write_function_with_layout(config, out, f, Layout::Horizontal)
+            }
+            Layout::Vertical => self.write_function_with_layout(config, out, f, Layout::Vertical),
+            Layout::Auto => {
+                let max_line_length = config.line_length;
+                if !out.try_write(
+                    |out| self.write_function_with_layout(config, out, f, Layout::Horizontal),
+                    max_line_length,
+                ) {
+                    self.write_function_with_layout(config, out, f, Layout::Vertical);
+                }
+            }
+        }
+    }
+
+    fn write_function_with_layout<W: Write>(
+        &mut self,
+        config: &Config,
+        out: &mut SourceWriter<W>,
+        func: &Function,
+        layout: Layout,
+    ) {
+        let prefix = config.function.prefix(&func.annotations);
+        let postfix = config.function.postfix(&func.annotations);
+
+        let condition = func.cfg.to_condition(config);
+        condition.write_before(config, out);
+
+        self.write_documentation(out, &func.documentation);
+
+        fn write_space<W: Write>(layout: Layout, out: &mut SourceWriter<W>) {
+            if layout == Layout::Vertical {
+                out.new_line();
+            } else {
+                out.write(" ")
+            }
+        }
+        if func.extern_decl {
+            out.write("extern ");
+        } else {
+            if let Some(ref prefix) = prefix {
+                write!(out, "{}", prefix);
+                write_space(layout, out);
+            }
+            if func.annotations.must_use(config) {
+                if let Some(ref anno) = config.function.must_use {
+                    write!(out, "{}", anno);
+                    write_space(layout, out);
+                }
+            }
+            if let Some(note) = func
+                .annotations
+                .deprecated_note(config, DeprecatedNoteKind::Function)
+            {
+                write!(out, "{}", note);
+                write_space(layout, out);
+            }
+        }
+        cdecl::write_func(self, out, func, layout, config);
+
+        if !func.extern_decl {
+            if let Some(ref postfix) = postfix {
+                write_space(layout, out);
+                write!(out, "{}", postfix);
+            }
+        }
+
+        if let Some(ref swift_name_macro) = config.function.swift_name_macro {
+            if let Some(swift_name) = func.swift_name(config) {
+                // XXX Should this account for `layout`?
+                write!(out, " {}({})", swift_name_macro, swift_name);
+            }
+        }
+
+        out.write(";");
+        condition.write_after(config, out);
+    }
+
+    fn write_type<W: Write>(&mut self, out: &mut SourceWriter<W>, t: &Type);
+    fn write_documentation<W: Write>(&mut self, out: &mut SourceWriter<W>, d: &Documentation);
+    fn write_literal<W: Write>(&mut self, out: &mut SourceWriter<W>, l: &Literal);
+
+    fn write_bindings<W: Write>(&mut self, out: &mut SourceWriter<W>, b: &Bindings) {
+        self.write_headers(out, &b.package_version);
+        self.open_namespaces(out);
+        self.write_primitive_constants(out, b);
+        self.write_items(out, b);
+        self.write_non_primitive_constants(out, b);
+        self.write_globals(out, b);
+        self.write_functions(out, b);
+        self.close_namespaces(out);
+        self.write_footers(out);
+        self.write_trailer(out, b);
+    }
+
+    fn write_primitive_constants<W: Write>(&mut self, out: &mut SourceWriter<W>, b: &Bindings) {
+        for constant in &b.constants {
+            if constant.uses_only_primitive_types() {
+                out.new_line_if_not_start();
+                constant.write(&b.config, self, out, None);
+                out.new_line();
+            }
+        }
+    }
+
+    fn write_items<W: Write>(&mut self, out: &mut SourceWriter<W>, b: &Bindings) {
+        for item in &b.items {
+            if item
+                .deref()
+                .annotations()
+                .bool("no-export")
+                .unwrap_or(false)
+            {
+                continue;
+            }
+
+            out.new_line_if_not_start();
+            match *item {
+                ItemContainer::Constant(..) => unreachable!(),
+                ItemContainer::Static(..) => unreachable!(),
+                ItemContainer::Enum(ref x) => self.write_enum(out, x),
+                ItemContainer::Struct(ref x) => self.write_struct(out, x),
+                ItemContainer::Union(ref x) => self.write_union(out, x),
+                ItemContainer::OpaqueItem(ref x) => self.write_opaque_item(out, x),
+                ItemContainer::Typedef(ref x) => self.write_type_def(out, x),
+            }
+            out.new_line();
+        }
+    }
+
+    fn write_non_primitive_constants<W: Write>(&mut self, out: &mut SourceWriter<W>, b: &Bindings) {
+        for constant in &b.constants {
+            if !constant.uses_only_primitive_types() {
+                out.new_line_if_not_start();
+                constant.write(&b.config, self, out, None);
+                out.new_line();
+            }
+        }
+    }
+
+    fn write_globals<W: Write>(&mut self, out: &mut SourceWriter<W>, b: &Bindings) {
+        self.write_globals_default(out, b)
+    }
+
+    fn write_globals_default<W: Write>(&mut self, out: &mut SourceWriter<W>, b: &Bindings) {
+        for global in &b.globals {
+            out.new_line_if_not_start();
+            self.write_static(out, global);
+            out.new_line();
+        }
+    }
+
+    fn write_functions<W: Write>(&mut self, out: &mut SourceWriter<W>, b: &Bindings) {
+        self.write_functions_default(out, b)
+    }
+
+    fn write_functions_default<W: Write>(&mut self, out: &mut SourceWriter<W>, b: &Bindings) {
+        for function in &b.functions {
+            out.new_line_if_not_start();
+            self.write_function(&b.config, out, function);
+            out.new_line();
+        }
+    }
+
+    fn write_trailer<W: Write>(&mut self, out: &mut SourceWriter<W>, b: &Bindings) {
+        if let Some(ref f) = b.config.trailer {
+            out.new_line_if_not_start();
+            write!(out, "{}", f);
+            if !f.ends_with('\n') {
+                out.new_line();
+            }
+        }
+    }
+}
diff --git a/src/bindgen/mod.rs b/src/bindgen/mod.rs
index d0789da2e..5d7882118 100644
--- a/src/bindgen/mod.rs
+++ b/src/bindgen/mod.rs
@@ -46,6 +46,7 @@ mod declarationtyperesolver;
 mod dependencies;
 mod error;
 mod ir;
+mod language_backend;
 mod library;
 mod mangle;
 mod monomorph;
diff --git a/src/bindgen/writer.rs b/src/bindgen/writer.rs
index eed691723..f36757d26 100644
--- a/src/bindgen/writer.rs
+++ b/src/bindgen/writer.rs
@@ -6,7 +6,8 @@ use std::cmp;
 use std::io;
 use std::io::Write;
 
-use crate::bindgen::config::{Braces, Config, Language};
+use crate::bindgen::config::{Braces, Language};
+use crate::bindgen::language_backend::LanguageBackend;
 use crate::bindgen::Bindings;
 
 /// A type of way to format a list.
@@ -77,7 +78,7 @@ impl<'a, F: Write> SourceWriter<'a, F> {
     /// written.
     pub fn try_write<T>(&mut self, func: T, max_line_length: usize) -> bool
     where
-        T: Fn(&mut MeasureWriter),
+        T: FnOnce(&mut MeasureWriter),
     {
         if self.line_length > max_line_length {
             return false;
@@ -207,13 +208,19 @@ impl<'a, F: Write> SourceWriter<'a, F> {
         InnerWriter(self).write_fmt(fmt).unwrap();
     }
 
-    pub fn write_horizontal_source_list<S: Source>(
+    pub fn write_horizontal_source_list<
+        LB: LanguageBackend,
+        S,
+        WF: Fn(&mut LB, &mut SourceWriter<F>, &S),
+    >(
         &mut self,
+        language_backend: &mut LB,
         items: &[S],
         list_type: ListType<'_>,
+        writer: WF,
     ) {
         for (i, item) in items.iter().enumerate() {
-            item.write(&self.bindings.config, self);
+            writer(language_backend, self, item);
 
             match list_type {
                 ListType::Join(text) => {
@@ -228,11 +235,21 @@ impl<'a, F: Write> SourceWriter<'a, F> {
         }
     }
 
-    pub fn write_vertical_source_list<S: Source>(&mut self, items: &[S], list_type: ListType<'_>) {
+    pub fn write_vertical_source_list<
+        LB: LanguageBackend,
+        S,
+        WF: Fn(&mut LB, &mut SourceWriter<F>, &S),
+    >(
+        &mut self,
+        language_backend: &mut LB,
+        items: &[S],
+        list_type: ListType<'_>,
+        writer: WF,
+    ) {
         let align_length = self.line_length_for_align();
         self.push_set_spaces(align_length);
         for (i, item) in items.iter().enumerate() {
-            item.write(&self.bindings.config, self);
+            writer(language_backend, self, item);
 
             match list_type {
                 ListType::Join(text) => {
@@ -252,7 +269,3 @@ impl<'a, F: Write> SourceWriter<'a, F> {
         self.pop_tab();
     }
 }
-
-pub trait Source {
-    fn write<F: Write>(&self, config: &Config, _: &mut SourceWriter<F>);
-}


Test patch: