Skip to content

Commit

Permalink
Add ParseCallbacks::process_comment (#2347)
Browse files Browse the repository at this point in the history
This method can be used to process comments and replace them with whatever the user wants.
  • Loading branch information
pvdrz authored Nov 22, 2022
1 parent 34f0bac commit f160d11
Show file tree
Hide file tree
Showing 7 changed files with 45 additions and 90 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 5 additions & 0 deletions bindgen/callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ pub trait ParseCallbacks: fmt::Debug {
fn add_derives(&self, _info: &DeriveInfo<'_>) -> Vec<String> {
vec![]
}

/// Process a source code comment.
fn process_comment(&self, _comment: &str) -> Option<String> {
None
}
}

/// Relevant information about a type to which new derive attributes will be added using
Expand Down
8 changes: 5 additions & 3 deletions bindgen/codegen/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
52 changes: 4 additions & 48 deletions bindgen/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -1282,7 +1279,6 @@ impl<'a> FieldCodegen<'a> for Field {
data.codegen(
ctx,
fields_should_be_private,
codegen_depth,
accessor_kind,
parent,
result,
Expand All @@ -1296,7 +1292,6 @@ impl<'a> FieldCodegen<'a> for Field {
unit.codegen(
ctx,
fields_should_be_private,
codegen_depth,
accessor_kind,
parent,
result,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -1630,7 +1622,6 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit {
bf.codegen(
ctx,
fields_should_be_private,
codegen_depth,
accessor_kind,
parent,
result,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -1894,7 +1883,6 @@ impl CodeGenerator for CompInfo {
field.codegen(
ctx,
fields_should_be_private,
codegen_depth,
struct_accessor_kind,
self,
result,
Expand Down Expand Up @@ -2703,41 +2691,27 @@ impl std::str::FromStr for EnumVariation {
/// A helper type to construct different enum variations.
enum EnumBuilder<'a> {
Rust {
codegen_depth: usize,
attrs: Vec<proc_macro2::TokenStream>,
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,
is_global: bool,
},
Consts {
variants: Vec<proc_macro2::TokenStream>,
codegen_depth: usize,
},
ModuleConsts {
codegen_depth: usize,
module_name: &'a str,
module_items: Vec<proc_macro2::TokenStream>,
},
}

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 { .. })
Expand All @@ -2750,7 +2724,6 @@ impl<'a> EnumBuilder<'a> {
mut attrs: Vec<proc_macro2::TokenStream>,
repr: proc_macro2::TokenStream,
enum_variation: EnumVariation,
enum_codegen_depth: usize,
) -> Self {
let ident = Ident::new(name, Span::call_site());

Expand All @@ -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 )*
Expand All @@ -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,
Expand All @@ -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 => {
Expand All @@ -2807,7 +2775,6 @@ impl<'a> EnumBuilder<'a> {
};

EnumBuilder::ModuleConsts {
codegen_depth: enum_codegen_depth + 1,
module_name: name,
module_items: vec![type_definition],
}
Expand Down Expand Up @@ -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);
}
}
Expand All @@ -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
Expand Down Expand Up @@ -2918,7 +2882,6 @@ impl<'a> EnumBuilder<'a> {
self
}
EnumBuilder::ModuleConsts {
codegen_depth,
module_name,
mut module_items,
} => {
Expand All @@ -2932,7 +2895,6 @@ impl<'a> EnumBuilder<'a> {
EnumBuilder::ModuleConsts {
module_name,
module_items,
codegen_depth,
}
}
}
Expand Down Expand Up @@ -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();
Expand Down
51 changes: 16 additions & 35 deletions bindgen/ir/comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
}
}
Expand All @@ -31,56 +31,34 @@ fn kind(comment: &str) -> Option<Kind> {
}
}

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();
}

Expand All @@ -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"
);
}
}
Loading

0 comments on commit f160d11

Please sign in to comment.