Skip to content

Commit 4719ae2

Browse files
authored
Unrolled build for rust-lang#116841
Rollup merge of rust-lang#116841 - chenyukang:yukang-suggest-unwrap-expect, r=b-naber Suggest unwrap/expect for let binding type mismatch Found it when investigating rust-lang#116738 I'm not sure whether it's a good style to suggest `unwrap`, seems it's may helpful for newcomers. rust-lang#116738 needs another fix to improve it.
2 parents 98b4a64 + f3d20be commit 4719ae2

10 files changed

+327
-1
lines changed

compiler/rustc_hir_typeck/src/demand.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
5858
|| self.suggest_into(err, expr, expr_ty, expected)
5959
|| self.suggest_floating_point_literal(err, expr, expected)
6060
|| self.suggest_null_ptr_for_literal_zero_given_to_ptr_arg(err, expr, expected)
61-
|| self.suggest_coercing_result_via_try_operator(err, expr, expected, expr_ty);
61+
|| self.suggest_coercing_result_via_try_operator(err, expr, expected, expr_ty)
62+
|| self.suggest_missing_unwrap_expect(err, expr, expected, expr_ty);
6263

6364
if !suggested {
6465
self.note_source_of_type_mismatch_constraint(

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+80
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
66
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
77
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
88
use rustc_hir as hir;
9+
use rustc_hir::def::Res;
910
use rustc_hir::def::{CtorKind, CtorOf, DefKind};
1011
use rustc_hir::lang_items::LangItem;
1112
use rustc_hir::{
@@ -1738,4 +1739,83 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17381739
// If the field is hygienic it must come from the same syntax context.
17391740
&& self.tcx.def_ident_span(field.did).unwrap().normalize_to_macros_2_0().eq_ctxt(span)
17401741
}
1742+
1743+
pub(crate) fn suggest_missing_unwrap_expect(
1744+
&self,
1745+
err: &mut Diagnostic,
1746+
expr: &hir::Expr<'tcx>,
1747+
expected: Ty<'tcx>,
1748+
found: Ty<'tcx>,
1749+
) -> bool {
1750+
let ty::Adt(adt, args) = found.kind() else { return false };
1751+
let ret_ty_matches = |diagnostic_item| {
1752+
if let Some(ret_ty) = self
1753+
.ret_coercion
1754+
.as_ref()
1755+
.map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty()))
1756+
&& let ty::Adt(kind, _) = ret_ty.kind()
1757+
&& self.tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did())
1758+
{
1759+
true
1760+
} else {
1761+
false
1762+
}
1763+
};
1764+
1765+
// don't suggest anything like `Ok(ok_val).unwrap()` , `Some(some_val).unwrap()`,
1766+
// `None.unwrap()` etc.
1767+
let is_ctor = matches!(
1768+
expr.kind,
1769+
hir::ExprKind::Call(
1770+
hir::Expr {
1771+
kind: hir::ExprKind::Path(hir::QPath::Resolved(
1772+
None,
1773+
hir::Path { res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. },
1774+
)),
1775+
..
1776+
},
1777+
..,
1778+
) | hir::ExprKind::Path(hir::QPath::Resolved(
1779+
None,
1780+
hir::Path { res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. },
1781+
)),
1782+
);
1783+
1784+
let (article, kind, variant, sugg_operator) =
1785+
if self.tcx.is_diagnostic_item(sym::Result, adt.did()) {
1786+
("a", "Result", "Err", ret_ty_matches(sym::Result))
1787+
} else if self.tcx.is_diagnostic_item(sym::Option, adt.did()) {
1788+
("an", "Option", "None", ret_ty_matches(sym::Option))
1789+
} else {
1790+
return false;
1791+
};
1792+
if is_ctor || !self.can_coerce(args.type_at(0), expected) {
1793+
return false;
1794+
}
1795+
1796+
let (msg, sugg) = if sugg_operator {
1797+
(
1798+
format!(
1799+
"use the `?` operator to extract the `{found}` value, propagating \
1800+
{article} `{kind}::{variant}` value to the caller"
1801+
),
1802+
"?",
1803+
)
1804+
} else {
1805+
(
1806+
format!(
1807+
"consider using `{kind}::expect` to unwrap the `{found}` value, \
1808+
panicking if the value is {article} `{kind}::{variant}`"
1809+
),
1810+
".expect(\"REASON\")",
1811+
)
1812+
};
1813+
err.span_suggestion_verbose(
1814+
expr.span.shrink_to_hi(),
1815+
msg,
1816+
sugg,
1817+
Applicability::HasPlaceholders,
1818+
);
1819+
return true;
1820+
}
17411821
}

