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