Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 1739041

Browse files
authored
Merge pull request rust-lang#3002 from lqd/normalize-doc-attributes
normalize_doc_attributes option: convert doc attributes to comments
2 parents 2267c2c + 0c73b94 commit 1739041

File tree

7 files changed

+254
-8
lines changed

7 files changed

+254
-8
lines changed

Configurations.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2188,6 +2188,32 @@ If you want to format code that requires edition 2018, add the following to your
21882188
edition = "2018"
21892189
```
21902190

2191+
## `normalize_doc_attributes`
2192+
2193+
Convert `#![doc]` and `#[doc]` attributes to `//!` and `///` doc comments.
2194+
2195+
- **Default value**: `false`
2196+
- **Possible values**: `true`, `false`
2197+
- **Stable**: No
2198+
2199+
#### `false` (default):
2200+
2201+
```rust
2202+
#![doc = "Example documentation"]
2203+
2204+
#[doc = "Example item documentation"]
2205+
pub enum Foo {}
2206+
```
2207+
2208+
#### `true`:
2209+
2210+
```rust
2211+
//! Example documentation
2212+
2213+
/// Example item documentation
2214+
pub enum Foo {}
2215+
```
2216+
21912217
## `emit_mode`
21922218

21932219
Internal option

src/attr.rs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
//! Format attributes and meta items.
1212
13-
use comment::{contains_comment, rewrite_doc_comment};
13+
use comment::{contains_comment, rewrite_doc_comment, CommentStyle};
1414
use config::lists::*;
1515
use config::IndentStyle;
1616
use expr::rewrite_literal;
@@ -350,13 +350,34 @@ impl Rewrite for ast::Attribute {
350350
if contains_comment(snippet) {
351351
return Some(snippet.to_owned());
352352
}
353-
// 1 = `[`
354-
let shape = shape.offset_left(prefix.len() + 1)?;
355-
Some(
356-
self.meta()
357-
.and_then(|meta| meta.rewrite(context, shape))
358-
.map_or_else(|| snippet.to_owned(), |rw| format!("{}[{}]", prefix, rw)),
359-
)
353+
354+
if let Some(ref meta) = self.meta() {
355+
// This attribute is possibly a doc attribute needing normalization to a doc comment
356+
if context.config.normalize_doc_attributes() && meta.check_name("doc") {
357+
if let Some(ref literal) = meta.value_str() {
358+
let comment_style = match self.style {
359+
ast::AttrStyle::Inner => CommentStyle::Doc,
360+
ast::AttrStyle::Outer => CommentStyle::TripleSlash,
361+
};
362+
363+
let doc_comment = format!("{}{}", comment_style.opener(), literal);
364+
return rewrite_doc_comment(
365+
&doc_comment,
366+
shape.comment(context.config),
367+
context.config,
368+
);
369+
}
370+
}
371+
372+
// 1 = `[`
373+
let shape = shape.offset_left(prefix.len() + 1)?;
374+
Some(
375+
meta.rewrite(context, shape)
376+
.map_or_else(|| snippet.to_owned(), |rw| format!("{}[{}]", prefix, rw)),
377+
)
378+
} else {
379+
Some(snippet.to_owned())
380+
}
360381
}
361382
}
362383
}

src/config/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ create_config! {
4949
comment_width: usize, 80, false,
5050
"Maximum length of comments. No effect unless wrap_comments = true";
5151
normalize_comments: bool, false, false, "Convert /* */ comments to // comments where possible";
52+
normalize_doc_attributes: bool, false, false, "Normalize doc attributes as doc comments";
5253
license_template_path: String, String::default(), false,
5354
"Beginning of file must match license template";
5455
format_strings: bool, false, false, "Format string literals where necessary";

tests/source/attrib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ impl Bar {
3030
/// Blah blah blooo.
3131
/// Blah blah blooo.
3232
#[an_attribute]
33+
#[doc = "an attribute that shouldn't be normalized to a doc comment"]
3334
fn foo(&mut self) -> isize {
3435
}
3536

tests/source/doc-attrib.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// rustfmt-wrap_comments: true
2+
// rustfmt-normalize_doc_attributes: true
3+
4+
// Only doc = "" attributes should be normalized
5+
#![doc = "Example doc attribute comment"]
6+
#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
7+
html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
8+
html_root_url = "https://doc.rust-lang.org/nightly/",
9+
html_playground_url = "https://play.rust-lang.org/", test(attr(deny(warnings))))]
10+
11+
12+
// Long `#[doc = "..."]`
13+
struct A { #[doc = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] b: i32 }
14+
15+
16+
#[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."]
17+
struct B { b: i32 }
18+
19+
20+
#[doc = "Level 1 comment"]
21+
mod tests {
22+
#[doc = "Level 2 comment"]
23+
impl A {
24+
#[doc = "Level 3 comment"]
25+
fn f() {
26+
#[doc = "Level 4 comment"]
27+
fn g() {
28+
}
29+
}
30+
}
31+
}
32+
33+
struct C {
34+
#[doc = "item doc attrib comment"]
35+
// regular item comment
36+
b: i32,
37+
38+
// regular item comment
39+
#[doc = "item doc attrib comment"]
40+
c: i32,
41+
}
42+
43+
// non-regression test for regular attributes, from #2647
44+
#[cfg(feature = "this_line_is_101_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")]
45+
pub fn foo() {}
46+
47+
// path attrs
48+
#[clippy::bar]
49+
#[clippy::bar=foo]
50+
#[clippy::bar(a, b, c)]
51+
pub fn foo() {}
52+
53+
mod issue_2620 {
54+
#[derive(Debug, StructOpt)]
55+
#[structopt(about = "Display information about the character on FF Logs")]
56+
pub struct Params {
57+
#[structopt(help = "The server the character is on")]
58+
server: String,
59+
#[structopt(help = "The character's first name")]
60+
first_name: String,
61+
#[structopt(help = "The character's last name")]
62+
last_name: String,
63+
#[structopt(
64+
short = "j",
65+
long = "job",
66+
help = "The job to look at",
67+
parse(try_from_str)
68+
)]
69+
job: Option<Job>
70+
}
71+
}
72+
73+
// non-regression test for regular attributes, from #2969
74+
#[cfg(not(all(feature="std",
75+
any(target_os = "linux", target_os = "android",
76+
target_os = "netbsd",
77+
target_os = "dragonfly",
78+
target_os = "haiku",
79+
target_os = "emscripten",
80+
target_os = "solaris",
81+
target_os = "cloudabi",
82+
target_os = "macos", target_os = "ios",
83+
target_os = "freebsd",
84+
target_os = "openbsd", target_os = "bitrig",
85+
target_os = "redox",
86+
target_os = "fuchsia",
87+
windows,
88+
all(target_arch = "wasm32", feature = "stdweb"),
89+
all(target_arch = "wasm32", feature = "wasm-bindgen"),
90+
))))]
91+
type Os = NoSource;

