about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2021-03-19 15:03:21 +0100
committerGitHub <noreply@github.com>2021-03-19 15:03:21 +0100
commit61372e1af6a100aedc203d0739073b42f8977e4e (patch)
treee03d9ecf658034f333855d44b9904a445f6c0c76
parent29609711a58312fc105a589e353d2ae197532fd5 (diff)
parent1d26e6b632c78dedff2dd19d93d0687b2c97717d (diff)
downloadrust-61372e1af6a100aedc203d0739073b42f8977e4e.tar.gz
rust-61372e1af6a100aedc203d0739073b42f8977e4e.zip
Rollup merge of #82846 - GuillaumeGomez:doc-alias-list, r=jyn514
rustdoc: allow list syntax for #[doc(alias)] attributes

Fixes https://github.com/rust-lang/rust/issues/81205.

It now allows to have:

```rust
#[doc(alias = "x")]
// and:
#[doc(alias("y", "z"))]
```

cc ``@jplatte``
r? ``@jyn514``
-rw-r--r--compiler/rustc_passes/src/check_attr.rs113
-rw-r--r--src/doc/rustdoc/src/advanced-features.md7
-rw-r--r--src/librustdoc/clean/types.rs23
-rw-r--r--src/test/rustdoc-ui/check-doc-alias-attr.rs14
-rw-r--r--src/test/rustdoc-ui/check-doc-alias-attr.stderr72
-rw-r--r--src/test/ui/rustdoc/check-doc-alias-attr.rs14
-rw-r--r--src/test/ui/rustdoc/check-doc-alias-attr.stderr72
7 files changed, 255 insertions, 60 deletions
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 775e27e68f8..d91d0e1765b 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -394,33 +394,50 @@ 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 {
+        let tcx = self.tcx;
+        let err_fn = move |span: Span, msg: &str| {
+            tcx.sess.span_err(
+                span,
+                &format!(
+                    "`#[doc(alias{})]` {}",
+                    if is_list { "(\"...\")" } else { " = \"...\"" },
+                    msg,
+                ),
+            );
+            false
+        };
         if doc_alias.is_empty() {
-            self.doc_attr_str_error(meta, "alias");
-            return false;
+            return err_fn(
+                meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
+                "attribute cannot have empty value",
+            );
         }
         if let Some(c) =
             doc_alias.chars().find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
         {
-            self.tcx
-                .sess
-                .struct_span_err(
-                    meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
-                    &format!("{:?} character isn't allowed in `#[doc(alias = \"...\")]`", c),
-                )
-                .emit();
+            self.tcx.sess.span_err(
+                meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
+                &format!(
+                    "{:?} character isn't allowed in `#[doc(alias{})]`",
+                    c,
+                    if is_list { "(\"...\")" } else { " = \"...\"" },
+                ),
+            );
             return false;
         }
         if doc_alias.starts_with(' ') || doc_alias.ends_with(' ') {
-            self.tcx
-                .sess
-                .struct_span_err(
-                    meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
-                    "`#[doc(alias = \"...\")]` cannot start or end with ' '",
-                )
-                .emit();
-            return false;
+            return err_fn(
+                meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
+                "cannot start or end with ' '",
+            );
         }
         if let Some(err) = match target {
             Target::Impl => Some("implementation block"),
@@ -446,27 +463,63 @@ impl CheckAttrVisitor<'tcx> {
             }
             _ => None,
         } {
-            self.tcx
-                .sess
-                .struct_span_err(
-                    meta.span(),
-                    &format!("`#[doc(alias = \"...\")]` isn't allowed on {}", err),
-                )
-                .emit();
-            return false;
+            return err_fn(meta.span(), &format!("isn't allowed on {}", err));
         }
         let item_name = self.tcx.hir().name(hir_id);
         if &*item_name.as_str() == doc_alias {
+            return err_fn(meta.span(), "is the same as the item's name");
+        }
+        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(),
-                    &format!("`#[doc(alias = \"...\")]` is the same as the item's name"),
+                    "doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of \
+                     strings `#[doc(alias(\"a\", \"b\"))]`",
                 )
                 .emit();
