Skip to content

Commit 61372e1

Browse files
authored
Rollup merge of #82846 - GuillaumeGomez:doc-alias-list, r=jyn514
rustdoc: allow list syntax for #[doc(alias)] attributes Fixes #81205. It now allows to have: ```rust #[doc(alias = "x")] // and: #[doc(alias("y", "z"))] ``` cc ``@jplatte`` r? ``@jyn514``
2 parents 2960971 + 1d26e6b commit 61372e1

File tree

7 files changed

+255
-60
lines changed

7 files changed

+255
-60
lines changed

compiler/rustc_passes/src/check_attr.rs

+83-30
Original file line numberDiff line numberDiff line change
@@ -394,33 +394,50 @@ impl CheckAttrVisitor<'tcx> {
394394
.emit();
395395
}
396396

397-
fn check_doc_alias(&self, meta: &NestedMetaItem, hir_id: HirId, target: Target) -> bool {
398-
let doc_alias = meta.value_str().map(|s| s.to_string()).unwrap_or_else(String::new);
397+
fn check_doc_alias_value(
398+
&self,
399+
meta: &NestedMetaItem,
400+
doc_alias: &str,
401+
hir_id: HirId,
402+
target: Target,
403+
is_list: bool,
404+
) -> bool {
405+
let tcx = self.tcx;
406+
let err_fn = move |span: Span, msg: &str| {
407+
tcx.sess.span_err(
408+
span,
409+
&format!(
410+
"`#[doc(alias{})]` {}",
411+
if is_list { "(\"...\")" } else { " = \"...\"" },
412+
msg,
413+
),
414+
);
415+
false
416+
};
399417
if doc_alias.is_empty() {
400-
self.doc_attr_str_error(meta, "alias");
401-
return false;
418+
return err_fn(
419+
meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
420+
"attribute cannot have empty value",
421+
);
402422
}
403423
if let Some(c) =
404424
doc_alias.chars().find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
405425
{
406-
self.tcx
407-
.sess
408-
.struct_span_err(
409-
meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
410-
&format!("{:?} character isn't allowed in `#[doc(alias = \"...\")]`", c),
411-
)
412-
.emit();
426+
self.tcx.sess.span_err(
427+
meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
428+
&format!(
429+
"{:?} character isn't allowed in `#[doc(alias{})]`",
430+
c,
431+
if is_list { "(\"...\")" } else { " = \"...\"" },
432+
),
433+
);
413434
return false;
414435
}
415436
if doc_alias.starts_with(' ') || doc_alias.ends_with(' ') {
416-
self.tcx
417-
.sess
418-
.struct_span_err(
419-
meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
420-
"`#[doc(alias = \"...\")]` cannot start or end with ' '",
421-
)
422-
.emit();
423-
return false;
437+
return err_fn(
438+
meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
439+
"cannot start or end with ' '",
440+
);
424441
}
425442
if let Some(err) = match target {
426443
Target::Impl => Some("implementation block"),
@@ -446,27 +463,63 @@ impl CheckAttrVisitor<'tcx> {
446463
}
447464
_ => None,
448465
} {
449-
self.tcx
450-
.sess
451-
.struct_span_err(
452-
meta.span(),
453-
&format!("`#[doc(alias = \"...\")]` isn't allowed on {}", err),
454-
)
455-
.emit();
456-
return false;
466+
return err_fn(meta.span(), &format!("isn't allowed on {}", err));
457467
}
458468
let item_name = self.tcx.hir().name(hir_id);
459469
if &*item_name.as_str() == doc_alias {
470+
return err_fn(meta.span(), "is the same as the item's name");
471+
}
472+
true
473+
}
474+
475+
fn check_doc_alias(&self, meta: &NestedMetaItem, hir_id: HirId, target: Target) -> bool {
476+
if let Some(values) = meta.meta_item_list() {
477+
let mut errors = 0;
478+
for v in values {
479+
match v.literal() {
480+
Some(l) => match l.kind {
481+
LitKind::Str(s, _) => {
482+
if !self.check_doc_alias_value(v, &s.as_str(), hir_id, target, true) {
483+
errors += 1;
484+
}
485+
}
486+
_ => {
487+
self.tcx
488+
.sess
489+
.struct_span_err(
490+
v.span(),
491+
"`#[doc(alias(\"a\"))]` expects string literals",
492+
)
493+
.emit();
494+
errors += 1;
495+
}
496+
},
497+
None => {
498+
self.tcx
499+
.sess
500+
.struct_span_err(
501+
v.span(),
502+
"`#[doc(alias(\"a\"))]` expects string literals",
503+
)
504+
.emit();
505+
errors += 1;
506+
}
507+
}
508+
}
509+
errors == 0
510+
} else if let Some(doc_alias) = meta.value_str().map(|s| s.to_string()) {
511+
self.check_doc_alias_value(meta, &doc_alias, hir_id, target, false)
512+
} else {
460513
self.tcx
461514
.sess
462515
.struct_span_err(
463516
meta.span(),
464-
&format!("`#[doc(alias = \"...\")]` is the same as the item's name"),
517+
"doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of \
518+
strings `#[doc(alias(\"a\", \"b\"))]`",
465519
)
466520
.emit();
467-
return false;
521+
false
468522
}
469-
true
470523
}
471524

