|
1 |
| -use std::ops::ControlFlow; |
2 |
| - |
| 1 | +use clippy_config::types::DisallowedPath; |
3 | 2 | use clippy_utils::diagnostics::span_lint_and_then;
|
4 | 3 | use clippy_utils::source::snippet_with_applicability;
|
5 |
| -use clippy_utils::visitors::for_each_expr_with_closures; |
6 | 4 | use clippy_utils::{def_path_def_ids, fn_def_id, is_lint_allowed};
|
7 | 5 | use rustc_data_structures::fx::FxHashMap;
|
8 | 6 | use rustc_errors::{Applicability, Diagnostic};
|
9 | 7 | use rustc_hir::def_id::DefId;
|
10 |
| -use rustc_hir::hir_id::CRATE_HIR_ID; |
11 |
| -use rustc_hir::{Body, Expr, ExprKind, GeneratorKind, HirIdSet}; |
| 8 | +use rustc_hir::{Body, CoroutineKind, Expr, ExprKind}; |
12 | 9 | use rustc_lint::{LateContext, LateLintPass};
|
13 | 10 | use rustc_session::{declare_tool_lint, impl_lint_pass};
|
14 | 11 | use rustc_span::Span;
|
@@ -48,113 +45,98 @@ declare_clippy_lint! {
|
48 | 45 | }
|
49 | 46 |
|
50 | 47 | pub(crate) struct UnnecessaryBlockingOps {
|
51 |
| - blocking_ops: Vec<String>, |
52 |
| - blocking_ops_with_suggs: Vec<[String; 2]>, |
| 48 | + blocking_ops: Vec<DisallowedPath>, |
53 | 49 | /// Map of resolved funtion def_id with suggestion string after checking crate
|
54 | 50 | id_with_suggs: FxHashMap<DefId, Option<String>>,
|
55 |
| - /// Keep track of visited block ids to skip checking the same bodies in `check_body` calls |
56 |
| - visited_block: HirIdSet, |
| 51 | + is_in_async: bool, |
57 | 52 | }
|
58 | 53 |
|
59 | 54 | impl UnnecessaryBlockingOps {
|
60 |
| - pub(crate) fn new(blocking_ops: Vec<String>, blocking_ops_with_suggs: Vec<[String; 2]>) -> Self { |
| 55 | + pub(crate) fn new(blocking_ops: Vec<DisallowedPath>) -> Self { |
61 | 56 | Self {
|
62 | 57 | blocking_ops,
|
63 |
| - blocking_ops_with_suggs, |
64 | 58 | id_with_suggs: FxHashMap::default(),
|
65 |
| - visited_block: HirIdSet::default(), |
| 59 | + is_in_async: false, |
66 | 60 | }
|
67 | 61 | }
|
68 | 62 | }
|
69 | 63 |
|
70 | 64 | impl_lint_pass!(UnnecessaryBlockingOps => [UNNECESSARY_BLOCKING_OPS]);
|
71 | 65 |
|
72 |
| -static HARD_CODED_BLOCKING_OPS: [&[&str]; 21] = [ |
73 |
| - &["std", "thread", "sleep"], |
| 66 | +static HARD_CODED_BLOCKING_OP_PATHS: &[&str] = &[ |
| 67 | + "std::thread::sleep", |
74 | 68 | // Filesystem functions
|
75 |
| - &["std", "fs", "try_exists"], |
76 |
| - &["std", "fs", "canonicalize"], |
77 |
| - &["std", "fs", "copy"], |
78 |
| - &["std", "fs", "create_dir"], |
79 |
| - &["std", "fs", "create_dir_all"], |
80 |
| - &["std", "fs", "hard_link"], |
81 |
| - &["std", "fs", "metadata"], |
82 |
| - &["std", "fs", "read"], |
83 |
| - &["std", "fs", "read_dir"], |
84 |
| - &["std", "fs", "read_link"], |
85 |
| - &["std", "fs", "read_to_string"], |
86 |
| - &["std", "fs", "remove_dir"], |
87 |
| - &["std", "fs", "remove_dir_all"], |
88 |
| - &["std", "fs", "remove_file"], |
89 |
| - &["std", "fs", "rename"], |
90 |
| - &["std", "fs", "set_permissions"], |
91 |
| - &["std", "fs", "symlink_metadata"], |
92 |
| - &["std", "fs", "write"], |
| 69 | + "std::fs::try_exists", |
| 70 | + "std::fs::canonicalize", |
| 71 | + "std::fs::copy", |
| 72 | + "std::fs::create_dir", |
| 73 | + "std::fs::create_dir_all", |
| 74 | + "std::fs::hard_link", |
| 75 | + "std::fs::metadata", |
| 76 | + "std::fs::read", |
| 77 | + "std::fs::read_dir", |
| 78 | + "std::fs::read_link", |
| 79 | + "std::fs::read_to_string", |
| 80 | + "std::fs::remove_dir", |
| 81 | + "std::fs::remove_dir_all", |
| 82 | + "std::fs::remove_file", |
| 83 | + "std::fs::rename", |
| 84 | + "std::fs::set_permissions", |
| 85 | + "std::fs::symlink_metadata", |
| 86 | + "std::fs::write", |
93 | 87 | // IO functions
|
94 |
| - &["std", "io", "copy"], |
95 |
| - &["std", "io", "read_to_string"], |
| 88 | + "std::io::copy", |
| 89 | + "std::io::read_to_string", |
96 | 90 | ];
|
97 | 91 |
|
98 | 92 | impl<'tcx> LateLintPass<'tcx> for UnnecessaryBlockingOps {
|
99 | 93 | fn check_crate(&mut self, cx: &LateContext<'tcx>) {
|
100 |
| - // Avoids processing and storing a long list of paths if this lint was allowed entirely |
101 |
| - if is_lint_allowed(cx, UNNECESSARY_BLOCKING_OPS, CRATE_HIR_ID) { |
102 |
| - return; |
103 |
| - } |
104 |
| - |
105 |
| - let full_fn_list = HARD_CODED_BLOCKING_OPS |
106 |
| - .into_iter() |
107 |
| - .map(|p| (p.to_vec(), None)) |
108 |
| - // Chain configured functions without suggestions |
109 |
| - .chain( |
110 |
| - self.blocking_ops |
111 |
| - .iter() |
112 |
| - .map(|p| (p.split("::").collect::<Vec<_>>(), None)), |
113 |
| - ) |
114 |
| - // Chain configured functions with suggestions |
115 |
| - .chain( |
116 |
| - self.blocking_ops_with_suggs |
117 |
| - .iter() |
118 |
| - .map(|[p, s]| (p.split("::").collect::<Vec<_>>(), Some(s.as_str()))), |
119 |
| - ); |
120 |
| - for (path, maybe_sugg_str) in full_fn_list { |
| 94 | + let full_fn_list = HARD_CODED_BLOCKING_OP_PATHS |
| 95 | + .iter() |
| 96 | + .map(|p| (*p, None)) |
| 97 | + // Chain configured functions with possible suggestions |
| 98 | + .chain(self.blocking_ops.iter().map(|p| (p.path(), p.suggestion()))); |
| 99 | + for (path_str, maybe_sugg_str) in full_fn_list { |
| 100 | + let path: Vec<&str> = path_str.split("::").collect(); |
121 | 101 | for did in def_path_def_ids(cx, &path) {
|
122 | 102 | self.id_with_suggs.insert(did, maybe_sugg_str.map(ToOwned::to_owned));
|
123 | 103 | }
|
124 | 104 | }
|
125 | 105 | }
|
126 | 106 |
|
127 | 107 | fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) {
|
128 |
| - if is_lint_allowed(cx, UNNECESSARY_BLOCKING_OPS, body.value.hir_id) |
129 |
| - || self.visited_block.contains(&body.value.hir_id) |
130 |
| - { |
| 108 | + if is_lint_allowed(cx, UNNECESSARY_BLOCKING_OPS, body.value.hir_id) { |
131 | 109 | return;
|
132 | 110 | }
|
133 |
| - if let Some(GeneratorKind::Async(_)) = body.generator_kind() { |
134 |
| - for_each_expr_with_closures(cx, body, |ex| { |
135 |
| - match ex.kind { |
136 |
| - ExprKind::Block(block, _) => { |
137 |
| - self.visited_block.insert(block.hir_id); |
138 |
| - } |
139 |
| - ExprKind::Call(call, _) |
140 |
| - if let Some(call_did) = fn_def_id(cx, ex) && |
141 |
| - let Some(maybe_sugg) = self.id_with_suggs.get(&call_did) => { |
142 |
| - span_lint_and_then( |
143 |
| - cx, |
144 |
| - UNNECESSARY_BLOCKING_OPS, |
145 |
| - call.span, |
146 |
| - "blocking function call detected in an async body", |
147 |
| - |diag| { |
148 |
| - if let Some(sugg_fn_path) = maybe_sugg { |
149 |
| - make_suggestion(diag, cx, ex, call.span, sugg_fn_path); |
150 |
| - } |
151 |
| - } |
152 |
| - ); |
| 111 | + |
| 112 | + if let Some(CoroutineKind::Async(_)) = body.coroutine_kind() { |
| 113 | + self.is_in_async = true; |
| 114 | + } |
| 115 | + } |
| 116 | + |
| 117 | + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { |
| 118 | + if self.is_in_async && |
| 119 | + let ExprKind::Call(call, _) = &expr.kind && |
| 120 | + let Some(call_did) = fn_def_id(cx, expr) && |
| 121 | + let Some(maybe_sugg) = self.id_with_suggs.get(&call_did) |
| 122 | + { |
| 123 | + span_lint_and_then( |
| 124 | + cx, |
| 125 | + UNNECESSARY_BLOCKING_OPS, |
| 126 | + call.span, |
| 127 | + "blocking function call detected in an async body", |
| 128 | + |diag| { |
| 129 | + if let Some(sugg_fn_path) = maybe_sugg { |
| 130 | + make_suggestion(diag, cx, expr, call.span, sugg_fn_path); |
153 | 131 | }
|
154 |
| - _ => {} |
155 | 132 | }
|
156 |
| - ControlFlow::<()>::Continue(()) |
157 |
| - }); |
| 133 | + ); |
| 134 | + } |
| 135 | + } |
| 136 | + |
| 137 | + fn check_body_post(&mut self, _: &LateContext<'tcx>, body: &'tcx Body<'tcx>) { |
| 138 | + if !matches!(body.coroutine_kind(), Some(CoroutineKind::Async(_))) { |
| 139 | + self.is_in_async = false; |
158 | 140 | }
|
159 | 141 | }
|
160 | 142 | }
|
|
0 commit comments