Skip to content

Commit b615c82

Browse files
committed
Add new lint: used_underscore_items
1 parent e5a1ef0 commit b615c82

File tree

6 files changed

+308
-55
lines changed

6 files changed

+308
-55
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6012,6 +6012,7 @@ Released 2018-09-13
60126012
[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
60136013
[`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self
60146014
[`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding
6015+
[`used_underscore_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_items
60156016
[`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref
60166017
[`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute
60176018
[`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
485485
crate::misc::SHORT_CIRCUIT_STATEMENT_INFO,
486486
crate::misc::TOPLEVEL_REF_ARG_INFO,
487487
crate::misc::USED_UNDERSCORE_BINDING_INFO,
488+
crate::misc::USED_UNDERSCORE_ITEMS_INFO,
488489
crate::misc_early::BUILTIN_TYPE_SHADOW_INFO,
489490
crate::misc_early::DOUBLE_NEG_INFO,
490491
crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO,

clippy_lints/src/misc.rs

Lines changed: 131 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,45 @@ declare_clippy_lint! {
8080
"using a binding which is prefixed with an underscore"
8181
}
8282

83+
declare_clippy_lint! {
84+
/// ### What it does
85+
/// Checks for the use of item with a single leading
86+
/// underscore.
87+
///
88+
/// ### Why is this bad?
89+
/// A single leading underscore is usually used to indicate
90+
/// that a item will not be used. Using such a item breaks this
91+
/// expectation.
92+
///
93+
/// ### Example
94+
/// ```no_run
95+
/// fn _foo() {}
96+
///
97+
/// struct _FooStruct {}
98+
///
99+
/// fn main() {
100+
/// _foo();
101+
/// let _ = _FooStruct{};
102+
/// }
103+
/// ```
104+
///
105+
/// Use instead:
106+
/// ```no_run
107+
/// fn foo() {}
108+
///
109+
/// struct FooStruct {}
110+
///
111+
/// fn main() {
112+
/// foo();
113+
/// let _ = FooStruct{};
114+
/// }
115+
/// ```
116+
#[clippy::version = "pre 1.29.0"]
117+
pub USED_UNDERSCORE_ITEMS,
118+
pedantic,
119+
"using a item which is prefixed with an underscore"
120+
}
121+
83122
declare_clippy_lint! {
84123
/// ### What it does
85124
/// Checks for the use of short circuit boolean conditions as
@@ -104,6 +143,7 @@ declare_clippy_lint! {
104143
declare_lint_pass!(LintPass => [
105144
TOPLEVEL_REF_ARG,
106145
USED_UNDERSCORE_BINDING,
146+
USED_UNDERSCORE_ITEMS,
107147
SHORT_CIRCUIT_STATEMENT,
108148
]);
109149

@@ -205,51 +245,99 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
205245
{
206246
return;
207247
}
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-
}
248+
249+
used_underscore_binding(cx, expr);
250+
used_underscore_items(cx, expr);
251+
}
252+
}
253+
254+
fn used_underscore_items<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
255+
let (def_id, ident) = match expr.kind {
256+
ExprKind::Call(func, ..) => {
257+
if let ExprKind::Path(QPath::Resolved(.., path)) = func.kind
258+
&& let Some(last_segment) = path.segments.last()
259+
&& let Res::Def(_, def_id) = last_segment.res
260+
{
261+
(def_id, last_segment.ident)
262+
} else {
263+
return;
264+
}
265+
},
266+
ExprKind::MethodCall(path, ..) => {
267+
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
268+
(def_id, path.ident)
269+
} else {
270+
return;
271+
}
272+
},
273+
ExprKind::Struct(QPath::Resolved(_, path), ..) => {
274+
if let Some(last_segment) = path.segments.last()
275+
&& let Res::Def(_, def_id) = last_segment.res
276+
{
277+
(def_id, last_segment.ident)
278+
} else {
279+
return;
280+
}
281+
},
282+
_ => return,
283+
};
284+
let name = ident.name.as_str();
285+
let definition_span = cx.tcx.def_span(def_id);
286+
if name.starts_with('_') && !name.starts_with("__") && !definition_span.from_expansion() {
287+
span_lint_and_then(
288+
cx,
289+
USED_UNDERSCORE_ITEMS,
290+
expr.span,
291+
"used underscore-prefixed item".to_string(),
292+
|diag| {
293+
diag.span_note(definition_span, "item is defined here".to_string());
229294
},
230-
_ => return,
231-
};
295+
);
296+
}
297+
}
232298

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-
}
299+
fn used_underscore_binding<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
300+
let (definition_hir_id, ident) = match expr.kind {
301+
ExprKind::Path(ref qpath) => {
302+
if let QPath::Resolved(None, path) = qpath
303+
&& let Res::Local(id) = path.res
304+
&& is_used(cx, expr)
305+
{
306+
(id, last_path_segment(qpath).ident)
307+
} else {
308+
return;
309+
}
310+
},
311+
ExprKind::Field(recv, ident) => {
312+
if let Some(adt_def) = cx.typeck_results().expr_ty_adjusted(recv).ty_adt_def()
313+
&& let Some(field) = adt_def.all_fields().find(|field| field.name == ident.name)
314+
&& let Some(local_did) = field.did.as_local()
315+
&& !cx.tcx.type_of(field.did).skip_binder().is_phantom_data()
316+
{
317+
(cx.tcx.local_def_id_to_hir_id(local_did), ident)
318+
} else {
319+
return;
320+
}
321+
},
322+
_ => return,
323+
};
324+
325+
let name = ident.name.as_str();
326+
if name.starts_with('_')
327+
&& !name.starts_with("__")
328+
&& let definition_span = cx.tcx.hir().span(definition_hir_id)
329+
&& !definition_span.from_expansion()
330+
&& !fulfill_or_allowed(cx, USED_UNDERSCORE_BINDING, [expr.hir_id, definition_hir_id])
331+
{
332+
span_lint_and_then(
333+
cx,
334+
USED_UNDERSCORE_BINDING,
335+
expr.span,
336+
"used underscore-prefixed binding".to_string(),
337+
|diag| {
338+
diag.span_note(definition_span, "binding is defined here".to_string());
339+
},
340+
);
253341
}
254342
}
255343

