@@ -1697,6 +1697,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
16971697            struct_span_err ! ( self . tcx. sess,  ident. span,  E0603 ,  "{} `{}` is private" ,  descr,  ident) ; 
16981698        err. span_label ( ident. span ,  format ! ( "private {descr}" ) ) ; 
16991699
1700+         let  mut  not_publicly_reexported = false ; 
17001701        if  let  Some ( ( this_res,  outer_ident) )  = outermost_res { 
17011702            let  import_suggestions = self . lookup_import_candidates ( 
17021703                outer_ident, 
@@ -1717,6 +1718,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
17171718            ) ; 
17181719            // If we suggest importing a public re-export, don't point at the definition. 
17191720            if  point_to_def && ident. span  != outer_ident. span  { 
1721+                 not_publicly_reexported = true ; 
17201722                err. span_label ( 
17211723                    outer_ident. span , 
17221724                    format ! ( "{} `{outer_ident}` is not publicly re-exported" ,  this_res. descr( ) ) , 
@@ -1749,10 +1751,51 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
17491751            } 
17501752        } 
17511753
1754+         let  mut  sugg_paths = vec ! [ ] ; 
1755+         if  let  Some ( mut  def_id)  = res. opt_def_id ( )  { 
1756+             // We can't use `def_path_str` in resolve. 
1757+             let  mut  path = vec ! [ def_id] ; 
1758+             while  let  Some ( parent)  = self . tcx . opt_parent ( def_id)  { 
1759+                 def_id = parent; 
1760+                 if  !def_id. is_top_level_module ( )  { 
1761+                     path. push ( def_id) ; 
1762+                 }  else  { 
1763+                     break ; 
1764+                 } 
1765+             } 
1766+             // We will only suggest importing directly if it is accessible through that path. 
1767+             let  path_names:  Option < Vec < String > >  = path
1768+                 . iter ( ) 
1769+                 . rev ( ) 
1770+                 . map ( |def_id| { 
1771+                     self . tcx . opt_item_name ( * def_id) . map ( |n| { 
1772+                         if  def_id. is_top_level_module ( )  { 
1773+                             "crate" . to_string ( ) 
1774+                         }  else  { 
1775+                             n. to_string ( ) 
1776+                         } 
1777+                     } ) 
1778+                 } ) 
1779+                 . collect ( ) ; 
1780+             if  let  Some ( def_id)  = path. get ( 0 ) 
1781+                 && let  Some ( path)  = path_names
1782+             { 
1783+                 if  let  Some ( def_id)  = def_id. as_local ( )  { 
1784+                     if  self . effective_visibilities . is_directly_public ( def_id)  { 
1785+                         sugg_paths. push ( ( path,  false ) ) ; 
1786+                     } 
1787+                 }  else  if  self . is_accessible_from ( self . tcx . visibility ( def_id) ,  parent_scope. module ) 
1788+                 { 
1789+                     sugg_paths. push ( ( path,  false ) ) ; 
1790+                 } 
1791+             } 
1792+         } 
1793+ 
17521794        // Print the whole import chain to make it easier to see what happens. 
17531795        let  first_binding = binding; 
17541796        let  mut  next_binding = Some ( binding) ; 
17551797        let  mut  next_ident = ident; 
1798+         let  mut  path = vec ! [ ] ; 
17561799        while  let  Some ( binding)  = next_binding { 
17571800            let  name = next_ident; 
17581801            next_binding = match  binding. kind  { 
@@ -1771,6 +1814,21 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
17711814                _ => None , 
17721815            } ; 
17731816
1817+             match  binding. kind  { 
1818+                 NameBindingKind :: Import  {  import,  .. }  => { 
1819+                     for  segment in  import. module_path . iter ( ) . skip ( 1 )  { 
1820+                         path. push ( segment. ident . to_string ( ) ) ; 
1821+                     } 
1822+                     sugg_paths. push ( ( 
1823+                         path. iter ( ) 
1824+                             . cloned ( ) 
1825+                             . chain ( vec ! [ ident. to_string( ) ] . into_iter ( ) ) 
1826+                             . collect :: < Vec < _ > > ( ) , 
1827+                         true ,  // re-export 
1828+                     ) ) ; 
1829+                 } 
1830+                 NameBindingKind :: Res ( _)  | NameBindingKind :: Module ( _)  => { } 
1831+             } 
17741832            let  first = binding == first_binding; 
17751833            let  msg = format ! ( 
17761834                "{and_refers_to}the {item} `{name}`{which} is defined here{dots}" , 
@@ -1782,7 +1840,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
17821840            let  def_span = self . tcx . sess . source_map ( ) . guess_head_span ( binding. span ) ; 
17831841            let  mut  note_span = MultiSpan :: from_span ( def_span) ; 
17841842            if  !first && binding. vis . is_public ( )  { 
1785-                 note_span. push_span_label ( def_span,  "consider importing it directly" ) ; 
1843+                 let  desc = match  binding. kind  { 
1844+                     NameBindingKind :: Import  {  .. }  => "re-export" , 
1845+                     _ => "directly" , 
1846+                 } ; 
1847+                 note_span. push_span_label ( def_span,  format ! ( "you could import this {desc}" ) ) ; 
17861848            } 
17871849            // Final step in the import chain, point out if the ADT is `non_exhaustive` 
17881850            // which is probably why this privacy violation occurred. 
@@ -1796,6 +1858,29 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
17961858            } 
17971859            err. span_note ( note_span,  msg) ; 
17981860        } 
1861+         // We prioritize shorter paths, non-core imports and direct imports over the alternatives. 
1862+         sugg_paths. sort_by_key ( |( p,  reexport) | ( p. len ( ) ,  p[ 0 ]  == "core" ,  * reexport) ) ; 
1863+         for  ( sugg,  reexport)  in  sugg_paths { 
1864+             if  not_publicly_reexported { 
1865+                 break ; 
1866+             } 
1867+             if  sugg. len ( )  <= 1  { 
1868+                 // A single path segment suggestion is wrong. This happens on circular imports. 
1869+                 // `tests/ui/imports/issue-55884-2.rs` 
1870+                 continue ; 
1871+             } 
1872+             let  path = sugg. join ( "::" ) ; 
1873+             err. span_suggestion_verbose ( 
1874+                 dedup_span, 
1875+                 format ! ( 
1876+                     "import `{ident}` {}" , 
1877+                     if  reexport {  "through the re-export"  }  else {  "directly"  } 
1878+                 ) , 
1879+                 path, 
1880+                 Applicability :: MachineApplicable , 
1881+             ) ; 
1882+             break ; 
1883+         } 
17991884
18001885        err. emit ( ) ; 
18011886    } 
0 commit comments