]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #55432 - zackmdavis:single_life, r=nikomatsakis
authorbors <bors@rust-lang.org>
Sun, 4 Nov 2018 09:45:49 +0000 (09:45 +0000)
committerbors <bors@rust-lang.org>
Sun, 4 Nov 2018 09:45:49 +0000 (09:45 +0000)
single life

 * structured ~~autofixable~~ (well, pending #53934 and rust-lang-nursery/rustfix#141) suggestions for the single-use-lifetimes lint in the case of function and method reference args
 * don't consider the anonymous lifetime `'_` as "single-use" (it's intended for exactly this sort of thing)

![single_life](https://user-images.githubusercontent.com/1076988/47613227-3b2b6400-da48-11e8-8efd-cb975ddf537d.png)

r? @nikomatsakis

1  2 
src/librustc/middle/resolve_lifetime.rs

index a387765085a29c940362c952c719d12a8214f17f,31e34895052a3759872c3d1476104a5dc70c321e..79cd8b21f1b6aa7d7fd68499f4f8f604c4ca3553
@@@ -447,17 -447,6 +447,17 @@@ fn krate<'tcx>(tcx: TyCtxt<'_, 'tcx, 't
      map
  }
  
 +/// In traits, there is an implicit `Self` type parameter which comes before the generics.
 +/// We have to account for this when computing the index of the other generic parameters.
 +/// This function returns whether there is such an implicit parameter defined on the given item.
 +fn sub_items_have_self_param(node: &hir::ItemKind) -> bool {
 +    match *node {
 +        hir::ItemKind::Trait(..) |
 +        hir::ItemKind::TraitAlias(..) => true,
 +        _ => false,
 +    }
 +}
 +
  impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
      fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
          NestedVisitorMap::All(&self.tcx.hir)
                      hir::ItemKind::Impl(..) => true,
                      _ => false,
                  };
 -                // These kinds of items have only early bound lifetime parameters.
 -                let mut index = if let hir::ItemKind::Trait(..) = item.node {
 +                // These kinds of items have only early-bound lifetime parameters.
 +                let mut index = if sub_items_have_self_param(&item.node) {
                      1 // Self comes before lifetimes
                  } else {
                      0
@@@ -1454,23 -1443,101 +1454,101 @@@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx
      /// helper method to determine the span to remove when suggesting the
      /// deletion of a lifetime
      fn lifetime_deletion_span(&self, name: ast::Ident, generics: &hir::Generics) -> Option<Span> {
-         if generics.params.len() == 1 {
-             // if sole lifetime, remove the `<>` brackets
-             Some(generics.span)
-         } else {
-             generics.params.iter().enumerate().find_map(|(i, param)| {
-                 if param.name.ident() == name {
-                     // We also want to delete a leading or trailing comma
-                     // as appropriate
-                     if i >= generics.params.len() - 1 {
-                         Some(generics.params[i - 1].span.shrink_to_hi().to(param.span))
-                     } else {
-                         Some(param.span.to(generics.params[i + 1].span.shrink_to_lo()))
+         generics.params.iter().enumerate().find_map(|(i, param)| {
+             if param.name.ident() == name {
+                 let mut in_band = false;
+                 if let hir::GenericParamKind::Lifetime { kind } = param.kind {
+                     if let hir::LifetimeParamKind::InBand = kind {
+                         in_band = true;
                      }
+                 }
+                 if in_band {
+                     Some(param.span)
                  } else {
-                     None
+                     if generics.params.len() == 1 {
+                         // if sole lifetime, remove the entire `<>` brackets
+                         Some(generics.span)
+                     } else {
+                         // if removing within `<>` brackets, we also want to
+                         // delete a leading or trailing comma as appropriate
+                         if i >= generics.params.len() - 1 {
+                             Some(generics.params[i - 1].span.shrink_to_hi().to(param.span))
+                         } else {
+                             Some(param.span.to(generics.params[i + 1].span.shrink_to_lo()))
+                         }
+                     }
                  }
-             })
+             } else {
+                 None
+             }
+         })
+     }
+     // helper method to issue suggestions from `fn rah<'a>(&'a T)` to `fn rah(&T)`
+     fn suggest_eliding_single_use_lifetime(
+         &self, err: &mut DiagnosticBuilder<'_>, def_id: DefId, lifetime: &hir::Lifetime
+     ) {
+         // FIXME: future work: also suggest `impl Foo<'_>` for `impl<'a> Foo<'a>`
+         let name = lifetime.name.ident();
+         let mut remove_decl = None;
+         if let Some(parent_def_id) = self.tcx.parent(def_id) {
+             if let Some(generics) = self.tcx.hir.get_generics(parent_def_id) {
+                 remove_decl = self.lifetime_deletion_span(name, generics);
+             }
+         }
+         let mut remove_use = None;
+         let mut find_arg_use_span = |inputs: &hir::HirVec<hir::Ty>| {
+             for input in inputs {
+                 if let hir::TyKind::Rptr(lt, _) = input.node {
+                     if lt.name.ident() == name {
+                         // include the trailing whitespace between the ampersand and the type name
+                         let lt_through_ty_span = lifetime.span.to(input.span.shrink_to_hi());
+                         remove_use = Some(
+                             self.tcx.sess.source_map()
+                                 .span_until_non_whitespace(lt_through_ty_span)
+                         );
+                         break;
+                     }
+                 }
+             }
+         };
+         if let Node::Lifetime(hir_lifetime) = self.tcx.hir.get(lifetime.id) {
+             if let Some(parent) = self.tcx.hir.find(self.tcx.hir.get_parent(hir_lifetime.id)) {
+                 match parent {
+                     Node::Item(item) => {
+                         if let hir::ItemKind::Fn(decl, _, _, _) = &item.node {
+                             find_arg_use_span(&decl.inputs);
+                         }
+                     },
+                     Node::ImplItem(impl_item) => {
+                         if let hir::ImplItemKind::Method(sig, _) = &impl_item.node {
+                             find_arg_use_span(&sig.decl.inputs);
+                         }
+                     }
+                     _ => {}
+                 }
+             }
+         }
+         if let (Some(decl_span), Some(use_span)) = (remove_decl, remove_use) {
+             // if both declaration and use deletion spans start at the same
+             // place ("start at" because the latter includes trailing
+             // whitespace), then this is an in-band lifetime
+             if decl_span.shrink_to_lo() == use_span.shrink_to_lo() {
+                 err.span_suggestion_with_applicability(
+                     use_span,
+                     "elide the single-use lifetime",
+                     String::new(),
+                     Applicability::MachineApplicable,
+                 );
+             } else {
+                 err.multipart_suggestion_with_applicability(
+                     "elide the single-use lifetime",
+                     vec![(decl_span, String::new()), (use_span, String::new())],
+                     Applicability::MachineApplicable,
+                 );
+             }
          }
      }
  
                          _ => None,
                      } {
                          debug!("id = {:?} span = {:?} name = {:?}", node_id, span, name);
+                         if name == keywords::UnderscoreLifetime.ident() {
+                             continue;
+                         }
                          let mut err = self.tcx.struct_span_lint_node(
                              lint::builtin::SINGLE_USE_LIFETIMES,
                              id,
                              span,
                              &format!("lifetime parameter `{}` only used once", name),
                          );
-                         err.span_label(span, "this lifetime...");
-                         err.span_label(lifetime.span, "...is used only here");
+                         if span == lifetime.span {
+                             // spans are the same for in-band lifetime declarations
+                             err.span_label(span, "this lifetime is only used here");
+                         } else {
+                             err.span_label(span, "this lifetime...");
+                             err.span_label(lifetime.span, "...is used only here");
+                         }
+                         self.suggest_eliding_single_use_lifetime(&mut err, def_id, lifetime);
                          err.emit();
                      }
                  }
                                  if let Some(span) = unused_lt_span {
                                      err.span_suggestion_with_applicability(
                                          span,
-                                         "remove it",
+                                         "elide the unused lifetime",
                                          String::new(),
                                          Applicability::MachineApplicable,
                                      );
          let mut index = 0;
          if let Some(parent_id) = parent_id {
              let parent = self.tcx.hir.expect_item(parent_id);
 -            if let hir::ItemKind::Trait(..) = parent.node {
 -                index += 1; // Self comes first.
 +            if sub_items_have_self_param(&parent.node) {
 +                index += 1; // Self comes before lifetimes
              }
              match parent.node {
                  hir::ItemKind::Trait(_, _, ref generics, ..)