Skip to content

Commit

Permalink
Ignore format_code_in_doc_comments if doc is in the middle of comment
Browse files Browse the repository at this point in the history
  • Loading branch information
stepancheg committed Jan 11, 2024
1 parent 6356fca commit 1c0ab1b
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 8 deletions.
69 changes: 66 additions & 3 deletions src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,60 @@ where
&attrs[..len]
}

/// Attr is doc attr with doc.
///
/// ```ignore
/// #[doc = "doc"]
/// ```
fn is_doc_eq_attr(attr: &ast::Attribute) -> bool {
match &attr.kind {
ast::AttrKind::Normal(normal) if normal.item.path == sym::doc => match normal.item.args {
ast::AttrArgs::Eq(..) => true,
_ => false,
},
_ => false,
}
}

/// Is there a `#[doc = "doc"]` attribute in the middle of doc comments.
///
/// If there's such attribute, we skip formatting, because the doc can point to an external file,
/// and we don't know how that file may break the formatting.
fn has_doc_eq_attr_in_the_middle_of_comments(attrs: &[ast::Attribute]) -> bool {
enum Stage {
Start,
SeenDocComment,
SeenDocAttr,
}
let mut stage = Stage::Start;
for attr in attrs {
match &stage {
Stage::Start => {
if attr.is_doc_comment() {
stage = Stage::SeenDocComment;
}
}
Stage::SeenDocComment => {
if is_doc_eq_attr(attr) {
stage = Stage::SeenDocAttr;
}
}
Stage::SeenDocAttr => {
if attr.is_doc_comment() {
return true;
}
}
}
}
false
}

