]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
Update upstream source from tag 'upstream/1.52.1+dfsg1'
[rustc.git] / src / tools / clippy / clippy_lints / src / fallible_impl_from.rs
CommitLineData
f20569fa
XL
1use crate::utils::{is_expn_of, is_type_diagnostic_item, match_panic_def_id, method_chain_args, span_lint_and_then};
2use if_chain::if_chain;
3use rustc_hir as hir;
4use rustc_lint::{LateContext, LateLintPass};
5use rustc_middle::hir::map::Map;
6use rustc_middle::ty;
7use rustc_session::{declare_lint_pass, declare_tool_lint};
8use rustc_span::{sym, Span};
9
10declare_clippy_lint! {
11 /// **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`
12 ///
13 /// **Why is this bad?** `TryFrom` should be used if there's a possibility of failure.
14 ///
15 /// **Known problems:** None.
16 ///
17 /// **Example:**
18 /// ```rust
19 /// struct Foo(i32);
20 ///
21 /// // Bad
22 /// impl From<String> for Foo {
23 /// fn from(s: String) -> Self {
24 /// Foo(s.parse().unwrap())
25 /// }
26 /// }
27 /// ```
28 ///
29 /// ```rust
30 /// // Good
31 /// struct Foo(i32);
32 ///
33 /// use std::convert::TryFrom;
34 /// impl TryFrom<String> for Foo {
35 /// type Error = ();
36 /// fn try_from(s: String) -> Result<Self, Self::Error> {
37 /// if let Ok(parsed) = s.parse() {
38 /// Ok(Foo(parsed))
39 /// } else {
40 /// Err(())
41 /// }
42 /// }
43 /// }
44 /// ```
45 pub FALLIBLE_IMPL_FROM,
46 nursery,
47 "Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`"
48}
49
50declare_lint_pass!(FallibleImplFrom => [FALLIBLE_IMPL_FROM]);
51
52impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom {
53 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
54 // check for `impl From<???> for ..`
55 if_chain! {
56 if let hir::ItemKind::Impl(impl_) = &item.kind;
57 if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.def_id);
58 if cx.tcx.is_diagnostic_item(sym::from_trait, impl_trait_ref.def_id);
59 then {
60 lint_impl_body(cx, item.span, impl_.items);
61 }
62 }
63 }
64}
65
66fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[hir::ImplItemRef<'_>]) {
67 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
68 use rustc_hir::{Expr, ExprKind, ImplItemKind, QPath};
69
70 struct FindPanicUnwrap<'a, 'tcx> {
71 lcx: &'a LateContext<'tcx>,
72 typeck_results: &'tcx ty::TypeckResults<'tcx>,
73 result: Vec<Span>,
74 }
75
76 impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
77 type Map = Map<'tcx>;
78
79 fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
80 // check for `begin_panic`
81 if_chain! {
82 if let ExprKind::Call(ref func_expr, _) = expr.kind;
83 if let ExprKind::Path(QPath::Resolved(_, ref path)) = func_expr.kind;
84 if let Some(path_def_id) = path.res.opt_def_id();
85 if match_panic_def_id(self.lcx, path_def_id);
86 if is_expn_of(expr.span, "unreachable").is_none();
87 then {
88 self.result.push(expr.span);
89 }
90 }
91
92 // check for `unwrap`
93 if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
94 let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
95 if is_type_diagnostic_item(self.lcx, reciever_ty, sym::option_type)
96 || is_type_diagnostic_item(self.lcx, reciever_ty, sym::result_type)
97 {
98 self.result.push(expr.span);
99 }
100 }
101
102 // and check sub-expressions
103 intravisit::walk_expr(self, expr);
104 }
105
106 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
107 NestedVisitorMap::None
108 }
109 }
110
111 for impl_item in impl_items {
112 if_chain! {
113 if impl_item.ident.name == sym::from;
114 if let ImplItemKind::Fn(_, body_id) =
115 cx.tcx.hir().impl_item(impl_item.id).kind;
116 then {
117 // check the body for `begin_panic` or `unwrap`
118 let body = cx.tcx.hir().body(body_id);
119 let mut fpu = FindPanicUnwrap {
120 lcx: cx,
121 typeck_results: cx.tcx.typeck(impl_item.id.def_id),
122 result: Vec::new(),
123 };
124 fpu.visit_expr(&body.value);
125
126 // if we've found one, lint
127 if !fpu.result.is_empty() {
128 span_lint_and_then(
129 cx,
130 FALLIBLE_IMPL_FROM,
131 impl_span,
132 "consider implementing `TryFrom` instead",
133 move |diag| {
134 diag.help(
135 "`From` is intended for infallible conversions only. \
136 Use `TryFrom` if there's a possibility for the conversion to fail");
137 diag.span_note(fpu.result, "potential failure(s)");
138 });
139 }
140 }
141 }
142 }
143}