]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/iter_not_returning_iterator.rs
Shrink `hir::def::Res`.
[rust.git] / clippy_lints / src / iter_not_returning_iterator.rs
1 use clippy_utils::{diagnostics::span_lint, get_parent_node, ty::implements_trait};
2 use rustc_hir::{def_id::LocalDefId, FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind};
3 use rustc_lint::{LateContext, LateLintPass};
4 use rustc_session::{declare_lint_pass, declare_tool_lint};
5 use rustc_span::symbol::sym;
6
7 declare_clippy_lint! {
8     /// ### What it does
9     /// Detects methods named `iter` or `iter_mut` that do not have a return type that implements `Iterator`.
10     ///
11     /// ### Why is this bad?
12     /// Methods named `iter` or `iter_mut` conventionally return an `Iterator`.
13     ///
14     /// ### Example
15     /// ```rust
16     /// // `String` does not implement `Iterator`
17     /// struct Data {}
18     /// impl Data {
19     ///     fn iter(&self) -> String {
20     ///         todo!()
21     ///     }
22     /// }
23     /// ```
24     /// Use instead:
25     /// ```rust
26     /// use std::str::Chars;
27     /// struct Data {}
28     /// impl Data {
29     ///    fn iter(&self) -> Chars<'static> {
30     ///        todo!()
31     ///    }
32     /// }
33     /// ```
34     #[clippy::version = "1.57.0"]
35     pub ITER_NOT_RETURNING_ITERATOR,
36     pedantic,
37     "methods named `iter` or `iter_mut` that do not return an `Iterator`"
38 }
39
40 declare_lint_pass!(IterNotReturningIterator => [ITER_NOT_RETURNING_ITERATOR]);
41
42 impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator {
43     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
44         let name = item.ident.name.as_str();
45         if matches!(name, "iter" | "iter_mut") {
46             if let TraitItemKind::Fn(fn_sig, _) = &item.kind {
47                 check_sig(cx, name, fn_sig, item.def_id.def_id);
48             }
49         }
50     }
51
52     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) {
53         let name = item.ident.name.as_str();
54         if matches!(name, "iter" | "iter_mut")
55             && !matches!(
56                 get_parent_node(cx.tcx, item.hir_id()),
57                 Some(Node::Item(Item { kind: ItemKind::Impl(i), .. })) if i.of_trait.is_some()
58             )
59         {
60             if let ImplItemKind::Fn(fn_sig, _) = &item.kind {
61                 check_sig(cx, name, fn_sig, item.def_id.def_id);
62             }
63         }
64     }
65 }
66
67 fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefId) {
68     if sig.decl.implicit_self.has_implicit_self() {
69         let ret_ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(fn_id).output());
70         let ret_ty = cx
71             .tcx
72             .try_normalize_erasing_regions(cx.param_env, ret_ty)
73             .unwrap_or(ret_ty);
74         if cx
75             .tcx
76             .get_diagnostic_item(sym::Iterator)
77             .map_or(false, |iter_id| !implements_trait(cx, ret_ty, iter_id, &[]))
78         {
79             span_lint(
80                 cx,
81                 ITER_NOT_RETURNING_ITERATOR,
82                 sig.span,
83                 &format!(
84                     "this method is named `{}` but its return type does not implement `Iterator`",
85                     name
86                 ),
87             );
88         }
89     }
90 }