Skip to content

Commit b38b026

Browse files
committed
Auto merge of #4823 - Areredify:must_use_res, r=flip1995
Add `let_underscore_must_use` lint changelog: closes #4812 , added a new `let_underscore_must_use` lint, moved `is_must_use_ty` to utils, added `is_must_use_fn` util function
2 parents 40881e7 + a310cb2 commit b38b026

File tree

9 files changed

+331
-49
lines changed

9 files changed

+331
-49
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1064,6 +1064,7 @@ Released 2018-09-13
10641064
[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
10651065
[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
10661066
[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
1067+
[`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
10671068
[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
10681069
[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
10691070
[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
88

9-
[There are 339 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 340 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

1111
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1212

clippy_lints/src/functions.rs

+3-46
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::utils::{
2-
attrs::is_proc_macro, iter_input_pats, match_def_path, qpath_res, return_ty, snippet, snippet_opt,
3-
span_help_and_lint, span_lint, span_lint_and_then, trait_ref_of_method, type_is_unsafe_function,
2+
attrs::is_proc_macro, is_must_use_ty, iter_input_pats, match_def_path, must_use_attr, qpath_res, return_ty,
3+
snippet, snippet_opt, span_help_and_lint, span_lint, span_lint_and_then, trait_ref_of_method,
4+
type_is_unsafe_function,
45
};
56
use matches::matches;
67
use rustc::hir::{self, def::Res, def_id::DefId, intravisit};
@@ -466,15 +467,6 @@ fn check_must_use_candidate<'a, 'tcx>(
466467
});
467468
}
468469

469-
fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
470-
attrs.iter().find(|attr| {
471-
attr.ident().map_or(false, |ident| {
472-
let ident: &str = &ident.as_str();
473-
"must_use" == ident
474-
})
475-
})
476-
}
477-
478470
fn returns_unit(decl: &hir::FnDecl) -> bool {
479471
match decl.output {
480472
hir::FunctionRetTy::DefaultReturn(_) => true,
@@ -486,41 +478,6 @@ fn returns_unit(decl: &hir::FnDecl) -> bool {
486478
}
487479
}
488480

489-
fn is_must_use_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
490-
use ty::TyKind::*;
491-
match ty.kind {
492-
Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(),
493-
Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(),
494-
Slice(ref ty) | Array(ref ty, _) | RawPtr(ty::TypeAndMut { ref ty, .. }) | Ref(_, ref ty, _) => {
495-
// for the Array case we don't need to care for the len == 0 case
496-
// because we don't want to lint functions returning empty arrays
497-
is_must_use_ty(cx, *ty)
498-
},
499-
Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)),
500-
Opaque(ref def_id, _) => {
501-
for (predicate, _) in cx.tcx.predicates_of(*def_id).predicates {
502-
if let ty::Predicate::Trait(ref poly_trait_predicate) = predicate {
503-
if must_use_attr(&cx.tcx.get_attrs(poly_trait_predicate.skip_binder().trait_ref.def_id)).is_some() {
504-
return true;
505-
}
506-
}
507-
}
508-
false
509-
},
510-
Dynamic(binder, _) => {
511-
for predicate in binder.skip_binder().iter() {
512-
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate {
513-
if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() {
514-
return true;
515-
}
516-
}
517-
}
518-
false
519-
},
520-
_ => false,
521-
}
522-
}
523-
524481
fn has_mutable_arg(cx: &LateContext<'_, '_>, body: &hir::Body<'_>) -> bool {
525482
let mut tys = FxHashSet::default();
526483
body.params.iter().any(|param| is_mutable_pat(cx, &param.pat, &mut tys))

clippy_lints/src/let_underscore.rs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use if_chain::if_chain;
2+
use rustc::declare_lint_pass;
3+
use rustc::hir::*;
4+
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
5+
use rustc_session::declare_tool_lint;
6+
7+
use crate::utils::{is_must_use_func_call, is_must_use_ty, span_help_and_lint};
8+
9+
declare_clippy_lint! {
10+
/// **What it does:** Checks for `let _ = <expr>`
11+
/// where expr is #[must_use]
12+
///
13+
/// **Why is this bad?** It's better to explicitly
14+
/// handle the value of a #[must_use] expr
15+
///
16+
/// **Known problems:** None.
17+
///
18+
/// **Example:**
19+
/// ```rust
20+
/// fn f() -> Result<u32, u32> {
21+
/// Ok(0)
22+
/// }
23+
///
24+
/// let _ = f();
25+
/// // is_ok() is marked #[must_use]
26+
/// let _ = f().is_ok();
27+
/// ```
28+
pub LET_UNDERSCORE_MUST_USE,
29+
restriction,
30+
"non-binding let on a #[must_use] expression"
31+
}
32+
33+
declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE]);
34+
35+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetUnderscore {
36+
fn check_stmt(&mut self, cx: &LateContext<'_, '_>, stmt: &Stmt) {
37+
if_chain! {
38+
if let StmtKind::Local(ref local) = stmt.kind;
39+
if let PatKind::Wild = local.pat.kind;
40+
if let Some(ref init) = local.init;
41+
then {
42+
if is_must_use_ty(cx, cx.tables.expr_ty(init)) {
43+
span_help_and_lint(
44+
cx,
45+
LET_UNDERSCORE_MUST_USE,
46+
stmt.span,
47+
"non-binding let on an expression with #[must_use] type",
48+
"consider explicitly using expression value"
49+
)
50+
} else if is_must_use_func_call(cx, init) {
51+
span_help_and_lint(
52+
cx,
53+
LET_UNDERSCORE_MUST_USE,
54+
stmt.span,
55+
"non-binding let on a result of a #[must_use] function",
56+
"consider explicitly using function result"
57+
)
58+
}
59+
}
60+
}
61+
}
62+
}

clippy_lints/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ pub mod large_enum_variant;
220220
pub mod large_stack_arrays;
221221
pub mod len_zero;
222222
pub mod let_if_seq;
223+
pub mod let_underscore;
223224
pub mod lifetimes;
224225
pub mod literal_representation;
225226
pub mod loops;
@@ -555,6 +556,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
555556
&len_zero::LEN_WITHOUT_IS_EMPTY,
556557
&len_zero::LEN_ZERO,
557558
&let_if_seq::USELESS_LET_IF_SEQ,
559+
&let_underscore::LET_UNDERSCORE_MUST_USE,
558560
&lifetimes::EXTRA_UNUSED_LIFETIMES,
559561
&lifetimes::NEEDLESS_LIFETIMES,
560562
&literal_representation::DECIMAL_LITERAL_REPRESENTATION,
@@ -970,6 +972,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
970972
store.register_late_pass(move || box large_stack_arrays::LargeStackArrays::new(array_size_threshold));
971973
store.register_early_pass(|| box as_conversions::AsConversions);
972974
store.register_early_pass(|| box utils::internal_lints::ProduceIce);
975+
store.register_late_pass(|| box let_underscore::LetUnderscore);
973976

974977
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
975978
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -982,6 +985,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
982985
LintId::of(&indexing_slicing::INDEXING_SLICING),
983986
LintId::of(&inherent_impl::MULTIPLE_INHERENT_IMPL),
984987
LintId::of(&integer_division::INTEGER_DIVISION),
988+
LintId::of(&let_underscore::LET_UNDERSCORE_MUST_USE),
985989
LintId::of(&literal_representation::DECIMAL_LITERAL_REPRESENTATION),
986990
LintId::of(&matches::WILDCARD_ENUM_MATCH_ARM),
987991
LintId::of(&mem_forget::MEM_FORGET),

clippy_lints/src/utils/mod.rs

+69-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use rustc::ty::{
4141
};
4242
use rustc_errors::Applicability;
4343
use smallvec::SmallVec;
44-
use syntax::ast::{self, LitKind};
44+
use syntax::ast::{self, Attribute, LitKind};
4545
use syntax::attr;
4646
use syntax::source_map::{Span, DUMMY_SP};
4747
use syntax::symbol::{kw, Symbol};
@@ -1233,3 +1233,71 @@ pub fn parent_node_is_if_expr<'a, 'b>(expr: &Expr, cx: &LateContext<'a, 'b>) ->
12331233
_ => false,
12341234
}
12351235
}
1236+
1237+
pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
1238+
attrs.iter().find(|attr| {
1239+
attr.ident().map_or(false, |ident| {
1240+
let ident: &str = &ident.as_str();
1241+
"must_use" == ident
1242+
})
1243+
})
1244+
}
1245+
1246+
// Returns whether the type has #[must_use] attribute
1247+
pub fn is_must_use_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
1248+
use ty::TyKind::*;
1249+
match ty.kind {
1250+
Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(),
1251+
Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(),
1252+
Slice(ref ty) | Array(ref ty, _) | RawPtr(ty::TypeAndMut { ref ty, .. }) | Ref(_, ref ty, _) => {
1253+
// for the Array case we don't need to care for the len == 0 case
1254+
// because we don't want to lint functions returning empty arrays
1255+
is_must_use_ty(cx, *ty)
1256+
},
1257+
Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)),
1258+
Opaque(ref def_id, _) => {
1259+
for (predicate, _) in cx.tcx.predicates_of(*def_id).predicates {
1260+
if let ty::Predicate::Trait(ref poly_trait_predicate) = predicate {
1261+
if must_use_attr(&cx.tcx.get_attrs(poly_trait_predicate.skip_binder().trait_ref.def_id)).is_some() {
1262+
return true;
1263+
}
1264+
}
1265+
}
1266+
false
1267+
},
1268+
Dynamic(binder, _) => {
1269+
for predicate in binder.skip_binder().iter() {
1270+
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate {
1271+
if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() {
1272+
return true;
1273+
}
1274+
}
1275+
}
1276+
false
1277+
},
1278+
_ => false,
1279+
}
1280+
}
1281+
1282+
// check if expr is calling method or function with #[must_use] attribyte
1283+
pub fn is_must_use_func_call(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
1284+
let did = match expr.kind {
1285+
ExprKind::Call(ref path, _) => if_chain! {
1286+
if let ExprKind::Path(ref qpath) = path.kind;
1287+
if let def::Res::Def(_, did) = cx.tables.qpath_res(qpath, path.hir_id);
1288+
then {
1289+
Some(did)
1290+
} else {
1291+
None
1292+
}
1293+
},
1294+
ExprKind::MethodCall(_, _, _) => cx.tables.type_dependent_def_id(expr.hir_id),
1295+
_ => None,
1296+
};
1297+
1298+
if let Some(did) = did {
1299+
must_use_attr(&cx.tcx.get_attrs(did)).is_some()
1300+
} else {
1301+
false
1302+
}
1303+
}

