Rewrite ExcessiveBools
to be a LateLintPass
lint
changelog: [`fn_params_excessive_bools`] Make it possible to allow the lint at the method level
This commit is contained in:
parent
586bd3f735
commit
9c69e93595
@ -1,8 +1,11 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use rustc_ast::ast::{AssocItemKind, Extern, Fn, FnSig, Impl, Item, ItemKind, Trait, Ty, TyKind};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use clippy_utils::{get_parent_node, is_bool};
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, Node, Ty};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -83,6 +86,12 @@ pub struct ExcessiveBools {
|
||||
max_fn_params_bools: u64,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
enum Kind {
|
||||
Struct,
|
||||
Fn,
|
||||
}
|
||||
|
||||
impl ExcessiveBools {
|
||||
#[must_use]
|
||||
pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self {
|
||||
@ -92,21 +101,20 @@ pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_fn_sig(&self, cx: &EarlyContext<'_>, fn_sig: &FnSig, span: Span) {
|
||||
match fn_sig.header.ext {
|
||||
Extern::Implicit(_) | Extern::Explicit(_, _) => return,
|
||||
Extern::None => (),
|
||||
fn too_many_bools<'tcx>(&self, tys: impl Iterator<Item = &'tcx Ty<'tcx>>, kind: Kind) -> bool {
|
||||
if let Ok(bools) = tys.filter(|ty| is_bool(ty)).count().try_into() {
|
||||
(if Kind::Fn == kind {
|
||||
self.max_fn_params_bools
|
||||
} else {
|
||||
self.max_struct_bools
|
||||
}) < bools
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
let fn_sig_bools = fn_sig
|
||||
.decl
|
||||
.inputs
|
||||
.iter()
|
||||
.filter(|param| is_bool_ty(¶m.ty))
|
||||
.count()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
if self.max_fn_params_bools < fn_sig_bools {
|
||||
fn check_fn_sig(&self, cx: &LateContext<'_>, fn_decl: &FnDecl<'_>, span: Span) {
|
||||
if self.too_many_bools(fn_decl.inputs.iter(), Kind::Fn) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
FN_PARAMS_EXCESSIVE_BOOLS,
|
||||
@ -121,55 +129,53 @@ fn check_fn_sig(&self, cx: &EarlyContext<'_>, fn_sig: &FnSig, span: Span) {
|
||||
|
||||
impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]);
|
||||
|
||||
fn is_bool_ty(ty: &Ty) -> bool {
|
||||
if let TyKind::Path(None, path) = &ty.kind {
|
||||
if let [name] = path.segments.as_slice() {
|
||||
return name.ident.name == sym::bool;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
impl EarlyLintPass for ExcessiveBools {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
impl<'tcx> LateLintPass<'tcx> for ExcessiveBools {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||
if item.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
match &item.kind {
|
||||
ItemKind::Struct(variant_data, _) => {
|
||||
if item.attrs.iter().any(|attr| attr.has_name(sym::repr)) {
|
||||
return;
|
||||
}
|
||||
if let ItemKind::Struct(variant_data, _) = &item.kind {
|
||||
if cx
|
||||
.tcx
|
||||
.hir()
|
||||
.attrs(item.hir_id())
|
||||
.iter()
|
||||
.any(|attr| attr.has_name(sym::repr))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if let Ok(struct_bools) = variant_data
|
||||
.fields()
|
||||
.iter()
|
||||
.filter(|field| is_bool_ty(&field.ty))
|
||||
.count()
|
||||
.try_into() && self.max_struct_bools < struct_bools
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
STRUCT_EXCESSIVE_BOOLS,
|
||||
item.span,
|
||||
&format!("more than {} bools in a struct", self.max_struct_bools),
|
||||
None,
|
||||
"consider using a state machine or refactoring bools into two-variant enums",
|
||||
)
|
||||
}
|
||||
},
|
||||
ItemKind::Impl(box Impl {
|
||||
of_trait: None, items, ..
|
||||
})
|
||||
| ItemKind::Trait(box Trait { items, .. }) => {
|
||||
for item in items {
|
||||
if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind {
|
||||
self.check_fn_sig(cx, sig, item.span);
|
||||
}
|
||||
}
|
||||
},
|
||||
ItemKind::Fn(box Fn { sig, .. }) => self.check_fn_sig(cx, sig, item.span),
|
||||
_ => (),
|
||||
if self.too_many_bools(variant_data.fields().iter().map(|field| field.ty), Kind::Struct) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
STRUCT_EXCESSIVE_BOOLS,
|
||||
item.span,
|
||||
&format!("more than {} bools in a struct", self.max_struct_bools),
|
||||
None,
|
||||
"consider using a state machine or refactoring bools into two-variant enums",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
cx: &LateContext<'tcx>,
|
||||
fn_kind: FnKind<'tcx>,
|
||||
fn_decl: &'tcx FnDecl<'tcx>,
|
||||
_: &'tcx Body<'tcx>,
|
||||
span: Span,
|
||||
hir_id: HirId,
|
||||
) {
|
||||
if let Some(fn_header) = fn_kind.header()
|
||||
&& fn_header.abi == Abi::Rust
|
||||
&& if let Some(Node::Item(item)) = get_parent_node(cx.tcx, hir_id) {
|
||||
!matches!(item.kind, ItemKind::ExternCrate(..))
|
||||
} else {
|
||||
true
|
||||
}
|
||||
&& !span.from_expansion() {
|
||||
self.check_fn_sig(cx, fn_decl, span)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -793,7 +793,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_early_pass(|| Box::new(single_component_path_imports::SingleComponentPathImports));
|
||||
let max_fn_params_bools = conf.max_fn_params_bools;
|
||||
let max_struct_bools = conf.max_struct_bools;
|
||||
store.register_early_pass(move || {
|
||||
store.register_late_pass(move |_| {
|
||||
Box::new(excessive_bools::ExcessiveBools::new(
|
||||
max_struct_bools,
|
||||
max_fn_params_bools,
|
||||
|
@ -104,11 +104,10 @@
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
||||
use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
|
||||
use clippy_utils::{contains_return, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty};
|
||||
use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind};
|
||||
use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind};
|
||||
use rustc_hir_analysis::hir_ty_to_ty;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
@ -3966,14 +3965,6 @@ fn matches(self, ty: &hir::FnRetTy<'_>) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_bool(ty: &hir::Ty<'_>) -> bool {
|
||||
if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
|
||||
matches!(path.res, Res::PrimTy(PrimTy::Bool))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
|
||||
expected.constness == actual.constness
|
||||
&& expected.unsafety == actual.unsafety
|
||||
|
@ -8,7 +8,7 @@
|
||||
use rustc_hir::{
|
||||
ArrayLen, BinOpKind, BindingAnnotation, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg,
|
||||
GenericArgs, Guard, HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path,
|
||||
PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
|
||||
PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
|
||||
};
|
||||
use rustc_lexer::{tokenize, TokenKind};
|
||||
use rustc_lint::LateContext;
|
||||
@ -1030,6 +1030,14 @@ pub fn hash_stmt(cx: &LateContext<'_>, s: &Stmt<'_>) -> u64 {
|
||||
h.finish()
|
||||
}
|
||||
|
||||
pub fn is_bool(ty: &Ty<'_>) -> bool {
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
|
||||
matches!(path.res, Res::PrimTy(PrimTy::Bool))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 {
|
||||
let mut h = SpanlessHash::new(cx);
|
||||
h.hash_expr(e);
|
||||
|
@ -66,7 +66,7 @@
|
||||
pub use self::attrs::*;
|
||||
pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
|
||||
pub use self::hir_utils::{
|
||||
both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
|
||||
both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
|
||||
};
|
||||
|
||||
use core::ops::ControlFlow;
|
||||
|
@ -2,6 +2,7 @@
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
|
||||
extern "C" {
|
||||
// Should not lint, most of the time users have no control over extern function signatures
|
||||
fn f(_: bool, _: bool, _: bool, _: bool);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: more than 3 bools in function parameters
|
||||
--> $DIR/fn_params_excessive_bools.rs:18:1
|
||||
--> $DIR/fn_params_excessive_bools.rs:19:1
|
||||
|
|
||||
LL | fn g(_: bool, _: bool, _: bool, _: bool) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -8,7 +8,7 @@ LL | fn g(_: bool, _: bool, _: bool, _: bool) {}
|
||||
= note: `-D clippy::fn-params-excessive-bools` implied by `-D warnings`
|
||||
|
||||
error: more than 3 bools in function parameters
|
||||
--> $DIR/fn_params_excessive_bools.rs:21:1
|
||||
--> $DIR/fn_params_excessive_bools.rs:22:1
|
||||
|
|
||||
LL | fn t(_: S, _: S, _: Box<S>, _: Vec<u32>, _: bool, _: bool, _: bool, _: bool) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -16,15 +16,7 @@ LL | fn t(_: S, _: S, _: Box<S>, _: Vec<u32>, _: bool, _: bool, _: bool, _: bool
|
||||
= help: consider refactoring bools into two-variant enums
|
||||
|
||||
error: more than 3 bools in function parameters
|
||||
--> $DIR/fn_params_excessive_bools.rs:25:5
|
||||
|
|
||||
LL | fn f(_: bool, _: bool, _: bool, _: bool);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider refactoring bools into two-variant enums
|
||||
|
||||
error: more than 3 bools in function parameters
|
||||
--> $DIR/fn_params_excessive_bools.rs:30:5
|
||||
--> $DIR/fn_params_excessive_bools.rs:31:5
|
||||
|
|
||||
LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -32,7 +24,15 @@ LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {}
|
||||
= help: consider refactoring bools into two-variant enums
|
||||
|
||||
error: more than 3 bools in function parameters
|
||||
--> $DIR/fn_params_excessive_bools.rs:42:5
|
||||
--> $DIR/fn_params_excessive_bools.rs:38:5
|
||||
|
|
||||
LL | fn f(_: bool, _: bool, _: bool, _: bool) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider refactoring bools into two-variant enums
|
||||
|
||||
error: more than 3 bools in function parameters
|
||||
--> $DIR/fn_params_excessive_bools.rs:43:5
|
||||
|
|
||||
LL | / fn n(_: bool, _: u32, _: bool, _: Box<u32>, _: bool, _: bool) {
|
||||
LL | | fn nn(_: bool, _: bool, _: bool, _: bool) {}
|
||||
@ -42,7 +42,7 @@ LL | | }
|
||||
= help: consider refactoring bools into two-variant enums
|
||||
|
||||
error: more than 3 bools in function parameters
|
||||
--> $DIR/fn_params_excessive_bools.rs:43:9
|
||||
--> $DIR/fn_params_excessive_bools.rs:44:9
|
||||
|
|
||||
LL | fn nn(_: bool, _: bool, _: bool, _: bool) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
Loading…
Reference in New Issue
Block a user