Skip to content

Commit c379165

Browse files
committed
Move lints related to must_use to their own module
1 parent 9782fc4 commit c379165

File tree

2 files changed

+283
-248
lines changed

2 files changed

+283
-248
lines changed

clippy_lints/src/functions/mod.rs

+11-248
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
1+
mod must_use;
12
mod not_unsafe_ptr_arg_deref;
23
mod too_many_arguments;
34
mod too_many_lines;
45

5-
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
6-
use clippy_utils::source::snippet_opt;
7-
use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item};
8-
use clippy_utils::{attr_by_name, attrs::is_proc_macro, match_def_path, must_use_attr, return_ty, trait_ref_of_method};
6+
use clippy_utils::diagnostics::span_lint_and_help;
7+
use clippy_utils::trait_ref_of_method;
8+
use clippy_utils::ty::is_type_diagnostic_item;
99
use if_chain::if_chain;
10-
use rustc_ast::ast::Attribute;
11-
use rustc_data_structures::fx::FxHashSet;
12-
use rustc_errors::Applicability;
1310
use rustc_hir as hir;
1411
use rustc_hir::intravisit;
15-
use rustc_hir::{def::Res, def_id::DefId, QPath};
1612
use rustc_lint::{LateContext, LateLintPass, LintContext};
17-
use rustc_middle::hir::map::Map;
1813
use rustc_middle::lint::in_external_macro;
19-
use rustc_middle::ty::{self, Ty};
14+
use rustc_middle::ty;
2015
use rustc_session::{declare_tool_lint, impl_lint_pass};
2116
use rustc_span::{sym, Span};
2217
use rustc_typeck::hir_ty_to_ty;
@@ -260,88 +255,38 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
260255
}
261256

262257
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
263-
let attrs = cx.tcx.hir().attrs(item.hir_id());
264-
let attr = must_use_attr(attrs);
265-
if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind {
258+
must_use::check_item(cx, item);
259+
if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind {
266260
let is_public = cx.access_levels.is_exported(item.hir_id());
267261
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
268262
if is_public {
269263
check_result_unit_err(cx, &sig.decl, item.span, fn_header_span);
270264
}
271-
if let Some(attr) = attr {
272-
check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr);
273-
return;
274-
}
275-
if is_public && !is_proc_macro(cx.sess(), attrs) && attr_by_name(attrs, "no_mangle").is_none() {
276-
check_must_use_candidate(
277-
cx,
278-
&sig.decl,
279-
cx.tcx.hir().body(*body_id),
280-
item.span,
281-
item.hir_id(),
282-
item.span.with_hi(sig.decl.output.span().hi()),
283-
"this function could have a `#[must_use]` attribute",
284-
);
285-
}
286265
}
287266
}
288267

289268
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
290-
if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind {
269+
must_use::check_impl_item(cx, item);
270+
if let hir::ImplItemKind::Fn(ref sig, _) = item.kind {
291271
let is_public = cx.access_levels.is_exported(item.hir_id());
292272
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
293273
if is_public && trait_ref_of_method(cx, item.hir_id()).is_none() {
294274
check_result_unit_err(cx, &sig.decl, item.span, fn_header_span);
295275
}
296-
let attrs = cx.tcx.hir().attrs(item.hir_id());
297-
let attr = must_use_attr(attrs);
298-
if let Some(attr) = attr {
299-
check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr);
300-
} else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.hir_id()).is_none()
301-
{
302-
check_must_use_candidate(
303-
cx,
304-
&sig.decl,
305-
cx.tcx.hir().body(*body_id),
306-
item.span,
307-
item.hir_id(),
308-
item.span.with_hi(sig.decl.output.span().hi()),
309-
"this method could have a `#[must_use]` attribute",
310-
);
311-
}
312276
}
313277
}
314278

315279
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
316280
too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold);
317281
not_unsafe_ptr_arg_deref::check_trait_item(cx, item);
282+
must_use::check_trait_item(cx, item);
318283

