Skip to content

Commit f3290cd

Browse files
committed
rustc: support turning trait errors into lints via ObligationCauseCode.
1 parent 992d1e4 commit f3290cd

File tree

25 files changed

+345
-254
lines changed

25 files changed

+345
-254
lines changed

src/librustc/infer/error_reporting/need_type_info.rs

Lines changed: 85 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -8,71 +8,14 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use hir::{self, Local, Pat, Body, HirId};
12-
use hir::intravisit::{self, Visitor, NestedVisitorMap};
11+
use hir::{self, HirId};
1312
use infer::InferCtxt;
1413
use infer::type_variable::TypeVariableOrigin;
14+
use traits;
1515
use ty::{self, Ty, Infer, TyVar};
1616
use syntax::source_map::CompilerDesugaringKind;
17-
use syntax_pos::Span;
1817
use errors::DiagnosticBuilder;
1918

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: HirId) -> 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-
(&Infer(TyVar(a_vid)), &Infer(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.hir_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.hir_id) {
68-
self.found_arg_pattern = Some(&*argument.pat);
69-
}
70-
}
71-
intravisit::walk_body(self, body);
72-
}
73-
}
74-
75-
7619
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
7720
pub fn extract_type_name(&self, ty: &'a Ty<'tcx>) -> String {
7821
if let ty::Infer(ty::TyVar(ty_vid)) = (*ty).sty {
@@ -89,38 +32,85 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
8932
}
9033

9134
pub fn need_type_info_err(&self,
92-
body_id: Option<hir::BodyId>,
93-
span: Span,
94-
ty: Ty<'tcx>)
95-
-> DiagnosticBuilder<'gcx> {
35+
cause: &traits::ObligationCause<'tcx>,
36+
ty: Ty<'tcx>)
37+
-> DiagnosticBuilder<'gcx> {
9638
let ty = self.resolve_type_vars_if_possible(&ty);
9739
let name = self.extract_type_name(&ty);
9840

99-
let mut err_span = span;
10041
let mut labels = vec![(
101-
span,
42+
cause.span,
10243
if &name == "_" {
10344
"cannot infer type".to_string()
10445
} else {
10546
format!("cannot infer type for `{}`", name)
10647
},
10748
)];
49+
let mut span = cause.span;
10850

109-
let mut local_visitor = FindLocalByTypeVisitor {
110-
infcx: &self,
111-
target_ty: &ty,
112-
hir_map: &self.tcx.hir,
113-
found_local_pattern: None,
114-
found_arg_pattern: None,
115-
};
116-
117-
if let Some(body_id) = body_id {
118-
let expr = self.tcx.hir.expect_expr(body_id.node_id);
119-
local_visitor.visit_expr(expr);
51+
// NB. Lower values are more preferred.
52+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
53+
enum LocalKind {
54+
ClosureArg,
55+
Let,
12056
}
12157

122-
if let Some(pattern) = local_visitor.found_arg_pattern {
123-
err_span = pattern.span;
58+
let found_local = self.in_progress_tables.and_then(|tables| {
59+
let tables = tables.borrow();
60+
let local_id_root = tables.local_id_root?;
61+
assert!(local_id_root.is_local());
62+
63+
tables.node_types().iter().filter_map(|(&local_id, &node_ty)| {
64+
let node_id = self.tcx.hir.hir_to_node_id(HirId {
65+
owner: local_id_root.index,
66+
local_id,
67+
});
68+
69+
let (kind, pattern) = match self.tcx.hir.find(node_id) {
70+
Some(hir::Node::Local(local)) => {
71+
(LocalKind::Let, &*local.pat)
72+
}
73+
74+
Some(hir::Node::Binding(pat)) |
75+
Some(hir::Node::Pat(pat)) => {
76+
let parent_id = self.tcx.hir.get_parent_node(node_id);
77+
match self.tcx.hir.find(parent_id) {
78+
Some(hir::Node::Expr(e)) => {
79+
match e.node {
80+
hir::ExprKind::Closure(..) => {}
81+
_ => return None,
82+
}
83+
}
84+
_ => return None,
85+
}
86+
87+
(LocalKind::ClosureArg, pat)
88+
}
89+
90+
_ => return None
91+
};
92+
93+
let node_ty = self.resolve_type_vars_if_possible(&node_ty);
94+
let matches_type = node_ty.walk().any(|inner_ty| {
95+
inner_ty == ty || match (&inner_ty.sty, &ty.sty) {
96+
(&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => {
97+
self.type_variables
98+
.borrow_mut()
99+
.sub_unified(a_vid, b_vid)
100+
}
101+
_ => false,
102+
}
103+
});
104+
if !matches_type {
105+
return None;
106+
}
107+
108+
Some((kind, pattern))
109+
}).min_by_key(|&(kind, pattern)| (kind, pattern.hir_id.local_id))
110+
});
111+
112+
if let Some((LocalKind::ClosureArg, pattern)) = found_local {
113+
span = pattern.span;
124114
// We don't want to show the default label for closures.
125115
//
126116
// So, before clearing, the output would look something like this:
@@ -139,7 +129,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
139129
labels.clear();
140130
labels.push(
141131
(pattern.span, "consider giving this closure parameter a type".to_string()));
142-
} else if let Some(pattern) = local_visitor.found_local_pattern {
132+
} else if let Some((LocalKind::Let, pattern)) = found_local {
143133
if let Some(simple_ident) = pattern.simple_ident() {
144134
match pattern.span.compiler_desugaring_kind() {
145135
None => labels.push((pattern.span,
@@ -155,10 +145,22 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
155145
}
156146
}
157147

158-
let mut err = struct_span_err!(self.tcx.sess,
159-
err_span,
160-
E0282,
161-
"type annotations needed");
148+
let lint = self.get_lint_from_cause_code(&cause.code);
149+
macro_rules! struct_span_err_or_lint {
150+
($code:ident, $($message:tt)*) => {
151+
match lint {
152+
Some((lint, id)) => {
153+
let message = format!($($message)*);
154+
self.tcx.struct_span_lint_node(lint, id, span, &message)
155+
}
156+
None => {
157+
struct_span_err!(self.tcx.sess, span, $code, $($message)*)
158+
}
159+
}
160+
}
161+
}
162+
163+
let mut err = struct_span_err_or_lint!(E0282, "type annotations needed");
162164

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

src/librustc/infer/mod.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,8 +1424,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
14241424
/// new obligations that must further be processed.
14251425
pub fn partially_normalize_associated_types_in<T>(
14261426
&self,
1427-
span: Span,
1428-
body_id: ast::NodeId,
1427+
cause: ObligationCause<'tcx>,
14291428
param_env: ty::ParamEnv<'tcx>,
14301429
value: &T,
14311430
) -> InferOk<'tcx, T>
@@ -1434,7 +1433,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
14341433
{
14351434
debug!("partially_normalize_associated_types_in(value={:?})", value);
14361435
let mut selcx = traits::SelectionContext::new(self);
1437-
let cause = ObligationCause::misc(span, body_id);
14381436
let traits::Normalized { value, obligations } =
14391437
traits::normalize(&mut selcx, param_env, cause, value);
14401438
debug!(

src/librustc/infer/outlives/env.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,9 @@
1010

1111
use infer::{GenericKind, InferCtxt};
1212
use infer::outlives::free_region_map::FreeRegionMap;
13-
use traits::query::outlives_bounds::{self, OutlivesBound};
13+
use traits::{self, query::outlives_bounds::{self, OutlivesBound}};
1414
use ty::{self, Ty};
1515

16-
use syntax::ast;
17-
use syntax_pos::Span;
18-
1916
/// The `OutlivesEnvironment` collects information about what outlives
2017
/// what in a given type-checking setting. For example, if we have a
2118
/// where-clause like `where T: 'a` in scope, then the
@@ -136,15 +133,14 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> {
136133
&mut self,
137134
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
138135
fn_sig_tys: &[Ty<'tcx>],
139-
body_id: ast::NodeId,
140-
span: Span,
136+
cause: &traits::ObligationCause<'tcx>,
141137
) {
142138
debug!("add_implied_bounds()");
143139

144140
for &ty in fn_sig_tys {
145141
let ty = infcx.resolve_type_vars_if_possible(&ty);
146142
debug!("add_implied_bounds: ty = {}", ty);
147-
let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty, span);
143+
let implied_bounds = infcx.implied_outlives_bounds(self.param_env, cause, ty);
148144
self.add_outlives_bounds(Some(infcx), implied_bounds)
149145
}
150146
}

src/librustc/mir/interpret/error.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
5555
self.struct_generic(tcx, message, None)
5656
}
5757

58+
pub fn struct_lint(&self,
59+
tcx: TyCtxtAt<'a, 'gcx, 'tcx>,
60+
message: &str,
61+
lint_root: ast::NodeId,
62+
lint: Option<&'static ::lint::Lint>)
63+
-> Option<DiagnosticBuilder<'tcx>>
64+
{
65+
self.struct_generic(tcx, message, Some((lint_root, lint)))
66+
}
67+
5868
pub fn report_as_error(&self,
5969
tcx: TyCtxtAt<'a, 'gcx, 'tcx>,
6070
message: &str
@@ -73,7 +83,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
7383
let lint = self.struct_generic(
7484
tcx,
7585
message,
76-
Some(lint_root),
86+
Some((lint_root, None)),
7787
);
7888
if let Some(mut lint) = lint {
7989
lint.emit();
@@ -84,7 +94,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
8494
&self,
8595
tcx: TyCtxtAt<'a, 'gcx, 'tcx>,
8696
message: &str,
87-
lint_root: Option<ast::NodeId>,
97+
lint_root: Option<(ast::NodeId, Option<&'static ::lint::Lint>)>,
8898
) -> Option<DiagnosticBuilder<'tcx>> {
8999
match self.error.kind {
90100
::mir::interpret::EvalErrorKind::TypeckError |
@@ -97,15 +107,15 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
97107
_ => {},
98108
}
99109
trace!("reporting const eval failure at {:?}", self.span);
100-
let mut err = if let Some(lint_root) = lint_root {
110+
let mut err = if let Some((lint_root, lint)) = lint_root {
101111
let node_id = self.stacktrace
102112
.iter()
103113
.rev()
104114
.filter_map(|frame| frame.lint_root)
105115
.next()
106116
.unwrap_or(lint_root);
107117
tcx.struct_span_lint_node(
108-
::rustc::lint::builtin::CONST_ERR,
118+
lint.unwrap_or(::rustc::lint::builtin::CONST_ERR),
109119
node_id,
110120
tcx.span,
111121
message,

0 commit comments

Comments
 (0)