@@ -243,6 +243,15 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>(
243243 }
244244}
245245
246+ /// A single layer of `&` peeled from an expression, used by
247+ /// [`TypeErrCtxt::peel_expr_refs`].
248+ struct PeeledRef < ' tcx > {
249+ /// The span covering the `&` (and any whitespace/mutability keyword) to remove.
250+ span : Span ,
251+ /// The type after peeling this layer (and all prior layers).
252+ peeled_ty : Ty < ' tcx > ,
253+ }
254+
246255impl < ' a , ' tcx > TypeErrCtxt < ' a , ' tcx > {
247256 pub fn note_field_shadowed_by_private_candidate_in_cause (
248257 & self ,
@@ -1818,6 +1827,85 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
18181827 ) ;
18191828 }
18201829
1830+ /// Peel `&`-borrows from an expression, following through untyped let-bindings.
1831+ /// Returns a list of removable `&` layers (each with the span to remove and the
1832+ /// resulting type), plus an optional terminal [`hir::Param`] when the chain ends
1833+ /// at a function parameter (including async-fn desugared parameters).
1834+ fn peel_expr_refs (
1835+ & self ,
1836+ mut expr : & ' tcx hir:: Expr < ' tcx > ,
1837+ mut ty : Ty < ' tcx > ,
1838+ ) -> ( Vec < PeeledRef < ' tcx > > , Option < & ' tcx hir:: Param < ' tcx > > ) {
1839+ let mut refs = Vec :: new ( ) ;
1840+ ' outer: loop {
1841+ while let hir:: ExprKind :: AddrOf ( _, _, borrowed) = expr. kind {
1842+ let span =
1843+ if let Some ( borrowed_span) = borrowed. span . find_ancestor_inside ( expr. span ) {
1844+ expr. span . until ( borrowed_span)
1845+ } else {
1846+ break ' outer;
1847+ } ;
1848+
1849+ // Double check that the span actually corresponds to a borrow,
1850+ // rather than some macro garbage.
1851+ // The span may include leading parens from parenthesized expressions
1852+ // (e.g., `(&expr)` where HIR removes the Paren but keeps the span).
1853+ // In that case, trim the span to start at the `&`.
1854+ let span = match self . tcx . sess . source_map ( ) . span_to_snippet ( span) {
1855+ Ok ( ref snippet) if snippet. starts_with ( "&" ) => span,
1856+ Ok ( ref snippet) if let Some ( amp) = snippet. find ( '&' ) => {
1857+ span. with_lo ( span. lo ( ) + BytePos ( amp as u32 ) )
1858+ }
1859+ _ => break ' outer,
1860+ } ;
1861+
1862+ let ty:: Ref ( _, inner_ty, _) = ty. kind ( ) else {
1863+ break ' outer;
1864+ } ;
1865+ ty = * inner_ty;
1866+ refs. push ( PeeledRef { span, peeled_ty : ty } ) ;
1867+ expr = borrowed;
1868+ }
1869+ if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , path) ) = expr. kind
1870+ && let Res :: Local ( hir_id) = path. res
1871+ && let hir:: Node :: Pat ( binding) = self . tcx . hir_node ( hir_id)
1872+ {
1873+ match self . tcx . parent_hir_node ( binding. hir_id ) {
1874+ // Untyped let-binding: follow to its initializer.
1875+ hir:: Node :: LetStmt ( local)
1876+ if local. ty . is_none ( )
1877+ && let Some ( init) = local. init =>
1878+ {
1879+ expr = init;
1880+ continue ;
1881+ }
1882+ // Async fn desugared parameter: `let x = __arg0;` with AsyncFn source.
1883+ // Follow to the original parameter.
1884+ hir:: Node :: LetStmt ( local)
1885+ if matches ! ( local. source, hir:: LocalSource :: AsyncFn )
1886+ && let Some ( init) = local. init
1887+ && let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , arg_path) ) =
1888+ init. kind
1889+ && let Res :: Local ( arg_hir_id) = arg_path. res
1890+ && let hir:: Node :: Pat ( arg_binding) = self . tcx . hir_node ( arg_hir_id)
1891+ && let hir:: Node :: Param ( param) =
1892+ self . tcx . parent_hir_node ( arg_binding. hir_id ) =>
1893+ {
1894+ return ( refs, Some ( param) ) ;
1895+ }
1896+ // Direct parameter reference.
1897+ hir:: Node :: Param ( param) => {
1898+ return ( refs, Some ( param) ) ;
1899+ }
1900+ _ => break ' outer,
1901+ }
1902+ } else {
1903+ break ' outer;
1904+ }
1905+ }
1906+ ( refs, None )
1907+ }
1908+
18211909 /// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`,
18221910 /// suggest removing these references until we reach a type that implements the trait.
18231911 pub ( super ) fn suggest_remove_reference (
@@ -1894,53 +1982,40 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
18941982 }
18951983
18961984 // Maybe suggest removal of borrows from expressions, like in `for i in &&&foo {}`.
1897- let Some ( mut expr) = expr_finder. result else {
1985+ let Some ( expr) = expr_finder. result else {
18981986 return false ;
18991987 } ;
1900- let mut count = 0 ;
1901- let mut suggestions = vec ! [ ] ;
19021988 // Skipping binder here, remapping below
1903- let mut suggested_ty = trait_pred. self_ty ( ) . skip_binder ( ) ;
1904- ' outer: loop {
1905- while let hir:: ExprKind :: AddrOf ( _, _, borrowed) = expr. kind {
1906- count += 1 ;
1907- let span =
1908- if let Some ( borrowed_span) = borrowed. span . find_ancestor_inside ( expr. span ) {
1909- expr. span . until ( borrowed_span)
1910- } else {
1911- break ' outer;
1912- } ;
1913-
1914- // Double check that the span we extracted actually corresponds to a borrow,
1915- // rather than some macro garbage.
1916- match self . tcx . sess . source_map ( ) . span_to_snippet ( span) {
1917- Ok ( snippet) if snippet. starts_with ( "&" ) => { }
1918- _ => break ' outer,
1919- }
1920-
1921- suggestions. push ( ( span, String :: new ( ) ) ) ;
1922-
1923- let ty:: Ref ( _, inner_ty, _) = suggested_ty. kind ( ) else {
1924- break ' outer;
1925- } ;
1926- suggested_ty = * inner_ty;
1927-
1928- expr = borrowed;
1989+ let suggested_ty = trait_pred. self_ty ( ) . skip_binder ( ) ;
1990+ let ( peeled_refs, _) = self . peel_expr_refs ( expr, suggested_ty) ;
1991+ for ( i, peeled) in peeled_refs. iter ( ) . enumerate ( ) {
1992+ let suggestions: Vec < _ > =
1993+ peeled_refs[ ..=i] . iter ( ) . map ( |r| ( r. span , String :: new ( ) ) ) . collect ( ) ;
1994+ if maybe_suggest ( peeled. peeled_ty , i + 1 , suggestions) {
1995+ return true ;
1996+ }
1997+ }
1998+ false
1999+ }
19292000
1930- if maybe_suggest ( suggested_ty, count, suggestions. clone ( ) ) {
2001+ /// Suggest removing `&` from a function parameter type like `&impl Future`.
2002+ fn suggest_remove_ref_from_param ( & self , param : & hir:: Param < ' _ > , err : & mut Diag < ' _ > ) -> bool {
2003+ if let Some ( decl) = self . tcx . parent_hir_node ( param. hir_id ) . fn_decl ( )
2004+ && let Some ( input_ty) = decl. inputs . iter ( ) . find ( |t| param. ty_span . contains ( t. span ) )
2005+ && let hir:: TyKind :: Ref ( _, mut_ty) = input_ty. kind
2006+ {
2007+ let ref_span = input_ty. span . until ( mut_ty. ty . span ) ;
2008+ match self . tcx . sess . source_map ( ) . span_to_snippet ( ref_span) {
2009+ Ok ( snippet) if snippet. starts_with ( "&" ) => {
2010+ err. span_suggestion_verbose (
2011+ ref_span,
2012+ "consider removing the `&` from the parameter type" ,
2013+ "" ,
2014+ Applicability :: MaybeIncorrect ,
2015+ ) ;
19312016 return true ;
19322017 }
1933- }
1934- if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , path) ) = expr. kind
1935- && let Res :: Local ( hir_id) = path. res
1936- && let hir:: Node :: Pat ( binding) = self . tcx . hir_node ( hir_id)
1937- && let hir:: Node :: LetStmt ( local) = self . tcx . parent_hir_node ( binding. hir_id )
1938- && let None = local. ty
1939- && let Some ( binding_expr) = local. init
1940- {
1941- expr = binding_expr;
1942- } else {
1943- break ' outer;
2018+ _ => { }
19442019 }
19452020 }
19462021 false
@@ -1959,6 +2034,89 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
19592034 // could also check if it is an fn call (very likely) and suggest changing *that*, if
19602035 // it is from the local crate.
19612036
2037+ // If the type is `&..&T` where `T: Future`, suggest removing `&`
2038+ // instead of removing `.await`.
2039+ if let ty:: PredicateKind :: Clause ( ty:: ClauseKind :: Trait ( pred) ) =
2040+ obligation. predicate . kind ( ) . skip_binder ( )
2041+ {
2042+ let self_ty = pred. self_ty ( ) ;
2043+ let future_trait =
2044+ self . tcx . require_lang_item ( LangItem :: Future , obligation. cause . span ) ;
2045+
2046+ // Peel through references to check if there's a Future underneath.
2047+ let has_future = {
2048+ let mut ty = self_ty;
2049+ loop {
2050+ match * ty. kind ( ) {
2051+ ty:: Ref ( _, inner_ty, _)
2052+ if !matches ! ( inner_ty. kind( ) , ty:: Dynamic ( ..) ) =>
2053+ {
2054+ if self
2055+ . type_implements_trait (
2056+ future_trait,
2057+ [ inner_ty] ,
2058+ obligation. param_env ,
2059+ )
2060+ . must_apply_modulo_regions ( )
2061+ {
2062+ break true ;
2063+ }
2064+ ty = inner_ty;
2065+ }
2066+ _ => break false ,
2067+ }
2068+ }
2069+ } ;
2070+
2071+ if has_future {
2072+ let ( peeled_refs, terminal_param) = self . peel_expr_refs ( expr, self_ty) ;
2073+
2074+ // Try removing `&`s from the expression.
2075+ for ( i, peeled) in peeled_refs. iter ( ) . enumerate ( ) {
2076+ if self
2077+ . type_implements_trait (
2078+ future_trait,
2079+ [ peeled. peeled_ty ] ,
2080+ obligation. param_env ,
2081+ )
2082+ . must_apply_modulo_regions ( )
2083+ {
2084+ let count = i + 1 ;
2085+ let msg = if count == 1 {
2086+ "consider removing the leading `&`-reference" . to_string ( )
2087+ } else {
2088+ format ! ( "consider removing {count} leading `&`-references" )
2089+ } ;
2090+ let suggestions: Vec < _ > =
2091+ peeled_refs[ ..=i] . iter ( ) . map ( |r| ( r. span , String :: new ( ) ) ) . collect ( ) ;
2092+ err. multipart_suggestion (
2093+ msg,
2094+ suggestions,
2095+ Applicability :: MachineApplicable ,
2096+ ) ;
2097+ return ;
2098+ }
2099+ }
2100+
2101+ // Try removing `&` from the parameter type, but only when there's
2102+ // no `&` in the expression itself (otherwise removing from the param
2103+ // alone wouldn't fix the error).
2104+ if peeled_refs. is_empty ( )
2105+ && let Some ( param) = terminal_param
2106+ && self . suggest_remove_ref_from_param ( param, err)
2107+ {
2108+ return ;
2109+ }
2110+
2111+ // Fallback: emit a help message when we can't provide a specific span.
2112+ err. help (
2113+ "a reference to a future is not a future; \
2114+ consider removing the leading `&`-reference",
2115+ ) ;
2116+ return ;
2117+ }
2118+ }
2119+
19622120 // use nth(1) to skip one layer of desugaring from `IntoIter::into_iter`
19632121 if let Some ( ( _, hir:: Node :: Expr ( await_expr) ) ) = self . tcx . hir_parent_iter ( * hir_id) . nth ( 1 )
19642122 && let Some ( expr_span) = expr. span . find_ancestor_inside_same_ctxt ( await_expr. span )
0 commit comments