Improve code and better check doc(cfg(...)) attributes

This commit is contained in:
Guillaume Gomez
2025-05-29 18:39:08 +02:00
parent 1561efe41a
commit 553308b115
13 changed files with 213 additions and 79 deletions

View File

@@ -183,6 +183,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
gate_doc!(
"experimental" {
cfg => doc_cfg
auto_cfg => doc_cfg
masked => doc_masked
notable_trait => doc_notable_trait
}

View File

@@ -150,7 +150,7 @@ passes_doc_auto_cfg_hide_show_expects_list =
`#![doc(auto_cfg({$attr_name}(...)))]` expects a list of items
passes_doc_auto_cfg_hide_show_unexpected_item =
`#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/values items
`#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/value items
passes_doc_auto_cfg_wrong_literal =
`expected boolean for #[doc(auto_cfg = ...)]`

View File

@@ -1176,7 +1176,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
MetaItemKind::List(list) => {
for item in list {
let Some(attr_name) = item.name() else { continue };
let Some(attr_name) = item.name() else {
self.tcx.emit_node_span_lint(
INVALID_DOC_ATTRIBUTES,
hir_id,
meta.span,
errors::DocAutoCfgExpectsHideOrShow,
);
continue;
};
if attr_name != sym::hide && attr_name != sym::show {
self.tcx.emit_node_span_lint(
INVALID_DOC_ATTRIBUTES,
@@ -1195,6 +1203,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
attr_name: attr_name.as_str(),
},
);
} else if match item {
MetaItemInner::Lit(_) => true,
// We already checked above that it's not a list.
MetaItemInner::MetaItem(meta) => meta.path.segments.len() != 1,
} {
self.tcx.emit_node_span_lint(
INVALID_DOC_ATTRIBUTES,
hir_id,
item.span(),
errors::DocAutoCfgHideShowUnexpectedItem {
attr_name: attr_name.as_str(),
},
);
}
}
} else {

View File

@@ -1150,7 +1150,6 @@ symbols! {
hashset_iter_ty,
hexagon_target_feature,
hidden,
hidden_cfg,
hide,
hint,
homogeneous_aggregate,

View File

@@ -605,6 +605,10 @@ pub(crate) fn build_impl(
});
}
// In here, we pass an empty `CfgInfo` because the computation of `cfg` happens later, so it
// doesn't matter at this point.
//
// We need to pass this empty `CfgInfo` because `merge_attrs` is used when computing the `cfg`.
let (merged_attrs, cfg) = merge_attrs(cx, load_attrs(cx, did), attrs, &mut CfgInfo::default());
trace!("merged_attrs={merged_attrs:?}");

View File

