|
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