Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit f948c79

Browse files
Rollup merge of #142472 - GuillaumeGomez:doc-attribute-attribute, r=fmease
Add new `doc(attribute = "...")` attribute Fixes #141123. The implementation and purpose of this new `#[doc(attribute = "...")]` attribute is very close to `#[doc(keyword = "...")]`. Which means that luckily for us, most of the code needed was already in place and `@Noratrieb` nicely wrote a first draft that helped me implement this new attribute very fast. Now with all this said, there is one thing I didn't do yet: adding a `rustdoc-js-std` test. I added GUI tests with search results for attributes so should be fine but I still plan on adding one for it once documentation for builtin attributes will be written into the core/std libs. You can test it [here](https://rustdoc.crud.net/imperio/doc-attribute-attribute/foo/index.html). cc `@Noratrieb` `@Veykril`
2 parents 1f7dcc8 + f3c0234 commit f948c79

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+376
-55
lines changed

‎compiler/rustc_ast_passes/src/feature_gate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
188188
notable_trait => doc_notable_trait
189189
}
190190
"meant for internal use only" {
191+
attribute => rustdoc_internals
191192
keyword => rustdoc_internals
192193
fake_variadic => rustdoc_internals
193194
search_unbox => rustdoc_internals

‎compiler/rustc_passes/messages.ftl

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@ passes_doc_alias_start_end =
145145
passes_doc_attr_not_crate_level =
146146
`#![doc({$attr_name} = "...")]` isn't allowed as a crate-level attribute
147147
148+
passes_doc_attribute_not_attribute =
149+
nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = "...")]`
150+
.help = only existing builtin attributes are allowed in core/std
151+
148152
passes_doc_cfg_hide_takes_list =
149153
`#[doc(cfg_hide(...))]` takes a list of attributes
150154
@@ -173,16 +177,16 @@ passes_doc_inline_only_use =
173177
passes_doc_invalid =
174178
invalid `doc` attribute
175179
176-
passes_doc_keyword_empty_mod =
177-
`#[doc(keyword = "...")]` should be used on empty modules
180+
passes_doc_keyword_attribute_empty_mod =
181+
`#[doc({$attr_name} = "...")]` should be used on empty modules
182+
183+
passes_doc_keyword_attribute_not_mod =
184+
`#[doc({$attr_name} = "...")]` should be used on modules
178185
179186
passes_doc_keyword_not_keyword =
180187
nonexistent keyword `{$keyword}` used in `#[doc(keyword = "...")]`
181188
.help = only existing keywords are allowed in core/std
182189
183-
passes_doc_keyword_not_mod =
184-
`#[doc(keyword = "...")]` should be used on modules
185-
186190
passes_doc_keyword_only_impl =
187191
`#[doc(keyword = "...")]` should be used on impl blocks
188192

‎compiler/rustc_passes/src/check_attr.rs

Lines changed: 68 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,21 @@ impl IntoDiagArg for ProcMacroKind {
9999
}
100100
}
101101

102+
#[derive(Clone, Copy)]
103+
enum DocFakeItemKind {
104+
Attribute,
105+
Keyword,
106+
}
107+
108+
impl DocFakeItemKind {
109+
fn name(self) -> &'static str {
110+
match self {
111+
Self::Attribute => "attribute",
112+
Self::Keyword => "keyword",
113+
}
114+
}
115+
}
116+
102117
struct CheckAttrVisitor<'tcx> {
103118
tcx: TyCtxt<'tcx>,
104119

@@ -853,17 +868,27 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
853868
}
854869
}
855870

