Skip to content

Commit 6240710

Browse files
authored
allow needless_option_take to report for more cases (#13684)
changelog: ``` changelog: [`needless_option_take`]: now lints for all temporary expressions of type Option<T>. ``` fixes #13671 In general, needless_option_take should report whenever take() is called on a temporary value, not just when the temporary value is created by as_ref(). Also, we stop emitting machine applicable fixes in these cases, since sometimes the intention of the user is to actually clear the original option, in cases such as `option.as_mut().take()`.
2 parents 5ac1805 + b8cd513 commit 6240710

File tree

4 files changed

+146
-40
lines changed

4 files changed

+146
-40
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,24 @@
1-
use clippy_utils::diagnostics::span_lint_and_sugg;
2-
use clippy_utils::match_def_path;
3-
use clippy_utils::source::snippet_with_applicability;
1+
use clippy_utils::diagnostics::span_lint_and_note;
42
use clippy_utils::ty::is_type_diagnostic_item;
5-
use rustc_errors::Applicability;
6-
use rustc_hir::Expr;
3+
use rustc_hir::{Expr, ExprKind, QPath};
74
use rustc_lint::LateContext;
85
use rustc_span::sym;
96

107
use super::NEEDLESS_OPTION_TAKE;
118

129
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
1310
// Checks if expression type is equal to sym::Option and if the expr is not a syntactic place
14-
if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) && has_expr_as_ref_path(cx, recv) {
15-
let mut applicability = Applicability::MachineApplicable;
16-
span_lint_and_sugg(
17-
cx,
18-
NEEDLESS_OPTION_TAKE,
19-
expr.span,
20-
"called `Option::take()` on a temporary value",
21-
"try",
22-
format!(
23-
"{}",
24-
snippet_with_applicability(cx, recv.span, "..", &mut applicability)
25-
),
26-
applicability,
27-
);
11+
if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) {
12+
if let Some(function_name) = source_of_temporary_value(recv) {
13+
span_lint_and_note(
14+
cx,
15+
NEEDLESS_OPTION_TAKE,
16+
expr.span,
17+
"called `Option::take()` on a temporary value",
18+
None,
19+
format!("`{function_name}` creates a temporary value, so calling take() has no effect"),
20+
);
21+
}
2822
}
2923
}
3024

@@ -33,9 +27,24 @@ fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3327
is_type_diagnostic_item(cx, expr_type, sym::Option)
3428
}
3529

