about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_passes/src/check_attr.rs93
-rw-r--r--src/doc/rustdoc/src/advanced-features.md7
-rw-r--r--src/librustdoc/clean/types.rs16
3 files changed, 107 insertions, 9 deletions
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 6cc649c1180..0257a63e50f 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -390,10 +390,25 @@ impl CheckAttrVisitor<'tcx> {
             .emit();
     }
 
-    fn check_doc_alias(&self, meta: &NestedMetaItem, hir_id: HirId, target: Target) -> bool {
-        let doc_alias = meta.value_str().map(|s| s.to_string()).unwrap_or_else(String::new);
+    fn check_doc_alias_value(
+        &self,
+        meta: &NestedMetaItem,
+        doc_alias: &str,
+        hir_id: HirId,
+        target: Target,
+        is_list: bool,
+    ) -> bool {
         if doc_alias.is_empty() {
-            self.doc_attr_str_error(meta, "alias");
+            self.tcx
+                .sess
+                .struct_span_err(
+                    meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
+                    &format!(
+                        "`#[doc(alias{})]` attribute cannot have empty value",
+                        if is_list { "(\"...\")" } else { " = \"...\"" },
+                    ),
+                )
+                .emit();
             return false;
         }
         if let Some(c) =
@@ -403,7 +418,11 @@ impl CheckAttrVisitor<'tcx> {
                 .sess
                 .struct_span_err(
                     meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
-                    &format!("{:?} character isn't allowed in `#[doc(alias = \"...\")]`", c),
+                    &format!(
+                        "{:?} character isn't allowed in `#[doc(alias{})]`",
+                        c,
+                        if is_list { "(\"...\")" } else { " = \"...\"" },
+                    ),
                 )
                 .emit();
             return false;
@@ -413,7 +432,10 @@ impl CheckAttrVisitor<'tcx> {
                 .sess
                 .struct_span_err(
                     meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
-                    "`#[doc(alias = \"...\")]` cannot start or end with ' '",
+                    &format!(
+                        "`#[doc(alias{})]` cannot start or end with ' '",
+                        if is_list { "(\"...\")" } else { " = \"...\"" },
+                    ),
                 )
                 .emit();
             return false;
@@ -446,7 +468,11 @@ impl CheckAttrVisitor<'tcx> {
                 .sess
                 .struct_span_err(
                     meta.span(),
-                    &format!("`#[doc(alias = \"...\")]` isn't allowed on {}", err),
+                    &format!(
+                        "`#[doc(alias{})]` isn't allowed on {}",
+                        if is_list { "(\"...\")" } else { " = \"...\"" },
+                        err,
+                    ),
                 )
                 .emit();
             return false;
@@ -457,7 +483,10 @@ impl CheckAttrVisitor<'tcx> {
                 .sess
                 .struct_span_err(
                     meta.span(),
-                    &format!("`#[doc(alias = \"...\")]` is the same as the item's name"),
+                    &format!(
+                        "`#[doc(alias{})]` is the same as the item's name",
+                        if is_list { "(\"...\")" } else { " = \"...\"" },
+                    ),
                 )
                 .emit();
             return false;
@@ -465,6 +494,56 @@ impl CheckAttrVisitor<'tcx> {
         true
     }
 
+    fn check_doc_alias(&self, meta: &NestedMetaItem, hir_id: HirId, target: Target) -> bool {
+        if let Some(values) = meta.meta_item_list() {
+            let mut errors = 0;
+            for v in values {
+                match v.literal() {
+                    Some(l) => match l.kind {
+                        LitKind::Str(s, _) => {
+                            if !self.check_doc_alias_value(v, &s.as_str(), hir_id, target, true) {
+                                errors += 1;
+                            }
+                        }
+                        _ => {
+                            self.tcx
+                                .sess
+                                .struct_span_err(
+                                    v.span(),
+                                    "`#[doc(alias(\"a\")]` expects string literals",
+                                )
+                                .emit();
+                            errors += 1;
+                        }
+                    },
+                    None => {
+                        self.tcx
+                            .sess
+                            .struct_span_err(
+                                v.span(),
+                                "`#[doc(alias(\"a\")]` expects string literals",
+                            )
+                            .emit();
+                        errors += 1;
+                    }
+                }
+            }
+            errors == 0
+        } else if let Some(doc_alias) = meta.value_str().map(|s| s.to_string()) {
+            self.check_doc_alias_value(meta, &doc_alias, hir_id, target, false)
+        } else {
+            self.tcx
+                .sess
+                .struct_span_err(
+                    meta.span(),
+                    "doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of \
+                     strings: `#[doc(alias(\"a\", \"b\")]`",
+                )
+                .emit();
+            false
+        }
+    }
+
     fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
         let doc_keyword = meta.value_str().map(|s| s.to_string()).unwrap_or_else(String::new);
         if doc_keyword.is_empty() {
diff --git a/src/doc/rustdoc/src/advanced-features.md b/src/doc/rustdoc/src/advanced-features.md
index abdc2e4025d..6147bd0a97a 100644
--- a/src/doc/rustdoc/src/advanced-features.md
+++ b/src/doc/rustdoc/src/advanced-features.md
@@ -81,3 +81,10 @@ Then, when looking for it through the `rustdoc` search, if you enter "x" or
 "big", search will show the `BigX` struct first.
 
 There are some limitations on the doc alias names though: you can't use `"` or whitespace.
+
+You can add multiple aliases at the same time by using a list:
+
+```rust,no_run
+#[doc(alias("x", "big"))]
+pub struct BigX;
+```
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index a94ee918c24..b67af484510 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -914,8 +914,20 @@ impl Attributes {
         self.other_attrs
             .lists(sym::doc)
             .filter(|a| a.has_name(sym::alias))
-            .filter_map(|a| a.value_str().map(|s| s.to_string()))
-            .filter(|v| !v.is_empty())
+            .map(|a| {
+                if let Some(values) = a.meta_item_list() {
+                    values
+                        .iter()
+                        .map(|l| match l.literal().unwrap().kind {
+                            ast::LitKind::Str(s, _) => s.as_str().to_string(),
+                            _ => unreachable!(),
+                        })
+                        .collect::<Vec<_>>()
+                } else {
+                    vec![a.value_str().map(|s| s.to_string()).unwrap()]
+                }
+            })
+            .flatten()
             .collect::<FxHashSet<_>>()
     }
 }