diff --git a/CHANGELOG.md b/CHANGELOG.md index 9224c018a7..2c9889f263 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -178,6 +178,8 @@ matching a regular expression. * new feature: allow using the `C-unwind` ABI in `--override-abi` on nightly rust. + * new feature: `process_comments` method to the `ParseCallbacks` trait to + handle source code comments. ## Changed diff --git a/bindgen/callbacks.rs b/bindgen/callbacks.rs index bebd6978aa..5e8ac78839 100644 --- a/bindgen/callbacks.rs +++ b/bindgen/callbacks.rs @@ -108,6 +108,11 @@ pub trait ParseCallbacks: fmt::Debug { fn add_derives(&self, _info: &DeriveInfo<'_>) -> Vec { vec![] } + + /// Process a source code comment. + fn process_comment(&self, _comment: &str) -> Option { + None + } } /// Relevant information about a type to which new derive attributes will be added using diff --git a/bindgen/codegen/helpers.rs b/bindgen/codegen/helpers.rs index 5bf36acb42..088c7f9363 100644 --- a/bindgen/codegen/helpers.rs +++ b/bindgen/codegen/helpers.rs @@ -55,9 +55,11 @@ pub mod attributes { } pub fn doc(comment: String) -> TokenStream { - // NOTE(emilio): By this point comments are already preprocessed and in - // `///` form. Quote turns them into `#[doc]` comments, but oh well. - TokenStream::from_str(&comment).unwrap() + if comment.is_empty() { + quote!() + } else { + quote!(#[doc = #comment]) + } } pub fn link_name(name: &str) -> TokenStream { diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index e758963ac0..c1e92b77d5 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -20,7 +20,6 @@ use super::BindgenOptions; use crate::ir::analysis::{HasVtable, Sizedness}; use crate::ir::annotations::FieldAccessorKind; -use crate::ir::comment; use crate::ir::comp::{ Bitfield, BitfieldUnit, CompInfo, CompKind, Field, FieldData, FieldMethods, Method, MethodKind, @@ -1245,7 +1244,6 @@ trait FieldCodegen<'a> { &self, ctx: &BindgenContext, fields_should_be_private: bool, - codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, result: &mut CodegenResult, @@ -1265,7 +1263,6 @@ impl<'a> FieldCodegen<'a> for Field { &self, ctx: &BindgenContext, fields_should_be_private: bool, - codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, result: &mut CodegenResult, @@ -1282,7 +1279,6 @@ impl<'a> FieldCodegen<'a> for Field { data.codegen( ctx, fields_should_be_private, - codegen_depth, accessor_kind, parent, result, @@ -1296,7 +1292,6 @@ impl<'a> FieldCodegen<'a> for Field { unit.codegen( ctx, fields_should_be_private, - codegen_depth, accessor_kind, parent, result, @@ -1346,7 +1341,6 @@ impl<'a> FieldCodegen<'a> for FieldData { &self, ctx: &BindgenContext, fields_should_be_private: bool, - codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, result: &mut CodegenResult, @@ -1392,8 +1386,7 @@ impl<'a> FieldCodegen<'a> for FieldData { let mut field = quote! {}; if ctx.options().generate_comments { if let Some(raw_comment) = self.comment() { - let comment = - comment::preprocess(raw_comment, codegen_depth + 1); + let comment = ctx.options().process_comment(raw_comment); field = attributes::doc(comment); } } @@ -1553,7 +1546,6 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit { &self, ctx: &BindgenContext, fields_should_be_private: bool, - codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, result: &mut CodegenResult, @@ -1630,7 +1622,6 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit { bf.codegen( ctx, fields_should_be_private, - codegen_depth, accessor_kind, parent, result, @@ -1705,7 +1696,6 @@ impl<'a> FieldCodegen<'a> for Bitfield { &self, ctx: &BindgenContext, fields_should_be_private: bool, - _codegen_depth: usize, _accessor_kind: FieldAccessorKind, parent: &CompInfo, _result: &mut CodegenResult, @@ -1883,7 +1873,6 @@ impl CodeGenerator for CompInfo { let mut methods = vec![]; if !is_opaque { - let codegen_depth = item.codegen_depth(ctx); let fields_should_be_private = item.annotations().private_fields().unwrap_or(false); let struct_accessor_kind = item @@ -1894,7 +1883,6 @@ impl CodeGenerator for CompInfo { field.codegen( ctx, fields_should_be_private, - codegen_depth, struct_accessor_kind, self, result, @@ -2703,14 +2691,12 @@ impl std::str::FromStr for EnumVariation { /// A helper type to construct different enum variations. enum EnumBuilder<'a> { Rust { - codegen_depth: usize, attrs: Vec, ident: Ident, tokens: proc_macro2::TokenStream, emitted_any_variants: bool, }, NewType { - codegen_depth: usize, canonical_name: &'a str, tokens: proc_macro2::TokenStream, is_bitfield: bool, @@ -2718,26 +2704,14 @@ enum EnumBuilder<'a> { }, Consts { variants: Vec, - codegen_depth: usize, }, ModuleConsts { - codegen_depth: usize, module_name: &'a str, module_items: Vec, }, } impl<'a> EnumBuilder<'a> { - /// Returns the depth of the code generation for a variant of this enum. - fn codegen_depth(&self) -> usize { - match *self { - EnumBuilder::Rust { codegen_depth, .. } | - EnumBuilder::NewType { codegen_depth, .. } | - EnumBuilder::ModuleConsts { codegen_depth, .. } | - EnumBuilder::Consts { codegen_depth, .. } => codegen_depth, - } - } - /// Returns true if the builder is for a rustified enum. fn is_rust_enum(&self) -> bool { matches!(*self, EnumBuilder::Rust { .. }) @@ -2750,7 +2724,6 @@ impl<'a> EnumBuilder<'a> { mut attrs: Vec, repr: proc_macro2::TokenStream, enum_variation: EnumVariation, - enum_codegen_depth: usize, ) -> Self { let ident = Ident::new(name, Span::call_site()); @@ -2759,7 +2732,6 @@ impl<'a> EnumBuilder<'a> { is_bitfield, is_global, } => EnumBuilder::NewType { - codegen_depth: enum_codegen_depth, canonical_name: name, tokens: quote! { #( #attrs )* @@ -2774,7 +2746,6 @@ impl<'a> EnumBuilder<'a> { attrs.insert(0, quote! { #[repr( #repr )] }); let tokens = quote!(); EnumBuilder::Rust { - codegen_depth: enum_codegen_depth + 1, attrs, ident, tokens, @@ -2790,10 +2761,7 @@ impl<'a> EnumBuilder<'a> { pub type #ident = #repr; }); - EnumBuilder::Consts { - variants, - codegen_depth: enum_codegen_depth, - } + EnumBuilder::Consts { variants } } EnumVariation::ModuleConsts => { @@ -2807,7 +2775,6 @@ impl<'a> EnumBuilder<'a> { }; EnumBuilder::ModuleConsts { - codegen_depth: enum_codegen_depth + 1, module_name: name, module_items: vec![type_definition], } @@ -2839,8 +2806,7 @@ impl<'a> EnumBuilder<'a> { let mut doc = quote! {}; if ctx.options().generate_comments { if let Some(raw_comment) = variant.comment() { - let comment = - comment::preprocess(raw_comment, self.codegen_depth()); + let comment = ctx.options().process_comment(raw_comment); doc = attributes::doc(comment); } } @@ -2851,13 +2817,11 @@ impl<'a> EnumBuilder<'a> { ident, tokens, emitted_any_variants: _, - codegen_depth, } => { let name = ctx.rust_ident(variant_name); EnumBuilder::Rust { attrs, ident, - codegen_depth, tokens: quote! { #tokens #doc @@ -2918,7 +2882,6 @@ impl<'a> EnumBuilder<'a> { self } EnumBuilder::ModuleConsts { - codegen_depth, module_name, mut module_items, } => { @@ -2932,7 +2895,6 @@ impl<'a> EnumBuilder<'a> { EnumBuilder::ModuleConsts { module_name, module_items, - codegen_depth, } } } @@ -3211,13 +3173,7 @@ impl CodeGenerator for Enum { let repr = repr.to_rust_ty_or_opaque(ctx, item); - let mut builder = EnumBuilder::new( - &name, - attrs, - repr, - variation, - item.codegen_depth(ctx), - ); + let mut builder = EnumBuilder::new(&name, attrs, repr, variation); // A map where we keep a value -> variant relation. let mut seen_values = HashMap::<_, Ident>::default(); diff --git a/bindgen/ir/comment.rs b/bindgen/ir/comment.rs index c96e3ebb9e..3eb17aacb9 100644 --- a/bindgen/ir/comment.rs +++ b/bindgen/ir/comment.rs @@ -12,10 +12,10 @@ enum Kind { } /// Preprocesses a C/C++ comment so that it is a valid Rust comment. -pub fn preprocess(comment: &str, indent: usize) -> String { +pub fn preprocess(comment: &str) -> String { match self::kind(comment) { - Some(Kind::SingleLines) => preprocess_single_lines(comment, indent), - Some(Kind::MultiLine) => preprocess_multi_line(comment, indent), + Some(Kind::SingleLines) => preprocess_single_lines(comment), + Some(Kind::MultiLine) => preprocess_multi_line(comment), None => comment.to_owned(), } } @@ -31,56 +31,34 @@ fn kind(comment: &str) -> Option { } } -fn make_indent(indent: usize) -> String { - const RUST_INDENTATION: usize = 4; - " ".repeat(indent * RUST_INDENTATION) -} - /// Preprocesses multiple single line comments. /// /// Handles lines starting with both `//` and `///`. -fn preprocess_single_lines(comment: &str, indent: usize) -> String { +fn preprocess_single_lines(comment: &str) -> 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().trim_start_matches('/')) - .map(|l| { - let indent = if is_first { "" } else { &*indent }; - is_first = false; - format!("{}///{}", indent, l) - }) .collect(); lines.join("\n") } -fn preprocess_multi_line(comment: &str, indent: usize) -> String { +fn preprocess_multi_line(comment: &str) -> String { let comment = comment .trim_start_matches('/') .trim_end_matches('/') .trim_end_matches('*'); - 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_start_matches('*').trim_start_matches('!')) .skip_while(|line| line.trim().is_empty()) // Skip the first empty lines. - .map(|line| { - let indent = if is_first { "" } else { &*indent }; - is_first = false; - format!("{}///{}", indent, line) - }) .collect(); // Remove the trailing line corresponding to the `*/`. - if lines - .last() - .map_or(false, |l| l.trim().is_empty() || l.trim() == "///") - { + if lines.last().map_or(false, |l| l.trim().is_empty()) { lines.pop(); } @@ -99,21 +77,24 @@ mod test { #[test] fn processes_single_lines_correctly() { - assert_eq!(preprocess("/// hello", 0), "/// hello"); - assert_eq!(preprocess("// hello", 0), "/// hello"); - assert_eq!(preprocess("// hello", 0), "/// hello"); + assert_eq!(preprocess("///"), ""); + assert_eq!(preprocess("/// hello"), " hello"); + assert_eq!(preprocess("// hello"), " hello"); + assert_eq!(preprocess("// hello"), " hello"); } #[test] fn processes_multi_lines_correctly() { + assert_eq!(preprocess("/**/"), ""); + assert_eq!( - preprocess("/** hello \n * world \n * foo \n */", 0), - "/// hello\n/// world\n/// foo" + preprocess("/** hello \n * world \n * foo \n */"), + " hello\n world\n foo" ); assert_eq!( - preprocess("/**\nhello\n*world\n*foo\n*/", 0), - "///hello\n///world\n///foo" + preprocess("/**\nhello\n*world\n*foo\n*/"), + "hello\nworld\nfoo" ); } } diff --git a/bindgen/ir/item.rs b/bindgen/ir/item.rs index 446d78bda5..5e9aff9102 100644 --- a/bindgen/ir/item.rs +++ b/bindgen/ir/item.rs @@ -3,7 +3,6 @@ use super::super::codegen::{EnumVariation, CONSTIFIED_ENUM_MODULE_REPR_NAME}; use super::analysis::{HasVtable, HasVtableResult, Sizedness, SizednessResult}; use super::annotations::Annotations; -use super::comment; use super::comp::{CompKind, MethodKind}; use super::context::{BindgenContext, ItemId, PartialType, TypeId}; use super::derive::{ @@ -515,9 +514,9 @@ impl Item { return None; } - self.comment.as_ref().map(|comment| { - comment::preprocess(comment, self.codegen_depth(ctx)) - }) + self.comment + .as_ref() + .map(|comment| ctx.options().process_comment(comment)) } /// What kind of item is this? diff --git a/bindgen/lib.rs b/bindgen/lib.rs index bfc22bb6b4..d281e94967 100644 --- a/bindgen/lib.rs +++ b/bindgen/lib.rs @@ -78,6 +78,8 @@ doc_mod!(ir, ir_docs); doc_mod!(parse, parse_docs); doc_mod!(regex_set, regex_set_docs); +use ir::comment; + pub use crate::codegen::{ AliasVariation, EnumVariation, MacroTypeVariation, NonCopyUnionStyle, }; @@ -2176,6 +2178,14 @@ impl BindgenOptions { .flat_map(|cb| f(cb.as_ref())) .collect() } + + fn process_comment(&self, comment: &str) -> String { + let comment = comment::preprocess(comment); + self.parse_callbacks + .last() + .and_then(|cb| cb.process_comment(&comment)) + .unwrap_or(comment) + } } impl Default for BindgenOptions {