Skip to content

Commit

Permalink
Merge pull request rust-lang#3002 from lqd/normalize-doc-attributes
Browse files Browse the repository at this point in the history
normalize_doc_attributes option: convert doc attributes to comments
  • Loading branch information
nrc authored Sep 18, 2018
2 parents 2267c2c + 0c73b94 commit 1739041
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 8 deletions.
26 changes: 26 additions & 0 deletions Configurations.md
Original file line number Diff line number Diff line change
Expand Up @@ -2188,6 +2188,32 @@ If you want to format code that requires edition 2018, add the following to your
edition = "2018"
```

## `normalize_doc_attributes`

Convert `#![doc]` and `#[doc]` attributes to `//!` and `///` doc comments.

- **Default value**: `false`
- **Possible values**: `true`, `false`
- **Stable**: No

#### `false` (default):

```rust
#![doc = "Example documentation"]

#[doc = "Example item documentation"]
pub enum Foo {}
```

#### `true`:

```rust
//! Example documentation

/// Example item documentation
pub enum Foo {}
```

## `emit_mode`

Internal option
Expand Down
37 changes: 29 additions & 8 deletions src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

//! Format attributes and meta items.

use comment::{contains_comment, rewrite_doc_comment};
use comment::{contains_comment, rewrite_doc_comment, CommentStyle};
use config::lists::*;
use config::IndentStyle;
use expr::rewrite_literal;
Expand Down Expand Up @@ -350,13 +350,34 @@ impl Rewrite for ast::Attribute {
if contains_comment(snippet) {
return Some(snippet.to_owned());
}
// 1 = `[`
let shape = shape.offset_left(prefix.len() + 1)?;
Some(
self.meta()
.and_then(|meta| meta.rewrite(context, shape))
.map_or_else(|| snippet.to_owned(), |rw| format!("{}[{}]", prefix, rw)),
)

if let Some(ref meta) = self.meta() {
// This attribute is possibly a doc attribute needing normalization to a doc comment
if context.config.normalize_doc_attributes() && meta.check_name("doc") {
if let Some(ref literal) = meta.value_str() {
let comment_style = match self.style {
ast::AttrStyle::Inner => CommentStyle::Doc,
ast::AttrStyle::Outer => CommentStyle::TripleSlash,
};

let doc_comment = format!("{}{}", comment_style.opener(), literal);
return rewrite_doc_comment(
&doc_comment,
shape.comment(context.config),
context.config,
);
}
}

// 1 = `[`
let shape = shape.offset_left(prefix.len() + 1)?;
Some(
meta.rewrite(context, shape)
.map_or_else(|| snippet.to_owned(), |rw| format!("{}[{}]", prefix, rw)),
)
} else {
Some(snippet.to_owned())
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ create_config! {
comment_width: usize, 80, false,
"Maximum length of comments. No effect unless wrap_comments = true";
normalize_comments: bool, false, false, "Convert /* */ comments to // comments where possible";
normalize_doc_attributes: bool, false, false, "Normalize doc attributes as doc comments";
license_template_path: String, String::default(), false,
"Beginning of file must match license template";
format_strings: bool, false, false, "Format string literals where necessary";
Expand Down
1 change: 1 addition & 0 deletions tests/source/attrib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ impl Bar {
/// Blah blah blooo.
/// Blah blah blooo.
#[an_attribute]
#[doc = "an attribute that shouldn't be normalized to a doc comment"]
fn foo(&mut self) -> isize {
}

Expand Down
91 changes: 91 additions & 0 deletions tests/source/doc-attrib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// rustfmt-wrap_comments: true
// rustfmt-normalize_doc_attributes: true

// Only doc = "" attributes should be normalized
#![doc = "Example doc attribute comment"]
#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
html_root_url = "https://doc.rust-lang.org/nightly/",
html_playground_url = "https://play.rust-lang.org/", test(attr(deny(warnings))))]


// Long `#[doc = "..."]`
struct A { #[doc = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] b: i32 }


#[doc = "The `nodes` and `edges` method each return instantiations of `Cow<[T]>` to leave implementers the freedom to create entirely new vectors or to pass back slices into internally owned vectors."]
struct B { b: i32 }


#[doc = "Level 1 comment"]
mod tests {
#[doc = "Level 2 comment"]
impl A {
#[doc = "Level 3 comment"]
fn f() {
#[doc = "Level 4 comment"]
fn g() {
}
}
}
}

struct C {
#[doc = "item doc attrib comment"]
// regular item comment
b: i32,

// regular item comment
#[doc = "item doc attrib comment"]
c: i32,
}

// non-regression test for regular attributes, from #2647
#[cfg(feature = "this_line_is_101_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")]
pub fn foo() {}

// path attrs
#[clippy::bar]
#[clippy::bar=foo]
#[clippy::bar(a, b, c)]
pub fn foo() {}

mod issue_2620 {
#[derive(Debug, StructOpt)]
#[structopt(about = "Display information about the character on FF Logs")]
pub struct Params {
#[structopt(help = "The server the character is on")]
server: String,
#[structopt(help = "The character's first name")]
first_name: String,
#[structopt(help = "The character's last name")]
last_name: String,
#[structopt(
short = "j",
long = "job",
help = "The job to look at",
parse(try_from_str)
)]
job: Option<Job>
}
}

// non-regression test for regular attributes, from #2969
#[cfg(not(all(feature="std",
any(target_os = "linux", target_os = "android",
target_os = "netbsd",
target_os = "dragonfly",
target_os = "haiku",
target_os = "emscripten",
target_os = "solaris",
target_os = "cloudabi",
target_os = "macos", target_os = "ios",
target_os = "freebsd",
target_os = "openbsd", target_os = "bitrig",
target_os = "redox",
target_os = "fuchsia",
windows,
all(target_arch = "wasm32", feature = "stdweb"),
all(target_arch = "wasm32", feature = "wasm-bindgen"),
))))]
type Os = NoSource;
1 change: 1 addition & 0 deletions tests/target/attrib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ impl Bar {
/// Blah blah blooo.
/// Blah blah blooo.
#[an_attribute]
#[doc = "an attribute that shouldn't be normalized to a doc comment"]
fn foo(&mut self) -> isize {}

/// Blah blah bing.
Expand Down
105 changes: 105 additions & 0 deletions tests/target/doc-attrib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// rustfmt-wrap_comments: true
// rustfmt-normalize_doc_attributes: true

// Only doc = "" attributes should be normalized
//! Example doc attribute comment
#![doc(
html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
html_root_url = "https://doc.rust-lang.org/nightly/",
html_playground_url = "https://play.rust-lang.org/",
test(attr(deny(warnings)))
)]