tests/target/attrib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ impl Bar {
3333
/// Blah blah blooo.
3434
/// Blah blah blooo.
3535
#[an_attribute]
36+
#[doc = "an attribute that shouldn't be normalized to a doc comment"]
3637
fn foo(&mut self) -> isize {}
3738

3839
/// Blah blah bing.

tests/target/doc-attrib.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// rustfmt-wrap_comments: true
2+
// rustfmt-normalize_doc_attributes: true
3+
4+
// Only doc = "" attributes should be normalized
5+
//! Example doc attribute comment
6+
#![doc(
7+
html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
8+
html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
9+
html_root_url = "https://doc.rust-lang.org/nightly/",
10+
html_playground_url = "https://play.rust-lang.org/",
11+
test(attr(deny(warnings)))
12+
)]
13+
14+
// Long `#[doc = "..."]`
15+
struct A {
16+
/// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
17+
b: i32,
18+
}
19+
20+
/// The `nodes` and `edges` method each return instantiations of `Cow<[T]>` to
21+
/// leave implementers the freedom to create entirely new vectors or to pass
22+
/// back slices into internally owned vectors.
23+
struct B {
24+
b: i32,
25+
}
26+
27+
/// Level 1 comment
28+
mod tests {
29+
/// Level 2 comment
30+
impl A {
31+
/// Level 3 comment
32+
fn f() {
33+
/// Level 4 comment
34+
fn g() {}
35+
}
36+
}
37+
}
38+
39+
struct C {
40+
/// item doc attrib comment
41+
// regular item comment
42+
b: i32,
43+
44+
// regular item comment
45+
/// item doc attrib comment
46+
c: i32,
47+
}
48+
49+
// non-regression test for regular attributes, from #2647
50+
#[cfg(
51+
feature = "this_line_is_101_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
52+
)]
53+
pub fn foo() {}
54+
55+
// path attrs
56+
#[clippy::bar]
57+
#[clippy::bar=foo]
58+
#[clippy::bar(a, b, c)]
59+
pub fn foo() {}
60+
61+
mod issue_2620 {
62+
#[derive(Debug, StructOpt)]
63+
#[structopt(about = "Display information about the character on FF Logs")]
64+
pub struct Params {
65+
#[structopt(help = "The server the character is on")]
66+
server: String,
67+
#[structopt(help = "The character's first name")]
68+
first_name: String,
69+
#[structopt(help = "The character's last name")]
70+
last_name: String,
71+
#[structopt(
72+
short = "j",
73+
long = "job",
74+
help = "The job to look at",
75+
parse(try_from_str)
76+
)]
77+
job: Option<Job>,
78+
}
79+
}
80+
81+
// non-regression test for regular attributes, from #2969
82+
#[cfg(not(all(
83+
feature = "std",
84+
any(
85+
target_os = "linux",
86+
target_os = "android",
87+
target_os = "netbsd",
88+
target_os = "dragonfly",
89+
target_os = "haiku",
90+
target_os = "emscripten",
91+
target_os = "solaris",
92+
target_os = "cloudabi",
93+
target_os = "macos",
94+
target_os = "ios",
95+
target_os = "freebsd",
96+
target_os = "openbsd",
97+
target_os = "bitrig",
98+
target_os = "redox",
99+
target_os = "fuchsia",
100+
windows,
101+
all(target_arch = "wasm32", feature = "stdweb"),
102+
all(target_arch = "wasm32", feature = "wasm-bindgen"),
103+
)
104+
)))]
105+
type Os = NoSource;

0 commit comments

Comments
 (0)