Skip to content

Commit 7155690

Browse files
committed
Auto merge of #57158 - estebank:as-ref, r=zackmdavis
Suggest `.as_ref()` when appropriate for `Option` and `Result` Fix #55198.
2 parents 0e6f898 + 0ecc128 commit 7155690

File tree

5 files changed

+172
-61
lines changed

5 files changed

+172
-61
lines changed

src/librustc/infer/error_reporting/mod.rs

+67
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
956956
diag.span_label(span, message);
957957
}
958958
}
959+
self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
959960
}
960961

961962
diag.note_expected_found(&"type", expected, found);
@@ -972,6 +973,72 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
972973
self.note_error_origin(diag, &cause);
973974
}
974975

976+
/// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
977+
/// suggest it.
978+
fn suggest_as_ref_where_appropriate(
979+
&self,
980+
span: Span,
981+
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
982+
diag: &mut DiagnosticBuilder<'tcx>,
983+
) {
984+
match (&exp_found.expected.sty, &exp_found.found.sty) {
985+
(TyKind::Adt(exp_def, exp_substs), TyKind::Ref(_, found_ty, _)) => {
986+
if let TyKind::Adt(found_def, found_substs) = found_ty.sty {
987+
let path_str = format!("{:?}", exp_def);
988+
if exp_def == &found_def {
989+
let opt_msg = "you can convert from `&Option<T>` to `Option<&T>` using \
990+
`.as_ref()`";
991+
let result_msg = "you can convert from `&Result<T, E>` to \
992+
`Result<&T, &E>` using `.as_ref()`";
993+
let have_as_ref = &[
994+
("std::option::Option", opt_msg),
995+
("core::option::Option", opt_msg),
996+
("std::result::Result", result_msg),
997+
("core::result::Result", result_msg),
998+
];
999+
if let Some(msg) = have_as_ref.iter()
1000+
.filter_map(|(path, msg)| if &path_str == path {
1001+
Some(msg)
1002+
} else {
1003+
None
1004+
}).next()
1005+
{
1006+
let mut show_suggestion = true;
1007+
for (exp_ty, found_ty) in exp_substs.types().zip(found_substs.types()) {
1008+
match exp_ty.sty {
1009+
TyKind::Ref(_, exp_ty, _) => {
1010+
match (&exp_ty.sty, &found_ty.sty) {
1011+
(_, TyKind::Param(_)) |
1012+
(_, TyKind::Infer(_)) |
1013+
(TyKind::Param(_), _) |
1014+
(TyKind::Infer(_), _) => {}
1015+
_ if ty::TyS::same_type(exp_ty, found_ty) => {}
1016+
_ => show_suggestion = false,
1017+
};
1018+
}
1019+
TyKind::Param(_) | TyKind::Infer(_) => {}
1020+
_ => show_suggestion = false,
1021+
}
1022+
}
1023+
if let (Ok(snippet), true) = (
1024+
self.tcx.sess.source_map().span_to_snippet(span),
1025+
show_suggestion,
1026+
) {
1027+
diag.span_suggestion_with_applicability(
1028+
span,
1029+
msg,
1030+
format!("{}.as_ref()", snippet),
1031+
Applicability::MachineApplicable,
1032+
);
1033+
}
1034+
}
1035+
}
1036+
}
1037+
}
1038+
_ => {}
1039+
}
1040+
}
1041+
9751042
pub fn report_and_explain_type_error(
9761043
&self,
9771044
trace: TypeTrace<'tcx>,

src/librustc/ty/util.rs

+14-14
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,19 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
658658
tcx.needs_drop_raw(param_env.and(self))
659659
}
660660

661+
pub fn same_type(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
662+
match (&a.sty, &b.sty) {
663+
(&Adt(did_a, substs_a), &Adt(did_b, substs_b)) => {
664+
if did_a != did_b {
665+
return false;
666+
}
667+
668+
substs_a.types().zip(substs_b.types()).all(|(a, b)| Self::same_type(a, b))
669+
}
670+
_ => a == b,
671+
}
672+
}
673+
661674
/// Check whether a type is representable. This means it cannot contain unboxed
662675
/// structural recursion. This check is needed for structs and enums.
663676
pub fn is_representable(&'tcx self,
@@ -730,19 +743,6 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
730743
}
731744
}
732745

