@@ -9,7 +9,7 @@ use rustc_middle::ty::query::Providers;
99use rustc_middle:: ty:: TyCtxt ;
1010
1111use rustc_ast:: { ast, AttrStyle , Attribute , Lit , LitKind , NestedMetaItem } ;
12- use rustc_data_structures:: stable_set :: FxHashSet ;
12+ use rustc_data_structures:: fx :: { FxHashMap , FxHashSet } ;
1313use rustc_errors:: { pluralize, struct_span_err, Applicability } ;
1414use rustc_feature:: { AttributeType , BUILTIN_ATTRIBUTE_MAP } ;
1515use rustc_hir as hir;
@@ -66,6 +66,7 @@ impl CheckAttrVisitor<'tcx> {
6666 target : Target ,
6767 item : Option < ItemLike < ' _ > > ,
6868 ) {
69+ let mut doc_aliases = FxHashMap :: default ( ) ;
6970 let mut is_valid = true ;
7071 let mut specified_inline = None ;
7172 let mut seen = FxHashSet :: default ( ) ;
@@ -79,7 +80,13 @@ impl CheckAttrVisitor<'tcx> {
7980 sym:: track_caller => {
8081 self . check_track_caller ( hir_id, & attr. span , attrs, span, target)
8182 }
82- sym:: doc => self . check_doc_attrs ( attr, hir_id, target, & mut specified_inline) ,
83+ sym:: doc => self . check_doc_attrs (
84+ attr,
85+ hir_id,
86+ target,
87+ & mut specified_inline,
88+ & mut doc_aliases,
89+ ) ,
8390 sym:: no_link => self . check_no_link ( hir_id, & attr, span, target) ,
8491 sym:: export_name => self . check_export_name ( hir_id, & attr, span, target) ,
8592 sym:: rustc_layout_scalar_valid_range_start
@@ -512,6 +519,7 @@ impl CheckAttrVisitor<'tcx> {
512519 hir_id : HirId ,
513520 target : Target ,
514521 is_list : bool ,
522+ aliases : & mut FxHashMap < String , Span > ,
515523 ) -> bool {
516524 let tcx = self . tcx ;
517525 let err_fn = move |span : Span , msg : & str | {
@@ -582,17 +590,38 @@ impl CheckAttrVisitor<'tcx> {
582590 if & * item_name. as_str ( ) == doc_alias {
583591 return err_fn ( meta. span ( ) , "is the same as the item's name" ) ;
584592 }
593+ let span = meta. span ( ) ;
594+ if let Err ( entry) = aliases. try_insert ( doc_alias. to_owned ( ) , span) {
595+ self . tcx . struct_span_lint_hir ( UNUSED_ATTRIBUTES , hir_id, span, |lint| {
596+ lint. build ( "doc alias is duplicated" )
597+ . span_label ( * entry. entry . get ( ) , "first defined here" )
598+ . emit ( ) ;
599+ } ) ;
600+ }
585601 true
586602 }
587603
588- fn check_doc_alias ( & self , meta : & NestedMetaItem , hir_id : HirId , target : Target ) -> bool {
604+ fn check_doc_alias (
605+ & self ,
606+ meta : & NestedMetaItem ,
607+ hir_id : HirId ,
608+ target : Target ,
609+ aliases : & mut FxHashMap < String , Span > ,
610+ ) -> bool {
589611 if let Some ( values) = meta. meta_item_list ( ) {
590612 let mut errors = 0 ;
591613 for v in values {
592614 match v. literal ( ) {
593615 Some ( l) => match l. kind {
594616 LitKind :: Str ( s, _) => {
595- if !self . check_doc_alias_value ( v, & s. as_str ( ) , hir_id, target, true ) {
617+ if !self . check_doc_alias_value (
618+ v,
619+ & s. as_str ( ) ,
620+ hir_id,
621+ target,
622+ true ,
623+ aliases,
624+ ) {
596625 errors += 1 ;
597626 }
598627 }
@@ -621,7 +650,7 @@ impl CheckAttrVisitor<'tcx> {
621650 }
622651 errors == 0
623652 } else if let Some ( doc_alias) = meta. value_str ( ) . map ( |s| s. to_string ( ) ) {
624- self . check_doc_alias_value ( meta, & doc_alias, hir_id, target, false )
653+ self . check_doc_alias_value ( meta, & doc_alias, hir_id, target, false , aliases )
625654 } else {
626655 self . tcx
627656 . sess
@@ -858,6 +887,7 @@ impl CheckAttrVisitor<'tcx> {
858887 hir_id : HirId ,
859888 target : Target ,
860889 specified_inline : & mut Option < ( bool , Span ) > ,
890+ aliases : & mut FxHashMap < String , Span > ,
861891 ) -> bool {
862892 let mut is_valid = true ;
863893
@@ -867,7 +897,7 @@ impl CheckAttrVisitor<'tcx> {
867897 match i_meta. name_or_empty ( ) {
868898 sym:: alias
869899 if !self . check_attr_not_crate_level ( & meta, hir_id, "alias" )
870- || !self . check_doc_alias ( & meta, hir_id, target) =>
900+ || !self . check_doc_alias ( & meta, hir_id, target, aliases ) =>
871901 {
872902 is_valid = false
873903 }
0 commit comments