rust-clippy/clippy_lints/src/methods/or_then_unwrap.rs

83 lines
2.4 KiB
Rust
Raw Normal View History

2022-03-18 21:44:56 +00:00
use clippy_utils::diagnostics::span_lint_and_sugg;
2022-03-18 22:18:36 +00:00
use clippy_utils::source::snippet_with_applicability;
2022-03-18 20:11:54 +00:00
use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
2022-03-18 21:44:56 +00:00
use rustc_errors::Applicability;
2022-03-18 20:11:54 +00:00
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::LateContext;
use rustc_span::{sym, Span};
use super::OR_THEN_UNWRAP;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
unwrap_expr: &Expr<'_>,
recv: &'tcx Expr<'tcx>,
or_arg: &'tcx Expr<'_>,
or_span: Span,
) {
let ty = cx.typeck_results().expr_ty(recv); // get type of x (we later check if it's Option or Result)
let title;
2022-03-18 22:18:36 +00:00
let or_arg_content: Span;
2022-03-18 20:11:54 +00:00
if is_type_diagnostic_item(cx, ty, sym::Option) {
title = ".or(Some(…)).unwrap() found";
2022-03-18 22:18:36 +00:00
if let Some(content) = get_content_if_is(or_arg, "Some") {
or_arg_content = content;
} else {
2022-03-18 20:11:54 +00:00
return;
}
} else if is_type_diagnostic_item(cx, ty, sym::Result) {
title = ".or(Ok(…)).unwrap() found";
2022-03-18 22:18:36 +00:00
if let Some(content) = get_content_if_is(or_arg, "Ok") {
or_arg_content = content;
} else {
2022-03-18 20:11:54 +00:00
return;
}
} else {
// Someone has implemented a struct with .or(...).unwrap() chaining,
// but it's not an Option or a Result, so bail
return;
}
let unwrap_span = if let ExprKind::MethodCall(_, _, span) = unwrap_expr.kind {
span
} else {
// unreachable. but fallback to ident's span ("()" are missing)
unwrap_expr.span
};
2022-03-18 22:18:36 +00:00
let mut applicability = Applicability::MachineApplicable;
let suggestion = format!(
"unwrap_or({})",
snippet_with_applicability(cx, or_arg_content, "..", &mut applicability)
);
2022-03-18 21:44:56 +00:00
span_lint_and_sugg(
2022-03-18 20:11:54 +00:00
cx,
OR_THEN_UNWRAP,
or_span.to(unwrap_span),
title,
2022-03-18 21:44:56 +00:00
"try this",
2022-03-18 22:18:36 +00:00
suggestion,
applicability,
2022-03-18 20:11:54 +00:00
);
}
2022-03-18 22:18:36 +00:00
/// is expr a Call to name? if so, return what it's wrapping
2022-03-18 20:11:54 +00:00
/// name might be "Some", "Ok", "Err", etc.
2022-03-18 22:18:36 +00:00
fn get_content_if_is<'a>(expr: &Expr<'a>, name: &str) -> Option<Span> {
2022-03-18 20:11:54 +00:00
if_chain! {
2022-03-18 22:18:36 +00:00
if let ExprKind::Call(some_expr, [arg]) = expr.kind;
2022-03-18 20:11:54 +00:00
if let ExprKind::Path(QPath::Resolved(_, path)) = &some_expr.kind;
if let Some(path_segment) = path.segments.first();
if path_segment.ident.name.as_str() == name;
then {
2022-03-18 22:18:36 +00:00
Some(arg.span)
2022-03-18 20:11:54 +00:00
}
else {
2022-03-18 22:18:36 +00:00
None
2022-03-18 20:11:54 +00:00
}
}
}