Skip to content

Commit 6eb4735

Browse files
jyn514petrochenkov
andcommitted
Unify rustc and rustdoc parsing of cfg()
This extracts a new `parse_cfg` function that's used between both. - Treat `#[doc(cfg(x), cfg(y))]` the same as `#[doc(cfg(x)] #[doc(cfg(y))]`. Previously it would be completely ignored. - Treat `#[doc(inline, cfg(x))]` the same as `#[doc(inline)] #[doc(cfg(x))]`. Previously, the cfg would be ignored. - Pass the cfg predicate through to rustc_expand to be validated Co-authored-by: Vadim Petrochenkov <[email protected]>
1 parent 78c9639 commit 6eb4735

File tree

10 files changed

+82
-64
lines changed

10 files changed

+82
-64
lines changed

compiler/rustc_expand/src/config.rs

+29-25
Original file line numberDiff line numberDiff line change
@@ -464,31 +464,9 @@ impl<'a> StripUnconfigured<'a> {
464464
return true;
465465
}
466466
};
467-
let error = |span, msg, suggestion: &str| {
468-
let mut err = self.sess.parse_sess.span_diagnostic.struct_span_err(span, msg);
469-
if !suggestion.is_empty() {
470-
err.span_suggestion(
471-
span,
472-
"expected syntax is",
473-
suggestion.into(),
474-
Applicability::MaybeIncorrect,
475-
);
476-
}
477-
err.emit();
478-
true
479-
};
480-
let span = meta_item.span;
481-
match meta_item.meta_item_list() {
482-
None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"),
483-
Some([]) => error(span, "`cfg` predicate is not specified", ""),
484-
Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""),
485-
Some([single]) => match single.meta_item() {
486-
Some(meta_item) => {
487-
attr::cfg_matches(meta_item, &self.sess.parse_sess, self.features)
488-
}
489-
None => error(single.span(), "`cfg` predicate key cannot be a literal", ""),
490-
},
491-
}
467+
parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| {
468+
attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.features)
469+
})
492470
})
493471
}
494472

@@ -532,6 +510,32 @@ impl<'a> StripUnconfigured<'a> {
532510
}
533511
}
534512

513+
pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a MetaItem> {
514+
let error = |span, msg, suggestion: &str| {
515+
let mut err = sess.parse_sess.span_diagnostic.struct_span_err(span, msg);
516+
if !suggestion.is_empty() {
517+
err.span_suggestion(
518+
span,
519+
"expected syntax is",
520+
suggestion.into(),
521+
Applicability::HasPlaceholders,
522+
);
523+
}
524+
err.emit();
525+
None
526+
};
527+
let span = meta_item.span;
528+
match meta_item.meta_item_list() {
529+
None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"),
530+
Some([]) => error(span, "`cfg` predicate is not specified", ""),
531+
Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""),
532+
Some([single]) => match single.meta_item() {
533+
Some(meta_item) => Some(meta_item),
534+
None => error(single.span(), "`cfg` predicate key cannot be a literal", ""),
535+
},
536+
}
537+
}
538+
535539
fn is_cfg(sess: &Session, attr: &Attribute) -> bool {
536540
sess.check_name(attr, sym::cfg)
537541
}

src/librustdoc/clean/inline.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -307,10 +307,10 @@ fn merge_attrs(
307307
} else {
308308
Attributes::from_ast(&both, None)
309309
},
310-
both.cfg(cx.sess().diagnostic()),
310+
both.cfg(cx.sess()),
311311
)
312312
} else {
313-
(old_attrs.clean(cx), old_attrs.cfg(cx.sess().diagnostic()))
313+
(old_attrs.clean(cx), old_attrs.cfg(cx.sess()))
314314
}
315315
}
316316

src/librustdoc/clean/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2006,7 +2006,7 @@ fn clean_extern_crate(
20062006
def_id: crate_def_id,
20072007
visibility: krate.vis.clean(cx),
20082008
kind: box ExternCrateItem { src: orig_name },
2009-
cfg: attrs.cfg(cx.sess().diagnostic()),
2009+
cfg: attrs.cfg(cx.sess()),
20102010
}]
20112011
}
20122012

src/librustdoc/clean/types.rs

+21-33
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ impl Item {
320320
kind,
321321
box ast_attrs.clean(cx),
322322
cx,
323-
ast_attrs.cfg(cx.sess().diagnostic()),
323+
ast_attrs.cfg(cx.sess()),
324324
)
325325
}
326326

