/
trait_impl_difference.rs
150 lines (141 loc) · 5.81 KB
/
trait_impl_difference.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//! Error Reporting for `impl` items that do not match the obligations from their `trait`.
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
use crate::infer::lexical_region_resolve::RegionResolutionError;
use crate::infer::{Subtype, TyCtxtInferExt, ValuePairs};
use crate::traits::ObligationCauseCode::CompareImplMethodObligation;
use rustc_errors::ErrorReported;
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor;
use rustc_middle::ty::error::ExpectedFound;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::{MultiSpan, Span};
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
/// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`.
pub(super) fn try_report_impl_not_conforming_to_trait(&self) -> Option<ErrorReported> {
if let Some(ref error) = self.error {
debug!("try_report_impl_not_conforming_to_trait {:?}", error);
if let RegionResolutionError::SubSupConflict(
_,
var_origin,
sub_origin,
_sub,
sup_origin,
_sup,
) = error.clone()
{
if let (&Subtype(ref sup_trace), &Subtype(ref sub_trace)) =
(&sup_origin, &sub_origin)
{
if let (
ValuePairs::Types(sub_expected_found),
ValuePairs::Types(sup_expected_found),
CompareImplMethodObligation { trait_item_def_id, .. },
) = (&sub_trace.values, &sup_trace.values, &sub_trace.cause.code)
{
if sup_expected_found == sub_expected_found {
self.emit_err(
var_origin.span(),
sub_expected_found.expected,
sub_expected_found.found,
*trait_item_def_id,
);
return Some(ErrorReported);
}
}
}
}
}
None
}
fn emit_err(&self, sp: Span, expected: Ty<'tcx>, found: Ty<'tcx>, trait_def_id: DefId) {
let tcx = self.tcx();
let trait_sp = self.tcx().def_span(trait_def_id);
let mut err = self
.tcx()
.sess
.struct_span_err(sp, "`impl` item signature doesn't match `trait` item signature");
err.span_label(sp, &format!("found `{:?}`", found));
err.span_label(trait_sp, &format!("expected `{:?}`", expected));
// Get the span of all the used type parameters in the method.
let assoc_item = self.tcx().associated_item(trait_def_id);
let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] };
match assoc_item.kind {
ty::AssocKind::Fn => {
let hir = self.tcx().hir();
if let Some(hir_id) = assoc_item.def_id.as_local().map(|id| hir.as_local_hir_id(id))
{
if let Some(decl) = hir.fn_decl_by_hir_id(hir_id) {
visitor.visit_fn_decl(decl);
}
}
}
_ => {}
}
let mut type_param_span: MultiSpan = visitor.types.to_vec().into();
for &span in &visitor.types {
type_param_span.push_span_label(
span,
"consider borrowing this type parameter in the trait".to_string(),
);
}
if let Some((expected, found)) = tcx
.infer_ctxt()
.enter(|infcx| infcx.expected_found_str_ty(&ExpectedFound { expected, found }))
{
// Highlighted the differences when showing the "expected/found" note.
err.note_expected_found(&"", expected, &"", found);
} else {
// This fallback shouldn't be necessary, but let's keep it in just in case.
err.note(&format!("expected `{:?}`\n found `{:?}`", expected, found));
}
err.span_help(
type_param_span,
"the lifetime requirements from the `impl` do not correspond to the requirements in \
the `trait`",
);
if visitor.types.is_empty() {
err.help(
"verify the lifetime relationships in the `trait` and `impl` between the `self` \
argument, the other inputs and its output",
);
}
err.emit();
}
}
struct TypeParamSpanVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
types: Vec<Span>,
}
impl Visitor<'tcx> for TypeParamSpanVisitor<'tcx> {
type Map = rustc_middle::hir::map::Map<'tcx>;
fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
hir::intravisit::NestedVisitorMap::OnlyBodies(self.tcx.hir())
}
fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
match arg.kind {
hir::TyKind::Rptr(_, ref mut_ty) => {
// We don't want to suggest looking into borrowing `&T` or `&Self`.
hir::intravisit::walk_ty(self, mut_ty.ty);
return;
}
hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments {
[segment]
if segment
.res
.map(|res| match res {
Res::SelfTy(_, _) | Res::Def(hir::def::DefKind::TyParam, _) => true,
_ => false,
})
.unwrap_or(false) =>
{
self.types.push(path.span);
}
_ => {}
},
_ => {}
}
hir::intravisit::walk_ty(self, arg);
}
}