@@ -11,6 +11,7 @@ use rustc_ast::{
1111 Item , ItemKind , MethodCall , NodeId , Path , PathSegment , Ty , TyKind ,
1212} ;
1313use rustc_ast_pretty:: pprust:: where_bound_predicate_to_string;
14+ use rustc_attr_parsing:: is_doc_alias_attrs_contain_symbol;
1415use rustc_data_structures:: fx:: { FxHashSet , FxIndexSet } ;
1516use rustc_errors:: codes:: * ;
1617use rustc_errors:: {
@@ -39,7 +40,7 @@ use crate::late::{
3940} ;
4041use crate :: ty:: fast_reject:: SimplifiedType ;
4142use crate :: {
42- Module , ModuleKind , ModuleOrUniformRoot , PathResult , PathSource , Segment , errors,
43+ Module , ModuleKind , ModuleOrUniformRoot , PathResult , PathSource , Resolver , Segment , errors,
4344 path_names_to_string,
4445} ;
4546
@@ -477,6 +478,19 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
477478 return ( err, Vec :: new ( ) ) ;
478479 }
479480
481+ if let Some ( ( did, item) ) = self . lookup_doc_alias_name ( path, source. namespace ( ) ) {
482+ let item_name = item. name ;
483+ let suggestion_name = self . r . tcx . item_name ( did) ;
484+ err. span_suggestion (
485+ item. span ,
486+ format ! ( "`{suggestion_name}` has a name defined in the doc alias attribute as `{item_name}`" ) ,
487+ suggestion_name,
488+ Applicability :: MaybeIncorrect
489+ ) ;
490+
491+ return ( err, Vec :: new ( ) ) ;
492+ } ;
493+
480494 let ( found, suggested_candidates, mut candidates) = self . try_lookup_name_relaxed (
481495 & mut err,
482496 source,
@@ -852,6 +866,65 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
852866 ( false , suggested_candidates, candidates)
853867 }
854868
869+ fn lookup_doc_alias_name ( & mut self , path : & [ Segment ] , ns : Namespace ) -> Option < ( DefId , Ident ) > {
870+ let find_doc_alias_name = |r : & mut Resolver < ' ra , ' _ > , m : Module < ' ra > , item_name : Symbol | {
871+ for resolution in r. resolutions ( m) . borrow ( ) . values ( ) {
872+ let Some ( did) =
873+ resolution. borrow ( ) . binding . and_then ( |binding| binding. res ( ) . opt_def_id ( ) )
874+ else {
875+ continue ;
876+ } ;
877+ if did. is_local ( ) {
878+ // We don't record the doc alias name in the local crate
879+ // because the people who write doc alias are usually not
880+ // confused by them.
881+ continue ;
882+ }
883+ if is_doc_alias_attrs_contain_symbol ( r. tcx . get_attrs ( did, sym:: doc) , item_name) {
884+ return Some ( did) ;
885+ }
886+ }
887+ None
888+ } ;
889+
890+ if path. len ( ) == 1 {
891+ for rib in self . ribs [ ns] . iter ( ) . rev ( ) {
892+ let item = path[ 0 ] . ident ;
893+ if let RibKind :: Module ( module) = rib. kind
894+ && let Some ( did) = find_doc_alias_name ( self . r , module, item. name )
895+ {
896+ return Some ( ( did, item) ) ;
897+ }
898+ }
899+ } else {
900+ // Finds to the last resolved module item in the path
901+ // and searches doc aliases within that module.
902+ //
903+ // Example: For the path `a::b::last_resolved::not_exist::c::d`,
904+ // we will try to find any item has doc aliases named `not_exist`
905+ // in `last_resolved` module.
906+ //
907+ // - Use `skip(1)` because the final segment must remain unresolved.
908+ for ( idx, seg) in path. iter ( ) . enumerate ( ) . rev ( ) . skip ( 1 ) {
909+ let Some ( id) = seg. id else {
910+ continue ;
911+ } ;
912+ let Some ( res) = self . r . partial_res_map . get ( & id) else {
913+ continue ;
914+ } ;
915+ if let Res :: Def ( DefKind :: Mod , module) = res. expect_full_res ( )
916+ && let Some ( module) = self . r . get_module ( module)
917+ && let item = path[ idx + 1 ] . ident
918+ && let Some ( did) = find_doc_alias_name ( self . r , module, item. name )
919+ {
920+ return Some ( ( did, item) ) ;
921+ }
922+ break ;
923+ }
924+ }
925+ None
926+ }
927+
855928 fn suggest_trait_and_bounds (
856929 & mut self ,
857930 err : & mut Diag < ' _ > ,
0 commit comments