diff --git a/Cargo.lock b/Cargo.lock index 5daefa20bd..1615200e11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ [root] name = "bindgen" -version = "0.26.3" +version = "0.27.0" dependencies = [ "aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", "cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 5fbe1a269e..06772302ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ name = "bindgen" readme = "README.md" repository = "https://github.com/servo/rust-bindgen" documentation = "https://docs.rs/bindgen" -version = "0.26.3" +version = "0.27.0" build = "build.rs" exclude = [ diff --git a/src/codegen/helpers.rs b/src/codegen/helpers.rs index eb843a6539..4a64f79949 100644 --- a/src/codegen/helpers.rs +++ b/src/codegen/helpers.rs @@ -30,8 +30,8 @@ pub mod attributes { aster::AstBuilder::new().attr().word("inline") } - pub fn doc(comment: &str) -> ast::Attribute { - aster::AstBuilder::new().attr().doc(comment) + pub fn doc(comment: String) -> ast::Attribute { + aster::AstBuilder::new().attr().doc(&*comment) } pub fn link_name(name: &str) -> ast::Attribute { diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 0621ea62eb..6f2bf96b99 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -11,14 +11,14 @@ use aster::struct_field::StructFieldBuilder; use ir::annotations::FieldAccessorKind; use ir::comp::{Base, BitfieldUnit, Bitfield, CompInfo, CompKind, Field, FieldData, FieldMethods, Method, MethodKind}; +use ir::comment; use ir::context::{BindgenContext, ItemId}; use ir::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault}; use ir::dot; use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue}; use ir::function::{Abi, Function, FunctionSig}; use ir::int::IntKind; -use ir::item::{IsOpaque, Item, ItemAncestors, ItemCanonicalName, - ItemCanonicalPath, ItemSet}; +use ir::item::{IsOpaque, Item, ItemCanonicalName, ItemCanonicalPath, ItemSet}; use ir::item_kind::ItemKind; use ir::layout::Layout; use ir::module::Module; @@ -42,23 +42,13 @@ use syntax::ptr::P; // Name of type defined in constified enum module pub static CONSTIFIED_ENUM_MODULE_REPR_NAME: &'static str = "Type"; -fn root_import_depth(ctx: &BindgenContext, item: &Item) -> usize { - if !ctx.options().enable_cxx_namespaces { - return 0; - } - - item.ancestors(ctx) - .filter(|id| ctx.resolve_item(*id).is_module()) - .fold(1, |i, _| i + 1) -} - fn top_level_path(ctx: &BindgenContext, item: &Item) -> Vec { let mut path = vec![ctx.rust_ident_raw("self")]; if ctx.options().enable_cxx_namespaces { let super_ = ctx.rust_ident_raw("super"); - for _ in 0..root_import_depth(ctx, item) { + for _ in 0..item.codegen_depth(ctx) { path.push(super_.clone()); } } @@ -616,10 +606,8 @@ impl CodeGenerator for Type { let rust_name = ctx.rust_ident(&name); let mut typedef = aster::AstBuilder::new().item().pub_(); - if ctx.options().generate_comments { - if let Some(comment) = item.comment() { - typedef = typedef.attr().doc(comment); - } + if let Some(comment) = item.comment(ctx) { + typedef = typedef.with_attr(attributes::doc(comment)); } // We prefer using `pub use` over `pub type` because of: @@ -839,6 +827,7 @@ trait FieldCodegen<'a> { fn codegen(&self, ctx: &BindgenContext, fields_should_be_private: bool, + codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, anon_field_names: &mut AnonFieldNames, @@ -857,6 +846,7 @@ impl<'a> FieldCodegen<'a> for Field { fn codegen(&self, ctx: &BindgenContext, fields_should_be_private: bool, + codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, anon_field_names: &mut AnonFieldNames, @@ -872,6 +862,7 @@ impl<'a> FieldCodegen<'a> for Field { Field::DataMember(ref data) => { data.codegen(ctx, fields_should_be_private, + codegen_depth, accessor_kind, parent, anon_field_names, @@ -884,6 +875,7 @@ impl<'a> FieldCodegen<'a> for Field { Field::Bitfields(ref unit) => { unit.codegen(ctx, fields_should_be_private, + codegen_depth, accessor_kind, parent, anon_field_names, @@ -903,6 +895,7 @@ impl<'a> FieldCodegen<'a> for FieldData { fn codegen(&self, ctx: &BindgenContext, fields_should_be_private: bool, + codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, anon_field_names: &mut AnonFieldNames, @@ -945,8 +938,9 @@ impl<'a> FieldCodegen<'a> for FieldData { let mut attrs = vec![]; if ctx.options().generate_comments { - if let Some(comment) = self.comment() { - attrs.push(attributes::doc(comment)); + if let Some(raw_comment) = self.comment() { + let comment = comment::preprocess(raw_comment, codegen_depth + 1); + attrs.push(attributes::doc(comment)) } } @@ -1153,6 +1147,7 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit { fn codegen(&self, ctx: &BindgenContext, fields_should_be_private: bool, + codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, anon_field_names: &mut AnonFieldNames, @@ -1197,6 +1192,7 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit { for bf in self.bitfields() { bf.codegen(ctx, fields_should_be_private, + codegen_depth, accessor_kind, parent, anon_field_names, @@ -1277,6 +1273,7 @@ impl<'a> FieldCodegen<'a> for Bitfield { fn codegen(&self, ctx: &BindgenContext, _fields_should_be_private: bool, + _codegen_depth: usize, _accessor_kind: FieldAccessorKind, parent: &CompInfo, _anon_field_names: &mut AnonFieldNames, @@ -1409,10 +1406,8 @@ impl CodeGenerator for CompInfo { let mut attributes = vec![]; let mut needs_clone_impl = false; let mut needs_default_impl = false; - if ctx.options().generate_comments { - if let Some(comment) = item.comment() { - attributes.push(attributes::doc(comment)); - } + if let Some(comment) = item.comment(ctx) { + attributes.push(attributes::doc(comment)); } if self.packed() { attributes.push(attributes::repr_list(&["C", "packed"])); @@ -1545,9 +1540,11 @@ impl CodeGenerator for CompInfo { let mut methods = vec![]; let mut anon_field_names = AnonFieldNames::default(); + let codegen_depth = item.codegen_depth(ctx); for field in self.fields() { field.codegen(ctx, fields_should_be_private, + codegen_depth, struct_accessor_kind, self, &mut anon_field_names, @@ -2367,10 +2364,8 @@ impl CodeGenerator for Enum { builder = builder.with_attr(attributes::repr("C")); } - if ctx.options().generate_comments { - if let Some(comment) = item.comment() { - builder = builder.with_attr(attributes::doc(comment)); - } + if let Some(comment) = item.comment(ctx) { + builder = builder.with_attr(attributes::doc(comment)); } if !is_constified_enum { @@ -3069,10 +3064,8 @@ impl CodeGenerator for Function { let mut attributes = vec![]; - if ctx.options().generate_comments { - if let Some(comment) = item.comment() { - attributes.push(attributes::doc(comment)); - } + if let Some(comment) = item.comment(ctx) { + attributes.push(attributes::doc(comment)); } if let Some(mangled) = mangled_name { diff --git a/src/ir/comment.rs b/src/ir/comment.rs index 15237b919e..f772482e46 100644 --- a/src/ir/comment.rs +++ b/src/ir/comment.rs @@ -1,5 +1,7 @@ //! Utilities for manipulating C/C++ comments. +use std::iter; + /// The type of a comment. #[derive(Debug, PartialEq, Eq)] enum Kind { @@ -10,11 +12,12 @@ enum Kind { /// entire block ends with `*/`. MultiLine, } + /// Preprocesses a C/C++ comment so that it is a valid Rust comment. -pub fn preprocess(comment: String) -> String { +pub fn preprocess(comment: &str, indent: usize) -> String { match self::kind(&comment) { - Some(Kind::SingleLines) => preprocess_single_lines(&comment), - Some(Kind::MultiLine) => preprocess_multi_line(&comment), + Some(Kind::SingleLines) => preprocess_single_lines(comment, indent), + Some(Kind::MultiLine) => preprocess_multi_line(comment, indent), None => comment.to_owned(), } } @@ -30,38 +33,59 @@ fn kind(comment: &str) -> Option { } } +fn make_indent(indent: usize) -> String { + const RUST_INDENTATION: usize = 4; + + iter::repeat(' ').take(indent * RUST_INDENTATION).collect() +} + /// Preprocesses mulitple single line comments. /// /// Handles lines starting with both `//` and `///`. -fn preprocess_single_lines(comment: &str) -> String { - assert!(comment.starts_with("//"), "comment is not single line"); +fn preprocess_single_lines(comment: &str, indent: usize) -> String { + debug_assert!(comment.starts_with("//"), "comment is not single line"); + let indent = make_indent(indent); + let mut is_first = true; let lines: Vec<_> = comment.lines() - .map(|l| l.trim_left_matches('/').trim()) - .map(|l| format!("/// {}", l).trim().to_owned()) - .collect(); + .map(|l| l.trim_left_matches('/').trim()) + .map(|l| { + let indent = if is_first { "" } else { &*indent }; + is_first = false; + let maybe_space = if l.is_empty() { "" } else { " " }; + format!("{}///{}{}", indent, maybe_space, l) + }) + .collect(); lines.join("\n") } -fn preprocess_multi_line(comment: &str) -> String { +fn preprocess_multi_line(comment: &str, indent: usize) -> String { let comment = comment.trim_left_matches('/') - .trim_left_matches("*") - .trim_left_matches("!") + .trim_left_matches('*') + .trim_left_matches('!') .trim_right_matches('/') .trim_right_matches('*') .trim(); + let indent = make_indent(indent); // Strip any potential `*` characters preceding each line. + let mut is_first = true; let mut lines: Vec<_> = comment.lines() .map(|line| line.trim().trim_left_matches('*').trim()) .skip_while(|line| line.is_empty()) // Skip the first empty lines. - .map(|line| format!("/// {}", line).trim().to_owned()) + .map(|line| { + let indent = if is_first { "" } else { &*indent }; + is_first = false; + let maybe_space = if line.is_empty() { "" } else { " " }; + format!("{}///{}{}", indent, maybe_space, line) + }) .collect(); - // Remove the trailing `*/`. - let last_idx = lines.len() - 1; - if lines[last_idx].is_empty() { - lines.remove(last_idx); + // Remove the trailing line corresponding to the `*/`. + let last_line_is_empty = lines.last().map_or(false, |l| l.is_empty()); + + if last_line_is_empty { + lines.pop(); } lines.join("\n") @@ -79,16 +103,16 @@ mod test { #[test] fn processes_single_lines_correctly() { - assert_eq!(preprocess("/// hello".to_owned()), "/// hello"); - assert_eq!(preprocess("// hello".to_owned()), "/// hello"); + assert_eq!(preprocess("/// hello", 0), "/// hello"); + assert_eq!(preprocess("// hello", 0), "/// hello"); } #[test] fn processes_multi_lines_correctly() { - assert_eq!(preprocess("/** hello \n * world \n * foo \n */".to_owned()), + assert_eq!(preprocess("/** hello \n * world \n * foo \n */", 0), "/// hello\n/// world\n/// foo"); - assert_eq!(preprocess("/**\nhello\n*world\n*foo\n*/".to_owned()), + assert_eq!(preprocess("/**\nhello\n*world\n*foo\n*/", 0), "/// hello\n/// world\n/// foo"); } } diff --git a/src/ir/comp.rs b/src/ir/comp.rs index 4854949fc1..6dfc998028 100644 --- a/src/ir/comp.rs +++ b/src/ir/comp.rs @@ -1,7 +1,6 @@ //! Compound types (unions and structs) in our intermediate representation. use super::annotations::Annotations; -use super::comment; use super::context::{BindgenContext, ItemId}; use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault}; use super::dot::DotAttributes; @@ -1102,7 +1101,7 @@ impl CompInfo { Some(potential_id), ctx); - let comment = cur.raw_comment().map(comment::preprocess); + let comment = cur.raw_comment(); let annotations = Annotations::new(&cur); let name = cur.spelling(); let is_mutable = cursor.is_mutable_field(); diff --git a/src/ir/enum_ty.rs b/src/ir/enum_ty.rs index 731961a66e..38de01d9c8 100644 --- a/src/ir/enum_ty.rs +++ b/src/ir/enum_ty.rs @@ -1,6 +1,5 @@ //! Intermediate representation for C/C++ enumerations. -use super::comment; use super::context::{BindgenContext, ItemId}; use super::item::Item; use super::ty::TypeKind; @@ -114,7 +113,7 @@ impl Enum { }) }); - let comment = cursor.raw_comment().map(comment::preprocess); + let comment = cursor.raw_comment(); variants.push(EnumVariant::new(name, comment, val, diff --git a/src/ir/function.rs b/src/ir/function.rs index 299bd65c46..9865997ded 100644 --- a/src/ir/function.rs +++ b/src/ir/function.rs @@ -1,6 +1,5 @@ //! Intermediate representation for C/C++ functions and methods. -use super::comment; use super::context::{BindgenContext, ItemId}; use super::dot::DotAttributes; use super::item::Item; @@ -406,7 +405,7 @@ impl ClangSubItemParser for Function { mangled_name = None; } - let comment = cursor.raw_comment().map(comment::preprocess); + let comment = cursor.raw_comment(); let function = Self::new(name, mangled_name, sig, comment); Ok(ParseResult::New(function, Some(cursor))) diff --git a/src/ir/item.rs b/src/ir/item.rs index cd721c61df..e65b7a99d1 100644 --- a/src/ir/item.rs +++ b/src/ir/item.rs @@ -480,9 +480,35 @@ impl Item { self.parent_id = id; } - /// Get this `Item`'s comment, if it has any. - pub fn comment(&self) -> Option<&str> { - self.comment.as_ref().map(|c| &**c) + /// Returns the depth this item is indented to. + /// + /// FIXME(emilio): This may need fixes for the enums within modules stuff. + pub fn codegen_depth(&self, ctx: &BindgenContext) -> usize { + if !ctx.options().enable_cxx_namespaces { + return 0; + } + + self.ancestors(ctx) + .filter(|id| { + ctx.resolve_item(*id).as_module().map_or(false, |module| { + !module.is_inline() || + ctx.options().conservative_inline_namespaces + }) + }) + .count() + 1 + } + + + /// Get this `Item`'s comment, if it has any, already preprocessed and with + /// the right indentation. + pub fn comment(&self, ctx: &BindgenContext) -> Option { + if !ctx.options().generate_comments { + return None; + } + + self.comment.as_ref().map(|comment| { + comment::preprocess(comment, self.codegen_depth(ctx)) + }) } /// What kind of item is this? @@ -1012,7 +1038,7 @@ impl ClangItemParser for Item { return Err(ParseError::Continue); } - let comment = cursor.raw_comment().map(comment::preprocess); + let comment = cursor.raw_comment(); let annotations = Annotations::new(&cursor); let current_module = ctx.current_module(); @@ -1218,8 +1244,7 @@ impl ClangItemParser for Item { }; let comment = decl.raw_comment() - .or_else(|| location.raw_comment()) - .map(comment::preprocess); + .or_else(|| location.raw_comment()); let annotations = Annotations::new(&decl) .or_else(|| Annotations::new(&location)); diff --git a/tests/expectations/tests/comment-indent.rs b/tests/expectations/tests/comment-indent.rs new file mode 100644 index 0000000000..127c1b0e07 --- /dev/null +++ b/tests/expectations/tests/comment-indent.rs @@ -0,0 +1,114 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] + + +#[allow(non_snake_case, non_camel_case_types, non_upper_case_globals)] +pub mod root { + #[allow(unused_imports)] + use self::super::root; + /// This is a multi-line doc comment. + /// + /// This class is really really interesting, look! + #[repr(C)] + #[derive(Debug, Default, Copy)] + pub struct Foo { + pub _address: u8, + } + /// This nested class is also a multi-line doc comment. + /// + /// This class is not so interesting, but worth a bit of docs too! + #[repr(C)] + #[derive(Debug, Default, Copy)] + pub struct Foo_Bar { + pub _address: u8, + } + #[test] + fn bindgen_test_layout_Foo_Bar() { + assert_eq!(::std::mem::size_of::() , 1usize , concat ! ( + "Size of: " , stringify ! ( Foo_Bar ) )); + assert_eq! (::std::mem::align_of::() , 1usize , concat ! ( + "Alignment of " , stringify ! ( Foo_Bar ) )); + } + impl Clone for Foo_Bar { + fn clone(&self) -> Self { *self } + } + #[test] + fn bindgen_test_layout_Foo() { + assert_eq!(::std::mem::size_of::() , 1usize , concat ! ( + "Size of: " , stringify ! ( Foo ) )); + assert_eq! (::std::mem::align_of::() , 1usize , concat ! ( + "Alignment of " , stringify ! ( Foo ) )); + } + impl Clone for Foo { + fn clone(&self) -> Self { *self } + } + pub mod test { + #[allow(unused_imports)] + use self::super::super::root; + /// I'm in a namespace, and thus I may be on a rust module, most of the time. + /// My documentation is pretty extensive, I guess. + #[repr(C)] + #[derive(Debug, Default, Copy)] + pub struct Baz { + /// This member is plain awesome, just amazing. + /// + /// It also has super-extensive docs, with even a nice ascii-art diagram. + /// + /// +------+ +-------+ + /// | foo | ----> | bar | + /// +------+ +-------+ + pub member: ::std::os::raw::c_int, + } + #[test] + fn bindgen_test_layout_Baz() { + assert_eq!(::std::mem::size_of::() , 4usize , concat ! ( + "Size of: " , stringify ! ( Baz ) )); + assert_eq! (::std::mem::align_of::() , 4usize , concat ! ( + "Alignment of " , stringify ! ( Baz ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const Baz ) ) . member as * const _ as + usize } , 0usize , concat ! ( + "Alignment of field: " , stringify ! ( Baz ) , "::" , + stringify ! ( member ) )); + } + impl Clone for Baz { + fn clone(&self) -> Self { *self } + } + /// I'm in an inline namespace, and as such I shouldn't get generated inside + /// a rust module, except when the relevant option is specified. Also, this + /// comment shouldn't be misaligned. + #[repr(C)] + #[derive(Debug, Default, Copy)] + pub struct InInlineNS { + pub _address: u8, + } + #[test] + fn bindgen_test_layout_InInlineNS() { + assert_eq!(::std::mem::size_of::() , 1usize , concat ! + ( "Size of: " , stringify ! ( InInlineNS ) )); + assert_eq! (::std::mem::align_of::() , 1usize , concat + ! ( "Alignment of " , stringify ! ( InInlineNS ) )); + } + impl Clone for InInlineNS { + fn clone(&self) -> Self { *self } + } + + #[repr(C)] + #[derive(Debug, Default, Copy)] + pub struct Bazz { + pub _address: u8, + } + #[test] + fn bindgen_test_layout_Bazz() { + assert_eq!(::std::mem::size_of::() , 1usize , concat ! ( + "Size of: " , stringify ! ( Bazz ) )); + assert_eq! (::std::mem::align_of::() , 1usize , concat ! ( + "Alignment of " , stringify ! ( Bazz ) )); + } + impl Clone for Bazz { + fn clone(&self) -> Self { *self } + } + } +} diff --git a/tests/expectations/tests/layout_eth_conf.rs b/tests/expectations/tests/layout_eth_conf.rs index f5cafbfc66..cc32c3cc57 100644 --- a/tests/expectations/tests/layout_eth_conf.rs +++ b/tests/expectations/tests/layout_eth_conf.rs @@ -1182,12 +1182,12 @@ pub struct rte_eth_fdir_masks { /// Bit mask for L4 destination port in big endian. pub dst_port_mask: u16, /// 6 bit mask for proper 6 bytes of Mac address, bit 0 matches the -/// first byte on the wire + /// first byte on the wire pub mac_addr_byte_mask: u8, /// Bit mask for tunnel ID in big endian. pub tunnel_id_mask: u32, /// < 1 - Match tunnel type, -/// 0 - Ignore tunnel type. + /// 0 - Ignore tunnel type. pub tunnel_type_mask: u8, } #[test] @@ -1460,29 +1460,29 @@ impl Clone for rte_intr_conf { #[repr(C)] pub struct rte_eth_conf { /// < bitmap of ETH_LINK_SPEED_XXX of speeds to be -/// used. ETH_LINK_SPEED_FIXED disables link -/// autonegotiation, and a unique speed shall be -/// set. Otherwise, the bitmap defines the set of -/// speeds to be advertised. If the special value -/// ETH_LINK_SPEED_AUTONEG (0) is used, all speeds -/// supported are advertised. + /// used. ETH_LINK_SPEED_FIXED disables link + /// autonegotiation, and a unique speed shall be + /// set. Otherwise, the bitmap defines the set of + /// speeds to be advertised. If the special value + /// ETH_LINK_SPEED_AUTONEG (0) is used, all speeds + /// supported are advertised. pub link_speeds: u32, /// < Port RX configuration. pub rxmode: rte_eth_rxmode, /// < Port TX configuration. pub txmode: rte_eth_txmode, /// < Loopback operation mode. By default the value -/// is 0, meaning the loopback mode is disabled. -/// Read the datasheet of given ethernet controller -/// for details. The possible values of this field -/// are defined in implementation of each driver. + /// is 0, meaning the loopback mode is disabled. + /// Read the datasheet of given ethernet controller + /// for details. The possible values of this field + /// are defined in implementation of each driver. pub lpbk_mode: u32, /// < Port RX filtering configuration (union). pub rx_adv_conf: rte_eth_conf__bindgen_ty_1, /// < Port TX DCB configuration (union). pub tx_adv_conf: rte_eth_conf__bindgen_ty_2, /// Currently,Priority Flow Control(PFC) are supported,if DCB with PFC -/// is needed,and the variable must be set ETH_DCB_PFC_SUPPORT. + /// is needed,and the variable must be set ETH_DCB_PFC_SUPPORT. pub dcb_capability_en: u32, /// < FDIR configuration. pub fdir_conf: rte_fdir_conf, diff --git a/tests/expectations/tests/layout_mbuf.rs b/tests/expectations/tests/layout_mbuf.rs index e527ac9252..29779b3cbd 100644 --- a/tests/expectations/tests/layout_mbuf.rs +++ b/tests/expectations/tests/layout_mbuf.rs @@ -98,7 +98,7 @@ pub struct rte_mbuf { pub next: *mut rte_mbuf, pub __bindgen_anon_4: rte_mbuf__bindgen_ty_5, /// Size of the application private data. In case of an indirect -/// mbuf, it stores the direct mbuf private data size. + /// mbuf, it stores the direct mbuf private data size. pub priv_size: u16, /// Timesync flags for use with IEEE1588. pub timesync: u16, diff --git a/tests/headers/comment-indent.hpp b/tests/headers/comment-indent.hpp new file mode 100644 index 0000000000..96c13fccb6 --- /dev/null +++ b/tests/headers/comment-indent.hpp @@ -0,0 +1,47 @@ +// bindgen-flags: --enable-cxx-namespaces + +/** + * This is a multi-line doc comment. + * + * This class is really really interesting, look! + */ +class Foo { + /** + * This nested class is also a multi-line doc comment. + * + * This class is not so interesting, but worth a bit of docs too! + */ + class Bar { }; +}; + +namespace test { + /** + * I'm in a namespace, and thus I may be on a rust module, most of the time. + * My documentation is pretty extensive, I guess. + */ + class Baz { + /** + * This member is plain awesome, just amazing. + * + * It also has super-extensive docs, with even a nice ascii-art diagram. + * + * +------+ +-------+ + * | foo | ----> | bar | + * +------+ +-------+ + */ + int member; + }; + + inline namespace foobiedoobie { + /** + * I'm in an inline namespace, and as such I shouldn't get generated inside + * a rust module, except when the relevant option is specified. Also, this + * comment shouldn't be misaligned. + */ + class InInlineNS { + }; + } + + /**/ + class Bazz {}; +}