From febf34e2b4ed9b66fc3095d55ae13cef9b6b154b Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 24 Mar 2021 23:12:08 +0900 Subject: [PATCH 1/6] Move too_many_arguments to its own module --- .../src/{functions.rs => functions/mod.rs} | 67 ++++++----------- .../src/functions/too_many_arguments.rs | 73 +++++++++++++++++++ clippy_lints/src/lib.rs | 6 +- 3 files changed, 97 insertions(+), 49 deletions(-) rename clippy_lints/src/{functions.rs => functions/mod.rs} (92%) create mode 100644 clippy_lints/src/functions/too_many_arguments.rs diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions/mod.rs similarity index 92% rename from clippy_lints/src/functions.rs rename to clippy_lints/src/functions/mod.rs index 730492fc7e3ef..68badb87b22d7 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions/mod.rs @@ -1,9 +1,11 @@ +mod too_many_arguments; + use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, type_is_unsafe_function}; use clippy_utils::{ - attr_by_name, attrs::is_proc_macro, is_trait_impl_item, iter_input_pats, match_def_path, must_use_attr, - path_to_local, return_ty, trait_ref_of_method, + attr_by_name, attrs::is_proc_macro, iter_input_pats, match_def_path, must_use_attr, path_to_local, return_ty, + trait_ref_of_method, }; use if_chain::if_chain; use rustc_ast::ast::Attribute; @@ -17,9 +19,7 @@ use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::Span; -use rustc_span::sym; -use rustc_target::spec::abi::Abi; +use rustc_span::{sym, Span}; use rustc_typeck::hir_ty_to_ty; declare_clippy_lint! { @@ -222,13 +222,16 @@ declare_clippy_lint! { #[derive(Copy, Clone)] pub struct Functions { - threshold: u64, - max_lines: u64, + too_many_arguments_threshold: u64, + too_many_lines_threshold: u64, } impl Functions { - pub fn new(threshold: u64, max_lines: u64) -> Self { - Self { threshold, max_lines } + pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64) -> Self { + Self { + too_many_arguments_threshold, + too_many_lines_threshold, + } } } @@ -252,31 +255,14 @@ impl<'tcx> LateLintPass<'tcx> for Functions { span: Span, hir_id: hir::HirId, ) { + too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); + let unsafety = match kind { intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _) => unsafety, intravisit::FnKind::Method(_, sig, _) => sig.header.unsafety, intravisit::FnKind::Closure => return, }; - // don't warn for implementations, it's not their fault - if !is_trait_impl_item(cx, hir_id) { - // don't lint extern functions decls, it's not their fault either - match kind { - intravisit::FnKind::Method( - _, - &hir::FnSig { - header: hir::FnHeader { abi: Abi::Rust, .. }, - .. - }, - _, - ) - | intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }, _) => { - self.check_arg_number(cx, decl, span.with_hi(decl.output.span().hi())) - }, - _ => {}, - } - } - Self::check_raw_ptr(cx, unsafety, decl, body, hir_id); self.check_line_number(cx, span, body); } @@ -335,11 +321,9 @@ impl<'tcx> LateLintPass<'tcx> for Functions { } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { + too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold); + if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind { - // don't lint extern functions decls, it's not their fault - if sig.header.abi == Abi::Rust { - self.check_arg_number(cx, &sig.decl, item.span.with_hi(sig.decl.output.span().hi())); - } let is_public = cx.access_levels.is_exported(item.hir_id()); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if is_public { @@ -372,18 +356,6 @@ impl<'tcx> LateLintPass<'tcx> for Functions { } impl<'tcx> Functions { - fn check_arg_number(self, cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, fn_span: Span) { - let args = decl.inputs.len() as u64; - if args > self.threshold { - span_lint( - cx, - TOO_MANY_ARGUMENTS, - fn_span, - &format!("this function has too many arguments ({}/{})", args, self.threshold), - ); - } - } - fn check_line_number(self, cx: &LateContext<'_>, span: Span, body: &'tcx hir::Body<'_>) { if in_external_macro(cx.sess(), span) { return; @@ -430,12 +402,15 @@ impl<'tcx> Functions { } } - if line_count > self.max_lines { + if line_count > self.too_many_lines_threshold { span_lint( cx, TOO_MANY_LINES, span, - &format!("this function has too many lines ({}/{})", line_count, self.max_lines), + &format!( + "this function has too many lines ({}/{})", + line_count, self.too_many_lines_threshold + ), ) } } diff --git a/clippy_lints/src/functions/too_many_arguments.rs b/clippy_lints/src/functions/too_many_arguments.rs new file mode 100644 index 0000000000000..62b1e6bd7ca4e --- /dev/null +++ b/clippy_lints/src/functions/too_many_arguments.rs @@ -0,0 +1,73 @@ +use rustc_hir::{self as hir, intravisit}; +use rustc_lint::LateContext; +use rustc_span::Span; +use rustc_target::spec::abi::Abi; + +use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_trait_impl_item; + +use super::TOO_MANY_ARGUMENTS; + +pub(super) fn check_fn( + cx: &LateContext<'tcx>, + kind: intravisit::FnKind<'tcx>, + decl: &'tcx hir::FnDecl<'_>, + span: Span, + hir_id: hir::HirId, + too_many_arguments_threshold: u64, +) { + // don't warn for implementations, it's not their fault + if !is_trait_impl_item(cx, hir_id) { + // don't lint extern functions decls, it's not their fault either + match kind { + intravisit::FnKind::Method( + _, + &hir::FnSig { + header: hir::FnHeader { abi: Abi::Rust, .. }, + .. + }, + _, + ) + | intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }, _) => check_arg_number( + cx, + decl, + span.with_hi(decl.output.span().hi()), + too_many_arguments_threshold, + ), + _ => {}, + } + } +} + +pub(super) fn check_trait_item( + cx: &LateContext<'tcx>, + item: &'tcx hir::TraitItem<'_>, + too_many_arguments_threshold: u64, +) { + if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { + // don't lint extern functions decls, it's not their fault + if sig.header.abi == Abi::Rust { + check_arg_number( + cx, + &sig.decl, + item.span.with_hi(sig.decl.output.span().hi()), + too_many_arguments_threshold, + ); + } + } +} + +fn check_arg_number(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, fn_span: Span, too_many_arguments_threshold: u64) { + let args = decl.inputs.len() as u64; + if args > too_many_arguments_threshold { + span_lint( + cx, + TOO_MANY_ARGUMENTS, + fn_span, + &format!( + "this function has too many arguments ({}/{})", + args, too_many_arguments_threshold + ), + ); + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f013613119cf1..3d3e57f7d2fbe 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1124,9 +1124,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box new_without_default::NewWithoutDefault::default()); let blacklisted_names = conf.blacklisted_names.iter().cloned().collect::>(); store.register_late_pass(move || box blacklisted_name::BlacklistedName::new(blacklisted_names.clone())); - let too_many_arguments_threshold1 = conf.too_many_arguments_threshold; - let too_many_lines_threshold2 = conf.too_many_lines_threshold; - store.register_late_pass(move || box functions::Functions::new(too_many_arguments_threshold1, too_many_lines_threshold2)); + let too_many_arguments_threshold = conf.too_many_arguments_threshold; + let too_many_lines_threshold = conf.too_many_lines_threshold; + store.register_late_pass(move || box functions::Functions::new(too_many_arguments_threshold, too_many_lines_threshold)); let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::>(); store.register_late_pass(move || box doc::DocMarkdown::new(doc_valid_idents.clone())); store.register_late_pass(|| box neg_multiply::NegMultiply); From 7c028de05f7b6803c062e12ee09a41ea9007d1e1 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 24 Mar 2021 23:20:31 +0900 Subject: [PATCH 2/6] Move too_many_lines to its own module --- clippy_lints/src/functions/mod.rs | 64 +----------------- clippy_lints/src/functions/too_many_lines.rs | 68 ++++++++++++++++++++ 2 files changed, 71 insertions(+), 61 deletions(-) create mode 100644 clippy_lints/src/functions/too_many_lines.rs diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 68badb87b22d7..97d3623d9192b 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -1,7 +1,8 @@ mod too_many_arguments; +mod too_many_lines; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then}; -use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::source::snippet_opt; use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, type_is_unsafe_function}; use clippy_utils::{ attr_by_name, attrs::is_proc_macro, iter_input_pats, match_def_path, must_use_attr, path_to_local, return_ty, @@ -256,6 +257,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { hir_id: hir::HirId, ) { too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); + too_many_lines::check(cx, span, body, self.too_many_lines_threshold); let unsafety = match kind { intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _) => unsafety, @@ -264,7 +266,6 @@ impl<'tcx> LateLintPass<'tcx> for Functions { }; Self::check_raw_ptr(cx, unsafety, decl, body, hir_id); - self.check_line_number(cx, span, body); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { @@ -356,65 +357,6 @@ impl<'tcx> LateLintPass<'tcx> for Functions { } impl<'tcx> Functions { - fn check_line_number(self, cx: &LateContext<'_>, span: Span, body: &'tcx hir::Body<'_>) { - if in_external_macro(cx.sess(), span) { - return; - } - - let code_snippet = snippet(cx, body.value.span, ".."); - let mut line_count: u64 = 0; - let mut in_comment = false; - let mut code_in_line; - - // Skip the surrounding function decl. - let start_brace_idx = code_snippet.find('{').map_or(0, |i| i + 1); - let end_brace_idx = code_snippet.rfind('}').unwrap_or_else(|| code_snippet.len()); - let function_lines = code_snippet[start_brace_idx..end_brace_idx].lines(); - - for mut line in function_lines { - code_in_line = false; - loop { - line = line.trim_start(); - if line.is_empty() { - break; - } - if in_comment { - if let Some(i) = line.find("*/") { - line = &line[i + 2..]; - in_comment = false; - continue; - } - } else { - let multi_idx = line.find("/*").unwrap_or_else(|| line.len()); - let single_idx = line.find("//").unwrap_or_else(|| line.len()); - code_in_line |= multi_idx > 0 && single_idx > 0; - // Implies multi_idx is below line.len() - if multi_idx < single_idx { - line = &line[multi_idx + 2..]; - in_comment = true; - continue; - } - } - break; - } - if code_in_line { - line_count += 1; - } - } - - if line_count > self.too_many_lines_threshold { - span_lint( - cx, - TOO_MANY_LINES, - span, - &format!( - "this function has too many lines ({}/{})", - line_count, self.too_many_lines_threshold - ), - ) - } - } - fn check_raw_ptr( cx: &LateContext<'tcx>, unsafety: hir::Unsafety, diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs new file mode 100644 index 0000000000000..99d5028befc5c --- /dev/null +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -0,0 +1,68 @@ +use rustc_hir as hir; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_span::Span; + +use clippy_utils::diagnostics::span_lint; +use clippy_utils::source::snippet; + +use super::TOO_MANY_LINES; + +pub(super) fn check(cx: &LateContext<'_>, span: Span, body: &'tcx hir::Body<'_>, too_many_lines_threshold: u64) { + if in_external_macro(cx.sess(), span) { + return; + } + + let code_snippet = snippet(cx, body.value.span, ".."); + let mut line_count: u64 = 0; + let mut in_comment = false; + let mut code_in_line; + + // Skip the surrounding function decl. + let start_brace_idx = code_snippet.find('{').map_or(0, |i| i + 1); + let end_brace_idx = code_snippet.rfind('}').unwrap_or_else(|| code_snippet.len()); + let function_lines = code_snippet[start_brace_idx..end_brace_idx].lines(); + + for mut line in function_lines { + code_in_line = false; + loop { + line = line.trim_start(); + if line.is_empty() { + break; + } + if in_comment { + if let Some(i) = line.find("*/") { + line = &line[i + 2..]; + in_comment = false; + continue; + } + } else { + let multi_idx = line.find("/*").unwrap_or_else(|| line.len()); + let single_idx = line.find("//").unwrap_or_else(|| line.len()); + code_in_line |= multi_idx > 0 && single_idx > 0; + // Implies multi_idx is below line.len() + if multi_idx < single_idx { + line = &line[multi_idx + 2..]; + in_comment = true; + continue; + } + } + break; + } + if code_in_line { + line_count += 1; + } + } + + if line_count > too_many_lines_threshold { + span_lint( + cx, + TOO_MANY_LINES, + span, + &format!( + "this function has too many lines ({}/{})", + line_count, too_many_lines_threshold + ), + ) + } +} From 9782fc42852927428736f27f0496deece545f46c Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 27 Mar 2021 20:43:59 +0900 Subject: [PATCH 3/6] move not_unsafe_ptr_arg_deref to its own module --- clippy_lints/src/functions/mod.rs | 116 +--------------- .../src/functions/not_unsafe_ptr_arg_deref.rs | 125 ++++++++++++++++++ 2 files changed, 131 insertions(+), 110 deletions(-) create mode 100644 clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 97d3623d9192b..f53edc1b3c77f 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -1,13 +1,11 @@ +mod not_unsafe_ptr_arg_deref; mod too_many_arguments; mod too_many_lines; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, type_is_unsafe_function}; -use clippy_utils::{ - attr_by_name, attrs::is_proc_macro, iter_input_pats, match_def_path, must_use_attr, path_to_local, return_ty, - trait_ref_of_method, -}; +use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item}; +use clippy_utils::{attr_by_name, attrs::is_proc_macro, match_def_path, must_use_attr, return_ty, trait_ref_of_method}; use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_data_structures::fx::FxHashSet; @@ -258,14 +256,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { ) { too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); too_many_lines::check(cx, span, body, self.too_many_lines_threshold); - - let unsafety = match kind { - intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _) => unsafety, - intravisit::FnKind::Method(_, sig, _) => sig.header.unsafety, - intravisit::FnKind::Closure => return, - }; - - Self::check_raw_ptr(cx, unsafety, decl, body, hir_id); + not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { @@ -323,6 +314,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold); + not_unsafe_ptr_arg_deref::check_trait_item(cx, item); if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind { let is_public = cx.access_levels.is_exported(item.hir_id()); @@ -338,8 +330,6 @@ impl<'tcx> LateLintPass<'tcx> for Functions { } if let hir::TraitFn::Provided(eid) = *eid { let body = cx.tcx.hir().body(eid); - Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id()); - if attr.is_none() && is_public && !is_proc_macro(cx.sess(), attrs) { check_must_use_candidate( cx, @@ -356,35 +346,6 @@ impl<'tcx> LateLintPass<'tcx> for Functions { } } -impl<'tcx> Functions { - fn check_raw_ptr( - cx: &LateContext<'tcx>, - unsafety: hir::Unsafety, - decl: &'tcx hir::FnDecl<'_>, - body: &'tcx hir::Body<'_>, - hir_id: hir::HirId, - ) { - let expr = &body.value; - if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(hir_id) { - let raw_ptrs = iter_input_pats(decl, body) - .zip(decl.inputs.iter()) - .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty)) - .collect::>(); - - if !raw_ptrs.is_empty() { - let typeck_results = cx.tcx.typeck_body(body.id()); - let mut v = DerefVisitor { - cx, - ptrs: raw_ptrs, - typeck_results, - }; - - intravisit::walk_expr(&mut v, expr); - } - } - } -} - fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) { if_chain! { if !in_external_macro(cx.sess(), item_span); @@ -524,71 +485,6 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &m } } -fn raw_ptr_arg(arg: &hir::Param<'_>, ty: &hir::Ty<'_>) -> Option { - if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.kind, &ty.kind) { - Some(id) - } else { - None - } -} - -struct DerefVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - ptrs: FxHashSet, - typeck_results: &'a ty::TypeckResults<'tcx>, -} - -impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - match expr.kind { - hir::ExprKind::Call(ref f, args) => { - let ty = self.typeck_results.expr_ty(f); - - if type_is_unsafe_function(self.cx, ty) { - for arg in args { - self.check_arg(arg); - } - } - }, - hir::ExprKind::MethodCall(_, _, args, _) => { - let def_id = self.typeck_results.type_dependent_def_id(expr.hir_id).unwrap(); - let base_type = self.cx.tcx.type_of(def_id); - - if type_is_unsafe_function(self.cx, base_type) { - for arg in args { - self.check_arg(arg); - } - } - }, - hir::ExprKind::Unary(hir::UnOp::Deref, ref ptr) => self.check_arg(ptr), - _ => (), - } - - intravisit::walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } -} - -impl<'a, 'tcx> DerefVisitor<'a, 'tcx> { - fn check_arg(&self, ptr: &hir::Expr<'_>) { - if let Some(id) = path_to_local(ptr) { - if self.ptrs.contains(&id) { - span_lint( - self.cx, - NOT_UNSAFE_PTR_ARG_DEREF, - ptr.span, - "this public function dereferences a raw pointer but is not marked `unsafe`", - ); - } - } - } -} - struct StaticMutVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, mutates_static: bool, diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs new file mode 100644 index 0000000000000..ac02b60a356f6 --- /dev/null +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -0,0 +1,125 @@ +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::{self as hir, intravisit}; +use rustc_lint::LateContext; +use rustc_middle::{hir::map::Map, ty}; + +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::type_is_unsafe_function; +use clippy_utils::{iter_input_pats, path_to_local}; + +use super::NOT_UNSAFE_PTR_ARG_DEREF; + +pub(super) fn check_fn( + cx: &LateContext<'tcx>, + kind: intravisit::FnKind<'tcx>, + decl: &'tcx hir::FnDecl<'tcx>, + body: &'tcx hir::Body<'tcx>, + hir_id: hir::HirId, +) { + let unsafety = match kind { + intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _) => unsafety, + intravisit::FnKind::Method(_, sig, _) => sig.header.unsafety, + intravisit::FnKind::Closure => return, + }; + + check_raw_ptr(cx, unsafety, decl, body, hir_id); +} + +pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { + if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind { + let body = cx.tcx.hir().body(eid); + check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id()); + } +} + +fn check_raw_ptr( + cx: &LateContext<'tcx>, + unsafety: hir::Unsafety, + decl: &'tcx hir::FnDecl<'tcx>, + body: &'tcx hir::Body<'tcx>, + hir_id: hir::HirId, +) { + let expr = &body.value; + if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(hir_id) { + let raw_ptrs = iter_input_pats(decl, body) + .zip(decl.inputs.iter()) + .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty)) + .collect::>(); + + if !raw_ptrs.is_empty() { + let typeck_results = cx.tcx.typeck_body(body.id()); + let mut v = DerefVisitor { + cx, + ptrs: raw_ptrs, + typeck_results, + }; + + intravisit::walk_expr(&mut v, expr); + } + } +} + +fn raw_ptr_arg(arg: &hir::Param<'_>, ty: &hir::Ty<'_>) -> Option { + if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.kind, &ty.kind) { + Some(id) + } else { + None + } +} + +struct DerefVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + ptrs: FxHashSet, + typeck_results: &'a ty::TypeckResults<'tcx>, +} + +impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + match expr.kind { + hir::ExprKind::Call(ref f, args) => { + let ty = self.typeck_results.expr_ty(f); + + if type_is_unsafe_function(self.cx, ty) { + for arg in args { + self.check_arg(arg); + } + } + }, + hir::ExprKind::MethodCall(_, _, args, _) => { + let def_id = self.typeck_results.type_dependent_def_id(expr.hir_id).unwrap(); + let base_type = self.cx.tcx.type_of(def_id); + + if type_is_unsafe_function(self.cx, base_type) { + for arg in args { + self.check_arg(arg); + } + } + }, + hir::ExprKind::Unary(hir::UnOp::Deref, ref ptr) => self.check_arg(ptr), + _ => (), + } + + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } +} + +impl<'a, 'tcx> DerefVisitor<'a, 'tcx> { + fn check_arg(&self, ptr: &hir::Expr<'_>) { + if let Some(id) = path_to_local(ptr) { + if self.ptrs.contains(&id) { + span_lint( + self.cx, + NOT_UNSAFE_PTR_ARG_DEREF, + ptr.span, + "this public function dereferences a raw pointer but is not marked `unsafe`", + ); + } + } + } +} From c37916501db2c649f57d2cc0d84ee41db34094c4 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 27 Mar 2021 22:41:55 +0900 Subject: [PATCH 4/6] Move lints related to must_use to their own module --- clippy_lints/src/functions/mod.rs | 259 +---------------------- clippy_lints/src/functions/must_use.rs | 272 +++++++++++++++++++++++++ 2 files changed, 283 insertions(+), 248 deletions(-) create mode 100644 clippy_lints/src/functions/must_use.rs diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index f53edc1b3c77f..a1f4a453608dc 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -1,22 +1,17 @@ +mod must_use; mod not_unsafe_ptr_arg_deref; mod too_many_arguments; mod too_many_lines; -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; -use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item}; -use clippy_utils::{attr_by_name, attrs::is_proc_macro, match_def_path, must_use_attr, return_ty, trait_ref_of_method}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::trait_ref_of_method; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; -use rustc_ast::ast::Attribute; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit; -use rustc_hir::{def::Res, def_id::DefId, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; use rustc_typeck::hir_ty_to_ty; @@ -260,88 +255,38 @@ impl<'tcx> LateLintPass<'tcx> for Functions { } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = must_use_attr(attrs); - if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind { + must_use::check_item(cx, item); + if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind { let is_public = cx.access_levels.is_exported(item.hir_id()); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if is_public { check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); } - if let Some(attr) = attr { - check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); - return; - } - if is_public && !is_proc_macro(cx.sess(), attrs) && attr_by_name(attrs, "no_mangle").is_none() { - check_must_use_candidate( - cx, - &sig.decl, - cx.tcx.hir().body(*body_id), - item.span, - item.hir_id(), - item.span.with_hi(sig.decl.output.span().hi()), - "this function could have a `#[must_use]` attribute", - ); - } } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { - if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind { + must_use::check_impl_item(cx, item); + if let hir::ImplItemKind::Fn(ref sig, _) = item.kind { let is_public = cx.access_levels.is_exported(item.hir_id()); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if is_public && trait_ref_of_method(cx, item.hir_id()).is_none() { check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); } - let attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = must_use_attr(attrs); - if let Some(attr) = attr { - check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); - } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.hir_id()).is_none() - { - check_must_use_candidate( - cx, - &sig.decl, - cx.tcx.hir().body(*body_id), - item.span, - item.hir_id(), - item.span.with_hi(sig.decl.output.span().hi()), - "this method could have a `#[must_use]` attribute", - ); - } } } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold); not_unsafe_ptr_arg_deref::check_trait_item(cx, item); + must_use::check_trait_item(cx, item); - if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind { + if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { let is_public = cx.access_levels.is_exported(item.hir_id()); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if is_public { check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); } - - let attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = must_use_attr(attrs); - if let Some(attr) = attr { - check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); - } - if let hir::TraitFn::Provided(eid) = *eid { - let body = cx.tcx.hir().body(eid); - if attr.is_none() && is_public && !is_proc_macro(cx.sess(), attrs) { - check_must_use_candidate( - cx, - &sig.decl, - body, - item.span, - item.hir_id(), - item.span.with_hi(sig.decl.output.span().hi()), - "this method could have a `#[must_use]` attribute", - ); - } - } } } } @@ -367,185 +312,3 @@ fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span } } } - -fn check_needless_must_use( - cx: &LateContext<'_>, - decl: &hir::FnDecl<'_>, - item_id: hir::HirId, - item_span: Span, - fn_header_span: Span, - attr: &Attribute, -) { - if in_external_macro(cx.sess(), item_span) { - return; - } - if returns_unit(decl) { - span_lint_and_then( - cx, - MUST_USE_UNIT, - fn_header_span, - "this unit-returning function has a `#[must_use]` attribute", - |diag| { - diag.span_suggestion( - attr.span, - "remove the attribute", - "".into(), - Applicability::MachineApplicable, - ); - }, - ); - } else if !attr.is_value_str() && is_must_use_ty(cx, return_ty(cx, item_id)) { - span_lint_and_help( - cx, - DOUBLE_MUST_USE, - fn_header_span, - "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`", - None, - "either add some descriptive text or remove the attribute", - ); - } -} - -fn check_must_use_candidate<'tcx>( - cx: &LateContext<'tcx>, - decl: &'tcx hir::FnDecl<'_>, - body: &'tcx hir::Body<'_>, - item_span: Span, - item_id: hir::HirId, - fn_span: Span, - msg: &str, -) { - if has_mutable_arg(cx, body) - || mutates_static(cx, body) - || in_external_macro(cx.sess(), item_span) - || returns_unit(decl) - || !cx.access_levels.is_exported(item_id) - || is_must_use_ty(cx, return_ty(cx, item_id)) - { - return; - } - span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| { - if let Some(snippet) = snippet_opt(cx, fn_span) { - diag.span_suggestion( - fn_span, - "add the attribute", - format!("#[must_use] {}", snippet), - Applicability::MachineApplicable, - ); - } - }); -} - -fn returns_unit(decl: &hir::FnDecl<'_>) -> bool { - match decl.output { - hir::FnRetTy::DefaultReturn(_) => true, - hir::FnRetTy::Return(ref ty) => match ty.kind { - hir::TyKind::Tup(ref tys) => tys.is_empty(), - hir::TyKind::Never => true, - _ => false, - }, - } -} - -fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool { - let mut tys = FxHashSet::default(); - body.params.iter().any(|param| is_mutable_pat(cx, ¶m.pat, &mut tys)) -} - -fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet) -> bool { - if let hir::PatKind::Wild = pat.kind { - return false; // ignore `_` patterns - } - if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) { - is_mutable_ty(cx, &cx.tcx.typeck(pat.hir_id.owner).pat_ty(pat), pat.span, tys) - } else { - false - } -} - -static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]]; - -fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut FxHashSet) -> bool { - match *ty.kind() { - // primitive types are never mutable - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, - ty::Adt(ref adt, ref substs) => { - tys.insert(adt.did) && !ty.is_freeze(cx.tcx.at(span), cx.param_env) - || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did, path)) - && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) - }, - ty::Tuple(ref substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)), - ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys), - ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => { - mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys) - }, - // calling something constitutes a side effect, so return true on all callables - // also never calls need not be used, so return true for them, too - _ => true, - } -} - -struct StaticMutVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - mutates_static: bool, -} - -impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall}; - - if self.mutates_static { - return; - } - match expr.kind { - Call(_, args) | MethodCall(_, _, args, _) => { - let mut tys = FxHashSet::default(); - for arg in args { - if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) - && is_mutable_ty( - self.cx, - self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg), - arg.span, - &mut tys, - ) - && is_mutated_static(arg) - { - self.mutates_static = true; - return; - } - tys.clear(); - } - }, - Assign(ref target, ..) | AssignOp(_, ref target, _) | AddrOf(_, hir::Mutability::Mut, ref target) => { - self.mutates_static |= is_mutated_static(target) - }, - _ => {}, - } - } - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } -} - -fn is_mutated_static(e: &hir::Expr<'_>) -> bool { - use hir::ExprKind::{Field, Index, Path}; - - match e.kind { - Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)), - Path(_) => true, - Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(inner), - _ => false, - } -} - -fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool { - let mut v = StaticMutVisitor { - cx, - mutates_static: false, - }; - intravisit::walk_expr(&mut v, &body.value); - v.mutates_static -} diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs new file mode 100644 index 0000000000000..3825699936ffd --- /dev/null +++ b/clippy_lints/src/functions/must_use.rs @@ -0,0 +1,272 @@ +use rustc_ast::ast::Attribute; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir::{self as hir, def::Res, def_id::DefId, intravisit, QPath}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::{ + hir::map::Map, + lint::in_external_macro, + ty::{self, Ty}, +}; +use rustc_span::Span; + +use clippy_utils::attrs::is_proc_macro; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::is_must_use_ty; +use clippy_utils::{attr_by_name, match_def_path, must_use_attr, return_ty, trait_ref_of_method}; + +use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; + +pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let attr = must_use_attr(attrs); + if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind { + let is_public = cx.access_levels.is_exported(item.hir_id()); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if let Some(attr) = attr { + check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); + return; + } else if is_public && !is_proc_macro(cx.sess(), attrs) && attr_by_name(attrs, "no_mangle").is_none() { + check_must_use_candidate( + cx, + &sig.decl, + cx.tcx.hir().body(*body_id), + item.span, + item.hir_id(), + item.span.with_hi(sig.decl.output.span().hi()), + "this function could have a `#[must_use]` attribute", + ); + } + } +} + +pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { + if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind { + let is_public = cx.access_levels.is_exported(item.hir_id()); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let attr = must_use_attr(attrs); + if let Some(attr) = attr { + check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); + } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.hir_id()).is_none() { + check_must_use_candidate( + cx, + &sig.decl, + cx.tcx.hir().body(*body_id), + item.span, + item.hir_id(), + item.span.with_hi(sig.decl.output.span().hi()), + "this method could have a `#[must_use]` attribute", + ); + } + } +} + +pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { + if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind { + let is_public = cx.access_levels.is_exported(item.hir_id()); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let attr = must_use_attr(attrs); + if let Some(attr) = attr { + check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); + } else if let hir::TraitFn::Provided(eid) = *eid { + let body = cx.tcx.hir().body(eid); + if attr.is_none() && is_public && !is_proc_macro(cx.sess(), attrs) { + check_must_use_candidate( + cx, + &sig.decl, + body, + item.span, + item.hir_id(), + item.span.with_hi(sig.decl.output.span().hi()), + "this method could have a `#[must_use]` attribute", + ); + } + } + } +} + +fn check_needless_must_use( + cx: &LateContext<'_>, + decl: &hir::FnDecl<'_>, + item_id: hir::HirId, + item_span: Span, + fn_header_span: Span, + attr: &Attribute, +) { + if in_external_macro(cx.sess(), item_span) { + return; + } + if returns_unit(decl) { + span_lint_and_then( + cx, + MUST_USE_UNIT, + fn_header_span, + "this unit-returning function has a `#[must_use]` attribute", + |diag| { + diag.span_suggestion( + attr.span, + "remove the attribute", + "".into(), + Applicability::MachineApplicable, + ); + }, + ); + } else if !attr.is_value_str() && is_must_use_ty(cx, return_ty(cx, item_id)) { + span_lint_and_help( + cx, + DOUBLE_MUST_USE, + fn_header_span, + "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`", + None, + "either add some descriptive text or remove the attribute", + ); + } +} + +fn check_must_use_candidate<'tcx>( + cx: &LateContext<'tcx>, + decl: &'tcx hir::FnDecl<'_>, + body: &'tcx hir::Body<'_>, + item_span: Span, + item_id: hir::HirId, + fn_span: Span, + msg: &str, +) { + if has_mutable_arg(cx, body) + || mutates_static(cx, body) + || in_external_macro(cx.sess(), item_span) + || returns_unit(decl) + || !cx.access_levels.is_exported(item_id) + || is_must_use_ty(cx, return_ty(cx, item_id)) + { + return; + } + span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| { + if let Some(snippet) = snippet_opt(cx, fn_span) { + diag.span_suggestion( + fn_span, + "add the attribute", + format!("#[must_use] {}", snippet), + Applicability::MachineApplicable, + ); + } + }); +} + +fn returns_unit(decl: &hir::FnDecl<'_>) -> bool { + match decl.output { + hir::FnRetTy::DefaultReturn(_) => true, + hir::FnRetTy::Return(ref ty) => match ty.kind { + hir::TyKind::Tup(ref tys) => tys.is_empty(), + hir::TyKind::Never => true, + _ => false, + }, + } +} + +fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool { + let mut tys = FxHashSet::default(); + body.params.iter().any(|param| is_mutable_pat(cx, ¶m.pat, &mut tys)) +} + +fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet) -> bool { + if let hir::PatKind::Wild = pat.kind { + return false; // ignore `_` patterns + } + if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) { + is_mutable_ty(cx, &cx.tcx.typeck(pat.hir_id.owner).pat_ty(pat), pat.span, tys) + } else { + false + } +} + +static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]]; + +fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut FxHashSet) -> bool { + match *ty.kind() { + // primitive types are never mutable + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, + ty::Adt(ref adt, ref substs) => { + tys.insert(adt.did) && !ty.is_freeze(cx.tcx.at(span), cx.param_env) + || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did, path)) + && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) + }, + ty::Tuple(ref substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)), + ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys), + ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => { + mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys) + }, + // calling something constitutes a side effect, so return true on all callables + // also never calls need not be used, so return true for them, too + _ => true, + } +} + +struct StaticMutVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + mutates_static: bool, +} + +impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall}; + + if self.mutates_static { + return; + } + match expr.kind { + Call(_, args) | MethodCall(_, _, args, _) => { + let mut tys = FxHashSet::default(); + for arg in args { + if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) + && is_mutable_ty( + self.cx, + self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg), + arg.span, + &mut tys, + ) + && is_mutated_static(arg) + { + self.mutates_static = true; + return; + } + tys.clear(); + } + }, + Assign(ref target, ..) | AssignOp(_, ref target, _) | AddrOf(_, hir::Mutability::Mut, ref target) => { + self.mutates_static |= is_mutated_static(target) + }, + _ => {}, + } + } + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } +} + +fn is_mutated_static(e: &hir::Expr<'_>) -> bool { + use hir::ExprKind::{Field, Index, Path}; + + match e.kind { + Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)), + Path(_) => true, + Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(inner), + _ => false, + } +} + +fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool { + let mut v = StaticMutVisitor { + cx, + mutates_static: false, + }; + intravisit::walk_expr(&mut v, &body.value); + v.mutates_static +} From add3e5094f83e7b36f7dfa0813be4c9d8d0706c1 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 27 Mar 2021 22:48:25 +0900 Subject: [PATCH 5/6] Move result_unit_err to its own module --- clippy_lints/src/functions/mod.rs | 61 ++--------------- clippy_lints/src/functions/result_unit_err.rs | 66 +++++++++++++++++++ clippy_lints/src/functions/too_many_lines.rs | 2 +- 3 files changed, 74 insertions(+), 55 deletions(-) create mode 100644 clippy_lints/src/functions/result_unit_err.rs diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index a1f4a453608dc..4ba5166059fb5 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -1,20 +1,14 @@ mod must_use; mod not_unsafe_ptr_arg_deref; +mod result_unit_err; mod too_many_arguments; mod too_many_lines; -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::trait_ref_of_method; -use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::intravisit; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, Span}; -use rustc_typeck::hir_ty_to_ty; +use rustc_span::Span; declare_clippy_lint! { /// **What it does:** Checks for functions with too many parameters. @@ -250,65 +244,24 @@ impl<'tcx> LateLintPass<'tcx> for Functions { hir_id: hir::HirId, ) { too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); - too_many_lines::check(cx, span, body, self.too_many_lines_threshold); + too_many_lines::check_fn(cx, span, body, self.too_many_lines_threshold); not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { must_use::check_item(cx, item); - if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind { - let is_public = cx.access_levels.is_exported(item.hir_id()); - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); - if is_public { - check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); - } - } + result_unit_err::check_item(cx, item); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { must_use::check_impl_item(cx, item); - if let hir::ImplItemKind::Fn(ref sig, _) = item.kind { - let is_public = cx.access_levels.is_exported(item.hir_id()); - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); - if is_public && trait_ref_of_method(cx, item.hir_id()).is_none() { - check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); - } - } + result_unit_err::check_impl_item(cx, item); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold); not_unsafe_ptr_arg_deref::check_trait_item(cx, item); must_use::check_trait_item(cx, item); - - if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { - let is_public = cx.access_levels.is_exported(item.hir_id()); - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); - if is_public { - check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); - } - } - } -} - -fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) { - if_chain! { - if !in_external_macro(cx.sess(), item_span); - if let hir::FnRetTy::Return(ref ty) = decl.output; - let ty = hir_ty_to_ty(cx.tcx, ty); - if is_type_diagnostic_item(cx, ty, sym::result_type); - if let ty::Adt(_, substs) = ty.kind(); - let err_ty = substs.type_at(1); - if err_ty.is_unit(); - then { - span_lint_and_help( - cx, - RESULT_UNIT_ERR, - fn_header_span, - "this returns a `Result<_, ()>", - None, - "use a custom Error type instead", - ); - } + result_unit_err::check_trait_item(cx, item); } } diff --git a/clippy_lints/src/functions/result_unit_err.rs b/clippy_lints/src/functions/result_unit_err.rs new file mode 100644 index 0000000000000..f71bfc690f6c6 --- /dev/null +++ b/clippy_lints/src/functions/result_unit_err.rs @@ -0,0 +1,66 @@ +use rustc_hir as hir; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; +use rustc_span::{sym, Span}; +use rustc_typeck::hir_ty_to_ty; + +use if_chain::if_chain; + +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::trait_ref_of_method; +use clippy_utils::ty::is_type_diagnostic_item; + +use super::RESULT_UNIT_ERR; + +pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind { + let is_public = cx.access_levels.is_exported(item.hir_id()); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if is_public { + check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); + } + } +} + +pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { + if let hir::ImplItemKind::Fn(ref sig, _) = item.kind { + let is_public = cx.access_levels.is_exported(item.hir_id()); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if is_public && trait_ref_of_method(cx, item.hir_id()).is_none() { + check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); + } + } +} + +pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { + if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { + let is_public = cx.access_levels.is_exported(item.hir_id()); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if is_public { + check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); + } + } +} + +fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) { + if_chain! { + if !in_external_macro(cx.sess(), item_span); + if let hir::FnRetTy::Return(ref ty) = decl.output; + let ty = hir_ty_to_ty(cx.tcx, ty); + if is_type_diagnostic_item(cx, ty, sym::result_type); + if let ty::Adt(_, substs) = ty.kind(); + let err_ty = substs.type_at(1); + if err_ty.is_unit(); + then { + span_lint_and_help( + cx, + RESULT_UNIT_ERR, + fn_header_span, + "this returns a `Result<_, ()>", + None, + "use a custom Error type instead", + ); + } + } +} diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs index 99d5028befc5c..aa5494d5a7d2c 100644 --- a/clippy_lints/src/functions/too_many_lines.rs +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -8,7 +8,7 @@ use clippy_utils::source::snippet; use super::TOO_MANY_LINES; -pub(super) fn check(cx: &LateContext<'_>, span: Span, body: &'tcx hir::Body<'_>, too_many_lines_threshold: u64) { +pub(super) fn check_fn(cx: &LateContext<'_>, span: Span, body: &'tcx hir::Body<'_>, too_many_lines_threshold: u64) { if in_external_macro(cx.sess(), span) { return; } From 541c8b8f69b1b14fe3de272d5fad4323cfd1ae0c Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 30 Mar 2021 11:45:54 +0900 Subject: [PATCH 6/6] Improve documents in functions group --- clippy_lints/src/functions/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 4ba5166059fb5..2beb9bc94bf06 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -56,7 +56,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for public functions that dereference raw pointer - /// arguments but are not marked unsafe. + /// arguments but are not marked `unsafe`. /// /// **Why is this bad?** The function should probably be marked `unsafe`, since /// for an arbitrary raw pointer, there is no way of telling for sure if it is @@ -165,7 +165,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for public functions that return a `Result` /// with an `Err` type of `()`. It suggests using a custom type that - /// implements [`std::error::Error`]. + /// implements `std::error::Error`. /// /// **Why is this bad?** Unit does not implement `Error` and carries no /// further information about what went wrong.