@@ -7,6 +7,7 @@ use clippy_utils::{
77} ;
88use rustc_errors:: Applicability ;
99use rustc_hir:: def:: Res ;
10+ use rustc_hir:: def_id:: LOCAL_CRATE ;
1011use rustc_hir:: intravisit:: FnKind ;
1112use rustc_hir:: {
1213 BinOpKind , BindingMode , Body , ByRef , Expr , ExprKind , FnDecl , Mutability , PatKind , QPath , Stmt , StmtKind ,
@@ -80,6 +81,45 @@ declare_clippy_lint! {
8081 "using a binding which is prefixed with an underscore"
8182}
8283
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+
83123declare_clippy_lint ! {
84124 /// ### What it does
85125 /// Checks for the use of short circuit boolean conditions as
@@ -104,6 +144,7 @@ declare_clippy_lint! {
104144declare_lint_pass ! ( LintPass => [
105145 TOPLEVEL_REF_ARG ,
106146 USED_UNDERSCORE_BINDING ,
147+ USED_UNDERSCORE_ITEMS ,
107148 SHORT_CIRCUIT_STATEMENT ,
108149] ) ;
109150
@@ -205,51 +246,104 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
205246 {
206247 return ;
207248 }
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 ( ) ) ;
229300 } ,
230- _ => return ,
231- } ;
301+ ) ;
302+ }
303+ }
232304
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+ ) ;
253347 }
254348}
255349
0 commit comments