]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/functions/must_use.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / functions / must_use.rs
CommitLineData
cdc7bbd5
XL
1use rustc_ast::ast::Attribute;
2use rustc_errors::Applicability;
94222f64 3use rustc_hir::def_id::{DefIdSet, LocalDefId};
2b03887a 4use rustc_hir::{self as hir, def::Res, QPath};
cdc7bbd5
XL
5use rustc_lint::{LateContext, LintContext};
6use rustc_middle::{
cdc7bbd5
XL
7 lint::in_external_macro,
8 ty::{self, Ty},
9};
2b03887a 10use rustc_span::{sym, Span, Symbol};
cdc7bbd5
XL
11
12use clippy_utils::attrs::is_proc_macro;
13use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
14use clippy_utils::source::snippet_opt;
15use clippy_utils::ty::is_must_use_ty;
2b03887a
FG
16use clippy_utils::visitors::for_each_expr;
17use clippy_utils::{return_ty, trait_ref_of_method};
18
19use core::ops::ControlFlow;
cdc7bbd5
XL
20
21use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT};
22
5099ac24 23pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
cdc7bbd5 24 let attrs = cx.tcx.hir().attrs(item.hir_id());
2b03887a 25 let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use);
04454e1e 26 if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind {
2b03887a 27 let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
cdc7bbd5
XL
28 let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
29 if let Some(attr) = attr {
30 check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
cdc7bbd5
XL
31 } else if is_public && !is_proc_macro(cx.sess(), attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) {
32 check_must_use_candidate(
33 cx,
34 sig.decl,
35 cx.tcx.hir().body(*body_id),
36 item.span,
2b03887a 37 item.owner_id.def_id,
cdc7bbd5
XL
38 item.span.with_hi(sig.decl.output.span().hi()),
39 "this function could have a `#[must_use]` attribute",
40 );
41 }
42 }
43}
44
5099ac24 45pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
cdc7bbd5 46 if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind {
2b03887a 47 let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
cdc7bbd5
XL
48 let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
49 let attrs = cx.tcx.hir().attrs(item.hir_id());
2b03887a 50 let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use);
cdc7bbd5
XL
51 if let Some(attr) = attr {
52 check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
6522a427
EL
53 } else if is_public
54 && !is_proc_macro(cx.sess(), attrs)
55 && trait_ref_of_method(cx, item.owner_id.def_id).is_none()
2b03887a 56 {
cdc7bbd5
XL
57 check_must_use_candidate(
58 cx,
59 sig.decl,
60 cx.tcx.hir().body(*body_id),
61 item.span,
2b03887a 62 item.owner_id.def_id,
cdc7bbd5
XL
63 item.span.with_hi(sig.decl.output.span().hi()),
64 "this method could have a `#[must_use]` attribute",
65 );
66 }
67 }
68}
69
5099ac24 70pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
cdc7bbd5 71 if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind {
2b03887a 72 let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
cdc7bbd5
XL
73 let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
74
75 let attrs = cx.tcx.hir().attrs(item.hir_id());
2b03887a 76 let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use);
cdc7bbd5
XL
77 if let Some(attr) = attr {
78 check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
79 } else if let hir::TraitFn::Provided(eid) = *eid {
80 let body = cx.tcx.hir().body(eid);
81 if attr.is_none() && is_public && !is_proc_macro(cx.sess(), attrs) {
82 check_must_use_candidate(
83 cx,
84 sig.decl,
85 body,
86 item.span,
2b03887a 87 item.owner_id.def_id,
cdc7bbd5
XL
88 item.span.with_hi(sig.decl.output.span().hi()),
89 "this method could have a `#[must_use]` attribute",
90 );
91 }
92 }
93 }
94}
95
96fn check_needless_must_use(
97 cx: &LateContext<'_>,
98 decl: &hir::FnDecl<'_>,
99 item_id: hir::HirId,
100 item_span: Span,
101 fn_header_span: Span,
102 attr: &Attribute,
103) {
104 if in_external_macro(cx.sess(), item_span) {
105 return;
106 }
107 if returns_unit(decl) {
108 span_lint_and_then(
109 cx,
110 MUST_USE_UNIT,
111 fn_header_span,
112 "this unit-returning function has a `#[must_use]` attribute",
113 |diag| {
04454e1e 114 diag.span_suggestion(attr.span, "remove the attribute", "", Applicability::MachineApplicable);
cdc7bbd5
XL
115 },
116 );
117 } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {
118 span_lint_and_help(
119 cx,
120 DOUBLE_MUST_USE,
121 fn_header_span,
122 "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`",
123 None,
124 "either add some descriptive text or remove the attribute",
125 );
126 }
127}
128
129fn check_must_use_candidate<'tcx>(
130 cx: &LateContext<'tcx>,
131 decl: &'tcx hir::FnDecl<'_>,
132 body: &'tcx hir::Body<'_>,
133 item_span: Span,
94222f64 134 item_id: LocalDefId,
cdc7bbd5
XL
135 fn_span: Span,
136 msg: &str,
137) {
138 if has_mutable_arg(cx, body)
139 || mutates_static(cx, body)
140 || in_external_macro(cx.sess(), item_span)
141 || returns_unit(decl)
2b03887a 142 || !cx.effective_visibilities.is_exported(item_id)
94222f64 143 || is_must_use_ty(cx, return_ty(cx, cx.tcx.hir().local_def_id_to_hir_id(item_id)))
cdc7bbd5
XL
144 {
145 return;
146 }
147 span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| {
148 if let Some(snippet) = snippet_opt(cx, fn_span) {
149 diag.span_suggestion(
150 fn_span,
151 "add the attribute",
2b03887a 152 format!("#[must_use] {snippet}"),
cdc7bbd5
XL
153 Applicability::MachineApplicable,
154 );
155 }
156 });
157}
158
159fn returns_unit(decl: &hir::FnDecl<'_>) -> bool {
160 match decl.output {
161 hir::FnRetTy::DefaultReturn(_) => true,
162 hir::FnRetTy::Return(ty) => match ty.kind {
163 hir::TyKind::Tup(tys) => tys.is_empty(),
164 hir::TyKind::Never => true,
165 _ => false,
166 },
167 }
168}
169
170fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool {
171 let mut tys = DefIdSet::default();
172 body.params.iter().any(|param| is_mutable_pat(cx, param.pat, &mut tys))
173}
174
175fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) -> bool {
176 if let hir::PatKind::Wild = pat.kind {
177 return false; // ignore `_` patterns
178 }
179 if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) {
6522a427 180 is_mutable_ty(cx, cx.tcx.typeck(pat.hir_id.owner.def_id).pat_ty(pat), tys)
cdc7bbd5
XL
181 } else {
182 false
183 }
184}
185
2b03887a 186static KNOWN_WRAPPER_TYS: &[Symbol] = &[sym::Rc, sym::Arc];
cdc7bbd5 187
6522a427 188fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, tys: &mut DefIdSet) -> bool {
cdc7bbd5
XL
189 match *ty.kind() {
190 // primitive types are never mutable
191 ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
192 ty::Adt(adt, substs) => {
2b03887a
FG
193 tys.insert(adt.did()) && !ty.is_freeze(cx.tcx, cx.param_env)
194 || KNOWN_WRAPPER_TYS
195 .iter()
196 .any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did()))
6522a427 197 && substs.types().any(|ty| is_mutable_ty(cx, ty, tys))
cdc7bbd5 198 },
6522a427
EL
199 ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, tys)),
200 ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, tys),
cdc7bbd5 201 ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => {
6522a427 202 mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, tys)
cdc7bbd5
XL
203 },
204 // calling something constitutes a side effect, so return true on all callables
205 // also never calls need not be used, so return true for them, too
206 _ => true,
207 }
208}
209
2b03887a
FG
210fn is_mutated_static(e: &hir::Expr<'_>) -> bool {
211 use hir::ExprKind::{Field, Index, Path};
212
213 match e.kind {
214 Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)),
215 Path(_) => true,
216 Field(inner, _) | Index(inner, _) => is_mutated_static(inner),
217 _ => false,
218 }
cdc7bbd5
XL
219}
220
2b03887a
FG
221fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool {
222 for_each_expr(body.value, |e| {
cdc7bbd5
XL
223 use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
224
2b03887a 225 match e.kind {
f2b60f7d 226 Call(_, args) => {
cdc7bbd5
XL
227 let mut tys = DefIdSet::default();
228 for arg in args {
2b03887a 229 if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
6522a427 230 && is_mutable_ty(cx, cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), &mut tys)
cdc7bbd5
XL
231 && is_mutated_static(arg)
232 {
2b03887a 233 return ControlFlow::Break(());
cdc7bbd5
XL
234 }
235 tys.clear();
236 }
2b03887a 237 ControlFlow::Continue(())
cdc7bbd5 238 },
f2b60f7d
FG
239 MethodCall(_, receiver, args, _) => {
240 let mut tys = DefIdSet::default();
241 for arg in std::iter::once(receiver).chain(args.iter()) {
2b03887a 242 if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
6522a427 243 && is_mutable_ty(cx, cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), &mut tys)
f2b60f7d
FG
244 && is_mutated_static(arg)
245 {
2b03887a 246 return ControlFlow::Break(());
f2b60f7d
FG
247 }
248 tys.clear();
249 }
2b03887a 250 ControlFlow::Continue(())
f2b60f7d 251 },
2b03887a
FG
252 Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target)
253 if is_mutated_static(target) =>
254 {
255 ControlFlow::Break(())
cdc7bbd5 256 },
2b03887a 257 _ => ControlFlow::Continue(()),
cdc7bbd5 258 }
2b03887a
FG
259 })
260 .is_some()
cdc7bbd5 261}