319-
if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind {
284+
if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
320285
let is_public = cx.access_levels.is_exported(item.hir_id());
321286
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
322287
if is_public {
323288
check_result_unit_err(cx, &sig.decl, item.span, fn_header_span);
324289
}
325-
326-
let attrs = cx.tcx.hir().attrs(item.hir_id());
327-
let attr = must_use_attr(attrs);
328-
if let Some(attr) = attr {
329-
check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr);
330-
}
331-
if let hir::TraitFn::Provided(eid) = *eid {
332-
let body = cx.tcx.hir().body(eid);
333-
if attr.is_none() && is_public && !is_proc_macro(cx.sess(), attrs) {
334-
check_must_use_candidate(
335-
cx,
336-
&sig.decl,
337-
body,
338-
item.span,
339-
item.hir_id(),
340-
item.span.with_hi(sig.decl.output.span().hi()),
341-
"this method could have a `#[must_use]` attribute",
342-
);
343-
}
344-
}
345290
}
346291
}
347292
}
@@ -367,185 +312,3 @@ fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span
367312
}
368313
}
369314
}
370-
371-
fn check_needless_must_use(
372-
cx: &LateContext<'_>,
373-
decl: &hir::FnDecl<'_>,
374-
item_id: hir::HirId,
375-
item_span: Span,
376-
fn_header_span: Span,
377-
attr: &Attribute,
378-
) {
379-
if in_external_macro(cx.sess(), item_span) {
380-
return;
381-
}
382-
if returns_unit(decl) {
383-
span_lint_and_then(
384-
cx,
385-
MUST_USE_UNIT,
386-
fn_header_span,
387-
"this unit-returning function has a `#[must_use]` attribute",
388-
|diag| {
389-
diag.span_suggestion(
390-
attr.span,
391-
"remove the attribute",
392-
"".into(),
393-
Applicability::MachineApplicable,
394-
);
395-
},
396-
);
397-
} else if !attr.is_value_str() && is_must_use_ty(cx, return_ty(cx, item_id)) {
398-
span_lint_and_help(
399-
cx,
400-
DOUBLE_MUST_USE,
401-
fn_header_span,
402-
"this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`",
403-
None,
404-
"either add some descriptive text or remove the attribute",
405-
);
406-
}
407-
}
408-
409-
fn check_must_use_candidate<'tcx>(
410-
cx: &LateContext<'tcx>,
411-
decl: &'tcx hir::FnDecl<'_>,
412-
body: &'tcx hir::Body<'_>,
413-
item_span: Span,
414-
item_id: hir::HirId,
415-
fn_span: Span,
416-
msg: &str,
417-
) {
418-
if has_mutable_arg(cx, body)
419-
|| mutates_static(cx, body)
420-
|| in_external_macro(cx.sess(), item_span)
421-
|| returns_unit(decl)
422-
|| !cx.access_levels.is_exported(item_id)
423-
|| is_must_use_ty(cx, return_ty(cx, item_id))
424-
{
425-
return;
426-
}
427-
span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| {
428-
if let Some(snippet) = snippet_opt(cx, fn_span) {
429-
diag.span_suggestion(
430-
fn_span,
431-
"add the attribute",
432-
format!("#[must_use] {}", snippet),
433-
Applicability::MachineApplicable,
434-
);
435-
}
436-
});
437-
}
438-
439-
fn returns_unit(decl: &hir::FnDecl<'_>) -> bool {
440-
match decl.output {
441-
hir::FnRetTy::DefaultReturn(_) => true,
442-
hir::FnRetTy::Return(ref ty) => match ty.kind {
443-
hir::TyKind::Tup(ref tys) => tys.is_empty(),
444-
hir::TyKind::Never => true,
445-
_ => false,
446-
},
447-
}
448-
}
449-
450-
fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool {
451-
let mut tys = FxHashSet::default();
452-
body.params.iter().any(|param| is_mutable_pat(cx, &param.pat, &mut tys))
453-
}
454-
455-
fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet<DefId>) -> bool {
456-
if let hir::PatKind::Wild = pat.kind {
457-
return false; // ignore `_` patterns
458-
}
459-
if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) {
460-
is_mutable_ty(cx, &cx.tcx.typeck(pat.hir_id.owner).pat_ty(pat), pat.span, tys)
461-
} else {
462-
false
463-
}
464-
}
465-
466-
static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]];
467-
468-
fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut FxHashSet<DefId>) -> bool {
469-
match *ty.kind() {
470-
// primitive types are never mutable
471-
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
472-
ty::Adt(ref adt, ref substs) => {
473-
tys.insert(adt.did) && !ty.is_freeze(cx.tcx.at(span), cx.param_env)
474-
|| KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did, path))
475-
&& substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys))
476-
},
477-
ty::Tuple(ref substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)),
478-
ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys),
479-
ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => {
480-
mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys)
481-
},
482-
// calling something constitutes a side effect, so return true on all callables
483-
// also never calls need not be used, so return true for them, too
484-
_ => true,
485-
}
486-
}
487-
488-
struct StaticMutVisitor<'a, 'tcx> {
489-
cx: &'a LateContext<'tcx>,
490-
mutates_static: bool,
491-
}
492-
493-
impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
494-
type Map = Map<'tcx>;
495-
496-
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
497-
use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
498-
499-
if self.mutates_static {
500-
return;
501-
}
502-
match expr.kind {
503-
Call(_, args) | MethodCall(_, _, args, _) => {
504-
let mut tys = FxHashSet::default();
505-
for arg in args {
506-
if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
507-
&& is_mutable_ty(
508-
self.cx,
509-
self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg),
510-
arg.span,
511-
&mut tys,
512-
)
513-
&& is_mutated_static(arg)
514-
{
515-
self.mutates_static = true;
516-
return;
517-
}
518-
tys.clear();
519-
}
520-
},
521-
Assign(ref target, ..) | AssignOp(_, ref target, _) | AddrOf(_, hir::Mutability::Mut, ref target) => {
522-
self.mutates_static |= is_mutated_static(target)
523-
},
524-
_ => {},
525-
}
526-
}
527-
528-
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
529-
intravisit::NestedVisitorMap::None
530-
}
531-
}
532-
533-
fn is_mutated_static(e: &hir::Expr<'_>) -> bool {
534-
use hir::ExprKind::{Field, Index, Path};
535-
536-
match e.kind {
537-
Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)),
538-
Path(_) => true,
539-
Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(inner),
540-
_ => false,
541-
}
542-
}
543-
544-
fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool {
545-
let mut v = StaticMutVisitor {
546-
cx,
547-
mutates_static: false,
548-
};
549-
intravisit::walk_expr(&mut v, &body.value);
550-
v.mutates_static
551-
}

0 commit comments

Comments
 (0)