This commit is contained in:
Chandra Kiran G 2025-12-16 16:39:34 -05:00 committed by GitHub
commit 36cca9717b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 428 additions and 5 deletions

View File

@ -1,16 +1,23 @@
use std::fmt; use std::fmt;
use anyhow::Result;
use ruff_python_ast::name::Name;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use ruff_diagnostics::{Applicability, Edit, Fix};
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, Expr, LiteralExpressionRef}; use ruff_python_ast::{
self as ast, Expr, ExprBinOp, ExprContext, ExprName, ExprSubscript, LiteralExpressionRef,
Operator,
};
use ruff_python_semantic::SemanticModel; use ruff_python_semantic::SemanticModel;
use ruff_python_semantic::analyze::typing::traverse_union; use ruff_python_semantic::analyze::typing::traverse_union;
use ruff_text_size::Ranged; use ruff_text_size::{Ranged, TextRange};
use crate::Violation;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::snippet::SourceCodeSnippet; use crate::fix::snippet::SourceCodeSnippet;
use crate::importer::ImportRequest;
use crate::{FixAvailability, Violation};
/// ## What it does /// ## What it does
/// Checks for redundant unions between a `Literal` and a builtin supertype of /// Checks for redundant unions between a `Literal` and a builtin supertype of
@ -41,14 +48,18 @@ use crate::fix::snippet::SourceCodeSnippet;
pub(crate) struct RedundantLiteralUnion { pub(crate) struct RedundantLiteralUnion {
literal: SourceCodeSnippet, literal: SourceCodeSnippet,
builtin_type: ExprType, builtin_type: ExprType,
union_kind: UnionKind,
} }
impl Violation for RedundantLiteralUnion { impl Violation for RedundantLiteralUnion {
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
let RedundantLiteralUnion { let RedundantLiteralUnion {
literal, literal,
builtin_type, builtin_type,
..
} = self; } = self;
if let Some(literal) = literal.full_display() { if let Some(literal) = literal.full_display() {
format!("`Literal[{literal}]` is redundant in a union with `{builtin_type}`") format!("`Literal[{literal}]` is redundant in a union with `{builtin_type}`")
@ -56,18 +67,59 @@ impl Violation for RedundantLiteralUnion {
format!("`Literal` is redundant in a union with `{builtin_type}`") format!("`Literal` is redundant in a union with `{builtin_type}`")
} }
} }
fn fix_title(&self) -> Option<String> {
let RedundantLiteralUnion {
literal,
builtin_type,
union_kind,
} = self;
if let Some(literal) = literal.full_display() {
match union_kind {
UnionKind::TypingUnion => Some(format!(
"Replace `typing.Union[Literal[{literal}], {builtin_type}]` with `{builtin_type}`"
)),
UnionKind::PEP604 => Some(format!(
"Replace `Literal[{literal}] | {builtin_type}` with `{builtin_type}`"
)),
}
} else {
Some(format!("Replace with `{builtin_type}`"))
}
}
} }
/// PYI051 /// PYI051
pub(crate) fn redundant_literal_union<'a>(checker: &Checker, union: &'a Expr) { pub(crate) fn redundant_literal_union<'a>(checker: &Checker, union: &'a Expr) {
let mut typing_literal_exprs = Vec::new(); let mut typing_literal_exprs = Vec::new();
let mut builtin_types_in_union = FxHashSet::default(); let mut builtin_types_in_union = FxHashSet::default();
let mut literal_subscript = None;
let mut literal_exprs = Vec::new();
let subscript = union.as_subscript_expr();
let union_kind = match subscript {
Some(subscript) => {
if !checker
.semantic()
.match_typing_expr(&subscript.value, "Union")
{
return;
}
UnionKind::TypingUnion
}
None => UnionKind::PEP604,
};
// Adds a member to `literal_exprs` for each value in a `Literal`, and any builtin types // Adds a member to `literal_exprs` for each value in a `Literal`, and any builtin types
// to `builtin_types_in_union`. // to `builtin_types_in_union`.
let mut func = |expr: &'a Expr, _parent: &'a Expr| { let mut func = |expr: &'a Expr, _parent: &'a Expr| {
if let Expr::Subscript(ast::ExprSubscript { value, slice, .. }) = expr { if let Expr::Subscript(ast::ExprSubscript { value, slice, .. }) = expr {
if checker.semantic().match_typing_expr(value, "Literal") { if checker.semantic().match_typing_expr(value, "Literal") {
literal_exprs.push(expr);
if literal_subscript.is_none() {
literal_subscript = Some(value.as_ref());
}
if let Expr::Tuple(tuple) = &**slice { if let Expr::Tuple(tuple) = &**slice {
typing_literal_exprs.extend(tuple); typing_literal_exprs.extend(tuple);
} else { } else {
@ -85,23 +137,226 @@ pub(crate) fn redundant_literal_union<'a>(checker: &Checker, union: &'a Expr) {
traverse_union(&mut func, checker.semantic(), union); traverse_union(&mut func, checker.semantic(), union);
let Some(literal_subscript) = literal_subscript else {
return;
};
let mut diagnostics: Vec<(RedundantLiteralUnion, TextRange)> = Vec::new();
let mut non_redundant_literal_types = Vec::new();
for typing_literal_expr in typing_literal_exprs { for typing_literal_expr in typing_literal_exprs {
let Some(literal_type) = match_literal_type(typing_literal_expr) else { let Some(literal_type) = match_literal_type(typing_literal_expr) else {
continue; continue;
}; };
if builtin_types_in_union.contains(&literal_type) { if builtin_types_in_union.contains(&literal_type) {
checker.report_diagnostic( diagnostics.push((
RedundantLiteralUnion { RedundantLiteralUnion {
literal: SourceCodeSnippet::from_str( literal: SourceCodeSnippet::from_str(
checker.locator().slice(typing_literal_expr), checker.locator().slice(typing_literal_expr),
), ),
builtin_type: literal_type, builtin_type: literal_type,
union_kind,
}, },
typing_literal_expr.range(), typing_literal_expr.range(),
); ));
} else {
non_redundant_literal_types.push(typing_literal_expr);
} }
} }
if checker.settings().preview.is_disabled() {
for (kind, range) in diagnostics {
let _ = checker.report_diagnostic(kind, range);
}
return;
}
let mut new_literal_expr_types: Vec<LiteralExprType<'a>> = Vec::new();
// Group all the non-redundant literal types together based on the `Literals`
let mut func = |expr: &'a Expr, _parent: &'a Expr| {
if let Expr::Subscript(ast::ExprSubscript { value, slice, .. }) = expr {
if checker.semantic().match_typing_expr(value, "Literal") {
let mut group = Vec::new();
if let Expr::Tuple(tuple) = &**slice {
for tuple_slice in tuple {
if non_redundant_literal_types.contains(&tuple_slice) {
group.push(tuple_slice);
}
}
} else {
if non_redundant_literal_types.contains(&slice.as_ref()) {
group.push(slice);
}
}
if !group.is_empty() {
new_literal_expr_types.push(LiteralExprType::NonRedundantTypes(group.clone()));
}
}
return;
}
let Some(_) = match_builtin_type(expr, checker.semantic()) else {
return;
};
new_literal_expr_types.push(LiteralExprType::BuiltinType(expr));
};
traverse_union(&mut func, checker.semantic(), union);
// This generates new individual `Literal` exprs and builtins
let mut new_exprs = Vec::new();
for group in new_literal_expr_types {
match group {
LiteralExprType::BuiltinType(expr) => {
new_exprs.push(expr.to_owned());
}
LiteralExprType::NonRedundantTypes(group) => {
let new_literal_expr = Expr::Subscript(ast::ExprSubscript {
node_index: Default::default(),
value: Box::new(literal_subscript.clone()),
range: TextRange::default(),
ctx: ExprContext::Load,
slice: Box::new(if group.len() > 1 {
Expr::Tuple(ast::ExprTuple {
node_index: Default::default(),
elts: group.into_iter().cloned().collect(),
range: TextRange::default(),
ctx: ExprContext::Load,
parenthesized: true,
})
} else {
let Some(group) = group.first() else {
return;
};
group.to_owned().clone()
}),
});
new_exprs.push(new_literal_expr);
}
}
}
let applicability = if checker.comment_ranges().intersects(union.range()) {
Applicability::Unsafe
} else {
Applicability::Safe
};
for (kind, range) in diagnostics {
let mut diagnostic = checker.report_diagnostic(kind, range);
match union_kind {
UnionKind::PEP604 => {
diagnostic.try_set_optional_fix(|| -> anyhow::Result<Option<Fix>> {
Ok(generate_pep604_fix(
checker,
&new_exprs,
union,
applicability,
))
});
}
UnionKind::TypingUnion => {
diagnostic.try_set_optional_fix(|| -> anyhow::Result<Option<Fix>> {
generate_typing_union_fix(checker, &new_exprs, union, applicability)
});
}
}
}
}
fn generate_pep604_fix(
checker: &Checker,
new_exprs: &[Expr],
union: &Expr,
applicability: Applicability,
) -> Option<Fix> {
if let [new_expr] = new_exprs {
return Some(Fix::applicable_edit(
Edit::range_replacement(checker.generator().expr(new_expr), union.range()),
applicability,
));
}
let new_expr = new_exprs.iter().fold(None, |acc, right| {
if let Some(left) = acc {
Some(Expr::BinOp(ExprBinOp {
node_index: Default::default(),
left: Box::new(left),
op: Operator::BitOr,
right: Box::new(right.clone()),
range: TextRange::default(),
}))
} else {
Some(right.clone())
}
})?;
Some(Fix::applicable_edit(
Edit::range_replacement(checker.generator().expr(&new_expr), union.range()),
applicability,
))
}
fn generate_typing_union_fix(
checker: &Checker,
new_exprs: &[Expr],
union: &Expr,
applicability: Applicability,
) -> Result<Option<Fix>> {
let (import_edit, binding) = checker.importer().get_or_import_symbol(
&ImportRequest::import_from("typing", "Union"),
union.range().start(),
checker.semantic(),
)?;
// Construct the expression as `Subscript[typing.Union, Tuple[expr, [expr, ...]]]`
let new_expr = Expr::Subscript(ExprSubscript {
node_index: Default::default(),
range: TextRange::default(),
value: Box::new(Expr::Name(ExprName {
node_index: Default::default(),
id: Name::new(binding),
ctx: ExprContext::Store,
range: TextRange::default(),
})),
slice: Box::new(if new_exprs.len() > 1 {
Expr::Tuple(ast::ExprTuple {
node_index: Default::default(),
elts: new_exprs.to_vec(),
range: TextRange::default(),
ctx: ExprContext::Load,
parenthesized: true,
})
} else {
let Some(new_exprs) = new_exprs.first() else {
return Ok(None);
};
new_exprs.clone()
}),
ctx: ExprContext::Load,
});
Ok(Some(Fix::applicable_edits(
Edit::range_replacement(checker.generator().expr(&new_expr), union.range()),
[import_edit],
applicability,
)))
}
#[derive(Debug, Clone)]
enum LiteralExprType<'a> {
NonRedundantTypes(Vec<&'a Expr>),
BuiltinType(&'a Expr),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum UnionKind {
/// E.g., `typing.Union[int, str]`
TypingUnion,
/// E.g., `int | str`
PEP604,
} }
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]

View File

@ -11,6 +11,7 @@ PYI051 `Literal["foo"]` is redundant in a union with `str`
5 | B: TypeAlias = typing.Union[Literal[b"bar", b"foo"], bytes, str] 5 | B: TypeAlias = typing.Union[Literal[b"bar", b"foo"], bytes, str]
6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]] 6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]]
| |
help: Replace `Literal["foo"] | str` with `str`
PYI051 `Literal[b"bar"]` is redundant in a union with `bytes` PYI051 `Literal[b"bar"]` is redundant in a union with `bytes`
--> PYI051.py:5:37 --> PYI051.py:5:37
@ -21,6 +22,7 @@ PYI051 `Literal[b"bar"]` is redundant in a union with `bytes`
6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]] 6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]]
7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int] 7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
| |
help: Replace `typing.Union[Literal[b"bar"], bytes]` with `bytes`
PYI051 `Literal[b"foo"]` is redundant in a union with `bytes` PYI051 `Literal[b"foo"]` is redundant in a union with `bytes`
--> PYI051.py:5:45 --> PYI051.py:5:45
@ -31,6 +33,7 @@ PYI051 `Literal[b"foo"]` is redundant in a union with `bytes`
6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]] 6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]]
7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int] 7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
| |
help: Replace `typing.Union[Literal[b"foo"], bytes]` with `bytes`
PYI051 `Literal[5]` is redundant in a union with `int` PYI051 `Literal[5]` is redundant in a union with `int`
--> PYI051.py:6:37 --> PYI051.py:6:37
@ -42,6 +45,7 @@ PYI051 `Literal[5]` is redundant in a union with `int`
7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int] 7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]] 8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]]
| |
help: Replace `typing.Union[Literal[5], int]` with `int`
PYI051 `Literal["foo"]` is redundant in a union with `str` PYI051 `Literal["foo"]` is redundant in a union with `str`
--> PYI051.py:6:67 --> PYI051.py:6:67
@ -53,6 +57,7 @@ PYI051 `Literal["foo"]` is redundant in a union with `str`
7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int] 7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]] 8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]]
| |
help: Replace `typing.Union[Literal["foo"], str]` with `str`
PYI051 `Literal[b"str_bytes"]` is redundant in a union with `bytes` PYI051 `Literal[b"str_bytes"]` is redundant in a union with `bytes`
--> PYI051.py:7:37 --> PYI051.py:7:37
@ -64,6 +69,7 @@ PYI051 `Literal[b"str_bytes"]` is redundant in a union with `bytes`
8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]] 8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]]
9 | F: TypeAlias = typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]] 9 | F: TypeAlias = typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
| |
help: Replace `typing.Union[Literal[b"str_bytes"], bytes]` with `bytes`
PYI051 `Literal[42]` is redundant in a union with `int` PYI051 `Literal[42]` is redundant in a union with `int`
--> PYI051.py:7:51 --> PYI051.py:7:51
@ -75,6 +81,7 @@ PYI051 `Literal[42]` is redundant in a union with `int`
8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]] 8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]]
9 | F: TypeAlias = typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]] 9 | F: TypeAlias = typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
| |
help: Replace `typing.Union[Literal[42], int]` with `int`
PYI051 `Literal["foo"]` is redundant in a union with `str` PYI051 `Literal["foo"]` is redundant in a union with `str`
--> PYI051.py:8:76 --> PYI051.py:8:76
@ -86,6 +93,7 @@ PYI051 `Literal["foo"]` is redundant in a union with `str`
9 | F: TypeAlias = typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]] 9 | F: TypeAlias = typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
10 | G: typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]] 10 | G: typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
| |
help: Replace `typing.Union[Literal["foo"], str]` with `str`
PYI051 `Literal["foo"]` is redundant in a union with `str` PYI051 `Literal["foo"]` is redundant in a union with `str`
--> PYI051.py:9:81 --> PYI051.py:9:81
@ -96,6 +104,7 @@ PYI051 `Literal["foo"]` is redundant in a union with `str`
| ^^^^^ | ^^^^^
10 | G: typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]] 10 | G: typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
| |
help: Replace `typing.Union[Literal["foo"], str]` with `str`
PYI051 `Literal["foo"]` is redundant in a union with `str` PYI051 `Literal["foo"]` is redundant in a union with `str`
--> PYI051.py:10:69 --> PYI051.py:10:69
@ -107,6 +116,7 @@ PYI051 `Literal["foo"]` is redundant in a union with `str`
11 | 11 |
12 | def func(x: complex | Literal[1J], y: Union[Literal[3.14], float]): ... 12 | def func(x: complex | Literal[1J], y: Union[Literal[3.14], float]): ...
| |
help: Replace `typing.Union[Literal["foo"], str]` with `str`
PYI051 `Literal[1J]` is redundant in a union with `complex` PYI051 `Literal[1J]` is redundant in a union with `complex`
--> PYI051.py:12:31 --> PYI051.py:12:31
@ -118,6 +128,7 @@ PYI051 `Literal[1J]` is redundant in a union with `complex`
13 | 13 |
14 | # OK 14 | # OK
| |
help: Replace `Literal[1J] | complex` with `complex`
PYI051 `Literal[3.14]` is redundant in a union with `float` PYI051 `Literal[3.14]` is redundant in a union with `float`
--> PYI051.py:12:53 --> PYI051.py:12:53
@ -129,3 +140,4 @@ PYI051 `Literal[3.14]` is redundant in a union with `float`
13 | 13 |
14 | # OK 14 | # OK
| |
help: Replace `typing.Union[Literal[3.14], float]` with `float`

