Skip to content

Commit

Permalink
Intelligently convert C/C++ comments to Rust
Browse files Browse the repository at this point in the history
With this change, we can correctly parse C++ block comments.

```
/**
 * Does a thing
 *
 * More documentation. This test does something
 * useful.
 */
```

into

```
/// Does a thing
///
/// More documentation. This test does something
/// useful.
```

Fixes rust-lang#426.
  • Loading branch information
dylanmckay committed Jul 6, 2017
1 parent 78e7546 commit 37f5d85
Show file tree
Hide file tree
Showing 25 changed files with 464 additions and 458 deletions.
1 change: 1 addition & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod codegen {

quasi_codegen::expand(&src, &dst).unwrap();
println!("cargo:rerun-if-changed=src/codegen/mod.rs");
println!("cargo:rerun-if-changed=src/codegen/comment.rs");
println!("cargo:rerun-if-changed=src/codegen/error.rs");
println!("cargo:rerun-if-changed=src/codegen/helpers.rs");
println!("cargo:rerun-if-changed=src/codegen/struct_layout.rs");
Expand Down
94 changes: 94 additions & 0 deletions src/codegen/comment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/// The type of a comment.
#[derive(Debug, PartialEq, Eq)]
enum Kind {
/// A `///` comment, or something of the like.
/// All lines in a comment should start with the same symbol.
SingleLines,
/// A `/**` comment, where each other line can start with `*` and the
/// entire block ends with `*/`.
MultiLine,
}

/// Checks if a comment should be emitted as a Rust doc comment.
pub fn is_doc(comment: &str) -> bool {
self::kind(comment).is_some()
}

/// Gets the kind of the doc comment, if it is one.
fn kind(comment: &str) -> Option<Kind> {
if comment.starts_with("/**") {
Some(Kind::MultiLine)
} else if comment.starts_with("///") {
Some(Kind::SingleLines)
} else {
None
}
}

/// Preprocesses a C/C++ comment so that it is a valid Rust comment.
pub fn preprocess(comment: &str) -> String {
match self::kind(comment) {
Some(Kind::SingleLines) => preprocess_single_lines(comment),
Some(Kind::MultiLine) => preprocess_multi_line(comment),
None => panic!("comment is not a doc comment"),
}
}

fn preprocess_single_lines(comment: &str) -> String {
assert!(comment.starts_with("///"), "comment is not single line");
// We don't need to amend the comment because each line already
// starts with `///`.
comment.to_owned()
}

fn preprocess_multi_line(comment: &str) -> String {
let comment = comment.trim_left_matches("/")
.trim_right_matches("/")
.trim_right_matches("*")
.trim();

// Strip any potential `*` characters preceding each line.
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))
.collect();

// Remove the trailing `*/`.
let last_idx = lines.len() - 1;
if lines[last_idx].is_empty() {
lines.remove(last_idx);
}

lines.join("\n")
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn ignores_non_doc_comments() {
assert_eq!(kind("// hello"), None);
}

#[test]
fn picks_up_single_and_multi_line_doc_comments() {
assert_eq!(kind("/// hello"), Some(Kind::SingleLines));
assert_eq!(kind("/** world */"), Some(Kind::MultiLine));
}

#[test]
fn processes_single_lines_correctly() {
assert_eq!(preprocess("/// hello"), "/// hello");
}

#[test]
fn processes_multi_lines_correctly() {
assert_eq!(preprocess("/** hello \n * world \n * foo \n */"),
"/// hello\n/// world\n/// foo");

assert_eq!(preprocess("/**\nhello\n*world\n*foo\n*/"),
"/// hello\n/// world\n/// foo");
}
}
23 changes: 18 additions & 5 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod error;
mod helpers;
mod comment;
pub mod struct_layout;

use self::helpers::{BlobTyBuilder, attributes};
Expand Down Expand Up @@ -618,7 +619,10 @@ impl CodeGenerator for Type {

if ctx.options().generate_comments {
if let Some(comment) = item.comment() {
typedef = typedef.attr().doc(comment);
if comment::is_doc(comment) {
typedef = typedef.attr()
.doc(&comment::preprocess(comment)[..]);
}
}
}

Expand Down Expand Up @@ -946,7 +950,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 comment::is_doc(comment) {
attrs.push(attributes::doc(&comment::preprocess(comment)[..]));
}
}
}

