Skip to content

Intelligently convert C/C++ comments to Rust #791

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions src/ir/comment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//! Utilities for manipulating C/C++ comments.

/// 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,
}
/// Preprocesses a C/C++ comment so that it is a valid Rust comment.
pub fn preprocess(comment: String) -> String {
match self::kind(&comment) {
Some(Kind::SingleLines) => preprocess_single_lines(&comment),
Some(Kind::MultiLine) => preprocess_multi_line(&comment),
None => comment.to_owned(),
}
}

/// 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 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");

let lines: Vec<_> = comment.lines()
.map(|l| l.trim_left_matches('/').trim())
.map(|l| format!("/// {}", l).trim().to_owned())
.collect();
lines.join("\n")
}

fn preprocess_multi_line(comment: &str) -> String {
let comment = comment.trim_left_matches('/')
.trim_left_matches("*")
.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).trim().to_owned())
.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 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".to_owned()), "/// hello");
assert_eq!(preprocess("// hello".to_owned()), "/// hello");
}

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

assert_eq!(preprocess("/**\nhello\n*world\n*foo\n*/".to_owned()),
"/// hello\n/// world\n/// foo");
}
}
3 changes: 2 additions & 1 deletion src/ir/comp.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! 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;
Expand Down Expand Up @@ -1101,7 +1102,7 @@ impl CompInfo {
Some(potential_id),
ctx);

let comment = cur.raw_comment();
let comment = cur.raw_comment().map(comment::preprocess);
let annotations = Annotations::new(&cur);
let name = cur.spelling();
let is_mutable = cursor.is_mutable_field();
Expand Down
3 changes: 2 additions & 1 deletion src/ir/enum_ty.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Intermediate representation for C/C++ enumerations.

use super::comment;
use super::context::{BindgenContext, ItemId};
use super::item::Item;
use super::ty::TypeKind;
Expand Down Expand Up @@ -113,7 +114,7 @@ impl Enum {
})
});

let comment = cursor.raw_comment();
let comment = cursor.raw_comment().map(comment::preprocess);
variants.push(EnumVariant::new(name,
comment,
val,
Expand Down
3 changes: 2 additions & 1 deletion src/ir/function.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Intermediate representation for C/C++ functions and methods.

use super::comment;
use super::context::{BindgenContext, ItemId};
use super::dot::DotAttributes;
use super::item::Item;
Expand Down Expand Up @@ -405,7 +406,7 @@ impl ClangSubItemParser for Function {
mangled_name = None;
}

let comment = cursor.raw_comment();
let comment = cursor.raw_comment().map(comment::preprocess);

let function = Self::new(name, mangled_name, sig, comment);
Ok(ParseResult::New(function, Some(cursor)))
Expand Down
6 changes: 4 additions & 2 deletions src/ir/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use super::super::codegen::CONSTIFIED_ENUM_MODULE_REPR_NAME;
use super::annotations::Annotations;
use super::comment;
use super::context::{BindgenContext, ItemId, PartialType};
use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault};
use super::dot::DotAttributes;
Expand Down Expand Up @@ -1001,7 +1002,7 @@ impl ClangItemParser for Item {
return Err(ParseError::Continue);
}

let comment = cursor.raw_comment();
let comment = cursor.raw_comment().map(comment::preprocess);
let annotations = Annotations::new(&cursor);

let current_module = ctx.current_module();
Expand Down Expand Up @@ -1207,7 +1208,8 @@ impl ClangItemParser for Item {
};

let comment = decl.raw_comment()
.or_else(|| location.raw_comment());
.or_else(|| location.raw_comment())
.map(comment::preprocess);
let annotations = Annotations::new(&decl)
.or_else(|| Annotations::new(&location));

Expand Down
1 change: 1 addition & 0 deletions src/ir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

pub mod annotations;
pub mod comp;
pub mod comment;
pub mod context;
pub mod derive;
pub mod dot;
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
46 changes: 46 additions & 0 deletions tests/expectations/tests/convert-cpp-comment-to-rust.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* automatically generated by rust-bindgen */


#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]


pub type mbedtls_mpi_uint = ::std::os::raw::c_uint;
/// \brief MPI structure
#[repr(C)]
#[derive(Debug, Copy)]
pub struct mbedtls_mpi {
/// < integer sign
pub s: ::std::os::raw::c_int,
/// < total # of limbs
pub n: ::std::os::raw::c_ulong,
/// < pointer to limbs
pub p: *mut mbedtls_mpi_uint,
}
#[test]
fn bindgen_test_layout_mbedtls_mpi() {
assert_eq!(::std::mem::size_of::<mbedtls_mpi>() , 24usize , concat ! (
"Size of: " , stringify ! ( mbedtls_mpi ) ));
assert_eq! (::std::mem::align_of::<mbedtls_mpi>() , 8usize , concat ! (
"Alignment of " , stringify ! ( mbedtls_mpi ) ));
assert_eq! (unsafe {
& ( * ( 0 as * const mbedtls_mpi ) ) . s as * const _ as usize
} , 0usize , concat ! (
"Alignment of field: " , stringify ! ( mbedtls_mpi ) , "::" ,
stringify ! ( s ) ));
assert_eq! (unsafe {
& ( * ( 0 as * const mbedtls_mpi ) ) . n as * const _ as usize
} , 8usize , concat ! (
"Alignment of field: " , stringify ! ( mbedtls_mpi ) , "::" ,
stringify ! ( n ) ));
assert_eq! (unsafe {
& ( * ( 0 as * const mbedtls_mpi ) ) . p as * const _ as usize
} , 16usize , concat ! (
"Alignment of field: " , stringify ! ( mbedtls_mpi ) , "::" ,
stringify ! ( p ) ));
}
impl Clone for mbedtls_mpi {
fn clone(&self) -> Self { *self }
}
impl Default for mbedtls_mpi {
fn default() -> Self { unsafe { ::std::mem::zeroed() } }
}
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
Loading