Add code documentation, improve code and improve error message

This commit is contained in:
Guillaume Gomez 2025-05-27 17:14:46 +02:00
parent c06a076634
commit 1561efe41a
5 changed files with 28 additions and 14 deletions

View file

@ -147,7 +147,7 @@ passes_doc_auto_cfg_expects_hide_or_show =
`only "hide" or "show" are allowed in "#[doc(auto_cfg(...))]"`
passes_doc_auto_cfg_hide_show_expects_list =
`#![doc(auto_cfg({$attr_name}(...)))]` only expects a list of items
`#![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

View file

@ -10,7 +10,7 @@ use std::collections::hash_map::Entry;
use std::slice;
use rustc_abi::{Align, ExternAbi, Size};
use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, ast};
use rustc_ast::{AttrStyle, LitKind, MetaItem, MetaItemInner, MetaItemKind, ast};
use rustc_attr_parsing::{AttributeParser, Late};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey};
@ -1161,10 +1161,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
/// Check that the `#![doc(auto_cfg(..))]` attribute has expected input.
fn check_doc_auto_cfg(&self, meta: &MetaItemInner, hir_id: HirId) {
let MetaItemInner::MetaItem(meta) = meta else {
unreachable!();
};
fn check_doc_auto_cfg(&self, meta: &MetaItem, hir_id: HirId) {
match &meta.kind {
MetaItemKind::Word => {}
MetaItemKind::NameValue(lit) => {
@ -1286,7 +1283,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
Some(sym::auto_cfg) => {
self.check_doc_auto_cfg(meta, hir_id);
self.check_doc_auto_cfg(i_meta, hir_id);
}
Some(sym::inline | sym::no_inline) => {

View file

@ -922,11 +922,19 @@ pub(crate) fn hir_attr_lists<'a, I: IntoIterator<Item = &'a hir::Attribute>>(
.flatten()
}
/// This type keeps track of (doc) cfg information as we go down the item tree.
#[derive(Clone, Debug)]
pub(crate) struct CfgInfo {
/// List of `doc(auto_cfg(hide(...)))` cfgs.
hidden_cfg: FxHashSet<Cfg>,
/// Current computed `cfg`. Each time we enter a new item, this field is updated as well while
/// taking into account the `hidden_cfg` information.
current_cfg: Cfg,
/// Whether the `doc(auto_cfg())` feature is enabled or not at this point.
auto_cfg_active: bool,
/// If the parent item used `doc(cfg(...))`, then we don't want to overwrite `current_cfg`,
/// instead we will concatenate with it. However, if it's not the case, we need to overwrite
/// `current_cfg`.
parent_is_doc_cfg: bool,
}
@ -962,6 +970,11 @@ 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.
///
/// 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.
fn handle_auto_cfg_hide_show(
tcx: TyCtxt<'_>,
cfg_info: &mut CfgInfo,

View file

@ -30,7 +30,8 @@ struct CfgPropagator<'a, 'tcx> {
cfg_info: CfgInfo,
}
fn should_retain(token: &TokenTree) -> bool {
/// Returns true if the provided `token` is a `cfg` ident.
fn is_cfg_token(token: &TokenTree) -> bool {
// We only keep `doc(cfg)` items.
matches!(
token,
@ -47,7 +48,9 @@ fn should_retain(token: &TokenTree) -> bool {
)
}
fn filter_tokens_from_list(args_tokens: &TokenStream) -> Vec<TokenTree> {
/// We only want to keep `#[cfg()]` and `#[doc(cfg())]` attributes so we rebuild a vec of
/// `TokenTree` with only the tokens we're interested into.
fn filter_non_cfg_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() {
@ -58,7 +61,7 @@ fn filter_tokens_from_list(args_tokens: &TokenStream) -> Vec<TokenTree> {
}
skip_next_delimited = false;
}
token if should_retain(token) => {
token if is_cfg_token(token) => {
skip_next_delimited = false;
tokens.push(token.clone());
}
@ -70,7 +73,8 @@ fn filter_tokens_from_list(args_tokens: &TokenStream) -> Vec<TokenTree> {
tokens
}
// We only care about `#[cfg()]` and `#[doc(cfg())]`, we discard everything else.
/// This function goes through the attributes list (`new_attrs`) and extract the `cfg` tokens from
/// it and put them into `attrs`.
fn add_only_cfg_attributes(attrs: &mut Vec<Attribute>, new_attrs: &[Attribute]) {
for attr in new_attrs {
if attr.is_doc_comment() {
@ -84,7 +88,7 @@ fn add_only_cfg_attributes(attrs: &mut Vec<Attribute>, new_attrs: &[Attribute])
if ident == sym::doc
&& let AttrArgs::Delimited(args) = &mut normal.args
{
let tokens = filter_tokens_from_list(&args.tokens);
let tokens = filter_non_cfg_tokens_from_list(&args.tokens);
args.tokens = TokenStream::new(tokens);
attrs.push(attr);
} else if ident == sym::cfg_trace {

View file

@ -1,4 +1,4 @@
error: `#![doc(auto_cfg(hide(...)))]` only expects a list of items
error: `#![doc(auto_cfg(hide(...)))]` expects a list of items
--> $DIR/doc_cfg_hide.rs:2:8
|
LL | #![doc(auto_cfg(hide = "test"))]
@ -6,7 +6,7 @@ LL | #![doc(auto_cfg(hide = "test"))]
|
= note: `#[deny(invalid_doc_attributes)]` on by default
error: `#![doc(auto_cfg(hide(...)))]` only expects a list of items
error: `#![doc(auto_cfg(hide(...)))]` expects a list of items
--> $DIR/doc_cfg_hide.rs:3:8
|
LL | #![doc(auto_cfg(hide))]