• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>)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.owner_id.def_id);
48             }
49         }
50     }
51 
check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>)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.owner_id.def_id);
62             }
63         }
64     }
65 }
66 
check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefId)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
70             .tcx
71             .erase_late_bound_regions(cx.tcx.fn_sig(fn_id).subst_identity().output());
72         let ret_ty = cx
73             .tcx
74             .try_normalize_erasing_regions(cx.param_env, ret_ty)
75             .unwrap_or(ret_ty);
76         if cx
77             .tcx
78             .get_diagnostic_item(sym::Iterator)
79             .map_or(false, |iter_id| !implements_trait(cx, ret_ty, iter_id, &[]))
80         {
81             span_lint(
82                 cx,
83                 ITER_NOT_RETURNING_ITERATOR,
84                 sig.span,
85                 &format!("this method is named `{name}` but its return type does not implement `Iterator`"),
86             );
87         }
88     }
89 }
90