Skip to content

Commit cf2a260

Browse files
Correctly differentiate between sugared and raw doc comments
1 parent e4a4ccb commit cf2a260

File tree

10 files changed

+117
-59
lines changed

10 files changed

+117
-59
lines changed

compiler/rustc_ast/src/attr/mod.rs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ use crate::ast::{
1313
Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NormalAttr, Path,
1414
PathSegment, Safety,
1515
};
16-
use crate::token::{self, CommentKind, Delimiter, InvisibleOrigin, MetaVarKind, Token};
16+
use crate::token::{
17+
self, CommentKind, Delimiter, DocFragmentKind, InvisibleOrigin, MetaVarKind, Token,
18+
};
1719
use crate::tokenstream::{
1820
DelimSpan, LazyAttrTokenStream, Spacing, TokenStream, TokenStreamIter, TokenTree,
1921
};
@@ -179,15 +181,21 @@ impl AttributeExt for Attribute {
179181
}
180182

181183
/// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.
182-
/// * `///doc` returns `Some(("doc", CommentKind::Line))`.
183-
/// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
184-
/// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
184+
/// * `///doc` returns `Some(("doc", DocFragmentKind::Sugared(CommentKind::Line)))`.
185+
/// * `/** doc */` returns `Some(("doc", DocFragmentKind::Sugared(CommentKind::Block)))`.
186+
/// * `#[doc = "doc"]` returns `Some(("doc", DocFragmentKind::Raw))`.
185187
/// * `#[doc(...)]` returns `None`.
186-
fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
188+
fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {
187189
match &self.kind {
188-
AttrKind::DocComment(kind, data) => Some((*data, *kind)),
190+
AttrKind::DocComment(kind, data) => Some((*data, DocFragmentKind::Sugared(*kind))),
189191
AttrKind::Normal(normal) if normal.item.path == sym::doc => {
190-
normal.item.value_str().map(|s| (s, CommentKind::Line))
192+
if let Some(value) = normal.item.value_str()
193+
&& let Some(value_span) = normal.item.value_span()
194+
{
195+
Some((value, DocFragmentKind::Raw(value_span)))
196+
} else {
197+
None
198+
}
191199
}
192200
_ => None,
193201
}
@@ -305,6 +313,25 @@ impl AttrItem {
305313
}
306314
}
307315

