1 use clippy_utils::higher::If;
2 use rustc_ast::LitKind;
3 use rustc_hir::{Block, ExprKind};
4 use rustc_lint::{LateContext, LateLintPass};
5 use rustc_session::{declare_lint_pass, declare_tool_lint};
7 use clippy_utils::{diagnostics::span_lint_and_then, in_constant, is_else_clause, is_integer_literal, sugg::Sugg};
8 use rustc_errors::Applicability;
10 declare_clippy_lint! {
12 /// Instead of using an if statement to convert a bool to an int,
13 /// this lint suggests using a `from()` function or an `as` coercion.
15 /// ### Why is this bad?
16 /// Coercion or `from()` is another way to convert bool to a number.
17 /// Both methods are guaranteed to return 1 for true, and 0 for false.
19 /// See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E
23 /// # let condition = false;
32 /// # let condition = false;
33 /// i64::from(condition);
37 /// # let condition = false;
40 #[clippy::version = "1.65.0"]
41 pub BOOL_TO_INT_WITH_IF,
43 "using if to convert bool to int"
45 declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]);
47 impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf {
48 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
49 if !expr.span.from_expansion() && !in_constant(cx, expr.hir_id) {
50 check_if_else(cx, expr);
55 fn check_if_else<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
56 if let Some(If { cond, then, r#else: Some(r#else) }) = If::hir(expr)
57 && let Some(then_lit) = int_literal(then)
58 && let Some(else_lit) = int_literal(r#else)
60 let inverted = if is_integer_literal(then_lit, 1) && is_integer_literal(else_lit, 0) {
62 } else if is_integer_literal(then_lit, 0) && is_integer_literal(else_lit, 1) {
65 // Expression isn't boolean, exit
68 let mut applicability = Applicability::MachineApplicable;
70 let mut sugg = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability);
77 let ty = cx.typeck_results().expr_ty(then_lit); // then and else must be of same type
80 let wrap_in_curly = is_else_clause(cx.tcx, expr);
81 let mut s = Sugg::NonParen(format!("{ty}::from({snippet})").into());
86 }; // when used in else clause if statement should be wrapped in curly braces
88 let into_snippet = snippet.clone().maybe_par();
89 let as_snippet = snippet.as_ty(ty);
91 span_lint_and_then(cx,
94 "boolean to int conversion using if",
102 diag.note(format!("`{as_snippet}` or `{into_snippet}.into()` can also be valid options"));
107 // If block contains only a int literal expression, return literal expression
108 fn int_literal<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>) -> Option<&'tcx rustc_hir::Expr<'tcx>> {
109 if let ExprKind::Block(block, _) = expr.kind
111 stmts: [], // Shouldn't lint if statements with side effects
115 && let ExprKind::Lit(lit) = &expr.kind
116 && let LitKind::Int(_, _) = lit.node