Skip to content

Commit 5b150d2

Browse files
committed
Auto merge of #149645 - GuillaumeGomez:doc-attr-based, r=jdonszelmann,jonathanbrouwer
Port `doc` attributes to new attribute API Part of #131229. This PR ports the `doc` attributes to the new attribute API. However, there are things that will need to be fixed in a follow-up: * Some part of `cfg_old.rs` are likely unused now, so they should be removed. * Not all error/lints are emitted at the same time anymore, making them kinda less useful considering that you need to run and fix rustc/rustdoc multiple times to get through all of them. * For coherency with the other attribute errors, I didn't modify the default output too much, meaning that we have some new messages now. I'll likely come back to that to check if the previous ones were better in a case-by-case approach. * `doc(test(attr(...)))` is handled in a horrifying manner currently. Until we can handle it correctly with the `Attribute` system, it'll remain that thing we're all very ashamed of. 😈 * A type in rustdoc got its size increased, I'll check the impact on performance. But in any case, I plan to improve it in a follow-up so should be "ok". * Because of error reporting, some fields of `Doc` are suboptimal, like `inline` which instead of being an `Option` is a `ThinVec` because we report the error later on. Part of the things I'm not super happy about but can be postponed to future me. * In `src/librustdoc/clean/cfg.rs`, the `pub(crate) fn parse(cfg: &MetaItemInner) -> Result<Cfg, InvalidCfgError> {` function should be removed once `cfg_trace` has been ported to new `cfg` API. * Size of type `DocFragment` went from 32 to 48. Would be nice to get it back to 32. * ``malformed `doc` attribute input`` wasn't meant for so many candidates, should be improved. * See how many of the checks in `check_attr` we can move to attribute parsing * Port target checking to be in the attribute parser completely * Fix target checking for `doc(alias)` on fields & patterns And finally, once this PR is merged, I plan to finally stabilize `doc_cfg` feature. :) cc `@jdonszelmann` r? `@JonathanBrouwer`
2 parents f520900 + 5180632 commit 5b150d2

File tree

119 files changed

+3624
-2311
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

119 files changed

+3624
-2311
lines changed