// Long `#[doc = "..."]`
struct A {
/// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
b: i32,
}

/// The `nodes` and `edges` method each return instantiations of `Cow<[T]>` to
/// leave implementers the freedom to create entirely new vectors or to pass
/// back slices into internally owned vectors.
struct B {
b: i32,
}

/// Level 1 comment
mod tests {
/// Level 2 comment
impl A {
/// Level 3 comment
fn f() {
/// Level 4 comment
fn g() {}
}
}
}

struct C {
/// item doc attrib comment
// regular item comment
b: i32,

// regular item comment
/// item doc attrib comment
c: i32,
}

// non-regression test for regular attributes, from #2647
#[cfg(
feature = "this_line_is_101_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
)]
pub fn foo() {}

// path attrs
#[clippy::bar]
#[clippy::bar=foo]
#[clippy::bar(a, b, c)]
pub fn foo() {}

mod issue_2620 {
#[derive(Debug, StructOpt)]
#[structopt(about = "Display information about the character on FF Logs")]
pub struct Params {
#[structopt(help = "The server the character is on")]
server: String,
#[structopt(help = "The character's first name")]
first_name: String,
#[structopt(help = "The character's last name")]
last_name: String,
#[structopt(
short = "j",
long = "job",
help = "The job to look at",
parse(try_from_str)
)]
job: Option<Job>,
}
}

// non-regression test for regular attributes, from #2969
#[cfg(not(all(
feature = "std",
any(
target_os = "linux",
target_os = "android",
target_os = "netbsd",
target_os = "dragonfly",
target_os = "haiku",
target_os = "emscripten",
target_os = "solaris",
target_os = "cloudabi",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "openbsd",
target_os = "bitrig",
target_os = "redox",
target_os = "fuchsia",
windows,
all(target_arch = "wasm32", feature = "stdweb"),
all(target_arch = "wasm32", feature = "wasm-bindgen"),
)
)))]
type Os = NoSource;

0 comments on commit 1739041

Please sign in to comment.