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 3bb248b commit aefffc6
Show file tree
Hide file tree
Showing 30 changed files with 733 additions and 458 deletions.
92 changes: 92 additions & 0 deletions src/ir/comment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/// 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
Loading

0 comments on commit aefffc6

Please sign in to comment.