88 lines
2.5 KiB
Rust
88 lines
2.5 KiB
Rust
use clippy_utils::diagnostics::span_lint_and_then;
|
|
use clippy_utils::ty::{is_never_like, is_type_diagnostic_item};
|
|
use clippy_utils::{is_in_test, is_inside_always_const_context, is_lint_allowed};
|
|
use rustc_hir::Expr;
|
|
use rustc_lint::{LateContext, Lint};
|
|
use rustc_middle::ty;
|
|
use rustc_span::sym;
|
|
|
|
use super::{EXPECT_USED, UNWRAP_USED};
|
|
|
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
|
pub(super) enum Variant {
|
|
Unwrap,
|
|
Expect,
|
|
}
|
|
|
|
impl Variant {
|
|
fn method_name(self, is_err: bool) -> &'static str {
|
|
match (self, is_err) {
|
|
(Variant::Unwrap, true) => "unwrap_err",
|
|
(Variant::Unwrap, false) => "unwrap",
|
|
(Variant::Expect, true) => "expect_err",
|
|
(Variant::Expect, false) => "expect",
|
|
}
|
|
}
|
|
|
|
fn lint(self) -> &'static Lint {
|
|
match self {
|
|
Variant::Unwrap => UNWRAP_USED,
|
|
Variant::Expect => EXPECT_USED,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Lint usage of `unwrap` or `unwrap_err` for `Result` and `unwrap()` for `Option` (and their
|
|
/// `expect` counterparts).
|
|
pub(super) fn check(
|
|
cx: &LateContext<'_>,
|
|
expr: &Expr<'_>,
|
|
recv: &Expr<'_>,
|
|
is_err: bool,
|
|
allow_unwrap_in_consts: bool,
|
|
allow_unwrap_in_tests: bool,
|
|
variant: Variant,
|
|
) {
|
|
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
|
|
|
let (kind, none_value, none_prefix) = if is_type_diagnostic_item(cx, ty, sym::Option) && !is_err {
|
|
("an `Option`", "None", "")
|
|
} else if is_type_diagnostic_item(cx, ty, sym::Result)
|
|
&& let ty::Adt(_, substs) = ty.kind()
|
|
&& let Some(t_or_e_ty) = substs[usize::from(!is_err)].as_type()
|
|
{
|
|
if is_never_like(t_or_e_ty) {
|
|
return;
|
|
}
|
|
|
|
("a `Result`", if is_err { "Ok" } else { "Err" }, "an ")
|
|
} else {
|
|
return;
|
|
};
|
|
|
|
let method_suffix = if is_err { "_err" } else { "" };
|
|
|
|
if allow_unwrap_in_tests && is_in_test(cx.tcx, expr.hir_id) {
|
|
return;
|
|
}
|
|
|
|
if allow_unwrap_in_consts && is_inside_always_const_context(cx.tcx, expr.hir_id) {
|
|
return;
|
|
}
|
|
|
|
span_lint_and_then(
|
|
cx,
|
|
variant.lint(),
|
|
expr.span,
|
|
format!("used `{}()` on {kind} value", variant.method_name(is_err)),
|
|
|diag| {
|
|
diag.note(format!("if this value is {none_prefix}`{none_value}`, it will panic"));
|
|
|
|
if variant == Variant::Unwrap && is_lint_allowed(cx, EXPECT_USED, expr.hir_id) {
|
|
diag.help(format!(
|
|
"consider using `expect{method_suffix}()` to provide a better panic message"
|
|
));
|
|
}
|
|
},
|
|
);
|
|
}
|