mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-15 16:10:17 +00:00
[ty] support del statement and deletion of except handler names (#18593)
## Summary This PR closes https://github.com/astral-sh/ty/issues/238. Since `DefinitionState::Deleted` was introduced in #18041, support for the `del` statement (and deletion of except handler names) is straightforward. However, it is difficult to determine whether references to attributes or subscripts are unresolved after they are deleted. This PR only invalidates narrowing by assignment if the attribute or subscript is deleted. ## Test Plan `mdtest/del.md` is added. --------- Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
parent
96171f41c2
commit
ef564094a9
4 changed files with 193 additions and 7 deletions
|
@ -449,6 +449,12 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
|||
}
|
||||
}
|
||||
|
||||
fn delete_binding(&mut self, place: ScopedPlaceId) {
|
||||
let is_place_name = self.current_place_table().place_expr(place).is_name();
|
||||
self.current_use_def_map_mut()
|
||||
.delete_binding(place, is_place_name);
|
||||
}
|
||||
|
||||
/// Push a new [`Definition`] onto the list of definitions
|
||||
/// associated with the `definition_node` AST node.
|
||||
///
|
||||
|
@ -1817,7 +1823,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
|||
// If `handled_exceptions` above was `None`, it's something like `except as e:`,
|
||||
// which is invalid syntax. However, it's still pretty obvious here that the user
|
||||
// *wanted* `e` to be bound, so we should still create a definition here nonetheless.
|
||||
if let Some(symbol_name) = symbol_name {
|
||||
let symbol = if let Some(symbol_name) = symbol_name {
|
||||
let symbol = self.add_symbol(symbol_name.id.clone());
|
||||
|
||||
self.add_definition(
|
||||
|
@ -1827,9 +1833,16 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
|||
is_star: *is_star,
|
||||
}),
|
||||
);
|
||||
}
|
||||
Some(symbol)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.visit_body(handler_body);
|
||||
// The caught exception is cleared at the end of the except clause
|
||||
if let Some(symbol) = symbol {
|
||||
self.delete_binding(symbol);
|
||||
}
|
||||
// Each `except` block is mutually exclusive with all other `except` blocks.
|
||||
post_except_states.push(self.flow_snapshot());
|
||||
|
||||
|
@ -1903,13 +1916,15 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
|||
walk_stmt(self, stmt);
|
||||
}
|
||||
ast::Stmt::Delete(ast::StmtDelete { targets, range: _ }) => {
|
||||
// We will check the target expressions and then delete them.
|
||||
walk_stmt(self, stmt);
|
||||
for target in targets {
|
||||
if let Ok(target) = PlaceExpr::try_from(target) {
|
||||
let place_id = self.add_place(target);
|
||||
self.current_place_table().mark_place_used(place_id);
|
||||
self.delete_binding(place_id);
|
||||
}
|
||||
}
|
||||
walk_stmt(self, stmt);
|
||||
}
|
||||
ast::Stmt::Expr(ast::StmtExpr { value, range: _ }) if self.in_module_scope() => {
|
||||
if let Some(expr) = dunder_all_extend_argument(value) {
|
||||
|
@ -1956,7 +1971,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
|||
}
|
||||
(ast::ExprContext::Load, _) => (true, false),
|
||||
(ast::ExprContext::Store, _) => (false, true),
|
||||
(ast::ExprContext::Del, _) => (false, true),
|
||||
(ast::ExprContext::Del, _) => (true, true),
|
||||
(ast::ExprContext::Invalid, _) => (false, false),
|
||||
};
|
||||
let place_id = self.add_place(place_expr);
|
||||
|
|
|
@ -6145,7 +6145,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
fn infer_name_expression(&mut self, name: &ast::ExprName) -> Type<'db> {
|
||||
match name.ctx {
|
||||
ExprContext::Load => self.infer_name_load(name),
|
||||
ExprContext::Store | ExprContext::Del => Type::Never,
|
||||
ExprContext::Store => Type::Never,
|
||||
ExprContext::Del => {
|
||||
self.infer_name_load(name);
|
||||
Type::Never
|
||||
}
|
||||
ExprContext::Invalid => Type::unknown(),
|
||||
}
|
||||
}
|
||||
|
@ -6254,10 +6258,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
|
||||
match ctx {
|
||||
ExprContext::Load => self.infer_attribute_load(attribute),
|
||||
ExprContext::Store | ExprContext::Del => {
|
||||
ExprContext::Store => {
|
||||
self.infer_expression(value);
|
||||
Type::Never
|
||||
}
|
||||
ExprContext::Del => {
|
||||
self.infer_attribute_load(attribute);
|
||||
Type::Never
|
||||
}
|
||||
ExprContext::Invalid => {
|
||||
self.infer_expression(value);
|
||||
Type::unknown()
|
||||
|
@ -7646,12 +7654,16 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
|
||||
match ctx {
|
||||
ExprContext::Load => self.infer_subscript_load(subscript),
|
||||
ExprContext::Store | ExprContext::Del => {
|
||||
ExprContext::Store => {
|
||||
let value_ty = self.infer_expression(value);
|
||||
let slice_ty = self.infer_expression(slice);
|
||||
self.infer_subscript_expression_types(value, value_ty, slice_ty);
|
||||
Type::Never
|
||||
}
|
||||
ExprContext::Del => {
|
||||
self.infer_subscript_load(subscript);
|
||||
Type::Never
|
||||
}
|
||||
ExprContext::Invalid => {
|
||||
let value_ty = self.infer_expression(value);
|
||||
let slice_ty = self.infer_expression(slice);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue