-
Notifications
You must be signed in to change notification settings - Fork 943
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a rule to detect string members in runtime-evaluated unions
- Loading branch information
1 parent
d1a7bc3
commit e777927
Showing
8 changed files
with
128 additions
and
2 deletions.
There are no files selected for viewing
2 changes: 2 additions & 0 deletions
2
crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TCH006.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
x: "int" | str | ||
x: ("int" | str) | "bool" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 2 additions & 0 deletions
2
crates/ruff_linter/src/rules/flake8_type_checking/rules/mod.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
pub(crate) use empty_type_checking_block::*; | ||
pub(crate) use runtime_import_in_type_checking_block::*; | ||
pub(crate) use runtime_string_union::*; | ||
pub(crate) use typing_only_runtime_import::*; | ||
|
||
mod empty_type_checking_block; | ||
mod runtime_import_in_type_checking_block; | ||
mod runtime_string_union; | ||
mod typing_only_runtime_import; |
90 changes: 90 additions & 0 deletions
90
crates/ruff_linter/src/rules/flake8_type_checking/rules/runtime_string_union.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
use ruff_diagnostics::{Diagnostic, Violation}; | ||
use ruff_macros::{derive_message_formats, violation}; | ||
use ruff_python_ast as ast; | ||
use ruff_python_ast::{Expr, Operator}; | ||
use ruff_text_size::Ranged; | ||
|
||
use crate::checkers::ast::Checker; | ||
|
||
/// ## What it does | ||
/// Checks for the presence of string literals in `X | Y`-style union types. | ||
/// | ||
/// ## Why is this bad? | ||
/// [PEP 604] introduced a new syntax for union type annotations based on the | ||
/// `|` operator. | ||
/// | ||
/// While Python's type annotations can typically be wrapped in strings to | ||
/// avoid runtime evaluation, the use of a string member within an `X | Y`-style | ||
/// union type will cause a runtime error. | ||
/// | ||
/// Instead, remove the quotes, wrap the _entire_ union in quotes, or use | ||
/// `from __future__ import annotations` to disable runtime evaluation of | ||
/// annotations entirely. | ||
/// | ||
/// ## Example | ||
/// ```python | ||
/// var: str | "int" | ||
/// ``` | ||
/// | ||
/// Use instead: | ||
/// ```python | ||
/// var: str | int | ||
/// ``` | ||
/// | ||
/// ## References | ||
/// - [PEP 535](https://peps.python.org/pep-0563/) | ||
/// - [PEP 604](https://peps.python.org/pep-0604/) | ||
/// | ||
/// [PEP 604]: https://peps.python.org/pep-0604/ | ||
#[violation] | ||
pub struct RuntimeStringUnion; | ||
|
||
impl Violation for RuntimeStringUnion { | ||
#[derive_message_formats] | ||
fn message(&self) -> String { | ||
format!("Invalid string member in `X | Y`-style union type") | ||
} | ||
} | ||
|
||
/// TCH006 | ||
pub(crate) fn runtime_string_union(checker: &mut Checker, expr: &Expr) { | ||
if !checker.semantic().in_type_definition() { | ||
return; | ||
} | ||
|
||
if !checker.semantic().execution_context().is_runtime() { | ||
return; | ||
} | ||
|
||
// Search for strings within the binary operator. | ||
let mut strings = Vec::new(); | ||
traverse_op(expr, &mut strings); | ||
|
||
for string in strings { | ||
checker | ||
.diagnostics | ||
.push(Diagnostic::new(RuntimeStringUnion, string.range())); | ||
} | ||
} | ||
|
||
/// Collect all string members in possibly-nested binary `|` expressions. | ||
fn traverse_op<'a>(expr: &'a Expr, strings: &mut Vec<&'a Expr>) { | ||
match expr { | ||
Expr::StringLiteral(_) => { | ||
strings.push(expr); | ||
} | ||
Expr::BytesLiteral(_) => { | ||
strings.push(expr); | ||
} | ||
Expr::BinOp(ast::ExprBinOp { | ||
left, | ||
right, | ||
op: Operator::BitOr, | ||
.. | ||
}) => { | ||
traverse_op(left, strings); | ||
traverse_op(right, strings); | ||
} | ||
_ => {} | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
...hots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TCH006.py.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
--- | ||
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs | ||
--- | ||
TCH006.py:1:4: TCH006 Invalid string member in `X | Y`-style union type | ||
| | ||
1 | x: "int" | str | ||
| ^^^^^ TCH006 | ||
2 | x: ("int" | str) | "bool" | ||
| | ||
|
||
TCH006.py:2:5: TCH006 Invalid string member in `X | Y`-style union type | ||
| | ||
1 | x: "int" | str | ||
2 | x: ("int" | str) | "bool" | ||
| ^^^^^ TCH006 | ||
| | ||
|
||
TCH006.py:2:20: TCH006 Invalid string member in `X | Y`-style union type | ||
| | ||
1 | x: "int" | str | ||
2 | x: ("int" | str) | "bool" | ||
| ^^^^^^ TCH006 | ||
| | ||
|
||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.