mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-16 16:40:19 +00:00
[ruff
] Add never-union
rule to detect redundant typing.NoReturn
and typing.Never
(#9217)
## Summary Adds a rule to detect unions that include `typing.NoReturn` or `typing.Never`. In such cases, the use of the bottom type is redundant. Closes https://github.com/astral-sh/ruff/issues/9113. ## Test Plan `cargo test`
This commit is contained in:
parent
a3e06e5a9d
commit
a9ceef5b5d
17 changed files with 283 additions and 73 deletions
|
@ -333,6 +333,58 @@ pub fn is_sys_version_block(stmt: &ast::StmtIf, semantic: &SemanticModel) -> boo
|
|||
})
|
||||
}
|
||||
|
||||
/// Traverse a "union" type annotation, applying `func` to each union member.
|
||||
/// Supports traversal of `Union` and `|` union expressions.
|
||||
/// The function is called with each expression in the union (excluding declarations of nested unions)
|
||||
/// and the parent expression (if any).
|
||||
pub fn traverse_union<'a, F>(
|
||||
func: &mut F,
|
||||
semantic: &SemanticModel,
|
||||
expr: &'a Expr,
|
||||
parent: Option<&'a Expr>,
|
||||
) where
|
||||
F: FnMut(&'a Expr, Option<&'a Expr>),
|
||||
{
|
||||
// Ex) x | y
|
||||
if let Expr::BinOp(ast::ExprBinOp {
|
||||
op: Operator::BitOr,
|
||||
left,
|
||||
right,
|
||||
range: _,
|
||||
}) = expr
|
||||
{
|
||||
// The union data structure usually looks like this:
|
||||
// a | b | c -> (a | b) | c
|
||||
//
|
||||
// However, parenthesized expressions can coerce it into any structure:
|
||||
// a | (b | c)
|
||||
//
|
||||
// So we have to traverse both branches in order (left, then right), to report members
|
||||
// in the order they appear in the source code.
|
||||
|
||||
// Traverse the left then right arms
|
||||
traverse_union(func, semantic, left, Some(expr));
|
||||
traverse_union(func, semantic, right, Some(expr));
|
||||
return;
|
||||
}
|
||||
|
||||
// Ex) Union[x, y]
|
||||
if let Expr::Subscript(ast::ExprSubscript { value, slice, .. }) = expr {
|
||||
if semantic.match_typing_expr(value, "Union") {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = slice.as_ref() {
|
||||
// Traverse each element of the tuple within the union recursively to handle cases
|
||||
// such as `Union[..., Union[...]]
|
||||
elts.iter()
|
||||
.for_each(|elt| traverse_union(func, semantic, elt, Some(expr)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, call the function on expression
|
||||
func(expr, parent);
|
||||
}
|
||||
|
||||
/// Abstraction for a type checker, conservatively checks for the intended type(s).
|
||||
trait TypeChecker {
|
||||
/// Check annotation expression to match the intended type(s).
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue