1 use crate::lints
::MappingToUnit
;
2 use crate::{LateContext, LateLintPass, LintContext}
;
4 use rustc_hir
::{Expr, ExprKind, HirId, Stmt, StmtKind}
;
11 /// The `map_unit_fn` lint checks for `Iterator::map` receive
12 /// a callable that returns `()`.
17 /// fn foo(items: &mut Vec<u8>) {
22 /// let mut x: Vec<Vec<u8>> = vec![
26 /// x.iter_mut().map(foo);
34 /// Mapping to `()` is almost always a mistake.
37 "`Iterator::map` call that discard the iterator's values"
40 declare_lint_pass
!(MapUnitFn
=> [MAP_UNIT_FN
]);
42 impl<'tcx
> LateLintPass
<'tcx
> for MapUnitFn
{
43 fn check_stmt(&mut self, cx
: &LateContext
<'tcx
>, stmt
: &Stmt
<'_
>) {
44 if stmt
.span
.from_expansion() {
48 if let StmtKind
::Semi(expr
) = stmt
.kind
{
49 if let ExprKind
::MethodCall(path
, receiver
, args
, span
) = expr
.kind
{
50 if path
.ident
.name
.as_str() == "map" {
51 if receiver
.span
.from_expansion()
52 || args
.iter().any(|e
| e
.span
.from_expansion())
53 || !is_impl_slice(cx
, receiver
)
54 || !is_diagnostic_name(cx
, expr
.hir_id
, "IteratorMap")
58 let arg_ty
= cx
.typeck_results().expr_ty(&args
[0]);
59 let default_span
= args
[0].span
;
60 if let ty
::FnDef(id
, _
) = arg_ty
.kind() {
61 let fn_ty
= cx
.tcx
.fn_sig(id
).skip_binder();
62 let ret_ty
= fn_ty
.output().skip_binder();
63 if is_unit_type(ret_ty
) {
71 .unwrap_or(default_span
),
72 argument_label
: args
[0].span
,
73 map_label
: arg_ty
.default_span(cx
.tcx
),
74 suggestion
: path
.ident
.span
,
75 replace
: "for_each".to_string(),
79 } else if let ty
::Closure(id
, subs
) = arg_ty
.kind() {
80 let cl_ty
= subs
.as_closure().sig();
81 let ret_ty
= cl_ty
.output().skip_binder();
82 if is_unit_type(ret_ty
) {
90 .unwrap_or(default_span
),
91 argument_label
: args
[0].span
,
92 map_label
: arg_ty
.default_span(cx
.tcx
),
93 suggestion
: path
.ident
.span
,
94 replace
: "for_each".to_string(),
105 fn is_impl_slice(cx
: &LateContext
<'_
>, expr
: &Expr
<'_
>) -> bool
{
106 if let Some(method_id
) = cx
.typeck_results().type_dependent_def_id(expr
.hir_id
) {
107 if let Some(impl_id
) = cx
.tcx
.impl_of_method(method_id
) {
108 return cx
.tcx
.type_of(impl_id
).skip_binder().is_slice();
114 fn is_unit_type(ty
: Ty
<'_
>) -> bool
{
115 ty
.is_unit() || ty
.is_never()
118 fn is_diagnostic_name(cx
: &LateContext
<'_
>, id
: HirId
, name
: &str) -> bool
{
119 if let Some(def_id
) = cx
.typeck_results().type_dependent_def_id(id
) {
120 if let Some(item
) = cx
.tcx
.get_diagnostic_name(def_id
) {
121 if item
.as_str() == name
{