]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/panic_unimplemented.rs
Auto merge of #4973 - JohnTitor:rustup-1231, r=phansch
[rust.git] / clippy_lints / src / panic_unimplemented.rs
1 use crate::utils::{is_direct_expn_of, is_expn_of, match_function_call, paths, span_lint};
2 use if_chain::if_chain;
3 use rustc::declare_lint_pass;
4 use rustc::hir::*;
5 use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
6 use rustc_session::declare_tool_lint;
7 use rustc_span::Span;
8 use syntax::ast::LitKind;
9
10 declare_clippy_lint! {
11     /// **What it does:** Checks for missing parameters in `panic!`.
12     ///
13     /// **Why is this bad?** Contrary to the `format!` family of macros, there are
14     /// two forms of `panic!`: if there are no parameters given, the first argument
15     /// is not a format string and used literally. So while `format!("{}")` will
16     /// fail to compile, `panic!("{}")` will not.
17     ///
18     /// **Known problems:** None.
19     ///
20     /// **Example:**
21     /// ```no_run
22     /// panic!("This `panic!` is probably missing a parameter there: {}");
23     /// ```
24     pub PANIC_PARAMS,
25     style,
26     "missing parameters in `panic!` calls"
27 }
28
29 declare_clippy_lint! {
30     /// **What it does:** Checks for usage of `panic!`.
31     ///
32     /// **Why is this bad?** `panic!` will stop the execution of the executable
33     ///
34     /// **Known problems:** None.
35     ///
36     /// **Example:**
37     /// ```no_run
38     /// panic!("even with a good reason");
39     /// ```
40     pub PANIC,
41     restriction,
42     "usage of the `panic!` macro"
43 }
44
45 declare_clippy_lint! {
46     /// **What it does:** Checks for usage of `unimplemented!`.
47     ///
48     /// **Why is this bad?** This macro should not be present in production code
49     ///
50     /// **Known problems:** None.
51     ///
52     /// **Example:**
53     /// ```no_run
54     /// unimplemented!();
55     /// ```
56     pub UNIMPLEMENTED,
57     restriction,
58     "`unimplemented!` should not be present in production code"
59 }
60
61 declare_clippy_lint! {
62     /// **What it does:** Checks for usage of `todo!`.
63     ///
64     /// **Why is this bad?** This macro should not be present in production code
65     ///
66     /// **Known problems:** None.
67     ///
68     /// **Example:**
69     /// ```no_run
70     /// todo!();
71     /// ```
72     pub TODO,
73     restriction,
74     "`todo!` should not be present in production code"
75 }
76
77 declare_clippy_lint! {
78     /// **What it does:** Checks for usage of `unreachable!`.
79     ///
80     /// **Why is this bad?** This macro can cause code to panic
81     ///
82     /// **Known problems:** None.
83     ///
84     /// **Example:**
85     /// ```no_run
86     /// unreachable!();
87     /// ```
88     pub UNREACHABLE,
89     restriction,
90     "`unreachable!` should not be present in production code"
91 }
92
93 declare_lint_pass!(PanicUnimplemented => [PANIC_PARAMS, UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]);
94
95 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PanicUnimplemented {
96     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
97         if_chain! {
98             if let ExprKind::Block(ref block, _) = expr.kind;
99             if let Some(ref ex) = block.expr;
100             if let Some(params) = match_function_call(cx, ex, &paths::BEGIN_PANIC);
101             if params.len() == 2;
102             then {
103                 if is_expn_of(expr.span, "unimplemented").is_some() {
104                     let span = get_outer_span(expr);
105                     span_lint(cx, UNIMPLEMENTED, span,
106                               "`unimplemented` should not be present in production code");
107                 } else if is_expn_of(expr.span, "todo").is_some() {
108                     let span = get_outer_span(expr);
109                     span_lint(cx, TODO, span,
110                               "`todo` should not be present in production code");
111                 } else if is_expn_of(expr.span, "unreachable").is_some() {
112                     let span = get_outer_span(expr);
113                     span_lint(cx, UNREACHABLE, span,
114                               "`unreachable` should not be present in production code");
115                 } else if is_expn_of(expr.span, "panic").is_some() {
116                     let span = get_outer_span(expr);
117                     span_lint(cx, PANIC, span,
118                               "`panic` should not be present in production code");
119                     match_panic(params, expr, cx);
120                 }
121             }
122         }
123     }
124 }
125
126 fn get_outer_span(expr: &Expr<'_>) -> Span {
127     if_chain! {
128         if expr.span.from_expansion();
129         let first = expr.span.ctxt().outer_expn_data();
130         if first.call_site.from_expansion();
131         let second = first.call_site.ctxt().outer_expn_data();
132         then {
133             second.call_site
134         } else {
135             expr.span
136         }
137     }
138 }
139
140 fn match_panic(params: &[Expr<'_>], expr: &Expr<'_>, cx: &LateContext<'_, '_>) {
141     if_chain! {
142         if let ExprKind::Lit(ref lit) = params[0].kind;
143         if is_direct_expn_of(expr.span, "panic").is_some();
144         if let LitKind::Str(ref string, _) = lit.node;
145         let string = string.as_str().replace("{{", "").replace("}}", "");
146         if let Some(par) = string.find('{');
147         if string[par..].contains('}');
148         if params[0].span.source_callee().is_none();
149         if params[0].span.lo() != params[0].span.hi();
150         then {
151             span_lint(cx, PANIC_PARAMS, params[0].span,
152                       "you probably are missing some parameter in your format string");
153         }
154     }
155 }