@@ -1697,6 +1697,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
1697
1697
struct_span_err ! ( self . tcx. sess, ident. span, E0603 , "{} `{}` is private" , descr, ident) ;
1698
1698
err. span_label ( ident. span , format ! ( "private {descr}" ) ) ;
1699
1699
1700
+ let mut not_publicly_reexported = false ;
1700
1701
if let Some ( ( this_res, outer_ident) ) = outermost_res {
1701
1702
let import_suggestions = self . lookup_import_candidates (
1702
1703
outer_ident,
@@ -1717,6 +1718,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
1717
1718
) ;
1718
1719
// If we suggest importing a public re-export, don't point at the definition.
1719
1720
if point_to_def && ident. span != outer_ident. span {
1721
+ not_publicly_reexported = true ;
1720
1722
err. span_label (
1721
1723
outer_ident. span ,
1722
1724
format ! ( "{} `{outer_ident}` is not publicly re-exported" , this_res. descr( ) ) ,
@@ -1749,10 +1751,51 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
1749
1751
}
1750
1752
}
1751
1753
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
+
1752
1794
// Print the whole import chain to make it easier to see what happens.
1753
1795
let first_binding = binding;
1754
1796
let mut next_binding = Some ( binding) ;
1755
1797
let mut next_ident = ident;
1798
+ let mut path = vec ! [ ] ;
1756
1799
while let Some ( binding) = next_binding {
1757
1800
let name = next_ident;
1758
1801
next_binding = match binding. kind {
@@ -1771,6 +1814,21 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
1771
1814
_ => None ,
1772
1815
} ;
1773
1816
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
+ }
1774
1832
let first = binding == first_binding;
1775
1833
let msg = format ! (
1776
1834
"{and_refers_to}the {item} `{name}`{which} is defined here{dots}" ,
@@ -1782,7 +1840,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
1782
1840
let def_span = self . tcx . sess . source_map ( ) . guess_head_span ( binding. span ) ;
1783
1841
let mut note_span = MultiSpan :: from_span ( def_span) ;
1784
1842
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}" ) ) ;
1786
1848
}
1787
1849
// Final step in the import chain, point out if the ADT is `non_exhaustive`
1788
1850
// which is probably why this privacy violation occurred.
@@ -1796,6 +1858,29 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
1796
1858
}
1797
1859
err. span_note ( note_span, msg) ;
1798
1860
}
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
+ }
1799
1884
1800
1885
err. emit ( ) ;
1801
1886
}
0 commit comments