@@ -18,7 +18,7 @@ use rustc::infer::InferCtxt;
18
18
use rustc:: ty:: { self , ParamEnv , TyCtxt } ;
19
19
use rustc:: ty:: query:: Providers ;
20
20
use rustc:: lint:: builtin:: UNUSED_MUT ;
21
- use rustc:: mir:: { AggregateKind , BasicBlock , BorrowCheckResult , BorrowKind } ;
21
+ use rustc:: mir:: { self , AggregateKind , BasicBlock , BorrowCheckResult , BorrowKind } ;
22
22
use rustc:: mir:: { ClearCrossCrate , Local , Location , Place , Mir , Mutability , Operand } ;
23
23
use rustc:: mir:: { Projection , ProjectionElem , Rvalue , Field , Statement , StatementKind } ;
24
24
use rustc:: mir:: { Terminator , TerminatorKind } ;
@@ -1658,36 +1658,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1658
1658
}
1659
1659
}
1660
1660
1661
- fn specialized_description ( & self , place : & Place < ' tcx > ) -> Option < String > {
1662
- if let Some ( _name) = self . describe_place ( place) {
1663
- Some ( format ! ( "data in a `&` reference" ) )
1664
- } else {
1665
- None
1666
- }
1667
- }
1668
-
1669
- fn get_default_err_msg ( & self , place : & Place < ' tcx > ) -> String {
1670
- match self . describe_place ( place) {
1671
- Some ( name) => format ! ( "immutable item `{}`" , name) ,
1672
- None => "immutable item" . to_owned ( ) ,
1673
- }
1674
- }
1675
-
1676
- fn get_secondary_err_msg ( & self , place : & Place < ' tcx > ) -> String {
1677
- match self . specialized_description ( place) {
1678
- Some ( _) => format ! ( "data in a `&` reference" ) ,
1679
- None => self . get_default_err_msg ( place)
1680
- }
1681
- }
1682
-
1683
- fn get_primary_err_msg ( & self , place : & Place < ' tcx > ) -> String {
1684
- if let Some ( name) = self . describe_place ( place) {
1685
- format ! ( "`{}` is a `&` reference, so the data it refers to cannot be written" , name)
1686
- } else {
1687
- format ! ( "cannot assign through `&`-reference" )
1688
- }
1689
- }
1690
-
1691
1661
/// Check the permissions for the given place and read or write kind
1692
1662
///
1693
1663
/// Returns true if an error is reported, false otherwise.
@@ -1703,6 +1673,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1703
1673
place, kind, is_local_mutation_allowed
1704
1674
) ;
1705
1675
1676
+ #[ derive( Copy , Clone , Debug ) ]
1677
+ enum AccessKind {
1678
+ MutableBorrow ,
1679
+ Mutate ,
1680
+ }
1681
+ let error_access;
1682
+ let the_place_err;
1683
+
1706
1684
match kind {
1707
1685
Reservation ( WriteKind :: MutableBorrow ( borrow_kind @ BorrowKind :: Unique ) )
1708
1686
| Reservation ( WriteKind :: MutableBorrow ( borrow_kind @ BorrowKind :: Mut { .. } ) )
@@ -1720,20 +1698,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1720
1698
return false ;
1721
1699
}
1722
1700
Err ( place_err) => {
1723
- let item_msg = self . get_default_err_msg ( place) ;
1724
- let mut err = self . tcx
1725
- . cannot_borrow_path_as_mutable ( span, & item_msg, Origin :: Mir ) ;
1726
- err. span_label ( span, "cannot borrow as mutable" ) ;
1727
-
1728
- if place != place_err {
1729
- if let Some ( name) = self . describe_place ( place_err) {
1730
- err. note ( & format ! ( "the value which is causing this path not to be \
1731
- mutable is...: `{}`", name) ) ;
1732
- }
1733
- }
1734
-
1735
- err. emit ( ) ;
1736
- return true ;
1701
+ error_access = AccessKind :: MutableBorrow ;
1702
+ the_place_err = place_err;
1737
1703
}
1738
1704
}
1739
1705
}
@@ -1744,64 +1710,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1744
1710
return false ;
1745
1711
}
1746
1712
Err ( place_err) => {
1747
- let err_info = if let Place :: Projection (
1748
- box Projection {
1749
- base : Place :: Local ( local) ,
1750
- elem : ProjectionElem :: Deref
1751
- }
1752
- ) = * place_err {
1753
- let locations = self . mir . find_assignments ( local) ;
1754
- if locations. len ( ) > 0 {
1755
- let item_msg = self . get_secondary_err_msg ( & Place :: Local ( local) ) ;
1756
- let sp = self . mir . source_info ( locations[ 0 ] ) . span ;
1757
- let mut to_suggest_span = String :: new ( ) ;
1758
- if let Ok ( src) =
1759
- self . tcx . sess . codemap ( ) . span_to_snippet ( sp) {
1760
- to_suggest_span = src[ 1 ..] . to_string ( ) ;
1761
- } ;
1762
- Some ( ( sp,
1763
- "consider changing this to be a \
1764
- mutable reference",
1765
- to_suggest_span,
1766
- item_msg,
1767
- self . get_primary_err_msg ( & Place :: Local ( local) ) ) )
1768
- } else {
1769
- None
1770
- }
1771
- } else {
1772
- None
1773
- } ;
1774
-
1775
- if let Some ( ( err_help_span,
1776
- err_help_stmt,
1777
- to_suggest_span,
1778
- item_msg,
1779
- sec_span) ) = err_info {
1780
- let mut err = self . tcx . cannot_assign ( span, & item_msg, Origin :: Mir ) ;
1781
- err. span_suggestion ( err_help_span,
1782
- err_help_stmt,
1783
- format ! ( "&mut {}" , to_suggest_span) ) ;
1784
- if place != place_err {
1785
- err. span_label ( span, sec_span) ;
1786
- }
1787
- err. emit ( )
1788
- } else {
1789
- let item_msg = self . get_default_err_msg ( place) ;
1790
- let mut err = self . tcx . cannot_assign ( span, & item_msg, Origin :: Mir ) ;
1791
- err. span_label ( span, "cannot mutate" ) ;
1792
- if place != place_err {
1793
- if let Some ( name) = self . describe_place ( place_err) {
1794
- err. note ( & format ! ( "the value which is causing this path not \
1795
- to be mutable is...: `{}`", name) ) ;
1796
- }
1797
- }
1798
- err. emit ( ) ;
1799
- }
1800
-
1801
- return true ;
1713
+ error_access = AccessKind :: Mutate ;
1714
+ the_place_err = place_err;
1802
1715
}
1803
1716
}
1804
1717
}
1718
+
1805
1719
Reservation ( WriteKind :: Move )
1806
1720
| Write ( WriteKind :: Move )
1807
1721
| Reservation ( WriteKind :: StorageDeadOrDrop )
@@ -1831,6 +1745,197 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1831
1745
return false ;
1832
1746
}
1833
1747
}
1748
+
1749
+ // at this point, we have set up the error reporting state.
1750
+
1751
+ let mut err;
1752
+ let item_msg = match self . describe_place ( place) {
1753
+ Some ( name) => format ! ( "immutable item `{}`" , name) ,
1754
+ None => "immutable item" . to_owned ( ) ,
1755
+ } ;
1756
+
1757
+ // `act` and `acted_on` are strings that let us abstract over
1758
+ // the verbs used in some diagnostic messages.
1759
+ let act; let acted_on;
1760
+
1761
+ match error_access {
1762
+ AccessKind :: Mutate => {
1763
+ let item_msg = match the_place_err {
1764
+ Place :: Projection ( box Projection {
1765
+ base : _,
1766
+ elem : ProjectionElem :: Deref }
1767
+ ) => match self . describe_place ( place) {
1768
+ Some ( description) =>
1769
+ format ! ( "`{}` which is behind a `&` reference" , description) ,
1770
+ None => format ! ( "data in a `&` reference" ) ,
1771
+ } ,
1772
+ _ => item_msg,
1773
+ } ;
1774
+ err = self . tcx . cannot_assign ( span, & item_msg, Origin :: Mir ) ;
1775
+ act = "assign" ; acted_on = "written" ;
1776
+ }
1777
+ AccessKind :: MutableBorrow => {
1778
+ err = self . tcx
1779
+ . cannot_borrow_path_as_mutable ( span, & item_msg, Origin :: Mir ) ;
1780
+ act = "borrow as mutable" ; acted_on = "borrowed as mutable" ;
1781
+ }
1782
+ }
1783
+
1784
+ match the_place_err {
1785
+ // We want to suggest users use `let mut` for local (user
1786
+ // variable) mutations...
1787
+ Place :: Local ( local) if local_can_be_made_mutable ( self . mir , * local) => {
1788
+ // ... but it doesn't make sense to suggest it on
1789
+ // variables that are `ref x`, `ref mut x`, `&self`,
1790
+ // or `&mut self` (such variables are simply not
1791
+ // mutable)..
1792
+ let local_decl = & self . mir . local_decls [ * local] ;
1793
+ assert_eq ! ( local_decl. mutability, Mutability :: Not ) ;
1794
+
1795
+ err. span_label ( span, format ! ( "cannot {ACT}" , ACT =act) ) ;
1796
+ err. span_suggestion ( local_decl. source_info . span ,
1797
+ "consider changing this to be mutable" ,
1798
+ format ! ( "mut {}" , local_decl. name. unwrap( ) ) ) ;
1799
+ }
1800
+
1801
+ // complete hack to approximate old AST-borrowck
1802
+ // diagnostic: if the span starts with a mutable borrow of
1803
+ // a local variable, then just suggest the user remove it.
1804
+ Place :: Local ( _) if {
1805
+ if let Ok ( snippet) = self . tcx . sess . codemap ( ) . span_to_snippet ( span) {
1806
+ snippet. starts_with ( "&mut " )
1807
+ } else {
1808
+ false
1809
+ }
1810
+ } => {
1811
+ err. span_label ( span, format ! ( "cannot {ACT}" , ACT =act) ) ;
1812
+ err. span_label ( span, "try removing `&mut` here" ) ;
1813
+ }
1814
+
1815
+ // We want to point out when a `&` can be readily replaced
1816
+ // with an `&mut`.
1817
+ //
1818
+ // FIXME: can this case be generalized to work for an
1819
+ // arbitrary base for the projection?
1820
+ Place :: Projection ( box Projection { base : Place :: Local ( local) ,
1821
+ elem : ProjectionElem :: Deref } )
1822
+ if local_is_nonref_binding ( self . mir , * local) =>
1823
+ {
1824
+ let ( err_help_span, suggested_code) =
1825
+ find_place_to_suggest_ampmut ( self . tcx , self . mir , * local) ;
1826
+ err. span_suggestion ( err_help_span,
1827
+ "consider changing this to be a mutable reference" ,
1828
+ suggested_code) ;
1829
+
1830
+ let local_decl = & self . mir . local_decls [ * local] ;
1831
+ if let Some ( name) = local_decl. name {
1832
+ err. span_label (
1833
+ span, format ! ( "`{NAME}` is a `&` reference, \
1834
+ so the data it refers to cannot be {ACTED_ON}",
1835
+ NAME =name, ACTED_ON =acted_on) ) ;
1836
+ } else {
1837
+ err. span_label ( span, format ! ( "cannot {ACT} through `&`-reference" , ACT =act) ) ;
1838
+ }
1839
+ }
1840
+
1841
+ _ => {
1842
+ err. span_label ( span, format ! ( "cannot {ACT}" , ACT =act) ) ;
1843
+ }
1844
+ }
1845
+
1846
+ err. emit ( ) ;
1847
+ return true ;
1848
+
1849
+ // Returns true if local is a binding that can itself be made
1850
+ // mutable via the addition of the `mut` keyword, namely
1851
+ // something like:
1852
+ // - `fn foo(x: Type) { ... }`,
1853
+ // - `let x = ...`,
1854
+ // - or `match ... { C(x) => ... }`
1855
+ fn local_can_be_made_mutable ( mir : & Mir , local : mir:: Local ) -> bool
1856
+ {
1857
+ let local = & mir. local_decls [ local] ;
1858
+ match local. is_user_variable {
1859
+ Some ( ClearCrossCrate :: Set ( mir:: BindingForm :: Var ( mir:: VarBindingForm {
1860
+ binding_mode : ty:: BindingMode :: BindByValue ( _) ,
1861
+ opt_ty_info : _,
1862
+ } ) ) ) => true ,
1863
+
1864
+ _ => false ,
1865
+ }
1866
+ }
1867
+
1868
+ // Returns true if local is definitely not a `ref ident` or
1869
+ // `ref mut ident` binding. (Such bindings cannot be made into
1870
+ // mutable bindings.)
1871
+ fn local_is_nonref_binding ( mir : & Mir , local : mir:: Local ) -> bool
1872
+ {
1873
+ let local = & mir. local_decls [ local] ;
1874
+ match local. is_user_variable {
1875
+ Some ( ClearCrossCrate :: Set ( mir:: BindingForm :: Var ( mir:: VarBindingForm {
1876
+ binding_mode : ty:: BindingMode :: BindByValue ( _) ,
1877
+ opt_ty_info : _,
1878
+ } ) ) ) => true ,
1879
+
1880
+ Some ( ClearCrossCrate :: Set ( mir:: BindingForm :: ImplicitSelf ) ) => true ,
1881
+
1882
+ _ => false ,
1883
+ }
1884
+ }
1885
+
1886
+ // Returns the span to highlight and the associated text to
1887
+ // present when suggesting that the user use an `&mut`.
1888
+ //
1889
+ // When we want to suggest a user change a local variable to be a `&mut`, there
1890
+ // are three potential "obvious" things to highlight:
1891
+ //
1892
+ // let ident [: Type] [= RightHandSideExresssion];
1893
+ // ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
1894
+ // (1.) (2.) (3.)
1895
+ //
1896
+ // We can always fallback on highlighting the first. But chances are good that
1897
+ // the user experience will be better if we highlight one of the others if possible;
1898
+ // for example, if the RHS is present and the Type is not, then the type is going to
1899
+ // be inferred *from* the RHS, which means we should highlight that (and suggest
1900
+ // that they borrow the RHS mutably).
1901
+ fn find_place_to_suggest_ampmut < ' cx , ' gcx , ' tcx > ( tcx : TyCtxt < ' cx , ' gcx , ' tcx > ,
1902
+ mir : & Mir < ' tcx > ,
1903
+ local : Local ) -> ( Span , String )
1904
+ {
1905
+ // This implementation attempts to emulate AST-borrowck prioritization
1906
+ // by trying (3.), then (2.) and finally falling back on (1.).
1907
+ let locations = mir. find_assignments ( local) ;
1908
+ if locations. len ( ) > 0 {
1909
+ let assignment_rhs_span = mir. source_info ( locations[ 0 ] ) . span ;
1910
+ let snippet = tcx. sess . codemap ( ) . span_to_snippet ( assignment_rhs_span) ;
1911
+ if let Ok ( src) = snippet {
1912
+ // pnkfelix inherited code; believes intention is
1913
+ // highlighted text will always be `&<expr>` and
1914
+ // thus can transform to `&mut` by slicing off
1915
+ // first ASCII character and prepending "&mut ".
1916
+ let borrowed_expr = src[ 1 ..] . to_string ( ) ;
1917
+ return ( assignment_rhs_span, format ! ( "&mut {}" , borrowed_expr) ) ;
1918
+ }
1919
+ }
1920
+
1921
+ let local_decl = & mir. local_decls [ local] ;
1922
+ let highlight_span = match local_decl. is_user_variable {
1923
+ // if this is a variable binding with an explicit type,
1924
+ // try to highlight that for the suggestion.
1925
+ Some ( ClearCrossCrate :: Set ( mir:: BindingForm :: Var ( mir:: VarBindingForm {
1926
+ opt_ty_info : Some ( ty_span) , .. } ) ) ) => ty_span,
1927
+
1928
+ Some ( ClearCrossCrate :: Clear ) => bug ! ( "saw cleared local state" ) ,
1929
+
1930
+ // otherwise, just highlight the span associated with
1931
+ // the (MIR) LocalDecl.
1932
+ _ => local_decl. source_info . span ,
1933
+ } ;
1934
+
1935
+ let ty_mut = local_decl. ty . builtin_deref ( true ) . unwrap ( ) ;
1936
+ assert_eq ! ( ty_mut. mutbl, hir:: MutImmutable ) ;
1937
+ return ( highlight_span, format ! ( "&mut {}" , ty_mut. ty) ) ;
1938
+ }
1834
1939
}
1835
1940
1836
1941
/// Adds the place into the used mutable variables set
0 commit comments