]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
New upstream version 1.76.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / fallible_impl_from.rs
CommitLineData
cdc7bbd5 1use clippy_utils::diagnostics::span_lint_and_then;
5099ac24
FG
2use clippy_utils::macros::{is_panic, root_macro_call_first_node};
3use clippy_utils::method_chain_args;
cdc7bbd5 4use clippy_utils::ty::is_type_diagnostic_item;
f20569fa
XL
5use rustc_hir as hir;
6use rustc_lint::{LateContext, LateLintPass};
f20569fa 7use rustc_middle::ty;
4b012472 8use rustc_session::declare_lint_pass;
f20569fa
XL
9use rustc_span::{sym, Span};
10
11declare_clippy_lint! {
94222f64
XL
12 /// ### What it does
13 /// Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`
f20569fa 14 ///
94222f64
XL
15 /// ### Why is this bad?
16 /// `TryFrom` should be used if there's a possibility of failure.
f20569fa 17 ///
94222f64 18 /// ### Example
ed00b5ec 19 /// ```no_run
f20569fa
XL
20 /// struct Foo(i32);
21 ///
f20569fa
XL
22 /// impl From<String> for Foo {
23 /// fn from(s: String) -> Self {
24 /// Foo(s.parse().unwrap())
25 /// }
26 /// }
27 /// ```
28 ///
923072b8 29 /// Use instead:
ed00b5ec 30 /// ```no_run
f20569fa
XL
31 /// struct Foo(i32);
32 ///
f20569fa
XL
33 /// impl TryFrom<String> for Foo {
34 /// type Error = ();
35 /// fn try_from(s: String) -> Result<Self, Self::Error> {
36 /// if let Ok(parsed) = s.parse() {
37 /// Ok(Foo(parsed))
38 /// } else {
39 /// Err(())
40 /// }
41 /// }
42 /// }
43 /// ```
a2a8927a 44 #[clippy::version = "pre 1.29.0"]
f20569fa
XL
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 ..`
4b012472
FG
55 if let hir::ItemKind::Impl(impl_) = &item.kind
56 && let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id)
57 && cx
58 .tcx
59 .is_diagnostic_item(sym::From, impl_trait_ref.skip_binder().def_id)
60 {
61 lint_impl_body(cx, item.span, impl_.items);
f20569fa
XL
62 }
63 }
64}
65
487cf647 66fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::ImplItemRef]) {
5099ac24
FG
67 use rustc_hir::intravisit::{self, Visitor};
68 use rustc_hir::{Expr, ImplItemKind};
f20569fa
XL
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> {
f20569fa 77 fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
5099ac24
FG
78 if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) {
79 if is_panic(self.lcx, macro_call.def_id) {
f20569fa
XL
80 self.result.push(expr.span);
81 }
82 }
83
84 // check for `unwrap`
85 if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
f2b60f7d 86 let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
5e7ed085
FG
87 if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option)
88 || is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result)
f20569fa
XL
89 {
90 self.result.push(expr.span);
91 }
92 }
93
94 // and check sub-expressions
95 intravisit::walk_expr(self, expr);
96 }
f20569fa
XL
97 }
98
99 for impl_item in impl_items {
4b012472
FG
100 if impl_item.ident.name == sym::from
101 && let ImplItemKind::Fn(_, body_id) = cx.tcx.hir().impl_item(impl_item.id).kind
102 {
103 // check the body for `begin_panic` or `unwrap`
104 let body = cx.tcx.hir().body(body_id);
105 let mut fpu = FindPanicUnwrap {
106 lcx: cx,
107 typeck_results: cx.tcx.typeck(impl_item.id.owner_id.def_id),
108 result: Vec::new(),
109 };
110 fpu.visit_expr(body.value);
f20569fa 111
4b012472
FG
112 // if we've found one, lint
113 if !fpu.result.is_empty() {
114 span_lint_and_then(
115 cx,
116 FALLIBLE_IMPL_FROM,
117 impl_span,
118 "consider implementing `TryFrom` instead",
119 move |diag| {
120 diag.help(
121 "`From` is intended for infallible conversions only. \
122 Use `TryFrom` if there's a possibility for the conversion to fail",
123 );
124 diag.span_note(fpu.result, "potential failure(s)");
125 },
126 );
f20569fa
XL
127 }
128 }
129 }
130}