1 // Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution.
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
11 use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
12 use crate::rustc::{declare_tool_lint, lint_array};
13 use if_chain::if_chain;
14 use crate::rustc::hir;
16 use crate::syntax_pos::Span;
17 use crate::utils::{match_def_path, method_chain_args, span_lint_and_then, walk_ptrs_ty, is_expn_of, opt_def_id};
18 use crate::utils::paths::{BEGIN_PANIC, BEGIN_PANIC_FMT, FROM_TRAIT, OPTION, RESULT};
20 /// **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`
22 /// **Why is this bad?** `TryFrom` should be used if there's a possibility of failure.
24 /// **Known problems:** None.
29 /// impl From<String> for Foo {
30 /// fn from(s: String) -> Self {
31 /// Foo(s.parse().unwrap())
35 declare_clippy_lint! {
36 pub FALLIBLE_IMPL_FROM,
38 "Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`"
41 pub struct FallibleImplFrom;
43 impl LintPass for FallibleImplFrom {
44 fn get_lints(&self) -> LintArray {
45 lint_array!(FALLIBLE_IMPL_FROM)
49 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FallibleImplFrom {
50 fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item) {
51 // check for `impl From<???> for ..`
52 let impl_def_id = cx.tcx.hir.local_def_id(item.id);
54 if let hir::ItemKind::Impl(.., ref impl_items) = item.node;
55 if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_def_id);
56 if match_def_path(cx.tcx, impl_trait_ref.def_id, &FROM_TRAIT);
58 lint_impl_body(cx, item.span, impl_items);
64 fn lint_impl_body<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, impl_span: Span, impl_items: &hir::HirVec<hir::ImplItemRef>) {
65 use crate::rustc::hir::*;
66 use crate::rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
68 struct FindPanicUnwrap<'a, 'tcx: 'a> {
69 tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
70 tables: &'tcx ty::TypeckTables<'tcx>,
74 impl<'a, 'tcx: 'a> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
75 fn visit_expr(&mut self, expr: &'tcx Expr) {
76 // check for `begin_panic`
78 if let ExprKind::Call(ref func_expr, _) = expr.node;
79 if let ExprKind::Path(QPath::Resolved(_, ref path)) = func_expr.node;
80 if let Some(path_def_id) = opt_def_id(path.def);
81 if match_def_path(self.tcx, path_def_id, &BEGIN_PANIC) ||
82 match_def_path(self.tcx, path_def_id, &BEGIN_PANIC_FMT);
83 if is_expn_of(expr.span, "unreachable").is_none();
85 self.result.push(expr.span);
90 if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
91 let reciever_ty = walk_ptrs_ty(self.tables.expr_ty(&arglists[0][0]));
92 if match_type(self.tcx, reciever_ty, &OPTION) || match_type(self.tcx, reciever_ty, &RESULT) {
93 self.result.push(expr.span);
97 // and check sub-expressions
98 intravisit::walk_expr(self, expr);
101 fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
102 NestedVisitorMap::None
106 for impl_item in impl_items {
108 if impl_item.ident.name == "from";
109 if let ImplItemKind::Method(_, body_id) =
110 cx.tcx.hir.impl_item(impl_item.id).node;
112 // check the body for `begin_panic` or `unwrap`
113 let body = cx.tcx.hir.body(body_id);
114 let impl_item_def_id = cx.tcx.hir.local_def_id(impl_item.id.node_id);
115 let mut fpu = FindPanicUnwrap {
117 tables: cx.tcx.typeck_tables_of(impl_item_def_id),
120 fpu.visit_expr(&body.value);
122 // if we've found one, lint
123 if !fpu.result.is_empty() {
128 "consider implementing `TryFrom` instead",
131 "`From` is intended for infallible conversions only. \
132 Use `TryFrom` if there's a possibility for the conversion to fail.");
133 db.span_note(fpu.result, "potential failure(s)");
141 fn match_type(tcx: ty::TyCtxt<'_, '_, '_>, ty: ty::Ty<'_>, path: &[&str]) -> bool {
143 ty::Adt(adt, _) => match_def_path(tcx, adt.did, path),