Expand Down Expand Up @@ -1411,7 +1417,9 @@ impl CodeGenerator for CompInfo {
let mut needs_default_impl = false;
if ctx.options().generate_comments {
if let Some(comment) = item.comment() {
attributes.push(attributes::doc(comment));
if comment::is_doc(comment) {
attributes.push(attributes::doc(&comment::preprocess(comment)[..]));
}
}
}
if self.packed() {
Expand Down Expand Up @@ -2359,7 +2367,10 @@ impl CodeGenerator for Enum {

if ctx.options().generate_comments {
if let Some(comment) = item.comment() {
builder = builder.with_attr(attributes::doc(comment));
if comment::is_doc(comment) {
builder = builder.with_attr(
attributes::doc(&comment::preprocess(comment)[..]));
}
}
}

Expand Down Expand Up @@ -3061,7 +3072,9 @@ impl CodeGenerator for Function {

if ctx.options().generate_comments {
if let Some(comment) = item.comment() {
attributes.push(attributes::doc(comment));
if comment::is_doc(comment) {
attributes.push(attributes::doc(&comment::preprocess(comment)[..]));
}
}
}

Expand Down
22 changes: 11 additions & 11 deletions tests/expectations/tests/accessors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
#[derive(Debug, Default, Copy)]
pub struct SomeAccessors {
pub mNoAccessor: ::std::os::raw::c_int,
/** <div rustbindgen accessor></div> */
/// <div rustbindgen accessor></div>
pub mBothAccessors: ::std::os::raw::c_int,
/** <div rustbindgen accessor="unsafe"></div> */
/// <div rustbindgen accessor="unsafe"></div>
pub mUnsafeAccessors: ::std::os::raw::c_int,
/** <div rustbindgen accessor="immutable"></div> */
/// <div rustbindgen accessor="immutable"></div>
pub mImmutableAccessor: ::std::os::raw::c_int,
}
#[test]
Expand Down Expand Up @@ -68,7 +68,7 @@ impl SomeAccessors {
&self.mImmutableAccessor
}
}
/** <div rustbindgen accessor></div> */
/// <div rustbindgen accessor></div>
#[repr(C)]
#[derive(Debug, Default, Copy)]
pub struct AllAccessors {
Expand Down Expand Up @@ -114,7 +114,7 @@ impl AllAccessors {
&mut self.mAlsoBothAccessors
}
}
/** <div rustbindgen accessor="unsafe"></div> */
/// <div rustbindgen accessor="unsafe"></div>
#[repr(C)]
#[derive(Debug, Default, Copy)]
pub struct AllUnsafeAccessors {
Expand Down Expand Up @@ -162,16 +162,16 @@ impl AllUnsafeAccessors {
&mut self.mAlsoBothAccessors
}
}
/** <div rustbindgen accessor></div> */
/// <div rustbindgen accessor></div>
#[repr(C)]
#[derive(Debug, Default, Copy)]
pub struct ContradictAccessors {
pub mBothAccessors: ::std::os::raw::c_int,
/** <div rustbindgen accessor="false"></div> */
/// <div rustbindgen accessor="false"></div>
pub mNoAccessors: ::std::os::raw::c_int,
/** <div rustbindgen accessor="unsafe"></div> */
/// <div rustbindgen accessor="unsafe"></div>
pub mUnsafeAccessors: ::std::os::raw::c_int,
/** <div rustbindgen accessor="immutable"></div> */
/// <div rustbindgen accessor="immutable"></div>
pub mImmutableAccessor: ::std::os::raw::c_int,
}
#[test]
Expand Down Expand Up @@ -229,7 +229,7 @@ impl ContradictAccessors {
&self.mImmutableAccessor
}
}
/** <div rustbindgen accessor replaces="Replaced"></div> */
/// <div rustbindgen accessor replaces="Replaced"></div>
#[repr(C)]
#[derive(Debug, Default, Copy)]
pub struct Replaced {
Expand Down Expand Up @@ -258,7 +258,7 @@ impl Replaced {
&mut self.mAccessor
}
}
/** <div rustbindgen accessor></div> */
/// <div rustbindgen accessor></div>
#[repr(C)]
#[derive(Debug, Default, Copy)]
pub struct Wrapper {
Expand Down
4 changes: 1 addition & 3 deletions tests/expectations/tests/annotation_hide.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]


/**
* <div rustbindgen opaque></div>
*/
/// <div rustbindgen opaque></div>
#[repr(C)]
#[derive(Debug, Default, Copy)]
pub struct D {
Expand Down
4 changes: 1 addition & 3 deletions tests/expectations/tests/class_use_as.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]


/**
* <div rustbindgen="true" replaces="whatever"></div>
*/
/// <div rustbindgen="true" replaces="whatever"></div>
#[repr(C)]
#[derive(Debug, Default, Copy)]
pub struct whatever {
Expand Down
12 changes: 6 additions & 6 deletions tests/expectations/tests/layout_align.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ impl <T> ::std::marker::Copy for __IncompleteArrayField<T> { }
#[repr(C)]
#[derive(Debug, Copy)]
pub struct rte_kni_fifo {
/**< Next position to be written*/
/// < Next position to be written
pub write: ::std::os::raw::c_uint,
/**< Next position to be read */
/// < Next position to be read
pub read: ::std::os::raw::c_uint,
/**< Circular buffer length */
/// < Circular buffer length
pub len: ::std::os::raw::c_uint,
/**< Pointer size - for 32/64 bit OS */
/// < Pointer size - for 32/64 bit OS
pub elem_size: ::std::os::raw::c_uint,
/**< The buffer contains mbuf pointers */
/// < The buffer contains mbuf pointers
pub buffer: __IncompleteArrayField<*mut ::std::os::raw::c_void>,
pub __bindgen_align: [u64; 0usize],
}
Expand All @@ -68,7 +68,7 @@ impl Default for rte_kni_fifo {
#[repr(C)]
#[derive(Debug, Default, Copy)]
pub struct rte_eth_link {
/**< ETH_SPEED_NUM_ */
/// < ETH_SPEED_NUM_
pub link_speed: u32,
pub _bitfield_1: u8,
pub __bindgen_padding_0: [u8; 3usize],
Expand Down
38 changes: 16 additions & 22 deletions tests/expectations/tests/layout_arp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,19 @@ pub const ARP_OP_REVREQUEST: ::std::os::raw::c_uint = 3;
pub const ARP_OP_REVREPLY: ::std::os::raw::c_uint = 4;
pub const ARP_OP_INVREQUEST: ::std::os::raw::c_uint = 8;
pub const ARP_OP_INVREPLY: ::std::os::raw::c_uint = 9;
/**
* Ethernet address:
* A universally administered address is uniquely assigned to a device by its
* manufacturer. The first three octets (in transmission order) contain the
* Organizationally Unique Identifier (OUI). The following three (MAC-48 and
* EUI-48) octets are assigned by that organization with the only constraint
* of uniqueness.
* A locally administered address is assigned to a device by a network
* administrator and does not contain OUIs.
* See http://standards.ieee.org/regauth/groupmac/tutorial.html
*/
/// Ethernet address:
/// A universally administered address is uniquely assigned to a device by its
/// manufacturer. The first three octets (in transmission order) contain the
/// Organizationally Unique Identifier (OUI). The following three (MAC-48 and
/// EUI-48) octets are assigned by that organization with the only constraint
/// of uniqueness.
/// A locally administered address is assigned to a device by a network
/// administrator and does not contain OUIs.
/// See http://standards.ieee.org/regauth/groupmac/tutorial.html
#[repr(C, packed)]
#[derive(Debug, Default, Copy)]
pub struct ether_addr {
/**< Addr bytes in tx order */
/// < Addr bytes in tx order
pub addr_bytes: [u8; 6usize],
}
#[test]
Expand All @@ -44,19 +42,17 @@ fn bindgen_test_layout_ether_addr() {
impl Clone for ether_addr {
fn clone(&self) -> Self { *self }
}
/**
* ARP header IPv4 payload.
*/
/// ARP header IPv4 payload.
#[repr(C, packed)]
#[derive(Debug, Default, Copy)]
pub struct arp_ipv4 {
/**< sender hardware address */
/// < sender hardware address
pub arp_sha: ether_addr,
/**< sender IP address */
/// < sender IP address
pub arp_sip: u32,
/**< target hardware address */
/// < target hardware address
pub arp_tha: ether_addr,
/**< target IP address */
/// < target IP address
pub arp_tip: u32,
}
#[test]
Expand Down Expand Up @@ -89,9 +85,7 @@ fn bindgen_test_layout_arp_ipv4() {
impl Clone for arp_ipv4 {
fn clone(&self) -> Self { *self }
}
/**
* ARP header.
*/
/// ARP header.
#[repr(C, packed)]
#[derive(Debug, Default, Copy)]
pub struct arp_hdr {
Expand Down
Loading

0 comments on commit 37f5d85

Please sign in to comment.