50 lines
2 KiB
Rust
50 lines
2 KiB
Rust
use clippy_utils::diagnostics::span_lint_and_note;
|
|
use clippy_utils::ty::is_type_diagnostic_item;
|
|
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) {
|
|
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"),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|
let expr_type = cx.typeck_results().expr_ty(expr);
|
|
is_type_diagnostic_item(cx, expr_type, sym::Option)
|
|
}
|
|
|
|
/// 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,
|
|
}
|
|
}
|