mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-07 21:25:08 +00:00
[flake8-pyi
] Also remove self
and cls
's annotation (PYI034
) (#14801)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
This commit is contained in:
parent
0e9427255f
commit
aa6b812a73
8 changed files with 785 additions and 73 deletions
|
@ -1,10 +1,11 @@
|
|||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::analyze::typing;
|
||||
use crate::{BindingId, SemanticModel};
|
||||
use ruff_python_ast as ast;
|
||||
use ruff_python_ast::helpers::map_subscript;
|
||||
use ruff_python_ast::name::QualifiedName;
|
||||
use ruff_python_ast::Expr;
|
||||
use ruff_python_ast::{Expr, ExprName, ExprStarred, ExprSubscript, ExprTuple};
|
||||
|
||||
/// Return `true` if any base class matches a [`QualifiedName`] predicate.
|
||||
pub fn any_qualified_base_class(
|
||||
|
@ -129,9 +130,9 @@ pub enum IsMetaclass {
|
|||
Maybe,
|
||||
}
|
||||
|
||||
impl From<IsMetaclass> for bool {
|
||||
fn from(value: IsMetaclass) -> Self {
|
||||
matches!(value, IsMetaclass::Yes)
|
||||
impl IsMetaclass {
|
||||
pub const fn is_yes(self) -> bool {
|
||||
matches!(self, IsMetaclass::Yes)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,3 +171,57 @@ pub fn is_metaclass(class_def: &ast::StmtClassDef, semantic: &SemanticModel) ->
|
|||
(false, _) => IsMetaclass::No,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if a class might generic.
|
||||
///
|
||||
/// A class is considered generic if at least one of its direct bases
|
||||
/// is subscripted with a `TypeVar`-like,
|
||||
/// or if it is defined using PEP 695 syntax.
|
||||
///
|
||||
/// Therefore, a class *might* be generic if it uses PEP-695 syntax
|
||||
/// or at least one of its direct bases is a subscript expression that
|
||||
/// is subscripted with an object that *might* be a `TypeVar`-like.
|
||||
pub fn might_be_generic(class_def: &ast::StmtClassDef, semantic: &SemanticModel) -> bool {
|
||||
if class_def.type_params.is_some() {
|
||||
return true;
|
||||
}
|
||||
|
||||
class_def.bases().iter().any(|base| {
|
||||
let Expr::Subscript(ExprSubscript { slice, .. }) = base else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let Expr::Tuple(ExprTuple { elts, .. }) = slice.as_ref() else {
|
||||
return expr_might_be_typevar_like(slice, semantic);
|
||||
};
|
||||
|
||||
elts.iter()
|
||||
.any(|elt| expr_might_be_typevar_like(elt, semantic))
|
||||
})
|
||||
}
|
||||
|
||||
fn expr_might_be_typevar_like(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||
is_known_typevar(expr, semantic) || expr_might_be_old_style_typevar_like(expr, semantic)
|
||||
}
|
||||
|
||||
fn is_known_typevar(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||
semantic.match_typing_expr(expr, "AnyStr")
|
||||
}
|
||||
|
||||
fn expr_might_be_old_style_typevar_like(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||
match expr {
|
||||
Expr::Attribute(..) => true,
|
||||
Expr::Name(name) => might_be_old_style_typevar_like(name, semantic),
|
||||
Expr::Starred(ExprStarred { value, .. }) => {
|
||||
expr_might_be_old_style_typevar_like(value, semantic)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn might_be_old_style_typevar_like(name: &ExprName, semantic: &SemanticModel) -> bool {
|
||||
let Some(binding) = semantic.only_binding(name).map(|id| semantic.binding(id)) else {
|
||||
return !semantic.has_builtin_binding(&name.id);
|
||||
};
|
||||
typing::is_type_var_like(binding, semantic)
|
||||
}
|
||||
|
|
|
@ -778,6 +778,40 @@ impl TypeChecker for PathlibPathChecker {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct TypeVarLikeChecker;
|
||||
|
||||
impl TypeVarLikeChecker {
|
||||
/// Returns `true` if an [`Expr`] is a `TypeVar`, `TypeVarTuple`, or `ParamSpec` call.
|
||||
///
|
||||
/// See also [`ruff_linter::rules::flake8_pyi::rules::simple_defaults::is_type_var_like_call`].
|
||||
fn is_type_var_like_call(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||
let Expr::Call(ast::ExprCall { func, .. }) = expr else {
|
||||
return false;
|
||||
};
|
||||
let Some(qualified_name) = semantic.resolve_qualified_name(func) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
matches!(
|
||||
qualified_name.segments(),
|
||||
[
|
||||
"typing" | "typing_extensions",
|
||||
"TypeVar" | "TypeVarTuple" | "ParamSpec"
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeChecker for TypeVarLikeChecker {
|
||||
fn match_annotation(_annotation: &Expr, _semantic: &SemanticModel) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn match_initializer(initializer: &Expr, semantic: &SemanticModel) -> bool {
|
||||
Self::is_type_var_like_call(initializer, semantic)
|
||||
}
|
||||
}
|
||||
|
||||
/// Test whether the given binding can be considered a list.
|
||||
///
|
||||
/// For this, we check what value might be associated with it through it's initialization and
|
||||
|
@ -867,6 +901,11 @@ pub fn is_pathlib_path(binding: &Binding, semantic: &SemanticModel) -> bool {
|
|||
check_type::<PathlibPathChecker>(binding, semantic)
|
||||
}
|
||||
|
||||
/// Test whether the given binding is for an old-style `TypeVar`, `TypeVarTuple` or a `ParamSpec`.
|
||||
pub fn is_type_var_like(binding: &Binding, semantic: &SemanticModel) -> bool {
|
||||
check_type::<TypeVarLikeChecker>(binding, semantic)
|
||||
}
|
||||
|
||||
/// Find the [`ParameterWithDefault`] corresponding to the given [`Binding`].
|
||||
#[inline]
|
||||
fn find_parameter<'a>(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue