]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/return_self_not_must_use.rs
Auto merge of #8175 - Kage-Yami:feature/document-8145, r=llogiq
[rust.git] / clippy_lints / src / return_self_not_must_use.rs
1 use clippy_utils::ty::is_must_use_ty;
2 use clippy_utils::{diagnostics::span_lint, nth_arg, return_ty};
3 use rustc_hir::def_id::LocalDefId;
4 use rustc_hir::intravisit::FnKind;
5 use rustc_hir::{Body, FnDecl, HirId, TraitItem, TraitItemKind};
6 use rustc_lint::{LateContext, LateLintPass, LintContext};
7 use rustc_middle::lint::in_external_macro;
8 use rustc_session::{declare_lint_pass, declare_tool_lint};
9 use rustc_span::{sym, Span};
10
11 declare_clippy_lint! {
12     /// ### What it does
13     /// This lint warns when a method returning `Self` doesn't have the `#[must_use]` attribute.
14     ///
15     /// ### Why is this bad?
16     /// It prevents to "forget" to use the newly created value.
17     ///
18     /// ### Limitations
19     /// This lint is only applied on methods taking a `self` argument. It would be mostly noise
20     /// if it was added on constructors for example.
21     ///
22     /// ### Example
23     /// ```rust
24     /// pub struct Bar;
25     ///
26     /// impl Bar {
27     ///     // Bad
28     ///     pub fn bar(&self) -> Self {
29     ///         Self
30     ///     }
31     ///
32     ///     // Good
33     ///     #[must_use]
34     ///     pub fn foo(&self) -> Self {
35     ///         Self
36     ///     }
37     /// }
38     /// ```
39     #[clippy::version = "1.59.0"]
40     pub RETURN_SELF_NOT_MUST_USE,
41     suspicious,
42     "missing `#[must_use]` annotation on a method returning `Self`"
43 }
44
45 declare_lint_pass!(ReturnSelfNotMustUse => [RETURN_SELF_NOT_MUST_USE]);
46
47 fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalDefId, span: Span, hir_id: HirId) {
48     if_chain! {
49         // If it comes from an external macro, better ignore it.
50         if !in_external_macro(cx.sess(), span);
51         if decl.implicit_self.has_implicit_self();
52         // We only show this warning for public exported methods.
53         if cx.access_levels.is_exported(fn_def);
54         // We don't want to emit this lint if the `#[must_use]` attribute is already there.
55         if !cx.tcx.hir().attrs(hir_id).iter().any(|attr| attr.has_name(sym::must_use));
56         if cx.tcx.visibility(fn_def.to_def_id()).is_public();
57         let ret_ty = return_ty(cx, hir_id);
58         let self_arg = nth_arg(cx, hir_id, 0);
59         // If `Self` has the same type as the returned type, then we want to warn.
60         //
61         // For this check, we don't want to remove the reference on the returned type because if
62         // there is one, we shouldn't emit a warning!
63         if self_arg.peel_refs() == ret_ty;
64         // If `Self` is already marked as `#[must_use]`, no need for the attribute here.
65         if !is_must_use_ty(cx, ret_ty);
66
67         then {
68             span_lint(
69                 cx,
70                 RETURN_SELF_NOT_MUST_USE,
71                 span,
72                 "missing `#[must_use]` attribute on a method returning `Self`",
73             );
74         }
75     }
76 }
77
78 impl<'tcx> LateLintPass<'tcx> for ReturnSelfNotMustUse {
79     fn check_fn(
80         &mut self,
81         cx: &LateContext<'tcx>,
82         kind: FnKind<'tcx>,
83         decl: &'tcx FnDecl<'tcx>,
84         _: &'tcx Body<'tcx>,
85         span: Span,
86         hir_id: HirId,
87     ) {
88         if_chain! {
89             // We are only interested in methods, not in functions or associated functions.
90             if matches!(kind, FnKind::Method(_, _, _));
91             if let Some(fn_def) = cx.tcx.hir().opt_local_def_id(hir_id);
92             if let Some(impl_def) = cx.tcx.impl_of_method(fn_def.to_def_id());
93             // We don't want this method to be te implementation of a trait because the
94             // `#[must_use]` should be put on the trait definition directly.
95             if cx.tcx.trait_id_of_impl(impl_def).is_none();
96
97             then {
98                 check_method(cx, decl, fn_def, span, hir_id);
99             }
100         }
101     }
102
103     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) {
104         if let TraitItemKind::Fn(ref sig, _) = item.kind {
105             check_method(cx, sig.decl, item.def_id, item.span, item.hir_id());
106         }
107     }
108 }