-            return false;
+            false
         }
-        true
     }
 
     fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
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 114b68c56b8..840a42c2b8a 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -910,12 +910,23 @@ impl Attributes {
     }
 
     crate fn get_doc_aliases(&self) -> FxHashSet<String> {
-        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())
-            .collect::<FxHashSet<_>>()
+        let mut aliases = FxHashSet::default();
+
+        for attr in self.other_attrs.lists(sym::doc).filter(|a| a.has_name(sym::alias)) {
+            if let Some(values) = attr.meta_item_list() {
+                for l in values {
+                    match l.literal().unwrap().kind {
+                        ast::LitKind::Str(s, _) => {
+                            aliases.insert(s.as_str().to_string());
+                        }
+                        _ => unreachable!(),
+                    }
+                }
+            } else {
+                aliases.insert(attr.value_str().map(|s| s.to_string()).unwrap());
+            }
+        }
+        aliases
     }
 }
 
diff --git a/src/test/rustdoc-ui/check-doc-alias-attr.rs b/src/test/rustdoc-ui/check-doc-alias-attr.rs
index 912e35f9165..719b98604c4 100644
--- a/src/test/rustdoc-ui/check-doc-alias-attr.rs
+++ b/src/test/rustdoc-ui/check-doc-alias-attr.rs
@@ -1,11 +1,11 @@
 #![crate_type = "lib"]
 
 #[doc(alias = "foo")] // ok!
+#[doc(alias("bar", "baz"))] // ok!
 pub struct Bar;
 
 #[doc(alias)] //~ ERROR
 #[doc(alias = 0)] //~ ERROR
-#[doc(alias("bar"))] //~ ERROR
 #[doc(alias = "\"")] //~ ERROR
 #[doc(alias = "\n")] //~ ERROR
 #[doc(alias = "
@@ -13,4 +13,16 @@ pub struct Bar;
 #[doc(alias = "\t")] //~ ERROR
 #[doc(alias = " hello")] //~ ERROR
 #[doc(alias = "hello ")] //~ ERROR
+#[doc(alias = "")] //~ ERROR
 pub struct Foo;
+
+#[doc(alias(0))] //~ ERROR
+#[doc(alias("\""))] //~ ERROR
+#[doc(alias("\n"))] //~ ERROR
+#[doc(alias("
+"))] //~^ ERROR
+#[doc(alias("\t"))] //~ ERROR
+#[doc(alias(" hello"))] //~ ERROR
+#[doc(alias("hello "))] //~ ERROR
+#[doc(alias(""))] //~ ERROR
+pub struct Foo2;
diff --git a/src/test/rustdoc-ui/check-doc-alias-attr.stderr b/src/test/rustdoc-ui/check-doc-alias-attr.stderr
index 1c7fc83bb8d..f99d69dc101 100644
--- a/src/test/rustdoc-ui/check-doc-alias-attr.stderr
+++ b/src/test/rustdoc-ui/check-doc-alias-attr.stderr
@@ -1,21 +1,15 @@
-error: doc alias attribute expects a string: #[doc(alias = "a")]
-  --> $DIR/check-doc-alias-attr.rs:6:7
+error: doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]`
+  --> $DIR/check-doc-alias-attr.rs:7:7
    |
 LL | #[doc(alias)]
    |       ^^^^^
 
-error: doc alias attribute expects a string: #[doc(alias = "a")]
-  --> $DIR/check-doc-alias-attr.rs:7:7
+error: doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]`
+  --> $DIR/check-doc-alias-attr.rs:8:7
    |
 LL | #[doc(alias = 0)]
    |       ^^^^^^^^^
 
