mirror of https://github.com/astral-sh/ruff
Remove ExprKind::Call from call path collection (#2666)
This commit is contained in:
parent
124461bddf
commit
286d8c18dd
|
|
@ -72,14 +72,7 @@ fn collect_call_path_inner<'a>(expr: &'a Expr, parts: &mut CallPath<'a>) -> bool
|
|||
/// Convert an `Expr` to its [`CallPath`] segments (like `["typing", "List"]`).
|
||||
pub fn collect_call_path(expr: &Expr) -> CallPath {
|
||||
let mut segments = smallvec![];
|
||||
collect_call_path_inner(
|
||||
if let ExprKind::Call { func, .. } = &expr.node {
|
||||
func
|
||||
} else {
|
||||
expr
|
||||
},
|
||||
&mut segments,
|
||||
);
|
||||
collect_call_path_inner(expr, &mut segments);
|
||||
segments
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2280,7 +2280,7 @@ where
|
|||
|
||||
// pyupgrade
|
||||
if self.settings.rules.enabled(&Rule::TypeOfPrimitive) {
|
||||
pyupgrade::rules::type_of_primitive(self, expr, args);
|
||||
pyupgrade::rules::type_of_primitive(self, expr, func, args);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::DeprecatedUnittestAlias) {
|
||||
pyupgrade::rules::deprecated_unittest_alias(self, func);
|
||||
|
|
@ -2301,10 +2301,10 @@ where
|
|||
pyupgrade::rules::open_alias(self, expr, func);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::ReplaceUniversalNewlines) {
|
||||
pyupgrade::rules::replace_universal_newlines(self, expr, keywords);
|
||||
pyupgrade::rules::replace_universal_newlines(self, func, keywords);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::ReplaceStdoutStderr) {
|
||||
pyupgrade::rules::replace_stdout_stderr(self, expr, keywords);
|
||||
pyupgrade::rules::replace_stdout_stderr(self, expr, func, keywords);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::OSErrorAlias) {
|
||||
pyupgrade::rules::os_error_alias(self, &expr);
|
||||
|
|
@ -2335,7 +2335,7 @@ where
|
|||
.rules
|
||||
.enabled(&Rule::UselessContextlibSuppress)
|
||||
{
|
||||
flake8_bugbear::rules::useless_contextlib_suppress(self, expr, args);
|
||||
flake8_bugbear::rules::useless_contextlib_suppress(self, expr, func, args);
|
||||
}
|
||||
if self
|
||||
.settings
|
||||
|
|
@ -3242,7 +3242,7 @@ where
|
|||
args,
|
||||
keywords,
|
||||
} => {
|
||||
let callable = self.resolve_call_path(expr).and_then(|call_path| {
|
||||
let callable = self.resolve_call_path(func).and_then(|call_path| {
|
||||
if self.match_typing_call_path(&call_path, "ForwardRef") {
|
||||
Some(Callable::ForwardRef)
|
||||
} else if self.match_typing_call_path(&call_path, "cast") {
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ const IMMUTABLE_FUNCS: &[&[&str]] = &[
|
|||
&["re", "compile"],
|
||||
];
|
||||
|
||||
fn is_immutable_func(checker: &Checker, expr: &Expr, extend_immutable_calls: &[CallPath]) -> bool {
|
||||
checker.resolve_call_path(expr).map_or(false, |call_path| {
|
||||
fn is_immutable_func(checker: &Checker, func: &Expr, extend_immutable_calls: &[CallPath]) -> bool {
|
||||
checker.resolve_call_path(func).map_or(false, |call_path| {
|
||||
IMMUTABLE_FUNCS
|
||||
.iter()
|
||||
.any(|target| call_path.as_slice() == *target)
|
||||
|
|
@ -67,7 +67,7 @@ where
|
|||
{
|
||||
self.diagnostics.push((
|
||||
FunctionCallArgumentDefault {
|
||||
name: compose_call_path(expr),
|
||||
name: compose_call_path(func),
|
||||
}
|
||||
.into(),
|
||||
Range::from_located(expr),
|
||||
|
|
|
|||
|
|
@ -66,8 +66,8 @@ const IMMUTABLE_GENERIC_TYPES: &[&[&str]] = &[
|
|||
&["typing", "Tuple"],
|
||||
];
|
||||
|
||||
pub fn is_mutable_func(checker: &Checker, expr: &Expr) -> bool {
|
||||
checker.resolve_call_path(expr).map_or(false, |call_path| {
|
||||
pub fn is_mutable_func(checker: &Checker, func: &Expr) -> bool {
|
||||
checker.resolve_call_path(func).map_or(false, |call_path| {
|
||||
MUTABLE_FUNCS
|
||||
.iter()
|
||||
.any(|target| call_path.as_slice() == *target)
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ impl Violation for UselessContextlibSuppress {
|
|||
}
|
||||
|
||||
/// B005
|
||||
pub fn useless_contextlib_suppress(checker: &mut Checker, expr: &Expr, args: &[Expr]) {
|
||||
pub fn useless_contextlib_suppress(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
|
||||
if args.is_empty()
|
||||
&& checker.resolve_call_path(expr).map_or(false, |call_path| {
|
||||
&& checker.resolve_call_path(func).map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["contextlib", "suppress"]
|
||||
})
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ impl Violation for FailWithoutMessage {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn fail_call(checker: &mut Checker, call: &Expr, args: &[Expr], keywords: &[Keyword]) {
|
||||
if is_pytest_fail(call, checker) {
|
||||
pub fn fail_call(checker: &mut Checker, func: &Expr, args: &[Expr], keywords: &[Keyword]) {
|
||||
if is_pytest_fail(func, checker) {
|
||||
let call_args = SimpleCallArgs::new(args, keywords);
|
||||
let msg = call_args.get_argument("msg", Some(0));
|
||||
|
||||
|
|
@ -27,13 +27,13 @@ pub fn fail_call(checker: &mut Checker, call: &Expr, args: &[Expr], keywords: &[
|
|||
if is_empty_or_null_string(msg) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
FailWithoutMessage,
|
||||
Range::from_located(call),
|
||||
Range::from_located(func),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
FailWithoutMessage,
|
||||
Range::from_located(call),
|
||||
Range::from_located(func),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,15 +6,26 @@ use crate::checkers::ast::Checker;
|
|||
|
||||
const ITERABLE_INITIALIZERS: &[&str] = &["dict", "frozenset", "list", "tuple", "set"];
|
||||
|
||||
pub fn get_mark_decorators(decorators: &[Expr]) -> Vec<&Expr> {
|
||||
/// Given a decorators that can be used with or without explicit call syntax, return
|
||||
/// the underlying callable.
|
||||
fn callable_decorator(decorator: &Expr) -> &Expr {
|
||||
if let ExprKind::Call { func, .. } = &decorator.node {
|
||||
func
|
||||
} else {
|
||||
decorator
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mark_decorators(decorators: &[Expr]) -> impl Iterator<Item = &Expr> {
|
||||
decorators
|
||||
.iter()
|
||||
.filter(|decorator| is_pytest_mark(decorator))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_mark_name(decorator: &Expr) -> &str {
|
||||
collect_call_path(decorator).last().unwrap()
|
||||
collect_call_path(callable_decorator(decorator))
|
||||
.last()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn is_pytest_fail(call: &Expr, checker: &Checker) -> bool {
|
||||
|
|
@ -25,14 +36,18 @@ pub fn is_pytest_fail(call: &Expr, checker: &Checker) -> bool {
|
|||
|
||||
pub fn is_pytest_fixture(decorator: &Expr, checker: &Checker) -> bool {
|
||||
checker
|
||||
.resolve_call_path(decorator)
|
||||
.resolve_call_path(if let ExprKind::Call { func, .. } = &decorator.node {
|
||||
func
|
||||
} else {
|
||||
decorator
|
||||
})
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["pytest", "fixture"]
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_pytest_mark(decorator: &Expr) -> bool {
|
||||
let segments = collect_call_path(decorator);
|
||||
let segments = collect_call_path(callable_decorator(decorator));
|
||||
if segments.len() > 2 {
|
||||
segments[0] == "pytest" && segments[1] == "mark"
|
||||
} else {
|
||||
|
|
@ -42,7 +57,7 @@ pub fn is_pytest_mark(decorator: &Expr) -> bool {
|
|||
|
||||
pub fn is_pytest_yield_fixture(decorator: &Expr, checker: &Checker) -> bool {
|
||||
checker
|
||||
.resolve_call_path(decorator)
|
||||
.resolve_call_path(callable_decorator(decorator))
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["pytest", "yield_fixture"]
|
||||
})
|
||||
|
|
@ -100,7 +115,7 @@ pub fn is_falsy_constant(expr: &Expr) -> bool {
|
|||
|
||||
pub fn is_pytest_parametrize(decorator: &Expr, checker: &Checker) -> bool {
|
||||
checker
|
||||
.resolve_call_path(decorator)
|
||||
.resolve_call_path(callable_decorator(decorator))
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["pytest", "mark", "parametrize"]
|
||||
})
|
||||
|
|
|
|||
|
|
@ -35,14 +35,8 @@ pub fn use_capital_environment_variables(checker: &mut Checker, expr: &Expr) {
|
|||
return;
|
||||
}
|
||||
|
||||
// check `os.environ.get('foo')` and `os.getenv('foo')``
|
||||
if !checker.resolve_call_path(expr).map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["os", "environ", "get"] || call_path.as_slice() == ["os", "getenv"]
|
||||
}) {
|
||||
return;
|
||||
}
|
||||
|
||||
let ExprKind::Call { args, .. } = &expr.node else {
|
||||
// check `os.environ.get('foo')` and `os.getenv('foo')`.
|
||||
let ExprKind::Call { func, args, .. } = &expr.node else {
|
||||
return;
|
||||
};
|
||||
let Some(arg) = args.get(0) else {
|
||||
|
|
@ -51,6 +45,12 @@ pub fn use_capital_environment_variables(checker: &mut Checker, expr: &Expr) {
|
|||
let ExprKind::Constant { value: Constant::Str(env_var), kind } = &arg.node else {
|
||||
return;
|
||||
};
|
||||
if !checker.resolve_call_path(func).map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["os", "environ", "get"] || call_path.as_slice() == ["os", "getenv"]
|
||||
}) {
|
||||
return;
|
||||
}
|
||||
|
||||
let capital_env_var = env_var.to_ascii_uppercase();
|
||||
if &capital_env_var == env_var {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use itertools::Itertools;
|
||||
use ruff_python::string::{is_lower, is_upper};
|
||||
use rustpython_parser::ast::{Stmt, StmtKind};
|
||||
use rustpython_parser::ast::{ExprKind, Stmt, StmtKind};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
|
|
@ -26,7 +26,10 @@ pub fn is_namedtuple_assignment(checker: &Checker, stmt: &Stmt) -> bool {
|
|||
let StmtKind::Assign { value, .. } = &stmt.node else {
|
||||
return false;
|
||||
};
|
||||
checker.resolve_call_path(value).map_or(false, |call_path| {
|
||||
let ExprKind::Call {func, ..} = &value.node else {
|
||||
return false;
|
||||
};
|
||||
checker.resolve_call_path(func).map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["collections", "namedtuple"]
|
||||
|| call_path.as_slice() == ["typing", "NamedTuple"]
|
||||
})
|
||||
|
|
@ -36,7 +39,10 @@ pub fn is_typeddict_assignment(checker: &Checker, stmt: &Stmt) -> bool {
|
|||
let StmtKind::Assign { value, .. } = &stmt.node else {
|
||||
return false;
|
||||
};
|
||||
checker.resolve_call_path(value).map_or(false, |call_path| {
|
||||
let ExprKind::Call {func, ..} = &value.node else {
|
||||
return false;
|
||||
};
|
||||
checker.resolve_call_path(func).map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["typing", "TypedDict"]
|
||||
})
|
||||
}
|
||||
|
|
@ -45,7 +51,10 @@ pub fn is_type_var_assignment(checker: &Checker, stmt: &Stmt) -> bool {
|
|||
let StmtKind::Assign { value, .. } = &stmt.node else {
|
||||
return false;
|
||||
};
|
||||
checker.resolve_call_path(value).map_or(false, |call_path| {
|
||||
let ExprKind::Call {func, ..} = &value.node else {
|
||||
return false;
|
||||
};
|
||||
checker.resolve_call_path(func).map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["typing", "TypeVar"]
|
||||
|| call_path.as_slice() == ["typing", "NewType"]
|
||||
})
|
||||
|
|
|
|||
|
|
@ -104,8 +104,8 @@ fn generate_fix(
|
|||
}
|
||||
|
||||
/// UP022
|
||||
pub fn replace_stdout_stderr(checker: &mut Checker, expr: &Expr, kwargs: &[Keyword]) {
|
||||
if checker.resolve_call_path(expr).map_or(false, |call_path| {
|
||||
pub fn replace_stdout_stderr(checker: &mut Checker, expr: &Expr, func: &Expr, kwargs: &[Keyword]) {
|
||||
if checker.resolve_call_path(func).map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["subprocess", "run"]
|
||||
}) {
|
||||
// Find `stdout` and `stderr` kwargs.
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ impl AlwaysAutofixableViolation for ReplaceUniversalNewlines {
|
|||
}
|
||||
|
||||
/// UP021
|
||||
pub fn replace_universal_newlines(checker: &mut Checker, expr: &Expr, kwargs: &[Keyword]) {
|
||||
if checker.resolve_call_path(expr).map_or(false, |call_path| {
|
||||
pub fn replace_universal_newlines(checker: &mut Checker, func: &Expr, kwargs: &[Keyword]) {
|
||||
if checker.resolve_call_path(func).map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["subprocess", "run"]
|
||||
}) {
|
||||
let Some(kwarg) = find_keyword(kwargs, "universal_newlines") else { return; };
|
||||
|
|
|
|||
|
|
@ -27,12 +27,12 @@ impl AlwaysAutofixableViolation for TypeOfPrimitive {
|
|||
}
|
||||
|
||||
/// UP003
|
||||
pub fn type_of_primitive(checker: &mut Checker, expr: &Expr, args: &[Expr]) {
|
||||
pub fn type_of_primitive(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
|
||||
if args.len() != 1 {
|
||||
return;
|
||||
}
|
||||
if !checker
|
||||
.resolve_call_path(expr)
|
||||
.resolve_call_path(func)
|
||||
.map_or(false, |call_path| call_path.as_slice() == ["", "type"])
|
||||
{
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ fn check_type_check_test(checker: &mut Checker, test: &Expr) -> bool {
|
|||
.iter()
|
||||
.all(|expr| check_type_check_test(checker, expr)),
|
||||
ExprKind::UnaryOp { operand, .. } => check_type_check_test(checker, operand),
|
||||
ExprKind::Call { .. } => check_type_check_call(checker, test),
|
||||
ExprKind::Call { func, .. } => check_type_check_call(checker, func),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use ruff_macros::{define_violation, derive_message_formats};
|
||||
use rustpython_parser::ast::Expr;
|
||||
use rustpython_parser::ast::{Expr, ExprKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
|
|
@ -59,7 +59,11 @@ impl Violation for RaiseVanillaClass {
|
|||
/// TRY002
|
||||
pub fn raise_vanilla_class(checker: &mut Checker, expr: &Expr) {
|
||||
if checker
|
||||
.resolve_call_path(expr)
|
||||
.resolve_call_path(if let ExprKind::Call { func, .. } = &expr.node {
|
||||
func
|
||||
} else {
|
||||
expr
|
||||
})
|
||||
.map_or(false, |call_path| call_path.as_slice() == ["", "Exception"])
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
|
|
|
|||
Loading…
Reference in New Issue