316+
/// Returns the span in:
317+
///
318+
/// ```text
319+
/// #[attribute = "value"]
320+
/// ^^^^^^^
321+
/// ```
322+
///
323+
/// It returns `None` in any other cases like:
324+
///
325+
/// ```text
326+
/// #[attr("value")]
327+
/// ```
328+
fn value_span(&self) -> Option<Span> {
329+
match &self.args {
330+
AttrArgs::Eq { expr, .. } => Some(expr.span),
331+
AttrArgs::Delimited(_) | AttrArgs::Empty => None,
332+
}
333+
}
334+
308335
pub fn meta(&self, span: Span) -> Option<MetaItem> {
309336
Some(MetaItem {
310337
unsafety: Safety::Default,
@@ -825,7 +852,7 @@ pub trait AttributeExt: Debug {
825852
/// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
826853
/// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
827854
/// * `#[doc(...)]` returns `None`.
828-
fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)>;
855+
fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)>;
829856

830857
/// Returns outer or inner if this is a doc attribute or a sugared doc
831858
/// comment, otherwise None.
@@ -910,7 +937,7 @@ impl Attribute {
910937
AttributeExt::is_proc_macro_attr(self)
911938
}
912939

913-
pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
914-
AttributeExt::doc_str_and_comment_kind(self)
940+
pub fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {
941+
AttributeExt::doc_str_and_fragment_kind(self)
915942
}
916943
}

compiler/rustc_ast/src/token.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,31 @@ use rustc_span::{Ident, Symbol};
1616
use crate::ast;
1717
use crate::util::case::Case;
1818

19-
#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
19+
/// Represents the kind of doc comment it is, ie `///` or `#[doc = ""]`.
20+
#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)]
21+
pub enum DocFragmentKind {
22+
/// A sugared doc comment: `///` or `//!` or `/**` or `/*!`.
23+
Sugared(CommentKind),
24+
/// A "raw" doc comment: `#[doc = ""]`. The `Span` represents the string literal.
25+
Raw(Span),
26+
}
27+
28+
impl DocFragmentKind {
29+
pub fn is_sugared(self) -> bool {
30+
matches!(self, Self::Sugared(_))
31+
}
32+
33+
/// If it is `Sugared`, it will return its associated `CommentKind`, otherwise it will return
34+
/// `CommentKind::Line`.
35+
pub fn comment_kind(self) -> CommentKind {
36+
match self {
37+
Self::Sugared(kind) => kind,
38+
Self::Raw(_) => CommentKind::Line,
39+
}
40+
}
41+
}
42+
43+
#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)]
2044
pub enum CommentKind {
2145
Line,
2246
Block,

compiler/rustc_ast_pretty/src/pprust/state.rs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::borrow::Cow;
1010
use std::sync::Arc;
1111

1212
use rustc_ast::attr::AttrIdGenerator;
13-
use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
13+
use rustc_ast::token::{self, CommentKind, Delimiter, DocFragmentKind, Token, TokenKind};
1414
use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
1515
use rustc_ast::util::classify;
1616
use rustc_ast::util::comments::{Comment, CommentStyle};
@@ -381,15 +381,24 @@ fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool {
381381
}
382382

383383
pub fn doc_comment_to_string(
384-
comment_kind: CommentKind,
384+
fragment_kind: DocFragmentKind,
385385
attr_style: ast::AttrStyle,
386386
data: Symbol,
387387
) -> String {
388-
match (comment_kind, attr_style) {
389-
(CommentKind::Line, ast::AttrStyle::Outer) => format!("///{data}"),
390-
(CommentKind::Line, ast::AttrStyle::Inner) => format!("//!{data}"),
391-
(CommentKind::Block, ast::AttrStyle::Outer) => format!("/**{data}*/"),
392-
(CommentKind::Block, ast::AttrStyle::Inner) => format!("/*!{data}*/"),
388+
match fragment_kind {
389+
DocFragmentKind::Sugared(comment_kind) => match (comment_kind, attr_style) {
390+
(CommentKind::Line, ast::AttrStyle::Outer) => format!("///{data}"),
391+
(CommentKind::Line, ast::AttrStyle::Inner) => format!("//!{data}"),
392+
(CommentKind::Block, ast::AttrStyle::Outer) => format!("/**{data}*/"),
393+
(CommentKind::Block, ast::AttrStyle::Inner) => format!("/*!{data}*/"),
394+
},
395+
DocFragmentKind::Raw(_) => {
396+
format!(
397+
"#{}[doc = {:?}]",
398+
if attr_style == ast::AttrStyle::Inner { "!" } else { "" },
399+
data.to_string(),
400+
)
401+
}
393402
}
394403
}
395404

@@ -665,7 +674,11 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
665674
self.word("]");
666675
}
667676
ast::AttrKind::DocComment(comment_kind, data) => {
668-
self.word(doc_comment_to_string(*comment_kind, attr.style, *data));
677+
self.word(doc_comment_to_string(
678+
DocFragmentKind::Sugared(*comment_kind),
679+
attr.style,
680+
*data,
681+
));
669682
self.hardbreak()
670683
}
671684
}
@@ -1029,7 +1042,8 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
10291042

10301043
/* Other */
10311044
token::DocComment(comment_kind, attr_style, data) => {
1032-
doc_comment_to_string(comment_kind, attr_style, data).into()
1045+
doc_comment_to_string(DocFragmentKind::Sugared(comment_kind), attr_style, data)
1046+
.into()
10331047
}
10341048
token::Eof => "<eof>".into(),
10351049
}

compiler/rustc_attr_parsing/src/attributes/doc.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,9 @@ impl DocParser {
474474
if nv.value_as_str().is_none() {
475475
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
476476
} else {
477-
unreachable!("Should have been handled at the same time as sugar-syntaxed doc comments");
477+
unreachable!(
478+
"Should have been handled at the same time as sugar-syntaxed doc comments"
479+
);
478480
}
479481
}
480482
}

compiler/rustc_attr_parsing/src/interface.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use std::borrow::Cow;
22

33
use rustc_ast as ast;
4+
use rustc_ast::token::DocFragmentKind;
45
use rustc_ast::{AttrStyle, NodeId};
5-
use rustc_ast::token::CommentKind;
66
use rustc_errors::DiagCtxtHandle;
77
use rustc_feature::{AttributeTemplate, Features};
88
use rustc_hir::attrs::AttributeKind;
@@ -272,7 +272,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
272272