@@ -970,8 +970,10 @@ fn show_hide_show_conflict_error(
diag.emit();
}
/// This function checks if a same `cfg` is present in both `auto_cfg(hide(...))` and
/// `auto_cfg(show(...))` on the same item. If so, it emits an error.
/// This functions updates the `hidden_cfg` field of the provided `cfg_info` argument.
///
/// It also checks if a same `cfg` is present in both `auto_cfg(hide(...))` and
/// `auto_cfg(show(...))` on the same item and emits an error if it's the case.
///
/// Because we go through a list of `cfg`s, we keep track of the `cfg`s we saw in `new_show_attrs`
/// and in `new_hide_attrs` arguments.
@@ -1023,6 +1025,31 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute>
Some(item)
}
fn check_changed_auto_active_status(
changed_auto_active_status: &mut Option<rustc_span::Span>,
attr: &ast::MetaItem,
cfg_info: &mut CfgInfo,
tcx: TyCtxt<'_>,
new_value: bool,
) -> bool {
if let Some(first_change) = changed_auto_active_status {
if cfg_info.auto_cfg_active != new_value {
tcx.sess
.dcx()
.struct_span_err(
vec![*first_change, attr.span],
"`auto_cfg` was disabled and enabled more than once on the same item",
)
.emit();
return true;
}
} else {
*changed_auto_active_status = Some(attr.span);
}
cfg_info.auto_cfg_active = new_value;
false
}
let mut new_show_attrs = FxHashMap::default();
let mut new_hide_attrs = FxHashMap::default();
@@ -1070,49 +1097,39 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute>
};
match &attr.kind {
MetaItemKind::Word => {
if let Some(first_change) = changed_auto_active_status {
if !cfg_info.auto_cfg_active {
tcx.sess.dcx().struct_span_err(
vec![first_change, attr.span],
"`auto_cfg` was disabled and enabled more than once on the same item",
).emit();
if check_changed_auto_active_status(
&mut changed_auto_active_status,
attr,
cfg_info,
tcx,
true,
) {
return None;
}
} else {
changed_auto_active_status = Some(attr.span);
}
cfg_info.auto_cfg_active = true;
}
MetaItemKind::NameValue(lit) => {
if let LitKind::Bool(value) = lit.kind {
if let Some(first_change) = changed_auto_active_status {
if cfg_info.auto_cfg_active != value {
tcx.sess.dcx().struct_span_err(
vec![first_change, attr.span],
"`auto_cfg` was disabled and enabled more than once on the same item",
).emit();
if check_changed_auto_active_status(
&mut changed_auto_active_status,
attr,
cfg_info,
tcx,
value,
) {
return None;
}
} else {
changed_auto_active_status = Some(attr.span);
}
cfg_info.auto_cfg_active = value;
}
}
MetaItemKind::List(sub_attrs) => {
if let Some(first_change) = changed_auto_active_status {
if !cfg_info.auto_cfg_active {
tcx.sess.dcx().struct_span_err(
vec![first_change, attr.span],
"`auto_cfg` was disabled and enabled more than once on the same item",
).emit();
if check_changed_auto_active_status(
&mut changed_auto_active_status,
attr,
cfg_info,
tcx,
true,
) {
return None;
}
} else {
changed_auto_active_status = Some(attr.span);
}
// Whatever happens next, the feature is enabled again.
cfg_info.auto_cfg_active = true;
for sub_attr in sub_attrs.iter() {
if let Some(ident) = sub_attr.ident()
&& (ident.name == sym::show || ident.name == sym::hide)

View File

@@ -8,4 +8,13 @@
//~^^ WARN unexpected `cfg` condition name: `bar`
#[doc(cfg())] //~ ERROR
#[doc(cfg(foo, bar))] //~ ERROR
#[doc(auto_cfg(42))] //~ ERROR
#[doc(auto_cfg(hide(true)))] //~ ERROR
#[doc(auto_cfg(hide(42)))] //~ ERROR
#[doc(auto_cfg(hide("a")))] //~ ERROR
#[doc(auto_cfg(hide(foo::bar)))] //~ ERROR
// Shouldn't lint
#[doc(auto_cfg(hide(windows)))]
#[doc(auto_cfg(hide(feature = "windows")))]
#[doc(auto_cfg(hide(foo)))]
pub fn foo() {}

View File

@@ -1,3 +1,55 @@
error: `only "hide" or "show" are allowed in "#[doc(auto_cfg(...))]"`
--> $DIR/doc-cfg.rs:11:7
|
LL | #[doc(auto_cfg(42))]
| ^^^^^^^^^^^^
|
= note: `#[deny(invalid_doc_attributes)]` on by default
error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items
--> $DIR/doc-cfg.rs:12:21
|
LL | #[doc(auto_cfg(hide(true)))]
| ^^^^
error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items
--> $DIR/doc-cfg.rs:13:21
|
LL | #[doc(auto_cfg(hide(42)))]
| ^^
error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items
--> $DIR/doc-cfg.rs:14:21
|
LL | #[doc(auto_cfg(hide("a")))]
| ^^^
error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items
--> $DIR/doc-cfg.rs:15:21
|
LL | #[doc(auto_cfg(hide(foo::bar)))]
| ^^^^^^^^
warning: unexpected `cfg` condition name: `foo`
--> $DIR/doc-cfg.rs:6:11
|
LL | #[doc(cfg(foo), cfg(bar))]
| ^^^
|
= help: expected names are: `FALSE` and `test` and 31 more
= help: to expect this configuration use `--check-cfg=cfg(foo)`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration
= note: `#[warn(unexpected_cfgs)]` on by default
warning: unexpected `cfg` condition name: `bar`
--> $DIR/doc-cfg.rs:6:21
|
LL | #[doc(cfg(foo), cfg(bar))]
| ^^^
|
= help: to expect this configuration use `--check-cfg=cfg(bar)`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration
error: `cfg` predicate is not specified
--> $DIR/doc-cfg.rs:3:7
|
@@ -22,25 +74,5 @@ error: multiple `cfg` predicates are specified
LL | #[doc(cfg(foo, bar))]
| ^^^
warning: unexpected `cfg` condition name: `foo`
--> $DIR/doc-cfg.rs:6:11
|
LL | #[doc(cfg(foo), cfg(bar))]
| ^^^
|
= help: expected names are: `FALSE` and `test` and 31 more
= help: to expect this configuration use `--check-cfg=cfg(foo)`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration
= note: `#[warn(unexpected_cfgs)]` on by default
warning: unexpected `cfg` condition name: `bar`
--> $DIR/doc-cfg.rs:6:21
|
LL | #[doc(cfg(foo), cfg(bar))]
| ^^^
|
= help: to expect this configuration use `--check-cfg=cfg(bar)`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration
error: aborting due to 4 previous errors; 2 warnings emitted
error: aborting due to 9 previous errors; 2 warnings emitted

View File

@@ -0,0 +1,6 @@
#![doc(auto_cfg)] //~ ERROR
#![doc(auto_cfg(false))] //~ ERROR
#![doc(auto_cfg(true))] //~ ERROR
#![doc(auto_cfg(hide(feature = "solecism")))] //~ ERROR
#![doc(auto_cfg(show(feature = "bla")))] //~ ERROR
#![doc(cfg(feature = "solecism"))] //~ ERROR

View File

@@ -0,0 +1,63 @@
error[E0658]: `#[doc(auto_cfg)]` is experimental
--> $DIR/feature-gate-doc_cfg.rs:1:1
|
LL | #![doc(auto_cfg)]
| ^^^^^^^^^^^^^^^^^
|
= note: see issue #43781 <https://github.com/rust-lang/rust/issues/43781> for more information
= help: add `#![feature(doc_cfg)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: `#[doc(auto_cfg)]` is experimental
--> $DIR/feature-gate-doc_cfg.rs:2:1
|
LL | #![doc(auto_cfg(false))]
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #43781 <https://github.com/rust-lang/rust/issues/43781> for more information
= help: add `#![feature(doc_cfg)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: `#[doc(auto_cfg)]` is experimental
--> $DIR/feature-gate-doc_cfg.rs:3:1
|
LL | #![doc(auto_cfg(true))]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #43781 <https://github.com/rust-lang/rust/issues/43781> for more information
= help: add `#![feature(doc_cfg)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: `#[doc(auto_cfg)]` is experimental
--> $DIR/feature-gate-doc_cfg.rs:4:1
|
LL | #![doc(auto_cfg(hide(feature = "solecism")))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #43781 <https://github.com/rust-lang/rust/issues/43781> for more information
= help: add `#![feature(doc_cfg)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: `#[doc(auto_cfg)]` is experimental
--> $DIR/feature-gate-doc_cfg.rs:5:1
|
LL | #![doc(auto_cfg(show(feature = "bla")))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #43781 <https://github.com/rust-lang/rust/issues/43781> for more information
= help: add `#![feature(doc_cfg)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: `#[doc(cfg)]` is experimental
--> $DIR/feature-gate-doc_cfg.rs:6:1
|
LL | #![doc(cfg(feature = "solecism"))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #43781 <https://github.com/rust-lang/rust/issues/43781> for more information
= help: add `#![feature(doc_cfg)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0658`.

View File

@@ -1,8 +0,0 @@
// FIXME: Remove this file once feature is removed
#![doc(cfg_hide(test))] //~ ERROR
#[cfg(not(test))]
pub fn public_fn() {}
#[cfg(test)]
pub fn internal_use_only() {}

View File

@@ -1,10 +0,0 @@
error: unknown `doc` attribute `cfg_hide`
--> $DIR/feature-gate-doc_cfg_hide.rs:3:8
|
LL | #![doc(cfg_hide(test))]
| ^^^^^^^^^^^^^^
|
= note: `#[deny(invalid_doc_attributes)]` on by default
error: aborting due to 1 previous error

View File

@@ -12,7 +12,7 @@ error: `#![doc(auto_cfg(hide(...)))]` expects a list of items
LL | #![doc(auto_cfg(hide))]
| ^^^^^^^^^^^^^^
error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/values items
error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items
--> $DIR/doc_cfg_hide.rs:4:22
|
LL | #![doc(auto_cfg(hide(not(windows))))]