Skip to content

Commit 0188273

Browse files
committed
fix #102320, suggest unwrap_or_else when a closure is passed to unwrap_or instead of suggesting calling it
1 parent 756e7be commit 0188273

File tree

5 files changed

+96
-2
lines changed

5 files changed

+96
-2
lines changed

Diff for: compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
6565
}
6666

6767
/// When encountering an fn-like type, try accessing the output of the type
68-
/// // and suggesting calling it if it satisfies a predicate (i.e. if the
68+
/// and suggesting calling it if it satisfies a predicate (i.e. if the
6969
/// output has a method or a field):
7070
/// ```compile_fail,E0308
7171
/// fn foo(x: usize) -> usize { x }
@@ -139,7 +139,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
139139
sugg,
140140
applicability,
141141
);
142-
143142
return true;
144143
}
145144
false
@@ -338,6 +337,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
338337
} else {
339338
err.span_suggestion(sp, &msg, suggestion, applicability);
340339
}
340+
} else if self.suggest_else_fn_with_closure(err, expr, found, expected)
341+
{
341342
} else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
342343
&& let ty::FnDef(def_id, ..) = &found.kind()
343344
&& let Some(sp) = self.tcx.hir().span_if_local(*def_id)

Diff for: compiler/rustc_hir_analysis/src/check/method/suggest.rs

+54
Original file line numberDiff line numberDiff line change
@@ -2324,6 +2324,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23242324
}
23252325
}
23262326

2327+
/// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else`
2328+
/// FIXME: currently not working for suggesting `map_or_else`, see #102408
2329+
pub(crate) fn suggest_else_fn_with_closure(
2330+
&self,
2331+
err: &mut Diagnostic,
2332+
expr: &hir::Expr<'_>,
2333+
found: Ty<'tcx>,
2334+
expected: Ty<'tcx>,
2335+
) -> bool {
2336+
let Some((_def_id_or_name, output, _inputs)) = self.extract_callable_info(expr, found)
2337+
else { return false; };
2338+
2339+
if !self.can_coerce(output, expected) {
2340+
return false;
2341+
}
2342+
2343+
let parent = self.tcx.hir().get_parent_node(expr.hir_id);
2344+
if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) &&
2345+
let hir::ExprKind::MethodCall(
2346+
hir::PathSegment { ident: method_name, .. },
2347+
self_expr,
2348+
args,
2349+
..,
2350+
) = call_expr.kind &&
2351+
let Some(self_ty) = self.typeck_results.borrow().expr_ty_opt(self_expr) {
2352+
let new_name = Ident {
2353+
name: Symbol::intern(&format!("{}_else", method_name.as_str())),
2354+
span: method_name.span,
2355+
};
2356+
let probe = self.lookup_probe(
2357+
expr.span,
2358+
new_name,
2359+
self_ty,
2360+
self_expr,
2361+
ProbeScope::TraitsInScope,
2362+
);
2363+
2364+
// check the method arguments number
2365+
if let Ok(pick) = probe &&
2366+
let fn_sig = self.tcx.fn_sig(pick.item.def_id) &&
2367+
let fn_args = fn_sig.skip_binder().inputs() &&
2368+
fn_args.len() == args.len() + 1 {
2369+
err.span_suggestion_verbose(
2370+
method_name.span.shrink_to_hi(),
2371+
&format!("try calling `{}` instead", new_name.name.as_str()),
2372+
"_else",
2373+
Applicability::MaybeIncorrect,
2374+
);
2375+
return true;
2376+
}
2377+
}
2378+
false
2379+
}
2380+
23272381
/// Checks whether there is a local type somewhere in the chain of
23282382
/// autoderefs of `rcvr_ty`.
23292383
fn type_derefs_to_local(

Diff for: src/test/ui/suggestions/sugg-else-for-closure.fixed

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// run-rustfix
2+
3+
fn main() {
4+
let x = "com.example.app";
5+
let y: Option<&str> = None;
6+
let _s = y.unwrap_or_else(|| x.split('.').nth(1).unwrap());
7+
//~^ ERROR: mismatched types [E0308]
8+
}

Diff for: src/test/ui/suggestions/sugg-else-for-closure.rs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// run-rustfix
2+
3+
fn main() {
4+
let x = "com.example.app";
5+
let y: Option<&str> = None;
6+
let _s = y.unwrap_or(|| x.split('.').nth(1).unwrap());
7+
//~^ ERROR: mismatched types [E0308]
8+
}

Diff for: src/test/ui/suggestions/sugg-else-for-closure.stderr

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/sugg-else-for-closure.rs:6:26
3+
|
4+
LL | let _s = y.unwrap_or(|| x.split('.').nth(1).unwrap());
5+
| --------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&str`, found closure
6+
| |
7+
| arguments to this function are incorrect
8+
|
9+
= note: expected reference `&str`
10+
found closure `[closure@$DIR/sugg-else-for-closure.rs:6:26: 6:28]`
11+
note: associated function defined here
12+
--> $SRC_DIR/core/src/option.rs:LL:COL
13+
|
14+
LL | pub const fn unwrap_or(self, default: T) -> T
15+
| ^^^^^^^^^
16+
help: try calling `unwrap_or_else` instead
17+
|
18+
LL | let _s = y.unwrap_or_else(|| x.split('.').nth(1).unwrap());
19+
| +++++
20+
21+
error: aborting due to previous error
22+
23+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)