36-
fn has_expr_as_ref_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
37-
if let Some(ref_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
38-
return match_def_path(cx, ref_id, &["core", "option", "Option", "as_ref"]);
30+
/// Returns the string of the function call that creates the temporary.
31+
/// When this function is called, we are reasonably certain that the `ExprKind` is either
32+
/// `Call` or `MethodCall` because we already checked that the expression is not
33+
/// `is_syntactic_place_expr()`.
34+
fn source_of_temporary_value<'a>(expr: &'a Expr<'_>) -> Option<&'a str> {
35+
match expr.peel_borrows().kind {
36+
ExprKind::Call(function, _) => {
37+
if let ExprKind::Path(QPath::Resolved(_, func_path)) = function.kind {
38+
if !func_path.segments.is_empty() {
39+
return Some(func_path.segments[0].ident.name.as_str());
40+
}
41+
}
42+
if let ExprKind::Path(QPath::TypeRelative(_, func_path_segment)) = function.kind {
43+
return Some(func_path_segment.ident.name.as_str());
44+
}
45+
None
46+
},
47+
ExprKind::MethodCall(path_segment, ..) => Some(path_segment.ident.name.as_str()),
48+
_ => None,
3949
}
40-
false
4150
}

tests/ui/needless_option_take.fixed

-13
This file was deleted.

tests/ui/needless_option_take.rs

+46-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
struct MyStruct;
2+
3+
impl MyStruct {
4+
pub fn get_option() -> Option<Self> {
5+
todo!()
6+
}
7+
}
8+
9+
fn return_option() -> Option<i32> {
10+
todo!()
11+
}
12+
113
fn main() {
214
println!("Testing non erroneous option_take_on_temporary");
315
let mut option = Some(1);
@@ -7,7 +19,40 @@ fn main() {
719
let x = Some(3);
820
x.as_ref();
921

10-
println!("Testing erroneous option_take_on_temporary");
1122
let x = Some(3);
1223
x.as_ref().take();
24+
//~^ ERROR: called `Option::take()` on a temporary value
25+
26+
println!("Testing non erroneous option_take_on_temporary");
27+
let mut x = Some(3);
28+
let y = x.as_mut();
29+
30+
let mut x = Some(3);
31+
let y = x.as_mut().take();
32+
//~^ ERROR: called `Option::take()` on a temporary value
33+
let y = x.replace(289).take();
34+
//~^ ERROR: called `Option::take()` on a temporary value
35+
36+
let y = Some(3).as_mut().take();
37+
//~^ ERROR: called `Option::take()` on a temporary value
38+
39+
let y = Option::as_mut(&mut x).take();
40+
//~^ ERROR: called `Option::take()` on a temporary value
41+
42+
let x = return_option();
43+
let x = return_option().take();
44+
//~^ ERROR: called `Option::take()` on a temporary value
45+
46+
let x = MyStruct::get_option();
47+
let x = MyStruct::get_option().take();
48+
//~^ ERROR: called `Option::take()` on a temporary value
49+
50+
let mut my_vec = vec![1, 2, 3];
51+
my_vec.push(4);
52+
let y = my_vec.first();
53+
let y = my_vec.first().take();
54+
//~^ ERROR: called `Option::take()` on a temporary value
55+
56+
let y = my_vec.first().take();
57+
//~^ ERROR: called `Option::take()` on a temporary value
1358
}

tests/ui/needless_option_take.stderr

+68-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,76 @@
11
error: called `Option::take()` on a temporary value
2-
--> tests/ui/needless_option_take.rs:12:5
2+
--> tests/ui/needless_option_take.rs:23:5
33
|
44
LL | x.as_ref().take();
5-
| ^^^^^^^^^^^^^^^^^ help: try: `x.as_ref()`
5+
| ^^^^^^^^^^^^^^^^^
66
|
7+
= note: `as_ref` creates a temporary value, so calling take() has no effect
78
= note: `-D clippy::needless-option-take` implied by `-D warnings`
89
= help: to override `-D warnings` add `#[allow(clippy::needless_option_take)]`
910

10-
error: aborting due to 1 previous error
11+
error: called `Option::take()` on a temporary value
12+
--> tests/ui/needless_option_take.rs:31:13
13+
|
14+
LL | let y = x.as_mut().take();
15+
| ^^^^^^^^^^^^^^^^^
16+
|
17+
= note: `as_mut` creates a temporary value, so calling take() has no effect
18+
19+
error: called `Option::take()` on a temporary value
20+
--> tests/ui/needless_option_take.rs:33:13
21+
|
22+
LL | let y = x.replace(289).take();
23+
| ^^^^^^^^^^^^^^^^^^^^^
24+
|
25+
= note: `replace` creates a temporary value, so calling take() has no effect
26+
27+
error: called `Option::take()` on a temporary value
28+
--> tests/ui/needless_option_take.rs:36:13
29+
|
30+
LL | let y = Some(3).as_mut().take();
31+
| ^^^^^^^^^^^^^^^^^^^^^^^
32+
|
33+
= note: `as_mut` creates a temporary value, so calling take() has no effect
34+
35+
error: called `Option::take()` on a temporary value
36+
--> tests/ui/needless_option_take.rs:39:13
37+
|
38+
LL | let y = Option::as_mut(&mut x).take();
39+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
40+
|
41+
= note: `as_mut` creates a temporary value, so calling take() has no effect
42+
43+
error: called `Option::take()` on a temporary value
44+
--> tests/ui/needless_option_take.rs:43:13
45+
|
46+
LL | let x = return_option().take();
47+
| ^^^^^^^^^^^^^^^^^^^^^^
48+
|
49+
= note: `return_option` creates a temporary value, so calling take() has no effect
50+
51+
error: called `Option::take()` on a temporary value
52+
--> tests/ui/needless_option_take.rs:47:13
53+
|
54+
LL | let x = MyStruct::get_option().take();
55+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
56+
|
57+
= note: `get_option` creates a temporary value, so calling take() has no effect
58+
59+
error: called `Option::take()` on a temporary value
60+
--> tests/ui/needless_option_take.rs:53:13
61+
|
62+
LL | let y = my_vec.first().take();
63+
| ^^^^^^^^^^^^^^^^^^^^^
64+
|
65+
= note: `first` creates a temporary value, so calling take() has no effect
66+
67+
error: called `Option::take()` on a temporary value
68+
--> tests/ui/needless_option_take.rs:56:13
69+
|
70+
LL | let y = my_vec.first().take();
71+
| ^^^^^^^^^^^^^^^^^^^^^
72+
|
73+
= note: `first` creates a temporary value, so calling take() has no effect
74+
75+
error: aborting due to 9 previous errors
1176

0 commit comments

Comments
 (0)