-error: doc alias attribute expects a string: #[doc(alias = "a")]
-  --> $DIR/check-doc-alias-attr.rs:8:7
-   |
-LL | #[doc(alias("bar"))]
-   |       ^^^^^^^^^^^^
-
 error: '\"' character isn't allowed in `#[doc(alias = "...")]`
   --> $DIR/check-doc-alias-attr.rs:9:15
    |
@@ -54,5 +48,61 @@ error: `#[doc(alias = "...")]` cannot start or end with ' '
 LL | #[doc(alias = "hello ")]
    |               ^^^^^^^^
 
-error: aborting due to 9 previous errors
+error: `#[doc(alias = "...")]` attribute cannot have empty value
+  --> $DIR/check-doc-alias-attr.rs:16:15
+   |
+LL | #[doc(alias = "")]
+   |               ^^
+
+error: `#[doc(alias("a"))]` expects string literals
+  --> $DIR/check-doc-alias-attr.rs:19:13
+   |
+LL | #[doc(alias(0))]
+   |             ^
+
+error: '\"' character isn't allowed in `#[doc(alias("..."))]`
+  --> $DIR/check-doc-alias-attr.rs:20:13
+   |
+LL | #[doc(alias("\""))]
+   |             ^^^^
+
+error: '\n' character isn't allowed in `#[doc(alias("..."))]`
+  --> $DIR/check-doc-alias-attr.rs:21:13
+   |
+LL | #[doc(alias("\n"))]
+   |             ^^^^
+
+error: '\n' character isn't allowed in `#[doc(alias("..."))]`
+  --> $DIR/check-doc-alias-attr.rs:22:13
+   |
+LL |   #[doc(alias("
+   |  _____________^
+LL | | "))]
+   | |_^
+
+error: '\t' character isn't allowed in `#[doc(alias("..."))]`
+  --> $DIR/check-doc-alias-attr.rs:24:13
+   |
+LL | #[doc(alias("\t"))]
+   |             ^^^^
+
+error: `#[doc(alias("..."))]` cannot start or end with ' '
+  --> $DIR/check-doc-alias-attr.rs:25:13
+   |
+LL | #[doc(alias(" hello"))]
+   |             ^^^^^^^^
+
+error: `#[doc(alias("..."))]` cannot start or end with ' '
+  --> $DIR/check-doc-alias-attr.rs:26:13
+   |
+LL | #[doc(alias("hello "))]
+   |             ^^^^^^^^
+
+error: `#[doc(alias("..."))]` attribute cannot have empty value
+  --> $DIR/check-doc-alias-attr.rs:27:13
+   |
+LL | #[doc(alias(""))]
+   |             ^^
+
+error: aborting due to 17 previous errors
 
diff --git a/src/test/ui/rustdoc/check-doc-alias-attr.rs b/src/test/ui/rustdoc/check-doc-alias-attr.rs
index 912e35f9165..719b98604c4 100644
--- a/src/test/ui/rustdoc/check-doc-alias-attr.rs
+++ b/src/test/ui/rustdoc/check-doc-alias-attr.rs
@@ -1,11 +1,11 @@
 #![crate_type = "lib"]
 
 #[doc(alias = "foo")] // ok!
+#[doc(alias("bar", "baz"))] // ok!
 pub struct Bar;
 
 #[doc(alias)] //~ ERROR
 #[doc(alias = 0)] //~ ERROR
-#[doc(alias("bar"))] //~ ERROR
 #[doc(alias = "\"")] //~ ERROR
 #[doc(alias = "\n")] //~ ERROR
 #[doc(alias = "
@@ -13,4 +13,16 @@ pub struct Bar;
 #[doc(alias = "\t")] //~ ERROR
 #[doc(alias = " hello")] //~ ERROR
 #[doc(alias = "hello ")] //~ ERROR
+#[doc(alias = "")] //~ ERROR
 pub struct Foo;
