]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs
New upstream version 1.72.1+dfsg1
[rustc.git] / src / tools / clippy / 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.owner_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.owner_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
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 }