Skip to content

Commit c37c90b

Browse files
author
Ivan Dubrov
committed
🧚‍♀️Normalize doc attributes into ///
Stable `rustfmt` does not support normalizing doc attributes: rust-lang/rustfmt#3351 So, we "normalize" them ourselves by rendering `///`.
1 parent e6c6aec commit c37c90b

File tree

7 files changed

+104
-4
lines changed

7 files changed

+104
-4
lines changed

cli/Cargo.toml

+9
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,12 @@ dunce = "1.0.0"
2626
quote = "1.0.0"
2727
copy_dir = "0.1.2"
2828
pretty_assertions = "0.6.1"
29+
30+
[features]
31+
# Disable normalizing doc comments (`#[doc = r" hello"]`) into `///`.
32+
# On nightly, one can make `rustfmt` to do that via `normalize_doc_attributes` configuration parameter for `rustfmt`,
33+
# but on current stable this is not supported. So we support doing our own normalization by default. This feature
34+
# is to disable that normalization
35+
disable_normalize_doc_attributes = []
36+
37+
default = []

cli/src/generate.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,13 @@ impl std::fmt::Display for Replacement<'_> {
406406
f.write_char('\r')?;
407407
}
408408
f.write_char('\n')?;
409-
write!(f, "{}", self.tokens)
409+
410+
#[cfg(feature = "disable_normalize_doc_attributes")]
411+
write!(f, "{}", self.tokens)?;
412+
413+
#[cfg(not(feature = "disable_normalize_doc_attributes"))]
414+
crate::normalize::write_tokens_normalized(f, self.tokens.clone())?;
415+
416+
Ok(())
410417
}
411418
}

cli/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ use std::path::Path;
1313
mod error;
1414
mod generate;
1515
mod mods;
16+
#[cfg(not(feature = "disable_normalize_doc_attributes"))]
17+
mod normalize;
1618
mod region;
1719
mod rustfmt;
1820

cli/src/normalize.rs

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use proc_macro2::{Delimiter, Spacing, TokenStream, TokenTree};
2+
use syn::Lit;
3+
4+
/// Write tokens same way as `TokenStream::to_string` would do, but with normalization of doc
5+
/// attributes into `///`.
6+
pub fn write_tokens_normalized(
7+
f: &mut std::fmt::Formatter,
8+
tokens: TokenStream,
9+
) -> std::fmt::Result {
10+
let mut tokens = tokens.into_iter().peekable();
11+
let mut joint = false;
12+
let mut first = true;
13+
while let Some(tt) = tokens.next() {
14+
if !first && !joint {
15+
write!(f, " ")?;
16+
}
17+
first = false;
18+
joint = false;
19+
20+
if let Some(comment) = tokens
21+
.peek()
22+
.and_then(|lookahead| as_doc_comment(&tt, lookahead))
23+
{
24+
let _ignore = tokens.next();
25+
writeln!(f, "///{}", comment)?;
26+
continue;
27+
}
28+
match tt {
29+
TokenTree::Group(ref tt) => {
30+
let (start, end) = match tt.delimiter() {
31+
Delimiter::Parenthesis => ("(", ")"),
32+
Delimiter::Brace => ("{", "}"),
33+
Delimiter::Bracket => ("[", "]"),
34+
Delimiter::None => ("", ""),
35+
};
36+
if tt.stream().into_iter().next().is_none() {
37+
write!(f, "{} {}", start, end)?
38+
} else {
39+
write!(f, "{} ", start)?;
40+
write_tokens_normalized(f, tt.stream())?;
41+
write!(f, " {}", end)?
42+
}
43+
}
44+
TokenTree::Ident(ref tt) => write!(f, "{}", tt)?,
45+
TokenTree::Punct(ref tt) => {
46+
write!(f, "{}", tt.as_char())?;
47+
match tt.spacing() {
48+
Spacing::Alone => {}
49+
Spacing::Joint => joint = true,
50+
}
51+
}
52+
TokenTree::Literal(ref tt) => write!(f, "{}", tt)?,
53+
}
54+
}
55+
Ok(())
56+
}
57+
58+
fn as_doc_comment(first: &TokenTree, second: &TokenTree) -> Option<String> {
59+
match (first, second) {
60+
(TokenTree::Punct(first), TokenTree::Group(group))
61+
if first.as_char() == '#' && group.delimiter() == Delimiter::Bracket =>
62+
{
63+
let mut it = group.stream().into_iter();
64+
match (it.next(), it.next(), it.next()) {
65+
(
66+
Some(TokenTree::Ident(ident)),
67+
Some(TokenTree::Punct(punct)),
68+
Some(TokenTree::Literal(lit)),
69+
) => {
70+
if ident == "doc" && punct.as_char() == '=' {
71+
if let Lit::Str(lit) = Lit::new(lit) {
72+
return Some(lit.value());
73+
}
74+
}
75+
}
76+
_ => {}
77+
}
78+
}
79+
_ => {}
80+
}
81+
None
82+
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#[sourcegen::sourcegen(generator = "generate-doc-comments")]
22
// Generated. All manual edits to the block annotated with #[sourcegen...] will be discarded.
3-
#[doc = r" Some generated comment here"]
3+
/// Some generated comment here
44
struct Hello {
55
pub hello: String,
66
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![sourcegen::sourcegen(generator = "generate-file")]
22
// Generated. All manual edits below this line will be discarded.
3-
#[doc = r" Some generated comment here"]
3+
/// Some generated comment here
44
struct Hello {
55
pub hello: String,
66
}

cli/tests/test_data/006-complete-file-workaround/expected/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
struct __Unused;
33

44
// Generated. All manual edits below this line will be discarded.
5-
#[doc = r" Some generated comment here"]
5+
/// Some generated comment here
66
struct Hello {
77
pub hello: String,
88
}

0 commit comments

Comments
 (0)