View File

@ -11,6 +11,7 @@ PYI051 `Literal["foo"]` is redundant in a union with `str`
5 | B: TypeAlias = typing.Union[Literal[b"bar", b"foo"], bytes, str] 5 | B: TypeAlias = typing.Union[Literal[b"bar", b"foo"], bytes, str]
6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]] 6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]]
| |
= help: Replace `Literal["foo"] | str` with `str`
PYI051 `Literal[b"bar"]` is redundant in a union with `bytes` PYI051 `Literal[b"bar"]` is redundant in a union with `bytes`
--> PYI051.pyi:5:37 --> PYI051.pyi:5:37
@ -21,6 +22,7 @@ PYI051 `Literal[b"bar"]` is redundant in a union with `bytes`
6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]] 6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]]
7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int] 7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
| |
= help: Replace `typing.Union[Literal[b"bar"], bytes]` with `bytes`
PYI051 `Literal[b"foo"]` is redundant in a union with `bytes` PYI051 `Literal[b"foo"]` is redundant in a union with `bytes`
--> PYI051.pyi:5:45 --> PYI051.pyi:5:45
@ -31,6 +33,7 @@ PYI051 `Literal[b"foo"]` is redundant in a union with `bytes`
6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]] 6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]]
7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int] 7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
| |
= help: Replace `typing.Union[Literal[b"foo"], bytes]` with `bytes`
PYI051 `Literal[5]` is redundant in a union with `int` PYI051 `Literal[5]` is redundant in a union with `int`
--> PYI051.pyi:6:37 --> PYI051.pyi:6:37
@ -42,6 +45,7 @@ PYI051 `Literal[5]` is redundant in a union with `int`
7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int] 7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]] 8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]]
| |
= help: Replace `typing.Union[Literal[5], int]` with `int`
PYI051 `Literal["foo"]` is redundant in a union with `str` PYI051 `Literal["foo"]` is redundant in a union with `str`
--> PYI051.pyi:6:67 --> PYI051.pyi:6:67
@ -53,6 +57,7 @@ PYI051 `Literal["foo"]` is redundant in a union with `str`
7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int] 7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]] 8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]]
| |
= help: Replace `typing.Union[Literal["foo"], str]` with `str`
PYI051 `Literal[b"str_bytes"]` is redundant in a union with `bytes` PYI051 `Literal[b"str_bytes"]` is redundant in a union with `bytes`
--> PYI051.pyi:7:37 --> PYI051.pyi:7:37
@ -64,6 +69,7 @@ PYI051 `Literal[b"str_bytes"]` is redundant in a union with `bytes`
8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]] 8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]]
9 | F: TypeAlias = typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]] 9 | F: TypeAlias = typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
| |
= help: Replace `typing.Union[Literal[b"str_bytes"], bytes]` with `bytes`
PYI051 `Literal[42]` is redundant in a union with `int` PYI051 `Literal[42]` is redundant in a union with `int`
--> PYI051.pyi:7:51 --> PYI051.pyi:7:51
@ -75,6 +81,7 @@ PYI051 `Literal[42]` is redundant in a union with `int`
8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]] 8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]]
9 | F: TypeAlias = typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]] 9 | F: TypeAlias = typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
| |
= help: Replace `typing.Union[Literal[42], int]` with `int`
PYI051 `Literal["foo"]` is redundant in a union with `str` PYI051 `Literal["foo"]` is redundant in a union with `str`
--> PYI051.pyi:8:76 --> PYI051.pyi:8:76
@ -86,6 +93,7 @@ PYI051 `Literal["foo"]` is redundant in a union with `str`
9 | F: TypeAlias = typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]] 9 | F: TypeAlias = typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
10 | G: typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]] 10 | G: typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
| |
= help: Replace `typing.Union[Literal["foo"], str]` with `str`
PYI051 `Literal["foo"]` is redundant in a union with `str` PYI051 `Literal["foo"]` is redundant in a union with `str`
--> PYI051.pyi:9:81 --> PYI051.pyi:9:81
@ -96,6 +104,7 @@ PYI051 `Literal["foo"]` is redundant in a union with `str`
| ^^^^^ | ^^^^^
10 | G: typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]] 10 | G: typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
| |
= help: Replace `typing.Union[Literal["foo"], str]` with `str`
PYI051 `Literal["foo"]` is redundant in a union with `str` PYI051 `Literal["foo"]` is redundant in a union with `str`
--> PYI051.pyi:10:69 --> PYI051.pyi:10:69
@ -107,6 +116,7 @@ PYI051 `Literal["foo"]` is redundant in a union with `str`
11 | 11 |
12 | def func(x: complex | Literal[1J], y: Union[Literal[3.14], float]): ... 12 | def func(x: complex | Literal[1J], y: Union[Literal[3.14], float]): ...
| |
= help: Replace `typing.Union[Literal["foo"], str]` with `str`
PYI051 `Literal[1J]` is redundant in a union with `complex` PYI051 `Literal[1J]` is redundant in a union with `complex`
--> PYI051.pyi:12:31 --> PYI051.pyi:12:31
@ -118,6 +128,7 @@ PYI051 `Literal[1J]` is redundant in a union with `complex`
13 | 13 |
14 | # OK 14 | # OK
| |
= help: Replace `Literal[1J] | complex` with `complex`
PYI051 `Literal[3.14]` is redundant in a union with `float` PYI051 `Literal[3.14]` is redundant in a union with `float`
--> PYI051.pyi:12:53 --> PYI051.pyi:12:53
@ -129,3 +140,4 @@ PYI051 `Literal[3.14]` is redundant in a union with `float`
13 | 13 |
14 | # OK 14 | # OK
| |
= help: Replace `typing.Union[Literal[3.14], float]` with `float`

