Ignore NPY201 inside except blocks for compatibility with older numpy versions (#12490)

This commit is contained in:
Alex Waygood 2024-07-24 21:03:23 +01:00 committed by GitHub
parent e52be0951a
commit 928ffd6650
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 413 additions and 30 deletions

View file

@ -5,8 +5,9 @@ use bitflags::bitflags;
use crate::all::DunderAllName;
use ruff_index::{newtype_index, IndexSlice, IndexVec};
use ruff_python_ast::helpers::extract_handled_exceptions;
use ruff_python_ast::name::QualifiedName;
use ruff_python_ast::Stmt;
use ruff_python_ast::{self as ast, Stmt};
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange};
@ -114,6 +115,18 @@ impl<'a> Binding<'a> {
self.flags.contains(BindingFlags::PRIVATE_DECLARATION)
}
/// Return `true` if this [`Binding`] took place inside an exception handler,
/// e.g. `y` in:
/// ```python
/// try:
/// x = 42
/// except RuntimeError:
/// y = 42
/// ```
pub const fn in_exception_handler(&self) -> bool {
self.flags.contains(BindingFlags::IN_EXCEPT_HANDLER)
}
/// Return `true` if this binding "redefines" the given binding, as per Pyflake's definition of
/// redefinition.
pub fn redefines(&self, existing: &Binding) -> bool {
@ -333,6 +346,18 @@ bitflags! {
/// (x, y) = 1, 2
/// ```
const UNPACKED_ASSIGNMENT = 1 << 9;
/// The binding took place inside an exception handling.
///
/// For example, the `x` binding in the following example
/// would *not* have this flag set, but the `y` binding *would*:
/// ```python
/// try:
/// x = 42
/// except RuntimeError:
/// y = 42
/// ```
const IN_EXCEPT_HANDLER = 1 << 10;
}
}
@ -579,6 +604,26 @@ bitflags! {
const NAME_ERROR = 0b0000_0001;
const MODULE_NOT_FOUND_ERROR = 0b0000_0010;
const IMPORT_ERROR = 0b0000_0100;
const ATTRIBUTE_ERROR = 0b000_100;
}
}
impl Exceptions {
pub fn from_try_stmt(
ast::StmtTry { handlers, .. }: &ast::StmtTry,
semantic: &SemanticModel,
) -> Self {
let mut handled_exceptions = Self::empty();
for type_ in extract_handled_exceptions(handlers) {
handled_exceptions |= match semantic.resolve_builtin_symbol(type_) {
Some("NameError") => Self::NAME_ERROR,
Some("ModuleNotFoundError") => Self::MODULE_NOT_FOUND_ERROR,
Some("ImportError") => Self::IMPORT_ERROR,
Some("AttributeError") => Self::ATTRIBUTE_ERROR,
_ => continue,
}
}
handled_exceptions
}
}

View file

@ -124,7 +124,21 @@ pub struct SemanticModel<'a> {
/// Modules that have been seen by the semantic model.
pub seen: Modules,
/// Exceptions that have been handled by the current scope.
/// Exceptions that are handled by the current `try` block.
///
/// For example, if we're visiting the `x = 1` assignment below,
/// `AttributeError` is considered to be a "handled exception",
/// but `TypeError` is not:
///
/// ```py
/// try:
/// try:
/// foo()
/// except TypeError:
/// pass
/// except AttributeError:
/// pass
/// ```
pub handled_exceptions: Vec<Exceptions>,
/// Map from [`ast::ExprName`] node (represented as a [`NameId`]) to the [`Binding`] to which
@ -1193,6 +1207,14 @@ impl<'a> SemanticModel<'a> {
.expect("No statement found")
}
/// Returns an [`Iterator`] over the statements, starting from the given [`NodeId`].
/// through to any parents.
pub fn statements(&self, node_id: NodeId) -> impl Iterator<Item = &'a Stmt> + '_ {
self.nodes
.ancestor_ids(node_id)
.filter_map(move |id| self.nodes[id].as_statement())
}
/// Given a [`Stmt`], return its parent, if any.
#[inline]
pub fn parent_statement(&self, node_id: NodeId) -> Option<&'a Stmt> {