]> git.lizzy.rs Git - rust.git/commitdiff
Rollup merge of #48047 - etaoins:fix-ice-for-mismatched-args-on-target-without-span...
authorkennytm <kennytm@gmail.com>
Sat, 10 Feb 2018 06:23:58 +0000 (14:23 +0800)
committerGitHub <noreply@github.com>
Sat, 10 Feb 2018 06:23:58 +0000 (14:23 +0800)
Fix ICE for mismatched args on target without span

Commit 7ed00caacc improved our error reporting by including the target function in our error messages when there is an argument count mismatch. A simple example from the UI tests is:

```
error[E0593]: function is expected to take a single 2-tuple as argument, but it takes 0 arguments
  --> $DIR/closure-arg-count.rs:32:53
   |
32 |     let _it = vec![1, 2, 3].into_iter().enumerate().map(foo);
   |                                                     ^^^ expected function that takes a single 2-tuple as argument
...
44 | fn foo() {}
   | -------- takes 0 arguments
```

However, this assumed the target span was always available. This does not hold true if the target function is in `std` or another crate. A simple example from #48046 is assigning `str::split` to a function type with a different number of arguments.

Fix by omitting all of the labels and suggestions related to the target span when it's not found.

Fixes #48046

r? @estebank

1  2 
src/librustc/traits/error_reporting.rs

