@@ -18,7 +18,7 @@ use rustc::infer::InferCtxt;
1818use rustc:: ty:: { self , ParamEnv , TyCtxt } ;
1919use rustc:: ty:: query:: Providers ;
2020use rustc:: lint:: builtin:: UNUSED_MUT ;
21- use rustc:: mir:: { AggregateKind , BasicBlock , BorrowCheckResult , BorrowKind } ;
21+ use rustc:: mir:: { self , AggregateKind , BasicBlock , BorrowCheckResult , BorrowKind } ;
2222use rustc:: mir:: { ClearCrossCrate , Local , Location , Place , Mir , Mutability , Operand } ;
2323use rustc:: mir:: { Projection , ProjectionElem , Rvalue , Field , Statement , StatementKind } ;
2424use rustc:: mir:: { Terminator , TerminatorKind } ;
@@ -1658,36 +1658,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
16581658 }
16591659 }
16601660
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-
16911661 /// Check the permissions for the given place and read or write kind
16921662 ///
16931663 /// Returns true if an error is reported, false otherwise.
@@ -1703,6 +1673,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
17031673 place, kind, is_local_mutation_allowed
17041674 ) ;
17051675
1676+ #[ derive( Copy , Clone , Debug ) ]
1677+ enum AccessKind {
1678+ MutableBorrow ,
1679+ Mutate ,
1680+ }
1681+ let error_access;
1682+ let the_place_err;
1683+
17061684 match kind {
17071685 Reservation ( WriteKind :: MutableBorrow ( borrow_kind @ BorrowKind :: Unique ) )
17081686 | Reservation ( WriteKind :: MutableBorrow ( borrow_kind @ BorrowKind :: Mut { .. } ) )
@@ -1720,20 +1698,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
17201698 return false ;
17211699 }
17221700 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;
17371703 }
17381704 }
17391705 }
@@ -1744,64 +1710,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
17441710 return false ;
17451711 }
17461712 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;
18021715 }
18031716 }
18041717 }
1718+
18051719 Reservation ( WriteKind :: Move )
18061720 | Write ( WriteKind :: Move )
18071721 | Reservation ( WriteKind :: StorageDeadOrDrop )
@@ -1831,6 +1745,197 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
18311745 return false ;
18321746 }
18331747 }
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+ }
18341939 }
18351940
18361941 /// Adds the place into the used mutable variables set
0 commit comments