use rustc_middle::ty::TyCtxt;
use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, NestedMetaItem};
-use rustc_data_structures::stable_set::FxHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{pluralize, struct_span_err, Applicability};
use rustc_feature::{AttributeType, BUILTIN_ATTRIBUTE_MAP};
use rustc_hir as hir;
target: Target,
item: Option<ItemLike<'_>>,
) {
+ let mut doc_aliases = FxHashMap::default();
let mut is_valid = true;
let mut specified_inline = None;
let mut seen = FxHashSet::default();
sym::track_caller => {
self.check_track_caller(hir_id, &attr.span, attrs, span, target)
}
- sym::doc => self.check_doc_attrs(attr, hir_id, target, &mut specified_inline),
+ sym::doc => self.check_doc_attrs(
+ attr,
+ hir_id,
+ target,
+ &mut specified_inline,
+ &mut doc_aliases,
+ ),
sym::no_link => self.check_no_link(hir_id, &attr, span, target),
sym::export_name => self.check_export_name(hir_id, &attr, span, target),
sym::rustc_layout_scalar_valid_range_start
sym::default_method_body_is_const => {
self.check_default_method_body_is_const(attr, span, target)
}
+ sym::must_not_suspend => self.check_must_not_suspend(&attr, span, target),
sym::rustc_const_unstable
| sym::rustc_const_stable
| sym::unstable
hir_id: HirId,
target: Target,
is_list: bool,
+ aliases: &mut FxHashMap<String, Span>,
) -> bool {
let tcx = self.tcx;
let err_fn = move |span: Span, msg: &str| {
if &*item_name.as_str() == doc_alias {
return err_fn(meta.span(), "is the same as the item's name");
}
+ let span = meta.span();
+ if let Err(entry) = aliases.try_insert(doc_alias.to_owned(), span) {
+ self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, span, |lint| {
+ lint.build("doc alias is duplicated")
+ .span_label(*entry.entry.get(), "first defined here")
+ .emit();
+ });
+ }
true
}
- fn check_doc_alias(&self, meta: &NestedMetaItem, hir_id: HirId, target: Target) -> bool {
+ fn check_doc_alias(
+ &self,
+ meta: &NestedMetaItem,
+ hir_id: HirId,
+ target: Target,
+ aliases: &mut FxHashMap<String, Span>,
+ ) -> 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) {
+ if !self.check_doc_alias_value(
+ v,
+ &s.as_str(),
+ hir_id,
+ target,
+ true,
+ aliases,
+ ) {
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)
+ self.check_doc_alias_value(meta, &doc_alias, hir_id, target, false, aliases)
} else {
self.tcx
.sess
hir_id: HirId,
target: Target,
specified_inline: &mut Option<(bool, Span)>,
+ aliases: &mut FxHashMap<String, Span>,
) -> bool {
let mut is_valid = true;
match i_meta.name_or_empty() {
sym::alias
if !self.check_attr_not_crate_level(&meta, hir_id, "alias")
- || !self.check_doc_alias(&meta, hir_id, target) =>
+ || !self.check_doc_alias(&meta, hir_id, target, aliases) =>
{
is_valid = false
}
// plugins: removed, but rustdoc warns about it itself
sym::alias
| sym::cfg
+ | sym::cfg_hide
| sym::hidden
| sym::html_favicon_url
| sym::html_logo_url
is_valid
}
+ /// Checks if `#[must_not_suspend]` is applied to a function. Returns `true` if valid.
+ fn check_must_not_suspend(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
+ match target {
+ Target::Struct | Target::Enum | Target::Union | Target::Trait => true,
+ _ => {
+ self.tcx
+ .sess
+ .struct_span_err(attr.span, "`must_not_suspend` attribute should be applied to a struct, enum, or trait")
+ .span_label(*span, "is not a struct, enum, or trait")
+ .emit();
+ false
+ }
+ }
+ }
+
/// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid.
fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
match target {
fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) {
if target != Target::MacroDef {
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
- lint.build(&format!("`#[macro_export]` only has an effect on macro definitions"))
- .emit();
+ lint.build("`#[macro_export]` only has an effect on macro definitions").emit();
});
}
}