Skip to content

Commit 4f24013

Browse files
Auto merge of #149645 - GuillaumeGomez:doc-attr-based, r=<try>
Port `doc` attributes to new attribute API
2 parents 5372fc9 + 99104ef commit 4f24013

File tree

83 files changed

+2947
-2021
lines changed

Some content is hidden

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

83 files changed

+2947
-2021
lines changed

Cargo.lock

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4010,7 +4010,6 @@ dependencies = [
40104010
"itertools",
40114011
"rustc_abi",
40124012
"rustc_ast",
4013-
"rustc_attr_parsing",
40144013
"rustc_data_structures",
40154014
"rustc_errors",
40164015
"rustc_fluent_macro",
@@ -4432,7 +4431,6 @@ dependencies = [
44324431
"rustc_abi",
44334432
"rustc_ast",
44344433
"rustc_ast_lowering",
4435-
"rustc_ast_pretty",
44364434
"rustc_attr_parsing",
44374435
"rustc_data_structures",
44384436
"rustc_errors",
@@ -4869,6 +4867,7 @@ dependencies = [
48694867
"indexmap",
48704868
"itertools",
48714869
"minifier",
4870+
"proc-macro2",
48724871
"pulldown-cmark-escape",
48734872
"regex",
48744873
"rustdoc-json-types",

compiler/rustc_ast/src/attr/mod.rs

Lines changed: 45 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,11 @@ 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+
}
223236
}
224237

225238
impl Attribute {
@@ -300,6 +313,25 @@ impl AttrItem {
300313
}
301314
}
302315

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+
303335
pub fn meta(&self, span: Span) -> Option<MetaItem> {
304336
Some(MetaItem {
305337
unsafety: Safety::Default,
@@ -820,7 +852,7 @@ pub trait AttributeExt: Debug {
820852
/// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
821853
/// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
822854
/// * `#[doc(...)]` returns `None`.
823-
fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)>;
855+
fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)>;
824856

825857
/// Returns outer or inner if this is a doc attribute or a sugared doc
826858
/// comment, otherwise None.
@@ -830,6 +862,9 @@ pub trait AttributeExt: Debug {
830862
/// commented module (for inner doc) vs within its parent module (for outer
831863
/// doc).
832864
fn doc_resolution_scope(&self) -> Option<AttrStyle>;
865+
866+
/// Returns `true` if this attribute contains `doc(hidden)`.
867+
fn is_doc_hidden(&self) -> bool;
833868
}
834869

835870
// FIXME(fn_delegation): use function delegation instead of manually forwarding
@@ -902,7 +937,7 @@ impl Attribute {
902937
AttributeExt::is_proc_macro_attr(self)
903938
}
904939

905-
pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
906-
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)
907942
}
908943
}

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: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,93 @@ attr_parsing_unused_multiple =
261261
.suggestion = remove this attribute
262262
.note = attribute also specified here
263263
264+
265+
attr_parsing_doc_alias_duplicated = doc alias is duplicated
266+
.label = first defined here
267+
264268
-attr_parsing_previously_accepted =
265269
this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
266270
267271
attr_parsing_whole_archive_needs_static =
268272
linking modifier `whole-archive` is only compatible with `static` linking kind
273+
274+
attr_parsing_unused_no_lints_note =
275+
attribute `{$name}` without any lints has no effect
276+
277+
attr_parsing_doc_alias_empty =
278+
{$attr_str} attribute cannot have empty value
279+
280+
attr_parsing_doc_alias_bad_char =
281+
{$char_} character isn't allowed in {$attr_str}
282+
283+
attr_parsing_doc_alias_start_end =
284+
{$attr_str} cannot start or end with ' '
285+
286+
attr_parsing_doc_keyword_not_keyword =
287+
nonexistent keyword `{$keyword}` used in `#[doc(keyword = "...")]`
288+
.help = only existing keywords are allowed in core/std
289+
290+
attr_parsing_doc_attribute_not_attribute =
291+
nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = "...")]`
292+
.help = only existing builtin attributes are allowed in core/std
293+
294+
attr_parsing_doc_inline_conflict =
295+
conflicting doc inlining attributes
296+
.help = remove one of the conflicting attributes
297+
298+
attr_parsing_doc_inline_conflict_first =
299+
this attribute...
300+
301+
attr_parsing_doc_inline_conflict_second =
302+
{"."}..conflicts with this attribute
303+
304+
attr_parsing_doc_auto_cfg_expects_hide_or_show =
305+
only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]`
306+
307+
attr_parsing_doc_auto_cfg_hide_show_unexpected_item =
308+
`#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/value items
309+
310+
attr_parsing_doc_auto_cfg_hide_show_expects_list =
311+
`#![doc(auto_cfg({$attr_name}(...)))]` expects a list of items
312+
313+
attr_parsing_doc_invalid =
314+
invalid `doc` attribute
315+
316+
attr_parsing_doc_unknown_include =
317+
unknown `doc` attribute `include`
318+
.suggestion = use `doc = include_str!` instead
319+
320+
attr_parsing_doc_unknown_spotlight =
321+
unknown `doc` attribute `spotlight`
322+
.note = `doc(spotlight)` was renamed to `doc(notable_trait)`
323+
.suggestion = use `notable_trait` instead
324+
.no_op_note = `doc(spotlight)` is now a no-op
325+
326+
attr_parsing_doc_unknown_passes =
327+
unknown `doc` attribute `{$name}`
328+
.note = `doc` attribute `{$name}` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136>
329+
.label = no longer functions
330+
.no_op_note = `doc({$name})` is now a no-op
331+
332+
attr_parsing_doc_unknown_plugins =
333+
unknown `doc` attribute `plugins`
334+
.note = `doc` attribute `plugins` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136> and CVE-2018-1000622 <https://nvd.nist.gov/vuln/detail/CVE-2018-1000622>
335+
.label = no longer functions
336+
.no_op_note = `doc(plugins)` is now a no-op
337+
338+
attr_parsing_doc_unknown_any =
339+
unknown `doc` attribute `{$name}`
340+
341+
attr_parsing_doc_auto_cfg_wrong_literal =
342+
expected boolean for `#[doc(auto_cfg = ...)]`
343+
344+
attr_parsing_doc_test_takes_list =
345+
`#[doc(test(...)]` takes a list of attributes
346+
347+
attr_parsing_doc_test_unknown =
348+
unknown `doc(test)` attribute `{$name}`
349+
350+
attr_parsing_doc_test_literal = `#![doc(test(...)]` does not take a literal
351+
352+
attr_parsing_doc_alias_malformed =
353+
doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]`

compiler/rustc_attr_parsing/src/attributes/cfg.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use rustc_ast::token::Delimiter;
22
use rustc_ast::tokenstream::DelimSpan;
3-
use rustc_ast::{AttrItem, Attribute, CRATE_NODE_ID, LitKind, NodeId, ast, token};
3+
use rustc_ast::{AttrItem, Attribute, CRATE_NODE_ID, LitKind, ast, token};
44
use rustc_errors::{Applicability, PResult};
55
use rustc_feature::{AttrSuggestionStyle, AttributeTemplate, Features, template};
66
use rustc_hir::attrs::CfgEntry;
@@ -172,7 +172,7 @@ fn parse_cfg_entry_target<S: Stage>(
172172
Ok(CfgEntry::All(result, list.span))
173173
}
174174

175-
fn parse_name_value<S: Stage>(
175+
pub(crate) fn parse_name_value<S: Stage>(
176176
name: Symbol,
177177
name_span: Span,
178178
value: Option<&NameValueParser>,
@@ -199,14 +199,14 @@ fn parse_name_value<S: Stage>(
199199
pub fn eval_config_entry(
200200
sess: &Session,
201201
cfg_entry: &CfgEntry,
202-
id: NodeId,
202+
lint_emitter: &impl CfgMatchesLintEmitter,
203203
emit_lints: ShouldEmit,
204204
) -> EvalConfigResult {
205205
match cfg_entry {
206206
CfgEntry::All(subs, ..) => {
207207
let mut all = None;
208208
for sub in subs {
209-
let res = eval_config_entry(sess, sub, id, emit_lints);
209+
let res = eval_config_entry(sess, sub, lint_emitter, emit_lints);
210210
// We cannot short-circuit because `eval_config_entry` emits some lints
211211
if !res.as_bool() {
212212
all.get_or_insert(res);
@@ -217,7 +217,7 @@ pub fn eval_config_entry(
217217
CfgEntry::Any(subs, span) => {
218218
let mut any = None;
219219
for sub in subs {
220-
let res = eval_config_entry(sess, sub, id, emit_lints);
220+
let res = eval_config_entry(sess, sub, lint_emitter, emit_lints);
221221
// We cannot short-circuit because `eval_config_entry` emits some lints
222222
if res.as_bool() {
223223
any.get_or_insert(res);
@@ -229,7 +229,7 @@ pub fn eval_config_entry(
229229
})
230230
}
231231
CfgEntry::Not(sub, span) => {
232-
if eval_config_entry(sess, sub, id, emit_lints).as_bool() {
232+
if eval_config_entry(sess, sub, lint_emitter, emit_lints).as_bool() {
233233
EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span }
234234
} else {
235235
EvalConfigResult::True
@@ -248,15 +248,15 @@ pub fn eval_config_entry(
248248
Some(ExpectedValues::Some(values))
249249
if !values.contains(&value.map(|(v, _)| v)) =>
250250
{
251-
id.emit_span_lint(
251+
lint_emitter.emit_span_lint(
252252
sess,
253253
UNEXPECTED_CFGS,
254254
*span,
255255
BuiltinLintDiag::UnexpectedCfgValue((*name, *name_span), *value),
256256
);
257257
}
258258
None if sess.psess.check_config.exhaustive_names => {
259-
id.emit_span_lint(
259+
lint_emitter.emit_span_lint(
260260
sess,
261261
UNEXPECTED_CFGS,
262262
*span,

0 commit comments

Comments
 (0)