]>
Commit | Line | Data |
---|---|---|
5e7ed085 | 1 | use clippy_utils::diagnostics::span_lint_and_sugg; |
31ef2f64 | 2 | use clippy_utils::is_in_test; |
e8be2606 | 3 | use clippy_utils::macros::{macro_backtrace, MacroCall}; |
5e7ed085 | 4 | use clippy_utils::source::snippet_with_applicability; |
e8be2606 | 5 | use rustc_data_structures::fx::FxHashSet; |
f20569fa | 6 | use rustc_errors::Applicability; |
31ef2f64 | 7 | use rustc_hir::{Expr, ExprKind, Node}; |
49aad941 | 8 | use rustc_lint::{LateContext, LateLintPass, LintContext}; |
e8be2606 | 9 | use rustc_middle::lint::in_external_macro; |
4b012472 | 10 | use rustc_session::impl_lint_pass; |
e8be2606 | 11 | use rustc_span::{sym, Span, SyntaxContext}; |
f20569fa XL |
12 | |
13 | declare_clippy_lint! { | |
94222f64 | 14 | /// ### What it does |
9c376795 | 15 | /// Checks for usage of the [`dbg!`](https://doc.rust-lang.org/std/macro.dbg.html) macro. |
f20569fa | 16 | /// |
31ef2f64 | 17 | /// ### Why restrict this? |
9c376795 FG |
18 | /// The `dbg!` macro is intended as a debugging tool. It should not be present in released |
19 | /// software or committed to a version control system. | |
f20569fa | 20 | /// |
94222f64 | 21 | /// ### Example |
f20569fa | 22 | /// ```rust,ignore |
f20569fa | 23 | /// dbg!(true) |
923072b8 | 24 | /// ``` |
f20569fa | 25 | /// |
923072b8 FG |
26 | /// Use instead: |
27 | /// ```rust,ignore | |
f20569fa XL |
28 | /// true |
29 | /// ``` | |
a2a8927a | 30 | #[clippy::version = "1.34.0"] |
f20569fa XL |
31 | pub DBG_MACRO, |
32 | restriction, | |
33 | "`dbg!` macro is intended as a debugging tool" | |
34 | } | |
35 | ||
e8be2606 | 36 | #[derive(Clone)] |
923072b8 FG |
37 | pub struct DbgMacro { |
38 | allow_dbg_in_tests: bool, | |
e8be2606 FG |
39 | /// Tracks the `dbg!` macro callsites that are already checked. |
40 | checked_dbg_call_site: FxHashSet<Span>, | |
41 | /// Tracks the previous `SyntaxContext`, to avoid walking the same context chain. | |
42 | prev_ctxt: SyntaxContext, | |
923072b8 FG |
43 | } |
44 | ||
45 | impl_lint_pass!(DbgMacro => [DBG_MACRO]); | |
46 | ||
47 | impl DbgMacro { | |
48 | pub fn new(allow_dbg_in_tests: bool) -> Self { | |
e8be2606 FG |
49 | DbgMacro { |
50 | allow_dbg_in_tests, | |
51 | checked_dbg_call_site: FxHashSet::default(), | |
52 | prev_ctxt: SyntaxContext::root(), | |
53 | } | |
923072b8 FG |
54 | } |
55 | } | |
f20569fa | 56 | |
5e7ed085 FG |
57 | impl LateLintPass<'_> for DbgMacro { |
58 | fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { | |
e8be2606 FG |
59 | let cur_syntax_ctxt = expr.span.ctxt(); |
60 | ||
61 | if cur_syntax_ctxt != self.prev_ctxt && | |
62 | let Some(macro_call) = first_dbg_macro_in_expansion(cx, expr.span) && | |
63 | !in_external_macro(cx.sess(), macro_call.span) && | |
64 | self.checked_dbg_call_site.insert(macro_call.span) && | |
923072b8 | 65 | // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml |
31ef2f64 | 66 | !(self.allow_dbg_in_tests && is_in_test(cx.tcx, expr.hir_id)) |
e8be2606 | 67 | { |
5e7ed085 | 68 | let mut applicability = Applicability::MachineApplicable; |
49aad941 FG |
69 | |
70 | let (sugg_span, suggestion) = match expr.peel_drop_temps().kind { | |
5e7ed085 | 71 | // dbg!() |
49aad941 FG |
72 | ExprKind::Block(..) => { |
73 | // If the `dbg!` macro is a "free" statement and not contained within other expressions, | |
74 | // remove the whole statement. | |
c620b35d | 75 | if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) |
4b012472 | 76 | && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) |
49aad941 | 77 | { |
4b012472 | 78 | (macro_call.span.to(semi_span), String::new()) |
49aad941 FG |
79 | } else { |
80 | (macro_call.span, String::from("()")) | |
81 | } | |
5e7ed085 | 82 | }, |
49aad941 FG |
83 | // dbg!(1) |
84 | ExprKind::Match(val, ..) => ( | |
85 | macro_call.span, | |
86 | snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability).to_string(), | |
87 | ), | |
5e7ed085 FG |
88 | // dbg!(2, 3) |
89 | ExprKind::Tup( | |
90 | [ | |
91 | Expr { | |
92 | kind: ExprKind::Match(first, ..), | |
93 | .. | |
94 | }, | |
95 | .., | |
96 | Expr { | |
97 | kind: ExprKind::Match(last, ..), | |
98 | .. | |
99 | }, | |
100 | ], | |
101 | ) => { | |
102 | let snippet = snippet_with_applicability( | |
103 | cx, | |
104 | first.span.source_callsite().to(last.span.source_callsite()), | |
105 | "..", | |
106 | &mut applicability, | |
107 | ); | |
49aad941 | 108 | (macro_call.span, format!("({snippet})")) |
5e7ed085 FG |
109 | }, |
110 | _ => return, | |
111 | }; | |
112 | ||
e8be2606 FG |
113 | self.prev_ctxt = cur_syntax_ctxt; |
114 | ||
5e7ed085 FG |
115 | span_lint_and_sugg( |
116 | cx, | |
117 | DBG_MACRO, | |
49aad941 | 118 | sugg_span, |
9c376795 FG |
119 | "the `dbg!` macro is intended as a debugging tool", |
120 | "remove the invocation before committing it to a version control system", | |
5e7ed085 FG |
121 | suggestion, |
122 | applicability, | |
123 | ); | |
f20569fa XL |
124 | } |
125 | } | |
e8be2606 FG |
126 | |
127 | fn check_crate_post(&mut self, _: &LateContext<'_>) { | |
128 | self.checked_dbg_call_site = FxHashSet::default(); | |
129 | } | |
130 | } | |
131 | ||
e8be2606 FG |
132 | fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option<MacroCall> { |
133 | macro_backtrace(span).find(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id)) | |
f20569fa | 134 | } |