Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion compiler/rustc_attr_ir/src/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ pub struct DocAttribute {
pub keyword: Option<(Symbol, Span)>,
pub attribute: Option<(Symbol, Span)>,
pub masked: Option<Span>,
pub notable_trait: Option<Span>,
pub notable_trait: Option<(Option<(NotableTraitColor, Span)>, Span)>,
pub search_unbox: Option<Span>,

// valid on crate
Expand All @@ -520,6 +520,41 @@ pub struct DocAttribute {
pub no_crate_inject: Option<Span>,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(StableHash, Encodable, Decodable, PrintAttribute)]
pub enum NotableTraitColor {
Grey,
Red,
Green,
Yellow,
Blue,
Magenta,
Cyan,
Transparent,
}

impl From<NotableTraitColor> 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<E: rustc_span::SpanEncoder> rustc_serialize::Encodable<E> for DocAttribute {
fn encode(&self, encoder: &mut E) {
let DocAttribute {
Expand Down
152 changes: 139 additions & 13 deletions compiler/rustc_attr_parsing/src/attributes/doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, MultiSpan, msg};
Expand All @@ -15,13 +15,15 @@ use super::{AcceptMapping, AttributeParser, template};
use crate::context::{AcceptContext, FinalizeContext};
use crate::diagnostics::{
DocAliasBadChar, DocAliasDuplicated, DocAliasEmpty, DocAliasMalformed, DocAliasStartEnd,
DocAttrNotCrateLevel, DocAttributeNotAttribute, DocAutoCfgExpectsHideOrShow,
DocAutoCfgHideShowExpectsList, DocAutoCfgHideShowNoIdentBeforeValues,
DocAutoCfgHideShowUnexpectedItem, DocAutoCfgHideShowUnexpectedItemAfterValues,
DocAutoCfgHideShowValuesMix, DocAutoCfgWrongLiteral, DocInlineConflict, DocInlineOnlyUse,
DocKeywordNotKeyword, DocTestLiteral, DocTestTakesList, DocTestUnknown, DocUnknownAny,
DocUnknownInclude, DocUnknownPasses, DocUnknownPlugins, DocUnknownSpotlight, ExpectedNameValue,
ExpectedNoArgs, IllFormedAttributeInput, MalformedDoc, UnusedDuplicate,
DocAttrNotCrateLevel, DocAttrNotTraitLevel, DocAttributeNotAttribute,
DocAutoCfgExpectsHideOrShow, DocAutoCfgHideShowExpectsList,
DocAutoCfgHideShowNoIdentBeforeValues, DocAutoCfgHideShowUnexpectedItem,
DocAutoCfgHideShowUnexpectedItemAfterValues, DocAutoCfgHideShowValuesMix,
DocAutoCfgWrongLiteral, DocInlineConflict, DocInlineOnlyUse, DocKeywordNotKeyword,
DocTestLiteral, DocTestTakesList, DocTestUnknown, DocUnknownAny, DocUnknownInclude,
DocUnknownPasses, DocUnknownPlugins, DocUnknownSpotlight, DuplicateNotableTraitColor,
ExpectedNameValue, ExpectedNoArgs, IllFormedAttributeInput, InvalidNotableTraitAttr,
InvalidNotableTraitColor, MalformedDoc, UnusedDuplicate,
};
use crate::parser::{
ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser,
Expand Down Expand Up @@ -84,6 +86,124 @@ 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 {
let color_spans: Option<Vec<_>> = meta_item_list_parser
.mixed()
.map(|item| {
let meta = item.meta_item()?;
meta.path().word_is(sym::color).then(|| meta.path().span())
})
.collect();

if let Some(color_spans) = color_spans {
let first_span = color_spans[0];
let duplicate_spans = &color_spans[1..];

let mut spans = MultiSpan::from_spans(duplicate_spans.to_vec());
spans.push_span_label(first_span, "");

for &duplicate_span in duplicate_spans {
spans.push_span_label(duplicate_span, msg!("multiple colors specified"));
}

cx.emit_lint(INVALID_DOC_ATTRIBUTES, DuplicateNotableTraitColor, spans);
} else {
let spans = meta_item_list_parser
.mixed()
.map(|item| match item.meta_item() {
Some(meta) => meta.path().span(),
None => item.span(),
})
.collect();

cx.emit_lint(
INVALID_DOC_ATTRIBUTES,
InvalidNotableTraitAttr,
MultiSpan::from_spans(spans),
);
}

return;
}
}
ArgParser::NameValue(_) => {
// FIXME(164728): every invalid notable trait attribute lint produces exactly
// the same warning and we should switch to more specific diagnostics
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,
notable_trait_color_attr.path().span(),
);
return;
};

let color_value_span = notable_trait_color.value_span;

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" | "gray" => 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, InvalidNotableTraitColor, color_value_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,
Expand Down Expand Up @@ -164,14 +284,14 @@ impl DocParser {
}
Some(sym::attr) => {
let Some(list) = args.as_list() else {
// FIXME: remove this method once merged and uncomment the line below instead.
// cx.expected_list(cx.attr_span, args);
// FIXME(164728): remove this method once merged and uncomment the line
// below instead. cx.expected_list(cx.attr_span, args);
let span = cx.attr_span;
cx.emit_lint(INVALID_DOC_ATTRIBUTES, MalformedDoc, span);
return;
};

// FIXME: convert list into a Vec of `AttributeKind` because current code is awful.
// FIXME(164728): remove this method once merged and uncomment the line
// below instead. cx.expected_list(cx.attr_span, args);
for attr in list.mixed() {
// Arguments of `attr` are checked via the span, so can be safely ignored
attr.ignore_args();
Expand Down Expand Up @@ -611,7 +731,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);
Expand Down
34 changes: 34 additions & 0 deletions compiler/rustc_attr_parsing/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,29 @@ pub(crate) struct ExpectedNoArgs;
)]
pub(crate) struct ExpectedNameValue;

#[derive(Diagnostic)]
#[diag("expected either `doc(notable_trait)` or `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!"
)]
#[help("only one color should be specified")]
pub(crate) struct DuplicateNotableTraitColor;