856-
fn check_doc_keyword(&self, meta: &MetaItemInner, hir_id: HirId) {
871+
fn check_doc_keyword_and_attribute(
872+
&self,
873+
meta: &MetaItemInner,
874+
hir_id: HirId,
875+
attr_kind: DocFakeItemKind,
876+
) {
857877
fn is_doc_keyword(s: Symbol) -> bool {
858878
// FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we
859879
// can remove the `SelfTy` case here, remove `sym::SelfTy`, and update the
860880
// `#[doc(keyword = "SelfTy")` attribute in `library/std/src/keyword_docs.rs`.
861881
s.is_reserved(|| edition::LATEST_STABLE_EDITION) || s.is_weak() || s == sym::SelfTy
862882
}
863883

864-
let doc_keyword = match meta.value_str() {
884+
// FIXME: This should support attributes with namespace like `diagnostic::do_not_recommend`.
885+
fn is_builtin_attr(s: Symbol) -> bool {
886+
rustc_feature::BUILTIN_ATTRIBUTE_MAP.contains_key(&s)
887+
}
888+
889+
let value = match meta.value_str() {
865890
Some(value) if value != sym::empty => value,
866-
_ => return self.doc_attr_str_error(meta, "keyword"),
891+
_ => return self.doc_attr_str_error(meta, attr_kind.name()),
867892
};
868893

869894
let item_kind = match self.tcx.hir_node(hir_id) {
@@ -873,20 +898,38 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
873898
match item_kind {
874899
Some(ItemKind::Mod(_, module)) => {
875900
if !module.item_ids.is_empty() {
876-
self.dcx().emit_err(errors::DocKeywordEmptyMod { span: meta.span() });
901+
self.dcx().emit_err(errors::DocKeywordAttributeEmptyMod {
902+
span: meta.span(),
903+
attr_name: attr_kind.name(),
904+
});
877905
return;
878906
}
879907
}
880908
_ => {
881-
self.dcx().emit_err(errors::DocKeywordNotMod { span: meta.span() });
909+
self.dcx().emit_err(errors::DocKeywordAttributeNotMod {
910+
span: meta.span(),
911+
attr_name: attr_kind.name(),
912+
});
882913
return;
883914
}
884915
}
885-
if !is_doc_keyword(doc_keyword) {
886-
self.dcx().emit_err(errors::DocKeywordNotKeyword {
887-
span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
888-
keyword: doc_keyword,
889-
});
916+
match attr_kind {
917+
DocFakeItemKind::Keyword => {
918+
if !is_doc_keyword(value) {
919+
self.dcx().emit_err(errors::DocKeywordNotKeyword {
920+
span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
921+
keyword: value,
922+
});
923+
}
924+
}
925+
DocFakeItemKind::Attribute => {
926+
if !is_builtin_attr(value) {
927+
self.dcx().emit_err(errors::DocAttributeNotAttribute {
928+
span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
929+
attribute: value,
930+
});
931+
}
932+
}
890933
}
891934
}
892935

@@ -1146,7 +1189,21 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
11461189

11471190
Some(sym::keyword) => {
11481191
if self.check_attr_not_crate_level(meta, hir_id, "keyword") {
1149-
self.check_doc_keyword(meta, hir_id);
1192+
self.check_doc_keyword_and_attribute(
1193+
meta,
1194+
hir_id,
1195+
DocFakeItemKind::Keyword,
1196+
);
1197+
}
1198+
}
1199+
1200+
Some(sym::attribute) => {
1201+
if self.check_attr_not_crate_level(meta, hir_id, "attribute") {
1202+
self.check_doc_keyword_and_attribute(
1203+
meta,
1204+
hir_id,
1205+
DocFakeItemKind::Attribute,
1206+
);
11501207
}
11511208
}
11521209

‎compiler/rustc_passes/src/errors.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,10 +195,11 @@ pub(crate) struct DocAliasMalformed {
195195
}
196196

197197
#[derive(Diagnostic)]
198-
#[diag(passes_doc_keyword_empty_mod)]
199-
pub(crate) struct DocKeywordEmptyMod {
198+
#[diag(passes_doc_keyword_attribute_empty_mod)]
199+
pub(crate) struct DocKeywordAttributeEmptyMod {
200200
#[primary_span]
201201
pub span: Span,
202+
pub attr_name: &'static str,
202203
}
203204

204205
#[derive(Diagnostic)]
@@ -211,10 +212,20 @@ pub(crate) struct DocKeywordNotKeyword {
211212
}
212213

213214
#[derive(Diagnostic)]
214-
#[diag(passes_doc_keyword_not_mod)]
215-
pub(crate) struct DocKeywordNotMod {
215+
#[diag(passes_doc_attribute_not_attribute)]
216+
#[help]
217+
pub(crate) struct DocAttributeNotAttribute {
218+
#[primary_span]
219+
pub span: Span,
220+
pub attribute: Symbol,
221+
}
222+
223+
#[derive(Diagnostic)]
224+
#[diag(passes_doc_keyword_attribute_not_mod)]
225+
pub(crate) struct DocKeywordAttributeNotMod {
216226
#[primary_span]
217227
pub span: Span,
228+
pub attr_name: &'static str,
218229
}
219230

220231
#[derive(Diagnostic)]

‎compiler/rustc_resolve/src/late.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5016,7 +5016,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
50165016
}
50175017
ResolveDocLinks::Exported
50185018
if !maybe_exported.eval(self.r)
5019-
&& !rustdoc::has_primitive_or_keyword_docs(attrs) =>
5019+
&& !rustdoc::has_primitive_or_keyword_or_attribute_docs(attrs) =>
50205020
{
50215021
return;
50225022
}

‎compiler/rustc_resolve/src/rustdoc.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -373,16 +373,16 @@ pub fn inner_docs(attrs: &[impl AttributeExt]) -> bool {
373373
true
374374
}
375375

376-
/// Has `#[rustc_doc_primitive]` or `#[doc(keyword)]`.
377-
pub fn has_primitive_or_keyword_docs(attrs: &[impl AttributeExt]) -> bool {
376+
/// Has `#[rustc_doc_primitive]` or `#[doc(keyword)]` or `#[doc(attribute)]`.
377+
pub fn has_primitive_or_keyword_or_attribute_docs(attrs: &[impl AttributeExt]) -> bool {
378378
for attr in attrs {
379379
if attr.has_name(sym::rustc_doc_primitive) {
380380
return true;
381381
} else if attr.has_name(sym::doc)
382382
&& let Some(items) = attr.meta_item_list()
383383
{
384384
for item in items {
385-
if item.has_name(sym::keyword) {
385+
if item.has_name(sym::keyword) || item.has_name(sym::attribute){
386386
return true;
387387
}
388388
}

‎compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,7 @@ symbols! {
541541
att_syntax,
542542
attr,
543543
attr_literals,
544+
attribute,
544545
attributes,
545546
audit_that,
546547
augmented_assignments,

‎src/doc/rustdoc/src/unstable-features.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ to enable.
196196

197197
### Document keywords
198198

199-
This is for Rust compiler internal use only.
199+
This is for internal use in the std library.
200200

201201
Rust keywords are documented in the standard library (look for `match` for example).
202202

@@ -211,6 +211,23 @@ To do so, the `#[doc(keyword = "...")]` attribute is used. Example:
211211
mod empty_mod {}
212212
```
213213

214+
### Document builtin attributes
215+
216+
This is for internal use in the std library.
217+
218+
Rust builtin attributes are documented in the standard library (look for `repr` for example).
219+
220+
To do so, the `#[doc(attribute = "...")]` attribute is used. Example:
221+
222+
```rust
223+
#![feature(rustdoc_internals)]
224+
#![allow(internal_features)]
225+
226+
/// Some documentation about the attribute.
227+
#[doc(attribute = "repr")]
228+
mod empty_mod {}
229+
```
230+
214231
### Use the Rust logo as the crate logo
215232

216233
This is for official Rust project use only.

‎src/librustdoc/clean/types.rs

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -226,15 +226,28 @@ impl ExternalCrate {
226226
}
227227

228228
pub(crate) fn keywords(&self, tcx: TyCtxt<'_>) -> impl Iterator<Item = (DefId, Symbol)> {
229-
fn as_keyword(did: DefId, tcx: TyCtxt<'_>) -> Option<(DefId, Symbol)> {
229+
self.retrieve_keywords_or_documented_attributes(tcx, sym::keyword)
230+
}
231+
pub(crate) fn documented_attributes(
232+
&self,
233+
tcx: TyCtxt<'_>,
234+
) -> impl Iterator<Item = (DefId, Symbol)> {
235+
self.retrieve_keywords_or_documented_attributes(tcx, sym::attribute)
236+
}
237+
238+
fn retrieve_keywords_or_documented_attributes(
239+
&self,
240+
tcx: TyCtxt<'_>,
241+
name: Symbol,
242+
) -> impl Iterator<Item = (DefId, Symbol)> {
243+
let as_target = move |did: DefId, tcx: TyCtxt<'_>| -> Option<(DefId, Symbol)> {
230244
tcx.get_attrs(did, sym::doc)
231245
.flat_map(|attr| attr.meta_item_list().unwrap_or_default())
232-
.filter(|meta| meta.has_name(sym::keyword))
246+
.filter(|meta| meta.has_name(name))
233247
.find_map(|meta| meta.value_str())
234248
.map(|value| (did, value))
235-
}
236-
237-
self.mapped_root_modules(tcx, as_keyword)
249+
};
250+
self.mapped_root_modules(tcx, as_target)
238251
}
239252

240253
pub(crate) fn primitives(
@@ -592,6 +605,20 @@ impl Item {
592605
pub(crate) fn is_keyword(&self) -> bool {
593606
self.type_() == ItemType::Keyword
594607
}
608+
pub(crate) fn is_attribute(&self) -> bool {
609+
self.type_() == ItemType::Attribute
610+
}
611+
/// Returns `true` if the item kind is one of the following:
612+
///
613+
/// * `ItemType::Primitive`
614+
/// * `ItemType::Keyword`
615+
/// * `ItemType::Attribute`
616+
///
617+
/// They are considered fake because they only exist thanks to their
618+
/// `#[doc(primitive|keyword|attribute)]` attribute.
619+
pub(crate) fn is_fake_item(&self) -> bool {
620+
matches!(self.type_(), ItemType::Primitive | ItemType::Keyword | ItemType::Attribute)
621+
}
595622
pub(crate) fn is_stripped(&self) -> bool {
596623
match self.kind {
597624
StrippedItem(..) => true,
@@ -735,7 +762,9 @@ impl Item {
735762
// Primitives and Keywords are written in the source code as private modules.
736763
// The modules need to be private so that nobody actually uses them, but the
737764
// keywords and primitives that they are documenting are public.
738-
ItemKind::KeywordItem | ItemKind::PrimitiveItem(_) => return Some(Visibility::Public),
765+
ItemKind::KeywordItem | ItemKind::PrimitiveItem(_) | ItemKind::AttributeItem => {
766+
return Some(Visibility::Public);
767+
}
739768
// Variant fields inherit their enum's visibility.
740769
StructFieldItem(..) if is_field_vis_inherited(tcx, def_id) => {
741770
return None;
@@ -942,7 +971,12 @@ pub(crate) enum ItemKind {
942971
AssocTypeItem(Box<TypeAlias>, Vec<GenericBound>),
943972
/// An item that has been stripped by a rustdoc pass
944973
StrippedItem(Box<ItemKind>),
974+
/// This item represents a module with a `#[doc(keyword = "...")]` attribute which is used
975+
/// to generate documentation for Rust keywords.
945976
KeywordItem,
977+
/// This item represents a module with a `#[doc(attribute = "...")]` attribute which is used
978+
/// to generate documentation for Rust builtin attributes.
979+
AttributeItem,
946980
}
947981

948982
impl ItemKind {
@@ -983,7 +1017,8 @@ impl ItemKind {
9831017
| RequiredAssocTypeItem(..)
9841018
| AssocTypeItem(..)
9851019
| StrippedItem(_)
986-
| KeywordItem => [].iter(),
1020+
| KeywordItem
1021+
| AttributeItem => [].iter(),
9871022
}
9881023
}
9891024

‎src/librustdoc/clean/utils.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
6060
let local_crate = ExternalCrate { crate_num: LOCAL_CRATE };
6161
let primitives = local_crate.primitives(cx.tcx);
6262
let keywords = local_crate.keywords(cx.tcx);
63+
let documented_attributes = local_crate.documented_attributes(cx.tcx);
6364
{
6465
let ItemKind::ModuleItem(m) = &mut module.inner.kind else { unreachable!() };
6566
m.items.extend(primitives.map(|(def_id, prim)| {
@@ -73,6 +74,9 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
7374
m.items.extend(keywords.map(|(def_id, kw)| {
7475
Item::from_def_id_and_parts(def_id, Some(kw), ItemKind::KeywordItem, cx)
7576
}));
77+
m.items.extend(documented_attributes.into_iter().map(|(def_id, kw)| {
78+
Item::from_def_id_and_parts(def_id, Some(kw), ItemKind::AttributeItem, cx)
79+
}));
7680
}
7781

7882
Crate { module, external_traits: Box::new(mem::take(&mut cx.external_traits)) }

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /