Skip to content

Commit ab21557

Browse files
committed
Auto merge of #66682 - estebank:fn-type-err, r=davidtwco
Highlight parts of fn in type errors When a type error arises between two fn items, fn pointers or tuples, highlight only the differing parts of each. Examples: <img width="699" alt="" src="https://user-images.githubusercontent.com/1606434/69487597-ab561600-0e11-11ea-9b4e-d4fd9e91d5dc.png"> <img width="528" alt="" src="https://user-images.githubusercontent.com/1606434/69487207-9033d800-0e0a-11ea-93e3-8c4d002411a5.png"> <img width="468" alt="" src="https://user-images.githubusercontent.com/1606434/69487208-9033d800-0e0a-11ea-92e3-2b2cee120335.png"> <img width="775" alt="" src="https://user-images.githubusercontent.com/1606434/69487209-9033d800-0e0a-11ea-9e68-7f6ed5c8cb08.png">
2 parents 582a4ea + 9d7774c commit ab21557

16 files changed

+238
-45
lines changed

src/librustc/infer/error_reporting/mod.rs

+174-3
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ use crate::ty::{self, subst::{Subst, SubstsRef}, Region, Ty, TyCtxt, TypeFoldabl
6363

6464
use errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
6565
use rustc_error_codes::*;
66+
use rustc_target::spec::abi;
6667
use syntax_pos::{Pos, Span};
67-
6868
use std::{cmp, fmt};
6969

7070
mod note;
@@ -766,7 +766,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
766766
if len > 0 && i != len - 1 {
767767
value.push_normal(", ");
768768
}
769-
//self.push_comma(&mut value, &mut other_value, len, i);
770769
}
771770
if len > 0 {
772771
value.push_highlighted(">");
@@ -868,6 +867,120 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
868867
substs.truncate_to(self.tcx, &generics)
869868
}
870869

870+
/// Given two `fn` signatures highlight only sub-parts that are different.
871+
fn cmp_fn_sig(
872+
&self,
873+
sig1: &ty::PolyFnSig<'tcx>,
874+
sig2: &ty::PolyFnSig<'tcx>,
875+
) -> (DiagnosticStyledString, DiagnosticStyledString) {
876+
let get_lifetimes = |sig| {
877+
use crate::hir::def::Namespace;
878+
let mut s = String::new();
879+
let (_, (sig, reg)) = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS)
880+
.name_all_regions(sig)
881+
.unwrap();
882+
let lts: Vec<String> = reg.into_iter().map(|(_, kind)| kind.to_string()).collect();
883+
(if lts.is_empty() {
884+
String::new()
885+
} else {
886+
format!("for<{}> ", lts.join(", "))
887+
}, sig)
888+
};
889+
890+
let (lt1, sig1) = get_lifetimes(sig1);
891+
let (lt2, sig2) = get_lifetimes(sig2);
892+
893+
// unsafe extern "C" for<'a> fn(&'a T) -> &'a T
894+
let mut values = (
895+
DiagnosticStyledString::normal("".to_string()),
896+
DiagnosticStyledString::normal("".to_string()),
897+
);
898+
899+
// unsafe extern "C" for<'a> fn(&'a T) -> &'a T
900+
// ^^^^^^
901+
values.0.push(sig1.unsafety.prefix_str(), sig1.unsafety != sig2.unsafety);
902+
values.1.push(sig2.unsafety.prefix_str(), sig1.unsafety != sig2.unsafety);
903+
904+
// unsafe extern "C" for<'a> fn(&'a T) -> &'a T
905+
// ^^^^^^^^^^
906+
if sig1.abi != abi::Abi::Rust {
907+
values.0.push(format!("extern {} ", sig1.abi), sig1.abi != sig2.abi);
908+
}
909+
if sig2.abi != abi::Abi::Rust {
910+
values.1.push(format!("extern {} ", sig2.abi), sig1.abi != sig2.abi);
911+
}
912+
913+
// unsafe extern "C" for<'a> fn(&'a T) -> &'a T
914+
// ^^^^^^^^
915+
let lifetime_diff = lt1 != lt2;
916+
values.0.push(lt1, lifetime_diff);
917+
values.1.push(lt2, lifetime_diff);
918+
919+
// unsafe extern "C" for<'a> fn(&'a T) -> &'a T
920+
// ^^^
921+
values.0.push_normal("fn(");
922+
values.1.push_normal("fn(");
923+
924+
// unsafe extern "C" for<'a> fn(&'a T) -> &'a T
925+
// ^^^^^
926+
let len1 = sig1.inputs().len();
927+
let len2 = sig2.inputs().len();
928+
if len1 == len2 {
929+
for (i, (l, r)) in sig1.inputs().iter().zip(sig2.inputs().iter()).enumerate() {
930+
let (x1, x2) = self.cmp(l, r);
931+
(values.0).0.extend(x1.0);
932+
(values.1).0.extend(x2.0);
933+
self.push_comma(&mut values.0, &mut values.1, len1, i);
934+
}
935+
} else {
936+
for (i, l) in sig1.inputs().iter().enumerate() {
937+
values.0.push_highlighted(l.to_string());
938+
if i != len1 - 1 {
939+
values.0.push_highlighted(", ");
940+
}
941+
}
942+
for (i, r) in sig2.inputs().iter().enumerate() {
943+
values.1.push_highlighted(r.to_string());
944+
if i != len2 - 1 {
945+
values.1.push_highlighted(", ");
946+
}
947+
}
948+
}
949+
950+
if sig1.c_variadic {
951+
if len1 > 0 {
952+
values.0.push_normal(", ");
953+
}
954+
values.0.push("...", !sig2.c_variadic);
955+
}
956+
if sig2.c_variadic {
957+
if len2 > 0 {
958+
values.1.push_normal(", ");
959+
}
960+
values.1.push("...", !sig1.c_variadic);
961+
}
962+
963+
// unsafe extern "C" for<'a> fn(&'a T) -> &'a T
964+
// ^
965+
values.0.push_normal(")");
966+
values.1.push_normal(")");
967+
968+
// unsafe extern "C" for<'a> fn(&'a T) -> &'a T
969+
// ^^^^^^^^
970+
let output1 = sig1.output();
971+
let output2 = sig2.output();
972+
let (x1, x2) = self.cmp(output1, output2);
973+
if !output1.is_unit() {
974+
values.0.push_normal(" -> ");
975+
(values.0).0.extend(x1.0);
976+
}
977+
if !output2.is_unit() {
978+
values.1.push_normal(" -> ");
979+
(values.1).0.extend(x2.0);
980+
}
981+
values
982+
}
983+
871984
/// Compares two given types, eliding parts that are the same between them and highlighting
872985
/// relevant differences, and return two representation of those types for highlighted printing.
873986
fn cmp(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> (DiagnosticStyledString, DiagnosticStyledString) {
@@ -968,7 +1081,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
9681081
for (i, lifetimes) in lifetimes.enumerate() {
9691082
let l1 = lifetime_display(lifetimes.0);
9701083
let l2 = lifetime_display(lifetimes.1);
971-
if l1 == l2 {
1084+
if lifetimes.0 == lifetimes.1 {
9721085
values.0.push_normal("'_");
9731086
values.1.push_normal("'_");
9741087
} else {
@@ -1124,6 +1237,64 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
11241237
values
11251238
}
11261239

1240+
// When encountering tuples of the same size, highlight only the differing types
1241+
(&ty::Tuple(substs1), &ty::Tuple(substs2)) if substs1.len() == substs2.len() => {
1242+
let mut values = (
1243+
DiagnosticStyledString::normal("("),
1244+
DiagnosticStyledString::normal("("),
1245+
);
1246+
let len = substs1.len();
1247+
for (i, (left, right)) in substs1.types().zip(substs2.types()).enumerate() {
1248+
let (x1, x2) = self.cmp(left, right);
1249+
(values.0).0.extend(x1.0);
1250+
(values.1).0.extend(x2.0);
1251+
self.push_comma(&mut values.0, &mut values.1, len, i);
1252+
}
1253+
if len == 1 { // Keep the output for single element tuples as `(ty,)`.
1254+
values.0.push_normal(",");
1255+
values.1.push_normal(",");
1256+
}
1257+
values.0.push_normal(")");
1258+
values.1.push_normal(")");
1259+
values
1260+
}
1261+
1262+
(ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
1263+
let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
1264+
let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2);
1265+
let mut values = self.cmp_fn_sig(&sig1, &sig2);
1266+
let path1 = format!(" {{{}}}", self.tcx.def_path_str_with_substs(*did1, substs1));
1267+
let path2 = format!(" {{{}}}", self.tcx.def_path_str_with_substs(*did2, substs2));
1268+
let same_path = path1 == path2;
1269+
values.0.push(path1, !same_path);
1270+
values.1.push(path2, !same_path);
1271+
values
1272+
}
1273+
1274+
(ty::FnDef(did1, substs1), ty::FnPtr(sig2)) => {
1275+
let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
1276+
let mut values = self.cmp_fn_sig(&sig1, sig2);
1277+
values.0.push_normal(format!(
1278+
" {{{}}}",
1279+
self.tcx.def_path_str_with_substs(*did1, substs1)),
1280+
);
1281+
values
1282+
}
1283+
1284+
(ty::FnPtr(sig1), ty::FnDef(did2, substs2)) => {
1285+
let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2);
1286+
let mut values = self.cmp_fn_sig(sig1, &sig2);
1287+
values.1.push_normal(format!(
1288+
" {{{}}}",
1289+
self.tcx.def_path_str_with_substs(*did2, substs2)),
1290+
);
1291+
values
1292+
}
1293+
1294+
(ty::FnPtr(sig1), ty::FnPtr(sig2)) => {
1295+
self.cmp_fn_sig(sig1, sig2)
1296+
}
1297+
11271298
_ => {
11281299
if t1 == t2 {
11291300
// The two types are the same, elide and don't highlight.

src/librustc/ty/print/pretty.rs

+23-8
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use syntax::attr::{SignedInt, UnsignedInt};
1717
use syntax::symbol::{kw, Symbol};
1818

1919
use std::cell::Cell;
20+
use std::collections::BTreeMap;
2021
use std::fmt::{self, Write as _};
2122
use std::ops::{Deref, DerefMut};
2223

@@ -1081,7 +1082,7 @@ impl<F> FmtPrinter<'a, 'tcx, F> {
10811082
}
10821083
}
10831084

1084-
impl TyCtxt<'_> {
1085+
impl TyCtxt<'t> {
10851086
// HACK(eddyb) get rid of `def_path_str` and/or pass `Namespace` explicitly always
10861087
// (but also some things just print a `DefId` generally so maybe we need this?)
10871088
fn guess_def_namespace(self, def_id: DefId) -> Namespace {
@@ -1104,11 +1105,14 @@ impl TyCtxt<'_> {
11041105
/// Returns a string identifying this `DefId`. This string is
11051106
/// suitable for user output.
11061107
pub fn def_path_str(self, def_id: DefId) -> String {
1108+
self.def_path_str_with_substs(def_id, &[])
1109+
}
1110+
1111+
pub fn def_path_str_with_substs(self, def_id: DefId, substs: &'t [GenericArg<'t>]) -> String {
11071112
let ns = self.guess_def_namespace(def_id);
11081113
debug!("def_path_str: def_id={:?}, ns={:?}", def_id, ns);
11091114
let mut s = String::new();
1110-
let _ = FmtPrinter::new(self, &mut s, ns)
1111-
.print_def_path(def_id, &[]);
1115+
let _ = FmtPrinter::new(self, &mut s, ns).print_def_path(def_id, substs);
11121116
s
11131117
}
11141118
}
@@ -1521,7 +1525,10 @@ impl<F: fmt::Write> FmtPrinter<'_, '_, F> {
15211525
// HACK(eddyb) limited to `FmtPrinter` because of `binder_depth`,
15221526
// `region_index` and `used_region_names`.
15231527
impl<F: fmt::Write> FmtPrinter<'_, 'tcx, F> {
1524-
pub fn pretty_in_binder<T>(mut self, value: &ty::Binder<T>) -> Result<Self, fmt::Error>
1528+
pub fn name_all_regions<T>(
1529+
mut self,
1530+
value: &ty::Binder<T>,
1531+
) -> Result<(Self, (T, BTreeMap<ty::BoundRegion, ty::Region<'tcx>>)), fmt::Error>
15251532
where
15261533
T: Print<'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<'tcx>,
15271534
{
@@ -1554,8 +1561,7 @@ impl<F: fmt::Write> FmtPrinter<'_, 'tcx, F> {
15541561

15551562
define_scoped_cx!(self);
15561563

1557-
let old_region_index = self.region_index;
1558-
let mut region_index = old_region_index;
1564+
let mut region_index = self.region_index;
15591565
let new_value = self.tcx.replace_late_bound_regions(value, |br| {
15601566
let _ = start_or_continue(&mut self, "for<", ", ");
15611567
let br = match br {
@@ -1577,12 +1583,21 @@ impl<F: fmt::Write> FmtPrinter<'_, 'tcx, F> {
15771583
}
15781584
};
15791585
self.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br))
1580-
}).0;
1586+
});
15811587
start_or_continue(&mut self, "", "> ")?;
15821588

15831589
self.binder_depth += 1;
15841590
self.region_index = region_index;
1585-
let mut inner = new_value.print(self)?;
1591+
Ok((self, new_value))
1592+
}
1593+
1594+
pub fn pretty_in_binder<T>(self, value: &ty::Binder<T>) -> Result<Self, fmt::Error>
1595+
where
1596+
T: Print<'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<'tcx>,
1597+
{
1598+
let old_region_index = self.region_index;
1599+
let (new, new_value) = self.name_all_regions(value)?;
1600+
let mut inner = new_value.0.print(new)?;
15861601
inner.region_index = old_region_index;
15871602
inner.binder_depth -= 1;
15881603
Ok(inner)

src/librustc_errors/diagnostic.rs

+7
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ impl DiagnosticStyledString {
5252
pub fn push_highlighted<S: Into<String>>(&mut self, t: S) {
5353
self.0.push(StringPart::Highlighted(t.into()));
5454
}
55+
pub fn push<S: Into<String>>(&mut self, t: S, highlight: bool) {
56+
if highlight {
57+
self.push_highlighted(t);
58+
} else {
59+
self.push_normal(t);
60+
}
61+
}
5562
pub fn normal<S: Into<String>>(t: S) -> DiagnosticStyledString {
5663
DiagnosticStyledString(vec![StringPart::Normal(t.into())])
5764
}

src/test/ui/borrowck/regions-bound-missing-bound-in-impl.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ error[E0308]: method not compatible with trait
2222
LL | fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
2323
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
2424
|
25-
= note: expected fn pointer `fn(&'a isize, Inv<'c>, Inv<'c>, Inv<'d>)`
26-
found fn pointer `fn(&'a isize, Inv<'_>, Inv<'c>, Inv<'d>)`
25+
= note: expected fn pointer `fn(&'a isize, Inv<'c>, Inv<'c>, Inv<'_>)`
26+
found fn pointer `fn(&'a isize, Inv<'_>, Inv<'c>, Inv<'_>)`
2727
note: the lifetime `'c` as defined on the method body at 27:24...
2828
--> $DIR/regions-bound-missing-bound-in-impl.rs:27:24
2929
|

src/test/ui/c-variadic/variadic-ffi-1.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ LL | let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
3030
| |
3131
| expected due to this
3232
|
33-
= note: expected fn pointer `unsafe extern "C" fn(isize, u8)`
34-
found fn item `unsafe extern "C" fn(isize, u8, ...) {foo}`
33+
= note: expected fn pointer `unsafe extern "C" fn(_, _)`
34+
found fn item `unsafe extern "C" fn(_, _, ...) {foo}`
3535

3636
error[E0308]: mismatched types
3737
--> $DIR/variadic-ffi-1.rs:20:54
@@ -41,8 +41,8 @@ LL | let y: extern "C" fn(f: isize, x: u8, ...) = bar;
4141
| |
4242
| expected due to this
4343
|
44-
= note: expected fn pointer `extern "C" fn(isize, u8, ...)`
45-
found fn item `extern "C" fn(isize, u8) {bar}`
44+
= note: expected fn pointer `extern "C" fn(_, _, ...)`
45+
found fn item `extern "C" fn(_, _) {bar}`
4646

4747
error[E0617]: can't pass `f32` to variadic function
4848
--> $DIR/variadic-ffi-1.rs:22:19

src/test/ui/const-generics/fn-const-param-infer.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ error[E0308]: mismatched types
2323
LL | let _ = Checked::<{generic_arg::<u32>}>;
2424
| ^^^^^^^^^^^^^^^^^^ expected `usize`, found `u32`
2525
|
26-
= note: expected fn pointer `fn(usize) -> bool`
27-
found fn item `fn(u32) -> bool {generic_arg::<u32>}`
26+
= note: expected fn pointer `fn(usize) -> _`
27+
found fn item `fn(u32) -> _ {generic_arg::<u32>}`
2828

2929
error[E0282]: type annotations needed
3030
--> $DIR/fn-const-param-infer.rs:22:23

src/test/ui/fn/fn-item-type.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ impl<T> Foo for T { /* `foo` is still default here */ }
1212
fn main() {
1313
eq(foo::<u8>, bar::<u8>);
1414
//~^ ERROR mismatched types
15-
//~| expected fn item `fn(isize) -> isize {foo::<u8>}`
16-
//~| found fn item `fn(isize) -> isize {bar::<u8>}`
15+
//~| expected fn item `fn(_) -> _ {foo::<u8>}`
16+
//~| found fn item `fn(_) -> _ {bar::<u8>}`
1717
//~| expected fn item, found a different fn item
1818

1919
eq(foo::<u8>, foo::<i8>);
@@ -22,8 +22,8 @@ fn main() {
2222

2323
eq(bar::<String>, bar::<Vec<u8>>);
2424
//~^ ERROR mismatched types
25-
//~| expected fn item `fn(isize) -> isize {bar::<std::string::String>}`
26-
//~| found fn item `fn(isize) -> isize {bar::<std::vec::Vec<u8>>}`
25+
//~| expected fn item `fn(_) -> _ {bar::<std::string::String>}`
26+
//~| found fn item `fn(_) -> _ {bar::<std::vec::Vec<u8>>}`
2727
//~| expected struct `std::string::String`, found struct `std::vec::Vec`
2828

2929
// Make sure we distinguish between trait methods correctly.

0 commit comments

Comments
 (0)