Skip to content

Commit 37f5d85

Browse files
committed
Intelligently convert C/C++ comments to Rust
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.
1 parent 78e7546 commit 37f5d85

25 files changed

+464
-458
lines changed

build.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ mod codegen {
1212

1313
quasi_codegen::expand(&src, &dst).unwrap();
1414
println!("cargo:rerun-if-changed=src/codegen/mod.rs");
15+
println!("cargo:rerun-if-changed=src/codegen/comment.rs");
1516
println!("cargo:rerun-if-changed=src/codegen/error.rs");
1617
println!("cargo:rerun-if-changed=src/codegen/helpers.rs");
1718
println!("cargo:rerun-if-changed=src/codegen/struct_layout.rs");

src/codegen/comment.rs

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/// The type of a comment.
2+
#[derive(Debug, PartialEq, Eq)]
3+
enum Kind {
4+
/// A `///` comment, or something of the like.
5+
/// All lines in a comment should start with the same symbol.
6+
SingleLines,
7+
/// A `/**` comment, where each other line can start with `*` and the
8+
/// entire block ends with `*/`.
9+
MultiLine,
10+
}
11+
12+
/// Checks if a comment should be emitted as a Rust doc comment.
13+
pub fn is_doc(comment: &str) -> bool {
14+
self::kind(comment).is_some()
15+
}
16+
17+
/// Gets the kind of the doc comment, if it is one.
18+
fn kind(comment: &str) -> Option<Kind> {
19+
if comment.starts_with("/**") {
20+
Some(Kind::MultiLine)
21+
} else if comment.starts_with("///") {
22+
Some(Kind::SingleLines)
23+
} else {
24+
None
25+
}
26+
}
27+
28+
/// Preprocesses a C/C++ comment so that it is a valid Rust comment.
29+
pub fn preprocess(comment: &str) -> String {
30+
match self::kind(comment) {
31+
Some(Kind::SingleLines) => preprocess_single_lines(comment),
32+
Some(Kind::MultiLine) => preprocess_multi_line(comment),
33+
None => panic!("comment is not a doc comment"),
34+
}
35+
}
36+
37+
fn preprocess_single_lines(comment: &str) -> String {
38+
assert!(comment.starts_with("///"), "comment is not single line");
39+
// We don't need to amend the comment because each line already
40+
// starts with `///`.
41+
comment.to_owned()
42+
}
43+
44+
fn preprocess_multi_line(comment: &str) -> String {
45+
let comment = comment.trim_left_matches("/")
46+
.trim_right_matches("/")
47+
.trim_right_matches("*")
48+
.trim();
49+
50+
// Strip any potential `*` characters preceding each line.
51+
let mut lines: Vec<_> = comment.lines()
52+
.map(|line| line.trim().trim_left_matches('*').trim())
53+
.skip_while(|line| line.is_empty()) // Skip the first empty lines.
54+
.map(|line| format!("/// {}", line))
55+
.collect();
56+
57+
// Remove the trailing `*/`.
58+
let last_idx = lines.len() - 1;
59+
if lines[last_idx].is_empty() {
60+
lines.remove(last_idx);
61+
}
62+
63+
lines.join("\n")
64+
}
65+
66+
#[cfg(test)]
67+
mod test {
68+
use super::*;
69+
70+
#[test]
71+
fn ignores_non_doc_comments() {
72+
assert_eq!(kind("// hello"), None);
73+
}
74+
75+
#[test]
76+
fn picks_up_single_and_multi_line_doc_comments() {
77+
assert_eq!(kind("/// hello"), Some(Kind::SingleLines));
78+
assert_eq!(kind("/** world */"), Some(Kind::MultiLine));
79+
}
80+
81+
#[test]
82+
fn processes_single_lines_correctly() {
83+
assert_eq!(preprocess("/// hello"), "/// hello");
84+
}
85+
86+
#[test]
87+
fn processes_multi_lines_correctly() {
88+
assert_eq!(preprocess("/** hello \n * world \n * foo \n */"),
89+
"/// hello\n/// world\n/// foo");
90+
91+
assert_eq!(preprocess("/**\nhello\n*world\n*foo\n*/"),
92+
"/// hello\n/// world\n/// foo");
93+
}
94+
}

src/codegen/mod.rs

+18-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod error;
22
mod helpers;
3+
mod comment;
34
pub mod struct_layout;
45

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

619620
if ctx.options().generate_comments {
620621
if let Some(comment) = item.comment() {
621-
typedef = typedef.attr().doc(comment);
622+
if comment::is_doc(comment) {
623+
typedef = typedef.attr()
624+
.doc(&comment::preprocess(comment)[..]);
625+
}
622626
}
623627
}
624628

@@ -946,7 +950,9 @@ impl<'a> FieldCodegen<'a> for FieldData {
946950
let mut attrs = vec![];
947951
if ctx.options().generate_comments {
948952
if let Some(comment) = self.comment() {
949-
attrs.push(attributes::doc(comment));
953+
if comment::is_doc(comment) {
954+
attrs.push(attributes::doc(&comment::preprocess(comment)[..]));
955+
}
950956
}
951957
}
952958

@@ -1411,7 +1417,9 @@ impl CodeGenerator for CompInfo {
14111417
let mut needs_default_impl = false;
14121418
if ctx.options().generate_comments {
14131419
if let Some(comment) = item.comment() {
1414-
attributes.push(attributes::doc(comment));
1420+
if comment::is_doc(comment) {
1421+
attributes.push(attributes::doc(&comment::preprocess(comment)[..]));
1422+
}
14151423
}
14161424
}
14171425
if self.packed() {
@@ -2359,7 +2367,10 @@ impl CodeGenerator for Enum {
23592367

23602368
if ctx.options().generate_comments {
23612369
if let Some(comment) = item.comment() {
2362-
builder = builder.with_attr(attributes::doc(comment));
2370+
if comment::is_doc(comment) {
2371+
builder = builder.with_attr(
2372+
attributes::doc(&comment::preprocess(comment)[..]));
2373+
}
23632374
}
23642375
}
23652376

@@ -3061,7 +3072,9 @@ impl CodeGenerator for Function {
30613072

30623073
if ctx.options().generate_comments {
30633074
if let Some(comment) = item.comment() {
3064-
attributes.push(attributes::doc(comment));
3075+
if comment::is_doc(comment) {
3076+
attributes.push(attributes::doc(&comment::preprocess(comment)[..]));
3077+
}
30653078
}
30663079
}
30673080

tests/expectations/tests/accessors.rs

+11-11
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
#[derive(Debug, Default, Copy)]
99
pub struct SomeAccessors {
1010
pub mNoAccessor: ::std::os::raw::c_int,
11-
/** <div rustbindgen accessor></div> */
11+
/// <div rustbindgen accessor></div>
1212
pub mBothAccessors: ::std::os::raw::c_int,
13-
/** <div rustbindgen accessor="unsafe"></div> */
13+
/// <div rustbindgen accessor="unsafe"></div>
1414
pub mUnsafeAccessors: ::std::os::raw::c_int,
15-
/** <div rustbindgen accessor="immutable"></div> */
15+
/// <div rustbindgen accessor="immutable"></div>
1616
pub mImmutableAccessor: ::std::os::raw::c_int,
1717
}
1818
#[test]
@@ -68,7 +68,7 @@ impl SomeAccessors {
6868
&self.mImmutableAccessor
6969
}
7070
}
71-
/** <div rustbindgen accessor></div> */
71+
/// <div rustbindgen accessor></div>
7272
#[repr(C)]
7373
#[derive(Debug, Default, Copy)]
7474
pub struct AllAccessors {
@@ -114,7 +114,7 @@ impl AllAccessors {
114114
&mut self.mAlsoBothAccessors
115115
}
116116
}
117-
/** <div rustbindgen accessor="unsafe"></div> */
117+
/// <div rustbindgen accessor="unsafe"></div>
118118
#[repr(C)]
119119
#[derive(Debug, Default, Copy)]
120120
pub struct AllUnsafeAccessors {
@@ -162,16 +162,16 @@ impl AllUnsafeAccessors {
162162
&mut self.mAlsoBothAccessors
163163
}
164164
}
165-
/** <div rustbindgen accessor></div> */
165+
/// <div rustbindgen accessor></div>
166166
#[repr(C)]
167167
#[derive(Debug, Default, Copy)]
168168
pub struct ContradictAccessors {
169169
pub mBothAccessors: ::std::os::raw::c_int,
170-
/** <div rustbindgen accessor="false"></div> */
170+
/// <div rustbindgen accessor="false"></div>
171171
pub mNoAccessors: ::std::os::raw::c_int,
172-
/** <div rustbindgen accessor="unsafe"></div> */
172+
/// <div rustbindgen accessor="unsafe"></div>
173173
pub mUnsafeAccessors: ::std::os::raw::c_int,
174-
/** <div rustbindgen accessor="immutable"></div> */
174+
/// <div rustbindgen accessor="immutable"></div>
175175
pub mImmutableAccessor: ::std::os::raw::c_int,
176176
}
177177
#[test]
@@ -229,7 +229,7 @@ impl ContradictAccessors {
229229
&self.mImmutableAccessor
230230
}
231231
}
232-
/** <div rustbindgen accessor replaces="Replaced"></div> */
232+
/// <div rustbindgen accessor replaces="Replaced"></div>
233233
#[repr(C)]
234234
#[derive(Debug, Default, Copy)]
235235
pub struct Replaced {
@@ -258,7 +258,7 @@ impl Replaced {
258258
&mut self.mAccessor
259259
}
260260
}
261-
/** <div rustbindgen accessor></div> */
261+
/// <div rustbindgen accessor></div>
262262
#[repr(C)]
263263
#[derive(Debug, Default, Copy)]
264264
pub struct Wrapper {

tests/expectations/tests/annotation_hide.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44
#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
55

66

7-
/**
8-
* <div rustbindgen opaque></div>
9-
*/
7+
/// <div rustbindgen opaque></div>
108
#[repr(C)]
119
#[derive(Debug, Default, Copy)]
1210
pub struct D {

tests/expectations/tests/class_use_as.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44
#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
55

66

7-
/**
8-
* <div rustbindgen="true" replaces="whatever"></div>
9-
*/
7+
/// <div rustbindgen="true" replaces="whatever"></div>
108
#[repr(C)]
119
#[derive(Debug, Default, Copy)]
1210
pub struct whatever {

tests/expectations/tests/layout_align.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,15 @@ impl <T> ::std::marker::Copy for __IncompleteArrayField<T> { }
4040
#[repr(C)]
4141
#[derive(Debug, Copy)]
4242
pub struct rte_kni_fifo {
43-
/**< Next position to be written*/
43+
/// < Next position to be written
4444
pub write: ::std::os::raw::c_uint,
45-
/**< Next position to be read */
45+
/// < Next position to be read
4646
pub read: ::std::os::raw::c_uint,
47-
/**< Circular buffer length */
47+
/// < Circular buffer length
4848
pub len: ::std::os::raw::c_uint,
49-
/**< Pointer size - for 32/64 bit OS */
49+
/// < Pointer size - for 32/64 bit OS
5050
pub elem_size: ::std::os::raw::c_uint,
51-
/**< The buffer contains mbuf pointers */
51+
/// < The buffer contains mbuf pointers
5252
pub buffer: __IncompleteArrayField<*mut ::std::os::raw::c_void>,
5353
pub __bindgen_align: [u64; 0usize],
5454
}
@@ -68,7 +68,7 @@ impl Default for rte_kni_fifo {
6868
#[repr(C)]
6969
#[derive(Debug, Default, Copy)]
7070
pub struct rte_eth_link {
71-
/**< ETH_SPEED_NUM_ */
71+
/// < ETH_SPEED_NUM_
7272
pub link_speed: u32,
7373
pub _bitfield_1: u8,
7474
pub __bindgen_padding_0: [u8; 3usize],

tests/expectations/tests/layout_arp.rs

+16-22
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,19 @@ pub const ARP_OP_REVREQUEST: ::std::os::raw::c_uint = 3;
1212
pub const ARP_OP_REVREPLY: ::std::os::raw::c_uint = 4;
1313
pub const ARP_OP_INVREQUEST: ::std::os::raw::c_uint = 8;
1414
pub const ARP_OP_INVREPLY: ::std::os::raw::c_uint = 9;
15-
/**
16-
* Ethernet address:
17-
* A universally administered address is uniquely assigned to a device by its
18-
* manufacturer. The first three octets (in transmission order) contain the
19-
* Organizationally Unique Identifier (OUI). The following three (MAC-48 and
20-
* EUI-48) octets are assigned by that organization with the only constraint
21-
* of uniqueness.
22-
* A locally administered address is assigned to a device by a network
23-
* administrator and does not contain OUIs.
24-
* See http://standards.ieee.org/regauth/groupmac/tutorial.html
25-
*/
15+
/// Ethernet address:
16+
/// A universally administered address is uniquely assigned to a device by its
17+
/// manufacturer. The first three octets (in transmission order) contain the
18+
/// Organizationally Unique Identifier (OUI). The following three (MAC-48 and
19+
/// EUI-48) octets are assigned by that organization with the only constraint
20+
/// of uniqueness.
21+
/// A locally administered address is assigned to a device by a network
22+
/// administrator and does not contain OUIs.
23+
/// See http://standards.ieee.org/regauth/groupmac/tutorial.html
2624
#[repr(C, packed)]
2725
#[derive(Debug, Default, Copy)]
2826
pub struct ether_addr {
29-
/**< Addr bytes in tx order */
27+
/// < Addr bytes in tx order
3028
pub addr_bytes: [u8; 6usize],
3129
}
3230
#[test]
@@ -44,19 +42,17 @@ fn bindgen_test_layout_ether_addr() {
4442
impl Clone for ether_addr {
4543
fn clone(&self) -> Self { *self }
4644
}
47-
/**
48-
* ARP header IPv4 payload.
49-
*/
45+
/// ARP header IPv4 payload.
5046
#[repr(C, packed)]
5147
#[derive(Debug, Default, Copy)]
5248
pub struct arp_ipv4 {
53-
/**< sender hardware address */
49+
/// < sender hardware address
5450
pub arp_sha: ether_addr,
55-
/**< sender IP address */
51+
/// < sender IP address
5652
pub arp_sip: u32,
57-
/**< target hardware address */
53+
/// < target hardware address
5854
pub arp_tha: ether_addr,
59-
/**< target IP address */
55+
/// < target IP address
6056
pub arp_tip: u32,
6157
}
6258
#[test]
@@ -89,9 +85,7 @@ fn bindgen_test_layout_arp_ipv4() {
8985
impl Clone for arp_ipv4 {
9086
fn clone(&self) -> Self { *self }
9187
}
92-
/**
93-
* ARP header.
94-
*/
88+
/// ARP header.
9589
#[repr(C, packed)]
9690
#[derive(Debug, Default, Copy)]
9791
pub struct arp_hdr {

0 commit comments

Comments
 (0)