Skip to content

Commit 5379a7d

Browse files
authored
Rollup merge of rust-lang#63337 - estebank:break-ee0308, r=Centril
Tweak mismatched types error - Change expected/found for type mismatches in `break` - Be more accurate when talking about diverging match arms - Tweak wording of function without a return value - Suggest calling bare functions when their return value can be coerced to the expected type - Give more parsing errors when encountering `foo(_, _, _)` Fix rust-lang#51767, fix rust-lang#62677, fix rust-lang#63136, cc rust-lang#37384, cc rust-lang#35241, cc rust-lang#51669.
2 parents a14318d + 45a5bc7 commit 5379a7d

36 files changed

+701
-109
lines changed

src/librustc/hir/map/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ impl<'hir> Map<'hir> {
287287
self.definitions.def_index_to_hir_id(def_id.to_def_id().index)
288288
}
289289

290-
fn def_kind(&self, hir_id: HirId) -> Option<DefKind> {
290+
pub fn def_kind(&self, hir_id: HirId) -> Option<DefKind> {
291291
let node = if let Some(node) = self.find(hir_id) {
292292
node
293293
} else {

src/librustc/infer/error_reporting/mod.rs

+10-28
Original file line numberDiff line numberDiff line change
@@ -662,19 +662,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
662662
}
663663
}
664664
_ => {
665+
// `last_ty` can be `!`, `expected` will have better info when present.
666+
let t = self.resolve_vars_if_possible(&match exp_found {
667+
Some(ty::error::ExpectedFound { expected, .. }) => expected,
668+
_ => last_ty,
669+
});
665670
let msg = "`match` arms have incompatible types";
666671
err.span_label(cause.span, msg);
667672
if prior_arms.len() <= 4 {
668673
for sp in prior_arms {
669-
err.span_label(*sp, format!(
670-
"this is found to be of type `{}`",
671-
self.resolve_vars_if_possible(&last_ty),
672-
));
674+
err.span_label( *sp, format!("this is found to be of type `{}`", t));
673675
}
674676
} else if let Some(sp) = prior_arms.last() {
675-
err.span_label(*sp, format!(
676-
"this and all prior arms are found to be of type `{}`", last_ty,
677-
));
677+
err.span_label(
678+
*sp,
679+
format!("this and all prior arms are found to be of type `{}`", t),
680+
);
678681
}
679682
}
680683
},
@@ -1143,27 +1146,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
11431146
}
11441147
(_, false, _) => {
11451148
if let Some(exp_found) = exp_found {
1146-
let (def_id, ret_ty) = match exp_found.found.sty {
1147-
ty::FnDef(def, _) => {
1148-
(Some(def), Some(self.tcx.fn_sig(def).output()))
1149-
}
1150-
_ => (None, None),
1151-
};
1152-
1153-
let exp_is_struct = match exp_found.expected.sty {
1154-
ty::Adt(def, _) => def.is_struct(),
1155-
_ => false,
1156-
};
1157-
1158-
if let (Some(def_id), Some(ret_ty)) = (def_id, ret_ty) {
1159-
if exp_is_struct && &exp_found.expected == ret_ty.skip_binder() {
1160-
let message = format!(
1161-
"did you mean `{}(/* fields */)`?",
1162-
self.tcx.def_path_str(def_id)
1163-
);
1164-
diag.span_label(span, message);
1165-
}
1166-
}
11671149
self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
11681150
}
11691151

src/librustc_typeck/check/expr.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
548548
coerce.coerce(self, &cause, e, e_ty);
549549
} else {
550550
assert!(e_ty.is_unit());
551-
coerce.coerce_forced_unit(self, &cause, &mut |_| (), true);
551+
let ty = coerce.expected_ty();
552+
coerce.coerce_forced_unit(self, &cause, &mut |err| {
553+
let val = match ty.sty {
554+
ty::Bool => "true",
555+
ty::Char => "'a'",
556+
ty::Int(_) | ty::Uint(_) => "42",
557+
ty::Float(_) => "3.14159",
558+
ty::Error | ty::Never => return,
559+
_ => "value",
560+
};
561+
let msg = "give it a value of the expected type";
562+
let label = destination.label
563+
.map(|l| format!(" {}", l.ident))
564+
.unwrap_or_else(String::new);
565+
let sugg = format!("break{} {}", label, val);
566+
err.span_suggestion(expr.span, msg, sugg, Applicability::HasPlaceholders);
567+
}, false);
552568
}
553569
} else {
554570
// If `ctxt.coerce` is `None`, we can just ignore

src/librustc_typeck/check/mod.rs

+108-1
Original file line numberDiff line numberDiff line change
@@ -3709,7 +3709,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
37093709
self.consider_hint_about_removing_semicolon(blk, expected_ty, err);
37103710
}
37113711
if let Some(fn_span) = fn_span {
3712-
err.span_label(fn_span, "this function's body doesn't return");
3712+
err.span_label(
3713+
fn_span,
3714+
"implicitly returns `()` as its body has no tail or `return` \
3715+
expression",
3716+
);
37133717
}
37143718
}, false);
37153719
}
@@ -3819,6 +3823,101 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
38193823
pointing_at_return_type
38203824
}
38213825