tests/ui/lifetimes/issue-26638.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ LL | fn parse_type(iter: Box<dyn Iterator<Item=&str>+'static>) -> &str { iter.ne
4444
|
4545
= note: expected reference `&str`
4646
found enum `Option<&str>`
47+
help: consider using `Option::expect` to unwrap the `Option<&str>` value, panicking if the value is an `Option::None`
48+
|
49+
LL | fn parse_type(iter: Box<dyn Iterator<Item=&str>+'static>) -> &str { iter.next().expect("REASON") }
50+
| +++++++++++++++++
4751

4852
error[E0061]: this function takes 1 argument but 0 arguments were supplied
4953
--> $DIR/issue-26638.rs:5:47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#![allow(unused, dead_code)]
2+
3+
fn test_unwrap() -> Option<i32> {
4+
let b: Result<i32, ()> = Ok(1);
5+
let v: i32 = b; // return type is not `Result`, we don't suggest ? here
6+
//~^ ERROR mismatched types
7+
Some(v)
8+
}
9+
10+
fn test_unwrap_option() -> Result<i32, ()> {
11+
let b = Some(1);
12+
let v: i32 = b; // return type is not `Option`, we don't suggest ? here
13+
//~^ ERROR mismatched types
14+
Ok(v)
15+
}
16+
17+
fn main() {
18+
let v: i32 = Some(0); //~ ERROR mismatched types
19+
20+
let c = Ok(false);
21+
let v: i32 = c; //~ ERROR mismatched types
22+
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/mismatch-ty-dont-suggest.rs:5:18
3+
|
4+
LL | let v: i32 = b; // return type is not `Result`, we don't suggest ? here
5+
| --- ^ expected `i32`, found `Result<i32, ()>`
6+
| |
7+
| expected due to this
8+
|
9+
= note: expected type `i32`
10+
found enum `Result<i32, ()>`
11+
help: consider using `Result::expect` to unwrap the `Result<i32, ()>` value, panicking if the value is a `Result::Err`
12+
|
13+
LL | let v: i32 = b.expect("REASON"); // return type is not `Result`, we don't suggest ? here
14+
| +++++++++++++++++
15+
16+
error[E0308]: mismatched types
17+
--> $DIR/mismatch-ty-dont-suggest.rs:12:18
18+
|
19+
LL | let v: i32 = b; // return type is not `Option`, we don't suggest ? here
20+
| --- ^ expected `i32`, found `Option<{integer}>`
21+
| |
22+
| expected due to this
23+
|
24+
= note: expected type `i32`
25+
found enum `Option<{integer}>`
26+
help: consider using `Option::expect` to unwrap the `Option<{integer}>` value, panicking if the value is an `Option::None`
27+
|
28+
LL | let v: i32 = b.expect("REASON"); // return type is not `Option`, we don't suggest ? here
29+
| +++++++++++++++++
30+
31+
error[E0308]: mismatched types
32+
--> $DIR/mismatch-ty-dont-suggest.rs:18:18
33+
|
34+
LL | let v: i32 = Some(0);
35+
| --- ^^^^^^^ expected `i32`, found `Option<{integer}>`
36+
| |
37+
| expected due to this
38+
|
39+
= note: expected type `i32`
40+
found enum `Option<{integer}>`
41+
42+
error[E0308]: mismatched types
43+
--> $DIR/mismatch-ty-dont-suggest.rs:21:18
44+
|
45+
LL | let v: i32 = c;
46+
| --- ^ expected `i32`, found `Result<bool, _>`
47+
| |
48+
| expected due to this
49+
|
50+
= note: expected type `i32`
51+
found enum `Result<bool, _>`
52+
53+
error: aborting due to 4 previous errors
54+
55+
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// run-rustfix
2+
#![allow(unused, dead_code)]
3+
4+
fn func() -> Option<i32> {
5+
Some(1)
6+
}
7+
8+
fn test_unwrap() -> Result<i32, ()> {
9+
let b: Result<i32, ()> = Ok(1);
10+
let v: i32 = b?; //~ ERROR mismatched types
11+
Ok(v)
12+
}
13+
14+
fn test_unwrap_option() -> Option<i32> {
15+
let b = Some(1);
16+
let v: i32 = b?; //~ ERROR mismatched types
17+
Some(v)
18+
}
19+
20+
fn main() {
21+
let a = Some(1);
22+
let v: i32 = a.expect("REASON"); //~ ERROR mismatched types
23+
24+
let b: Result<i32, ()> = Ok(1);
25+
let v: i32 = b.expect("REASON"); //~ ERROR mismatched types
26+
27+
let v: i32 = func().expect("REASON"); //~ ERROR mismatched types
28+
29+
let a = None;
30+
let v: i32 = a.expect("REASON"); //~ ERROR mismatched types
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// run-rustfix
2+
#![allow(unused, dead_code)]
3+
4+
fn func() -> Option<i32> {
5+
Some(1)
6+
}
7+
8+
fn test_unwrap() -> Result<i32, ()> {
9+
let b: Result<i32, ()> = Ok(1);
10+
let v: i32 = b; //~ ERROR mismatched types
11+
Ok(v)
12+
}
13+
14+
fn test_unwrap_option() -> Option<i32> {
15+
let b = Some(1);
16+
let v: i32 = b; //~ ERROR mismatched types
17+
Some(v)
18+
}
19+
20+
fn main() {
21+
let a = Some(1);
22+
let v: i32 = a; //~ ERROR mismatched types
23+
24+
let b: Result<i32, ()> = Ok(1);
25+
let v: i32 = b; //~ ERROR mismatched types
26+
27+
let v: i32 = func(); //~ ERROR mismatched types
28+
29+
let a = None;
30+
let v: i32 = a; //~ ERROR mismatched types
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/mismatch-ty-unwrap-expect.rs:10:18
3+
|
4+
LL | let v: i32 = b;
5+
| --- ^ expected `i32`, found `Result<i32, ()>`
6+
| |
7+
| expected due to this
8+
|
9+
= note: expected type `i32`
10+
found enum `Result<i32, ()>`
11+
help: use the `?` operator to extract the `Result<i32, ()>` value, propagating a `Result::Err` value to the caller
12+
|
13+
LL | let v: i32 = b?;
14+
| +
15+
16+
error[E0308]: mismatched types
17+
--> $DIR/mismatch-ty-unwrap-expect.rs:16:18
18+
|
19+
LL | let v: i32 = b;
20+
| --- ^ expected `i32`, found `Option<{integer}>`
21+
| |
22+
| expected due to this
23+
|
24+
= note: expected type `i32`
25+
found enum `Option<{integer}>`
26+
help: use the `?` operator to extract the `Option<{integer}>` value, propagating an `Option::None` value to the caller
27+
|
28+
LL | let v: i32 = b?;
29+
| +
30+
31+
error[E0308]: mismatched types
32+
--> $DIR/mismatch-ty-unwrap-expect.rs:22:18
33+
|
34+
LL | let v: i32 = a;
35+
| --- ^ expected `i32`, found `Option<{integer}>`
36+
| |
37+
| expected due to this
38+
|
39+
= note: expected type `i32`
40+
found enum `Option<{integer}>`
41+
help: consider using `Option::expect` to unwrap the `Option<{integer}>` value, panicking if the value is an `Option::None`
42+
|
43+
LL | let v: i32 = a.expect("REASON");
44+
| +++++++++++++++++
45+
46+
error[E0308]: mismatched types
47+
--> $DIR/mismatch-ty-unwrap-expect.rs:25:18
48+
|
49+
LL | let v: i32 = b;
50+
| --- ^ expected `i32`, found `Result<i32, ()>`
51+
| |
52+
| expected due to this
53+
|
54+
= note: expected type `i32`
55+
found enum `Result<i32, ()>`
56+
help: consider using `Result::expect` to unwrap the `Result<i32, ()>` value, panicking if the value is a `Result::Err`
57+
|
58+
LL | let v: i32 = b.expect("REASON");
59+
| +++++++++++++++++
60+
61+
error[E0308]: mismatched types
62+
--> $DIR/mismatch-ty-unwrap-expect.rs:27:18
63+
|
64+
LL | let v: i32 = func();
65+
| --- ^^^^^^ expected `i32`, found `Option<i32>`
66+
| |
67+
| expected due to this
68+
|
69+
= note: expected type `i32`
70+
found enum `Option<i32>`
71+
help: consider using `Option::expect` to unwrap the `Option<i32>` value, panicking if the value is an `Option::None`
72+
|
73+
LL | let v: i32 = func().expect("REASON");
74+
| +++++++++++++++++
75+
76+
error[E0308]: mismatched types
77+
--> $DIR/mismatch-ty-unwrap-expect.rs:30:18
78+
|
79+
LL | let v: i32 = a;
80+
| --- ^ expected `i32`, found `Option<_>`
81+
| |
82+
| expected due to this
83+
|
84+
= note: expected type `i32`
85+
found enum `Option<_>`
86+
help: consider using `Option::expect` to unwrap the `Option<_>` value, panicking if the value is an `Option::None`
87+
|
88+
LL | let v: i32 = a.expect("REASON");
89+
| +++++++++++++++++
90+
91+
error: aborting due to 6 previous errors
92+
93+
For more information about this error, try `rustc --explain E0308`.

tests/ui/noexporttypeexe.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ LL | let x: isize = noexporttypelib::foo();
88
|
99
= note: expected type `isize`
1010
found enum `Option<isize>`
11+
help: consider using `Option::expect` to unwrap the `Option<isize>` value, panicking if the value is an `Option::None`
12+
|
13+
LL | let x: isize = noexporttypelib::foo().expect("REASON");
14+
| +++++++++++++++++
1115

1216
error: aborting due to previous error
1317

tests/ui/typeck/tag-that-dare-not-speak-its-name.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ LL | let x : char = last(y);
88
|
99
= note: expected type `char`
1010
found enum `Option<_>`
11+
help: consider using `Option::expect` to unwrap the `Option<_>` value, panicking if the value is an `Option::None`
12+
|
13+
LL | let x : char = last(y).expect("REASON");
14+
| +++++++++++++++++
1115

1216
error: aborting due to previous error
1317

0 commit comments

Comments
 (0)