sim300 constant likelihood levels

This commit is contained in:
asafamr-mm 2023-12-16 21:01:22 +02:00
parent 5aaf99b856
commit 760ff26c06
3 changed files with 123 additions and 61 deletions

View File

@ -13,9 +13,10 @@ YODA > age # SIM300
YODA >= age # SIM300
JediOrder.YODA == age # SIM300
0 < (number - 100) # SIM300
SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
B<A[0][0]or B
B or(B)<A[0][0]
['upper'] == UPPER_LIST
{} == DummyHandler.CONFIG
# OK
compare == "yoda"
@ -31,3 +32,8 @@ age <= YODA
YODA == YODA
age == JediOrder.YODA
(number - 100) > 0
UPPER_LIST == ['upper']
DummyHandler.CONFIG == {}
{"thats": "acceptable"} == DummyHandler.CONFIG
SECONDS_IN_DAY == 60 * 60 * 24
SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60)

View File

@ -1,3 +1,5 @@
use std::cmp;
use anyhow::Result;
use libcst_native::CompOp;
@ -78,18 +80,51 @@ impl Violation for YodaConditions {
}
}
/// Return `true` if an [`Expr`] is a constant or a constant-like name.
fn is_constant_like(expr: &Expr) -> bool {
/// Comparisons left hand side must not be more [`ConstantLikelihood`] than their right hand side
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
enum ConstantLikelihood {
Unlikely = 0,
Probably = 1, // CAMEL_CASED vars
Definitely = 2, // literals, empty dicts and tuples...
}
fn how_likely_constant_str(s: &str) -> ConstantLikelihood {
if str::is_cased_uppercase(s) {
ConstantLikelihood::Probably
} else {
ConstantLikelihood::Unlikely
}
}
/// Return [`Expr`] [`ConstantLikelihood`] level depending on simple heuristics.
fn how_likely_constant(expr: &Expr) -> ConstantLikelihood {
if expr.is_literal_expr() {
return ConstantLikelihood::Definitely;
}
match expr {
Expr::Attribute(ast::ExprAttribute { attr, .. }) => str::is_cased_uppercase(attr),
Expr::Tuple(ast::ExprTuple { elts, .. }) => elts.iter().all(is_constant_like),
Expr::Name(ast::ExprName { id, .. }) => str::is_cased_uppercase(id),
Expr::Attribute(ast::ExprAttribute { attr, .. }) => how_likely_constant_str(attr),
Expr::Name(ast::ExprName { id, .. }) => how_likely_constant_str(id),
Expr::Tuple(ast::ExprTuple { elts, .. }) | Expr::List(ast::ExprList { elts, .. }) => elts
.iter()
.map(how_likely_constant)
.min()
.unwrap_or(ConstantLikelihood::Definitely),
Expr::Dict(ast::ExprDict { values: vs, .. }) => {
if vs.is_empty() {
ConstantLikelihood::Definitely
} else {
ConstantLikelihood::Probably
}
}
Expr::BinOp(ast::ExprBinOp { left, right, .. }) => {
cmp::min(how_likely_constant(left), how_likely_constant(right))
}
Expr::UnaryOp(ast::ExprUnaryOp {
op: UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert,
operand,
range: _,
}) => operand.is_literal_expr(),
_ => expr.is_literal_expr(),
}) => how_likely_constant(operand),
_ => ConstantLikelihood::Unlikely,
}
}
@ -180,7 +215,7 @@ pub(crate) fn yoda_conditions(
return;
}
if !is_constant_like(left) || is_constant_like(right) {
if how_likely_constant(left) <= how_likely_constant(right) {
return;
}

View File

@ -247,7 +247,7 @@ SIM300.py:13:1: SIM300 [*] Yoda conditions are discouraged, use `age <= YODA` in
13 |+age <= YODA # SIM300
14 14 | JediOrder.YODA == age # SIM300
15 15 | 0 < (number - 100) # SIM300
16 16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
16 16 | B<A[0][0]or B
SIM300.py:14:1: SIM300 [*] Yoda conditions are discouraged, use `age == JediOrder.YODA` instead
|
@ -256,7 +256,7 @@ SIM300.py:14:1: SIM300 [*] Yoda conditions are discouraged, use `age == JediOrde
14 | JediOrder.YODA == age # SIM300
| ^^^^^^^^^^^^^^^^^^^^^ SIM300
15 | 0 < (number - 100) # SIM300
16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
16 | B<A[0][0]or B
|
= help: Replace Yoda condition with `age == JediOrder.YODA`
@ -267,8 +267,8 @@ SIM300.py:14:1: SIM300 [*] Yoda conditions are discouraged, use `age == JediOrde
14 |-JediOrder.YODA == age # SIM300
14 |+age == JediOrder.YODA # SIM300
15 15 | 0 < (number - 100) # SIM300
16 16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
17 17 | B<A[0][0]or B
16 16 | B<A[0][0]or B
17 17 | B or(B)<A[0][0]
SIM300.py:15:1: SIM300 [*] Yoda conditions are discouraged, use `(number - 100) > 0` instead
|
@ -276,8 +276,8 @@ SIM300.py:15:1: SIM300 [*] Yoda conditions are discouraged, use `(number - 100)
14 | JediOrder.YODA == age # SIM300
15 | 0 < (number - 100) # SIM300
| ^^^^^^^^^^^^^^^^^^ SIM300
16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
17 | B<A[0][0]or B
16 | B<A[0][0]or B
17 | B or(B)<A[0][0]
|
= help: Replace Yoda condition with `(number - 100) > 0`
@ -287,70 +287,91 @@ SIM300.py:15:1: SIM300 [*] Yoda conditions are discouraged, use `(number - 100)
14 14 | JediOrder.YODA == age # SIM300
15 |-0 < (number - 100) # SIM300
15 |+(number - 100) > 0 # SIM300
16 16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
17 17 | B<A[0][0]or B
18 18 | B or(B)<A[0][0]
16 16 | B<A[0][0]or B
17 17 | B or(B)<A[0][0]
18 18 | ['upper'] == UPPER_LIST
SIM300.py:16:1: SIM300 [*] Yoda conditions are discouraged
SIM300.py:16:1: SIM300 [*] Yoda conditions are discouraged, use `A[0][0] > B` instead
|
14 | JediOrder.YODA == age # SIM300
15 | 0 < (number - 100) # SIM300
16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM300
17 | B<A[0][0]or B
18 | B or(B)<A[0][0]
16 | B<A[0][0]or B
| ^^^^^^^^^ SIM300
17 | B or(B)<A[0][0]
18 | ['upper'] == UPPER_LIST
|
= help: Replace Yoda condition
= help: Replace Yoda condition with `A[0][0] > B`
Safe fix
13 13 | YODA >= age # SIM300
14 14 | JediOrder.YODA == age # SIM300
15 15 | 0 < (number - 100) # SIM300
16 |-SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
16 |+(60 * 60) < SomeClass().settings.SOME_CONSTANT_VALUE # SIM300
17 17 | B<A[0][0]or B
18 18 | B or(B)<A[0][0]
19 19 |
16 |-B<A[0][0]or B
16 |+A[0][0] > B or B
17 17 | B or(B)<A[0][0]
18 18 | ['upper'] == UPPER_LIST
19 19 | {} == DummyHandler.CONFIG
SIM300.py:17:1: SIM300 [*] Yoda conditions are discouraged, use `A[0][0] > B` instead
SIM300.py:17:5: SIM300 [*] Yoda conditions are discouraged, use `A[0][0] > (B)` instead
|
15 | 0 < (number - 100) # SIM300
16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
17 | B<A[0][0]or B
| ^^^^^^^^^ SIM300
18 | B or(B)<A[0][0]
|
= help: Replace Yoda condition with `A[0][0] > B`
Safe fix
14 14 | JediOrder.YODA == age # SIM300
15 15 | 0 < (number - 100) # SIM300
16 16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
17 |-B<A[0][0]or B
17 |+A[0][0] > B or B
18 18 | B or(B)<A[0][0]
19 19 |
20 20 | # OK
SIM300.py:18:5: SIM300 [*] Yoda conditions are discouraged, use `A[0][0] > (B)` instead
|
16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
17 | B<A[0][0]or B
18 | B or(B)<A[0][0]
16 | B<A[0][0]or B
17 | B or(B)<A[0][0]
| ^^^^^^^^^^^ SIM300
19 |
20 | # OK
18 | ['upper'] == UPPER_LIST
19 | {} == DummyHandler.CONFIG
|
= help: Replace Yoda condition with `A[0][0] > (B)`
Safe fix
14 14 | JediOrder.YODA == age # SIM300
15 15 | 0 < (number - 100) # SIM300
16 16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
17 17 | B<A[0][0]or B
18 |-B or(B)<A[0][0]
18 |+B or A[0][0] > (B)
19 19 |
20 20 | # OK
21 21 | compare == "yoda"
16 16 | B<A[0][0]or B
17 |-B or(B)<A[0][0]
17 |+B or A[0][0] > (B)
18 18 | ['upper'] == UPPER_LIST
19 19 | {} == DummyHandler.CONFIG
20 20 |
SIM300.py:18:1: SIM300 [*] Yoda conditions are discouraged, use `UPPER_LIST == ['upper']` instead
|
16 | B<A[0][0]or B
17 | B or(B)<A[0][0]
18 | ['upper'] == UPPER_LIST
| ^^^^^^^^^^^^^^^^^^^^^^^ SIM300
19 | {} == DummyHandler.CONFIG
|
= help: Replace Yoda condition with `UPPER_LIST == ['upper']`
Safe fix
15 15 | 0 < (number - 100) # SIM300
16 16 | B<A[0][0]or B
17 17 | B or(B)<A[0][0]
18 |-['upper'] == UPPER_LIST
18 |+UPPER_LIST == ['upper']
19 19 | {} == DummyHandler.CONFIG
20 20 |
21 21 | # OK
SIM300.py:19:1: SIM300 [*] Yoda conditions are discouraged, use `DummyHandler.CONFIG == {}` instead
|
17 | B or(B)<A[0][0]
18 | ['upper'] == UPPER_LIST
19 | {} == DummyHandler.CONFIG
| ^^^^^^^^^^^^^^^^^^^^^^^^^ SIM300
20 |
21 | # OK
|
= help: Replace Yoda condition with `DummyHandler.CONFIG == {}`
Safe fix
16 16 | B<A[0][0]or B
17 17 | B or(B)<A[0][0]
18 18 | ['upper'] == UPPER_LIST
19 |-{} == DummyHandler.CONFIG
19 |+DummyHandler.CONFIG == {}
20 20 |
21 21 | # OK
22 22 | compare == "yoda"