Add box to stmt
This commit is contained in:
@@ -7,10 +7,7 @@ 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::{
|
||||
ExceptHandler, Expr, ExprName, ExprStarred, ExprSubscript, ExprTuple, Stmt, StmtFor, StmtIf,
|
||||
StmtMatch, StmtTry, StmtWhile, StmtWith,
|
||||
};
|
||||
use ruff_python_ast::{ExceptHandler, Expr, ExprName, ExprStarred, ExprSubscript, ExprTuple, Stmt};
|
||||
|
||||
/// Return `true` if any base class matches a [`QualifiedName`] predicate.
|
||||
pub fn any_qualified_base_class(
|
||||
@@ -183,18 +180,17 @@ pub fn any_member_declaration(
|
||||
Stmt::FunctionDef(function_def) => Some(ClassMemberKind::FunctionDef(function_def)),
|
||||
Stmt::Assign(assign) => Some(ClassMemberKind::Assign(assign)),
|
||||
Stmt::AnnAssign(assign) => Some(ClassMemberKind::AnnAssign(assign)),
|
||||
Stmt::With(StmtWith { body, .. }) => {
|
||||
if any_stmt_in_body(body, func, ClassMemberBoundness::PossiblyUnbound) {
|
||||
Stmt::With(node) => {
|
||||
if any_stmt_in_body(&node.body, func, ClassMemberBoundness::PossiblyUnbound) {
|
||||
return true;
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
Stmt::For(StmtFor { body, orelse, .. })
|
||||
| Stmt::While(StmtWhile { body, orelse, .. }) => {
|
||||
if any_stmt_in_body(body, func, ClassMemberBoundness::PossiblyUnbound)
|
||||
|| any_stmt_in_body(orelse, func, ClassMemberBoundness::PossiblyUnbound)
|
||||
Stmt::For(node) => {
|
||||
if any_stmt_in_body(&node.body, func, ClassMemberBoundness::PossiblyUnbound)
|
||||
|| any_stmt_in_body(&node.orelse, func, ClassMemberBoundness::PossiblyUnbound)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -202,13 +198,19 @@ pub fn any_member_declaration(
|
||||
None
|
||||
}
|
||||
|
||||
Stmt::If(StmtIf {
|
||||
body,
|
||||
elif_else_clauses,
|
||||
..
|
||||
}) => {
|
||||
if any_stmt_in_body(body, func, ClassMemberBoundness::PossiblyUnbound)
|
||||
|| elif_else_clauses.iter().any(|it| {
|
||||
Stmt::While(node) => {
|
||||
if any_stmt_in_body(&node.body, func, ClassMemberBoundness::PossiblyUnbound)
|
||||
|| any_stmt_in_body(&node.orelse, func, ClassMemberBoundness::PossiblyUnbound)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
Stmt::If(node) => {
|
||||
if any_stmt_in_body(&node.body, func, ClassMemberBoundness::PossiblyUnbound)
|
||||
|| node.elif_else_clauses.iter().any(|it| {
|
||||
any_stmt_in_body(&it.body, func, ClassMemberBoundness::PossiblyUnbound)
|
||||
})
|
||||
{
|
||||
@@ -217,8 +219,8 @@ pub fn any_member_declaration(
|
||||
None
|
||||
}
|
||||
|
||||
Stmt::Match(StmtMatch { cases, .. }) => {
|
||||
if cases.iter().any(|it| {
|
||||
Stmt::Match(node) => {
|
||||
if node.cases.iter().any(|it| {
|
||||
any_stmt_in_body(&it.body, func, ClassMemberBoundness::PossiblyUnbound)
|
||||
}) {
|
||||
return true;
|
||||
@@ -227,17 +229,11 @@ pub fn any_member_declaration(
|
||||
None
|
||||
}
|
||||
|
||||
Stmt::Try(StmtTry {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
..
|
||||
}) => {
|
||||
if any_stmt_in_body(body, func, ClassMemberBoundness::PossiblyUnbound)
|
||||
|| any_stmt_in_body(orelse, func, ClassMemberBoundness::PossiblyUnbound)
|
||||
|| any_stmt_in_body(finalbody, func, ClassMemberBoundness::PossiblyUnbound)
|
||||
|| handlers.iter().any(|ExceptHandler::ExceptHandler(it)| {
|
||||
Stmt::Try(node) => {
|
||||
if any_stmt_in_body(&node.body, func, ClassMemberBoundness::PossiblyUnbound)
|
||||
|| any_stmt_in_body(&node.orelse, func, ClassMemberBoundness::PossiblyUnbound)
|
||||
|| any_stmt_in_body(&node.finalbody, func, ClassMemberBoundness::PossiblyUnbound)
|
||||
|| node.handlers.iter().any(|ExceptHandler::ExceptHandler(it)| {
|
||||
any_stmt_in_body(&it.body, func, ClassMemberBoundness::PossiblyUnbound)
|
||||
})
|
||||
{
|
||||
@@ -336,12 +332,10 @@ impl IsMetaclass {
|
||||
fn has_metaclass_new_signature(class_def: &ast::StmtClassDef, semantic: &SemanticModel) -> bool {
|
||||
// Look for a __new__ method in the class body
|
||||
for stmt in &class_def.body {
|
||||
let ast::Stmt::FunctionDef(ast::StmtFunctionDef {
|
||||
name, parameters, ..
|
||||
}) = stmt
|
||||
else {
|
||||
let ast::Stmt::FunctionDef(func_def) = stmt else {
|
||||
continue;
|
||||
};
|
||||
let (name, parameters) = (&func_def.name, &func_def.parameters);
|
||||
|
||||
if name != "__new__" {
|
||||
continue;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use ruff_python_ast::helpers::map_callable;
|
||||
use ruff_python_ast::name::{QualifiedName, UnqualifiedName};
|
||||
use ruff_python_ast::{Decorator, Expr, Stmt, StmtExpr, StmtFunctionDef, StmtRaise};
|
||||
use ruff_python_ast::{Decorator, Expr, Stmt, StmtExpr, StmtFunctionDef};
|
||||
|
||||
use crate::model::SemanticModel;
|
||||
use crate::scope::Scope;
|
||||
@@ -146,12 +146,7 @@ pub fn is_stub(function_def: &StmtFunctionDef, semantic: &SemanticModel) -> bool
|
||||
Expr::StringLiteral(_) | Expr::EllipsisLiteral(_)
|
||||
)
|
||||
}
|
||||
Stmt::Raise(StmtRaise {
|
||||
range: _,
|
||||
node_index: _,
|
||||
exc: exception,
|
||||
cause: _,
|
||||
}) => exception.as_ref().is_some_and(|exc| {
|
||||
Stmt::Raise(raise_stmt) => raise_stmt.exc.as_ref().is_some_and(|exc| {
|
||||
semantic
|
||||
.resolve_builtin_symbol(map_callable(exc))
|
||||
.is_some_and(|name| matches!(name, "NotImplementedError" | "NotImplemented"))
|
||||
|
||||
@@ -12,11 +12,7 @@ use crate::SemanticModel;
|
||||
/// ```
|
||||
pub fn is_sys_path_modification(stmt: &Stmt, semantic: &SemanticModel) -> bool {
|
||||
match stmt {
|
||||
Stmt::Expr(ast::StmtExpr {
|
||||
value,
|
||||
range: _,
|
||||
node_index: _,
|
||||
}) => match value.as_ref() {
|
||||
Stmt::Expr(node) => match node.value.as_ref() {
|
||||
Expr::Call(ast::ExprCall { func, .. }) => semantic
|
||||
.resolve_qualified_name(func.as_ref())
|
||||
.is_some_and(|qualified_name| {
|
||||
@@ -38,8 +34,8 @@ pub fn is_sys_path_modification(stmt: &Stmt, semantic: &SemanticModel) -> bool {
|
||||
}),
|
||||
_ => false,
|
||||
},
|
||||
Stmt::AugAssign(ast::StmtAugAssign { target, .. }) => semantic
|
||||
.resolve_qualified_name(map_subscript(target))
|
||||
Stmt::AugAssign(node) => semantic
|
||||
.resolve_qualified_name(map_subscript(&node.target))
|
||||
.is_some_and(|qualified_name| matches!(qualified_name.segments(), ["sys", "path"])),
|
||||
_ => false,
|
||||
}
|
||||
@@ -53,7 +49,7 @@ pub fn is_sys_path_modification(stmt: &Stmt, semantic: &SemanticModel) -> bool {
|
||||
/// ```
|
||||
pub fn is_os_environ_modification(stmt: &Stmt, semantic: &SemanticModel) -> bool {
|
||||
match stmt {
|
||||
Stmt::Expr(ast::StmtExpr { value, .. }) => match value.as_ref() {
|
||||
Stmt::Expr(node) => match node.value.as_ref() {
|
||||
Expr::Call(ast::ExprCall { func, .. }) => semantic
|
||||
.resolve_qualified_name(func.as_ref())
|
||||
.is_some_and(|qualified_name| {
|
||||
@@ -69,25 +65,25 @@ pub fn is_os_environ_modification(stmt: &Stmt, semantic: &SemanticModel) -> bool
|
||||
}),
|
||||
_ => false,
|
||||
},
|
||||
Stmt::Delete(ast::StmtDelete { targets, .. }) => targets.iter().any(|target| {
|
||||
Stmt::Delete(node) => node.targets.iter().any(|target| {
|
||||
semantic
|
||||
.resolve_qualified_name(map_subscript(target))
|
||||
.is_some_and(|qualified_name| {
|
||||
matches!(qualified_name.segments(), ["os", "environ"])
|
||||
})
|
||||
}),
|
||||
Stmt::Assign(ast::StmtAssign { targets, .. }) => targets.iter().any(|target| {
|
||||
Stmt::Assign(node) => node.targets.iter().any(|target| {
|
||||
semantic
|
||||
.resolve_qualified_name(map_subscript(target))
|
||||
.is_some_and(|qualified_name| {
|
||||
matches!(qualified_name.segments(), ["os", "environ"])
|
||||
})
|
||||
}),
|
||||
Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => semantic
|
||||
.resolve_qualified_name(map_subscript(target))
|
||||
Stmt::AnnAssign(node) => semantic
|
||||
.resolve_qualified_name(map_subscript(&node.target))
|
||||
.is_some_and(|qualified_name| matches!(qualified_name.segments(), ["os", "environ"])),
|
||||
Stmt::AugAssign(ast::StmtAugAssign { target, .. }) => semantic
|
||||
.resolve_qualified_name(map_subscript(target))
|
||||
Stmt::AugAssign(node) => semantic
|
||||
.resolve_qualified_name(map_subscript(&node.target))
|
||||
.is_some_and(|qualified_name| matches!(qualified_name.segments(), ["os", "environ"])),
|
||||
_ => false,
|
||||
}
|
||||
|
||||
@@ -41,23 +41,31 @@ impl Terminal {
|
||||
|
||||
for stmt in stmts {
|
||||
match stmt {
|
||||
Stmt::For(ast::StmtFor { body, orelse, .. })
|
||||
| Stmt::While(ast::StmtWhile { body, orelse, .. }) => {
|
||||
if always_breaks(body) {
|
||||
Stmt::For(node) => {
|
||||
if always_breaks(&node.body) {
|
||||
continue;
|
||||
}
|
||||
|
||||
terminal = terminal.and_then(Self::from_body(body));
|
||||
terminal = terminal.and_then(Self::from_body(&node.body));
|
||||
|
||||
if !sometimes_breaks(body) {
|
||||
terminal = terminal.and_then(Self::from_body(orelse));
|
||||
if !sometimes_breaks(&node.body) {
|
||||
terminal = terminal.and_then(Self::from_body(&node.orelse));
|
||||
}
|
||||
}
|
||||
Stmt::If(ast::StmtIf {
|
||||
body,
|
||||
elif_else_clauses,
|
||||
..
|
||||
}) => {
|
||||
Stmt::While(node) => {
|
||||
if always_breaks(&node.body) {
|
||||
continue;
|
||||
}
|
||||
|
||||
terminal = terminal.and_then(Self::from_body(&node.body));
|
||||
|
||||
if !sometimes_breaks(&node.body) {
|
||||
terminal = terminal.and_then(Self::from_body(&node.orelse));
|
||||
}
|
||||
}
|
||||
Stmt::If(node) => {
|
||||
let body = &node.body;
|
||||
let elif_else_clauses = &node.elif_else_clauses;
|
||||
let branch_terminal = Terminal::branches(
|
||||
std::iter::once(Self::from_body(body)).chain(
|
||||
elif_else_clauses
|
||||
@@ -77,14 +85,14 @@ impl Terminal {
|
||||
terminal = terminal.and_then(Terminal::ConditionalReturn);
|
||||
}
|
||||
}
|
||||
Stmt::Match(ast::StmtMatch { cases, .. }) => {
|
||||
Stmt::Match(node) => {
|
||||
let branch_terminal = terminal.and_then(Terminal::branches(
|
||||
cases.iter().map(|case| Self::from_body(&case.body)),
|
||||
node.cases.iter().map(|case| Self::from_body(&case.body)),
|
||||
));
|
||||
|
||||
// If the `match` is known to be exhaustive (by way of including a wildcard
|
||||
// pattern)...
|
||||
if cases.iter().any(is_wildcard) {
|
||||
if node.cases.iter().any(is_wildcard) {
|
||||
// And all branches return, then the `match` statement returns.
|
||||
terminal = terminal.and_then(branch_terminal);
|
||||
} else {
|
||||
@@ -95,27 +103,21 @@ impl Terminal {
|
||||
}
|
||||
}
|
||||
}
|
||||
Stmt::Try(ast::StmtTry {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
..
|
||||
}) => {
|
||||
Stmt::Try(node) => {
|
||||
// If the body returns, then this can't be a non-returning function. We assume
|
||||
// that _any_ statement in the body could raise an exception, so we don't
|
||||
// consider the body to be exhaustive. In other words, we assume the exception
|
||||
// handlers exist for a reason.
|
||||
let body_terminal = Self::from_body(body);
|
||||
let body_terminal = Self::from_body(&node.body);
|
||||
if body_terminal.has_any_return() {
|
||||
terminal = terminal.and_then(Terminal::ConditionalReturn);
|
||||
}
|
||||
|
||||
// If the `finally` block returns, the `try` block must also return. (Similarly,
|
||||
// if the `finally` block raises, the `try` block must also raise.)
|
||||
terminal = terminal.and_then(Self::from_body(finalbody));
|
||||
terminal = terminal.and_then(Self::from_body(&node.finalbody));
|
||||
|
||||
let branch_terminal = Terminal::branches(handlers.iter().map(|handler| {
|
||||
let branch_terminal = Terminal::branches(node.handlers.iter().map(|handler| {
|
||||
let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
|
||||
body,
|
||||
..
|
||||
@@ -123,7 +125,7 @@ impl Terminal {
|
||||
Self::from_body(body)
|
||||
}));
|
||||
|
||||
if orelse.is_empty() {
|
||||
if node.orelse.is_empty() {
|
||||
// If there's no `else`, we may fall through, so only mark that this can't
|
||||
// be a non-returning function if any of the branches return.
|
||||
if branch_terminal.has_any_return() {
|
||||
@@ -133,11 +135,11 @@ impl Terminal {
|
||||
// If there's an `else`, we won't fall through. If all the handlers and
|
||||
// the `else` block return,, the `try` block also returns.
|
||||
terminal =
|
||||
terminal.and_then(branch_terminal.branch(Terminal::from_body(orelse)));
|
||||
terminal.and_then(branch_terminal.branch(Terminal::from_body(&node.orelse)));
|
||||
}
|
||||
}
|
||||
Stmt::With(ast::StmtWith { body, .. }) => {
|
||||
terminal = terminal.and_then(Self::from_body(body));
|
||||
Stmt::With(node) => {
|
||||
terminal = terminal.and_then(Self::from_body(&node.body));
|
||||
}
|
||||
Stmt::Return(_) => {
|
||||
terminal = terminal.and_then(Terminal::RaiseOrReturn);
|
||||
@@ -247,62 +249,52 @@ impl Terminal {
|
||||
fn sometimes_breaks(stmts: &[Stmt]) -> bool {
|
||||
for stmt in stmts {
|
||||
match stmt {
|
||||
Stmt::For(ast::StmtFor { body, orelse, .. }) => {
|
||||
if Terminal::from_body(body).has_any_return() {
|
||||
Stmt::For(node) => {
|
||||
if Terminal::from_body(&node.body).has_any_return() {
|
||||
return false;
|
||||
}
|
||||
if sometimes_breaks(orelse) {
|
||||
if sometimes_breaks(&node.orelse) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::While(ast::StmtWhile { body, orelse, .. }) => {
|
||||
if Terminal::from_body(body).has_any_return() {
|
||||
Stmt::While(node) => {
|
||||
if Terminal::from_body(&node.body).has_any_return() {
|
||||
return false;
|
||||
}
|
||||
if sometimes_breaks(orelse) {
|
||||
if sometimes_breaks(&node.orelse) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::If(ast::StmtIf {
|
||||
body,
|
||||
elif_else_clauses,
|
||||
..
|
||||
}) => {
|
||||
if std::iter::once(body)
|
||||
.chain(elif_else_clauses.iter().map(|clause| &clause.body))
|
||||
Stmt::If(node) => {
|
||||
if std::iter::once(&node.body)
|
||||
.chain(node.elif_else_clauses.iter().map(|clause| &clause.body))
|
||||
.any(|body| sometimes_breaks(body))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::Match(ast::StmtMatch { cases, .. }) => {
|
||||
if cases.iter().any(|case| sometimes_breaks(&case.body)) {
|
||||
Stmt::Match(node) => {
|
||||
if node.cases.iter().any(|case| sometimes_breaks(&case.body)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::Try(ast::StmtTry {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
..
|
||||
}) => {
|
||||
if sometimes_breaks(body)
|
||||
|| handlers.iter().any(|handler| {
|
||||
Stmt::Try(node) => {
|
||||
if sometimes_breaks(&node.body)
|
||||
|| node.handlers.iter().any(|handler| {
|
||||
let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
|
||||
body,
|
||||
..
|
||||
}) = handler;
|
||||
sometimes_breaks(body)
|
||||
})
|
||||
|| sometimes_breaks(orelse)
|
||||
|| sometimes_breaks(finalbody)
|
||||
|| sometimes_breaks(&node.orelse)
|
||||
|| sometimes_breaks(&node.finalbody)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::With(ast::StmtWith { body, .. }) => {
|
||||
if sometimes_breaks(body) {
|
||||
Stmt::With(node) => {
|
||||
if sometimes_breaks(&node.body) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ use ruff_python_ast::identifier::Identifier;
|
||||
use ruff_python_ast::name::QualifiedName;
|
||||
use ruff_python_ast::{
|
||||
self as ast, Expr, ExprCall, ExprName, Operator, ParameterWithDefault, Parameters, Stmt,
|
||||
StmtAssign,
|
||||
};
|
||||
use ruff_python_stdlib::typing::{
|
||||
as_pep_585_generic, is_immutable_generic_type, is_immutable_non_generic_type,
|
||||
@@ -366,9 +365,13 @@ pub fn is_immutable_newtype_call(
|
||||
return false;
|
||||
}
|
||||
|
||||
let Some(Stmt::Assign(StmtAssign { value, .. })) = binding.statement(semantic) else {
|
||||
let Some(stmt) = binding.statement(semantic) else {
|
||||
return false;
|
||||
};
|
||||
let Stmt::Assign(node) = stmt else {
|
||||
return false;
|
||||
};
|
||||
let value = &node.value;
|
||||
|
||||
let Expr::Call(ExprCall {
|
||||
func, arguments, ..
|
||||
@@ -632,18 +635,20 @@ pub fn check_type<T: TypeChecker>(binding: &Binding, semantic: &SemanticModel) -
|
||||
// ```
|
||||
//
|
||||
// The type checker might know how to infer the type based on `init_expr`.
|
||||
Some(Stmt::Assign(ast::StmtAssign { targets, value, .. })) => targets
|
||||
.iter()
|
||||
.find_map(|target| match_value(binding, target, value))
|
||||
.is_some_and(|value| T::match_initializer(value, semantic)),
|
||||
Some(Stmt::Assign(node)) => {
|
||||
node.targets
|
||||
.iter()
|
||||
.find_map(|target| match_value(binding, target, &node.value))
|
||||
.is_some_and(|value| T::match_initializer(value, semantic))
|
||||
}
|
||||
|
||||
// ```python
|
||||
// x: annotation = some_expr
|
||||
// ```
|
||||
//
|
||||
// In this situation, we check only the annotation.
|
||||
Some(Stmt::AnnAssign(ast::StmtAnnAssign { annotation, .. })) => {
|
||||
T::match_annotation(annotation, semantic)
|
||||
Some(Stmt::AnnAssign(node)) => {
|
||||
T::match_annotation(&node.annotation, semantic)
|
||||
}
|
||||
|
||||
_ => false,
|
||||
@@ -670,14 +675,16 @@ pub fn check_type<T: TypeChecker>(binding: &Binding, semantic: &SemanticModel) -
|
||||
// with open("file.txt") as x:
|
||||
// ...
|
||||
// ```
|
||||
Some(Stmt::With(ast::StmtWith { items, .. })) => items
|
||||
.iter()
|
||||
.find_map(|item| {
|
||||
let target = item.optional_vars.as_ref()?;
|
||||
let value = &item.context_expr;
|
||||
match_value(binding, target, value)
|
||||
})
|
||||
.is_some_and(|value| T::match_initializer(value, semantic)),
|
||||
Some(Stmt::With(node)) => {
|
||||
node.items
|
||||
.iter()
|
||||
.find_map(|item| {
|
||||
let target = item.optional_vars.as_ref()?;
|
||||
let value = &item.context_expr;
|
||||
match_value(binding, target, value)
|
||||
})
|
||||
.is_some_and(|value| T::match_initializer(value, semantic))
|
||||
}
|
||||
|
||||
_ => false,
|
||||
},
|
||||
@@ -689,8 +696,8 @@ pub fn check_type<T: TypeChecker>(binding: &Binding, semantic: &SemanticModel) -
|
||||
// ```
|
||||
//
|
||||
// We trust the annotation and see if the type checker matches the annotation.
|
||||
Some(Stmt::FunctionDef(ast::StmtFunctionDef { parameters, .. })) => {
|
||||
let Some(parameter) = find_parameter(parameters, binding) else {
|
||||
Some(Stmt::FunctionDef(node)) => {
|
||||
let Some(parameter) = find_parameter(&node.parameters, binding) else {
|
||||
return false;
|
||||
};
|
||||
let Some(annotation) = parameter.annotation() else {
|
||||
@@ -708,8 +715,8 @@ pub fn check_type<T: TypeChecker>(binding: &Binding, semantic: &SemanticModel) -
|
||||
// ```
|
||||
//
|
||||
// It's a typed declaration, type annotation is the only source of information.
|
||||
Some(Stmt::AnnAssign(ast::StmtAnnAssign { annotation, .. })) => {
|
||||
T::match_annotation(annotation, semantic)
|
||||
Some(Stmt::AnnAssign(node)) => {
|
||||
T::match_annotation(&node.annotation, semantic)
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
@@ -719,9 +726,11 @@ pub fn check_type<T: TypeChecker>(binding: &Binding, semantic: &SemanticModel) -
|
||||
// def foo() -> int:
|
||||
// ...
|
||||
// ```
|
||||
Some(Stmt::FunctionDef(ast::StmtFunctionDef { returns, .. })) => returns
|
||||
.as_ref()
|
||||
.is_some_and(|return_ann| T::match_annotation(return_ann, semantic)),
|
||||
Some(Stmt::FunctionDef(node)) => {
|
||||
node.returns
|
||||
.as_ref()
|
||||
.is_some_and(|return_ann| T::match_annotation(return_ann, semantic))
|
||||
}
|
||||
|
||||
_ => false,
|
||||
},
|
||||
@@ -1038,12 +1047,12 @@ pub fn is_dict(binding: &Binding, semantic: &SemanticModel) -> bool {
|
||||
// ...
|
||||
// ```
|
||||
if matches!(binding.kind, BindingKind::Argument) {
|
||||
if let Some(Stmt::FunctionDef(ast::StmtFunctionDef { parameters, .. })) =
|
||||
binding.statement(semantic)
|
||||
{
|
||||
if let Some(kwarg_parameter) = parameters.kwarg.as_deref() {
|
||||
if kwarg_parameter.name.range() == binding.range() {
|
||||
return true;
|
||||
if let Some(stmt) = binding.statement(semantic) {
|
||||
if let Stmt::FunctionDef(node) = stmt {
|
||||
if let Some(kwarg_parameter) = node.parameters.kwarg.as_deref() {
|
||||
if kwarg_parameter.name.range() == binding.range() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1091,12 +1100,12 @@ pub fn is_tuple(binding: &Binding, semantic: &SemanticModel) -> bool {
|
||||
// ...
|
||||
// ```
|
||||
if matches!(binding.kind, BindingKind::Argument) {
|
||||
if let Some(Stmt::FunctionDef(ast::StmtFunctionDef { parameters, .. })) =
|
||||
binding.statement(semantic)
|
||||
{
|
||||
if let Some(arg_parameter) = parameters.vararg.as_deref() {
|
||||
if arg_parameter.name.range() == binding.range() {
|
||||
return true;
|
||||
if let Some(stmt) = binding.statement(semantic) {
|
||||
if let Stmt::FunctionDef(node) = stmt {
|
||||
if let Some(arg_parameter) = node.parameters.vararg.as_deref() {
|
||||
if arg_parameter.name.range() == binding.range() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1183,10 +1192,14 @@ pub fn resolve_assignment<'a>(
|
||||
let binding_id = semantic.resolve_name(name)?;
|
||||
let statement = semantic.binding(binding_id).statement(semantic)?;
|
||||
match statement {
|
||||
Stmt::Assign(ast::StmtAssign { value, .. })
|
||||
| Stmt::AnnAssign(ast::StmtAnnAssign {
|
||||
value: Some(value), ..
|
||||
}) => {
|
||||
Stmt::Assign(node) => {
|
||||
let ast::ExprCall { func, .. } = node.value.as_call_expr()?;
|
||||
|
||||
let qualified_name = semantic.resolve_qualified_name(func)?;
|
||||
Some(qualified_name.extend_members(reversed_tail.into_iter().rev()))
|
||||
}
|
||||
Stmt::AnnAssign(node) => {
|
||||
let value = node.value.as_ref()?;
|
||||
let ast::ExprCall { func, .. } = value.as_call_expr()?;
|
||||
|
||||
let qualified_name = semantic.resolve_qualified_name(func)?;
|
||||
@@ -1237,24 +1250,23 @@ pub fn find_binding_value<'a>(binding: &Binding, semantic: &'a SemanticModel) ->
|
||||
}
|
||||
// Ex) `x = 1`
|
||||
BindingKind::Assignment => match binding.statement(semantic) {
|
||||
Some(Stmt::Assign(ast::StmtAssign { value, targets, .. })) => {
|
||||
return targets
|
||||
Some(Stmt::Assign(node)) => {
|
||||
return node
|
||||
.targets
|
||||
.iter()
|
||||
.find_map(|target| match_value(binding, target, value));
|
||||
.find_map(|target| match_value(binding, target, &node.value));
|
||||
}
|
||||
Some(Stmt::AnnAssign(ast::StmtAnnAssign {
|
||||
value: Some(value),
|
||||
target,
|
||||
..
|
||||
})) => {
|
||||
return match_value(binding, target, value);
|
||||
Some(Stmt::AnnAssign(node)) => {
|
||||
if let Some(value) = &node.value {
|
||||
return match_value(binding, &node.target, value);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
// Ex) `with open("file.txt") as f:`
|
||||
BindingKind::WithItemVar => match binding.statement(semantic) {
|
||||
Some(Stmt::With(ast::StmtWith { items, .. })) => {
|
||||
return items.iter().find_map(|item| {
|
||||
Some(Stmt::With(node)) => {
|
||||
return node.items.iter().find_map(|item| {
|
||||
let target = item.optional_vars.as_ref()?;
|
||||
let value = &item.context_expr;
|
||||
match_value(binding, target, value)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
use std::ops::Index;
|
||||
|
||||
use ruff_python_ast::{self as ast, Stmt};
|
||||
use ruff_python_ast::Stmt;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
@@ -74,12 +74,8 @@ impl<'a> GlobalsVisitor<'a> {
|
||||
impl<'a> StatementVisitor<'a> for GlobalsVisitor<'a> {
|
||||
fn visit_stmt(&mut self, stmt: &'a Stmt) {
|
||||
match stmt {
|
||||
Stmt::Global(ast::StmtGlobal {
|
||||
names,
|
||||
range: _,
|
||||
node_index: _,
|
||||
}) => {
|
||||
for name in names {
|
||||
Stmt::Global(global_stmt) => {
|
||||
for name in &global_stmt.names {
|
||||
self.0.insert(name.as_str(), name.range());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ impl serde::Serialize for NameImport {
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> serde::de::Deserialize<'de> for NameImports {
|
||||
fn deserialize<D: serde::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
use ruff_python_ast::{self as ast, Stmt};
|
||||
use ruff_python_ast::Stmt;
|
||||
use ruff_python_parser::Parsed;
|
||||
|
||||
struct AnyNameImportsVisitor;
|
||||
@@ -225,30 +225,22 @@ impl<'de> serde::de::Deserialize<'de> for NameImports {
|
||||
};
|
||||
|
||||
let imports = match stmt {
|
||||
Stmt::ImportFrom(ast::StmtImportFrom {
|
||||
module,
|
||||
names,
|
||||
level,
|
||||
range: _,
|
||||
node_index: _,
|
||||
}) => names
|
||||
Stmt::ImportFrom(import_from) => import_from
|
||||
.names
|
||||
.iter()
|
||||
.map(|name| {
|
||||
NameImport::ImportFrom(MemberNameImport {
|
||||
module: module.as_deref().map(ToString::to_string),
|
||||
module: import_from.module.as_deref().map(ToString::to_string),
|
||||
name: Alias {
|
||||
name: name.name.to_string(),
|
||||
as_name: name.asname.as_deref().map(ToString::to_string),
|
||||
},
|
||||
level: *level,
|
||||
level: import_from.level,
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
Stmt::Import(ast::StmtImport {
|
||||
names,
|
||||
range: _,
|
||||
node_index: _,
|
||||
}) => names
|
||||
Stmt::Import(import_stmt) => import_stmt
|
||||
.names
|
||||
.iter()
|
||||
.map(|name| {
|
||||
NameImport::Import(ModuleNameImport {
|
||||
|
||||
@@ -102,9 +102,9 @@ impl SemanticModel<'_> {
|
||||
let mut flags = DunderAllFlags::empty();
|
||||
|
||||
if let Some(value) = match stmt {
|
||||
Stmt::Assign(ast::StmtAssign { value, .. }) => Some(value),
|
||||
Stmt::AnnAssign(ast::StmtAnnAssign { value, .. }) => value.as_ref(),
|
||||
Stmt::AugAssign(ast::StmtAugAssign { value, .. }) => Some(value),
|
||||
Stmt::Assign(node) => Some(&node.value),
|
||||
Stmt::AnnAssign(node) => node.value.as_ref(),
|
||||
Stmt::AugAssign(node) => Some(&node.value),
|
||||
_ => None,
|
||||
} {
|
||||
if let Expr::BinOp(ast::ExprBinOp { left, right, .. }) = value.as_ref() {
|
||||
|
||||
Reference in New Issue
Block a user