@@ -332,7 +332,7 @@ impl Item {
332332
cx: &mut DocContext<'_>,
333333
cfg: Option<Arc<Cfg>>,
334334
) -> Item {
335-
debug!("name={:?}, def_id={:?}", name, def_id);
335+
trace!("name={:?}, def_id={:?}", name, def_id);
336336

337337
Item {
338338
def_id,
@@ -681,7 +681,7 @@ crate trait AttributesExt {
681681

682682
fn other_attrs(&self) -> Vec<ast::Attribute>;
683683

684-
fn cfg(&self, diagnostic: &::rustc_errors::Handler) -> Option<Arc<Cfg>>;
684+
fn cfg(&self, sess: &Session) -> Option<Arc<Cfg>>;
685685
}
686686

687687
impl AttributesExt for [ast::Attribute] {
@@ -706,17 +706,28 @@ impl AttributesExt for [ast::Attribute] {
706706
self.iter().filter(|attr| attr.doc_str().is_none()).cloned().collect()
707707
}
708708

709-
fn cfg(&self, diagnostic: &::rustc_errors::Handler) -> Option<Arc<Cfg>> {
709+
fn cfg(&self, sess: &Session) -> Option<Arc<Cfg>> {
710710
let mut cfg = Cfg::True;
711711

712712
for attr in self.iter() {
713+
// #[doc]
713714
if attr.doc_str().is_none() && attr.has_name(sym::doc) {
714-
if let Some(mi) = attr.meta() {
715-
if let Some(cfg_mi) = Attributes::extract_cfg(&mi) {
716-
// Extracted #[doc(cfg(...))]
717-
match Cfg::parse(cfg_mi) {
718-
Ok(new_cfg) => cfg &= new_cfg,
719-
Err(e) => diagnostic.span_err(e.span, e.msg),
715+
// #[doc(...)]
716+
if let Some(list) = attr.meta().as_ref().and_then(|mi| mi.meta_item_list()) {
717+
for item in list {
718+
// #[doc(include)]
719+
if !item.has_name(sym::cfg) {
720+
continue;
721+
}
722+
// #[doc(cfg(...))]
723+
if let Some(cfg_mi) = item
724+
.meta_item()
725+
.and_then(|item| rustc_expand::config::parse_cfg(&item, sess))
726+
{
727+
match Cfg::parse(&cfg_mi) {
728+
Ok(new_cfg) => cfg &= new_cfg,
729+
Err(e) => sess.span_err(e.span, e.msg),
730+
}
720731
}
721732
}
722733
}
@@ -883,29 +894,6 @@ impl Attributes {
883894
self.other_attrs.lists(name)
884895
}
885896

886-
/// Extracts the content from an attribute `#[doc(cfg(content))]`.
887-
crate fn extract_cfg(mi: &ast::MetaItem) -> Option<&ast::MetaItem> {
888-
use rustc_ast::NestedMetaItem::MetaItem;
889-
890-
if let ast::MetaItemKind::List(ref nmis) = mi.kind {
891-
if nmis.len() == 1 {
892-
if let MetaItem(ref cfg_mi) = nmis[0] {
893-
if cfg_mi.has_name(sym::cfg) {
894-
if let ast::MetaItemKind::List(ref cfg_nmis) = cfg_mi.kind {
895-
if cfg_nmis.len() == 1 {
896-
if let MetaItem(ref content_mi) = cfg_nmis[0] {
897-
return Some(content_mi);
898-
}
899-
}
900-
}
901-
}
902-
}
903-
}
904-
}
905-
906-
None
907-
}
908-
909897
/// Reads a `MetaItem` from within an attribute, looks for whether it is a
910898
/// `#[doc(include="file")]`, and returns the filename and contents of the file as loaded from
911899
/// its expansion.

src/librustdoc/doctest.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1095,7 +1095,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
10951095
let ast_attrs = self.tcx.hir().attrs(hir_id);
10961096
let mut attrs = Attributes::from_ast(ast_attrs, None);
10971097

1098-
if let Some(ref cfg) = ast_attrs.cfg(self.sess.diagnostic()) {
1098+
if let Some(ref cfg) = ast_attrs.cfg(self.sess) {
10991099
if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features_untracked())) {
11001100
return;
11011101
}

src/librustdoc/html/render/context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ impl<'tcx> Context<'tcx> {
154154
&self.cache
155155
}
156156

157-
fn sess(&self) -> &'tcx Session {
157+
pub(super) fn sess(&self) -> &'tcx Session {
158158
&self.shared.tcx.sess
159159
}
160160

src/librustdoc/html/render/print_item.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
292292
let import_item = clean::Item {
293293
def_id: import_def_id,
294294
attrs: import_attrs,
295-
cfg: ast_attrs.cfg(cx.tcx().sess.diagnostic()),
295+
cfg: ast_attrs.cfg(cx.sess()),
296296
..myitem.clone()
297297
};
298298

src/test/rustdoc-ui/invalid-cfg.rs

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#![feature(doc_cfg)]
2+
#[doc(cfg = "x")] //~ ERROR not followed by parentheses
3+
#[doc(cfg(x, y))] //~ ERROR multiple `cfg` predicates
4+
struct S {}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: `cfg` is not followed by parentheses
2+
--> $DIR/invalid-cfg.rs:2:7
3+
|
4+
LL | #[doc(cfg = "x")]
5+
| ^^^^^^^^^ help: expected syntax is: `cfg(/* predicate */)`
6+
7+
error: multiple `cfg` predicates are specified
8+
--> $DIR/invalid-cfg.rs:3:14
9+
|
10+
LL | #[doc(cfg(x, y))]
11+
| ^
12+
13+
error: aborting due to 2 previous errors
14+

src/test/rustdoc/doc-cfg.rs

+8
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,11 @@ pub unsafe fn uses_target_feature() {
9191
pub fn uses_cfg_target_feature() {
9292
uses_target_feature();
9393
}
94+
95+
// multiple attributes should be allowed
96+
// @has doc_cfg/fn.multiple_attrs.html \
97+
// '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
98+
// 'This is supported on x and y and z only.'
99+
#[doc(inline, cfg(x))]
100+
#[doc(cfg(y), cfg(z))]
101+
pub fn multiple_attrs() {}

0 commit comments

Comments
 (0)