Skip to content

Commit 6c8001b

Browse files
authored
Rollup merge of rust-lang#96008 - fmease:warn-on-useless-doc-hidden-on-assoc-impl-items, r=lcnr
Warn on unused `#[doc(hidden)]` attributes on trait impl items [Zulip conversation](https://rust-lang.zulipchat.com/#narrow/stream/266220-rustdoc/topic/.E2.9C.94.20Validy.20checks.20for.20.60.23.5Bdoc.28hidden.29.5D.60). Whether an associated item in a trait impl is shown or hidden in the documentation entirely depends on the corresponding item in the trait declaration. Rustdoc completely ignores `#[doc(hidden)]` attributes on impl items. No error or warning is emitted: ```rust pub trait Tr { fn f(); } pub struct Ty; impl Tr for Ty { #[doc(hidden)] fn f() {} } // ^^^^^^^^^^^^^^ ignored by rustdoc and currently // no error or warning issued ``` This may lead users to the wrong belief that the attribute has an effect. In fact, several such cases are found in the standard library (I've removed all of them in this PR). There does not seem to exist any incentive to allow this in the future either: Impl'ing a trait for a type means the type *fully* conforms to its API. Users can add `#[doc(hidden)]` to the whole impl if they want to hide the implementation or add the attribute to the corresponding associated item in the trait declaration to hide the specific item. Hiding an implementation of an associated item does not make much sense: The associated item can still be found on the trait page. This PR emits the warn-by-default lint `unused_attribute` for this case with a future-incompat warning. `@rustbot` label T-compiler T-rustdoc A-lint
2 parents 28d800c + 9d157ad commit 6c8001b

File tree

18 files changed

+225
-26
lines changed

18 files changed

+225
-26
lines changed

compiler/rustc_passes/src/check_attr.rs

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
//! conflicts between multiple such attributes attached to the same
55
//! item.
66
7-
use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
7+
use rustc_ast::tokenstream::DelimSpan;
8+
use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MacArgs, MetaItemKind, NestedMetaItem};
89
use rustc_data_structures::fx::FxHashMap;
910
use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
1011
use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
@@ -810,6 +811,68 @@ impl CheckAttrVisitor<'_> {
810811
}
811812
}
812813