index d4bcf00be80896ad23fd7d327e92ab984984a9ea,f58ac9f00e4cce5aa5da31bf186de85339053a64..a290839425ebe2051947c32eeaf4ae0d23560a39
@@@ -348,7 -348,7 +348,7 @@@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx
          if direct {
              // this is a "direct", user-specified, rather than derived,
              // obligation.
 -            flags.push(("direct", None));
 +            flags.push(("direct".to_string(), None));
          }
  
          if let ObligationCauseCode::ItemObligation(item) = obligation.cause.code {
              // Currently I'm leaving it for what I need for `try`.
              if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) {
                  method = self.tcx.item_name(item);
 -                flags.push(("from_method", None));
 -                flags.push(("from_method", Some(&*method)));
 +                flags.push(("from_method".to_string(), None));
 +                flags.push(("from_method".to_string(), Some(method.to_string())));
              }
          }
  
          if let Some(k) = obligation.cause.span.compiler_desugaring_kind() {
              desugaring = k.as_symbol().as_str();
 -            flags.push(("from_desugaring", None));
 -            flags.push(("from_desugaring", Some(&*desugaring)));
 +            flags.push(("from_desugaring".to_string(), None));
 +            flags.push(("from_desugaring".to_string(), Some(desugaring.to_string())));
 +        }
 +        let generics = self.tcx.generics_of(def_id);
 +        let self_ty = trait_ref.self_ty();
 +        let self_ty_str = self_ty.to_string();
 +        flags.push(("_Self".to_string(), Some(self_ty_str.clone())));
 +
 +        for param in generics.types.iter() {
 +            let name = param.name.as_str().to_string();
 +            let ty = trait_ref.substs.type_for_def(param);
 +            let ty_str = ty.to_string();
 +            flags.push((name.clone(),
 +                        Some(ty_str.clone())));
 +        }
 +
 +        if let Some(true) = self_ty.ty_to_def_id().map(|def_id| def_id.is_local()) {
 +            flags.push(("crate_local".to_string(), None));
          }
  
          if let Ok(Some(command)) = OnUnimplementedDirective::of_item(
              self.tcx, trait_ref.def_id, def_id
          ) {
 -            command.evaluate(self.tcx, trait_ref, &flags)
 +            command.evaluate(self.tcx, trait_ref, &flags[..])
          } else {
              OnUnimplementedNote::empty()
          }
                                  .map(|t| (format!(" in `{}`", t), format!("within `{}`, ", t)))
                              .unwrap_or((String::new(), String::new()));
  
 -                        let OnUnimplementedNote { message, label }
 +                        let OnUnimplementedNote { message, label, note }
                              = self.on_unimplemented_note(trait_ref, obligation);
                          let have_alt_message = message.is_some() || label.is_some();
  
                                                       trait_ref,
                                                       trait_ref.self_ty()));
                          }
 +                        if let Some(ref s) = note {
 +                            // If it has a custom "#[rustc_on_unimplemented]" note, let's display it
 +                            err.note(s.as_str());
 +                        }
  
                          self.suggest_borrow_on_unsized_slice(&obligation.cause.code, &mut err);
  
                  } else {
                      let (closure_span, found) = found_did
                          .and_then(|did| self.tcx.hir.get_if_local(did))
-                         .map(|node| self.get_fn_like_arguments(node))
-                         .unwrap_or((found_span.unwrap(), found));
+                         .map(|node| {
+                             let (found_span, found) = self.get_fn_like_arguments(node);
+                             (Some(found_span), found)
+                         }).unwrap_or((found_span, found));
  
                      self.report_arg_count_mismatch(span,
                                                     closure_span,
      fn report_arg_count_mismatch(
          &self,
          span: Span,
-         found_span: Span,
+         found_span: Option<Span>,
          expected_args: Vec<ArgKind>,
          found_args: Vec<ArgKind>,
          is_closure: bool,
          );
  
          err.span_label(span, format!( "expected {} that takes {}", kind, expected_str));
-         err.span_label(found_span, format!("takes {}", found_str));
-         if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] {
-             if fields.len() == expected_args.len() {
-                 let sugg = fields.iter()
-                     .map(|(name, _)| name.to_owned())
-                     .collect::<Vec<String>>().join(", ");
-                 err.span_suggestion(found_span,
-                                     "change the closure to take multiple arguments instead of \
-                                      a single tuple",
-                                     format!("|{}|", sugg));
+         if let Some(found_span) = found_span {
+             err.span_label(found_span, format!("takes {}", found_str));
+             if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] {
+                 if fields.len() == expected_args.len() {
+                     let sugg = fields.iter()
+                         .map(|(name, _)| name.to_owned())
+                         .collect::<Vec<String>>().join(", ");
+                     err.span_suggestion(found_span,
+                                         "change the closure to take multiple arguments instead of \
+                                          a single tuple",
+                                         format!("|{}|", sugg));
+                 }
              }
-         }
-         if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] {
-             if fields.len() == found_args.len() && is_closure {
-                 let sugg = format!(
-                     "|({}){}|",
-                     found_args.iter()
-                         .map(|arg| match arg {
-                             ArgKind::Arg(name, _) => name.to_owned(),
-                             _ => "_".to_owned(),
-                         })
-                         .collect::<Vec<String>>()
-                         .join(", "),
-                     // add type annotations if available
-                     if found_args.iter().any(|arg| match arg {
-                         ArgKind::Arg(_, ty) => ty != "_",
-                         _ => false,
-                     }) {
-                         format!(": ({})",
-                                 fields.iter()
-                                     .map(|(_, ty)| ty.to_owned())
-                                     .collect::<Vec<String>>()
-                                     .join(", "))
-                     } else {
-                         "".to_owned()
-                     },
-                 );
-                 err.span_suggestion(found_span,
-                                     "change the closure to accept a tuple instead of individual \
-                                      arguments",
-                                     sugg);
+             if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] {
+                 if fields.len() == found_args.len() && is_closure {
+                     let sugg = format!(
+                         "|({}){}|",
+                         found_args.iter()
+                             .map(|arg| match arg {
+                                 ArgKind::Arg(name, _) => name.to_owned(),
+                                 _ => "_".to_owned(),
+                             })
+                             .collect::<Vec<String>>()
+                             .join(", "),
+                         // add type annotations if available
+                         if found_args.iter().any(|arg| match arg {
+                             ArgKind::Arg(_, ty) => ty != "_",
+                             _ => false,
+                         }) {
+                             format!(": ({})",
+                                     fields.iter()
+                                         .map(|(_, ty)| ty.to_owned())
+                                         .collect::<Vec<String>>()
+                                         .join(", "))
+                         } else {
+                             "".to_owned()
+                         },
+                     );
+                     err.span_suggestion(found_span,
+                                         "change the closure to accept a tuple instead of \
+                                          individual arguments",
+                                         sugg);
+                 }
              }
          }