/// Rewrite the any doc comments which come before any other attributes.
fn rewrite_initial_doc_comments(
context: &RewriteContext<'_>,
attrs: &[ast::Attribute],
shape: Shape,
has_doc_attr_in_the_middle_of_comments: bool,
) -> Option<(usize, Option<String>)> {
if attrs.is_empty() {
return Some((0, None));
Expand All @@ -235,6 +284,7 @@ fn rewrite_initial_doc_comments(
&snippet,
shape.comment(context.config),
context.config,
has_doc_attr_in_the_middle_of_comments,
)?),
));
}
Expand Down Expand Up @@ -318,7 +368,12 @@ impl Rewrite for ast::Attribute {
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
let snippet = context.snippet(self.span);
if self.is_doc_comment() {
rewrite_doc_comment(snippet, shape.comment(context.config), context.config)
rewrite_doc_comment(
snippet,
shape.comment(context.config),
context.config,
false,
)
} else {
let should_skip = self
.ident()
Expand Down Expand Up @@ -347,6 +402,7 @@ impl Rewrite for ast::Attribute {
&doc_comment,
shape.comment(context.config),
context.config,
false,
);
}
}
Expand Down Expand Up @@ -378,6 +434,9 @@ impl Rewrite for [ast::Attribute] {
// or `#![rustfmt::skip::attributes(derive)]`
let skip_derives = context.skip_context.attributes.skip("derive");

let has_doc_attr_in_the_middle_of_comments =
has_doc_eq_attr_in_the_middle_of_comments(attrs);

// This is not just a simple map because we need to handle doc comments
// (where we take as many doc comment attributes as possible) and possibly
// merging derives into a single attribute.
Expand All @@ -387,8 +446,12 @@ impl Rewrite for [ast::Attribute] {
}

// Handle doc comments.
let (doc_comment_len, doc_comment_str) =
rewrite_initial_doc_comments(context, attrs, shape)?;
let (doc_comment_len, doc_comment_str) = rewrite_initial_doc_comments(
context,
attrs,
shape,
has_doc_attr_in_the_middle_of_comments,
)?;
if doc_comment_len > 0 {
let doc_comment_str = doc_comment_str.expect("doc comments, but no result");
result.push_str(&doc_comment_str);
Expand Down
37 changes: 32 additions & 5 deletions src/comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,20 @@ pub(crate) fn combine_strs_with_missing_comments(
Some(result)
}

pub(crate) fn rewrite_doc_comment(orig: &str, shape: Shape, config: &Config) -> Option<String> {
identify_comment(orig, false, shape, config, true)
pub(crate) fn rewrite_doc_comment(
orig: &str,
shape: Shape,
config: &Config,
has_doc_attr_in_the_middle_of_comments: bool,
) -> Option<String> {
identify_comment(
orig,
false,
shape,
config,
true,
has_doc_attr_in_the_middle_of_comments,
)
}

pub(crate) fn rewrite_comment(
Expand All @@ -258,7 +270,7 @@ pub(crate) fn rewrite_comment(
shape: Shape,
config: &Config,
) -> Option<String> {
identify_comment(orig, block_style, shape, config, false)
identify_comment(orig, block_style, shape, config, false, false)
}

fn identify_comment(
Expand All @@ -267,6 +279,7 @@ fn identify_comment(
shape: Shape,
config: &Config,
is_doc_comment: bool,
has_doc_attr_in_the_middle_of_comments: bool,
) -> Option<String> {
let style = comment_style(orig, false);

Expand Down Expand Up @@ -365,7 +378,9 @@ fn identify_comment(
&& !(
// `format_code_in_doc_comments` should only take effect on doc comments,
// so we only consider it when this comment block is a doc comment block.
is_doc_comment && config.format_code_in_doc_comments()
is_doc_comment
&& config.format_code_in_doc_comments()
&& !has_doc_attr_in_the_middle_of_comments
)
{
light_rewrite_comment(first_group, shape.indent, config, is_doc_comment)
Expand All @@ -377,6 +392,7 @@ fn identify_comment(
shape,
config,
is_doc_comment || style.is_doc_comment(),
has_doc_attr_in_the_middle_of_comments,
)?
};
if rest.is_empty() {
Expand All @@ -388,6 +404,7 @@ fn identify_comment(
shape,
config,
is_doc_comment,
has_doc_attr_in_the_middle_of_comments,
)
.map(|rest_str| {
format!(
Expand Down Expand Up @@ -725,6 +742,7 @@ impl<'a> CommentRewrite<'a> {
line: &'a str,
has_leading_whitespace: bool,
is_doc_comment: bool,
has_doc_attr_in_the_middle_of_comments: bool,
) -> bool {
let num_newlines = count_newlines(orig);
let is_last = i == num_newlines;
Expand Down Expand Up @@ -767,6 +785,7 @@ impl<'a> CommentRewrite<'a> {
let code_block = match self.code_block_attr.as_ref().unwrap() {
CodeBlockAttribute::Rust
if self.fmt.config.format_code_in_doc_comments()
&& !has_doc_attr_in_the_middle_of_comments
&& !self.code_block_buffer.trim().is_empty() =>
{
let mut config = self.fmt.config.clone();
Expand Down Expand Up @@ -912,6 +931,7 @@ fn rewrite_comment_inner(
shape: Shape,
config: &Config,
is_doc_comment: bool,
has_doc_attr_in_the_middle_of_comments: bool,
) -> Option<String> {
let mut rewriter = CommentRewrite::new(orig, block_style, shape, config);

Expand Down Expand Up @@ -941,7 +961,14 @@ fn rewrite_comment_inner(
});

for (i, (line, has_leading_whitespace)) in lines.enumerate() {
if rewriter.handle_line(orig, i, line, has_leading_whitespace, is_doc_comment) {
if rewriter.handle_line(
orig,
i,
line,
has_leading_whitespace,
is_doc_comment,
has_doc_attr_in_the_middle_of_comments,
) {
break;
}
}
Expand Down
21 changes: 21 additions & 0 deletions tests/source/doc-attr-in-the-middle-of-doc-comment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// rustfmt-format_code_in_doc_comments: true

//! <details open>
//! <summary>Example 1</summary>
//!
//! ```
#![doc = "test()"]
//! ```
//!
//! </details>
//!
//! <details open>
//! <summary>Example 2</summary>
//!
//! ```
//! example(2);
//! ```
//!
//! </details>
struct FooBar;
21 changes: 21 additions & 0 deletions tests/target/doc-attr-in-the-middle-of-doc-comment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// rustfmt-format_code_in_doc_comments: true

//! <details open>
//! <summary>Example 1</summary>
//!
//! ```
#![doc = "test()"]
//! ```
//!
//! </details>
//!
//! <details open>
//! <summary>Example 2</summary>
//!
//! ```
//! example(2);
//! ```
//!
//! </details>
struct FooBar;

0 comments on commit 1c0ab1b

Please sign in to comment.