814+
/// Checks `#[doc(hidden)]` attributes. Returns `true` if valid.
815+
fn check_doc_hidden(
816+
&self,
817+
attr: &Attribute,
818+
meta_index: usize,
819+
meta: &NestedMetaItem,
820+
hir_id: HirId,
821+
target: Target,
822+
) -> bool {
823+
if let Target::AssocConst
824+
| Target::AssocTy
825+
| Target::Method(MethodKind::Trait { body: true }) = target
826+
{
827+
let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
828+
let containing_item = self.tcx.hir().expect_item(parent_hir_id);
829+
830+
if Target::from_item(containing_item) == Target::Impl {
831+
let meta_items = attr.meta_item_list().unwrap();
832+
833+
let (span, replacement_span) = if meta_items.len() == 1 {
834+
(attr.span, attr.span)
835+
} else {
836+
let meta_span = meta.span();
837+
(
838+
meta_span,
839+
meta_span.until(match meta_items.get(meta_index + 1) {
840+
Some(next_item) => next_item.span(),
841+
None => match attr.get_normal_item().args {
842+
MacArgs::Delimited(DelimSpan { close, .. }, ..) => close,
843+
_ => unreachable!(),
844+
},
845+
}),
846+
)
847+
};
848+
849+
// FIXME: #[doc(hidden)] was previously erroneously allowed on trait impl items,
850+
// so for backward compatibility only emit a warning and do not mark it as invalid.
851+
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, span, |lint| {
852+
lint.build("`#[doc(hidden)]` is ignored on trait impl items")
853+
.warn(
854+
"this was previously accepted by the compiler but is \
855+
being phased out; it will become a hard error in \
856+
a future release!",
857+
)
858+
.note(
859+
"whether the impl item is `doc(hidden)` or not \
860+
entirely depends on the corresponding trait item",
861+
)
862+
.span_suggestion(
863+
replacement_span,
864+
"remove this attribute",
865+
String::new(),
866+
Applicability::MachineApplicable,
867+
)
868+
.emit();
869+
});
870+
}
871+
}
872+
873+
true
874+
}
875+
813876
/// Checks that an attribute is *not* used at the crate level. Returns `true` if valid.
814877
fn check_attr_not_crate_level(
815878
&self,
@@ -928,7 +991,7 @@ impl CheckAttrVisitor<'_> {
928991
let mut is_valid = true;
929992

930993
if let Some(mi) = attr.meta() && let Some(list) = mi.meta_item_list() {
931-
for meta in list {
994+
for (meta_index, meta) in list.into_iter().enumerate() {
932995
if let Some(i_meta) = meta.meta_item() {
933996
match i_meta.name_or_empty() {
934997
sym::alias
@@ -969,6 +1032,15 @@ impl CheckAttrVisitor<'_> {
9691032
is_valid = false;
9701033
}
9711034

1035+
sym::hidden if !self.check_doc_hidden(attr,
1036+
meta_index,
1037+
meta,
1038+
hir_id,
1039+
target,
1040+
) => {
1041+
is_valid = false;
1042+
}
1043+
9721044
// no_default_passes: deprecated
9731045
// passes: deprecated
9741046
// plugins: removed, but rustdoc warns about it itself

library/alloc/src/collections/vec_deque/iter.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ impl<'a, T> Iterator for Iter<'a, T> {
122122
}
123123

124124
#[inline]
125-
#[doc(hidden)]
126125
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
127126
// Safety: The TrustedRandomAccess contract requires that callers only pass an index
128127
// that is in bounds.

library/alloc/src/collections/vec_deque/iter_mut.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ impl<'a, T> Iterator for IterMut<'a, T> {
100100
}
101101

102102
#[inline]
103-
#[doc(hidden)]
104103
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
105104
// Safety: The TrustedRandomAccess contract requires that callers only pass an index
106105
// that is in bounds.

library/alloc/src/vec/into_iter.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,6 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
202202
self.len()
203203
}
204204

205-
#[doc(hidden)]
206205
unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> Self::Item
207206
where
208207
Self: TrustedRandomAccessNoCoerce,

library/core/src/convert/num.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ macro_rules! impl_float_to_int {
2525
$(
2626
#[unstable(feature = "convert_float_to_int", issue = "67057")]
2727
impl FloatToInt<$Int> for $Float {
28-
#[doc(hidden)]
2928
#[inline]
3029
unsafe fn to_int_unchecked(self) -> $Int {
3130
// SAFETY: the safety contract must be upheld by the caller.

library/core/src/iter/adapters/cloned.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ where
6060
self.it.map(T::clone).fold(init, f)
6161
}
6262

63-
#[doc(hidden)]
6463
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> T
6564
where
6665
Self: TrustedRandomAccessNoCoerce,

library/core/src/iter/adapters/copied.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ where
8181
self.it.advance_by(n)
8282
}
8383

84-
#[doc(hidden)]
8584
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> T
8685
where
8786
Self: TrustedRandomAccessNoCoerce,

library/core/src/iter/adapters/enumerate.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@ where
128128
}
129129

130130
#[rustc_inherit_overflow_checks]
131-
#[doc(hidden)]
132131
#[inline]
133132
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> <Self as Iterator>::Item
134133
where

library/core/src/iter/adapters/fuse.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ where
129129
}
130130

131131
#[inline]
132-
#[doc(hidden)]
133132
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item
134133
where
135134
Self: TrustedRandomAccessNoCoerce,

library/core/src/iter/adapters/map.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ where
124124
self.iter.fold(init, map_fold(self.f, g))
125125
}
126126

127-
#[doc(hidden)]
128127
#[inline]
129128
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> B
130129
where

0 commit comments

Comments
 (0)