src/lintlist/mod.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use lint::Lint;
66
pub use lint::LINT_LEVELS;
77

88
// begin lint list, do not remove this comment, it’s used in `update_lints`
9-
pub const ALL_LINTS: [Lint; 339] = [
9+
pub const ALL_LINTS: [Lint; 340] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -938,6 +938,13 @@ pub const ALL_LINTS: [Lint; 339] = [
938938
deprecation: None,
939939
module: "returns",
940940
},
941+
Lint {
942+
name: "let_underscore_must_use",
943+
group: "restriction",
944+
desc: "non-binding let on a #[must_use] expression",
945+
deprecation: None,
946+
module: "let_underscore",
947+
},
941948
Lint {
942949
name: "let_unit_value",
943950
group: "style",

tests/ui/let_underscore.rs

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#![warn(clippy::let_underscore_must_use)]
2+
3+
#[must_use]
4+
fn f() -> u32 {
5+
0
6+
}
7+
8+
fn g() -> Result<u32, u32> {
9+
Ok(0)
10+
}
11+
12+
#[must_use]
13+
fn l<T>(x: T) -> T {
14+
x
15+
}
16+
17+
fn h() -> u32 {
18+
0
19+
}
20+
21+
struct S {}
22+
23+
impl S {
24+
#[must_use]
25+
pub fn f(&self) -> u32 {
26+
0
27+
}
28+
29+
pub fn g(&self) -> Result<u32, u32> {
30+
Ok(0)
31+
}
32+
33+
fn k(&self) -> u32 {
34+
0
35+
}
36+
37+
#[must_use]
38+
fn h() -> u32 {
39+
0
40+
}
41+
42+
fn p() -> Result<u32, u32> {
43+
Ok(0)
44+
}
45+
}
46+
47+
trait Trait {
48+
#[must_use]
49+
fn a() -> u32;
50+
}
51+
52+
impl Trait for S {
53+
fn a() -> u32 {
54+
0
55+
}
56+
}
57+
58+
fn main() {
59+
let _ = f();
60+
let _ = g();
61+
let _ = h();
62+
let _ = l(0_u32);
63+
64+
let s = S {};
65+
66+
let _ = s.f();
67+
let _ = s.g();
68+
let _ = s.k();
69+
70+
let _ = S::h();
71+
let _ = S::p();
72+
73+
let _ = S::a();
74+
75+
let _ = if true { Ok(()) } else { Err(()) };
76+
77+
let a = Result::<(), ()>::Ok(());
78+
79+
let _ = a.is_ok();
80+
81+
let _ = a.map(|_| ());
82+
83+
let _ = a;
84+
}

0 commit comments

Comments
 (0)