3826+
/// When encountering an fn-like ctor that needs to unify with a value, check whether calling
3827+
/// the ctor would successfully solve the type mismatch and if so, suggest it:
3828+
/// ```
3829+
/// fn foo(x: usize) -> usize { x }
3830+
/// let x: usize = foo; // suggest calling the `foo` function: `foo(42)`
3831+
/// ```
3832+
fn suggest_fn_call(
3833+
&self,
3834+
err: &mut DiagnosticBuilder<'tcx>,
3835+
expr: &hir::Expr,
3836+
expected: Ty<'tcx>,
3837+
found: Ty<'tcx>,
3838+
) -> bool {
3839+
match found.sty {
3840+
ty::FnDef(..) | ty::FnPtr(_) => {}
3841+
_ => return false,
3842+
}
3843+
let hir = self.tcx.hir();
3844+
3845+
let sig = found.fn_sig(self.tcx);
3846+
let sig = self
3847+
.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, &sig)
3848+
.0;
3849+
let sig = self.normalize_associated_types_in(expr.span, &sig);
3850+
if let Ok(_) = self.try_coerce(expr, sig.output(), expected, AllowTwoPhase::No) {
3851+
let (mut sugg_call, applicability) = if sig.inputs().is_empty() {
3852+
(String::new(), Applicability::MachineApplicable)
3853+
} else {
3854+
("...".to_string(), Applicability::HasPlaceholders)
3855+
};
3856+
let mut msg = "call this function";
3857+
if let ty::FnDef(def_id, ..) = found.sty {
3858+
match hir.get_if_local(def_id) {
3859+
Some(Node::Item(hir::Item {
3860+
node: ItemKind::Fn(.., body_id),
3861+
..
3862+
})) |
3863+
Some(Node::ImplItem(hir::ImplItem {
3864+
node: hir::ImplItemKind::Method(_, body_id),
3865+
..
3866+
})) |
3867+
Some(Node::TraitItem(hir::TraitItem {
3868+
node: hir::TraitItemKind::Method(.., hir::TraitMethod::Provided(body_id)),
3869+
..
3870+
})) => {
3871+
let body = hir.body(*body_id);
3872+
sugg_call = body.arguments.iter()
3873+
.map(|arg| match &arg.pat.node {
3874+
hir::PatKind::Binding(_, _, ident, None)
3875+
if ident.name != kw::SelfLower => ident.to_string(),
3876+
_ => "_".to_string(),
3877+
}).collect::<Vec<_>>().join(", ");
3878+
}
3879+
Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => {
3880+
sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
3881+
match hir.as_local_hir_id(def_id).and_then(|hir_id| hir.def_kind(hir_id)) {
3882+
Some(hir::def::DefKind::Ctor(hir::def::CtorOf::Variant, _)) => {
3883+
msg = "instantiate this tuple variant";
3884+
}
3885+
Some(hir::def::DefKind::Ctor(hir::def::CtorOf::Struct, _)) => {
3886+
msg = "instantiate this tuple struct";
3887+
}
3888+
_ => {}
3889+
}
3890+
}
3891+
Some(Node::ForeignItem(hir::ForeignItem {
3892+
node: hir::ForeignItemKind::Fn(_, idents, _),
3893+
..
3894+
})) |
3895+
Some(Node::TraitItem(hir::TraitItem {
3896+
node: hir::TraitItemKind::Method(.., hir::TraitMethod::Required(idents)),
3897+
..
3898+
})) => sugg_call = idents.iter()
3899+
.map(|ident| if ident.name != kw::SelfLower {
3900+
ident.to_string()
3901+
} else {
3902+
"_".to_string()
3903+
}).collect::<Vec<_>>()
3904+
.join(", "),
3905+
_ => {}
3906+
}
3907+
};
3908+
if let Ok(code) = self.sess().source_map().span_to_snippet(expr.span) {
3909+
err.span_suggestion(
3910+
expr.span,
3911+
&format!("use parentheses to {}", msg),
3912+
format!("{}({})", code, sugg_call),
3913+
applicability,
3914+
);
3915+
return true;
3916+
}
3917+
}
3918+
false
3919+
}
3920+
38223921
pub fn suggest_ref_or_into(
38233922
&self,
38243923
err: &mut DiagnosticBuilder<'tcx>,
@@ -3833,6 +3932,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
38333932
suggestion,
38343933
Applicability::MachineApplicable,
38353934
);
3935+
} else if let (ty::FnDef(def_id, ..), true) = (
3936+
&found.sty,
3937+
self.suggest_fn_call(err, expr, expected, found),
3938+
) {
3939+
if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
3940+
let sp = self.sess().source_map().def_span(sp);
3941+
err.span_label(sp, &format!("{} defined here", found));
3942+
}
38363943
} else if !self.check_for_cast(err, expr, found, expected) {
38373944
let is_struct_pat_shorthand_field = self.is_hir_id_from_struct_pattern_shorthand_field(
38383945
expr.hir_id,

src/libsyntax/parse/parser.rs

+50-20
Original file line numberDiff line numberDiff line change
@@ -2052,9 +2052,23 @@ impl<'a> Parser<'a> {
20522052
while self.token != token::CloseDelim(token::Paren) {
20532053
es.push(match self.parse_expr() {
20542054
Ok(es) => es,
2055-
Err(err) => {
2055+
Err(mut err) => {
20562056
// recover from parse error in tuple list
2057-
return Ok(self.recover_seq_parse_error(token::Paren, lo, Err(err)));
2057+
match self.token.kind {
2058+
token::Ident(name, false)
2059+
if name == kw::Underscore && self.look_ahead(1, |t| {
2060+
t == &token::Comma
2061+
}) => {
2062+
// Special-case handling of `Foo<(_, _, _)>`
2063+
err.emit();
2064+
let sp = self.token.span;
2065+
self.bump();
2066+
self.mk_expr(sp, ExprKind::Err, ThinVec::new())
2067+
}
2068+
_ => return Ok(
2069+
self.recover_seq_parse_error(token::Paren, lo, Err(err)),
2070+
),
2071+
}
20582072
}
20592073
});
20602074
recovered = self.expect_one_of(
@@ -2456,37 +2470,35 @@ impl<'a> Parser<'a> {
24562470
}
24572471

24582472
/// Parses `a.b` or `a(13)` or `a[4]` or just `a`.
2459-
fn parse_dot_or_call_expr(&mut self,
2460-
already_parsed_attrs: Option<ThinVec<Attribute>>)
2461-
-> PResult<'a, P<Expr>> {
2473+
fn parse_dot_or_call_expr(
2474+
&mut self,
2475+
already_parsed_attrs: Option<ThinVec<Attribute>>,
2476+
) -> PResult<'a, P<Expr>> {
24622477
let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?;
24632478

24642479
let b = self.parse_bottom_expr();
24652480
let (span, b) = self.interpolated_or_expr_span(b)?;
24662481
self.parse_dot_or_call_expr_with(b, span, attrs)
24672482
}
24682483

2469-
fn parse_dot_or_call_expr_with(&mut self,
2470-
e0: P<Expr>,
2471-
lo: Span,
2472-
mut attrs: ThinVec<Attribute>)
2473-
-> PResult<'a, P<Expr>> {
2484+
fn parse_dot_or_call_expr_with(
2485+
&mut self,
2486+
e0: P<Expr>,
2487+
lo: Span,
2488+
mut attrs: ThinVec<Attribute>,
2489+
) -> PResult<'a, P<Expr>> {
24742490
// Stitch the list of outer attributes onto the return value.
24752491
// A little bit ugly, but the best way given the current code
24762492
// structure
2477-
self.parse_dot_or_call_expr_with_(e0, lo)
2478-
.map(|expr|
2493+
self.parse_dot_or_call_expr_with_(e0, lo).map(|expr|
24792494
expr.map(|mut expr| {
24802495
attrs.extend::<Vec<_>>(expr.attrs.into());
24812496
expr.attrs = attrs;
24822497
match expr.node {
24832498
ExprKind::If(..) if !expr.attrs.is_empty() => {
24842499
// Just point to the first attribute in there...
24852500
let span = expr.attrs[0].span;
2486-
2487-
self.span_err(span,
2488-
"attributes are not yet allowed on `if` \
2489-
expressions");
2501+
self.span_err(span, "attributes are not yet allowed on `if` expressions");
24902502
}
24912503
_ => {}
24922504
}
@@ -2624,7 +2636,24 @@ impl<'a> Parser<'a> {
26242636
}
26252637

26262638
fn parse_paren_expr_seq(&mut self) -> PResult<'a, Vec<P<Expr>>> {
2627-
self.parse_paren_comma_seq(|p| p.parse_expr()).map(|(r, _)| r)
2639+
self.parse_paren_comma_seq(|p| {
2640+
match p.parse_expr() {
2641+
Ok(expr) => Ok(expr),
2642+
Err(mut err) => match p.token.kind {
2643+
token::Ident(name, false)
2644+
if name == kw::Underscore && p.look_ahead(1, |t| {
2645+
t == &token::Comma
2646+
}) => {
2647+
// Special-case handling of `foo(_, _, _)`
2648+
err.emit();
2649+
let sp = p.token.span;
2650+
p.bump();
2651+
Ok(p.mk_expr(sp, ExprKind::Err, ThinVec::new()))
2652+
}
2653+
_ => Err(err),
2654+
},
2655+
}
2656+
}).map(|(r, _)| r)
26282657
}
26292658

26302659
crate fn process_potential_macro_variable(&mut self) {
@@ -2806,9 +2835,10 @@ impl<'a> Parser<'a> {
28062835
/// This parses an expression accounting for associativity and precedence of the operators in
28072836
/// the expression.
28082837
#[inline]
2809-
fn parse_assoc_expr(&mut self,
2810-
already_parsed_attrs: Option<ThinVec<Attribute>>)
2811-
-> PResult<'a, P<Expr>> {
2838+
fn parse_assoc_expr(
2839+
&mut self,
2840+
already_parsed_attrs: Option<ThinVec<Attribute>>,
2841+
) -> PResult<'a, P<Expr>> {
28122842
self.parse_assoc_expr_with(0, already_parsed_attrs.into())
28132843
}
28142844

src/test/ui/async-await/async-block-control-flow-static-semantics.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ error[E0308]: mismatched types
1616
LL | fn return_targets_async_block_not_fn() -> u8 {
1717
| --------------------------------- ^^ expected u8, found ()
1818
| |
19-
| this function's body doesn't return
19+
| implicitly returns `()` as its body has no tail or `return` expression
2020
|
2121
= note: expected type `u8`
2222
found type `()`
@@ -57,7 +57,7 @@ error[E0308]: mismatched types
5757
LL | fn rethrow_targets_async_block_not_fn() -> Result<u8, MyErr> {
5858
| ---------------------------------- ^^^^^^^^^^^^^^^^^ expected enum `std::result::Result`, found ()
5959
| |
60-
| this function's body doesn't return
60+
| implicitly returns `()` as its body has no tail or `return` expression
6161
|
6262
= note: expected type `std::result::Result<u8, MyErr>`
6363
found type `()`
@@ -68,7 +68,7 @@ error[E0308]: mismatched types
6868
LL | fn rethrow_targets_async_block_not_async_fn() -> Result<u8, MyErr> {
6969
| ---------------------------------------- ^^^^^^^^^^^^^^^^^ expected enum `std::result::Result`, found ()
7070
| |
71-
| this function's body doesn't return
71+
| implicitly returns `()` as its body has no tail or `return` expression
7272
|
7373
= note: expected type `std::result::Result<u8, MyErr>`
7474
found type `()`

src/test/ui/block-result/consider-removing-last-semi.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0308]: mismatched types
44
LL | fn f() -> String {
55
| - ^^^^^^ expected struct `std::string::String`, found ()
66
| |
7-
| this function's body doesn't return
7+
| implicitly returns `()` as its body has no tail or `return` expression
88
LL | 0u8;
99
LL | "bla".to_string();
1010
| - help: consider removing this semicolon
@@ -18,7 +18,7 @@ error[E0308]: mismatched types
1818
LL | fn g() -> String {
1919
| - ^^^^^^ expected struct `std::string::String`, found ()
2020
| |
21-
| this function's body doesn't return
21+
| implicitly returns `()` as its body has no tail or `return` expression
2222
LL | "this won't work".to_string();
2323
LL | "removeme".to_string();
2424
| - help: consider removing this semicolon

src/test/ui/block-result/issue-11714.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0308]: mismatched types
44
LL | fn blah() -> i32 {
55
| ---- ^^^ expected i32, found ()
66
| |
7-
| this function's body doesn't return
7+
| implicitly returns `()` as its body has no tail or `return` expression
88
...
99
LL | ;
1010
| - help: consider removing this semicolon

0 commit comments

Comments
 (0)