Merge pull request #399 from dtolnay/block

Preserve meaning of block boundaries
diff --git a/gen/src/block.rs b/gen/src/block.rs
new file mode 100644
index 0000000..e7f7c63
--- /dev/null
+++ b/gen/src/block.rs
@@ -0,0 +1,44 @@
+use proc_macro2::Ident;
+
+pub enum Block {
+    AnonymousNamespace,
+    Namespace(&'static str),
+    UserDefinedNamespace(Ident),
+    InlineNamespace(&'static str),
+    ExternC,
+}
+
+impl Block {
+    pub fn write_begin(&self, out: &mut String) {
+        if let Block::InlineNamespace(_) = self {
+            out.push_str("inline ");
+        }
+        self.write_common(out);
+        out.push_str(" {\n");
+    }
+
+    pub fn write_end(&self, out: &mut String) {
+        out.push_str("} // ");
+        self.write_common(out);
+        out.push('\n');
+    }
+
+    fn write_common(&self, out: &mut String) {
+        match self {
+            Block::AnonymousNamespace => out.push_str("namespace"),
+            Block::Namespace(name) => {
+                out.push_str("namespace ");
+                out.push_str(name);
+            }
+            Block::UserDefinedNamespace(name) => {
+                out.push_str("namespace ");
+                out.push_str(&name.to_string());
+            }
+            Block::InlineNamespace(name) => {
+                out.push_str("namespace ");
+                out.push_str(name);
+            }
+            Block::ExternC => out.push_str("extern \"C\""),
+        }
+    }
+}
diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs
index 448b6f8..e755e10 100644
--- a/gen/src/builtin.rs
+++ b/gen/src/builtin.rs
@@ -1,3 +1,4 @@
+use crate::gen::block::Block;
 use crate::gen::ifndef;
 use crate::gen::out::{Content, OutFile};
 
@@ -39,8 +40,8 @@
     let builtin = &mut out.builtin;
     let out = &mut builtin.content;
 
-    out.begin_block("namespace rust");
-    out.begin_block("inline namespace cxxbridge05");
+    out.begin_block(Block::Namespace("rust"));
+    out.begin_block(Block::InlineNamespace("cxxbridge05"));
     writeln!(out, "// #include \"rust/cxx.h\"");
 
     ifndef::write(out, builtin.panic, "CXXBRIDGE05_PANIC");
@@ -51,10 +52,10 @@
     }
 
     if builtin.rust_error {
-        out.begin_block("namespace");
+        out.begin_block(Block::AnonymousNamespace);
         writeln!(out, "template <typename T>");
         writeln!(out, "class impl;");
-        out.end_block("namespace");
+        out.end_block(Block::AnonymousNamespace);
     }
 
     ifndef::write(out, builtin.rust_string, "CXXBRIDGE05_RUST_STRING");
@@ -91,15 +92,15 @@
         writeln!(out, "}};");
     }
 
-    out.begin_block("namespace");
+    out.begin_block(Block::AnonymousNamespace);
 
     if builtin.ptr_len {
-        out.begin_block("namespace repr");
+        out.begin_block(Block::Namespace("repr"));
         writeln!(out, "struct PtrLen final {{");
         writeln!(out, "  const void *ptr;");
         writeln!(out, "  size_t len;");
         writeln!(out, "}};");
-        out.end_block("namespace repr");
+        out.end_block(Block::Namespace("repr"));
     }
 
     if builtin.rust_str_new_unchecked || builtin.rust_str_repr {
@@ -167,11 +168,11 @@
         writeln!(out, "}};");
     }
 
-    out.end_block("namespace");
-    out.end_block("namespace cxxbridge05");
+    out.end_block(Block::AnonymousNamespace);
+    out.end_block(Block::InlineNamespace("cxxbridge05"));
 
     if builtin.trycatch {
-        out.begin_block("namespace behavior");
+        out.begin_block(Block::Namespace("behavior"));
         include.exception = true;
         include.type_traits = true;
         include.utility = true;
@@ -190,8 +191,8 @@
         writeln!(out, "}} catch (const ::std::exception &e) {{");
         writeln!(out, "  fail(e.what());");
         writeln!(out, "}}");
-        out.end_block("namespace behavior");
+        out.end_block(Block::Namespace("behavior"));
     }
 
-    out.end_block("namespace rust");
+    out.end_block(Block::Namespace("rust"));
 }
diff --git a/gen/src/mod.rs b/gen/src/mod.rs
index a4a159d..8626058 100644
--- a/gen/src/mod.rs
+++ b/gen/src/mod.rs
@@ -2,6 +2,7 @@
 // the cxxbridge CLI command.
 
 mod alphasort;
