Skip to content

Commit f21f329

Browse files
committed
Print out type for uncertain if-let condition
1 parent 5414e0f commit f21f329

File tree

5 files changed

+83
-80
lines changed

5 files changed

+83
-80
lines changed

clippy_lints/src/manual_unwrap_or_default.rs

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ use rustc_errors::Applicability;
22
use rustc_hir::def::Res;
33
use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatKind, QPath};
44
use rustc_lint::{LateContext, LateLintPass, LintContext};
5-
use rustc_middle::ty::{self, GenericArgKind};
5+
use rustc_middle::ty::GenericArgKind;
66
use rustc_session::declare_lint_pass;
77
use rustc_span::sym;
88

99
use clippy_utils::diagnostics::span_lint_and_sugg;
1010
use clippy_utils::higher::IfLetOrMatch;
1111
use clippy_utils::sugg::Sugg;
12-
use clippy_utils::ty::implements_trait;
13-
use clippy_utils::{is_default_equivalent, is_in_const_context, peel_blocks, span_contains_comment};
12+
use clippy_utils::ty::{expr_type_is_certain, implements_trait};
13+
use clippy_utils::{is_default_equivalent, is_in_const_context, path_res, peel_blocks, span_contains_comment};
1414

1515
declare_clippy_lint! {
1616
/// ### What it does
@@ -152,48 +152,42 @@ fn handle<'tcx>(cx: &LateContext<'tcx>, if_let_or_match: IfLetOrMatch<'tcx>, exp
152152
&& is_default_equivalent(cx, body_none)
153153
&& let Some(receiver) = Sugg::hir_opt(cx, condition).map(Sugg::maybe_par)
154154
{
155-
// We check if the expression is not a None variant
156-
if let Some(none_def_id) = cx.tcx.lang_items().option_none_variant() {
157-
if let ExprKind::Path(QPath::Resolved(_, path)) = &condition.kind {
158-
if let Some(def_id) = path.res.opt_def_id() {
159-
if cx.tcx.parent(def_id) == none_def_id {
160-
return span_lint_and_sugg(
161-
cx,
162-
MANUAL_UNWRAP_OR_DEFAULT,
163-
expr.span,
164-
format!("{expr_name} can be simplified with `.unwrap_or_default()`"),
165-
"replace it with",
166-
format!("{receiver}::</* Type */>.unwrap_or_default()"),
167-
Applicability::HasPlaceholders,
168-
);
169-
}
170-
}
171-
}
172-
}
173-
174-
// We check if the expression is not a method or function with a unspecified return type
175-
if let ExprKind::MethodCall(_, expr, _, _) = &condition.kind {
176-
if let ty::Adt(_, substs) = cx.typeck_results().expr_ty(expr).kind() {
177-
if let Some(ok_type) = substs.first() {
178-
return span_lint_and_sugg(
179-
cx,
180-
MANUAL_UNWRAP_OR_DEFAULT,
181-
expr.span,
182-
format!("{expr_name} can be simplified with `.unwrap_or_default()`"),
183-
format!("explicit the type {ok_type} and replace your expression with"),
184-
format!("{receiver}.unwrap_or_default()"),
185-
Applicability::Unspecified,
186-
);
187-
}
188-
}
189-
}
190-
191155
// Machine applicable only if there are no comments present
192156
let applicability = if span_contains_comment(cx.sess().source_map(), expr.span) {
193157
Applicability::MaybeIncorrect
194158
} else {
195159
Applicability::MachineApplicable
196160
};
161+
162+
// We now check if the condition is a None variant, in which case we need to specify the type
163+
if path_res(cx, condition)
164+
.opt_def_id()
165+
.is_some_and(|id| Some(cx.tcx.parent(id)) == cx.tcx.lang_items().option_none_variant())
166+
{
167+
return span_lint_and_sugg(
168+
cx,
169+
MANUAL_UNWRAP_OR_DEFAULT,
170+
expr.span,
171+
format!("{expr_name} can be simplified with `.unwrap_or_default()`"),
172+
"replace it with",
173+
format!("{receiver}::<{expr_type}>.unwrap_or_default()"),
174+
applicability,
175+
);
176+
}
177+
178+
// We check if the expression type is still uncertain, in which case we ask the user to specify it
179+
if !expr_type_is_certain(cx, condition) {
180+
return span_lint_and_sugg(
181+
cx,
182+
MANUAL_UNWRAP_OR_DEFAULT,
183+
expr.span,
184+
format!("{expr_name} can be simplified with `.unwrap_or_default()`"),
185+
format!("ascribe the type {expr_type} and replace your expression with"),
186+
format!("{receiver}.unwrap_or_default()"),
187+
Applicability::Unspecified,
188+
);
189+
}
190+
197191
span_lint_and_sugg(
198192
cx,
199193
MANUAL_UNWRAP_OR_DEFAULT,

tests/ui/manual_unwrap_or_default.rs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -132,19 +132,3 @@ fn issue_12928() {
132132
let y = if let Some(Y(a, _)) = x { a } else { 0 };
133133
let y = if let Some(Y(a, ..)) = x { a } else { 0 };
134134
}
135-
136-
//@no-rustfix
137-
fn issue_12670() {
138-
// no auto: type not found
139-
#[allow(clippy::match_result_ok)]
140-
let _ = if let Some(x) = "1".parse().ok() {
141-
x
142-
} else {
143-
i32::default()
144-
};
145-
let _ = if let Some(x) = None { x } else { i32::default() };
146-
// auto fix with unwrap_or_default
147-
let a: Option<i32> = None;
148-
let _ = if let Some(x) = a { x } else { i32::default() };
149-
let _ = if let Some(x) = Some(99) { x } else { i32::default() };
150-
}

tests/ui/manual_unwrap_or_default.stderr

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -83,29 +83,5 @@ LL | | _ => 0,
8383
LL | | },
8484
| |_________^ help: replace it with: `(*b).unwrap_or_default()`
8585

86-
error: if let can be simplified with `.unwrap_or_default()`
87-
--> tests/ui/manual_unwrap_or_default.rs:104:30
88-
|
89-
LL | let _ = if let Some(x) = "1".parse().ok() {
90-
| ^^^^^^^^^^^ help: explicit the type i32 and replace your expression with: `"1".parse().ok().unwrap_or_default()`
91-
92-
error: if let can be simplified with `.unwrap_or_default()`
93-
--> tests/ui/manual_unwrap_or_default.rs:109:13
94-
|
95-
LL | let _ = if let Some(x) = None { x } else { i32::default() };
96-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `None::</* Type */>.unwrap_or_default()`
97-
98-
error: if let can be simplified with `.unwrap_or_default()`
99-
--> tests/ui/manual_unwrap_or_default.rs:112:13
100-
|
101-
LL | let _ = if let Some(x) = a { x } else { i32::default() };
102-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.unwrap_or_default()`
103-
104-
error: if let can be simplified with `.unwrap_or_default()`
105-
--> tests/ui/manual_unwrap_or_default.rs:113:13
106-
|
107-
LL | let _ = if let Some(x) = Some(99) { x } else { i32::default() };
108-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(99).unwrap_or_default()`
109-
110-
error: aborting due to 10 previous errors
86+
error: aborting due to 8 previous errors
11187

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@no-rustfix
2+
fn issue_12670() {
3+
// no auto: type not found
4+
#[allow(clippy::match_result_ok)]
5+
let _ = if let Some(x) = "1".parse().ok() {
6+
x
7+
} else {
8+
i32::default()
9+
};
10+
let _ = if let Some(x) = None { x } else { i32::default() };
11+
// auto fix with unwrap_or_default
12+
let a: Option<i32> = None;
13+
let _ = if let Some(x) = a { x } else { i32::default() };
14+
let _ = if let Some(x) = Some(99) { x } else { i32::default() };
15+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
error: if let can be simplified with `.unwrap_or_default()`
2+
--> tests/ui/manual_unwrap_or_default_unfixable.rs:5:13
3+
|
4+
LL | let _ = if let Some(x) = "1".parse().ok() {
5+
| _____________^
6+
LL | | x
7+
LL | | } else {
8+
LL | | i32::default()
9+
LL | | };
10+
| |_____^ help: ascribe the type i32 and replace your expression with: `"1".parse().ok().unwrap_or_default()`
11+
|
12+
= note: `-D clippy::manual-unwrap-or-default` implied by `-D warnings`
13+
= help: to override `-D warnings` add `#[allow(clippy::manual_unwrap_or_default)]`
14+
15+
error: if let can be simplified with `.unwrap_or_default()`
16+
--> tests/ui/manual_unwrap_or_default_unfixable.rs:10:13
17+
|
18+
LL | let _ = if let Some(x) = None { x } else { i32::default() };
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `None::<i32>.unwrap_or_default()`
20+
21+
error: if let can be simplified with `.unwrap_or_default()`
22+
--> tests/ui/manual_unwrap_or_default_unfixable.rs:13:13
23+
|
24+
LL | let _ = if let Some(x) = a { x } else { i32::default() };
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.unwrap_or_default()`
26+
27+
error: if let can be simplified with `.unwrap_or_default()`
28+
--> tests/ui/manual_unwrap_or_default_unfixable.rs:14:13
29+
|
30+
LL | let _ = if let Some(x) = Some(99) { x } else { i32::default() };
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(99).unwrap_or_default()`
32+
33+
error: aborting due to 4 previous errors
34+

0 commit comments

Comments
 (0)