found_arg_pattern: Option<&'tcx Pat>,
found_ty: Option<Ty<'tcx>>,
found_closure: Option<&'tcx ExprKind>,
- found_method_call: Option<&'tcx ExprKind>,
+ found_method_call: Option<&'tcx Expr>,
}
impl<'a, 'tcx> 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),
_ => {}
}
}
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
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
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(),
+ ));
+ }
+ }
}
}
}
/// typeck::check::fn_ctxt for details.
node_types: ItemLocalMap<Ty<'tcx>>,
+ node_method_sig: ItemLocalMap<ty::PolyFnSig<'tcx>>,
+
/// 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
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(),
}
}
+ 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 `{}`",
ref user_provided_types,
ref user_provided_sigs,
ref node_types,
+ ref node_method_sig,
ref node_substs,
ref adjustments,
ref pat_binding_modes,
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);
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::<params>`" 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)
}