View File

@ -0,0 +1,144 @@
---
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
assertion_line: 137
---
PYI051 `Literal["foo"]` is redundant in a union with `str`
--> PYI051.pyi:4:18
|
2 | from typing import Literal, TypeAlias, Union
3 |
4 | A: str | Literal["foo"]
| ^^^^^
5 | B: TypeAlias = typing.Union[Literal[b"bar", b"foo"], bytes, str]
6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]]
|
help: Replace `Literal["foo"] | str` with `str`
PYI051 `Literal[b"bar"]` is redundant in a union with `bytes`
--> PYI051.pyi:5:37
|
4 | A: str | Literal["foo"]
5 | B: TypeAlias = typing.Union[Literal[b"bar", b"foo"], bytes, str]
| ^^^^^^
6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]]
7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
|
help: Replace `typing.Union[Literal[b"bar"], bytes]` with `bytes`
PYI051 `Literal[b"foo"]` is redundant in a union with `bytes`
--> PYI051.pyi:5:45
|
4 | A: str | Literal["foo"]
5 | B: TypeAlias = typing.Union[Literal[b"bar", b"foo"], bytes, str]
| ^^^^^^
6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]]
7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
|
help: Replace `typing.Union[Literal[b"foo"], bytes]` with `bytes`
PYI051 `Literal[5]` is redundant in a union with `int`
--> PYI051.pyi:6:37
|
4 | A: str | Literal["foo"]
5 | B: TypeAlias = typing.Union[Literal[b"bar", b"foo"], bytes, str]
6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]]
| ^
7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]]
|
help: Replace `typing.Union[Literal[5], int]` with `int`
PYI051 `Literal["foo"]` is redundant in a union with `str`
--> PYI051.pyi:6:67
|
4 | A: str | Literal["foo"]
5 | B: TypeAlias = typing.Union[Literal[b"bar", b"foo"], bytes, str]
6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]]
| ^^^^^
7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]]
|
help: Replace `typing.Union[Literal["foo"], str]` with `str`
PYI051 `Literal[b"str_bytes"]` is redundant in a union with `bytes`
--> PYI051.pyi:7:37
|
5 | B: TypeAlias = typing.Union[Literal[b"bar", b"foo"], bytes, str]
6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]]
7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
| ^^^^^^^^^^^^
8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]]
9 | F: TypeAlias = typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
|
help: Replace `typing.Union[Literal[b"str_bytes"], bytes]` with `bytes`
PYI051 `Literal[42]` is redundant in a union with `int`
--> PYI051.pyi:7:51
|
5 | B: TypeAlias = typing.Union[Literal[b"bar", b"foo"], bytes, str]
6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]]
7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
| ^^
8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]]
9 | F: TypeAlias = typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
|
help: Replace `typing.Union[Literal[42], int]` with `int`
PYI051 `Literal["foo"]` is redundant in a union with `str`
--> PYI051.pyi:8:76
|
6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]]
7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]]
| ^^^^^
9 | F: TypeAlias = typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
10 | G: typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
|
help: Replace `typing.Union[Literal["foo"], str]` with `str`
PYI051 `Literal["foo"]` is redundant in a union with `str`
--> PYI051.pyi:9:81
|
7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]]
9 | F: TypeAlias = typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
| ^^^^^
10 | G: typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
|
help: Replace `typing.Union[Literal["foo"], str]` with `str`
PYI051 `Literal["foo"]` is redundant in a union with `str`
--> PYI051.pyi:10:69
|
8 | E: TypeAlias = typing.Union[typing.Union[typing.Union[typing.Union[Literal["foo"], str]]]]
9 | F: TypeAlias = typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
10 | G: typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
| ^^^^^
11 |
12 | def func(x: complex | Literal[1J], y: Union[Literal[3.14], float]): ...
|
help: Replace `typing.Union[Literal["foo"], str]` with `str`
PYI051 `Literal[1J]` is redundant in a union with `complex`
--> PYI051.pyi:12:31
|
10 | G: typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
11 |
12 | def func(x: complex | Literal[1J], y: Union[Literal[3.14], float]): ...
| ^^
13 |
14 | # OK
|
help: Replace `Literal[1J] | complex` with `complex`
PYI051 `Literal[3.14]` is redundant in a union with `float`
--> PYI051.pyi:12:53
|
10 | G: typing.Union[str, typing.Union[typing.Union[typing.Union[Literal["foo"], int]]]]
11 |
12 | def func(x: complex | Literal[1J], y: Union[Literal[3.14], float]): ...
| ^^^^
13 |
14 | # OK
|
help: Replace `typing.Union[Literal[3.14], float]` with `float`