1 use clippy_utils
::diagnostics
::span_lint_and_then
;
2 use clippy_utils
::ty
::is_type_diagnostic_item
;
3 use clippy_utils
::{is_expn_of, match_panic_def_id, method_chain_args}
;
4 use if_chain
::if_chain
;
6 use rustc_lint
::{LateContext, LateLintPass}
;
7 use rustc_middle
::hir
::map
::Map
;
9 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
10 use rustc_span
::{sym, Span}
;
12 declare_clippy_lint
! {
14 /// Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`
16 /// ### Why is this bad?
17 /// `TryFrom` should be used if there's a possibility of failure.
24 /// impl From<String> for Foo {
25 /// fn from(s: String) -> Self {
26 /// Foo(s.parse().unwrap())
35 /// use std::convert::TryFrom;
36 /// impl TryFrom<String> for Foo {
38 /// fn try_from(s: String) -> Result<Self, Self::Error> {
39 /// if let Ok(parsed) = s.parse() {
47 pub FALLIBLE_IMPL_FROM
,
49 "Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`"
52 declare_lint_pass
!(FallibleImplFrom
=> [FALLIBLE_IMPL_FROM
]);
54 impl<'tcx
> LateLintPass
<'tcx
> for FallibleImplFrom
{
55 fn check_item(&mut self, cx
: &LateContext
<'tcx
>, item
: &'tcx hir
::Item
<'_
>) {
56 // check for `impl From<???> for ..`
58 if let hir
::ItemKind
::Impl(impl_
) = &item
.kind
;
59 if let Some(impl_trait_ref
) = cx
.tcx
.impl_trait_ref(item
.def_id
);
60 if cx
.tcx
.is_diagnostic_item(sym
::from_trait
, impl_trait_ref
.def_id
);
62 lint_impl_body(cx
, item
.span
, impl_
.items
);
68 fn lint_impl_body
<'tcx
>(cx
: &LateContext
<'tcx
>, impl_span
: Span
, impl_items
: &[hir
::ImplItemRef
<'_
>]) {
69 use rustc_hir
::intravisit
::{self, NestedVisitorMap, Visitor}
;
70 use rustc_hir
::{Expr, ExprKind, ImplItemKind, QPath}
;
72 struct FindPanicUnwrap
<'a
, 'tcx
> {
73 lcx
: &'a LateContext
<'tcx
>,
74 typeck_results
: &'tcx ty
::TypeckResults
<'tcx
>,
78 impl<'a
, 'tcx
> Visitor
<'tcx
> for FindPanicUnwrap
<'a
, 'tcx
> {
81 fn visit_expr(&mut self, expr
: &'tcx Expr
<'_
>) {
82 // check for `begin_panic`
84 if let ExprKind
::Call(func_expr
, _
) = expr
.kind
;
85 if let ExprKind
::Path(QPath
::Resolved(_
, path
)) = func_expr
.kind
;
86 if let Some(path_def_id
) = path
.res
.opt_def_id();
87 if match_panic_def_id(self.lcx
, path_def_id
);
88 if is_expn_of(expr
.span
, "unreachable").is_none();
90 self.result
.push(expr
.span
);
95 if let Some(arglists
) = method_chain_args(expr
, &["unwrap"]) {
96 let reciever_ty
= self.typeck_results
.expr_ty(&arglists
[0][0]).peel_refs();
97 if is_type_diagnostic_item(self.lcx
, reciever_ty
, sym
::option_type
)
98 || is_type_diagnostic_item(self.lcx
, reciever_ty
, sym
::result_type
)
100 self.result
.push(expr
.span
);
104 // and check sub-expressions
105 intravisit
::walk_expr(self, expr
);
108 fn nested_visit_map(&mut self) -> NestedVisitorMap
<Self::Map
> {
109 NestedVisitorMap
::None
113 for impl_item
in impl_items
{
115 if impl_item
.ident
.name
== sym
::from
;
116 if let ImplItemKind
::Fn(_
, body_id
) =
117 cx
.tcx
.hir().impl_item(impl_item
.id
).kind
;
119 // check the body for `begin_panic` or `unwrap`
120 let body
= cx
.tcx
.hir().body(body_id
);
121 let mut fpu
= FindPanicUnwrap
{
123 typeck_results
: cx
.tcx
.typeck(impl_item
.id
.def_id
),
126 fpu
.visit_expr(&body
.value
);
128 // if we've found one, lint
129 if !fpu
.result
.is_empty() {
134 "consider implementing `TryFrom` instead",
137 "`From` is intended for infallible conversions only. \
138 Use `TryFrom` if there's a possibility for the conversion to fail");
139 diag
.span_note(fpu
.result
, "potential failure(s)");