Cargo.lock

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4011,7 +4011,6 @@ dependencies = [
40114011
"itertools",
40124012
"rustc_abi",
40134013
"rustc_ast",
4014-
"rustc_attr_parsing",
40154014
"rustc_data_structures",
40164015
"rustc_errors",
40174016
"rustc_fluent_macro",
@@ -4433,7 +4432,6 @@ dependencies = [
44334432
"rustc_abi",
44344433
"rustc_ast",
44354434
"rustc_ast_lowering",
4436-
"rustc_ast_pretty",
44374435
"rustc_attr_parsing",
44384436
"rustc_data_structures",
44394437
"rustc_errors",
@@ -4870,6 +4868,7 @@ dependencies = [
48704868
"indexmap",
48714869
"itertools",
48724870
"minifier",
4871+
"proc-macro2",
48734872
"pulldown-cmark-escape",
48744873
"regex",
48754874
"rustdoc-json-types",

compiler/rustc_ast/src/attr/mod.rs

Lines changed: 61 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
}
@@ -220,6 +228,24 @@ impl AttributeExt for Attribute {
220228
fn is_automatically_derived_attr(&self) -> bool {
221229
self.has_name(sym::automatically_derived)
222230
}
231+
232+
fn is_doc_hidden(&self) -> bool {
233+
self.has_name(sym::doc)
234+
&& self.meta_item_list().is_some_and(|l| list_contains_name(&l, sym::hidden))
235+
}
236+
237+
fn is_doc_keyword_or_attribute(&self) -> bool {
238+
if self.has_name(sym::doc)
239+
&& let Some(items) = self.meta_item_list()
240+
{
241+
for item in items {
242+
if item.has_name(sym::keyword) || item.has_name(sym::attribute) {
243+
return true;
244+
}
245+
}
246+
}
247+
false
248+
}
223249
}
224250

225251
impl Attribute {
@@ -300,6 +326,25 @@ impl AttrItem {
300326
}
301327
}
302328

329+
/// Returns the span in:
330+
///
331+
/// ```text
332+
/// #[attribute = "value"]
333+
/// ^^^^^^^
334+
/// ```
335+
///
336+
/// It returns `None` in any other cases like:
337+
///
338+
/// ```text
339+
/// #[attr("value")]
340+
/// ```
341+
fn value_span(&self) -> Option<Span> {
342+
match &self.args {
343+
AttrArgs::Eq { expr, .. } => Some(expr.span),
344+
AttrArgs::Delimited(_) | AttrArgs::Empty => None,
345+
}
346+
}
347+
303348
pub fn meta(&self, span: Span) -> Option<MetaItem> {
304349
Some(MetaItem {
305350
unsafety: Safety::Default,
@@ -820,7 +865,7 @@ pub trait AttributeExt: Debug {
820865
/// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
821866
/// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
822867
/// * `#[doc(...)]` returns `None`.
823-
fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)>;
868+
fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)>;
824869

825870
/// Returns outer or inner if this is a doc attribute or a sugared doc
826871
/// comment, otherwise None.
@@ -830,6 +875,12 @@ pub trait AttributeExt: Debug {
830875
/// commented module (for inner doc) vs within its parent module (for outer
831876
/// doc).
832877
fn doc_resolution_scope(&self) -> Option<AttrStyle>;
878+
879+
/// Returns `true` if this attribute contains `doc(hidden)`.
880+
fn is_doc_hidden(&self) -> bool;
881+
882+
/// Returns `true` is this attribute contains `doc(keyword)` or `doc(attribute)`.
883+
fn is_doc_keyword_or_attribute(&self) -> bool;
833884
}
834885

835886
// FIXME(fn_delegation): use function delegation instead of manually forwarding
@@ -902,7 +953,7 @@ impl Attribute {
902953
AttributeExt::is_proc_macro_attr(self)
903954
}
904955

905-
pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
906-
AttributeExt::doc_str_and_comment_kind(self)
956+
pub fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {
957+
AttributeExt::doc_str_and_fragment_kind(self)
907958
}
908959
}

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/messages.ftl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,29 @@ attr_parsing_deprecated_item_suggestion =
1414
.help = add `#![feature(deprecated_suggestion)]` to the crate root
1515
.note = see #94785 for more details
1616
17+
attr_parsing_doc_alias_bad_char =
18+
{$char_} character isn't allowed in {$attr_str}
19+
20+
attr_parsing_doc_alias_empty =
21+
{$attr_str} attribute cannot have empty value
22+
23+
attr_parsing_doc_alias_malformed =
24+
doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]`
25+
26+
attr_parsing_doc_alias_start_end =
27+
{$attr_str} cannot start or end with ' '
28+
29+
attr_parsing_doc_attribute_not_attribute =
30+
nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = "...")]`
31+
.help = only existing builtin attributes are allowed in core/std
32+
33+
attr_parsing_doc_keyword_not_keyword =
34+
nonexistent keyword `{$keyword}` used in `#[doc(keyword = "...")]`
35+
.help = only existing keywords are allowed in core/std
36+
1737
attr_parsing_empty_confusables =
1838
expected at least one confusable name
39+
1940
attr_parsing_empty_link_name =
2041
link name must not be empty
2142
.label = empty link name

compiler/rustc_attr_parsing/src/attributes/cfg.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ fn parse_cfg_entry_target<S: Stage>(
171171
Ok(CfgEntry::All(result, list.span))
172172
}
173173

174-
fn parse_name_value<S: Stage>(
174+
pub(crate) fn parse_name_value<S: Stage>(
175175
name: Symbol,
176176
name_span: Span,
177177
value: Option<&NameValueParser>,

compiler/rustc_attr_parsing/src/attributes/cfg_old.rs

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, Nod
22
use rustc_ast_pretty::pprust;
33
use rustc_feature::{Features, GatedCfg, find_gated_cfg};
44
use rustc_hir::RustcVersion;
5-
use rustc_hir::lints::AttributeLintKind;
65
use rustc_session::Session;
7-
use rustc_session::config::ExpectedValues;
8-
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
96
use rustc_session::lint::{BuiltinLintDiag, Lint};
107
use rustc_session::parse::feature_err;
118
use rustc_span::{Span, Symbol, sym};
@@ -37,44 +34,6 @@ pub struct Condition {
3734
pub span: Span,
3835
}
3936

40-
/// Tests if a cfg-pattern matches the cfg set
41-
pub fn cfg_matches(
42-
cfg: &MetaItemInner,
43-
sess: &Session,
44-
lint_emitter: impl CfgMatchesLintEmitter,
45-
features: Option<&Features>,
46-
) -> bool {
47-
eval_condition(cfg, sess, features, &mut |cfg| {
48-
try_gate_cfg(cfg.name, cfg.span, sess, features);
49-
match sess.psess.check_config.expecteds.get(&cfg.name) {
50-
Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => {
51-
lint_emitter.emit_span_lint(
52-
sess,
53-
UNEXPECTED_CFGS,
54-
cfg.span,
55-
BuiltinLintDiag::AttributeLint(AttributeLintKind::UnexpectedCfgValue(
56-
(cfg.name, cfg.name_span),
57-
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
58-
)),
59-
);
60-
}
61-
None if sess.psess.check_config.exhaustive_names => {
62-
lint_emitter.emit_span_lint(
63-
sess,
64-
UNEXPECTED_CFGS,
65-
cfg.span,
66-
BuiltinLintDiag::AttributeLint(AttributeLintKind::UnexpectedCfgName(
67-
(cfg.name, cfg.name_span),
68-
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
69-
)),
70-
);
71-
}
72-
_ => { /* not unexpected */ }
73-
}
74-
sess.psess.config.contains(&(cfg.name, cfg.value))
75-
})
76-
}
77-
7837
pub fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) {
7938
let gate = find_gated_cfg(|sym| sym == name);
8039
if let (Some(feats), Some(gated_cfg)) = (features, gate) {

0 commit comments

Comments
 (0)