273273
attributes.push(Attribute::Parsed(AttributeKind::DocComment {
274274
style: attr.style,
275-
kind: *comment_kind,
275+
kind: DocFragmentKind::Sugared(*comment_kind),
276276
span: lower_span(attr.span),
277277
comment: *symbol,
278278
}))
@@ -318,8 +318,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
318318
{
319319
attributes.push(Attribute::Parsed(AttributeKind::DocComment {
320320
style: attr.style,
321-
kind: CommentKind::Block,
322-
span: nv.value_span,
321+
kind: DocFragmentKind::Raw(nv.value_span),
322+
span: attr.span,
323323
comment,
324324
}));
325325
continue;

compiler/rustc_hir/src/attrs/data_structures.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::path::PathBuf;
33

44
pub use ReprAttr::*;
55
use rustc_abi::Align;
6-
use rustc_ast::token::CommentKind;
6+
use rustc_ast::token::DocFragmentKind;
77
use rustc_ast::{AttrStyle, ast};
88
use rustc_data_structures::fx::FxIndexMap;
99
use rustc_error_messages::{DiagArgValue, IntoDiagArg};
@@ -648,7 +648,7 @@ pub enum AttributeKind {
648648

649649
/// Represents specifically [`#[doc = "..."]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html).
650650
/// i.e. doc comments.
651-
DocComment { style: AttrStyle, kind: CommentKind, span: Span, comment: Symbol },
651+
DocComment { style: AttrStyle, kind: DocFragmentKind, span: Span, comment: Symbol },
652652

653653
/// Represents `#[rustc_dummy]`.
654654
Dummy,

compiler/rustc_hir/src/attrs/pretty_printing.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::num::NonZero;
22

33
use rustc_abi::Align;
4-
use rustc_ast::token::CommentKind;
4+
use rustc_ast::token::{CommentKind, DocFragmentKind};
55
use rustc_ast::{AttrStyle, IntTy, UintTy};
66
use rustc_ast_pretty::pp::Printer;
77
use rustc_data_structures::fx::FxIndexMap;
@@ -167,6 +167,7 @@ print_debug!(
167167
Align,
168168
AttrStyle,
169169
CommentKind,
170+
DocFragmentKind,
170171
Transparency,
171172
SanitizerSet,
172173
);

compiler/rustc_hir/src/hir.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::fmt;
44

55
use rustc_abi::ExternAbi;
66
use rustc_ast::attr::AttributeExt;
7-
use rustc_ast::token::CommentKind;
7+
use rustc_ast::token::DocFragmentKind;
88
use rustc_ast::util::parser::ExprPrecedence;
99
use rustc_ast::{
1010
self as ast, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitIntType,
@@ -1380,7 +1380,7 @@ impl AttributeExt for Attribute {
13801380
}
13811381

13821382
#[inline]
1383-
fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
1383+
fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {
13841384
match &self {
13851385
Attribute::Parsed(AttributeKind::DocComment { kind, comment, .. }) => {
13861386
Some((*comment, *kind))
@@ -1498,8 +1498,8 @@ impl Attribute {
14981498
}
14991499

15001500
#[inline]
1501-
pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
1502-
AttributeExt::doc_str_and_comment_kind(self)
1501+
pub fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {
1502+
AttributeExt::doc_str_and_fragment_kind(self)
15031503
}
15041504
}
15051505

compiler/rustc_resolve/src/rustdoc.rs

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use pulldown_cmark::{
1010
use rustc_ast as ast;
1111
use rustc_ast::attr::AttributeExt;
1212
use rustc_ast::join_path_syms;
13+
use rustc_ast::token::DocFragmentKind;
1314
use rustc_ast::util::comments::beautify_doc_string;
1415
use rustc_data_structures::fx::FxIndexMap;
1516
use rustc_data_structures::unord::UnordSet;
@@ -23,14 +24,6 @@ use tracing::{debug, trace};
2324
#[cfg(test)]
2425
mod tests;
2526

26-
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
27-
pub enum DocFragmentKind {
28-
/// A doc fragment created from a `///` or `//!` doc comment.
29-
SugaredDoc,
30-
/// A doc fragment created from a "raw" `#[doc=""]` attribute.
31-
RawDoc,
32-
}
33-
3427
/// A portion of documentation, extracted from a `#[doc]` attribute.
3528
///
3629
/// Each variant contains the line number within the complete doc-comment where the fragment
@@ -125,7 +118,7 @@ pub fn unindent_doc_fragments(docs: &mut [DocFragment]) {
125118
//
126119
// In this case, you want "hello! another" and not "hello! another".
127120
let add = if docs.windows(2).any(|arr| arr[0].kind != arr[1].kind)
128-
&& docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc)
121+
&& docs.iter().any(|d| d.kind.is_sugared())
129122
{
130123
// In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to
131124
// "decide" how much the minimum indent will be.
@@ -155,8 +148,7 @@ pub fn unindent_doc_fragments(docs: &mut [DocFragment]) {
155148
// Compare against either space or tab, ignoring whether they are
156149
// mixed or not.
157150
let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count();
158-
whitespace
159-
+ (if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add })
151+
whitespace + (if fragment.kind.is_sugared() { 0 } else { add })
160152
})
161153
.min()
162154
.unwrap_or(usize::MAX)
@@ -171,7 +163,7 @@ pub fn unindent_doc_fragments(docs: &mut [DocFragment]) {
171163
continue;
172164
}
173165

174-
let indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 {
166+
let indent = if !fragment.kind.is_sugared() && min_indent > 0 {
175167
min_indent - add
176168
} else {
177169
min_indent
@@ -214,19 +206,17 @@ pub fn attrs_to_doc_fragments<'a, A: AttributeExt + Clone + 'a>(
214206
let mut doc_fragments = Vec::with_capacity(size_hint);
215207
let mut other_attrs = ThinVec::<A>::with_capacity(if doc_only { 0 } else { size_hint });
216208
for (attr, item_id) in attrs {
217-
if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() {
218-
let doc = beautify_doc_string(doc_str, comment_kind);
219-
let (span, kind, from_expansion) = if let Some(span) = attr.is_doc_comment() {
220-
(span, DocFragmentKind::SugaredDoc, span.from_expansion())
221-
} else {
222-
let attr_span = attr.span();
223-
let (span, from_expansion) = match attr.value_span() {
224-
Some(sp) => (sp.with_ctxt(attr_span.ctxt()), sp.from_expansion()),
225-
None => (attr_span, attr_span.from_expansion()),
226-
};
227-
(span, DocFragmentKind::RawDoc, from_expansion)
209+
if let Some((doc_str, fragment_kind)) = attr.doc_str_and_fragment_kind() {
210+
let doc = beautify_doc_string(doc_str, fragment_kind.comment_kind());
211+
let attr_span = attr.span();
212+
let (span, from_expansion) = match fragment_kind {
213+
DocFragmentKind::Sugared(_) => (attr_span, attr_span.from_expansion()),
214+
DocFragmentKind::Raw(value_span) => {
215+
(value_span.with_ctxt(attr_span.ctxt()), value_span.from_expansion())
216+
}
228217
};
229-
let fragment = DocFragment { span, doc, kind, item_id, indent: 0, from_expansion };
218+
let fragment =
219+
DocFragment { span, doc, kind: fragment_kind, item_id, indent: 0, from_expansion };
230220
doc_fragments.push(fragment);
231221
} else if !doc_only {
232222
other_attrs.push(attr.clone());
@@ -571,7 +561,7 @@ pub fn source_span_for_markdown_range_inner(
571561
use rustc_span::BytePos;
572562

573563
if let &[fragment] = &fragments
574-
&& fragment.kind == DocFragmentKind::RawDoc
564+
&& !fragment.kind.is_sugared()
575565
&& let Ok(snippet) = map.span_to_snippet(fragment.span)
576566
&& snippet.trim_end() == markdown.trim_end()
577567
&& let Ok(md_range_lo) = u32::try_from(md_range.start)
@@ -589,7 +579,7 @@ pub fn source_span_for_markdown_range_inner(
589579
));
590580
}
591581

592-
let is_all_sugared_doc = fragments.iter().all(|frag| frag.kind == DocFragmentKind::SugaredDoc);
582+
let is_all_sugared_doc = fragments.iter().all(|frag| frag.kind.is_sugared());
593583

594584
if !is_all_sugared_doc {
595585
// This case ignores the markdown outside of the range so that it can

src/librustdoc/clean/types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2403,7 +2403,7 @@ mod size_asserts {
24032403
use super::*;
24042404
// tidy-alphabetical-start
24052405
static_assert_size!(Crate, 16); // frequently moved by-value
2406-
static_assert_size!(DocFragment, 32);
2406+
static_assert_size!(DocFragment, 48);
24072407
static_assert_size!(GenericArg, 32);
24082408
static_assert_size!(GenericArgs, 24);
24092409
static_assert_size!(GenericParamDef, 40);

0 commit comments

Comments
 (0)