Skip to content

Commit

Permalink
fix #102320, suggest unwrap_or_else when a closure is passed to unwra…
Browse files Browse the repository at this point in the history
…p_or instead of suggesting calling it
  • Loading branch information
chenyukang committed Oct 2, 2022
1 parent 756e7be commit 0188273
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 2 deletions.
5 changes: 3 additions & 2 deletions compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}

/// When encountering an fn-like type, try accessing the output of the type
/// // and suggesting calling it if it satisfies a predicate (i.e. if the
/// and suggesting calling it if it satisfies a predicate (i.e. if the
/// output has a method or a field):
/// ```compile_fail,E0308
/// fn foo(x: usize) -> usize { x }
Expand Down Expand Up @@ -139,7 +139,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
sugg,
applicability,
);

return true;
}
false
Expand Down Expand Up @@ -338,6 +337,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
err.span_suggestion(sp, &msg, suggestion, applicability);
}
} else if self.suggest_else_fn_with_closure(err, expr, found, expected)
{
} else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
&& let ty::FnDef(def_id, ..) = &found.kind()
&& let Some(sp) = self.tcx.hir().span_if_local(*def_id)
Expand Down
54 changes: 54 additions & 0 deletions compiler/rustc_hir_analysis/src/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2324,6 +2324,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

/// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else`
/// FIXME: currently not working for suggesting `map_or_else`, see #102408
pub(crate) fn suggest_else_fn_with_closure(
&self,
err: &mut Diagnostic,
expr: &hir::Expr<'_>,
found: Ty<'tcx>,
expected: Ty<'tcx>,
) -> bool {
let Some((_def_id_or_name, output, _inputs)) = self.extract_callable_info(expr, found)
else { return false; };

if !self.can_coerce(output, expected) {
return false;
}

let parent = self.tcx.hir().get_parent_node(expr.hir_id);
if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) &&
let hir::ExprKind::MethodCall(
hir::PathSegment { ident: method_name, .. },
self_expr,
args,
..,
) = call_expr.kind &&
let Some(self_ty) = self.typeck_results.borrow().expr_ty_opt(self_expr) {
let new_name = Ident {
name: Symbol::intern(&format!("{}_else", method_name.as_str())),
span: method_name.span,
};
let probe = self.lookup_probe(
expr.span,
new_name,
self_ty,
self_expr,
ProbeScope::TraitsInScope,
);

// check the method arguments number
if let Ok(pick) = probe &&
let fn_sig = self.tcx.fn_sig(pick.item.def_id) &&
let fn_args = fn_sig.skip_binder().inputs() &&
fn_args.len() == args.len() + 1 {
err.span_suggestion_verbose(
method_name.span.shrink_to_hi(),
&format!("try calling `{}` instead", new_name.name.as_str()),
"_else",
Applicability::MaybeIncorrect,
);
return true;
}
}
false
}

/// Checks whether there is a local type somewhere in the chain of
/// autoderefs of `rcvr_ty`.
fn type_derefs_to_local(
Expand Down
8 changes: 8 additions & 0 deletions src/test/ui/suggestions/sugg-else-for-closure.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// run-rustfix

fn main() {
let x = "com.example.app";
let y: Option<&str> = None;
let _s = y.unwrap_or_else(|| x.split('.').nth(1).unwrap());
//~^ ERROR: mismatched types [E0308]
}
8 changes: 8 additions & 0 deletions src/test/ui/suggestions/sugg-else-for-closure.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// run-rustfix

fn main() {
let x = "com.example.app";
let y: Option<&str> = None;
let _s = y.unwrap_or(|| x.split('.').nth(1).unwrap());
//~^ ERROR: mismatched types [E0308]
}
23 changes: 23 additions & 0 deletions src/test/ui/suggestions/sugg-else-for-closure.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
error[E0308]: mismatched types
--> $DIR/sugg-else-for-closure.rs:6:26
|
LL | let _s = y.unwrap_or(|| x.split('.').nth(1).unwrap());
| --------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&str`, found closure
| |
| arguments to this function are incorrect
|
= note: expected reference `&str`
found closure `[closure@$DIR/sugg-else-for-closure.rs:6:26: 6:28]`
note: associated function defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
|
LL | pub const fn unwrap_or(self, default: T) -> T
| ^^^^^^^^^
help: try calling `unwrap_or_else` instead
|
LL | let _s = y.unwrap_or_else(|| x.split('.').nth(1).unwrap());
| +++++

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.

0 comments on commit 0188273

Please sign in to comment.