1 use clippy_utils::{diagnostics::span_lint, must_use_attr, nth_arg, return_ty};
2 use rustc_hir::def_id::LocalDefId;
3 use rustc_hir::intravisit::FnKind;
4 use rustc_hir::{Body, FnDecl, HirId, TraitItem, TraitItemKind};
5 use rustc_lint::{LateContext, LateLintPass, LintContext};
6 use rustc_middle::lint::in_external_macro;
7 use rustc_session::{declare_lint_pass, declare_tool_lint};
8 use rustc_span::{sym, Span};
10 declare_clippy_lint! {
12 /// This lint warns when a method returning `Self` doesn't have the `#[must_use]` attribute.
14 /// ### Why is this bad?
15 /// It prevents to "forget" to use the newly created value.
18 /// This lint is only applied on methods taking a `self` argument. It would be mostly noise
19 /// if it was added on constructors for example.
27 /// pub fn bar(&self) -> Self {
33 /// pub fn foo(&self) -> Self {
38 #[clippy::version = "1.59.0"]
39 pub RETURN_SELF_NOT_MUST_USE,
41 "missing `#[must_use]` annotation on a method returning `Self`"
44 declare_lint_pass!(ReturnSelfNotMustUse => [RETURN_SELF_NOT_MUST_USE]);
46 fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalDefId, span: Span, hir_id: HirId) {
48 // If it comes from an external macro, better ignore it.
49 if !in_external_macro(cx.sess(), span);
50 if decl.implicit_self.has_implicit_self();
51 // We only show this warning for public exported methods.
52 if cx.access_levels.is_exported(fn_def);
53 if cx.tcx.visibility(fn_def.to_def_id()).is_public();
54 // No need to warn if the attribute is already present.
55 if must_use_attr(cx.tcx.hir().attrs(hir_id)).is_none();
56 let ret_ty = return_ty(cx, hir_id);
57 let self_arg = nth_arg(cx, hir_id, 0);
58 // If `Self` has the same type as the returned type, then we want to warn.
60 // For this check, we don't want to remove the reference on the returned type because if
61 // there is one, we shouldn't emit a warning!
62 if self_arg.peel_refs() == ret_ty;
67 RETURN_SELF_NOT_MUST_USE,
69 "missing `#[must_use]` attribute on a method returning `Self`",
75 impl<'tcx> LateLintPass<'tcx> for ReturnSelfNotMustUse {
78 cx: &LateContext<'tcx>,
80 decl: &'tcx FnDecl<'tcx>,
86 // We are only interested in methods, not in functions or associated functions.
87 if matches!(kind, FnKind::Method(_, _, _));
88 if let Some(fn_def) = cx.tcx.hir().opt_local_def_id(hir_id);
89 // We don't want to emit this lint if the `#[must_use]` attribute is already there.
90 if !cx.tcx.hir().attrs(hir_id).iter().any(|attr| attr.has_name(sym::must_use));
91 if let Some(impl_def) = cx.tcx.impl_of_method(fn_def.to_def_id());
92 // We don't want this method to be te implementation of a trait because the
93 // `#[must_use]` should be put on the trait definition directly.
94 if cx.tcx.trait_id_of_impl(impl_def).is_none();
97 check_method(cx, decl, fn_def, span, hir_id);
102 fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) {
103 if let TraitItemKind::Fn(ref sig, _) = item.kind {
104 check_method(cx, sig.decl, item.def_id, item.span, item.hir_id());