1
1
use crate :: FnCtxt ;
2
+ use rustc_data_structures:: fx:: FxHashMap ;
2
3
use rustc_hir as hir;
3
4
use rustc_hir:: intravisit:: { self , Visitor } ;
4
5
use rustc_hir:: PatKind ;
@@ -61,12 +62,12 @@ pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
61
62
// *distinct* cases. so track when we are hitting a pattern *within* an fn
62
63
// parameter.
63
64
outermost_fn_param_pat : Option < ( Span , hir:: HirId ) > ,
64
- let_binding_init : Option < Span > ,
65
+ pat_expr_map : FxHashMap < hir :: HirId , Span > ,
65
66
}
66
67
67
68
impl < ' a , ' tcx > GatherLocalsVisitor < ' a , ' tcx > {
68
69
pub ( super ) fn new ( fcx : & ' a FnCtxt < ' a , ' tcx > ) -> Self {
69
- Self { fcx, outermost_fn_param_pat : None , let_binding_init : None }
70
+ Self { fcx, outermost_fn_param_pat : None , pat_expr_map : Default :: default ( ) }
70
71
}
71
72
72
73
fn assign ( & mut self , span : Span , nid : hir:: HirId , ty_opt : Option < Ty < ' tcx > > ) -> Ty < ' tcx > {
@@ -155,14 +156,69 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
155
156
self . fcx. ty_to_string( * self . fcx. locals. borrow( ) . get( & decl. hir_id) . unwrap( ) )
156
157
) ;
157
158
}
159
+ }
160
+
161
+ /// Builds a correspondence mapping between the bindings in a pattern and the expression that
162
+ /// originates the value. This is then used on unsized locals errors to point at the sub expression
163
+ /// That corresponds to the sub-pattern with the `?Sized` type, instead of the binding.
164
+ ///
165
+ /// This is somewhat limited, as it only supports bindings, tuples and structs for now, falling back
166
+ /// on pointing at the binding otherwise.
167
+ struct JointVisitorExpr < ' hir > {
168
+ pat : & ' hir hir:: Pat < ' hir > ,
169
+ expr : & ' hir hir:: Expr < ' hir > ,
170
+ map : FxHashMap < hir:: HirId , Span > ,
171
+ }
158
172
159
- fn span_for_init_expr ( & self , expr : & ' tcx hir:: Expr < ' tcx > ) -> Span {
160
- // In other parts of the compiler, when emitting a bound error on a method call we point at
161
- // the method call path. We mimic that here so that error deduplication will work as
162
- // expected.
163
- match expr. kind {
164
- hir:: ExprKind :: MethodCall ( path, ..) => path. ident . span ,
165
- _ => expr. span ,
173
+ impl < ' hir > JointVisitorExpr < ' hir > {
174
+ fn walk ( & mut self ) {
175
+ match ( self . pat . kind , self . expr . kind ) {
176
+ ( hir:: PatKind :: Tuple ( pat_fields, pos) , hir:: ExprKind :: Tup ( expr_fields) )
177
+ if pat_fields. len ( ) == expr_fields. len ( ) && pos. as_opt_usize ( ) == None =>
178
+ {
179
+ for ( pat, expr) in pat_fields. iter ( ) . zip ( expr_fields. iter ( ) ) {
180
+ self . map . insert ( pat. hir_id , expr. span ) ;
181
+ let mut v = JointVisitorExpr { pat, expr, map : Default :: default ( ) } ;
182
+ v. walk ( ) ;
183
+ self . map . extend ( v. map ) ;
184
+ }
185
+ }
186
+ ( hir:: PatKind :: Binding ( ..) , hir:: ExprKind :: MethodCall ( path, ..) ) => {
187
+ self . map . insert ( self . pat . hir_id , path. ident . span ) ;
188
+ }
189
+ ( hir:: PatKind :: Binding ( ..) , _) => {
190
+ self . map . insert ( self . pat . hir_id , self . expr . span ) ;
191
+ }
192
+ (
193
+ hir:: PatKind :: Struct ( pat_path, pat_fields, _) ,
194
+ hir:: ExprKind :: Struct ( call_path, expr_fields, _) ,
195
+ ) if pat_path. res ( ) == call_path. res ( ) && pat_path. res ( ) . is_some ( ) => {
196
+ for ( pat_field, expr_field) in pat_fields. iter ( ) . zip ( expr_fields. iter ( ) ) {
197
+ self . map . insert ( pat_field. hir_id , expr_field. span ) ;
198
+ let mut v = JointVisitorExpr {
199
+ pat : pat_field. pat ,
200
+ expr : expr_field. expr ,
201
+ map : Default :: default ( ) ,
202
+ } ;
203
+ v. walk ( ) ;
204
+ self . map . extend ( v. map ) ;
205
+ }
206
+ }
207
+ (
208
+ hir:: PatKind :: TupleStruct ( pat_path, pat_fields, _) ,
209
+ hir:: ExprKind :: Call (
210
+ hir:: Expr { kind : hir:: ExprKind :: Path ( expr_path) , .. } ,
211
+ expr_fields,
212
+ ) ,
213
+ ) if pat_path. res ( ) == expr_path. res ( ) && pat_path. res ( ) . is_some ( ) => {
214
+ for ( pat, expr) in pat_fields. iter ( ) . zip ( expr_fields. iter ( ) ) {
215
+ self . map . insert ( pat. hir_id , expr. span ) ;
216
+ let mut v = JointVisitorExpr { pat : pat, expr : expr, map : Default :: default ( ) } ;
217
+ v. walk ( ) ;
218
+ self . map . extend ( v. map ) ;
219
+ }
220
+ }
221
+ _ => { }
166
222
}
167
223
}
168
224
}
@@ -171,20 +227,26 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
171
227
// Add explicitly-declared locals.
172
228
fn visit_local ( & mut self , local : & ' tcx hir:: LetStmt < ' tcx > ) {
173
229
self . declare ( local. into ( ) ) ;
174
- let sp = self . let_binding_init . take ( ) ;
175
- self . let_binding_init = local. init . map ( |e| self . span_for_init_expr ( e) ) ;
230
+ if let Some ( init) = local. init {
231
+ let mut v = JointVisitorExpr { pat : & local. pat , expr : & init, map : Default :: default ( ) } ;
232
+ v. walk ( ) ;
233
+ self . pat_expr_map . extend ( v. map ) ;
234
+ }
176
235
intravisit:: walk_local ( self , local) ;
177
- self . let_binding_init = sp;
178
236
}
179
237
180
238
fn visit_expr ( & mut self , expr : & ' tcx hir:: Expr < ' tcx > ) {
181
- let sp = self . let_binding_init . take ( ) ;
182
239
if let hir:: ExprKind :: Let ( let_expr) = expr. kind {
183
- self . let_binding_init = Some ( self . span_for_init_expr ( & let_expr. init ) ) ;
240
+ let mut v = JointVisitorExpr {
241
+ pat : & let_expr. pat ,
242
+ expr : & let_expr. init ,
243
+ map : Default :: default ( ) ,
244
+ } ;
245
+ v. walk ( ) ;
246
+ self . pat_expr_map . extend ( v. map ) ;
184
247
self . declare ( ( let_expr, expr. hir_id ) . into ( ) ) ;
185
248
}
186
249
intravisit:: walk_expr ( self , expr) ;
187
- self . let_binding_init = sp;
188
250
}
189
251
190
252
fn visit_param ( & mut self , param : & ' tcx hir:: Param < ' tcx > ) {
@@ -219,11 +281,8 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
219
281
}
220
282
} else {
221
283
if !self . fcx . tcx . features ( ) . unsized_locals {
222
- self . fcx . require_type_is_sized (
223
- var_ty,
224
- self . let_binding_init . unwrap_or ( p. span ) ,
225
- traits:: VariableType ( p. hir_id ) ,
226
- ) ;
284
+ let span = * self . pat_expr_map . get ( & p. hir_id ) . unwrap_or ( & p. span ) ;
285
+ self . fcx . require_type_is_sized ( var_ty, span, traits:: VariableType ( p. hir_id ) ) ;
227
286
}
228
287
}
229
288
@@ -234,11 +293,6 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
234
293
var_ty
235
294
) ;
236
295
}
237
- // We only point at the init expression if this is the top level pattern, otherwise we point
238
- // at the specific binding, because we might not be able to tie the binding to the,
239
- // expression, like `let (a, b) = foo();`. FIXME: We could specialize
240
- // `let (a, b) = (unsized(), bar());` to point at `unsized()` instead of `a`.
241
- self . let_binding_init . take ( ) ;
242
296
let old_outermost_fn_param_pat = self . outermost_fn_param_pat . take ( ) ;
243
297
intravisit:: walk_pat ( self , p) ;
244
298
self . outermost_fn_param_pat = old_outermost_fn_param_pat;
0 commit comments