]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_typeck/src/check/generator_interior.rs
must_not_suspend impl
[rust.git] / compiler / rustc_typeck / src / check / generator_interior.rs
1 //! This calculates the types which has storage which lives across a suspension point in a
2 //! generator from the perspective of typeck. The actual types used at runtime
3 //! is calculated in `rustc_mir::transform::generator` and may be a subset of the
4 //! types computed here.
5
6 use super::FnCtxt;
7 use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
8 use rustc_errors::pluralize;
9 use rustc_hir as hir;
10 use rustc_hir::def::{CtorKind, DefKind, Res};
11 use rustc_hir::def_id::DefId;
12 use rustc_hir::hir_id::HirIdSet;
13 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
14 use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind};
15 use rustc_middle::middle::region::{self, YieldData};
16 use rustc_middle::ty::{self, Ty, TyCtxt};
17 use rustc_span::symbol::sym;
18 use rustc_span::Span;
19 use smallvec::SmallVec;
20 use tracing::debug;
21
22 struct InteriorVisitor<'a, 'tcx> {
23     fcx: &'a FnCtxt<'a, 'tcx>,
24     types: FxIndexSet<ty::GeneratorInteriorTypeCause<'tcx>>,
25     region_scope_tree: &'tcx region::ScopeTree,
26     expr_count: usize,
27     kind: hir::GeneratorKind,
28     prev_unresolved_span: Option<Span>,
29     /// Match arm guards have temporary borrows from the pattern bindings.
30     /// In case there is a yield point in a guard with a reference to such bindings,
31     /// such borrows can span across this yield point.
32     /// As such, we need to track these borrows and record them despite of the fact
33     /// that they may succeed the said yield point in the post-order.
34     guard_bindings: SmallVec<[SmallVec<[HirId; 4]>; 1]>,
35     guard_bindings_set: HirIdSet,
36 }
37
38 impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
39     fn record(
40         &mut self,
41         ty: Ty<'tcx>,
42         hir_id: HirId,
43         scope: Option<region::Scope>,
44         expr: Option<&'tcx Expr<'tcx>>,
45         source_span: Span,
46         guard_borrowing_from_pattern: bool,
47     ) {
48         use rustc_span::DUMMY_SP;
49
50         debug!(
51             "generator_interior: attempting to record type {:?} {:?} {:?} {:?}",
52             ty, scope, expr, source_span
53         );
54
55         let live_across_yield = scope
56             .map(|s| {
57                 self.region_scope_tree.yield_in_scope(s).and_then(|yield_data| {
58                     // If we are recording an expression that is the last yield
59                     // in the scope, or that has a postorder CFG index larger
60                     // than the one of all of the yields, then its value can't
61                     // be storage-live (and therefore live) at any of the yields.
62                     //
63                     // See the mega-comment at `yield_in_scope` for a proof.
64
65                     debug!(
66                         "comparing counts yield: {} self: {}, source_span = {:?}",
67                         yield_data.expr_and_pat_count, self.expr_count, source_span
68                     );
69
70                     // If it is a borrowing happening in the guard,
71                     // it needs to be recorded regardless because they
72                     // do live across this yield point.
73                     if guard_borrowing_from_pattern
74                         || yield_data.expr_and_pat_count >= self.expr_count
75                     {
76                         Some(yield_data)
77                     } else {
78                         None
79                     }
80                 })
81             })
82             .unwrap_or_else(|| {
83                 Some(YieldData { span: DUMMY_SP, expr_and_pat_count: 0, source: self.kind.into() })
84             });
85
86         if let Some(yield_data) = live_across_yield {
87             let ty = self.fcx.resolve_vars_if_possible(ty);
88             debug!(
89                 "type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}",
90                 expr, scope, ty, self.expr_count, yield_data.span
91             );
92
93             if let Some((unresolved_type, unresolved_type_span)) =
94                 self.fcx.unresolved_type_vars(&ty)
95             {
96                 // If unresolved type isn't a ty_var then unresolved_type_span is None
97                 let span = self
98                     .prev_unresolved_span
99                     .unwrap_or_else(|| unresolved_type_span.unwrap_or(source_span));
100
101                 // If we encounter an int/float variable, then inference fallback didn't
102                 // finish due to some other error. Don't emit spurious additional errors.
103                 if let ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(_)) =
104                     unresolved_type.kind()
105                 {
106                     self.fcx
107                         .tcx
108                         .sess
109                         .delay_span_bug(span, &format!("Encountered var {:?}", unresolved_type));
110                 } else {
111                     let note = format!(
112                         "the type is part of the {} because of this {}",
113                         self.kind, yield_data.source
114                     );
115
116                     self.fcx
117                         .need_type_info_err_in_generator(self.kind, span, unresolved_type)
118                         .span_note(yield_data.span, &*note)
119                         .emit();
120                 }
121             } else {
122                 // Insert the type into the ordered set.
123                 let scope_span = scope.map(|s| s.span(self.fcx.tcx, self.region_scope_tree));
124
125                 check_must_not_suspend_ty(
126                     self.fcx,
127                     ty::ParamEnv::empty(),
128                     ty,
129                     hir_id,
130                     expr,
131                     source_span,
132                     yield_data.span,
133                     "",
134                     "",
135                     1,
136                 );
137
138                 self.types.insert(ty::GeneratorInteriorTypeCause {
139                     span: source_span,
140                     ty: &ty,
141                     scope_span,
142                     yield_span: yield_data.span,
143                     expr: expr.map(|e| e.hir_id),
144                 });
145             }
146         } else {
147             debug!(
148                 "no type in expr = {:?}, count = {:?}, span = {:?}",
149                 expr,
150                 self.expr_count,
151                 expr.map(|e| e.span)
152             );
153             let ty = self.fcx.resolve_vars_if_possible(ty);
154             if let Some((unresolved_type, unresolved_type_span)) =
155                 self.fcx.unresolved_type_vars(&ty)
156             {
157                 debug!(
158                     "remained unresolved_type = {:?}, unresolved_type_span: {:?}",
159                     unresolved_type, unresolved_type_span
160                 );
161                 self.prev_unresolved_span = unresolved_type_span;
162             }
163         }
164     }
165 }
166
167 pub fn resolve_interior<'a, 'tcx>(
168     fcx: &'a FnCtxt<'a, 'tcx>,
169     def_id: DefId,
170     body_id: hir::BodyId,
171     interior: Ty<'tcx>,
172     kind: hir::GeneratorKind,
173 ) {
174     let body = fcx.tcx.hir().body(body_id);
175     let mut visitor = InteriorVisitor {
176         fcx,
177         types: FxIndexSet::default(),
178         region_scope_tree: fcx.tcx.region_scope_tree(def_id),
179         expr_count: 0,
180         kind,
181         prev_unresolved_span: None,
182         guard_bindings: <_>::default(),
183         guard_bindings_set: <_>::default(),
184     };
185     intravisit::walk_body(&mut visitor, body);
186
187     // Check that we visited the same amount of expressions and the RegionResolutionVisitor
188     let region_expr_count = visitor.region_scope_tree.body_expr_count(body_id).unwrap();
189     assert_eq!(region_expr_count, visitor.expr_count);
190
191     // The types are already kept in insertion order.
192     let types = visitor.types;
193
194     // The types in the generator interior contain lifetimes local to the generator itself,
195     // which should not be exposed outside of the generator. Therefore, we replace these
196     // lifetimes with existentially-bound lifetimes, which reflect the exact value of the
197     // lifetimes not being known by users.
198     //
199     // These lifetimes are used in auto trait impl checking (for example,
200     // if a Sync generator contains an &'α T, we need to check whether &'α T: Sync),
201     // so knowledge of the exact relationships between them isn't particularly important.
202
203     debug!("types in generator {:?}, span = {:?}", types, body.value.span);
204
205     let mut counter = 0;
206     let mut captured_tys = FxHashSet::default();
207     let type_causes: Vec<_> = types
208         .into_iter()
209         .filter_map(|mut cause| {
210             // Erase regions and canonicalize late-bound regions to deduplicate as many types as we
211             // can.
212             let erased = fcx.tcx.erase_regions(cause.ty);
213             if captured_tys.insert(erased) {
214                 // Replace all regions inside the generator interior with late bound regions.
215                 // Note that each region slot in the types gets a new fresh late bound region,
216                 // which means that none of the regions inside relate to any other, even if
217                 // typeck had previously found constraints that would cause them to be related.
218                 let folded = fcx.tcx.fold_regions(erased, &mut false, |_, current_depth| {
219                     let br = ty::BoundRegion {
220                         var: ty::BoundVar::from_u32(counter),
221                         kind: ty::BrAnon(counter),
222                     };
223                     let r = fcx.tcx.mk_region(ty::ReLateBound(current_depth, br));
224                     counter += 1;
225                     r
226                 });
227
228                 cause.ty = folded;
229                 Some(cause)
230             } else {
231                 None
232             }
233         })
234         .collect();
235
236     // Extract type components to build the witness type.
237     let type_list = fcx.tcx.mk_type_list(type_causes.iter().map(|cause| cause.ty));
238     let bound_vars = fcx.tcx.mk_bound_variable_kinds(
239         (0..counter).map(|i| ty::BoundVariableKind::Region(ty::BrAnon(i))),
240     );
241     let witness =
242         fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars.clone()));
243
244     // Store the generator types and spans into the typeck results for this generator.
245     visitor.fcx.inh.typeck_results.borrow_mut().generator_interior_types =
246         ty::Binder::bind_with_vars(type_causes, bound_vars);
247
248     debug!(
249         "types in generator after region replacement {:?}, span = {:?}",
250         witness, body.value.span
251     );
252
253     // Unify the type variable inside the generator with the new witness
254     match fcx.at(&fcx.misc(body.value.span), fcx.param_env).eq(interior, witness) {
255         Ok(ok) => fcx.register_infer_ok_obligations(ok),
256         _ => bug!(),
257     }
258 }
259
260 // This visitor has to have the same visit_expr calls as RegionResolutionVisitor in
261 // librustc_middle/middle/region.rs since `expr_count` is compared against the results
262 // there.
263 impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
264     type Map = intravisit::ErasedMap<'tcx>;
265
266     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
267         NestedVisitorMap::None
268     }
269
270     fn visit_arm(&mut self, arm: &'tcx Arm<'tcx>) {
271         let Arm { guard, pat, body, .. } = arm;
272         self.visit_pat(pat);
273         if let Some(ref g) = guard {
274             self.guard_bindings.push(<_>::default());
275             ArmPatCollector {
276                 guard_bindings_set: &mut self.guard_bindings_set,
277                 guard_bindings: self
278                     .guard_bindings
279                     .last_mut()
280                     .expect("should have pushed at least one earlier"),
281             }
282             .visit_pat(pat);
283
284             match g {
285                 Guard::If(ref e) => {
286                     self.visit_expr(e);
287                 }
288                 Guard::IfLet(ref pat, ref e) => {
289                     self.visit_pat(pat);
290                     self.visit_expr(e);
291                 }
292             }
293
294             let mut scope_var_ids =
295                 self.guard_bindings.pop().expect("should have pushed at least one earlier");
296             for var_id in scope_var_ids.drain(..) {
297                 self.guard_bindings_set.remove(&var_id);
298             }
299         }
300         self.visit_expr(body);
301     }
302
303     fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
304         intravisit::walk_pat(self, pat);
305
306         self.expr_count += 1;
307
308         if let PatKind::Binding(..) = pat.kind {
309             let scope = self.region_scope_tree.var_scope(pat.hir_id.local_id);
310             let ty = self.fcx.typeck_results.borrow().pat_ty(pat);
311             self.record(ty, pat.hir_id, Some(scope), None, pat.span, false);
312         }
313     }
314
315     fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
316         let mut guard_borrowing_from_pattern = false;
317         match &expr.kind {
318             ExprKind::Call(callee, args) => match &callee.kind {
319                 ExprKind::Path(qpath) => {
320                     let res = self.fcx.typeck_results.borrow().qpath_res(qpath, callee.hir_id);
321                     match res {
322                         // Direct calls never need to keep the callee `ty::FnDef`
323                         // ZST in a temporary, so skip its type, just in case it
324                         // can significantly complicate the generator type.
325                         Res::Def(
326                             DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn),
327                             _,
328                         ) => {
329                             // NOTE(eddyb) this assumes a path expression has
330                             // no nested expressions to keep track of.
331                             self.expr_count += 1;
332
333                             // Record the rest of the call expression normally.
334                             for arg in *args {
335                                 self.visit_expr(arg);
336                             }
337                         }
338                         _ => intravisit::walk_expr(self, expr),
339                     }
340                 }
341                 _ => intravisit::walk_expr(self, expr),
342             },
343             ExprKind::Path(qpath) => {
344                 intravisit::walk_expr(self, expr);
345                 let res = self.fcx.typeck_results.borrow().qpath_res(qpath, expr.hir_id);
346                 match res {
347                     Res::Local(id) if self.guard_bindings_set.contains(&id) => {
348                         guard_borrowing_from_pattern = true;
349                     }
350                     _ => {}
351                 }
352             }
353             _ => intravisit::walk_expr(self, expr),
354         }
355
356         self.expr_count += 1;
357
358         let scope = self.region_scope_tree.temporary_scope(expr.hir_id.local_id);
359
360         // If there are adjustments, then record the final type --
361         // this is the actual value that is being produced.
362         if let Some(adjusted_ty) = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr) {
363             self.record(
364                 adjusted_ty,
365                 expr.hir_id,
366                 scope,
367                 Some(expr),
368                 expr.span,
369                 guard_borrowing_from_pattern,
370             );
371         }
372
373         // Also record the unadjusted type (which is the only type if
374         // there are no adjustments). The reason for this is that the
375         // unadjusted value is sometimes a "temporary" that would wind
376         // up in a MIR temporary.
377         //
378         // As an example, consider an expression like `vec![].push(x)`.
379         // Here, the `vec![]` would wind up MIR stored into a
380         // temporary variable `t` which we can borrow to invoke
381         // `<Vec<_>>::push(&mut t, x)`.
382         //
383         // Note that an expression can have many adjustments, and we
384         // are just ignoring those intermediate types. This is because
385         // those intermediate values are always linearly "consumed" by
386         // the other adjustments, and hence would never be directly
387         // captured in the MIR.
388         //
389         // (Note that this partly relies on the fact that the `Deref`
390         // traits always return references, which means their content
391         // can be reborrowed without needing to spill to a temporary.
392         // If this were not the case, then we could conceivably have
393         // to create intermediate temporaries.)
394         //
395         // The type table might not have information for this expression
396         // if it is in a malformed scope. (#66387)
397         if let Some(ty) = self.fcx.typeck_results.borrow().expr_ty_opt(expr) {
398             if guard_borrowing_from_pattern {
399                 // Match guards create references to all the bindings in the pattern that are used
400                 // in the guard, e.g. `y if is_even(y) => ...` becomes `is_even(*r_y)` where `r_y`
401                 // is a reference to `y`, so we must record a reference to the type of the binding.
402                 let tcx = self.fcx.tcx;
403                 let ref_ty = tcx.mk_ref(
404                     // Use `ReErased` as `resolve_interior` is going to replace all the regions anyway.
405                     tcx.mk_region(ty::RegionKind::ReErased),
406                     ty::TypeAndMut { ty, mutbl: hir::Mutability::Not },
407                 );
408                 self.record(
409                     ref_ty,
410                     expr.hir_id,
411                     scope,
412                     Some(expr),
413                     expr.span,
414                     guard_borrowing_from_pattern,
415                 );
416             }
417             self.record(
418                 ty,
419                 expr.hir_id,
420                 scope,
421                 Some(expr),
422                 expr.span,
423                 guard_borrowing_from_pattern,
424             );
425         } else {
426             self.fcx.tcx.sess.delay_span_bug(expr.span, "no type for node");
427         }
428     }
429 }
430
431 struct ArmPatCollector<'a> {
432     guard_bindings_set: &'a mut HirIdSet,
433     guard_bindings: &'a mut SmallVec<[HirId; 4]>,
434 }
435
436 impl<'a, 'tcx> Visitor<'tcx> for ArmPatCollector<'a> {
437     type Map = intravisit::ErasedMap<'tcx>;
438
439     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
440         NestedVisitorMap::None
441     }
442
443     fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
444         intravisit::walk_pat(self, pat);
445         if let PatKind::Binding(_, id, ..) = pat.kind {
446             self.guard_bindings.push(id);
447             self.guard_bindings_set.insert(id);
448         }
449     }
450 }
451
452 // Returns whether it emitted a diagnostic or not
453 // Note that this fn and the proceding one are based on the code
454 // for creating must_use diagnostics
455 pub fn check_must_not_suspend_ty<'tcx>(
456     fcx: &FnCtxt<'_, 'tcx>,
457     param_env: ty::ParamEnv<'tcx>,
458     ty: Ty<'tcx>,
459     hir_id: HirId,
460     expr: Option<&'tcx Expr<'tcx>>,
461     source_span: Span,
462     yield_span: Span,
463     descr_pre: &str,
464     descr_post: &str,
465     plural_len: usize,
466 ) -> bool {
467     if ty.is_unit()
468         || fcx.tcx.is_ty_uninhabited_from(fcx.tcx.parent_module(hir_id).to_def_id(), ty, param_env)
469     {
470         return true;
471     }
472
473     let plural_suffix = pluralize!(plural_len);
474
475     let emitted = match *ty.kind() {
476         ty::Adt(..) if ty.is_box() => {
477             let boxed_ty = ty.boxed_ty();
478             let descr_pre = &format!("{}boxed ", descr_pre);
479             check_must_not_suspend_ty(
480                 fcx,
481                 param_env,
482                 boxed_ty,
483                 hir_id,
484                 expr,
485                 source_span,
486                 yield_span,
487                 descr_pre,
488                 descr_post,
489                 plural_len,
490             )
491         }
492         ty::Adt(def, _) => check_must_not_suspend_def(
493             fcx.tcx,
494             def.did,
495             hir_id,
496             source_span,
497             yield_span,
498             descr_pre,
499             descr_post,
500         ),
501         ty::Opaque(def, _) => {
502             let mut has_emitted = false;
503             for &(predicate, _) in fcx.tcx.explicit_item_bounds(def) {
504                 // We only look at the `DefId`, so it is safe to skip the binder here.
505                 if let ty::PredicateKind::Trait(ref poly_trait_predicate) =
506                     predicate.kind().skip_binder()
507                 {
508                     let def_id = poly_trait_predicate.trait_ref.def_id;
509                     let descr_pre = &format!("{}implementer{} of ", descr_pre, plural_suffix,);
510                     if check_must_not_suspend_def(
511                         fcx.tcx,
512                         def_id,
513                         hir_id,
514                         source_span,
515                         yield_span,
516                         descr_pre,
517                         descr_post,
518                     ) {
519                         has_emitted = true;
520                         break;
521                     }
522                 }
523             }
524             has_emitted
525         }
526         ty::Dynamic(binder, _) => {
527             let mut has_emitted = false;
528             for predicate in binder.iter() {
529                 if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
530                     let def_id = trait_ref.def_id;
531                     let descr_post = &format!(" trait object{}{}", plural_suffix, descr_post,);
532                     if check_must_not_suspend_def(
533                         fcx.tcx,
534                         def_id,
535                         hir_id,
536                         source_span,
537                         yield_span,
538                         descr_pre,
539                         descr_post,
540                     ) {
541                         has_emitted = true;
542                         break;
543                     }
544                 }
545             }
546             has_emitted
547         }
548         ty::Tuple(ref tys) => {
549             let mut has_emitted = false;
550             /*
551             let spans = if let hir::ExprKind::Tup(comps) = &expr.kind {
552                 debug_assert_eq!(comps.len(), tys.len());
553                 comps.iter().map(|e| e.span).collect()
554             } else {
555                 vec![]
556             };
557             */
558             let spans = vec![];
559             for (i, ty) in tys.iter().map(|k| k.expect_ty()).enumerate() {
560                 let descr_post = &format!(" in tuple element {}", i);
561                 let span = *spans.get(i).unwrap_or(&source_span);
562                 if check_must_not_suspend_ty(
563                     fcx, param_env, ty, hir_id, expr, span, yield_span, descr_pre, descr_post,
564                     plural_len,
565                 ) {
566                     has_emitted = true;
567                 }
568             }
569             has_emitted
570         }
571         ty::Array(ty, len) => match len.try_eval_usize(fcx.tcx, param_env) {
572             // If the array is empty we don't lint, to avoid false positives
573             Some(0) | None => false,
574             // If the array is definitely non-empty, we can do `#[must_use]` checking.
575             Some(n) => {
576                 let descr_pre = &format!("{}array{} of ", descr_pre, plural_suffix,);
577                 check_must_not_suspend_ty(
578                     fcx,
579                     param_env,
580                     ty,
581                     hir_id,
582                     expr,
583                     source_span,
584                     yield_span,
585                     descr_pre,
586                     descr_post,
587                     n as usize + 1,
588                 )
589             }
590         },
591         _ => false,
592     };
593
594     // Don't move onto the "return value" path if we already sent a diagnostic
595     if emitted {
596         return true;
597     }
598
599     match expr {
600         Some(expr) => match expr.kind {
601             hir::ExprKind::Call(ref callee, _) => {
602                 match callee.kind {
603                     hir::ExprKind::Path(ref qpath) => {
604                         match fcx.typeck_results.borrow().qpath_res(qpath, callee.hir_id) {
605                             Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => {
606                                 check_must_not_suspend_def(
607                                     fcx.tcx,
608                                     def_id,
609                                     hir_id,
610                                     source_span,
611                                     yield_span,
612                                     "return value of ",
613                                     "",
614                                 )
615                             }
616
617                             // `Res::Local` if it was a closure, for which we
618                             // do not currently support must-not-suspend linting
619                             _ => false,
620                         }
621                     }
622                     _ => false,
623                 }
624             }
625             hir::ExprKind::MethodCall(..) => {
626                 if let Some(def_id) = fcx.typeck_results.borrow().type_dependent_def_id(expr.hir_id)
627                 {
628                     check_must_not_suspend_def(
629                         fcx.tcx,
630                         def_id,
631                         hir_id,
632                         source_span,
633                         yield_span,
634                         "return value of ",
635                         "",
636                     )
637                 } else {
638                     false
639                 }
640             }
641             _ => false,
642         },
643         None => false,
644     }
645 }
646
647 fn check_must_not_suspend_def(
648     tcx: TyCtxt<'_>,
649     def_id: DefId,
650     hir_id: HirId,
651     source_span: Span,
652     yield_span: Span,
653     descr_pre_path: &str,
654     descr_post_path: &str,
655 ) -> bool {
656     for attr in tcx.get_attrs(def_id).iter() {
657         if attr.has_name(sym::must_not_suspend) {
658             tcx.struct_span_lint_hir(
659                 rustc_session::lint::builtin::MUST_NOT_SUSPEND,
660                 hir_id,
661                 source_span,
662                 |lint| {
663                     let msg = format!(
664                         "{}`{}`{} held across a yield point, but should not be",
665                         descr_pre_path,
666                         tcx.def_path_str(def_id),
667                         descr_post_path
668                     );
669                     let mut err = lint.build(&msg);
670
671                     // Add optional reason note
672                     if let Some(note) = attr.value_str() {
673                         err.note(&note.as_str());
674                     }
675
676                     // add span pointing to the offending yield/await)
677                     err.span_label(yield_span, "The value is held across this yield point");
678
679                     // Add some quick suggestions on what to do
680                     err.span_help(
681                         source_span,
682                         "`drop` this value before the yield point, or use a block (`{ ... }`) \"
683                         to shrink its scope",
684                     );
685
686                     err.emit();
687                 },
688             );
689
690             /*
691             cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
692                 let msg = format!(
693                     "unused {}`{}`{} that must be used",
694                     descr_pre_path,
695                     cx.tcx.def_path_str(def_id),
696                     descr_post_path
697                 );
698                 let mut err = lint.build(&msg);
699                 // check for #[must_use = "..."]
700                 if let Some(note) = attr.value_str() {
701                     err.note(&note.as_str());
702                 }
703                 err.emit();
704             });
705             */
706             return true;
707         }
708     }
709     false
710 }