Skip to content

Commit 7b9519a

Browse files
committed
suppress trait errors that are implied by other errors
Instead of suppressing only trait errors that are "exact duplicates", display only the "most high-level" error when there are multiple trait errors with the same span that imply each-other. e.g. when there are both `[closure]: Fn` and `[closure]: FnOnce`, omit displaying the `[closure]: FnOnce` bound.
1 parent dfa7e21 commit 7b9519a

23 files changed

+277
-265
lines changed

src/librustc/infer/error_reporting/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ use syntax_pos::{Pos, Span};
7474
use errors::{DiagnosticBuilder, DiagnosticStyledString};
7575

7676
mod note;
77+
mod need_type_info;
7778

7879
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
7980
pub fn note_and_explain_region(self,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use hir::{self, map, Local, Pat, Body};
12+
use hir::intravisit::{self, Visitor, NestedVisitorMap};
13+
use infer::InferCtxt;
14+
use infer::type_variable::TypeVariableOrigin;
15+
use ty::{self, Ty, TyInfer, TyVar};
16+
17+
use syntax::ast::NodeId;
18+
use syntax_pos::Span;
19+
20+
struct FindLocalByTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
21+
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
22+
target_ty: &'a Ty<'tcx>,
23+
hir_map: &'a hir::map::Map<'gcx>,
24+
found_local_pattern: Option<&'gcx Pat>,
25+
found_arg_pattern: Option<&'gcx Pat>,
26+
}
27+
28+
impl<'a, 'gcx, 'tcx> FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
29+
fn node_matches_type(&mut self, node_id: NodeId) -> bool {
30+
let ty_opt = self.infcx.in_progress_tables.and_then(|tables| {
31+
tables.borrow().node_id_to_type_opt(node_id)
32+
});
33+
match ty_opt {
34+
Some(ty) => {
35+
let ty = self.infcx.resolve_type_vars_if_possible(&ty);
36+
ty.walk().any(|inner_ty| {
37+
inner_ty == *self.target_ty || match (&inner_ty.sty, &self.target_ty.sty) {
38+
(&TyInfer(TyVar(a_vid)), &TyInfer(TyVar(b_vid))) => {
39+
self.infcx
40+
.type_variables
41+
.borrow_mut()
42+
.sub_unified(a_vid, b_vid)
43+
}
44+
_ => false,
45+
}
46+
})
47+
}
48+
None => false,
49+
}
50+
}
51+
}
52+
53+
impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
54+
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
55+
NestedVisitorMap::OnlyBodies(&self.hir_map)
56+
}
57+
58+
fn visit_local(&mut self, local: &'gcx Local) {
59+
if self.found_local_pattern.is_none() && self.node_matches_type(local.id) {
60+
self.found_local_pattern = Some(&*local.pat);
61+
}
62+
intravisit::walk_local(self, local);
63+
}
64+
65+
fn visit_body(&mut self, body: &'gcx Body) {
66+
for argument in &body.arguments {
67+
if self.found_arg_pattern.is_none() && self.node_matches_type(argument.id) {
68+
self.found_arg_pattern = Some(&*argument.pat);
69+
}
70+
}
71+
intravisit::walk_body(self, body);
72+
}
73+
}
74+
75+
76+
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
77+
fn extract_type_name(&self, ty: &'a Ty<'tcx>) -> String {
78+
if let ty::TyInfer(ty::TyVar(ty_vid)) = (*ty).sty {
79+
let ty_vars = self.type_variables.borrow();
80+
if let TypeVariableOrigin::TypeParameterDefinition(_, name) =
81+
*ty_vars.var_origin(ty_vid) {
82+
name.to_string()
83+
} else {
84+
ty.to_string()
85+
}
86+
} else {
87+
ty.to_string()
88+
}
89+
}
90+
91+
pub fn need_type_info(&self, body_id: hir::BodyId, span: Span, ty: Ty<'tcx>) {
92+
let ty = self.resolve_type_vars_if_possible(&ty);
93+
let name = self.extract_type_name(&ty);
94+
95+
let mut err_span = span;
96+
let mut labels = vec![(span, format!("cannot infer type for `{}`", name))];
97+
98+
let mut local_visitor = FindLocalByTypeVisitor {
99+
infcx: &self,
100+
target_ty: &ty,
101+
hir_map: &self.tcx.hir,
102+
found_local_pattern: None,
103+
found_arg_pattern: None,
104+
};
105+
106+
// #40294: cause.body_id can also be a fn declaration.
107+
// Currently, if it's anything other than NodeExpr, we just ignore it
108+
match self.tcx.hir.find(body_id.node_id) {
109+
Some(map::NodeExpr(expr)) => local_visitor.visit_expr(expr),
110+
_ => ()
111+
}
112+
113+
if let Some(pattern) = local_visitor.found_arg_pattern {
114+
err_span = pattern.span;
115+
// We don't want to show the default label for closures.
116+
//
117+
// So, before clearing, the output would look something like this:
118+
// ```
119+
// let x = |_| { };
120+
// - ^^^^ cannot infer type for `[_; 0]`
121+
// |
122+
// consider giving this closure parameter a type
123+
// ```
124+
//
125+
// After clearing, it looks something like this:
126+
// ```
127+
// let x = |_| { };
128+
// ^ consider giving this closure parameter a type
129+
// ```
130+
labels.clear();
131+
labels.push((pattern.span, format!("consider giving this closure parameter a type")));
132+
}
133+
134+
if let Some(pattern) = local_visitor.found_local_pattern {
135+
if let Some(simple_name) = pattern.simple_name() {
136+
labels.push((pattern.span, format!("consider giving `{}` a type", simple_name)));
137+
} else {
138+
labels.push((pattern.span, format!("consider giving the pattern a type")));
139+
}
140+
}
141+
142+
let mut err = struct_span_err!(self.tcx.sess,
143+
err_span,
144+
E0282,
145+
"type annotations needed");
146+
147+
for (target_span, label_message) in labels {
148+
err.span_label(target_span, label_message);
149+
}
150+
151+
err.emit();
152+
}
153+
}

src/librustc/infer/mod.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use std::fmt;
3636
use syntax::ast;
3737
use errors::DiagnosticBuilder;
3838
use syntax_pos::{self, Span, DUMMY_SP};
39-
use util::nodemap::{FxHashMap, FxHashSet};
39+
use util::nodemap::FxHashMap;
4040
use arena::DroplessArena;
4141

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

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

115115
// When an error occurs, we want to avoid reporting "derived"
116116
// errors that are due to this original failure. Normally, we
@@ -350,6 +350,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> {
350350
global_tcx: self,
351351
arena: DroplessArena::new(),
352352
fresh_tables: None,
353+
353354
}
354355
}
355356
}
@@ -381,7 +382,7 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> {
381382
region_vars: RegionVarBindings::new(tcx),
382383
selection_cache: traits::SelectionCache::new(),
383384
evaluation_cache: traits::EvaluationCache::new(),
384-
reported_trait_errors: RefCell::new(FxHashSet()),
385+
reported_trait_errors: RefCell::new(FxHashMap()),
385386
tainted_by_errors_flag: Cell::new(false),
386387
err_count_on_creation: tcx.sess.err_count(),
387388
in_snapshot: Cell::new(false),

0 commit comments

Comments
 (0)