#[derive(Diagnostic)]
#[diag("expected either `doc(notable_trait)` or `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!"
)]
pub(crate) struct InvalidNotableTraitAttr;

#[derive(Diagnostic)]
#[diag("unknown color in `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!"
)]
#[help("expected grey, gray, red, green, yellow, blue, magenta, cyan, or transparent")]
pub(crate) struct InvalidNotableTraitColor;

#[derive(Diagnostic)]
#[diag("malformed `{$attribute}` attribute")]
#[help("{$options}")]
Expand Down Expand Up @@ -1052,6 +1075,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 ->
Expand Down
8 changes: 5 additions & 3 deletions compiler/rustc_middle/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ use rustc_ast::expand::allocator::AllocatorKind;
use rustc_ast::tokenstream::TokenStream;
use rustc_attr_ir::diagnostic_items::DiagnosticItems;
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,
};
Expand Down Expand Up @@ -1529,8 +1529,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) }
}

Expand Down
15 changes: 11 additions & 4 deletions compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{fmt, iter};

use rustc_abi::{Float, Integer, IntegerType, Size};
use rustc_apfloat::Float as _;
use rustc_attr_ir::find_attr;
use rustc_attr_ir::{NotableTraitColor, find_attr};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::stable_hash::{StableHash, StableHasher};
use rustc_errors::ErrorGuaranteed;
Expand Down Expand Up @@ -1701,8 +1701,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).
Expand Down Expand Up @@ -1732,7 +1739,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
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,7 @@ symbols! {
cold,
cold_path,
collapse_debuginfo,
color,
column,
compare_bytes,
compare_exchange,
Expand Down
2 changes: 1 addition & 1 deletion library/alloc/src/io/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/future/future.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/io/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/iter/traits/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ fn _assert_is_dyn_compatible(_: &dyn Iterator<Item = ()>) {}
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"]
Expand Down
Loading