+mod block;
 mod builtin;
 mod check;
 pub(super) mod error;
diff --git a/gen/src/out.rs b/gen/src/out.rs
index 6ba44aa..606a586 100644
--- a/gen/src/out.rs
+++ b/gen/src/out.rs
@@ -1,3 +1,4 @@
+use crate::gen::block::Block;
 use crate::gen::builtin::Builtins;
 use crate::gen::include::Includes;
 use crate::gen::Opt;
@@ -18,7 +19,7 @@
 pub struct Content {
     bytes: String,
     section_pending: bool,
-    blocks_pending: Vec<String>,
+    blocks_pending: Vec<Block>,
 }
 
 impl<'a> OutFile<'a> {
@@ -38,11 +39,11 @@
         self.content.get_mut().next_section();
     }
 
-    pub fn begin_block(&mut self, block: &str) {
+    pub fn begin_block(&mut self, block: Block) {
         self.content.get_mut().begin_block(block);
     }
 
-    pub fn end_block(&mut self, block: &str) {
+    pub fn end_block(&mut self, block: Block) {
         self.content.get_mut().end_block(block);
     }
 
@@ -95,15 +96,13 @@
         self.section_pending = true;
     }
 
-    pub fn begin_block(&mut self, block: &str) {
-        self.blocks_pending.push(block.to_owned());
+    pub fn begin_block(&mut self, block: Block) {
+        self.blocks_pending.push(block);
     }
 
-    pub fn end_block(&mut self, block: &str) {
+    pub fn end_block(&mut self, block: Block) {
         if self.blocks_pending.pop().is_none() {
-            self.bytes.push_str("} // ");
-            self.bytes.push_str(block);
-            self.bytes.push('\n');
+            Block::write_end(&block, &mut self.bytes);
             self.section_pending = true;
         }
     }
@@ -119,8 +118,7 @@
                     self.bytes.push('\n');
                 }
                 for block in self.blocks_pending.drain(..) {
-                    self.bytes.push_str(&block);
-                    self.bytes.push_str(" {\n");
+                    Block::write_begin(&block, &mut self.bytes);
                 }
                 self.section_pending = false;
             } else if self.section_pending {
diff --git a/gen/src/write.rs b/gen/src/write.rs
index 57c699c..a1f168f 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -1,4 +1,5 @@
 use crate::gen::alphasort::NamespaceEntries;
+use crate::gen::block::Block;
 use crate::gen::out::OutFile;
 use crate::gen::{builtin, include, Opt};
 use crate::syntax::atom::Atom::{self, *};
@@ -107,7 +108,7 @@
     }
 
     if !out.header {
-        out.begin_block("extern \"C\"");
+        out.begin_block(Block::ExternC);
         write_exception_glue(out, apis);
         for api in apis {
             match api {
@@ -116,7 +117,7 @@
                 _ => {}
             }
         }
-        out.end_block("extern \"C\"");
+        out.end_block(Block::ExternC);
     }
 
     for api in apis {
@@ -127,10 +128,9 @@
     }
 
     for (namespace, nested_ns_entries) in ns_entries.nested_content() {
-        let block = format!("namespace {}", namespace);
-        out.begin_block(&block);
+        out.begin_block(Block::UserDefinedNamespace(namespace.clone()));
         gen_namespace_contents(out, nested_ns_entries);
-        out.end_block(&block);
+        out.end_block(Block::UserDefinedNamespace(namespace.clone()));
     }
 }
 
@@ -973,7 +973,7 @@
 }
 
 fn write_generic_instantiations(out: &mut OutFile) {
-    out.begin_block("extern \"C\"");
+    out.begin_block(Block::ExternC);
     for ty in out.types {
         if let Type::RustBox(ty) = ty {
             if let Type::Ident(inner) = &ty.inner {
@@ -1009,10 +1009,10 @@
             }
         }
     }
-    out.end_block("extern \"C\"");
+    out.end_block(Block::ExternC);
 
-    out.begin_block("namespace rust");
-    out.begin_block("inline namespace cxxbridge05");
+    out.begin_block(Block::Namespace("rust"));
+    out.begin_block(Block::InlineNamespace("cxxbridge05"));
     for ty in out.types {
         if let Type::RustBox(ty) = ty {
             if let Type::Ident(inner) = &ty.inner {
@@ -1026,8 +1026,8 @@
             }
         }
     }
-    out.end_block("namespace cxxbridge05");
-    out.end_block("namespace rust");
+    out.end_block(Block::InlineNamespace("cxxbridge05"));
+    out.end_block(Block::Namespace("rust"));
 }
 
 fn write_rust_box_extern(out: &mut OutFile, ident: &CppName) {