mirror of https://github.com/astral-sh/ruff
Add quoted annotation rules
This commit is contained in:
parent
402b3c7f04
commit
603d3e1984
|
|
@ -0,0 +1,21 @@
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import module
|
||||
from module import Class
|
||||
|
||||
|
||||
def f(var: Class) -> Class:
|
||||
x: Class
|
||||
|
||||
|
||||
def f(var: module.Class) -> module.Class:
|
||||
x: module.Class
|
||||
|
||||
|
||||
def f():
|
||||
print(Class)
|
||||
|
||||
|
||||
def f():
|
||||
print(module.Class)
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
Class = ...
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from module import Class
|
||||
|
|
@ -4329,6 +4329,11 @@ impl<'a> Checker<'a> {
|
|||
ResolvedRead::Resolved(_) | ResolvedRead::ImplicitGlobal => {
|
||||
// Nothing to do.
|
||||
}
|
||||
ResolvedRead::TypingOnly(binding_id) => {
|
||||
if self.enabled(Rule::UnquotedAnnotation) {
|
||||
flake8_type_checking::rules::unquoted_annotation(self, binding_id, expr);
|
||||
}
|
||||
}
|
||||
ResolvedRead::WildcardImport => {
|
||||
// F405
|
||||
if self.enabled(Rule::UndefinedLocalWithImportStarUsage) {
|
||||
|
|
|
|||
|
|
@ -707,6 +707,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
|||
(Flake8TypeChecking, "003") => (RuleGroup::Unspecified, rules::flake8_type_checking::rules::TypingOnlyStandardLibraryImport),
|
||||
(Flake8TypeChecking, "004") => (RuleGroup::Unspecified, rules::flake8_type_checking::rules::RuntimeImportInTypeCheckingBlock),
|
||||
(Flake8TypeChecking, "005") => (RuleGroup::Unspecified, rules::flake8_type_checking::rules::EmptyTypeCheckingBlock),
|
||||
(Flake8TypeChecking, "200") => (RuleGroup::Unspecified, rules::flake8_type_checking::rules::UnquotedAnnotation),
|
||||
|
||||
// tryceratops
|
||||
(Tryceratops, "002") => (RuleGroup::Unspecified, rules::tryceratops::rules::RaiseVanillaClass),
|
||||
|
|
|
|||
|
|
@ -15,10 +15,13 @@ mod tests {
|
|||
use crate::test::{test_path, test_snippet};
|
||||
use crate::{assert_messages, settings};
|
||||
|
||||
#[test_case(Rule::TypingOnlyFirstPartyImport, Path::new("TCH001.py"))]
|
||||
#[test_case(Rule::TypingOnlyThirdPartyImport, Path::new("TCH002.py"))]
|
||||
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("TCH003.py"))]
|
||||
#[test_case(Rule::EmptyTypeCheckingBlock, Path::new("TCH005.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_1.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_10.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_11.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_12.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_13.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_14.pyi"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_2.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_3.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_4.py"))]
|
||||
|
|
@ -27,13 +30,12 @@ mod tests {
|
|||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_7.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_8.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_9.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_10.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_11.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_12.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_13.py"))]
|
||||
#[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_14.pyi"))]
|
||||
#[test_case(Rule::EmptyTypeCheckingBlock, Path::new("TCH005.py"))]
|
||||
#[test_case(Rule::TypingOnlyFirstPartyImport, Path::new("TCH001.py"))]
|
||||
#[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("TCH003.py"))]
|
||||
#[test_case(Rule::TypingOnlyThirdPartyImport, Path::new("TCH002.py"))]
|
||||
#[test_case(Rule::TypingOnlyThirdPartyImport, Path::new("strict.py"))]
|
||||
#[test_case(Rule::UnquotedAnnotation, Path::new("TCH200_0.py"))]
|
||||
#[test_case(Rule::UnquotedAnnotation, Path::new("TCH200_1.py"))]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
pub(crate) use empty_type_checking_block::*;
|
||||
pub(crate) use quoted_annotation::*;
|
||||
pub(crate) use runtime_import_in_type_checking_block::*;
|
||||
pub(crate) use typing_only_runtime_import::*;
|
||||
|
||||
mod empty_type_checking_block;
|
||||
mod quoted_annotation;
|
||||
mod runtime_import_in_type_checking_block;
|
||||
mod typing_only_runtime_import;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
use rustpython_parser::ast::{Expr, Ranged};
|
||||
|
||||
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_semantic::BindingId;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the presence of unnecessary quotes in type annotations.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// In Python, type annotations can be quoted to avoid forward references.
|
||||
/// However, if `from __future__ import annotations` is present, Python
|
||||
/// will always evaluate type annotations in a deferred manner, making
|
||||
/// the quotes unnecessary.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// from __future__ import annotations
|
||||
///
|
||||
///
|
||||
/// def foo(bar: "Bar") -> "Bar":
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// from __future__ import annotations
|
||||
///
|
||||
///
|
||||
/// def foo(bar: Bar) -> Bar:
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 563](https://peps.python.org/pep-0563/)
|
||||
/// - [Python documentation: `__future__`](https://docs.python.org/3/library/__future__.html#module-__future__)
|
||||
#[violation]
|
||||
pub struct UnquotedAnnotation {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Violation for UnquotedAnnotation {
|
||||
const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UnquotedAnnotation { name } = self;
|
||||
format!("Typing-only variable referenced in runtime annotation: `{name}`")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> Option<String> {
|
||||
Some("Add quotes".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// TCH200
|
||||
pub(crate) fn unquoted_annotation(checker: &mut Checker, binding_id: BindingId, expr: &Expr) {
|
||||
// If we're already in a quoted annotation, skip.
|
||||
if checker.semantic().in_deferred_type_definition() {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're in a typing-only context, skip.
|
||||
if checker.semantic().execution_context().is_typing() {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the reference resolved to a typing-only import, flag.
|
||||
if checker.semantic().bindings[binding_id].context.is_typing() {
|
||||
// Expand any attribute chains (e.g., flag `typing.List` in `typing.List[int]`).
|
||||
let mut expr = expr;
|
||||
for parent in checker.semantic().expr_ancestors() {
|
||||
if parent.is_attribute_expr() {
|
||||
expr = parent;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
UnquotedAnnotation {
|
||||
name: checker.locator.slice(expr.range()).to_string(),
|
||||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
// We can only _fix_ this if we're in a type annotation.
|
||||
if checker.semantic().in_runtime_annotation() {
|
||||
diagnostic.set_fix(Fix::automatic(Edit::range_replacement(
|
||||
format!("\"{}\"", checker.locator.slice(expr.range()).to_string()),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
|
||||
---
|
||||
TCH200_0.py:8:12: TCH200 [*] Typing-only variable referenced in runtime annotation: `Class`
|
||||
|
|
||||
8 | def f(var: Class) -> Class:
|
||||
| ^^^^^ TCH200
|
||||
9 | x: Class
|
||||
|
|
||||
= help: Add quotes
|
||||
|
||||
ℹ Fix
|
||||
5 5 | from module import Class
|
||||
6 6 |
|
||||
7 7 |
|
||||
8 |-def f(var: Class) -> Class:
|
||||
8 |+def f(var: "Class") -> Class:
|
||||
9 9 | x: Class
|
||||
10 10 |
|
||||
11 11 |
|
||||
|
||||
TCH200_0.py:8:22: TCH200 [*] Typing-only variable referenced in runtime annotation: `Class`
|
||||
|
|
||||
8 | def f(var: Class) -> Class:
|
||||
| ^^^^^ TCH200
|
||||
9 | x: Class
|
||||
|
|
||||
= help: Add quotes
|
||||
|
||||
ℹ Fix
|
||||
5 5 | from module import Class
|
||||
6 6 |
|
||||
7 7 |
|
||||
8 |-def f(var: Class) -> Class:
|
||||
8 |+def f(var: Class) -> "Class":
|
||||
9 9 | x: Class
|
||||
10 10 |
|
||||
11 11 |
|
||||
|
||||
TCH200_0.py:12:12: TCH200 [*] Typing-only variable referenced in runtime annotation: `module.Class`
|
||||
|
|
||||
12 | def f(var: module.Class) -> module.Class:
|
||||
| ^^^^^^^^^^^^ TCH200
|
||||
13 | x: module.Class
|
||||
|
|
||||
= help: Add quotes
|
||||
|
||||
ℹ Fix
|
||||
9 9 | x: Class
|
||||
10 10 |
|
||||
11 11 |
|
||||
12 |-def f(var: module.Class) -> module.Class:
|
||||
12 |+def f(var: "module.Class") -> module.Class:
|
||||
13 13 | x: module.Class
|
||||
14 14 |
|
||||
15 15 |
|
||||
|
||||
TCH200_0.py:12:29: TCH200 [*] Typing-only variable referenced in runtime annotation: `module.Class`
|
||||
|
|
||||
12 | def f(var: module.Class) -> module.Class:
|
||||
| ^^^^^^^^^^^^ TCH200
|
||||
13 | x: module.Class
|
||||
|
|
||||
= help: Add quotes
|
||||
|
||||
ℹ Fix
|
||||
9 9 | x: Class
|
||||
10 10 |
|
||||
11 11 |
|
||||
12 |-def f(var: module.Class) -> module.Class:
|
||||
12 |+def f(var: module.Class) -> "module.Class":
|
||||
13 13 | x: module.Class
|
||||
14 14 |
|
||||
15 15 |
|
||||
|
||||
TCH200_0.py:17:11: TCH200 Typing-only variable referenced in runtime annotation: `Class`
|
||||
|
|
||||
16 | def f():
|
||||
17 | print(Class)
|
||||
| ^^^^^ TCH200
|
||||
|
|
||||
= help: Add quotes
|
||||
|
||||
TCH200_0.py:21:11: TCH200 Typing-only variable referenced in runtime annotation: `module.Class`
|
||||
|
|
||||
20 | def f():
|
||||
21 | print(module.Class)
|
||||
| ^^^^^^^^^^^^ TCH200
|
||||
|
|
||||
= help: Add quotes
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
|
||||
---
|
||||
|
||||
|
|
@ -1,559 +0,0 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
UP037.py:18:14: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
18 | def foo(var: "MyClass") -> "MyClass":
|
||||
| ^^^^^^^^^ UP037
|
||||
19 | x: "MyClass"
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
15 15 | from mypy_extensions import Arg, DefaultArg, DefaultNamedArg, NamedArg, VarArg
|
||||
16 16 |
|
||||
17 17 |
|
||||
18 |-def foo(var: "MyClass") -> "MyClass":
|
||||
18 |+def foo(var: MyClass) -> "MyClass":
|
||||
19 19 | x: "MyClass"
|
||||
20 20 |
|
||||
21 21 |
|
||||
|
||||
UP037.py:18:28: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
18 | def foo(var: "MyClass") -> "MyClass":
|
||||
| ^^^^^^^^^ UP037
|
||||
19 | x: "MyClass"
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
15 15 | from mypy_extensions import Arg, DefaultArg, DefaultNamedArg, NamedArg, VarArg
|
||||
16 16 |
|
||||
17 17 |
|
||||
18 |-def foo(var: "MyClass") -> "MyClass":
|
||||
18 |+def foo(var: "MyClass") -> MyClass:
|
||||
19 19 | x: "MyClass"
|
||||
20 20 |
|
||||
21 21 |
|
||||
|
||||
UP037.py:19:8: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
18 | def foo(var: "MyClass") -> "MyClass":
|
||||
19 | x: "MyClass"
|
||||
| ^^^^^^^^^ UP037
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
16 16 |
|
||||
17 17 |
|
||||
18 18 | def foo(var: "MyClass") -> "MyClass":
|
||||
19 |- x: "MyClass"
|
||||
19 |+ x: MyClass
|
||||
20 20 |
|
||||
21 21 |
|
||||
22 22 | def foo(*, inplace: "bool"):
|
||||
|
||||
UP037.py:22:21: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
22 | def foo(*, inplace: "bool"):
|
||||
| ^^^^^^ UP037
|
||||
23 | pass
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
19 19 | x: "MyClass"
|
||||
20 20 |
|
||||
21 21 |
|
||||
22 |-def foo(*, inplace: "bool"):
|
||||
22 |+def foo(*, inplace: bool):
|
||||
23 23 | pass
|
||||
24 24 |
|
||||
25 25 |
|
||||
|
||||
UP037.py:26:16: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
26 | def foo(*args: "str", **kwargs: "int"):
|
||||
| ^^^^^ UP037
|
||||
27 | pass
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
23 23 | pass
|
||||
24 24 |
|
||||
25 25 |
|
||||
26 |-def foo(*args: "str", **kwargs: "int"):
|
||||
26 |+def foo(*args: str, **kwargs: "int"):
|
||||
27 27 | pass
|
||||
28 28 |
|
||||
29 29 |
|
||||
|
||||
UP037.py:26:33: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
26 | def foo(*args: "str", **kwargs: "int"):
|
||||
| ^^^^^ UP037
|
||||
27 | pass
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
23 23 | pass
|
||||
24 24 |
|
||||
25 25 |
|
||||
26 |-def foo(*args: "str", **kwargs: "int"):
|
||||
26 |+def foo(*args: "str", **kwargs: int):
|
||||
27 27 | pass
|
||||
28 28 |
|
||||
29 29 |
|
||||
|
||||
UP037.py:30:10: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
30 | x: Tuple["MyClass"]
|
||||
| ^^^^^^^^^ UP037
|
||||
31 |
|
||||
32 | x: Callable[["MyClass"], None]
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
27 27 | pass
|
||||
28 28 |
|
||||
29 29 |
|
||||
30 |-x: Tuple["MyClass"]
|
||||
30 |+x: Tuple[MyClass]
|
||||
31 31 |
|
||||
32 32 | x: Callable[["MyClass"], None]
|
||||
33 33 |
|
||||
|
||||
UP037.py:32:14: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
30 | x: Tuple["MyClass"]
|
||||
31 |
|
||||
32 | x: Callable[["MyClass"], None]
|
||||
| ^^^^^^^^^ UP037
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
29 29 |
|
||||
30 30 | x: Tuple["MyClass"]
|
||||
31 31 |
|
||||
32 |-x: Callable[["MyClass"], None]
|
||||
32 |+x: Callable[[MyClass], None]
|
||||
33 33 |
|
||||
34 34 |
|
||||
35 35 | class Foo(NamedTuple):
|
||||
|
||||
UP037.py:36:8: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
35 | class Foo(NamedTuple):
|
||||
36 | x: "MyClass"
|
||||
| ^^^^^^^^^ UP037
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
33 33 |
|
||||
34 34 |
|
||||
35 35 | class Foo(NamedTuple):
|
||||
36 |- x: "MyClass"
|
||||
36 |+ x: MyClass
|
||||
37 37 |
|
||||
38 38 |
|
||||
39 39 | class D(TypedDict):
|
||||
|
||||
UP037.py:40:27: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
39 | class D(TypedDict):
|
||||
40 | E: TypedDict("E", foo="int", total=False)
|
||||
| ^^^^^ UP037
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
37 37 |
|
||||
38 38 |
|
||||
39 39 | class D(TypedDict):
|
||||
40 |- E: TypedDict("E", foo="int", total=False)
|
||||
40 |+ E: TypedDict("E", foo=int, total=False)
|
||||
41 41 |
|
||||
42 42 |
|
||||
43 43 | class D(TypedDict):
|
||||
|
||||
UP037.py:44:31: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
43 | class D(TypedDict):
|
||||
44 | E: TypedDict("E", {"foo": "int"})
|
||||
| ^^^^^ UP037
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
41 41 |
|
||||
42 42 |
|
||||
43 43 | class D(TypedDict):
|
||||
44 |- E: TypedDict("E", {"foo": "int"})
|
||||
44 |+ E: TypedDict("E", {"foo": int})
|
||||
45 45 |
|
||||
46 46 |
|
||||
47 47 | x: Annotated["str", "metadata"]
|
||||
|
||||
UP037.py:47:14: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
47 | x: Annotated["str", "metadata"]
|
||||
| ^^^^^ UP037
|
||||
48 |
|
||||
49 | x: Arg("str", "name")
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
44 44 | E: TypedDict("E", {"foo": "int"})
|
||||
45 45 |
|
||||
46 46 |
|
||||
47 |-x: Annotated["str", "metadata"]
|
||||
47 |+x: Annotated[str, "metadata"]
|
||||
48 48 |
|
||||
49 49 | x: Arg("str", "name")
|
||||
50 50 |
|
||||
|
||||
UP037.py:49:8: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
47 | x: Annotated["str", "metadata"]
|
||||
48 |
|
||||
49 | x: Arg("str", "name")
|
||||
| ^^^^^ UP037
|
||||
50 |
|
||||
51 | x: DefaultArg("str", "name")
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
46 46 |
|
||||
47 47 | x: Annotated["str", "metadata"]
|
||||
48 48 |
|
||||
49 |-x: Arg("str", "name")
|
||||
49 |+x: Arg(str, "name")
|
||||
50 50 |
|
||||
51 51 | x: DefaultArg("str", "name")
|
||||
52 52 |
|
||||
|
||||
UP037.py:51:15: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
49 | x: Arg("str", "name")
|
||||
50 |
|
||||
51 | x: DefaultArg("str", "name")
|
||||
| ^^^^^ UP037
|
||||
52 |
|
||||
53 | x: NamedArg("str", "name")
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
48 48 |
|
||||
49 49 | x: Arg("str", "name")
|
||||
50 50 |
|
||||
51 |-x: DefaultArg("str", "name")
|
||||
51 |+x: DefaultArg(str, "name")
|
||||
52 52 |
|
||||
53 53 | x: NamedArg("str", "name")
|
||||
54 54 |
|
||||
|
||||
UP037.py:53:13: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
51 | x: DefaultArg("str", "name")
|
||||
52 |
|
||||
53 | x: NamedArg("str", "name")
|
||||
| ^^^^^ UP037
|
||||
54 |
|
||||
55 | x: DefaultNamedArg("str", "name")
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
50 50 |
|
||||
51 51 | x: DefaultArg("str", "name")
|
||||
52 52 |
|
||||
53 |-x: NamedArg("str", "name")
|
||||
53 |+x: NamedArg(str, "name")
|
||||
54 54 |
|
||||
55 55 | x: DefaultNamedArg("str", "name")
|
||||
56 56 |
|
||||
|
||||
UP037.py:55:20: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
53 | x: NamedArg("str", "name")
|
||||
54 |
|
||||
55 | x: DefaultNamedArg("str", "name")
|
||||
| ^^^^^ UP037
|
||||
56 |
|
||||
57 | x: DefaultNamedArg("str", name="name")
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
52 52 |
|
||||
53 53 | x: NamedArg("str", "name")
|
||||
54 54 |
|
||||
55 |-x: DefaultNamedArg("str", "name")
|
||||
55 |+x: DefaultNamedArg(str, "name")
|
||||
56 56 |
|
||||
57 57 | x: DefaultNamedArg("str", name="name")
|
||||
58 58 |
|
||||
|
||||
UP037.py:57:20: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
55 | x: DefaultNamedArg("str", "name")
|
||||
56 |
|
||||
57 | x: DefaultNamedArg("str", name="name")
|
||||
| ^^^^^ UP037
|
||||
58 |
|
||||
59 | x: VarArg("str")
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
54 54 |
|
||||
55 55 | x: DefaultNamedArg("str", "name")
|
||||
56 56 |
|
||||
57 |-x: DefaultNamedArg("str", name="name")
|
||||
57 |+x: DefaultNamedArg(str, name="name")
|
||||
58 58 |
|
||||
59 59 | x: VarArg("str")
|
||||
60 60 |
|
||||
|
||||
UP037.py:59:11: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
57 | x: DefaultNamedArg("str", name="name")
|
||||
58 |
|
||||
59 | x: VarArg("str")
|
||||
| ^^^^^ UP037
|
||||
60 |
|
||||
61 | x: List[List[List["MyClass"]]]
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
56 56 |
|
||||
57 57 | x: DefaultNamedArg("str", name="name")
|
||||
58 58 |
|
||||
59 |-x: VarArg("str")
|
||||
59 |+x: VarArg(str)
|
||||
60 60 |
|
||||
61 61 | x: List[List[List["MyClass"]]]
|
||||
62 62 |
|
||||
|
||||
UP037.py:61:19: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
59 | x: VarArg("str")
|
||||
60 |
|
||||
61 | x: List[List[List["MyClass"]]]
|
||||
| ^^^^^^^^^ UP037
|
||||
62 |
|
||||
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
58 58 |
|
||||
59 59 | x: VarArg("str")
|
||||
60 60 |
|
||||
61 |-x: List[List[List["MyClass"]]]
|
||||
61 |+x: List[List[List[MyClass]]]
|
||||
62 62 |
|
||||
63 63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 64 |
|
||||
|
||||
UP037.py:63:29: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
61 | x: List[List[List["MyClass"]]]
|
||||
62 |
|
||||
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
| ^^^^^ UP037
|
||||
64 |
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
60 60 |
|
||||
61 61 | x: List[List[List["MyClass"]]]
|
||||
62 62 |
|
||||
63 |-x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
63 |+x: NamedTuple("X", [("foo", int), ("bar", "str")])
|
||||
64 64 |
|
||||
65 65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 66 |
|
||||
|
||||
UP037.py:63:45: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
61 | x: List[List[List["MyClass"]]]
|
||||
62 |
|
||||
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
| ^^^^^ UP037
|
||||
64 |
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
60 60 |
|
||||
61 61 | x: List[List[List["MyClass"]]]
|
||||
62 62 |
|
||||
63 |-x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
63 |+x: NamedTuple("X", [("foo", "int"), ("bar", str)])
|
||||
64 64 |
|
||||
65 65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 66 |
|
||||
|
||||
UP037.py:65:29: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 |
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
| ^^^^^ UP037
|
||||
66 |
|
||||
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
62 62 |
|
||||
63 63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 64 |
|
||||
65 |-x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
65 |+x: NamedTuple("X", fields=[(foo, "int"), ("bar", "str")])
|
||||
66 66 |
|
||||
67 67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
68 68 |
|
||||
|
||||
UP037.py:65:36: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 |
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
| ^^^^^ UP037
|
||||
66 |
|
||||
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
62 62 |
|
||||
63 63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 64 |
|
||||
65 |-x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
65 |+x: NamedTuple("X", fields=[("foo", int), ("bar", "str")])
|
||||
66 66 |
|
||||
67 67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
68 68 |
|
||||
|
||||
UP037.py:65:45: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 |
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
| ^^^^^ UP037
|
||||
66 |
|
||||
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
62 62 |
|
||||
63 63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 64 |
|
||||
65 |-x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
65 |+x: NamedTuple("X", fields=[("foo", "int"), (bar, "str")])
|
||||
66 66 |
|
||||
67 67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
68 68 |
|
||||
|
||||
UP037.py:65:52: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 |
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
| ^^^^^ UP037
|
||||
66 |
|
||||
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
62 62 |
|
||||
63 63 | x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
64 64 |
|
||||
65 |-x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
65 |+x: NamedTuple("X", fields=[("foo", "int"), ("bar", str)])
|
||||
66 66 |
|
||||
67 67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
68 68 |
|
||||
|
||||
UP037.py:67:24: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 |
|
||||
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
| ^^^ UP037
|
||||
68 |
|
||||
69 | X: MyCallable("X")
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
64 64 |
|
||||
65 65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 66 |
|
||||
67 |-x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
67 |+x: NamedTuple(typename=X, fields=[("foo", "int")])
|
||||
68 68 |
|
||||
69 69 | X: MyCallable("X")
|
||||
70 70 |
|
||||
|
||||
UP037.py:67:38: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 |
|
||||
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
| ^^^^^ UP037
|
||||
68 |
|
||||
69 | X: MyCallable("X")
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
64 64 |
|
||||
65 65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 66 |
|
||||
67 |-x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
67 |+x: NamedTuple(typename="X", fields=[(foo, "int")])
|
||||
68 68 |
|
||||
69 69 | X: MyCallable("X")
|
||||
70 70 |
|
||||
|
||||
UP037.py:67:45: UP037 [*] Remove quotes from type annotation
|
||||
|
|
||||
65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 |
|
||||
67 | x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
| ^^^^^ UP037
|
||||
68 |
|
||||
69 | X: MyCallable("X")
|
||||
|
|
||||
= help: Remove quotes
|
||||
|
||||
ℹ Fix
|
||||
64 64 |
|
||||
65 65 | x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
66 66 |
|
||||
67 |-x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
67 |+x: NamedTuple(typename="X", fields=[("foo", int)])
|
||||
68 68 |
|
||||
69 69 | X: MyCallable("X")
|
||||
70 70 |
|
||||
|
||||
|
||||
|
|
@ -275,6 +275,7 @@ impl<'a> SemanticModel<'a> {
|
|||
|
||||
let mut seen_function = false;
|
||||
let mut import_starred = false;
|
||||
let mut annotation_id = None;
|
||||
for (index, scope_id) in self.scopes.ancestor_ids(self.scope_id).enumerate() {
|
||||
let scope = &self.scopes[scope_id];
|
||||
if scope.kind.is_class() {
|
||||
|
|
@ -293,7 +294,7 @@ impl<'a> SemanticModel<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(binding_id) = scope.get(symbol) {
|
||||
for binding_id in scope.get_all(symbol) {
|
||||
// Mark the binding as used.
|
||||
let context = self.execution_context();
|
||||
let reference_id = self.references.push(self.scope_id, range, context);
|
||||
|
|
@ -305,6 +306,27 @@ impl<'a> SemanticModel<'a> {
|
|||
self.bindings[binding_id].references.push(reference_id);
|
||||
}
|
||||
|
||||
// If we're in a runtime context, but the binding is typing-only, don't treat
|
||||
// it as resolved. For example, given:
|
||||
//
|
||||
// ```python
|
||||
// from typing import TYPE_CHECKING
|
||||
//
|
||||
// if TYPE_CHECKING:
|
||||
// from foo import Foo
|
||||
//
|
||||
// Foo()
|
||||
// ```
|
||||
//
|
||||
// The `Foo` in `Foo()` should be treated as unresolved at runtime, but the `Foo` in
|
||||
// `from foo import Foo` should be treated as used.
|
||||
if self.bindings[binding_id].context.is_typing() {
|
||||
if self.execution_context().is_runtime() {
|
||||
annotation_id = Some(binding_id);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
match self.bindings[binding_id].kind {
|
||||
// If it's a type annotation, don't treat it as resolved. For example, given:
|
||||
//
|
||||
|
|
@ -405,7 +427,9 @@ impl<'a> SemanticModel<'a> {
|
|||
import_starred = import_starred || scope.uses_star_imports();
|
||||
}
|
||||
|
||||
if import_starred {
|
||||
if let Some(annotation_id) = annotation_id {
|
||||
ResolvedRead::TypingOnly(annotation_id)
|
||||
} else if import_starred {
|
||||
ResolvedRead::WildcardImport
|
||||
} else {
|
||||
ResolvedRead::NotFound
|
||||
|
|
@ -1380,6 +1404,23 @@ pub enum ResolvedRead {
|
|||
/// The `x` in `print(x)` is resolved to the binding of `x` in `x = 1`.
|
||||
Resolved(BindingId),
|
||||
|
||||
/// The read reference is resolved to a specific binding, but the binding is only visible
|
||||
/// at type-checking time.
|
||||
///
|
||||
/// For example, given:
|
||||
/// ```python
|
||||
/// from typing import TYPE_CHECKING
|
||||
///
|
||||
/// if TYPE_CHECKING:
|
||||
/// from foo import Foo
|
||||
///
|
||||
/// Foo()
|
||||
/// ```
|
||||
///
|
||||
/// The `Foo` in `Foo()` is resolved to the binding of `Foo` in `from foo import Foo`, but
|
||||
/// the binding is only visible at type-checking time.
|
||||
TypingOnly(BindingId),
|
||||
|
||||
/// The read reference is resolved to a context-specific, implicit global (e.g., `__class__`
|
||||
/// within a class scope).
|
||||
///
|
||||
|
|
|
|||
Loading…
Reference in New Issue