-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Add lint [bool_to_int_with_if]
#9086
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
7444dca
initialize new lint
243e671
it detects errors
a925d91
fix unused variable
033e8f1
fromat and add some tests
d3b0535
Add lint
5548723
remove ?unnecessary? reference
63a5799
add type awareness test
d46ecd3
another type test
3b52d8e
remove coercion suggestion
3dec8dc
add run-rustfix
595e2ea
cargo dev bless
fe0172f
fix tests
cc045ab
fix style
0c2e07c
implement as version
74a65cd
Add note
e905ec2
cargo dev bless
ecf9a76
fix tests
ff3cda1
rollback to suggesting from
d55afc0
fix dogfood
1d5aa04
cargo dev bless
894363e
add lint description
75c93fd
fix doctest?
26468a8
restore author\struct test
c599291
Update clippy_lints/src/bool_to_int_with_if.rs
jst-r e3519e0
if_chain with curly braces
70f8a07
add tests
3bf093d
Merge branch 'master' of https://github.com/jst-r/rust-clippy
d857ce3
if chains and format
ddc16e2
cleaner code snippets
a673d6c
update description
85b9c05
handle possibly missing snippet
5cda293
handle else if
41929d1
update tests
daa005b
Update clippy_lints/src/bool_to_int_with_if.rs
jst-r f139c59
Update clippy_lints/src/bool_to_int_with_if.rs
jst-r c1961d3
formatting
267fc35
Merge branch 'master' of https://github.com/jst-r/rust-clippy
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
use rustc_ast::{ExprPrecedence, LitKind}; | ||
use rustc_hir::{Block, ExprKind}; | ||
use rustc_lint::{LateContext, LateLintPass}; | ||
use rustc_session::{declare_lint_pass, declare_tool_lint}; | ||
|
||
use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, source::snippet_block_with_applicability}; | ||
use rustc_errors::Applicability; | ||
|
||
declare_clippy_lint! { | ||
/// ### What it does | ||
/// Instead of using an if statement to convert a bool to an int, | ||
/// this lint suggests using a `from()` function or an `as` coercion. | ||
/// | ||
/// ### Why is this bad? | ||
/// Coercion or `from()` is idiomatic way to convert bool to a number. | ||
/// Both methods are guaranteed to return 1 for true, and 0 for false. | ||
/// | ||
/// See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E | ||
/// | ||
/// ### Example | ||
/// ```rust | ||
/// # let condition = false; | ||
/// if condition { | ||
/// 1_i64 | ||
/// } else { | ||
/// 0 | ||
/// }; | ||
/// ``` | ||
/// Use instead: | ||
/// ```rust | ||
/// # let condition = false; | ||
/// i64::from(condition); | ||
/// ``` | ||
/// or | ||
/// ```rust | ||
/// # let condition = false; | ||
/// condition as i64; | ||
/// ``` | ||
#[clippy::version = "1.65.0"] | ||
pub BOOL_TO_INT_WITH_IF, | ||
style, | ||
"using if to convert bool to int" | ||
} | ||
declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]); | ||
|
||
impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { | ||
fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { | ||
if !expr.span.from_expansion() { | ||
check_if_else(ctx, expr); | ||
} | ||
} | ||
} | ||
|
||
fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { | ||
jst-r marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if let ExprKind::If(check, then, Some(else_)) = expr.kind | ||
&& let Some(then_lit) = int_literal(then) | ||
&& let Some(else_lit) = int_literal(else_) | ||
&& check_int_literal_equals_val(then_lit, 1) | ||
&& check_int_literal_equals_val(else_lit, 0) | ||
{ | ||
let mut applicability = Applicability::MachineApplicable; | ||
let snippet = snippet_block_with_applicability(ctx, check.span, "..", None, &mut applicability); | ||
let snippet_with_braces = { | ||
let need_parens = should_have_parentheses(check); | ||
let (left_paren, right_paren) = if need_parens {("(", ")")} else {("", "")}; | ||
format!("{left_paren}{snippet}{right_paren}") | ||
}; | ||
|
||
let ty = ctx.typeck_results().expr_ty(then_lit); // then and else must be of same type | ||
|
||
let suggestion = { | ||
let wrap_in_curly = is_else_clause(ctx.tcx, expr); | ||
let (left_curly, right_curly) = if wrap_in_curly {("{", "}")} else {("", "")}; | ||
format!( | ||
"{left_curly}{ty}::from({snippet}){right_curly}" | ||
) | ||
}; // when used in else clause if statement should be wrapped in curly braces | ||
|
||
span_lint_and_then(ctx, | ||
BOOL_TO_INT_WITH_IF, | ||
expr.span, | ||
"boolean to int conversion using if", | ||
|diag| { | ||
diag.span_suggestion( | ||
expr.span, | ||
"replace with from", | ||
suggestion, | ||
applicability, | ||
); | ||
diag.note(format!("`{snippet_with_braces} as {ty}` or `{snippet_with_braces}.into()` can also be valid options")); | ||
}); | ||
}; | ||
} | ||
|
||
// If block contains only a int literal expression, return literal expression | ||
fn int_literal<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>) -> Option<&'tcx rustc_hir::Expr<'tcx>> { | ||
if let ExprKind::Block(block, _) = expr.kind | ||
&& let Block { | ||
stmts: [], // Shouldn't lint if statements with side effects | ||
expr: Some(expr), | ||
.. | ||
} = block | ||
&& let ExprKind::Lit(lit) = &expr.kind | ||
&& let LitKind::Int(_, _) = lit.node | ||
{ | ||
Some(expr) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
fn check_int_literal_equals_val<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>, expected_value: u128) -> bool { | ||
if let ExprKind::Lit(lit) = &expr.kind | ||
&& let LitKind::Int(val, _) = lit.node | ||
&& val == expected_value | ||
{ | ||
true | ||
} else { | ||
false | ||
} | ||
} | ||
|
||
fn should_have_parentheses<'tcx>(check: &'tcx rustc_hir::Expr<'tcx>) -> bool { | ||
check.precedence().order() < ExprPrecedence::Cast.order() | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// run-rustfix | ||
|
||
#![warn(clippy::bool_to_int_with_if)] | ||
#![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)] | ||
|
||
fn main() { | ||
let a = true; | ||
let b = false; | ||
|
||
let x = 1; | ||
let y = 2; | ||
|
||
// Should lint | ||
// precedence | ||
i32::from(a); | ||
i32::from(!a); | ||
i32::from(a || b); | ||
i32::from(cond(a, b)); | ||
i32::from(x + y < 4); | ||
|
||
// if else if | ||
if a { | ||
123 | ||
} else {i32::from(b)}; | ||
|
||
// Shouldn't lint | ||
|
||
if a { | ||
1 | ||
} else if b { | ||
0 | ||
} else { | ||
3 | ||
}; | ||
|
||
if a { | ||
3 | ||
} else if b { | ||
1 | ||
} else { | ||
-2 | ||
}; | ||
|
||
if a { | ||
3 | ||
} else { | ||
0 | ||
}; | ||
if a { | ||
side_effect(); | ||
1 | ||
} else { | ||
0 | ||
}; | ||
if a { | ||
1 | ||
} else { | ||
side_effect(); | ||
0 | ||
}; | ||
|
||
// multiple else ifs | ||
if a { | ||
123 | ||
} else if b { | ||
1 | ||
} else if a | b { | ||
0 | ||
} else { | ||
123 | ||
}; | ||
|
||
some_fn(a); | ||
} | ||
|
||
// Lint returns and type inference | ||
fn some_fn(a: bool) -> u8 { | ||
u8::from(a) | ||
} | ||
|
||
fn side_effect() {} | ||
|
||
fn cond(a: bool, b: bool) -> bool { | ||
a || b | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.