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::>() + } else { + vec![a.value_str().map(|s| s.to_string()).unwrap()] + } + }) + .flatten() .collect::>() } }