Skip to content

Commit

Permalink
Auto merge of #41840 - arielb1:deduplicate-selection-errors, r=nikoma…
Browse files Browse the repository at this point in the history
…tsakis

Suppress trait errors that are implied by other errors

this is currently a hack and should be cleaned up somewhat. Posting this to get some feedback.

r? @nikomatsakis
cc @estebank
  • Loading branch information
bors committed Jun 16, 2017
2 parents c3627e2 + 7b9519a commit 787d9da
Show file tree
Hide file tree
Showing 23 changed files with 277 additions and 265 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 @@ -74,6 +74,7 @@ use syntax_pos::{Pos, Span};
use errors::{DiagnosticBuilder, DiagnosticStyledString};

mod note;
mod need_type_info;

impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
pub fn note_and_explain_region(self,
Expand Down
153 changes: 153 additions & 0 deletions src/librustc/infer/error_reporting/need_type_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use hir::{self, map, Local, Pat, Body};
use hir::intravisit::{self, Visitor, NestedVisitorMap};
use infer::InferCtxt;
use infer::type_variable::TypeVariableOrigin;
use ty::{self, Ty, TyInfer, TyVar};

use syntax::ast::NodeId;
use syntax_pos::Span;

struct FindLocalByTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
target_ty: &'a Ty<'tcx>,
hir_map: &'a hir::map::Map<'gcx>,
found_local_pattern: Option<&'gcx Pat>,
found_arg_pattern: Option<&'gcx Pat>,
}

impl<'a, 'gcx, 'tcx> FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
fn node_matches_type(&mut self, node_id: NodeId) -> bool {
let ty_opt = self.infcx.in_progress_tables.and_then(|tables| {
tables.borrow().node_id_to_type_opt(node_id)
});
match ty_opt {
Some(ty) => {
let ty = self.infcx.resolve_type_vars_if_possible(&ty);
ty.walk().any(|inner_ty| {
inner_ty == *self.target_ty || match (&inner_ty.sty, &self.target_ty.sty) {
(&TyInfer(TyVar(a_vid)), &TyInfer(TyVar(b_vid))) => {
self.infcx
.type_variables
.borrow_mut()
.sub_unified(a_vid, b_vid)
}
_ => false,
}
})
}
None => false,
}
}
}

impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
NestedVisitorMap::OnlyBodies(&self.hir_map)
}

fn visit_local(&mut self, local: &'gcx Local) {
if self.found_local_pattern.is_none() && self.node_matches_type(local.id) {
self.found_local_pattern = Some(&*local.pat);
}
intravisit::walk_local(self, local);
}

fn visit_body(&mut self, body: &'gcx Body) {
for argument in &body.arguments {
if self.found_arg_pattern.is_none() && self.node_matches_type(argument.id) {
self.found_arg_pattern = Some(&*argument.pat);
}
}
intravisit::walk_body(self, body);
}
}


impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
fn extract_type_name(&self, ty: &'a Ty<'tcx>) -> String {
if let ty::TyInfer(ty::TyVar(ty_vid)) = (*ty).sty {
let ty_vars = self.type_variables.borrow();
if let TypeVariableOrigin::TypeParameterDefinition(_, name) =
*ty_vars.var_origin(ty_vid) {
name.to_string()
} else {
ty.to_string()
}
} else {
ty.to_string()
}
}

pub fn need_type_info(&self, body_id: hir::BodyId, span: Span, ty: Ty<'tcx>) {
let ty = self.resolve_type_vars_if_possible(&ty);
let name = self.extract_type_name(&ty);

let mut err_span = span;
let mut labels = vec![(span, format!("cannot infer type for `{}`", name))];

let mut local_visitor = FindLocalByTypeVisitor {
infcx: &self,
target_ty: &ty,
hir_map: &self.tcx.hir,
found_local_pattern: None,
found_arg_pattern: None,
};

// #40294: cause.body_id can also be a fn declaration.
// Currently, if it's anything other than NodeExpr, we just ignore it
match self.tcx.hir.find(body_id.node_id) {
Some(map::NodeExpr(expr)) => local_visitor.visit_expr(expr),
_ => ()
}

if let Some(pattern) = local_visitor.found_arg_pattern {
err_span = pattern.span;
// We don't want to show the default label for closures.
//
// So, before clearing, the output would look something like this:
// ```
// let x = |_| { };
// - ^^^^ cannot infer type for `[_; 0]`
// |
// consider giving this closure parameter a type
// ```
//
// After clearing, it looks something like this:
// ```
// let x = |_| { };
// ^ consider giving this closure parameter a type
// ```
labels.clear();
labels.push((pattern.span, format!("consider giving this closure parameter a type")));
}

if let Some(pattern) = local_visitor.found_local_pattern {
if let Some(simple_name) = pattern.simple_name() {
labels.push((pattern.span, format!("consider giving `{}` a type", simple_name)));
} else {
labels.push((pattern.span, format!("consider giving the pattern a type")));
}
}

let mut err = struct_span_err!(self.tcx.sess,
err_span,
E0282,
"type annotations needed");

for (target_span, label_message) in labels {
err.span_label(target_span, label_message);
}

err.emit();
}
}
7 changes: 4 additions & 3 deletions src/librustc/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use std::fmt;
use syntax::ast;
use errors::DiagnosticBuilder;
use syntax_pos::{self, Span, DUMMY_SP};
use util::nodemap::{FxHashMap, FxHashSet};
use util::nodemap::FxHashMap;
use arena::DroplessArena;

use self::combine::CombineFields;
Expand Down Expand Up @@ -110,7 +110,7 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {

// the set of predicates on which errors have been reported, to
// avoid reporting the same error twice.
pub reported_trait_errors: RefCell<FxHashSet<traits::TraitErrorKey<'tcx>>>,
pub reported_trait_errors: RefCell<FxHashMap<Span, Vec<ty::Predicate<'tcx>>>>,

// When an error occurs, we want to avoid reporting "derived"
// errors that are due to this original failure. Normally, we
Expand Down Expand Up @@ -350,6 +350,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> {
global_tcx: self,
arena: DroplessArena::new(),
fresh_tables: None,

}
}
}
Expand Down Expand Up @@ -381,7 +382,7 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> {
region_vars: RegionVarBindings::new(tcx),
selection_cache: traits::SelectionCache::new(),
evaluation_cache: traits::EvaluationCache::new(),
reported_trait_errors: RefCell::new(FxHashSet()),
reported_trait_errors: RefCell::new(FxHashMap()),
tainted_by_errors_flag: Cell::new(false),
err_count_on_creation: tcx.sess.err_count(),
in_snapshot: Cell::new(false),
Expand Down
Loading

0 comments on commit 787d9da

Please sign in to comment.