-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[
flake8-type-checking
] Adds implementation for TC006 (#14511)
Co-authored-by: Micha Reiser <[email protected]>
- Loading branch information
1 parent
b80de52
commit e25e704
Showing
9 changed files
with
295 additions
and
0 deletions.
There are no files selected for viewing
62 changes: 62 additions & 0 deletions
62
crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TC006.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,62 @@ | ||
def f(): | ||
from typing import cast | ||
|
||
cast(int, 3.0) # TC006 | ||
|
||
|
||
def f(): | ||
from typing import cast | ||
|
||
cast(list[tuple[bool | float | int | str]], 3.0) # TC006 | ||
|
||
|
||
def f(): | ||
from typing import Union, cast | ||
|
||
cast(list[tuple[Union[bool, float, int, str]]], 3.0) # TC006 | ||
|
||
|
||
def f(): | ||
from typing import cast | ||
|
||
cast("int", 3.0) # OK | ||
|
||
|
||
def f(): | ||
from typing import cast | ||
|
||
cast("list[tuple[bool | float | int | str]]", 3.0) # OK | ||
|
||
|
||
def f(): | ||
from typing import Union, cast | ||
|
||
cast("list[tuple[Union[bool, float, int, str]]]", 3.0) # OK | ||
|
||
|
||
def f(): | ||
from typing import cast as typecast | ||
|
||
typecast(int, 3.0) # TC006 | ||
|
||
|
||
def f(): | ||
import typing | ||
|
||
typing.cast(int, 3.0) # TC006 | ||
|
||
|
||
def f(): | ||
import typing as t | ||
|
||
t.cast(t.Literal["3.0", '3'], 3.0) # TC006 | ||
|
||
|
||
def f(): | ||
from typing import cast | ||
|
||
cast( | ||
int # TC006 (unsafe, because it will get rid of this comment) | ||
| None, | ||
3.0 | ||
) |
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
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,9 +1,11 @@ | ||
pub(crate) use empty_type_checking_block::*; | ||
pub(crate) use runtime_cast_value::*; | ||
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_cast_value; | ||
mod runtime_import_in_type_checking_block; | ||
mod runtime_string_union; | ||
mod typing_only_runtime_import; |
69 changes: 69 additions & 0 deletions
69
crates/ruff_linter/src/rules/flake8_type_checking/rules/runtime_cast_value.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,69 @@ | ||
use ruff_python_ast::Expr; | ||
|
||
use ruff_diagnostics::{Diagnostic, Fix, FixAvailability, Violation}; | ||
use ruff_macros::{derive_message_formats, violation}; | ||
use ruff_text_size::Ranged; | ||
|
||
use crate::checkers::ast::Checker; | ||
use crate::rules::flake8_type_checking::helpers::quote_type_expression; | ||
|
||
/// ## What it does | ||
/// Checks for an unquoted type expression in `typing.cast()` calls. | ||
/// | ||
/// ## Why is this bad? | ||
/// `typing.cast()` does not do anything at runtime, so the time spent | ||
/// on evaluating the type expression is wasted. | ||
/// | ||
/// ## Example | ||
/// ```python | ||
/// from typing import cast | ||
/// | ||
/// x = cast(dict[str, int], foo) | ||
/// ``` | ||
/// | ||
/// Use instead: | ||
/// ```python | ||
/// from typing import cast | ||
/// | ||
/// x = cast("dict[str, int]", foo) | ||
/// ``` | ||
/// | ||
/// ## Fix safety | ||
/// This fix is safe as long as the type expression doesn't span multiple | ||
/// lines and includes comments on any of the lines apart from the last one. | ||
#[violation] | ||
pub struct RuntimeCastValue; | ||
|
||
impl Violation for RuntimeCastValue { | ||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; | ||
|
||
#[derive_message_formats] | ||
fn message(&self) -> String { | ||
"Add quotes to type expression in `typing.cast()`".to_string() | ||
} | ||
|
||
fn fix_title(&self) -> Option<String> { | ||
Some("Add quotes".to_string()) | ||
} | ||
} | ||
|
||
/// TC006 | ||
pub(crate) fn runtime_cast_value(checker: &mut Checker, type_expr: &Expr) { | ||
if type_expr.is_string_literal_expr() { | ||
return; | ||
} | ||
|
||
let mut diagnostic = Diagnostic::new(RuntimeCastValue, type_expr.range()); | ||
let edit = quote_type_expression(type_expr, checker.semantic(), checker.stylist()).ok(); | ||
if let Some(edit) = edit { | ||
if checker | ||
.comment_ranges() | ||
.has_comments(type_expr, checker.source()) | ||
{ | ||
diagnostic.set_fix(Fix::unsafe_edit(edit)); | ||
} else { | ||
diagnostic.set_fix(Fix::safe_edit(edit)); | ||
} | ||
} | ||
checker.diagnostics.push(diagnostic); | ||
} |
138 changes: 138 additions & 0 deletions
138
...apshots/ruff_linter__rules__flake8_type_checking__tests__runtime-cast-value_TC006.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,138 @@ | ||
--- | ||
source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs | ||
--- | ||
TC006.py:4:10: TC006 [*] Add quotes to type expression in `typing.cast()` | ||
| | ||
2 | from typing import cast | ||
3 | | ||
4 | cast(int, 3.0) # TC006 | ||
| ^^^ TC006 | ||
| | ||
= help: Add quotes | ||
|
||
ℹ Safe fix | ||
1 1 | def f(): | ||
2 2 | from typing import cast | ||
3 3 | | ||
4 |- cast(int, 3.0) # TC006 | ||
4 |+ cast("int", 3.0) # TC006 | ||
5 5 | | ||
6 6 | | ||
7 7 | def f(): | ||
|
||
TC006.py:10:10: TC006 [*] Add quotes to type expression in `typing.cast()` | ||
| | ||
8 | from typing import cast | ||
9 | | ||
10 | cast(list[tuple[bool | float | int | str]], 3.0) # TC006 | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TC006 | ||
| | ||
= help: Add quotes | ||
|
||
ℹ Safe fix | ||
7 7 | def f(): | ||
8 8 | from typing import cast | ||
9 9 | | ||
10 |- cast(list[tuple[bool | float | int | str]], 3.0) # TC006 | ||
10 |+ cast("list[tuple[bool | float | int | str]]", 3.0) # TC006 | ||
11 11 | | ||
12 12 | | ||
13 13 | def f(): | ||
|
||
TC006.py:16:10: TC006 [*] Add quotes to type expression in `typing.cast()` | ||
| | ||
14 | from typing import Union, cast | ||
15 | | ||
16 | cast(list[tuple[Union[bool, float, int, str]]], 3.0) # TC006 | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TC006 | ||
| | ||
= help: Add quotes | ||
|
||
ℹ Safe fix | ||
13 13 | def f(): | ||
14 14 | from typing import Union, cast | ||
15 15 | | ||
16 |- cast(list[tuple[Union[bool, float, int, str]]], 3.0) # TC006 | ||
16 |+ cast("list[tuple[Union[bool, float, int, str]]]", 3.0) # TC006 | ||
17 17 | | ||
18 18 | | ||
19 19 | def f(): | ||
|
||
TC006.py:40:14: TC006 [*] Add quotes to type expression in `typing.cast()` | ||
| | ||
38 | from typing import cast as typecast | ||
39 | | ||
40 | typecast(int, 3.0) # TC006 | ||
| ^^^ TC006 | ||
| | ||
= help: Add quotes | ||
|
||
ℹ Safe fix | ||
37 37 | def f(): | ||
38 38 | from typing import cast as typecast | ||
39 39 | | ||
40 |- typecast(int, 3.0) # TC006 | ||
40 |+ typecast("int", 3.0) # TC006 | ||
41 41 | | ||
42 42 | | ||
43 43 | def f(): | ||
|
||
TC006.py:46:17: TC006 [*] Add quotes to type expression in `typing.cast()` | ||
| | ||
44 | import typing | ||
45 | | ||
46 | typing.cast(int, 3.0) # TC006 | ||
| ^^^ TC006 | ||
| | ||
= help: Add quotes | ||
|
||
ℹ Safe fix | ||
43 43 | def f(): | ||
44 44 | import typing | ||
45 45 | | ||
46 |- typing.cast(int, 3.0) # TC006 | ||
46 |+ typing.cast("int", 3.0) # TC006 | ||
47 47 | | ||
48 48 | | ||
49 49 | def f(): | ||
|
||
TC006.py:52:12: TC006 [*] Add quotes to type expression in `typing.cast()` | ||
| | ||
50 | import typing as t | ||
51 | | ||
52 | t.cast(t.Literal["3.0", '3'], 3.0) # TC006 | ||
| ^^^^^^^^^^^^^^^^^^^^^ TC006 | ||
| | ||
= help: Add quotes | ||
|
||
ℹ Safe fix | ||
49 49 | def f(): | ||
50 50 | import typing as t | ||
51 51 | | ||
52 |- t.cast(t.Literal["3.0", '3'], 3.0) # TC006 | ||
52 |+ t.cast("t.Literal['3.0', '3']", 3.0) # TC006 | ||
53 53 | | ||
54 54 | | ||
55 55 | def f(): | ||
|
||
TC006.py:59:9: TC006 [*] Add quotes to type expression in `typing.cast()` | ||
| | ||
58 | cast( | ||
59 | int # TC006 (unsafe, because it will get rid of this comment) | ||
| _________^ | ||
60 | | | None, | ||
| |______________^ TC006 | ||
61 | 3.0 | ||
62 | ) | ||
| | ||
= help: Add quotes | ||
|
||
ℹ Unsafe fix | ||
56 56 | from typing import cast | ||
57 57 | | ||
58 58 | cast( | ||
59 |- int # TC006 (unsafe, because it will get rid of this comment) | ||
60 |- | None, | ||
59 |+ "int | None", | ||
61 60 | 3.0 | ||
62 61 | ) |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.