733-
fn same_type<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
734-
match (&a.sty, &b.sty) {
735-
(&Adt(did_a, substs_a), &Adt(did_b, substs_b)) => {
736-
if did_a != did_b {
737-
return false;
738-
}
739-
740-
substs_a.types().zip(substs_b.types()).all(|(a, b)| same_type(a, b))
741-
}
742-
_ => a == b,
743-
}
744-
}
745-
746746
// Does the type `ty` directly (without indirection through a pointer)
747747
// contain any types on stack `seen`?
748748
fn is_type_structurally_recursive<'a, 'tcx>(
@@ -807,7 +807,7 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
807807
// struct Foo { Option<Option<Foo>> }
808808

809809
for &seen_type in iter {
810-
if same_type(ty, seen_type) {
810+
if ty::TyS::same_type(ty, seen_type) {
811811
debug!("ContainsRecursive: {:?} contains {:?}",
812812
seen_type,
813813
ty);

src/test/ui/as-ref.stderr

-47
This file was deleted.

src/test/ui/as-ref.rs renamed to src/test/ui/suggestions/as-ref.rs

+10
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,14 @@ fn main() {
1212
//~^ ERROR mismatched types [E0308]
1313
opt.and_then(|arg| Ok(takes_ref(arg)));
1414
//~^ ERROR mismatched types [E0308]
15+
let x: &Option<usize> = &Some(3);
16+
let y: Option<&usize> = x;
17+
//~^ ERROR mismatched types [E0308]
18+
let x: &Result<usize, usize> = &Ok(3);
19+
let y: Result<&usize, &usize> = x;
20+
//~^ ERROR mismatched types [E0308]
21+
// note: do not suggest because of `E: usize`
22+
let x: &Result<usize, usize> = &Ok(3);
23+
let y: Result<&usize, usize> = x;
24+
//~^ ERROR mismatched types [E0308]
1525
}

src/test/ui/suggestions/as-ref.stderr

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/as-ref.rs:6:27
3+
|
4+
LL | opt.map(|arg| takes_ref(arg));
5+
| - ^^^ expected &Foo, found struct `Foo`
6+
| |
7+
| help: consider using `as_ref` instead: `as_ref().`
8+
|
9+
= note: expected type `&Foo`
10+
found type `Foo`
11+
12+
error[E0308]: mismatched types
13+
--> $DIR/as-ref.rs:8:37
14+
|
15+
LL | opt.and_then(|arg| Some(takes_ref(arg)));
16+
| - ^^^ expected &Foo, found struct `Foo`
17+
| |
18+
| help: consider using `as_ref` instead: `as_ref().`
19+
|
20+
= note: expected type `&Foo`
21+
found type `Foo`
22+
23+
error[E0308]: mismatched types
24+
--> $DIR/as-ref.rs:11:27
25+
|
26+
LL | opt.map(|arg| takes_ref(arg));
27+
| - ^^^ expected &Foo, found struct `Foo`
28+
| |
29+
| help: consider using `as_ref` instead: `as_ref().`
30+
|
31+
= note: expected type `&Foo`
32+
found type `Foo`
33+
34+
error[E0308]: mismatched types
35+
--> $DIR/as-ref.rs:13:35
36+
|
37+
LL | opt.and_then(|arg| Ok(takes_ref(arg)));
38+
| - ^^^ expected &Foo, found struct `Foo`
39+
| |
40+
| help: consider using `as_ref` instead: `as_ref().`
41+
|
42+
= note: expected type `&Foo`
43+
found type `Foo`
44+
45+
error[E0308]: mismatched types
46+
--> $DIR/as-ref.rs:16:27
47+
|
48+
LL | let y: Option<&usize> = x;
49+
| ^
50+
| |
51+
| expected enum `std::option::Option`, found reference
52+
| help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `x.as_ref()`
53+
|
54+
= note: expected type `std::option::Option<&usize>`
55+
found type `&std::option::Option<usize>`
56+
57+
error[E0308]: mismatched types
58+
--> $DIR/as-ref.rs:19:35
59+
|
60+
LL | let y: Result<&usize, &usize> = x;
61+
| ^ expected enum `std::result::Result`, found reference
62+
|
63+
= note: expected type `std::result::Result<&usize, &usize>`
64+
found type `&std::result::Result<usize, usize>`
65+
help: you can convert from `&Result<T, E>` to `Result<&T, &E>` using `.as_ref()`
66+
|
67+
LL | let y: Result<&usize, &usize> = x.as_ref();
68+
| ^^^^^^^^^^
69+
70+
error[E0308]: mismatched types
71+
--> $DIR/as-ref.rs:23:34
72+
|
73+
LL | let y: Result<&usize, usize> = x;
74+
| ^ expected enum `std::result::Result`, found reference
75+
|
76+
= note: expected type `std::result::Result<&usize, usize>`
77+
found type `&std::result::Result<usize, usize>`
78+
79+
error: aborting due to 7 previous errors
80+
81+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)