@@ -7,6 +7,7 @@ use clippy_utils::{
7
7
} ;
8
8
use rustc_errors:: Applicability ;
9
9
use rustc_hir:: def:: Res ;
10
+ use rustc_hir:: def_id:: LOCAL_CRATE ;
10
11
use rustc_hir:: intravisit:: FnKind ;
11
12
use rustc_hir:: {
12
13
BinOpKind , BindingMode , Body , ByRef , Expr , ExprKind , FnDecl , Mutability , PatKind , QPath , Stmt , StmtKind ,
@@ -80,6 +81,45 @@ declare_clippy_lint! {
80
81
"using a binding which is prefixed with an underscore"
81
82
}
82
83
84
+ declare_clippy_lint ! {
85
+ /// ### What it does
86
+ /// Checks for the use of item with a single leading
87
+ /// underscore.
88
+ ///
89
+ /// ### Why is this bad?
90
+ /// A single leading underscore is usually used to indicate
91
+ /// that a item will not be used. Using such a item breaks this
92
+ /// expectation.
93
+ ///
94
+ /// ### Example
95
+ /// ```no_run
96
+ /// fn _foo() {}
97
+ ///
98
+ /// struct _FooStruct {}
99
+ ///
100
+ /// fn main() {
101
+ /// _foo();
102
+ /// let _ = _FooStruct{};
103
+ /// }
104
+ /// ```
105
+ ///
106
+ /// Use instead:
107
+ /// ```no_run
108
+ /// fn foo() {}
109
+ ///
110
+ /// struct FooStruct {}
111
+ ///
112
+ /// fn main() {
113
+ /// foo();
114
+ /// let _ = FooStruct{};
115
+ /// }
116
+ /// ```
117
+ #[ clippy:: version = "pre 1.29.0" ]
118
+ pub USED_UNDERSCORE_ITEMS ,
119
+ pedantic,
120
+ "using a item which is prefixed with an underscore"
121
+ }
122
+
83
123
declare_clippy_lint ! {
84
124
/// ### What it does
85
125
/// Checks for the use of short circuit boolean conditions as
@@ -104,6 +144,7 @@ declare_clippy_lint! {
104
144
declare_lint_pass ! ( LintPass => [
105
145
TOPLEVEL_REF_ARG ,
106
146
USED_UNDERSCORE_BINDING ,
147
+ USED_UNDERSCORE_ITEMS ,
107
148
SHORT_CIRCUIT_STATEMENT ,
108
149
] ) ;
109
150
@@ -205,51 +246,104 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
205
246
{
206
247
return ;
207
248
}
208
- let ( definition_hir_id, ident) = match expr. kind {
209
- ExprKind :: Path ( ref qpath) => {
210
- if let QPath :: Resolved ( None , path) = qpath
211
- && let Res :: Local ( id) = path. res
212
- && is_used ( cx, expr)
213
- {
214
- ( id, last_path_segment ( qpath) . ident )
215
- } else {
216
- return ;
217
- }
218
- } ,
219
- ExprKind :: Field ( recv, ident) => {
220
- if let Some ( adt_def) = cx. typeck_results ( ) . expr_ty_adjusted ( recv) . ty_adt_def ( )
221
- && let Some ( field) = adt_def. all_fields ( ) . find ( |field| field. name == ident. name )
222
- && let Some ( local_did) = field. did . as_local ( )
223
- && !cx. tcx . type_of ( field. did ) . skip_binder ( ) . is_phantom_data ( )
224
- {
225
- ( cx. tcx . local_def_id_to_hir_id ( local_did) , ident)
226
- } else {
227
- return ;
228
- }
249
+
250
+ used_underscore_binding ( cx, expr) ;
251
+ used_underscore_items ( cx, expr) ;
252
+ }
253
+ }
254
+
255
+ fn used_underscore_items < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
256
+ let ( def_id, ident) = match expr. kind {
257
+ ExprKind :: Call ( func, ..) => {
258
+ if let ExprKind :: Path ( QPath :: Resolved ( .., path) ) = func. kind
259
+ && let Some ( last_segment) = path. segments . last ( )
260
+ && let Res :: Def ( _, def_id) = last_segment. res
261
+ {
262
+ ( def_id, last_segment. ident )
263
+ } else {
264
+ return ;
265
+ }
266
+ } ,
267
+ ExprKind :: MethodCall ( path, ..) => {
268
+ if let Some ( def_id) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id ) {
269
+ ( def_id, path. ident )
270
+ } else {
271
+ return ;
272
+ }
273
+ } ,
274
+ ExprKind :: Struct ( QPath :: Resolved ( _, path) , ..) => {
275
+ if let Some ( last_segment) = path. segments . last ( )
276
+ && let Res :: Def ( _, def_id) = last_segment. res
277
+ {
278
+ ( def_id, last_segment. ident )
279
+ } else {
280
+ return ;
281
+ }
282
+ } ,
283
+ _ => return ,
284
+ } ;
285
+
286
+ let name = ident. name . as_str ( ) ;
287
+ let definition_span = cx. tcx . def_span ( def_id) ;
288
+ if name. starts_with ( '_' )
289
+ && !name. starts_with ( "__" )
290
+ && !definition_span. from_expansion ( )
291
+ && def_id. krate == LOCAL_CRATE
292
+ {
293
+ span_lint_and_then (
294
+ cx,
295
+ USED_UNDERSCORE_ITEMS ,
296
+ expr. span ,
297
+ "used underscore-prefixed item" . to_string ( ) ,
298
+ |diag| {
299
+ diag. span_note ( definition_span, "item is defined here" . to_string ( ) ) ;
229
300
} ,
230
- _ => return ,
231
- } ;
301
+ ) ;
302
+ }
303
+ }
232
304
233
- let name = ident. name . as_str ( ) ;
234
- if name. starts_with ( '_' )
235
- && !name. starts_with ( "__" )
236
- && let definition_span = cx. tcx . hir ( ) . span ( definition_hir_id)
237
- && !definition_span. from_expansion ( )
238
- && !fulfill_or_allowed ( cx, USED_UNDERSCORE_BINDING , [ expr. hir_id , definition_hir_id] )
239
- {
240
- span_lint_and_then (
241
- cx,
242
- USED_UNDERSCORE_BINDING ,
243
- expr. span ,
244
- format ! (
245
- "used binding `{name}` which is prefixed with an underscore. A leading \
246
- underscore signals that a binding will not be used"
247
- ) ,
248
- |diag| {
249
- diag. span_note ( definition_span, format ! ( "`{name}` is defined here" ) ) ;
250
- } ,
251
- ) ;
252
- }
305
+ fn used_underscore_binding < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
306
+ let ( definition_hir_id, ident) = match expr. kind {
307
+ ExprKind :: Path ( ref qpath) => {
308
+ if let QPath :: Resolved ( None , path) = qpath
309
+ && let Res :: Local ( id) = path. res
310
+ && is_used ( cx, expr)
311
+ {
312
+ ( id, last_path_segment ( qpath) . ident )
313
+ } else {
314
+ return ;
315
+ }
316
+ } ,
317
+ ExprKind :: Field ( recv, ident) => {
318
+ if let Some ( adt_def) = cx. typeck_results ( ) . expr_ty_adjusted ( recv) . ty_adt_def ( )
319
+ && let Some ( field) = adt_def. all_fields ( ) . find ( |field| field. name == ident. name )
320
+ && let Some ( local_did) = field. did . as_local ( )
321
+ && !cx. tcx . type_of ( field. did ) . skip_binder ( ) . is_phantom_data ( )
322
+ {
323
+ ( cx. tcx . local_def_id_to_hir_id ( local_did) , ident)
324
+ } else {
325
+ return ;
326
+ }
327
+ } ,
328
+ _ => return ,
329
+ } ;
330
+
331
+ let name = ident. name . as_str ( ) ;
332
+ if name. starts_with ( '_' )
333
+ && !name. starts_with ( "__" )
334
+ && let definition_span = cx. tcx . hir ( ) . span ( definition_hir_id)
335
+ && !definition_span. from_expansion ( )
336
+ && !fulfill_or_allowed ( cx, USED_UNDERSCORE_BINDING , [ expr. hir_id , definition_hir_id] )
337
+ {
338
+ span_lint_and_then (
339
+ cx,
340
+ USED_UNDERSCORE_BINDING ,
341
+ expr. span ,
342
+ "used underscore-prefixed binding" . to_string ( ) ,
343
+ |diag| {
344
+ diag. span_note ( definition_span, "binding is defined here" . to_string ( ) ) ;
345
+ } ,
346
+ ) ;
253
347
}
254
348
}
255
349
0 commit comments