+
+#[doc(alias(0))] //~ ERROR
+#[doc(alias("\""))] //~ ERROR
+#[doc(alias("\n"))] //~ ERROR
+#[doc(alias("
+"))] //~^ ERROR
+#[doc(alias("\t"))] //~ ERROR
+#[doc(alias(" hello"))] //~ ERROR
+#[doc(alias("hello "))] //~ ERROR
+#[doc(alias(""))] //~ ERROR
+pub struct Foo2;
diff --git a/src/test/ui/rustdoc/check-doc-alias-attr.stderr b/src/test/ui/rustdoc/check-doc-alias-attr.stderr
index 1c7fc83bb8d..f99d69dc101 100644
--- a/src/test/ui/rustdoc/check-doc-alias-attr.stderr
+++ b/src/test/ui/rustdoc/check-doc-alias-attr.stderr
@@ -1,21 +1,15 @@
-error: doc alias attribute expects a string: #[doc(alias = "a")]
-  --> $DIR/check-doc-alias-attr.rs:6:7
+error: doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]`
+  --> $DIR/check-doc-alias-attr.rs:7:7
    |
 LL | #[doc(alias)]
    |       ^^^^^
 
-error: doc alias attribute expects a string: #[doc(alias = "a")]
-  --> $DIR/check-doc-alias-attr.rs:7:7
+error: doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]`
+  --> $DIR/check-doc-alias-attr.rs:8:7
    |
 LL | #[doc(alias = 0)]
    |       ^^^^^^^^^
 
-error: doc alias attribute expects a string: #[doc(alias = "a")]
-  --> $DIR/check-doc-alias-attr.rs:8:7
-   |
-LL | #[doc(alias("bar"))]
-   |       ^^^^^^^^^^^^
-
 error: '\"' character isn't allowed in `#[doc(alias = "...")]`
   --> $DIR/check-doc-alias-attr.rs:9:15
    |
@@ -54,5 +48,61 @@ error: `#[doc(alias = "...")]` cannot start or end with ' '
 LL | #[doc(alias = "hello ")]
    |               ^^^^^^^^
 
-error: aborting due to 9 previous errors
+error: `#[doc(alias = "...")]` attribute cannot have empty value
+  --> $DIR/check-doc-alias-attr.rs:16:15
+   |
+LL | #[doc(alias = "")]
+   |               ^^
+
+error: `#[doc(alias("a"))]` expects string literals
+  --> $DIR/check-doc-alias-attr.rs:19:13
+   |
+LL | #[doc(alias(0))]
+   |             ^
+
+error: '\"' character isn't allowed in `#[doc(alias("..."))]`
+  --> $DIR/check-doc-alias-attr.rs:20:13
+   |
+LL | #[doc(alias("\""))]
+   |             ^^^^
+
+error: '\n' character isn't allowed in `#[doc(alias("..."))]`
+  --> $DIR/check-doc-alias-attr.rs:21:13
+   |
+LL | #[doc(alias("\n"))]
+   |             ^^^^
+
+error: '\n' character isn't allowed in `#[doc(alias("..."))]`
+  --> $DIR/check-doc-alias-attr.rs:22:13
+   |
+LL |   #[doc(alias("
+   |  _____________^
+LL | | "))]
+   | |_^
+
+error: '\t' character isn't allowed in `#[doc(alias("..."))]`
+  --> $DIR/check-doc-alias-attr.rs:24:13
+   |
+LL | #[doc(alias("\t"))]
+   |             ^^^^
+
+error: `#[doc(alias("..."))]` cannot start or end with ' '
+  --> $DIR/check-doc-alias-attr.rs:25:13
+   |
+LL | #[doc(alias(" hello"))]
+   |             ^^^^^^^^
+
+error: `#[doc(alias("..."))]` cannot start or end with ' '
+  --> $DIR/check-doc-alias-attr.rs:26:13
+   |
+LL | #[doc(alias("hello "))]
+   |             ^^^^^^^^
+
+error: `#[doc(alias("..."))]` attribute cannot have empty value
+  --> $DIR/check-doc-alias-attr.rs:27:13
+   |
+LL | #[doc(alias(""))]
+   |             ^^
+
+error: aborting due to 17 previous errors