Skip to content

Commit

Permalink
Add lint for 'toilet closures'
Browse files Browse the repository at this point in the history
  • Loading branch information
GnomedDev committed Oct 14, 2024
1 parent 04849bd commit ed4cb9f
Show file tree
Hide file tree
Showing 41 changed files with 354 additions and 187 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5976,6 +5976,7 @@ Released 2018-09-13
[`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
[`to_string_trait_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_trait_impl
[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
[`toilet_closures`]: https://rust-lang.github.io/rust-clippy/master/index.html#toilet_closures
[`too_long_first_doc_paragraph`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_long_first_doc_paragraph
[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO,
crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO,
crate::to_string_trait_impl::TO_STRING_TRAIT_IMPL_INFO,
crate::toilet_closures::TOILET_CLOSURES_INFO,
crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO,
crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO,
crate::trait_bounds::TYPE_REPETITION_IN_BOUNDS_INFO,
Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ mod temporary_assignment;
mod tests_outside_test_module;
mod to_digit_is_some;
mod to_string_trait_impl;
mod toilet_closures;
mod trailing_empty_array;
mod trait_bounds;
mod transmute;
Expand Down Expand Up @@ -949,5 +950,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions));
store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
store.register_late_pass(|_| Box::new(toilet_closures::ToiletClosures));
// add lints here, do not remove this comment, it's used in `new_lint`
}
4 changes: 3 additions & 1 deletion clippy_lints/src/returns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,9 @@ impl RetReplacement<'_> {
fn applicability(&self) -> Applicability {
match self {
Self::Expr(_, ap) | Self::NeedsPar(_, ap) => *ap,
_ => Applicability::MachineApplicable,
Self::Empty | Self::Unit => Applicability::MachineApplicable,
// This should be MachineApplicable, but it can trigger `toilet_closures`.
Self::Block => Applicability::MaybeIncorrect,
}
}
}
Expand Down
90 changes: 90 additions & 0 deletions clippy_lints/src/toilet_closures.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir::{Block, Closure, ClosureKind, Expr, ExprKind, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::declare_lint_pass;

declare_clippy_lint! {
/// ### What it does
///
/// Detects a closure which takes one argument and returns unit.
///
/// ### Why is this bad?
///
/// This is a less clear way to write `drop`.
///
/// ### Example
/// ```no_run
/// fn drop_ok<T, E>(result: Result<T, E>) -> Result<(), E> {
/// result.map(|_| ())
/// }
/// ```
/// Use instead:
/// ```no_run
/// fn drop_ok<T, E>(result: Result<T, E>) -> Result<(), E> {
/// result.map(drop)
/// }
/// ```
#[clippy::version = "1.83.0"]
pub TOILET_CLOSURES,
style,
"checks for 'toilet closure' usage"
}

declare_lint_pass!(ToiletClosures => [TOILET_CLOSURES]);

fn is_empty_expr(expr: &Expr<'_>) -> bool {
matches!(
expr.kind,
ExprKind::Tup([])
| ExprKind::Block(
Block {
stmts: [],
expr: None,
..
},
None,
)
)
}

impl<'tcx> LateLintPass<'tcx> for ToiletClosures {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
let ExprKind::Closure(Closure {
fn_decl,
body,
kind: ClosureKind::Closure,
..
}) = expr.kind
else {
return;
};

// Check that the closure takes one argument
if let [arg_ty] = fn_decl.inputs
// and returns `()` or `{}`
&& is_empty_expr(cx.tcx.hir().body(*body).value)
// and the closure is not higher ranked, which `drop` cannot replace
&& let ty::Closure(_, generic_args) = cx.typeck_results().expr_ty(expr).kind()
&& generic_args.as_closure().sig().bound_vars().is_empty()
{
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
TOILET_CLOSURES,
expr.span,
"defined a 'toilet closure'",
"replace with",
if matches!(arg_ty.kind, TyKind::Infer) {
String::from("drop")
} else {
let arg_ty_snippet = snippet_with_applicability(cx, arg_ty.span, "...", &mut applicability);
format!("drop::<{arg_ty_snippet}>",)
},
applicability,
);
}
}
}
1 change: 1 addition & 0 deletions tests/ui/crashes/ice-11337.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![feature(trait_alias)]
#![allow(clippy::toilet_closures)]

trait Confusing<F> = Fn(i32) where F: Fn(u32);

Expand Down
3 changes: 2 additions & 1 deletion tests/ui/explicit_auto_deref.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
clippy::too_many_arguments,
clippy::borrow_deref_ref,
clippy::let_unit_value,
clippy::needless_lifetimes
clippy::needless_lifetimes,
clippy::toilet_closures
)]

trait CallableStr {
Expand Down
3 changes: 2 additions & 1 deletion tests/ui/explicit_auto_deref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
clippy::too_many_arguments,
clippy::borrow_deref_ref,
clippy::let_unit_value,
clippy::needless_lifetimes
clippy::needless_lifetimes,
clippy::toilet_closures
)]

trait CallableStr {
Expand Down
Loading

0 comments on commit ed4cb9f

Please sign in to comment.