tests/ui/used_underscore_binding.stderr

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,72 @@
1-
error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
1+
error: used underscore-prefixed binding
22
--> tests/ui/used_underscore_binding.rs:23:5
33
|
44
LL | _foo + 1
55
| ^^^^
66
|
7-
note: `_foo` is defined here
7+
note: binding is defined here
88
--> tests/ui/used_underscore_binding.rs:22:22
99
|
1010
LL | fn prefix_underscore(_foo: u32) -> u32 {
1111
| ^^^^
1212
= note: `-D clippy::used-underscore-binding` implied by `-D warnings`
1313
= help: to override `-D warnings` add `#[allow(clippy::used_underscore_binding)]`
1414

15-
error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
15+
error: used underscore-prefixed binding
1616
--> tests/ui/used_underscore_binding.rs:28:20
1717
|
1818
LL | println!("{}", _foo);
1919
| ^^^^
2020
|
21-
note: `_foo` is defined here
21+
note: binding is defined here
2222
--> tests/ui/used_underscore_binding.rs:27:24
2323
|
2424
LL | fn in_macro_or_desugar(_foo: u32) {
2525
| ^^^^
2626

27-
error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
27+
error: used underscore-prefixed binding
2828
--> tests/ui/used_underscore_binding.rs:29:16
2929
|
3030
LL | assert_eq!(_foo, _foo);
3131
| ^^^^
3232
|
33-
note: `_foo` is defined here
33+
note: binding is defined here
3434
--> tests/ui/used_underscore_binding.rs:27:24
3535
|
3636
LL | fn in_macro_or_desugar(_foo: u32) {
3737
| ^^^^
3838

39-
error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
39+
error: used underscore-prefixed binding
4040
--> tests/ui/used_underscore_binding.rs:29:22
4141
|
4242
LL | assert_eq!(_foo, _foo);
4343
| ^^^^
4444
|
45-
note: `_foo` is defined here
45+
note: binding is defined here
4646
--> tests/ui/used_underscore_binding.rs:27:24
4747
|
4848
LL | fn in_macro_or_desugar(_foo: u32) {
4949
| ^^^^
5050

51-
error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
51+
error: used underscore-prefixed binding
5252
--> tests/ui/used_underscore_binding.rs:42:5
5353
|
5454
LL | s._underscore_field += 1;
5555
| ^^^^^^^^^^^^^^^^^^^
5656
|
57-
note: `_underscore_field` is defined here
57+
note: binding is defined here
5858
--> tests/ui/used_underscore_binding.rs:36:5
5959
|
6060
LL | _underscore_field: u32,
6161
| ^^^^^^^^^^^^^^^^^^^^^^
6262

63-
error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
63+
error: used underscore-prefixed binding
6464
--> tests/ui/used_underscore_binding.rs:103:16
6565
|
6666
LL | uses_i(_i);
6767
| ^^
6868
|
69-
note: `_i` is defined here
69+
note: binding is defined here
7070
--> tests/ui/used_underscore_binding.rs:102:13
7171
|
7272
LL | let _i = 5;

tests/ui/used_underscore_items.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#![allow(unused)]
2+
#![warn(clippy::used_underscore_items)]
3+
4+
// should not lint macro
5+
macro_rules! macro_wrap_func {
6+
() => {
7+
fn _marco_foo() {}
8+
};
9+
}
10+
11+
macro_wrap_func!();
12+
13+
struct _FooStruct {}
14+
15+
impl _FooStruct {
16+
fn _method_call(self) {}
17+
}
18+
19+
fn _foo1() {}
20+
21+
fn _foo2() -> i32 {
22+
0
23+
}
24+
25+
mod a {
26+
pub mod b {
27+
pub mod c {
28+
pub fn _foo3() {}
29+
30+
pub struct _FooStruct2 {}
31+
32+
impl _FooStruct2 {
33+
pub fn _method_call(self) {}
34+
}
35+
}
36+
}
37+
}
38+
39+
fn main() {
40+
_foo1();
41+
let _ = _foo2();
42+
a::b::c::_foo3();
43+
let _ = &_FooStruct {};
44+
let _ = _FooStruct {};
45+
46+
let foo_struct = _FooStruct {};
47+
foo_struct._method_call();
48+
49+
let foo_struct2 = a::b::c::_FooStruct2 {};
50+
foo_struct2._method_call();
51+
}

0 commit comments

Comments
 (0)