@@ -246,6 +246,15 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>(
246246 }
247247}
248248
249+ /// A single layer of `&` peeled from an expression, used by
250+ /// [`TypeErrCtxt::peel_expr_refs`].
251+ struct PeeledRef < ' tcx > {
252+ /// The span covering the `&` (and any whitespace/mutability keyword) to remove.
253+ span : Span ,
254+ /// The type after peeling this layer (and all prior layers).
255+ peeled_ty : Ty < ' tcx > ,
256+ }
257+
249258impl < ' a , ' tcx > TypeErrCtxt < ' a , ' tcx > {
250259 pub fn note_field_shadowed_by_private_candidate_in_cause (
251260 & self ,
@@ -1882,6 +1891,85 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
18821891 ) ;
18831892 }
18841893
1894+ /// Peel `&`-borrows from an expression, following through untyped let-bindings.
1895+ /// Returns a list of removable `&` layers (each with the span to remove and the
1896+ /// resulting type), plus an optional terminal [`hir::Param`] when the chain ends
1897+ /// at a function parameter (including async-fn desugared parameters).
1898+ fn peel_expr_refs (
1899+ & self ,
1900+ mut expr : & ' tcx hir:: Expr < ' tcx > ,
1901+ mut ty : Ty < ' tcx > ,
1902+ ) -> ( Vec < PeeledRef < ' tcx > > , Option < & ' tcx hir:: Param < ' tcx > > ) {
1903+ let mut refs = Vec :: new ( ) ;
1904+ ' outer: loop {
1905+ while let hir:: ExprKind :: AddrOf ( _, _, borrowed) = expr. kind {
1906+ let span =
1907+ if let Some ( borrowed_span) = borrowed. span . find_ancestor_inside ( expr. span ) {
1908+ expr. span . until ( borrowed_span)
1909+ } else {
1910+ break ' outer;
1911+ } ;
1912+
1913+ // Double check that the span actually corresponds to a borrow,
1914+ // rather than some macro garbage.
1915+ // The span may include leading parens from parenthesized expressions
1916+ // (e.g., `(&expr)` where HIR removes the Paren but keeps the span).
1917+ // In that case, trim the span to start at the `&`.
1918+ let span = match self . tcx . sess . source_map ( ) . span_to_snippet ( span) {
1919+ Ok ( ref snippet) if snippet. starts_with ( "&" ) => span,
1920+ Ok ( ref snippet) if let Some ( amp) = snippet. find ( '&' ) => {
1921+ span. with_lo ( span. lo ( ) + BytePos ( amp as u32 ) )
1922+ }
1923+ _ => break ' outer,
1924+ } ;
1925+
1926+ let ty:: Ref ( _, inner_ty, _) = ty. kind ( ) else {
1927+ break ' outer;
1928+ } ;
1929+ ty = * inner_ty;
1930+ refs. push ( PeeledRef { span, peeled_ty : ty } ) ;
1931+ expr = borrowed;
1932+ }
1933+ if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , path) ) = expr. kind
1934+ && let Res :: Local ( hir_id) = path. res
1935+ && let hir:: Node :: Pat ( binding) = self . tcx . hir_node ( hir_id)
1936+ {
1937+ match self . tcx . parent_hir_node ( binding. hir_id ) {
1938+ // Untyped let-binding: follow to its initializer.
1939+ hir:: Node :: LetStmt ( local)
1940+ if local. ty . is_none ( )
1941+ && let Some ( init) = local. init =>
1942+ {
1943+ expr = init;
1944+ continue ;
1945+ }
1946+ // Async fn desugared parameter: `let x = __arg0;` with AsyncFn source.
1947+ // Follow to the original parameter.
1948+ hir:: Node :: LetStmt ( local)
1949+ if matches ! ( local. source, hir:: LocalSource :: AsyncFn )
1950+ && let Some ( init) = local. init
1951+ && let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , arg_path) ) =
1952+ init. kind
1953+ && let Res :: Local ( arg_hir_id) = arg_path. res
1954+ && let hir:: Node :: Pat ( arg_binding) = self . tcx . hir_node ( arg_hir_id)
1955+ && let hir:: Node :: Param ( param) =
1956+ self . tcx . parent_hir_node ( arg_binding. hir_id ) =>
1957+ {
1958+ return ( refs, Some ( param) ) ;
1959+ }
1960+ // Direct parameter reference.
1961+ hir:: Node :: Param ( param) => {
1962+ return ( refs, Some ( param) ) ;
1963+ }
1964+ _ => break ' outer,
1965+ }
1966+ } else {
1967+ break ' outer;
1968+ }
1969+ }
1970+ ( refs, None )
1971+ }
1972+
18851973 /// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`,
18861974 /// suggest removing these references until we reach a type that implements the trait.
18871975 pub ( super ) fn suggest_remove_reference (
@@ -1958,53 +2046,40 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
19582046 }
19592047
19602048 // Maybe suggest removal of borrows from expressions, like in `for i in &&&foo {}`.
1961- let Some ( mut expr) = expr_finder. result else {
2049+ let Some ( expr) = expr_finder. result else {
19622050 return false ;
19632051 } ;
1964- let mut count = 0 ;
1965- let mut suggestions = vec ! [ ] ;
19662052 // Skipping binder here, remapping below
1967- let mut suggested_ty = trait_pred. self_ty ( ) . skip_binder ( ) ;
1968- ' outer: loop {
1969- while let hir:: ExprKind :: AddrOf ( _, _, borrowed) = expr. kind {
1970- count += 1 ;
1971- let span =
1972- if let Some ( borrowed_span) = borrowed. span . find_ancestor_inside ( expr. span ) {
1973- expr. span . until ( borrowed_span)
1974- } else {
1975- break ' outer;
1976- } ;
1977-
1978- // Double check that the span we extracted actually corresponds to a borrow,
1979- // rather than some macro garbage.
1980- match self . tcx . sess . source_map ( ) . span_to_snippet ( span) {
1981- Ok ( snippet) if snippet. starts_with ( "&" ) => { }
1982- _ => break ' outer,
1983- }
1984-
1985- suggestions. push ( ( span, String :: new ( ) ) ) ;
1986-
1987- let ty:: Ref ( _, inner_ty, _) = suggested_ty. kind ( ) else {
1988- break ' outer;
1989- } ;
1990- suggested_ty = * inner_ty;
1991-
1992- expr = borrowed;
2053+ let suggested_ty = trait_pred. self_ty ( ) . skip_binder ( ) ;
2054+ let ( peeled_refs, _) = self . peel_expr_refs ( expr, suggested_ty) ;
2055+ for ( i, peeled) in peeled_refs. iter ( ) . enumerate ( ) {
2056+ let suggestions: Vec < _ > =
2057+ peeled_refs[ ..=i] . iter ( ) . map ( |r| ( r. span , String :: new ( ) ) ) . collect ( ) ;
2058+ if maybe_suggest ( peeled. peeled_ty , i + 1 , suggestions) {
2059+ return true ;
2060+ }
2061+ }
2062+ false
2063+ }
19932064
1994- if maybe_suggest ( suggested_ty, count, suggestions. clone ( ) ) {
2065+ /// Suggest removing `&` from a function parameter type like `&impl Future`.
2066+ fn suggest_remove_ref_from_param ( & self , param : & hir:: Param < ' _ > , err : & mut Diag < ' _ > ) -> bool {
2067+ if let Some ( decl) = self . tcx . parent_hir_node ( param. hir_id ) . fn_decl ( )
2068+ && let Some ( input_ty) = decl. inputs . iter ( ) . find ( |t| param. ty_span . contains ( t. span ) )
2069+ && let hir:: TyKind :: Ref ( _, mut_ty) = input_ty. kind
2070+ {
2071+ let ref_span = input_ty. span . until ( mut_ty. ty . span ) ;
2072+ match self . tcx . sess . source_map ( ) . span_to_snippet ( ref_span) {
2073+ Ok ( snippet) if snippet. starts_with ( "&" ) => {
2074+ err. span_suggestion_verbose (
2075+ ref_span,
2076+ "consider removing the `&` from the parameter type" ,
2077+ "" ,
2078+ Applicability :: MaybeIncorrect ,
2079+ ) ;
19952080 return true ;
19962081 }
1997- }
1998- if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , path) ) = expr. kind
1999- && let Res :: Local ( hir_id) = path. res
2000- && let hir:: Node :: Pat ( binding) = self . tcx . hir_node ( hir_id)
2001- && let hir:: Node :: LetStmt ( local) = self . tcx . parent_hir_node ( binding. hir_id )
2002- && let None = local. ty
2003- && let Some ( binding_expr) = local. init
2004- {
2005- expr = binding_expr;
2006- } else {
2007- break ' outer;
2082+ _ => { }
20082083 }
20092084 }
20102085 false
@@ -2023,6 +2098,89 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
20232098 // could also check if it is an fn call (very likely) and suggest changing *that*, if
20242099 // it is from the local crate.
20252100
2101+ // If the type is `&..&T` where `T: Future`, suggest removing `&`
2102+ // instead of removing `.await`.
2103+ if let ty:: PredicateKind :: Clause ( ty:: ClauseKind :: Trait ( pred) ) =
2104+ obligation. predicate . kind ( ) . skip_binder ( )
2105+ {
2106+ let self_ty = pred. self_ty ( ) ;
2107+ let future_trait =
2108+ self . tcx . require_lang_item ( LangItem :: Future , obligation. cause . span ) ;
2109+
2110+ // Peel through references to check if there's a Future underneath.
2111+ let has_future = {
2112+ let mut ty = self_ty;
2113+ loop {
2114+ match * ty. kind ( ) {
2115+ ty:: Ref ( _, inner_ty, _)
2116+ if !matches ! ( inner_ty. kind( ) , ty:: Dynamic ( ..) ) =>
2117+ {
2118+ if self
2119+ . type_implements_trait (
2120+ future_trait,
2121+ [ inner_ty] ,
2122+ obligation. param_env ,
2123+ )
2124+ . must_apply_modulo_regions ( )
2125+ {
2126+ break true ;
2127+ }
2128+ ty = inner_ty;
2129+ }
2130+ _ => break false ,
2131+ }
2132+ }
2133+ } ;
2134+
2135+ if has_future {
2136+ let ( peeled_refs, terminal_param) = self . peel_expr_refs ( expr, self_ty) ;
2137+
2138+ // Try removing `&`s from the expression.
2139+ for ( i, peeled) in peeled_refs. iter ( ) . enumerate ( ) {
2140+ if self
2141+ . type_implements_trait (
2142+ future_trait,
2143+ [ peeled. peeled_ty ] ,
2144+ obligation. param_env ,
2145+ )
2146+ . must_apply_modulo_regions ( )
2147+ {
2148+ let count = i + 1 ;
2149+ let msg = if count == 1 {
2150+ "consider removing the leading `&`-reference" . to_string ( )
2151+ } else {
2152+ format ! ( "consider removing {count} leading `&`-references" )
2153+ } ;
2154+ let suggestions: Vec < _ > =
2155+ peeled_refs[ ..=i] . iter ( ) . map ( |r| ( r. span , String :: new ( ) ) ) . collect ( ) ;
2156+ err. multipart_suggestion (
2157+ msg,
2158+ suggestions,
2159+ Applicability :: MachineApplicable ,
2160+ ) ;
2161+ return ;
2162+ }
2163+ }
2164+
2165+ // Try removing `&` from the parameter type, but only when there's
2166+ // no `&` in the expression itself (otherwise removing from the param
2167+ // alone wouldn't fix the error).
2168+ if peeled_refs. is_empty ( )
2169+ && let Some ( param) = terminal_param
2170+ && self . suggest_remove_ref_from_param ( param, err)
2171+ {
2172+ return ;
2173+ }
2174+
2175+ // Fallback: emit a help message when we can't provide a specific span.
2176+ err. help (
2177+ "a reference to a future is not a future; \
2178+ consider removing the leading `&`-reference",
2179+ ) ;
2180+ return ;
2181+ }
2182+ }
2183+
20262184 // use nth(1) to skip one layer of desugaring from `IntoIter::into_iter`
20272185 if let Some ( ( _, hir:: Node :: Expr ( await_expr) ) ) = self . tcx . hir_parent_iter ( * hir_id) . nth ( 1 )
20282186 && let Some ( expr_span) = expr. span . find_ancestor_inside_same_ctxt ( await_expr. span )
0 commit comments