472525
fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {

src/doc/rustdoc/src/advanced-features.md

+7
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,10 @@ Then, when looking for it through the `rustdoc` search, if you enter "x" or
8181
"big", search will show the `BigX` struct first.
8282

8383
There are some limitations on the doc alias names though: you can't use `"` or whitespace.
84+
85+
You can add multiple aliases at the same time by using a list:
86+
87+
```rust,no_run
88+
#[doc(alias("x", "big"))]
89+
pub struct BigX;
90+
```

src/librustdoc/clean/types.rs

+17-6
Original file line numberDiff line numberDiff line change
@@ -910,12 +910,23 @@ impl Attributes {
910910
}
911911

912912
crate fn get_doc_aliases(&self) -> FxHashSet<String> {
913-
self.other_attrs
914-
.lists(sym::doc)
915-
.filter(|a| a.has_name(sym::alias))
916-
.filter_map(|a| a.value_str().map(|s| s.to_string()))
917-
.filter(|v| !v.is_empty())
918-
.collect::<FxHashSet<_>>()
913+
let mut aliases = FxHashSet::default();
914+
915+
for attr in self.other_attrs.lists(sym::doc).filter(|a| a.has_name(sym::alias)) {
916+
if let Some(values) = attr.meta_item_list() {
917+
for l in values {
918+
match l.literal().unwrap().kind {
919+
ast::LitKind::Str(s, _) => {
920+
aliases.insert(s.as_str().to_string());
921+
}
922+
_ => unreachable!(),
923+
}
924+
}
925+
} else {
926+
aliases.insert(attr.value_str().map(|s| s.to_string()).unwrap());
927+
}
928+
}
929+
aliases
919930
}
920931
}
921932

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
#![crate_type = "lib"]
22

33
#[doc(alias = "foo")] // ok!
4+
#[doc(alias("bar", "baz"))] // ok!
45
pub struct Bar;
56

67
#[doc(alias)] //~ ERROR
78
#[doc(alias = 0)] //~ ERROR
8-
#[doc(alias("bar"))] //~ ERROR
99
#[doc(alias = "\"")] //~ ERROR
1010
#[doc(alias = "\n")] //~ ERROR
1111
#[doc(alias = "
1212
")] //~^ ERROR
1313
#[doc(alias = "\t")] //~ ERROR
1414
#[doc(alias = " hello")] //~ ERROR
1515
#[doc(alias = "hello ")] //~ ERROR
16+
#[doc(alias = "")] //~ ERROR
1617
pub struct Foo;
18+
19+
#[doc(alias(0))] //~ ERROR
20+
#[doc(alias("\""))] //~ ERROR
21+
#[doc(alias("\n"))] //~ ERROR
22+
#[doc(alias("
23+
"))] //~^ ERROR
24+
#[doc(alias("\t"))] //~ ERROR
25+
#[doc(alias(" hello"))] //~ ERROR
26+
#[doc(alias("hello "))] //~ ERROR
27+
#[doc(alias(""))] //~ ERROR
28+
pub struct Foo2;

src/test/rustdoc-ui/check-doc-alias-attr.stderr

+61-11
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
1-
error: doc alias attribute expects a string: #[doc(alias = "a")]
2-
--> $DIR/check-doc-alias-attr.rs:6:7
1+
error: doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]`
2+
--> $DIR/check-doc-alias-attr.rs:7:7
33
|
44
LL | #[doc(alias)]
55
| ^^^^^
66

7-
error: doc alias attribute expects a string: #[doc(alias = "a")]
8-
--> $DIR/check-doc-alias-attr.rs:7:7
7+
error: doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]`
8+
--> $DIR/check-doc-alias-attr.rs:8:7
99
|
1010
LL | #[doc(alias = 0)]
1111
| ^^^^^^^^^
1212

