5 use utils
::{match_def_path, method_chain_args, span_lint_and_then, walk_ptrs_ty}
;
6 use utils
::paths
::{BEGIN_PANIC, BEGIN_PANIC_FMT, FROM_TRAIT, OPTION, RESULT}
;
8 /// **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`
10 /// **Why is this bad?** `TryFrom` should be used if there's a possibility of failure.
12 /// **Known problems:** None.
17 /// impl From<String> for Foo {
18 /// fn from(s: String) -> Self {
19 /// Foo(s.parse().unwrap())
24 pub FALLIBLE_IMPL_FROM
, Allow
,
25 "Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`"
28 pub struct FallibleImplFrom
;
30 impl LintPass
for FallibleImplFrom
{
31 fn get_lints(&self) -> LintArray
{
32 lint_array
!(FALLIBLE_IMPL_FROM
)
36 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for FallibleImplFrom
{
37 fn check_item(&mut self, cx
: &LateContext
<'a
, 'tcx
>, item
: &'tcx hir
::Item
) {
38 // check for `impl From<???> for ..`
39 let impl_def_id
= cx
.tcx
.hir
.local_def_id(item
.id
);
41 if let hir
::ItemImpl(.., ref impl_items
) = item
.node
;
42 if let Some(impl_trait_ref
) = cx
.tcx
.impl_trait_ref(impl_def_id
);
43 if match_def_path(cx
.tcx
, impl_trait_ref
.def_id
, &FROM_TRAIT
);
45 lint_impl_body(cx
, item
.span
, impl_items
);
51 fn lint_impl_body
<'a
, 'tcx
>(cx
: &LateContext
<'a
, 'tcx
>, impl_span
: Span
, impl_items
: &hir
::HirVec
<hir
::ImplItemRef
>) {
53 use rustc
::hir
::intravisit
::{self, NestedVisitorMap, Visitor}
;
55 struct FindPanicUnwrap
<'a
, 'tcx
: 'a
> {
56 tcx
: ty
::TyCtxt
<'a
, 'tcx
, 'tcx
>,
57 tables
: &'tcx ty
::TypeckTables
<'tcx
>,
61 impl<'a
, 'tcx
: 'a
> Visitor
<'tcx
> for FindPanicUnwrap
<'a
, 'tcx
> {
62 fn visit_expr(&mut self, expr
: &'tcx Expr
) {
63 // check for `begin_panic`
65 if let ExprCall(ref func_expr
, _
) = expr
.node
;
66 if let ExprPath(QPath
::Resolved(_
, ref path
)) = func_expr
.node
;
67 if match_def_path(self.tcx
, path
.def
.def_id(), &BEGIN_PANIC
) ||
68 match_def_path(self.tcx
, path
.def
.def_id(), &BEGIN_PANIC_FMT
);
70 self.result
.push(expr
.span
);
75 if let Some(arglists
) = method_chain_args(expr
, &["unwrap"]) {
76 let reciever_ty
= walk_ptrs_ty(self.tables
.expr_ty(&arglists
[0][0]));
77 if match_type(self.tcx
, reciever_ty
, &OPTION
) || match_type(self.tcx
, reciever_ty
, &RESULT
) {
78 self.result
.push(expr
.span
);
82 // and check sub-expressions
83 intravisit
::walk_expr(self, expr
);
86 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'tcx
> {
87 NestedVisitorMap
::None
91 for impl_item
in impl_items
{
93 if impl_item
.name
== "from";
94 if let ImplItemKind
::Method(_
, body_id
) =
95 cx
.tcx
.hir
.impl_item(impl_item
.id
).node
;
97 // check the body for `begin_panic` or `unwrap`
98 let body
= cx
.tcx
.hir
.body(body_id
);
99 let impl_item_def_id
= cx
.tcx
.hir
.local_def_id(impl_item
.id
.node_id
);
100 let mut fpu
= FindPanicUnwrap
{
102 tables
: cx
.tcx
.typeck_tables_of(impl_item_def_id
),
105 fpu
.visit_expr(&body
.value
);
107 // if we've found one, lint
108 if !fpu
.result
.is_empty() {
113 "consider implementing `TryFrom` instead",
116 "`From` is intended for infallible conversions only. \
117 Use `TryFrom` if there's a possibility for the conversion to fail.");
118 db
.span_note(fpu
.result
, "potential failure(s)");
126 fn match_type(tcx
: ty
::TyCtxt
, ty
: ty
::Ty
, path
: &[&str]) -> bool
{
128 ty
::TyAdt(adt
, _
) => match_def_path(tcx
, adt
.did
, path
),