1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
//! Propagates [`#[doc(cfg(...))]`](https://github.com/rust-lang/rust/issues/43781) to child items.
use std::sync::Arc;
use rustc_ast::token::{Token, TokenKind};
use rustc_ast::tokenstream::{TokenStream, TokenTree};
use rustc_hir::def_id::LocalDefId;
use rustc_hir::{AttrArgs, Attribute};
use rustc_span::symbol::sym;
use crate::clean::cfg::Cfg;
use crate::clean::inline::{load_attrs, merge_attrs};
use crate::clean::{CfgInfo, Crate, Item, ItemKind};
use crate::core::DocContext;
use crate::fold::DocFolder;
use crate::passes::Pass;
pub(crate) const PROPAGATE_DOC_CFG: Pass = Pass {
name: "propagate-doc-cfg",
run: Some(propagate_doc_cfg),
description: "propagates `#[doc(cfg(...))]` to child items",
};
pub(crate) fn propagate_doc_cfg(cr: Crate, cx: &mut DocContext<'_>) -> Crate {
CfgPropagator { parent_cfg: None, parent: None, cx, cfg_info: CfgInfo::default() }
.fold_crate(cr)
}
struct CfgPropagator<'a, 'tcx> {
parent_cfg: Option<Arc<Cfg>>,
parent: Option<LocalDefId>,
cx: &'a mut DocContext<'tcx>,
cfg_info: CfgInfo,
}
fn should_retain(token: &TokenTree) -> bool {
// We only keep `doc(cfg)` items.
matches!(
token,
TokenTree::Token(
Token {
kind: TokenKind::Ident(
ident,
_,
),
..
},
_,
) if *ident == sym::cfg,
)
}
fn filter_tokens_from_list(args_tokens: &TokenStream) -> Vec<TokenTree> {
let mut tokens = Vec::with_capacity(args_tokens.len());
let mut skip_next_delimited = false;
for token in args_tokens.iter() {
match token {
TokenTree::Delimited(..) => {
if !skip_next_delimited {
tokens.push(token.clone());
}
skip_next_delimited = false;
}
token if should_retain(token) => {
skip_next_delimited = false;
tokens.push(token.clone());
}
_ => {
skip_next_delimited = true;
}
}
}
tokens
}
// We only care about `#[cfg()]` and `#[doc(cfg())]`, we discard everything else.
fn add_only_cfg_attributes(attrs: &mut Vec<Attribute>, new_attrs: &[Attribute]) {
for attr in new_attrs {
if attr.is_doc_comment() {
continue;
}
let mut attr = attr.clone();
if let Attribute::Unparsed(ref mut normal) = attr
&& let [ident] = &*normal.path.segments
{
let ident = ident.name;
if ident == sym::doc
&& let AttrArgs::Delimited(args) = &mut normal.args
{
let tokens = filter_tokens_from_list(&args.tokens);
args.tokens = TokenStream::new(tokens);
attrs.push(attr);
} else if ident == sym::cfg_trace {
// If it's a `cfg()` attribute, we keep it.
attrs.push(attr);
}
}
}
}
impl CfgPropagator<'_, '_> {
// Some items need to merge their attributes with their parents' otherwise a few of them
// (mostly `cfg` ones) will be missing.
fn merge_with_parent_attributes(&mut self, item: &mut Item) {
let mut attrs = Vec::new();
// We only need to merge an item attributes with its parent's in case it's an impl as an
// impl might not be defined in the same module as the item it implements.
//
// Otherwise, `cfg_info` already tracks everything we need so nothing else to do!
if matches!(item.kind, ItemKind::ImplItem(_))
&& let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local())
{
let mut next_def_id = def_id;
while let Some(parent_def_id) = self.cx.tcx.opt_local_parent(next_def_id) {
let x = load_attrs(self.cx, parent_def_id.to_def_id());
add_only_cfg_attributes(&mut attrs, x);
next_def_id = parent_def_id;
}
}
let (_, cfg) = merge_attrs(
self.cx,
item.attrs.other_attrs.as_slice(),
Some((&attrs, None)),
&mut self.cfg_info,
);
item.inner.cfg = cfg;
}
}
impl DocFolder for CfgPropagator<'_, '_> {
fn fold_item(&mut self, mut item: Item) -> Option<Item> {
let old_cfg_info = self.cfg_info.clone();
let old_parent_cfg = self.parent_cfg.clone();
self.merge_with_parent_attributes(&mut item);
self.parent_cfg = item.inner.cfg.clone();
let old_parent =
if let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local()) {
self.parent.replace(def_id)
} else {
self.parent.take()
};
let result = self.fold_item_recur(item);
self.cfg_info = old_cfg_info;
self.parent_cfg = old_parent_cfg;
self.parent = old_parent;
Some(result)
}
}
|