13-
error: doc alias attribute expects a string: #[doc(alias = "a")]
14-
--> $DIR/check-doc-alias-attr.rs:8:7
15-
|
16-
LL | #[doc(alias("bar"))]
17-
| ^^^^^^^^^^^^
18-
1913
error: '\"' character isn't allowed in `#[doc(alias = "...")]`
2014
--> $DIR/check-doc-alias-attr.rs:9:15
2115
|
@@ -54,5 +48,61 @@ error: `#[doc(alias = "...")]` cannot start or end with ' '
5448
LL | #[doc(alias = "hello ")]
5549
| ^^^^^^^^
5650

57-
error: aborting due to 9 previous errors
51+
error: `#[doc(alias = "...")]` attribute cannot have empty value
52+
--> $DIR/check-doc-alias-attr.rs:16:15
53+
|
54+
LL | #[doc(alias = "")]
55+
| ^^
56+
57+
error: `#[doc(alias("a"))]` expects string literals
58+
--> $DIR/check-doc-alias-attr.rs:19:13
59+
|
60+
LL | #[doc(alias(0))]
61+
| ^
62+
63+
error: '\"' character isn't allowed in `#[doc(alias("..."))]`
64+
--> $DIR/check-doc-alias-attr.rs:20:13
65+
|
66+
LL | #[doc(alias("\""))]
67+
| ^^^^
68+
69+
error: '\n' character isn't allowed in `#[doc(alias("..."))]`
70+
--> $DIR/check-doc-alias-attr.rs:21:13
71+
|
72+
LL | #[doc(alias("\n"))]
73+
| ^^^^
74+
75+
error: '\n' character isn't allowed in `#[doc(alias("..."))]`
76+
--> $DIR/check-doc-alias-attr.rs:22:13
77+
|
78+
LL | #[doc(alias("
79+
| _____________^
80+
LL | | "))]
81+
| |_^
82+
83+
error: '\t' character isn't allowed in `#[doc(alias("..."))]`
84+
--> $DIR/check-doc-alias-attr.rs:24:13
85+
|
86+
LL | #[doc(alias("\t"))]
87+
| ^^^^
88+
89+
error: `#[doc(alias("..."))]` cannot start or end with ' '
90+
--> $DIR/check-doc-alias-attr.rs:25:13
91+
|
92+
LL | #[doc(alias(" hello"))]
93+
| ^^^^^^^^
94+
95+
error: `#[doc(alias("..."))]` cannot start or end with ' '
96+
--> $DIR/check-doc-alias-attr.rs:26:13
97+
|
98+
LL | #[doc(alias("hello "))]
99+
| ^^^^^^^^
100+
101+
error: `#[doc(alias("..."))]` attribute cannot have empty value
102+
--> $DIR/check-doc-alias-attr.rs:27:13
103+
|
104+
LL | #[doc(alias(""))]
105+
| ^^
106+
107+
error: aborting due to 17 previous errors
58108

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
#![crate_type = "lib"]
22

33
#[doc(alias = "foo")] // ok!
4+
#[doc(alias("bar", "baz"))] // ok!
45
pub struct Bar;
56

67
#[doc(alias)] //~ ERROR
78
#[doc(alias = 0)] //~ ERROR
8-
#[doc(alias("bar"))] //~ ERROR
99
#[doc(alias = "\"")] //~ ERROR
1010
#[doc(alias = "\n")] //~ ERROR
1111
#[doc(alias = "
1212
")] //~^ ERROR
1313
#[doc(alias = "\t")] //~ ERROR
1414
#[doc(alias = " hello")] //~ ERROR
1515
#[doc(alias = "hello ")] //~ ERROR
16+
#[doc(alias = "")] //~ ERROR
1617
pub struct Foo;
18+
19+
#[doc(alias(0))] //~ ERROR
20+
#[doc(alias("\""))] //~ ERROR
21+
#[doc(alias("\n"))] //~ ERROR
22+
#[doc(alias("
23+
"))] //~^ ERROR
24+
#[doc(alias("\t"))] //~ ERROR
25+
#[doc(alias(" hello"))] //~ ERROR
26+
#[doc(alias("hello "))] //~ ERROR
27+
#[doc(alias(""))] //~ ERROR
28+
pub struct Foo2;

0 commit comments

Comments
 (0)