mirror of https://github.com/astral-sh/ruff
165 lines
4.9 KiB
Rust
165 lines
4.9 KiB
Rust
use itertools::Itertools;
|
|
use rustc_hash::{FxHashMap, FxHashSet};
|
|
use rustpython_ast::{Alias, Arguments, Boolop, Cmpop, Expr, ExprKind, Stmt};
|
|
|
|
use crate::ast::types::{FunctionScope, Range, ScopeKind};
|
|
use crate::autofix::Fix;
|
|
use crate::check_ast::Checker;
|
|
use crate::checks::CheckKind;
|
|
use crate::Check;
|
|
|
|
/// PLC2201
|
|
pub fn misplaced_comparison_constant(
|
|
checker: &mut Checker,
|
|
expr: &Expr,
|
|
left: &Expr,
|
|
ops: &[Cmpop],
|
|
comparators: &[Expr],
|
|
) {
|
|
if let ([op], [right]) = (ops, comparators) {
|
|
if matches!(
|
|
op,
|
|
Cmpop::Eq | Cmpop::NotEq | Cmpop::Lt | Cmpop::LtE | Cmpop::Gt | Cmpop::GtE,
|
|
) && matches!(&left.node, &ExprKind::Constant { .. })
|
|
&& !matches!(&right.node, &ExprKind::Constant { .. })
|
|
{
|
|
let reversed_op = match op {
|
|
Cmpop::Eq => "==",
|
|
Cmpop::NotEq => "!=",
|
|
Cmpop::Lt => ">",
|
|
Cmpop::LtE => ">=",
|
|
Cmpop::Gt => "<",
|
|
Cmpop::GtE => "<=",
|
|
_ => unreachable!("Expected comparison operator"),
|
|
};
|
|
let suggestion = format!("{right} {reversed_op} {left}");
|
|
let mut check = Check::new(
|
|
CheckKind::MisplacedComparisonConstant(suggestion.clone()),
|
|
Range::from_located(expr),
|
|
);
|
|
if checker.patch(check.kind.code()) {
|
|
check.amend(Fix::replacement(
|
|
suggestion,
|
|
expr.location,
|
|
expr.end_location.unwrap(),
|
|
));
|
|
}
|
|
checker.add_check(check);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// PLC3002
|
|
pub fn unnecessary_direct_lambda_call(checker: &mut Checker, expr: &Expr, func: &Expr) {
|
|
if let ExprKind::Lambda { .. } = &func.node {
|
|
checker.add_check(Check::new(
|
|
CheckKind::UnnecessaryDirectLambdaCall,
|
|
Range::from_located(expr),
|
|
));
|
|
}
|
|
}
|
|
|
|
/// PLE1142
|
|
pub fn await_outside_async(checker: &mut Checker, expr: &Expr) {
|
|
if !checker
|
|
.current_scopes()
|
|
.find_map(|scope| {
|
|
if let ScopeKind::Function(FunctionScope { async_, .. }) = &scope.kind {
|
|
Some(*async_)
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.unwrap_or(true)
|
|
{
|
|
checker.add_check(Check::new(
|
|
CheckKind::AwaitOutsideAsync,
|
|
Range::from_located(expr),
|
|
));
|
|
}
|
|
}
|
|
|
|
/// PLR0206
|
|
pub fn property_with_parameters(
|
|
checker: &mut Checker,
|
|
stmt: &Stmt,
|
|
decorator_list: &[Expr],
|
|
args: &Arguments,
|
|
) {
|
|
if decorator_list
|
|
.iter()
|
|
.any(|d| matches!(&d.node, ExprKind::Name { id, .. } if id == "property"))
|
|
{
|
|
if checker.is_builtin("property")
|
|
&& args
|
|
.args
|
|
.iter()
|
|
.chain(args.posonlyargs.iter())
|
|
.chain(args.kwonlyargs.iter())
|
|
.count()
|
|
> 1
|
|
{
|
|
checker.add_check(Check::new(
|
|
CheckKind::PropertyWithParameters,
|
|
Range::from_located(stmt),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// PLR0402
|
|
pub fn consider_using_from_import(checker: &mut Checker, alias: &Alias) {
|
|
if let Some(asname) = &alias.node.asname {
|
|
if let Some((module, name)) = alias.node.name.rsplit_once('.') {
|
|
if name == asname {
|
|
checker.add_check(Check::new(
|
|
CheckKind::ConsiderUsingFromImport(module.to_string(), name.to_string()),
|
|
Range::from_located(alias),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// PLR1701
|
|
pub fn consider_merging_isinstance(
|
|
checker: &mut Checker,
|
|
expr: &Expr,
|
|
op: &Boolop,
|
|
values: &[Expr],
|
|
) {
|
|
if !matches!(op, Boolop::Or) || !checker.is_builtin("isinstance") {
|
|
return;
|
|
}
|
|
|
|
let mut obj_to_types: FxHashMap<String, FxHashSet<String>> = FxHashMap::default();
|
|
for value in values {
|
|
if let ExprKind::Call { func, args, .. } = &value.node {
|
|
if matches!(&func.node, ExprKind::Name { id, .. } if id == "isinstance") {
|
|
if let [obj, types] = &args[..] {
|
|
obj_to_types
|
|
.entry(obj.to_string())
|
|
.or_insert_with(FxHashSet::default)
|
|
.extend(match &types.node {
|
|
ExprKind::Tuple { elts, .. } => {
|
|
elts.iter().map(std::string::ToString::to_string).collect()
|
|
}
|
|
_ => {
|
|
vec![types.to_string()]
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (obj, types) in obj_to_types {
|
|
if types.len() > 1 {
|
|
checker.add_check(Check::new(
|
|
CheckKind::ConsiderMergingIsinstance(obj, types.into_iter().sorted().collect()),
|
|
Range::from_located(expr),
|
|
));
|
|
}
|
|
}
|
|
}
|