Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 32 additions & 23 deletions clippy_lints/src/methods/needless_option_take.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,24 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::match_def_path;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::LateContext;
use rustc_span::sym;

use super::NEEDLESS_OPTION_TAKE;

pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
// Checks if expression type is equal to sym::Option and if the expr is not a syntactic place
if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) && has_expr_as_ref_path(cx, recv) {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
NEEDLESS_OPTION_TAKE,
expr.span,
"called `Option::take()` on a temporary value",
"try",
format!(
"{}",
snippet_with_applicability(cx, recv.span, "..", &mut applicability)
),
applicability,
);
if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) {
if let Some(function_name) = source_of_temporary_value(recv) {
span_lint_and_note(
cx,
NEEDLESS_OPTION_TAKE,
expr.span,
"called `Option::take()` on a temporary value",
None,
format!("`{function_name}` creates a temporary value, so calling take() has no effect"),
);
}
}
}

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

fn has_expr_as_ref_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let Some(ref_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
return match_def_path(cx, ref_id, &["core", "option", "Option", "as_ref"]);
/// Returns the string of the function call that creates the temporary.
/// When this function is called, we are reasonably certain that the `ExprKind` is either
/// `Call` or `MethodCall` because we already checked that the expression is not
/// `is_syntactic_place_expr()`.
fn source_of_temporary_value<'a>(expr: &'a Expr<'_>) -> Option<&'a str> {
match expr.peel_borrows().kind {
ExprKind::Call(function, _) => {
if let ExprKind::Path(QPath::Resolved(_, func_path)) = function.kind {
if !func_path.segments.is_empty() {
return Some(func_path.segments[0].ident.name.as_str());
}
}
if let ExprKind::Path(QPath::TypeRelative(_, func_path_segment)) = function.kind {
return Some(func_path_segment.ident.name.as_str());
}
None
},
ExprKind::MethodCall(path_segment, ..) => Some(path_segment.ident.name.as_str()),
_ => None,
}
false
}
13 changes: 0 additions & 13 deletions tests/ui/needless_option_take.fixed

This file was deleted.

47 changes: 46 additions & 1 deletion tests/ui/needless_option_take.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
struct MyStruct;

impl MyStruct {
pub fn get_option() -> Option<Self> {
todo!()
}
}

fn return_option() -> Option<i32> {
todo!()
}

fn main() {
println!("Testing non erroneous option_take_on_temporary");
let mut option = Some(1);
Expand All @@ -7,7 +19,40 @@ fn main() {
let x = Some(3);
x.as_ref();

println!("Testing erroneous option_take_on_temporary");
let x = Some(3);
x.as_ref().take();
//~^ ERROR: called `Option::take()` on a temporary value

println!("Testing non erroneous option_take_on_temporary");
let mut x = Some(3);
let y = x.as_mut();

let mut x = Some(3);
let y = x.as_mut().take();
//~^ ERROR: called `Option::take()` on a temporary value
let y = x.replace(289).take();
//~^ ERROR: called `Option::take()` on a temporary value

let y = Some(3).as_mut().take();
//~^ ERROR: called `Option::take()` on a temporary value

let y = Option::as_mut(&mut x).take();
//~^ ERROR: called `Option::take()` on a temporary value

let x = return_option();
let x = return_option().take();
//~^ ERROR: called `Option::take()` on a temporary value

let x = MyStruct::get_option();
let x = MyStruct::get_option().take();
//~^ ERROR: called `Option::take()` on a temporary value

let mut my_vec = vec![1, 2, 3];
my_vec.push(4);
let y = my_vec.first();
let y = my_vec.first().take();
//~^ ERROR: called `Option::take()` on a temporary value

let y = my_vec.first().take();
//~^ ERROR: called `Option::take()` on a temporary value
}
71 changes: 68 additions & 3 deletions tests/ui/needless_option_take.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,76 @@
error: called `Option::take()` on a temporary value
--> tests/ui/needless_option_take.rs:12:5
--> tests/ui/needless_option_take.rs:23:5
|
LL | x.as_ref().take();
| ^^^^^^^^^^^^^^^^^ help: try: `x.as_ref()`
| ^^^^^^^^^^^^^^^^^
|
= note: `as_ref` creates a temporary value, so calling take() has no effect
= note: `-D clippy::needless-option-take` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::needless_option_take)]`

error: aborting due to 1 previous error
error: called `Option::take()` on a temporary value
--> tests/ui/needless_option_take.rs:31:13
|
LL | let y = x.as_mut().take();
| ^^^^^^^^^^^^^^^^^
|
= note: `as_mut` creates a temporary value, so calling take() has no effect

error: called `Option::take()` on a temporary value
--> tests/ui/needless_option_take.rs:33:13
|
LL | let y = x.replace(289).take();
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: `replace` creates a temporary value, so calling take() has no effect

error: called `Option::take()` on a temporary value
--> tests/ui/needless_option_take.rs:36:13
|
LL | let y = Some(3).as_mut().take();
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `as_mut` creates a temporary value, so calling take() has no effect

error: called `Option::take()` on a temporary value
--> tests/ui/needless_option_take.rs:39:13
|
LL | let y = Option::as_mut(&mut x).take();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `as_mut` creates a temporary value, so calling take() has no effect

error: called `Option::take()` on a temporary value
--> tests/ui/needless_option_take.rs:43:13
|
LL | let x = return_option().take();
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: `return_option` creates a temporary value, so calling take() has no effect

error: called `Option::take()` on a temporary value
--> tests/ui/needless_option_take.rs:47:13
|
LL | let x = MyStruct::get_option().take();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `get_option` creates a temporary value, so calling take() has no effect

error: called `Option::take()` on a temporary value
--> tests/ui/needless_option_take.rs:53:13
|
LL | let y = my_vec.first().take();
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: `first` creates a temporary value, so calling take() has no effect

error: called `Option::take()` on a temporary value
--> tests/ui/needless_option_take.rs:56:13
|
LL | let y = my_vec.first().take();
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: `first` creates a temporary value, so calling take() has no effect

error: aborting due to 9 previous errors

Loading