diff --git a/compiler/rustc_attr_ir/src/data_structures.rs b/compiler/rustc_attr_ir/src/data_structures.rs index d722d515582dc..1f27d1de01f91 100644 --- a/compiler/rustc_attr_ir/src/data_structures.rs +++ b/compiler/rustc_attr_ir/src/data_structures.rs @@ -505,7 +505,7 @@ pub struct DocAttribute { pub keyword: Option<(Symbol, Span)>, pub attribute: Option<(Symbol, Span)>, pub masked: Option, - pub notable_trait: Option, + pub notable_trait: Option<(Option<(NotableTraitColor, Span)>, Span)>, pub search_unbox: Option, // valid on crate @@ -522,6 +522,41 @@ pub struct DocAttribute { pub no_crate_inject: Option, } +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(StableHash, Encodable, Decodable, PrintAttribute)] +pub enum NotableTraitColor { + Grey, + Red, + Green, + Yellow, + Blue, + Magenta, + Cyan, + Transparent, +} + +impl From for &'static str { + fn from(color: NotableTraitColor) -> &'static str { + use NotableTraitColor::*; + match color { + Grey => "grey", + Red => "red", + Green => "green", + Yellow => "yellow", + Blue => "blue", + Magenta => "magenta", + Cyan => "cyan", + Transparent => "transparent", + } + } +} + +impl std::fmt::Display for NotableTraitColor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str((*self).into()) + } +} + impl rustc_serialize::Encodable for DocAttribute { fn encode(&self, encoder: &mut E) { let DocAttribute { diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index 7aeedc1493027..37d5bb4431d49 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -2,7 +2,7 @@ use rustc_ast::ast::{AttrStyle, LitKind, MetaItemLit}; use rustc_attr_ir::target::Target; use rustc_attr_ir::{ AttributeKind, CfgEntry, CfgHideShow, DocAttribute, DocCfgHideShow, DocCfgHideShowValue, - DocInline, HideOrShow, + DocInline, HideOrShow, NotableTraitColor, }; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry}; use rustc_errors::Applicability; @@ -15,13 +15,14 @@ use super::{AcceptMapping, AttributeParser, template}; use crate::context::{AcceptContext, FinalizeContext}; use crate::diagnostics::{ AttrCrateLevelOnly, DocAliasBadChar, DocAliasDuplicated, DocAliasEmpty, DocAliasMalformed, - DocAliasStartEnd, DocAttrNotCrateLevel, DocAttributeNotAttribute, DocAutoCfgExpectsHideOrShow, - DocAutoCfgHideShowExpectsList, DocAutoCfgHideShowNoIdentBeforeValues, - DocAutoCfgHideShowUnexpectedItem, DocAutoCfgHideShowUnexpectedItemAfterValues, - DocAutoCfgHideShowValuesMix, DocAutoCfgWrongLiteral, DocKeywordNotKeyword, DocTestLiteral, - DocTestTakesList, DocTestUnknown, DocUnknownAny, DocUnknownInclude, DocUnknownPasses, - DocUnknownPlugins, DocUnknownSpotlight, ExpectedNameValue, ExpectedNoArgs, - IllFormedAttributeInput, MalformedDoc, UnusedDuplicate, + DocAliasStartEnd, DocAttrNotCrateLevel, DocAttrNotTraitLevel, DocAttributeNotAttribute, + DocAutoCfgExpectsHideOrShow, DocAutoCfgHideShowExpectsList, + DocAutoCfgHideShowNoIdentBeforeValues, DocAutoCfgHideShowUnexpectedItem, + DocAutoCfgHideShowUnexpectedItemAfterValues, DocAutoCfgHideShowValuesMix, + DocAutoCfgWrongLiteral, DocKeywordNotKeyword, DocTestLiteral, DocTestTakesList, DocTestUnknown, + DocUnknownAny, DocUnknownInclude, DocUnknownPasses, DocUnknownPlugins, DocUnknownSpotlight, + ExpectedNameValue, ExpectedNoArgs, IllFormedAttributeInput, InvalidNotableTraitAttr, + MalformedDoc, UnusedDuplicate, }; use crate::parser::{ ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser, @@ -92,6 +93,78 @@ fn expected_string_literal( cx.emit_lint(INVALID_DOC_ATTRIBUTES, MalformedDoc, span); } +fn parse_notable_trait( + cx: &mut AcceptContext<'_, '_>, + path: &OwnedPathParser, + args: &ArgParser, + attr_value: &mut Option<(Option<(NotableTraitColor, Span)>, Span)>, + attr_name: Symbol, +) { + let span = path.span(); + + let notable_trait_color_attr = match args { + ArgParser::NoArgs => None, + ArgParser::List(meta_item_list_parser) => { + if meta_item_list_parser.is_empty() { + None + } else if let Some(meta_item) = meta_item_list_parser.as_single() { + Some(meta_item) + } else { + cx.emit_lint(INVALID_DOC_ATTRIBUTES, InvalidNotableTraitAttr, span); + return; + } + } + ArgParser::NameValue(_) => { + cx.emit_lint(INVALID_DOC_ATTRIBUTES, InvalidNotableTraitAttr, span); + return; + } + }; + + let notable_trait_color_and_span = + if let Some(notable_trait_color_attr) = notable_trait_color_attr { + let Some(notable_trait_color_attr) = notable_trait_color_attr.meta_item() else { + cx.emit_lint(INVALID_DOC_ATTRIBUTES, InvalidNotableTraitAttr, span); + return; + }; + if !notable_trait_color_attr.path().word_is(sym::color) { + cx.emit_lint(INVALID_DOC_ATTRIBUTES, InvalidNotableTraitAttr, span); + return; + } + let Some(notable_trait_color) = notable_trait_color_attr.args().as_name_value() else { + cx.emit_lint(INVALID_DOC_ATTRIBUTES, InvalidNotableTraitAttr, span); + return; + }; + let Some(notable_trait_color) = notable_trait_color.value_as_str() else { + cx.emit_lint(INVALID_DOC_ATTRIBUTES, InvalidNotableTraitAttr, span); + return; + }; + let notable_trait_color = match notable_trait_color.as_str() { + "grey" => NotableTraitColor::Grey, + "red" => NotableTraitColor::Red, + "green" => NotableTraitColor::Green, + "yellow" => NotableTraitColor::Yellow, + "blue" => NotableTraitColor::Blue, + "magenta" => NotableTraitColor::Magenta, + "cyan" => NotableTraitColor::Cyan, + "transparent" => NotableTraitColor::Transparent, + _ => { + cx.emit_lint(INVALID_DOC_ATTRIBUTES, InvalidNotableTraitAttr, span); + return; + } + }; + Some((notable_trait_color, notable_trait_color_attr.span())) + } else { + None + }; + + if cx.shared.target != Target::Trait { + cx.emit_lint(INVALID_DOC_ATTRIBUTES, DocAttrNotTraitLevel { span, attr_name }, span); + return; + } + + *attr_value = Some((notable_trait_color_and_span, span)); +} + fn parse_keyword_and_attribute( cx: &mut AcceptContext<'_, '_>, path: &OwnedPathParser, @@ -594,7 +667,13 @@ impl DocParser { } Some(sym::notable_trait) => { gated!(doc_notable_trait); - no_args!(notable_trait) + parse_notable_trait( + cx, + path, + args, + &mut self.attribute.notable_trait, + sym::notable_trait, + ) } Some(sym::keyword) => { gated!(rustdoc_internals); diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index e5f49690f71dc..e667d1fd457a6 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -336,6 +336,13 @@ pub(crate) struct ExpectedNoArgs; )] pub(crate) struct ExpectedNameValue; +#[derive(Diagnostic)] +#[diag("expected either `doc(notable_trait)` or `doc(notable_trait=\"...\")`")] +#[warning( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" +)] +pub(crate) struct InvalidNotableTraitAttr; + #[derive(Diagnostic)] #[diag("malformed `{$attribute}` attribute")] #[help("{$options}")] @@ -981,6 +988,17 @@ pub(crate) struct DocAttributeNotAttribute { pub attribute: Symbol, } +#[derive(Diagnostic)] +#[diag("`#![doc({$attr_name})]` must be a trait attribute")] +#[warning( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" +)] +pub(crate) struct DocAttrNotTraitLevel { + #[primary_span] + pub span: Span, + pub attr_name: Symbol, +} + #[derive(Diagnostic)] #[diag( "`#[target_feature]` cannot be applied to a {$kind -> diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 706d89ca52749..166d34a46f248 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -54,7 +54,7 @@ use rustc_ast as ast; use rustc_ast::expand::allocator::AllocatorKind; use rustc_ast::tokenstream::TokenStream; use rustc_attr_ir::lang_items::{LangItem, LanguageItems}; -use rustc_attr_ir::{CanonicalSymbols, EiiDecl, EiiImpl, StrippedCfgItem}; +use rustc_attr_ir::{CanonicalSymbols, EiiDecl, EiiImpl, NotableTraitColor, StrippedCfgItem}; use rustc_crate_store::{ CrateDepKind, CrateSource, ExternCrate, ForeignModule, LinkagePreference, NativeLib, }; @@ -1530,8 +1530,10 @@ rustc_queries! { separate_provide_extern } - /// Determines whether an item is annotated with `#[doc(notable_trait)]`. - query is_doc_notable_trait(def_id: DefId) -> bool { + /// If an item is annotated with `#[doc(notable_trait)]`, + /// returns the color used to render its badge. If the crate specifies + /// no color, `Transparent` is used. + query doc_notable_trait(def_id: DefId) -> Option<&'tcx NotableTraitColor> { desc { "checking whether `{}` is `doc(notable_trait)`", tcx.def_path_str(def_id) } } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index db983038e1af9..45c971632fd65 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -8,6 +8,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hash::{StableHash, StableHasher}; use rustc_errors::ErrorGuaranteed; use rustc_hashes::Hash128; +use rustc_hir::attrs::NotableTraitColor; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; use rustc_hir::{self as hir, find_attr}; @@ -1712,8 +1713,15 @@ fn is_doc_hidden(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { } /// Determines whether an item is annotated with `doc(notable_trait)`. -pub fn is_doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - find_attr!(tcx, def_id, Doc(doc) if doc.notable_trait.is_some()) +pub fn doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Option<&'_ NotableTraitColor> { + find_attr!(tcx, def_id, Doc(doc) if doc.notable_trait.is_some() => { + let (color, _span) = doc.notable_trait.as_ref()?; + if let Some((color, _span)) = color { + color + } else { + &NotableTraitColor::Transparent + } + }) } /// Determines whether an item is an intrinsic (which may be via Abi or via the `rustc_intrinsic` attribute). @@ -1743,7 +1751,7 @@ pub fn provide(providers: &mut Providers) { *providers = Providers { reveal_opaque_types_in_bounds, is_doc_hidden, - is_doc_notable_trait, + doc_notable_trait, intrinsic_raw, ..*providers } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 39d9d2b7b05ce..8f35d43201a2b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -665,6 +665,7 @@ symbols! { cold, cold_path, collapse_debuginfo, + color, column, common, compare_bytes, diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs index a05dade2bdcf6..145cee958aeb3 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -81,7 +81,7 @@ use crate::vec::Vec; /// [`&str`]: prim@str /// [`std::io`]: crate::io #[stable(feature = "rust1", since = "1.0.0")] -#[doc(notable_trait)] +#[doc(notable_trait(color = "transparent"))] #[cfg_attr(not(test), rustc_diagnostic_item = "IoRead")] #[rustc_must_implement_one_of(read_buf, read)] // Keep this order, it's important for rust-analyzer (the preferred-to-implement method should come first). pub trait Read { diff --git a/library/core/src/future/future.rs b/library/core/src/future/future.rs index fab13bb7c8535..b84b00510c41e 100644 --- a/library/core/src/future/future.rs +++ b/library/core/src/future/future.rs @@ -25,7 +25,7 @@ use crate::task::{Context, Poll}; /// /// [`async`]: ../../std/keyword.async.html /// [`Waker`]: crate::task::Waker -#[doc(notable_trait)] +#[doc(notable_trait(color = "transparent"))] #[doc(search_unbox)] #[must_use = "futures do nothing unless you `.await` or poll them"] #[stable(feature = "futures_api", since = "1.36.0")] diff --git a/library/core/src/io/write.rs b/library/core/src/io/write.rs index cdddd380885f4..1318d4bc0ee8a 100644 --- a/library/core/src/io/write.rs +++ b/library/core/src/io/write.rs @@ -47,7 +47,7 @@ use crate::io::{Error, IoSlice, Result}; /// /// [`write_all`]: Write::write_all #[stable(feature = "rust1", since = "1.0.0")] -#[doc(notable_trait)] +#[doc(notable_trait(color = "transparent"))] #[cfg_attr(not(test), rustc_diagnostic_item = "IoWrite")] pub trait Write { /// Writes a buffer into this writer, returning how many bytes were written. diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index cc4077d0ee26f..a5165217aca43 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -34,7 +34,7 @@ fn _assert_is_dyn_compatible(_: &dyn Iterator) {} label = "`{Self}` is not an iterator", message = "`{Self}` is not an iterator" )] -#[doc(notable_trait)] +#[doc(notable_trait(color = "transparent"))] #[lang = "iterator"] #[rustc_diagnostic_item = "Iterator"] #[must_use = "iterators are lazy and do nothing unless consumed"] diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index ab317ba1048a4..9bd795b759ec4 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -75,8 +75,21 @@ on them: `#[doc(notable_trait)]`. This means that you can apply this attribute to your own trait to include it in the "Notable traits" dialog in documentation. In addition to the "Notable traits" dialog, every type that implements a -`#[doc(notable_trait)]` trait renders a colored badge for that trait at the top +`#[doc(notable_trait)]` trait renders a badge for that trait at the top of its page, making the relationship easy to spot when browsing the type. +To set a color for the badge, write `#[doc(notable_trait(color="red"))]` or +one of the other colors in the list (from the [ANSI 3 bit terminal palette][]): + +[ANSI 3 bit terminal palette]: https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit + +- grey +- red +- green +- yellow +- blue +- magenta +- cyan +- transparent The `#[doc(notable_trait)]` attribute currently requires the `#![feature(doc_notable_trait)]` feature gate. For more information, see [its chapter in the Unstable Book][unstable-notable_trait] diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 744c7eb288dfa..47b2d8d5879a1 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1349,7 +1349,7 @@ impl Trait { tcx.trait_is_auto(self.def_id) } pub(crate) fn is_notable_trait(&self, tcx: TyCtxt<'_>) -> bool { - tcx.is_doc_notable_trait(self.def_id) + tcx.doc_notable_trait(self.def_id).is_some() } pub(crate) fn safety(&self, tcx: TyCtxt<'_>) -> hir::Safety { tcx.trait_def(self.def_id).safety diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index a9ecf6d66d001..6b278316c6153 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -53,7 +53,9 @@ use itertools::Either; use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir as hir; -use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation, RustcVersion}; +use rustc_hir::attrs::{ + AttributeKind, DeprecatedSince, Deprecation, NotableTraitColor, RustcVersion, +}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::{ConstStability, Mutability, StabilityLevel, StableSince}; @@ -1712,13 +1714,64 @@ fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option) -> (String, String) { +fn notable_traits_decl_write_html<'cx>( + notable_impls: impl Iterator, + f: &mut fmt::Formatter<'_>, + cx: &Context<'_>, +) -> Result<(), fmt::Error> { + let mut notable_impls = notable_impls.peekable(); + let has_notable_impl = if let Some((impl_, _)) = notable_impls.peek() { + write!( + f, + "

Notable traits for {}

\ +
",
+            print_type(&impl_.for_, cx),
+        )?;
+        true
+    } else {
+        false
+    };
+
+    for (impl_, trait_did) in notable_impls {
+        write!(f, "
{}
", print_impl(impl_, false, cx))?; + for it in &impl_.items { + let clean::AssocTypeItem(tydef, ..) = &it.kind else { + continue; + }; + + let empty_set = FxIndexSet::default(); + let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set); + + write!( + f, + "
{};
", + assoc_type( + it, + &tydef.generics, + &[], // intentionally leaving out bounds + Some(&tydef.type_), + src_link, + 0, + cx, + ) + )?; + } + } + + if !has_notable_impl { + f.write_str("
")?; + } + + Ok(()) +} + +fn notable_traits_decl_for_type(ty: &clean::Type, cx: &Context<'_>) -> (String, String) { let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this"); let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this"); let out = fmt::from_fn(|f| { - let mut notable_impls = impls + let notable_impls = impls .iter() .map(|impl_| impl_.inner_impl()) .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive) @@ -1736,60 +1789,57 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) { } else { None } - }) - .peekable(); - - let has_notable_impl = if let Some((impl_, _)) = notable_impls.peek() { - write!( - f, - "

Notable traits for {}

\ -
",
-                print_type(&impl_.for_, cx),
-            )?;
-            true
-        } else {
-            false
-        };
-
-        for (impl_, trait_did) in notable_impls {
-            write!(f, "
{}
", print_impl(impl_, false, cx))?; - for it in &impl_.items { - let clean::AssocTypeItem(tydef, ..) = &it.kind else { - continue; - }; - - let empty_set = FxIndexSet::default(); - let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set); + }); - write!( - f, - "
{};
", - assoc_type( - it, - &tydef.generics, - &[], // intentionally leaving out bounds - Some(&tydef.type_), - src_link, - 0, - cx, - ) - )?; - } - } + notable_traits_decl_write_html(notable_impls, f, cx) + }) + .to_string(); - if !has_notable_impl { - f.write_str("
")?; - } + (format!("{:#}", print_type(ty, cx)), out) +} - Ok(()) +fn notable_traits_decl_for_item(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> { + let out = fmt::from_fn(|f| { + let notable_impls = if let Some(def_id) = item.def_id() + && !is_notable_trait_passthrough(def_id, cx) + && let Some(impls) = cx.cache().impls.get(&def_id) + { + impls + .iter() + .map(Impl::inner_impl) + .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive) + .filter_map(|impl_| { + if let Some(trait_) = &impl_.trait_ + && let trait_did = trait_.def_id() + && let Some(trait_) = cx.cache().traits.get(&trait_did) + && trait_.is_notable_trait(cx.tcx()) + { + Some((impl_, trait_did)) + } else { + None + } + }) + } else { + return Ok(()); + }; + notable_traits_decl_write_html(notable_impls, f, cx) }) .to_string(); - (format!("{:#}", print_type(ty, cx)), out) + Some((item.name?.to_string(), out)) } -fn notable_traits_json<'a>(tys: impl Iterator, cx: &Context<'_>) -> String { - let mut mp = tys.map(|ty| notable_traits_decl(ty, cx)).collect::>(); +fn notable_traits_json<'a>( + tys: impl Iterator, + item: &clean::Item, + cx: &Context<'_>, +) -> String { + let mut mp = tys.map(|ty| notable_traits_decl_for_type(ty, cx)).collect::>(); + if let Some((item_name, item_decl)) = notable_traits_decl_for_item(item, cx) + && !item_decl.is_empty() + { + mp.insert(item_name, item_decl); + } mp.sort_unstable_keys(); serde_json::to_string(&mp).expect("serialize (string, string) -> json object cannot fail") } @@ -1799,6 +1849,8 @@ pub(crate) struct NotableTraitBadge { pub full_path: String, /// Relative URL to the trait page, or `None` if it cannot be linked. pub href: Option, + /// One of the color names. + pub color: NotableTraitColor, } /// Returns all `#[doc(notable_trait)]` traits that `item` implements, to be @@ -1816,15 +1868,15 @@ pub(crate) fn notable_trait_badges(item: &clean::Item, cx: &Context<'_>) -> Vec< .filter_map(|impl_| { if let Some(trait_) = &impl_.trait_ && let trait_did = trait_.def_id() - && let Some(trait_) = cx.cache().traits.get(&trait_did) - && trait_.is_notable_trait(tcx) + && cx.cache().traits.contains_key(&trait_did) + && let Some(&color) = tcx.doc_notable_trait(trait_did) { let name = tcx.item_name(trait_did).to_string(); let (full_path, href) = match href(trait_did, cx) { Ok(info) => (join_path_syms(&info.rust_path), Some(info.url)), Err(_) => (tcx.def_path_str(trait_did), None), }; - Some((name.clone(), NotableTraitBadge { name, full_path, href })) + Some((name.clone(), NotableTraitBadge { name, full_path, href, color })) } else { None } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 6f66dcf9eae83..68003f7751333 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1,8 +1,6 @@ use std::borrow::Cow; use std::cmp::Ordering; -use std::collections::hash_map::DefaultHasher; use std::fmt::{self, Display, Write as _}; -use std::hash::{Hash, Hasher}; use std::iter; use askama::Template; @@ -10,6 +8,7 @@ use rustc_abi::VariantIdx; use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_hir as hir; +use rustc_hir::attrs::NotableTraitColor; use rustc_hir::def::{CtorKind, MacroKinds}; use rustc_hir::def_id::DefId; use rustc_index::IndexVec; @@ -58,8 +57,8 @@ struct NotableTraitBadgeVars { full_path: String, /// Relative URL to the trait page, or `None` when not linkable. href: Option, - /// Index of the `.notable-trait-badge-{n}` color class. - color_index: u8, + /// Name of the `.notable-trait-badge-{color}` color class. + color: NotableTraitColor, } #[derive(Template)] @@ -126,22 +125,14 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item) -> impl fmt::Disp let notable_trait_badges: Vec = notable_trait_badges(item, cx) .into_iter() - .map(|info| { - // Stable per-trait color from a hash of the trait path so the - // same trait gets the same badge color across pages. - // This won't be stable between releases though. - let mut h = DefaultHasher::new(); - info.full_path.hash(&mut h); - const BADGE_COLORS: u8 = 6; - let color_index = (h.finish() as u8) % BADGE_COLORS; - NotableTraitBadgeVars { - name: info.name, - full_path: info.full_path, - href: info.href, - color_index, - } + .map(|info| NotableTraitBadgeVars { + name: info.name, + full_path: info.full_path, + href: info.href, + color: info.color, }) .collect(); + let has_notable_trait_badges = !notable_trait_badges.is_empty(); let path_components = if item.is_fake_item() { vec![] @@ -219,11 +210,11 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item) -> impl fmt::Disp // Render notable-traits.js used for all methods in this module. let mut types_with_notable_traits = cx.types_with_notable_traits.borrow_mut(); - if !types_with_notable_traits.is_empty() { + if !types_with_notable_traits.is_empty() || has_notable_trait_badges { write!( buf, r#""#, - notable_traits_json(types_with_notable_traits.iter(), cx), + notable_traits_json(types_with_notable_traits.iter(), item, cx), )?; types_with_notable_traits.clear(); } diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css index 085880b215d14..564e785a19f93 100644 --- a/src/librustdoc/html/static/css/noscript.css +++ b/src/librustdoc/html/static/css/noscript.css @@ -141,12 +141,14 @@ nav.sub { --scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0); --sidebar-resizer-hover: hsl(207, 90%, 66%); --sidebar-resizer-active: hsl(207, 90%, 54%); - --notable-badge-pink: oklch(0.88 0.21 0); - --notable-badge-red: oklch(0.88 0.21 40); - --notable-badge-orange: oklch(0.88 0.21 70); + --notable-badge-grey: oklch(0.88 0 0); + --notable-badge-red: oklch(0.88 0.21 20); --notable-badge-green: oklch(0.88 0.21 150); + --notable-badge-yellow: oklch(0.88 0.21 70); --notable-badge-blue: oklch(0.88 0.21 240); - --notable-badge-violet: oklch(0.88 0.21 300); + --notable-badge-magenta: oklch(0.88 0.21 320); + --notable-badge-cyan: oklch(0.88 0.21 180); + --notable-badge-transparent: transparent; } /* End theme: light */ @@ -258,12 +260,14 @@ nav.sub { --scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0); --sidebar-resizer-hover: hsl(207, 30%, 54%); --sidebar-resizer-active: hsl(207, 90%, 54%); - --notable-badge-pink: oklch(0.55 0.21 0); - --notable-badge-red: oklch(0.55 0.21 40); - --notable-badge-orange: oklch(0.55 0.21 70); + --notable-badge-grey: oklch(0.55 0 0); + --notable-badge-red: oklch(0.55 0.21 20); --notable-badge-green: oklch(0.55 0.21 150); + --notable-badge-yellow: oklch(0.55 0.21 100); --notable-badge-blue: oklch(0.55 0.21 240); - --notable-badge-violet: oklch(0.55 0.21 300); + --notable-badge-magenta: oklch(0.55 0.21 320); + --notable-badge-cyan: oklch(0.55 0.21 180); + --notable-badge-transparent: transparent; } /* End theme: dark */ } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 6e7a6daa307cd..e7479c030b630 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1648,13 +1648,23 @@ so that we can apply CSS-filters to change the arrow color in themes */ } .notable-trait-badge-container { - padding: 0.5rem 0; + padding-top: 8px; display: flex; flex-wrap: wrap; - gap: 0.5rem; + gap: 8px; + margin-left: var(--docblock-indent); + position: relative; } -.notable-trait-badge-container > a { +.notable-trait-badge-container > a.tooltip { + position: absolute; + width: 16px; + line-height: 1.5rem; + vertical-align: center; + left: calc(-1 * var(--docblock-indent)); +} + +.notable-trait-badge-container > a:not(.tooltip) { display: flex; align-items: center; width: fit-content; @@ -1666,16 +1676,47 @@ so that we can apply CSS-filters to change the arrow color in themes */ color: var(--main-color); } -.notable-trait-badge-container > a:hover { - text-decoration: none; +/* this list of colors is lifted from the ANSI escape codes list: + https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit + This way, CLI-based tooling can adapt them without having to do + complex platform support dances. */ +.notable-trait-badge-container > .notable-trait-badge-grey { + background: var(--notable-badge-grey); + border: solid 1px var(--notable-badge-grey); +} +.notable-trait-badge-container > .notable-trait-badge-red { + background: var(--notable-badge-red); + border: solid 1px var(--notable-badge-red); +} +.notable-trait-badge-container > .notable-trait-badge-green { + background: var(--notable-badge-green); + border: solid 1px var(--notable-badge-green); +} +.notable-trait-badge-container > .notable-trait-badge-yellow { + background: var(--notable-badge-yellow); + border: solid 1px var(--notable-badge-yellow); +} +.notable-trait-badge-container > .notable-trait-badge-blue { + background: var(--notable-badge-blue); + border: solid 1px var(--notable-badge-blue); +} +.notable-trait-badge-container > .notable-trait-badge-magenta { + background: var(--notable-badge-magenta); + border: solid 1px var(--notable-badge-magenta); +} +.notable-trait-badge-container > .notable-trait-badge-cyan { + background: var(--notable-badge-cyan); + border: solid 1px var(--notable-badge-cyan); +} +.notable-trait-badge-container > .notable-trait-badge-transparent { + background: var(--notable-badge-transparent); + border: solid 1px var(--border-color); } -.notable-trait-badge-container > .badge-0 { background: var(--notable-badge-pink); } -.notable-trait-badge-container > .badge-1 { background: var(--notable-badge-red); } -.notable-trait-badge-container > .badge-2 { background: var(--notable-badge-orange); } -.notable-trait-badge-container > .badge-3 { background: var(--notable-badge-green); } -.notable-trait-badge-container > .badge-4 { background: var(--notable-badge-blue); } -.notable-trait-badge-container > .badge-5 { background: var(--notable-badge-violet); } +.notable-trait-badge-container > a:hover:not(.tooltip) { + text-decoration: none; + border: solid 1px var(--settings-button-border-focus); +} .rightside { padding-left: 12px; @@ -3310,12 +3351,14 @@ by default. --scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0); --sidebar-resizer-hover: hsl(207, 90%, 66%); --sidebar-resizer-active: hsl(207, 90%, 54%); - --notable-badge-pink: oklch(0.88 0.21 0); - --notable-badge-red: oklch(0.88 0.21 40); - --notable-badge-orange: oklch(0.88 0.21 70); + --notable-badge-grey: oklch(0.88 0 0); + --notable-badge-red: oklch(0.88 0.21 20); --notable-badge-green: oklch(0.88 0.21 150); + --notable-badge-yellow: oklch(0.88 0.21 70); --notable-badge-blue: oklch(0.88 0.21 240); - --notable-badge-violet: oklch(0.88 0.21 300); + --notable-badge-magenta: oklch(0.88 0.21 320); + --notable-badge-cyan: oklch(0.88 0.21 180); + --notable-badge-transparent: transparent; } /* End theme: light */ @@ -3426,12 +3469,14 @@ by default. --scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0); --sidebar-resizer-hover: hsl(207, 30%, 54%); --sidebar-resizer-active: hsl(207, 90%, 54%); - --notable-badge-pink: oklch(0.55 0.21 0); - --notable-badge-red: oklch(0.55 0.21 40); - --notable-badge-orange: oklch(0.55 0.21 70); + --notable-badge-grey: oklch(0.55 0 0); + --notable-badge-red: oklch(0.55 0.21 20); --notable-badge-green: oklch(0.55 0.21 150); + --notable-badge-yellow: oklch(0.55 0.21 100); --notable-badge-blue: oklch(0.55 0.21 240); - --notable-badge-violet: oklch(0.55 0.21 300); + --notable-badge-magenta: oklch(0.55 0.21 320); + --notable-badge-cyan: oklch(0.55 0.21 180); + --notable-badge-transparent: transparent; } /* End theme: dark */ @@ -3546,12 +3591,14 @@ Original by Dempfi (https://github.com/dempfi/ayu) --scrape-example-code-wrapper-background-end: rgba(15, 20, 25, 0); --sidebar-resizer-hover: hsl(34, 50%, 33%); --sidebar-resizer-active: hsl(34, 100%, 66%); - --notable-badge-pink: oklch(0.49 0.21 0); - --notable-badge-red: oklch(0.49 0.21 40); - --notable-badge-orange: oklch(0.49 0.21 70); + --notable-badge-grey: oklch(0.49 0 0); + --notable-badge-red: oklch(0.49 0.21 20); --notable-badge-green: oklch(0.49 0.21 150); + --notable-badge-yellow: oklch(0.49 0.21 100); --notable-badge-blue: oklch(0.49 0.21 240); - --notable-badge-violet: oklch(0.49 0.21 300); + --notable-badge-magenta: oklch(0.49 0.21 320); + --notable-badge-cyan: oklch(0.49 0.21 180); + --notable-badge-transparent: transparent; } :root[data-theme="ayu"] h1, diff --git a/src/librustdoc/html/templates/print_item.html b/src/librustdoc/html/templates/print_item.html index 4a257923dc53c..e5d3d1adbdcb2 100644 --- a/src/librustdoc/html/templates/print_item.html +++ b/src/librustdoc/html/templates/print_item.html @@ -20,15 +20,6 @@

{# #} {# #} - {% if !notable_trait_badges.is_empty() %} -
- {% for badge in notable_trait_badges.iter() %} - {{badge.name}} - {% endfor %} -
- {% endif %} {% if !stability_since_raw.is_empty() %} {{ stability_since_raw|safe +}} {% endif %} @@ -38,5 +29,15 @@

Source {#+ #} {% else %} {% endmatch %} + {% if !notable_trait_badges.is_empty() %} +
{# #} + + {% for badge in notable_trait_badges.iter() %} + {{badge.name}} + {% endfor %} +
+ {% endif %} {# #} {# #} diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 588a61f8ad3cc..039bb6250df3d 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -1218,7 +1218,13 @@ fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>) name_value_attr(&mut ret, "keyword", keyword); name_value_attr(&mut ret, "attribute", attribute); toggle_attr(&mut ret, "masked", masked); - toggle_attr(&mut ret, "notable_trait", notable_trait); + if let Some((notable_trait, _span)) = notable_trait { + if let Some((color, _span)) = notable_trait { + ret.push(Attribute::Other(format!("#[doc(notable_trait = \"{color}\")]"))); + } else { + ret.push(Attribute::Other(format!("#[doc(notable_trait)]"))); + } + } toggle_attr(&mut ret, "search_unbox", search_unbox); name_value_attr(&mut ret, "html_favicon_url", html_favicon_url); name_value_attr(&mut ret, "html_logo_url", html_logo_url); diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index 1bfac8e67748c..117da9244aacf 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -49,7 +49,7 @@ pub(super) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> debug!("considering extern trait impl {trait_ref:?}"); if crate_items.contains(&ItemId::DefId(trait_ref.def_id())) || Some(trait_ref.def_id()) == tcx.lang_items().deref_trait() - || tcx.is_doc_notable_trait(trait_ref.def_id()) + || tcx.doc_notable_trait(trait_ref.def_id()).is_some() { debug!("-> inlining due to trait"); cx.with_param_env(impl_def_id, |cx| { diff --git a/tests/rustdoc-gui/notable-trait-badges.goml b/tests/rustdoc-gui/notable-trait-badges.goml new file mode 100644 index 0000000000000..3bb30991d031d --- /dev/null +++ b/tests/rustdoc-gui/notable-trait-badges.goml @@ -0,0 +1,81 @@ +// Ensures that the theme change is working as expected. +include: "utils.goml" +go-to: "file://" + |DOC_PATH| + "/lib2/notable_trait_colors/struct.NotableTraitColors.html" + +define-function: ( + "test-badges", + [ + grey, red, green, yellow, blue, magenta, cyan, transparent, transparent_border, + ], + block { + assert-css: (".notable-trait-badge-grey", {"background-color": |grey|, "border-color": |grey|}) + assert-css: (".notable-trait-badge-red", {"background-color": |red|, "border-color": |red|}) + assert-css: (".notable-trait-badge-green", {"background-color": |green|, "border-color": |green|}) + assert-css: (".notable-trait-badge-yellow", {"background-color": |yellow|, "border-color": |yellow|}) + assert-css: (".notable-trait-badge-blue", {"background-color": |blue|, "border-color": |blue|}) + assert-css: (".notable-trait-badge-magenta", {"background-color": |magenta|, "border-color": |magenta|}) + assert-css: (".notable-trait-badge-cyan", {"background-color": |cyan|, "border-color": |cyan|}) + assert-css: (".notable-trait-badge-transparent", { + "background-color": |transparent|, + "border-color": |transparent_border|, + }) + store-css: (".top-doc > .docblock", {"margin-left": top_docblock_margin_left}) + assert-css: (".notable-trait-badge-container", {"margin-left": |top_docblock_margin_left|}) + store-position: (".top-doc > .docblock", {"x": top_docblock_x}) + assert-position: (".notable-trait-badge-container", {"x": |top_docblock_x|}) + store-position: (".top-doc > summary::before", {"x": top_docblock_toggle_x}) + assert-position: (".notable-trait-badge-container > a.tooltip", {"x": |top_docblock_toggle_x|}) + } +) +call-function: ("switch-theme", {"theme": "dark"}) +call-function: ("test-badges", { + "grey": "oklch(0.55 0 0)", + "red": "oklch(0.55 0.21 20)", + "green": "oklch(0.55 0.21 150)", + "yellow": "oklch(0.55 0.21 100)", + "blue": "oklch(0.55 0.21 240)", + "magenta": "oklch(0.55 0.21 320)", + "cyan": "oklch(0.55 0.21 180)", + "transparent": "transparent", + "transparent_border": "#e0e0e0", +}) + +call-function: ("switch-theme", {"theme": "light"}) +call-function: ("test-badges", { + "grey": "oklch(0.88 0 0)", + "red": "oklch(0.88 0.21 20)", + "green": "oklch(0.88 0.21 150)", + "yellow": "oklch(0.88 0.21 70)", + "blue": "oklch(0.88 0.21 240)", + "magenta": "oklch(0.88 0.21 320)", + "cyan": "oklch(0.88 0.21 180)", + "transparent": "transparent", + "transparent_border": "#e0e0e0", +}) + +call-function: ("switch-theme", {"theme": "ayu"}) +call-function: ("test-badges", { + "grey": "oklch(0.49 0 0)", + "red": "oklch(0.49 0.21 20)", + "green": "oklch(0.49 0.21 150)", + "yellow": "oklch(0.49 0.21 100)", + "blue": "oklch(0.49 0.21 240)", + "magenta": "oklch(0.49 0.21 320)", + "cyan": "oklch(0.49 0.21 180)", + "transparent": "transparent", + "transparent_border": "#5c6773", +}) + +assert-false: ".tooltip.popover" +click: ".notable-trait-badge-container > a.tooltip" +wait-for: ".tooltip.popover" +assert-text: (".tooltip.popover h3", "Notable traits for NotableTraitColors") +assert-count: (".tooltip.popover .where", 8) +assert-text: (".tooltip.popover .where:nth-child(1)", "impl Grey for NotableTraitColors") +assert-text: (".tooltip.popover .where:nth-child(2)", "impl Red for NotableTraitColors") +assert-text: (".tooltip.popover .where:nth-child(3)", "impl Green for NotableTraitColors") +assert-text: (".tooltip.popover .where:nth-child(4)", "impl Yellow for NotableTraitColors") +assert-text: (".tooltip.popover .where:nth-child(5)", "impl Blue for NotableTraitColors") +assert-text: (".tooltip.popover .where:nth-child(6)", "impl Magenta for NotableTraitColors") +assert-text: (".tooltip.popover .where:nth-child(7)", "impl Cyan for NotableTraitColors") +assert-text: (".tooltip.popover .where:nth-child(8)", "impl Transparent for NotableTraitColors") diff --git a/tests/rustdoc-gui/notable-traits.goml b/tests/rustdoc-gui/notable-traits.goml index f31b83e12edf0..60a66b219b2f8 100644 --- a/tests/rustdoc-gui/notable-traits.goml +++ b/tests/rustdoc-gui/notable-traits.goml @@ -2,11 +2,12 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/notable/struct.Wrapper.html" show-text: true store-css: (".notable-trait-badge-container", {"background-color": background_color}) -// The background color should be different. -assert-css-false: (".notable-trait-badge-container > a", {"background-color": |background_color|}) +// The border color should be different. +assert-css-false: (".notable-trait-badge-container > a:not(.tooltip)", {"border-color": |background_color|}) +assert-css-false: (".notable-trait-badge-container > a:not(.tooltip)", {"border-color": "transparent"}) store-css: ("#trait-implementations", {"color": text_color}) -assert-css: (".notable-trait-badge-container > a", { +assert-css: (".notable-trait-badge-container > a:not(.tooltip)", { // The color should be the same as the "main color". "color": |text_color|, // Checking some additional CSS values. diff --git a/tests/rustdoc-gui/src/lib2/lib.rs b/tests/rustdoc-gui/src/lib2/lib.rs index 28dfa56ea1ad4..3104b88b183e3 100644 --- a/tests/rustdoc-gui/src/lib2/lib.rs +++ b/tests/rustdoc-gui/src/lib2/lib.rs @@ -1,7 +1,6 @@ // ignore-tidy-file-linelength -#![feature(doc_cfg)] -#![feature(negative_impls)] +#![feature(doc_cfg, negative_impls, doc_notable_trait)] pub mod another_folder; pub mod another_mod; @@ -411,3 +410,32 @@ pub mod deprecated { fn depr_deprecated_fn() {} } } + +pub mod notable_trait_colors { + /// Top doc + pub struct NotableTraitColors; + #[doc(notable_trait(color="grey"))] + pub trait Grey {} + impl Grey for NotableTraitColors {} + #[doc(notable_trait(color="red"))] + pub trait Red {} + impl Red for NotableTraitColors {} + #[doc(notable_trait(color="green"))] + pub trait Green {} + impl Green for NotableTraitColors {} + #[doc(notable_trait(color="yellow"))] + pub trait Yellow {} + impl Yellow for NotableTraitColors {} + #[doc(notable_trait(color="blue"))] + pub trait Blue {} + impl Blue for NotableTraitColors {} + #[doc(notable_trait(color="magenta"))] + pub trait Magenta {} + impl Magenta for NotableTraitColors {} + #[doc(notable_trait(color="cyan"))] + pub trait Cyan {} + impl Cyan for NotableTraitColors {} + #[doc(notable_trait(color="transparent"))] + pub trait Transparent {} + impl Transparent for NotableTraitColors {} +} diff --git a/tests/rustdoc-html/notable-trait/notable-trait-badge-negative.rs b/tests/rustdoc-html/notable-trait/notable-trait-badge-negative.rs index 225572513c40f..dc7b349ede2f3 100644 --- a/tests/rustdoc-html/notable-trait/notable-trait-badge-negative.rs +++ b/tests/rustdoc-html/notable-trait/notable-trait-badge-negative.rs @@ -8,7 +8,8 @@ pub trait Pos {} // A negative impl must not produce a badge. //@ has 'foo/struct.T.html' -//@ count - '//div[@class="notable-trait-badge-container"]/a' 1 +//@ count - '//div[@class="notable-trait-badge-container"]/a' 2 +//@ has - '//div[@class="notable-trait-badge-container"]/a[@href="#"]' 'ⓘ' //@ has - '//div[@class="notable-trait-badge-container"]/a[@href="trait.Pos.html"]' 'Pos' pub struct T; impl !Neg for T {} diff --git a/tests/rustdoc-html/notable-trait/notable-trait-badge-supertrait.rs b/tests/rustdoc-html/notable-trait/notable-trait-badge-supertrait.rs index 9bb3226099a51..2d29fbeedf80e 100644 --- a/tests/rustdoc-html/notable-trait/notable-trait-badge-supertrait.rs +++ b/tests/rustdoc-html/notable-trait/notable-trait-badge-supertrait.rs @@ -9,7 +9,8 @@ pub trait Derived: Base {} //@ has 'foo/struct.S.html' // Implementing `Derived` requires implementing the notable supertrait `Base`, // so its badge shows up. -//@ count - '//div[@class="notable-trait-badge-container"]/a' 1 +//@ count - '//div[@class="notable-trait-badge-container"]/a' 2 +//@ has - '//div[@class="notable-trait-badge-container"]/a[@href="#"]' 'ⓘ' //@ has - '//div[@class="notable-trait-badge-container"]/a[@href="trait.Base.html"]' 'Base' pub struct S; impl Base for S {} diff --git a/tests/rustdoc-html/notable-trait/notable-trait-badge-unlinkable-cross-crate.rs b/tests/rustdoc-html/notable-trait/notable-trait-badge-unlinkable-cross-crate.rs index de7008cf5ab0c..a249a100e6541 100644 --- a/tests/rustdoc-html/notable-trait/notable-trait-badge-unlinkable-cross-crate.rs +++ b/tests/rustdoc-html/notable-trait/notable-trait-badge-unlinkable-cross-crate.rs @@ -12,6 +12,7 @@ use notable_dep::Spaceship; // The badge is present... //@ has - '//div[@class="notable-trait-badge-container"]/a' 'Spaceship' // ...but unlinked: no badge carries an `href`. -//@ count - '//div[@class="notable-trait-badge-container"]/a[@href]' 0 +//@ count - '//div[@class="notable-trait-badge-container"]/a[@href]' 1 +//@ has - '//div[@class="notable-trait-badge-container"]/a[@href="#"]' 'ⓘ' pub struct Rocket; impl Spaceship for Rocket {} diff --git a/tests/rustdoc-html/notable-trait/notable-trait-badge.rs b/tests/rustdoc-html/notable-trait/notable-trait-badge.rs index b53d989d0f819..04e7a52918384 100644 --- a/tests/rustdoc-html/notable-trait/notable-trait-badge.rs +++ b/tests/rustdoc-html/notable-trait/notable-trait-badge.rs @@ -12,8 +12,9 @@ pub trait Plain {} //@ has 'foo/struct.Tagged.html' //@ has - '//div[@class="notable-trait-badge-container"]/a[@href="trait.Labeled.html"][@title="foo::Labeled"]' 'Labeled' // Badges are sorted by trait name, so `AlsoLabeled` precedes `Labeled`. -//@ has - '//div[@class="notable-trait-badge-container"]/a[1]' 'AlsoLabeled' -//@ has - '//div[@class="notable-trait-badge-container"]/a[2]' 'Labeled' +//@ has - '//div[@class="notable-trait-badge-container"]/a[1]' 'ⓘ' +//@ has - '//div[@class="notable-trait-badge-container"]/a[2]' 'AlsoLabeled' +//@ has - '//div[@class="notable-trait-badge-container"]/a[3]' 'Labeled' pub struct Tagged; impl Labeled for Tagged {} impl AlsoLabeled for Tagged {} diff --git a/tests/rustdoc-ui/lints/doc-notable_trait-check.rs b/tests/rustdoc-ui/lints/doc-notable_trait-check.rs new file mode 100644 index 0000000000000..e56ecd573ede1 --- /dev/null +++ b/tests/rustdoc-ui/lints/doc-notable_trait-check.rs @@ -0,0 +1,44 @@ +#![feature(doc_notable_trait)] +#![deny(invalid_doc_attributes)] + +#[doc(notable_trait)] +trait NoColor {} + +#[doc(notable_trait())] +trait NoColor2 {} + +#[doc(notable_trait(color="transparent"))] +trait Transparent {} + +#[doc(notable_trait(color="red"))] +trait Red {} + +#[doc(notable_trait="red")] +//~^ ERROR +//~| WARN previously accepted by the compiler +trait InvalidKV {} + +#[doc(notable_trait)] +//~^ ERROR +//~| WARN previously accepted by the compiler +struct InvalidNotTrait; + +#[doc(notable_trait(check="red"))] +//~^ ERROR +//~| WARN previously accepted by the compiler +trait InvalidKVArg {} + +#[doc(notable_trait(color))] +//~^ ERROR +//~| WARN previously accepted by the compiler +trait InvalidAtomArg {} + +#[doc(notable_trait(color="invalid_color"))] +//~^ ERROR +//~| WARN previously accepted by the compiler +trait InvalidColor {} + +#[doc(notable_trait(color="transparent", color="red"))] +//~^ ERROR +//~| WARN previously accepted by the compiler +trait InvalidMultiple {} diff --git a/tests/rustdoc-ui/lints/doc-notable_trait-check.stderr b/tests/rustdoc-ui/lints/doc-notable_trait-check.stderr new file mode 100644 index 0000000000000..1d53e44d12f86 --- /dev/null +++ b/tests/rustdoc-ui/lints/doc-notable_trait-check.stderr @@ -0,0 +1,55 @@ +error: expected either `doc(notable_trait)` or `doc(notable_trait="...")` + --> $DIR/doc-notable_trait-check.rs:16:7 + | +LL | #[doc(notable_trait="red")] + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +note: the lint level is defined here + --> $DIR/doc-notable_trait-check.rs:2:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: `#![doc(notable_trait)]` must be a trait attribute + --> $DIR/doc-notable_trait-check.rs:21:7 + | +LL | #[doc(notable_trait)] + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: expected either `doc(notable_trait)` or `doc(notable_trait="...")` + --> $DIR/doc-notable_trait-check.rs:26:7 + | +LL | #[doc(notable_trait(check="red"))] + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: expected either `doc(notable_trait)` or `doc(notable_trait="...")` + --> $DIR/doc-notable_trait-check.rs:31:7 + | +LL | #[doc(notable_trait(color))] + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: expected either `doc(notable_trait)` or `doc(notable_trait="...")` + --> $DIR/doc-notable_trait-check.rs:36:7 + | +LL | #[doc(notable_trait(color="invalid_color"))] + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: expected either `doc(notable_trait)` or `doc(notable_trait="...")` + --> $DIR/doc-notable_trait-check.rs:41:7 + | +LL | #[doc(notable_trait(color="transparent", color="red"))] + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: aborting due to 6 previous errors + diff --git a/tests/ui/feature-gates/feature-gate-doc_notable_trait.rs b/tests/ui/feature-gates/feature-gate-doc_notable_trait.rs index 1bc1028b9e0ef..b06b17650da30 100644 --- a/tests/ui/feature-gates/feature-gate-doc_notable_trait.rs +++ b/tests/ui/feature-gates/feature-gate-doc_notable_trait.rs @@ -4,5 +4,7 @@ trait SomeTrait {} fn main() { #[doc(notable_trait)] //~^ ERROR the `doc(notable_trait)` attribute is experimental [E0658] + //~| WARN `#![doc(notable_trait)]` must be a trait attribute + //~| WARN this was previously accepted println!(); } diff --git a/tests/ui/feature-gates/feature-gate-doc_notable_trait.stderr b/tests/ui/feature-gates/feature-gate-doc_notable_trait.stderr index 632c91b929185..a7be03385fc3f 100644 --- a/tests/ui/feature-gates/feature-gate-doc_notable_trait.stderr +++ b/tests/ui/feature-gates/feature-gate-doc_notable_trait.stderr @@ -8,6 +8,15 @@ LL | #[doc(notable_trait)] = help: add `#![feature(doc_notable_trait)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +warning: `#![doc(notable_trait)]` must be a trait attribute + --> $DIR/feature-gate-doc_notable_trait.rs:5:11 + | +LL | #[doc(notable_trait)] + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: `#[warn(invalid_doc_attributes)]` on by default + error[E0658]: the `doc(notable_trait)` attribute is experimental --> $DIR/feature-gate-doc_notable_trait.rs:1:7 | @@ -18,6 +27,6 @@ LL | #[doc(notable_trait)] = help: add `#![feature(doc_notable_trait)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`.