Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ignore format_code_in_doc_comments if doc is in the middle of comment #6020

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
Loading