Skip to content

Commit

Permalink
Auto merge of #65951 - estebank:type-inference-error, r=<try>
Browse files Browse the repository at this point in the history
Point at method call when type annotations are needed

- Point at method call instead of whole expression when type annotations are needed.
- Suggest use of turbofish on function and methods.

Fix #49391, fix #46333, fix #48089. CC #58517, #63502, #63082.

r? @nikomatsakis
  • Loading branch information
bors committed Dec 11, 2019
2 parents 7dbfb0a + 94ab9ec commit 7103949
Show file tree
Hide file tree
Showing 36 changed files with 398 additions and 100 deletions.
1 change: 1 addition & 0 deletions src/librustc/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ use std::{cmp, fmt};
mod note;

mod need_type_info;
pub use need_type_info::TypeAnnotationNeeded;

pub mod nice_region_error;

Expand Down
144 changes: 130 additions & 14 deletions src/librustc/infer/error_reporting/need_type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::infer::type_variable::TypeVariableOriginKind;
use crate::ty::{self, Ty, Infer, TyVar};
use crate::ty::print::Print;
use syntax::source_map::DesugaringKind;
use syntax::symbol::kw;
use syntax_pos::Span;
use errors::{Applicability, DiagnosticBuilder};

Expand All @@ -19,6 +20,7 @@ struct FindLocalByTypeVisitor<'a, 'tcx> {
found_arg_pattern: Option<&'tcx Pat>,
found_ty: Option<Ty<'tcx>>,
found_closure: Option<&'tcx ExprKind>,
found_method_call: Option<&'tcx Expr>,
}

impl<'a, 'tcx> FindLocalByTypeVisitor<'a, 'tcx> {
Expand All @@ -35,6 +37,7 @@ impl<'a, 'tcx> FindLocalByTypeVisitor<'a, 'tcx> {
found_arg_pattern: None,
found_ty: None,
found_closure: None,
found_method_call: None,
}
}

Expand Down Expand Up @@ -93,11 +96,12 @@ impl<'a, 'tcx> Visitor<'tcx> for FindLocalByTypeVisitor<'a, 'tcx> {
}

fn visit_expr(&mut self, expr: &'tcx Expr) {
if let (ExprKind::Closure(_, _fn_decl, _id, _sp, _), Some(_)) = (
&expr.kind,
self.node_matches_type(expr.hir_id),
) {
self.found_closure = Some(&expr.kind);
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),
_ => {}
}
}
intravisit::walk_expr(self, expr);
}
Expand Down Expand Up @@ -147,6 +151,25 @@ fn closure_args(fn_sig: &ty::PolyFnSig<'_>) -> String {
.unwrap_or_default()
}

pub enum TypeAnnotationNeeded {
E0282,
E0283,
E0284,
}

impl Into<errors::DiagnosticId> for TypeAnnotationNeeded {
fn into(self) -> errors::DiagnosticId {
syntax::diagnostic_used!(E0282);
syntax::diagnostic_used!(E0283);
syntax::diagnostic_used!(E0284);
errors::DiagnosticId::Error(match self {
Self::E0282 => "E0282".to_string(),
Self::E0283 => "E0283".to_string(),
Self::E0284 => "E0284".to_string(),
})
}
}

impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
pub fn extract_type_name(
&self,
Expand All @@ -157,7 +180,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
let ty_vars = self.type_variables.borrow();
let var_origin = ty_vars.var_origin(ty_vid);
if let TypeVariableOriginKind::TypeParameterDefinition(name) = var_origin.kind {
return (name.to_string(), Some(var_origin.span));
if name != kw::SelfUpper {
return (name.to_string(), Some(var_origin.span));
}
}
}

Expand All @@ -175,6 +200,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
body_id: Option<hir::BodyId>,
span: Span,
ty: Ty<'tcx>,
error_code: TypeAnnotationNeeded,
) -> DiagnosticBuilder<'tcx> {
let ty = self.resolve_vars_if_possible(&ty);
let (name, name_sp) = self.extract_type_name(&ty, None);
Expand All @@ -185,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
Expand All @@ -210,6 +236,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// 3 | let _ = x.sum() as f64;
// | ^^^ cannot infer type for `S`
span
} else if let Some(
ExprKind::MethodCall(_, call_span, _),
) = 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
// |
// 2 | vec![Ok(2)].into_iter().collect()?;
// | ^^^^^^^ cannot infer type
// |
// = note: cannot resolve `<_ as std::ops::Try>::Ok == _`
if span.contains(*call_span) {
*call_span
} else {
span
}
} else {
span
};
Expand Down Expand Up @@ -247,12 +289,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// | consider giving `b` the explicit type `std::result::Result<i32, E>`, where
// | the type parameter `E` is specified
// ```
let mut err = struct_span_err!(
self.tcx.sess,
let error_code = error_code.into();
let mut err = self.tcx.sess.struct_span_err_with_code(
err_span,
E0282,
"type annotations needed{}",
ty_msg,
&format!("type annotations needed{}", ty_msg),
error_code,
);

let suffix = match local_visitor.found_ty {
Expand Down Expand Up @@ -334,6 +375,36 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
format!("consider giving this pattern {}", suffix)
};
err.span_label(pattern.span, msg);
} else if let Some(e) = local_visitor.found_method_call {
if let ExprKind::MethodCall(segment, ..) = &e.kind {
// Suggest specifiying type params or point out the return type of the call:
//
// error[E0282]: type annotations needed
// --> $DIR/type-annotations-needed-expr.rs:2:39
// |
// LL | let _ = x.into_iter().sum() as f64;
// | ^^^
// | |
// | cannot infer type for `S`
// | help: consider specifying the type argument in
// | the method call: `sum::<S>`
// |
// = note: type must be known at this point
//
// or
//
// 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.annotate_method_call(segment, e, &mut err);
}
}
// Instead of the following:
// error[E0282]: type annotations needed
Expand All @@ -351,7 +422,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// | ^^^ cannot infer type for `S`
// |
// = note: type must be known at this point
let span = name_sp.unwrap_or(span);
let span = name_sp.unwrap_or(err_span);
if !err.span.span_labels().iter().any(|span_label| {
span_label.label.is_some() && span_label.span == span
}) && local_visitor.found_arg_pattern.is_none()
Expand All @@ -362,6 +433,51 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
err
}

/// If the `FnSig` for the method call can be found and type arguments are identified as
/// needed, suggest annotating the call, otherwise point out the resulting type of the call.
fn annotate_method_call(
&self,
segment: &hir::ptr::P<hir::PathSegment>,
e: &Expr,
err: &mut DiagnosticBuilder<'_>,
) {
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 method_defs = borrow.node_method_def_id();
if let Some(did) = method_defs.get(e.hir_id) {
let generics = self.tcx.generics_of(*did);
if !generics.params.is_empty() {
err.span_suggestion(
segment.ident.span,
&format!(
"consider specifying the type argument{} in the method call",
if generics.params.len() > 1 {
"s"
} else {
""
},
),
format!("{}::<{}>", snippet, generics.params.iter()
.map(|p| p.name.to_string())
.collect::<Vec<String>>()
.join(", ")),
Applicability::HasPlaceholders,
);
} else {
let sig = self.tcx.fn_sig(*did);
err.span_label(e.span, &format!(
"this method call resolves to `{:?}`",
sig.output().skip_binder(),
));
}
}
}
}

pub fn need_type_info_err_in_generator(
&self,
kind: hir::GeneratorKind,
Expand Down
Loading

0 comments on commit 7103949

Please sign in to comment.