diff --git a/src/librustc/infer/error_reporting/need_type_info.rs b/src/librustc/infer/error_reporting/need_type_info.rs index 98134915b45d..c5133e884322 100644 --- a/src/librustc/infer/error_reporting/need_type_info.rs +++ b/src/librustc/infer/error_reporting/need_type_info.rs @@ -20,7 +20,7 @@ struct FindLocalByTypeVisitor<'a, 'tcx> { found_arg_pattern: Option<&'tcx Pat>, found_ty: Option>, found_closure: Option<&'tcx ExprKind>, - found_method_call: Option<&'tcx ExprKind>, + found_method_call: Option<&'tcx Expr>, } impl<'a, 'tcx> FindLocalByTypeVisitor<'a, 'tcx> { @@ -99,7 +99,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindLocalByTypeVisitor<'a, 'tcx> { if self.node_matches_type(expr.hir_id).is_some() { match expr.kind { ExprKind::Closure(..) => self.found_closure = Some(&expr.kind), - ExprKind::MethodCall(..) => self.found_method_call = Some(&expr.kind), + ExprKind::MethodCall(..) => self.found_method_call = Some(&expr), _ => {} } } @@ -211,8 +211,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS); let ty_vars = self.type_variables.borrow(); let getter = move |ty_vid| { - if let TypeVariableOriginKind::TypeParameterDefinition(name) = - ty_vars.var_origin(ty_vid).kind { + let var_origin = ty_vars.var_origin(ty_vid); + if let TypeVariableOriginKind::TypeParameterDefinition(name) = var_origin.kind { return Some(name.to_string()); } None @@ -238,7 +238,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { span } else if let Some( ExprKind::MethodCall(_, call_span, _), - ) = local_visitor.found_method_call { + ) = local_visitor.found_method_call.map(|e| &e.kind) { // Point at the call instead of the whole expression: // error[E0284]: type annotations needed // --> file.rs:2:5 @@ -375,16 +375,48 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { format!("consider giving this pattern {}", suffix) }; err.span_label(pattern.span, msg); - } else if let Some(ExprKind::MethodCall(segment, ..)) = local_visitor.found_method_call { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(segment.ident.span) { - if segment.args.is_none() { - err.span_suggestion( - segment.ident.span, - "consider specifying the type argument in the method call", - // FIXME: we don't know how many type arguments should be set here. - format!("{}::<_>", snippet), - Applicability::HasPlaceholders, - ); + } else if let Some(e) = local_visitor.found_method_call { + if let ExprKind::MethodCall(segment, _call_sp, _args) = &e.kind { + if let (Ok(snippet), Some(tables), None) = ( + self.tcx.sess.source_map().span_to_snippet(segment.ident.span), + self.in_progress_tables, + &segment.args, + ) { + let borrow = tables.borrow(); + let sigs = borrow.node_method_sig(); + if let Some(sig) = sigs.get(e.hir_id) { + let mut params = vec![]; + for arg in sig.inputs_and_output().skip_binder().iter() { + if let ty::Param(param) = arg.kind { + if param.name != kw::SelfUpper { + let name = param.name.to_string(); + if !params.contains(&name) { + params.push(name); + } + } + } + } + if !params.is_empty() { + err.span_suggestion( + segment.ident.span, + &format!( + "consider specifying the type argument{} in the method call", + if params.len() > 1 { + "s" + } else { + "" + }, + ), + format!("{}::<{}>", snippet, params.join(", ")), + Applicability::HasPlaceholders, + ); + } else { + err.span_label(e.span, &format!( + "this method call resolves to `{:?}`", + sig.output().skip_binder(), + )); + } + } } } } diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index f7e422b0403d..2da43bdcc1e2 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -338,6 +338,8 @@ pub struct TypeckTables<'tcx> { /// typeck::check::fn_ctxt for details. node_types: ItemLocalMap>, + node_method_sig: ItemLocalMap>, + /// Stores the type parameters which were substituted to obtain the type /// of this node. This only applies to nodes that refer to entities /// parameterized by type parameters, such as generic fns, types, or @@ -442,6 +444,7 @@ impl<'tcx> TypeckTables<'tcx> { user_provided_types: Default::default(), user_provided_sigs: Default::default(), node_types: Default::default(), + node_method_sig: Default::default(), node_substs: Default::default(), adjustments: Default::default(), pat_binding_modes: Default::default(), @@ -542,6 +545,20 @@ impl<'tcx> TypeckTables<'tcx> { } } + pub fn node_method_sig(&self) -> LocalTableInContext<'_, ty::PolyFnSig<'tcx>> { + LocalTableInContext { + local_id_root: self.local_id_root, + data: &self.node_method_sig + } + } + + pub fn node_method_sig_mut(&mut self) -> LocalTableInContextMut<'_, ty::PolyFnSig<'tcx>> { + LocalTableInContextMut { + local_id_root: self.local_id_root, + data: &mut self.node_method_sig + } + } + pub fn node_type(&self, id: hir::HirId) -> Ty<'tcx> { self.node_type_opt(id).unwrap_or_else(|| bug!("node_type: no type for node `{}`", @@ -748,6 +765,7 @@ impl<'a, 'tcx> HashStable> for TypeckTables<'tcx> { ref user_provided_types, ref user_provided_sigs, ref node_types, + ref node_method_sig, ref node_substs, ref adjustments, ref pat_binding_modes, @@ -774,6 +792,7 @@ impl<'a, 'tcx> HashStable> for TypeckTables<'tcx> { user_provided_types.hash_stable(hcx, hasher); user_provided_sigs.hash_stable(hcx, hasher); node_types.hash_stable(hcx, hasher); + node_method_sig.hash_stable(hcx, hasher); node_substs.hash_stable(hcx, hasher); adjustments.hash_stable(hcx, hasher); pat_binding_modes.hash_stable(hcx, hasher); diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 5bfc60c75406..5f971a1ad505 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -871,6 +871,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr) { Ok(method) => { + let sig = self.tcx.fn_sig(method.def_id); + // We could add a "consider `foo::`" suggestion here, but I wasn't able to + // trigger this codepath causing `structuraly_resolved_type` to emit an error. + + // We could do this only when type params are present in the method to reducte + // memory usage, but doing it unconditionally lets us also point at the method + // expression and state the resolved return value: + // ``` + // error[E0282]: type annotations needed + // --> $DIR/issue-65611.rs:59:20 + // | + // LL | let x = buffer.last().unwrap().0.clone(); + // | -------^^^^-- + // | | | + // | | cannot infer type for `T` + // | this method call resolves to `std::option::Option<&T>` + // | + // = note: type must be known at this point + // ``` + self.tables.borrow_mut().node_method_sig_mut().insert(expr.hir_id, sig); + self.write_method_call(expr.hir_id, method); Ok(method) } diff --git a/src/test/ui/issues/issue-65611.stderr b/src/test/ui/issues/issue-65611.stderr index 905c5ae9461d..3be08b233e47 100644 --- a/src/test/ui/issues/issue-65611.stderr +++ b/src/test/ui/issues/issue-65611.stderr @@ -2,10 +2,10 @@ error[E0282]: type annotations needed --> $DIR/issue-65611.rs:59:20 | LL | let x = buffer.last().unwrap().0.clone(); - | ^^^^ - | | - | cannot infer type for `T` - | help: consider specifying the type argument in the method call: `last::<_>` + | -------^^^^-- + | | | + | | cannot infer type for `T` + | this method call resolves to `std::option::Option<&T>` | = note: type must be known at this point diff --git a/src/test/ui/question-mark-type-infer.stderr b/src/test/ui/question-mark-type-infer.stderr index d32d94976451..7911701946cd 100644 --- a/src/test/ui/question-mark-type-infer.stderr +++ b/src/test/ui/question-mark-type-infer.stderr @@ -5,7 +5,7 @@ LL | l.iter().map(f).collect()? | ^^^^^^^ | | | cannot infer type - | help: consider specifying the type argument in the method call: `collect::<_>` + | help: consider specifying the type argument in the method call: `collect::` | = note: cannot resolve `<_ as std::ops::Try>::Ok == _` diff --git a/src/test/ui/span/type-annotations-needed-expr.stderr b/src/test/ui/span/type-annotations-needed-expr.stderr index 1efb2720e0c5..bc1c2f6164b6 100644 --- a/src/test/ui/span/type-annotations-needed-expr.stderr +++ b/src/test/ui/span/type-annotations-needed-expr.stderr @@ -5,7 +5,7 @@ LL | let _ = (vec![1,2,3]).into_iter().sum() as f64; | ^^^ | | | cannot infer type for `S` - | help: consider specifying the type argument in the method call: `sum::<_>` + | help: consider specifying the type argument in the method call: `sum::` | = note: type must be known at this point