mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-19 01:50:38 +00:00
[ty] Add support for @warnings.deprecated
(#19376)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
* [x] basic handling * [x] parse and discover `@warnings.deprecated` attributes * [x] associate them with function definitions * [x] associate them with class definitions * [x] add a new "deprecated" diagnostic * [x] ensure diagnostic is styled appropriately for LSPs (DiagnosticTag::Deprecated) * [x] functions * [x] fire on calls * [x] fire on arbitrary references * [x] classes * [x] fire on initializers * [x] fire on arbitrary references * [x] methods * [x] fire on calls * [x] fire on arbitrary references * [ ] overloads * [ ] fire on calls * [ ] fire on arbitrary references(??? maybe not ???) * [ ] only fire if the actual selected overload is deprecated * [ ] dunder desugarring (warn on deprecated `__add__` if `+` is invoked) * [ ] alias supression? (don't warn on uses of variables that deprecated items were assigned to) * [ ] import logic * [x] fire on imports of deprecated items * [ ] suppress subsequent diagnostics if the import diagnostic fired (is this handled by alias supression?) * [x] fire on all qualified references (`module.mydeprecated`) * [x] fire on all references that depend on a `*` import Fixes https://github.com/astral-sh/ty/issues/153
This commit is contained in:
parent
e9a64e5825
commit
06f9f52e59
15 changed files with 1000 additions and 73 deletions
|
@ -527,14 +527,21 @@ impl<'a> ProjectBenchmark<'a> {
|
|||
|
||||
#[track_caller]
|
||||
fn bench_project(benchmark: &ProjectBenchmark, criterion: &mut Criterion) {
|
||||
fn check_project(db: &mut ProjectDatabase, max_diagnostics: usize) {
|
||||
fn check_project(db: &mut ProjectDatabase, project_name: &str, max_diagnostics: usize) {
|
||||
let result = db.check();
|
||||
let diagnostics = result.len();
|
||||
|
||||
assert!(
|
||||
diagnostics <= max_diagnostics,
|
||||
"Expected <={max_diagnostics} diagnostics but got {diagnostics}"
|
||||
);
|
||||
if diagnostics > max_diagnostics {
|
||||
let details = result
|
||||
.into_iter()
|
||||
.map(|diagnostic| diagnostic.concise_message().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n ");
|
||||
assert!(
|
||||
diagnostics <= max_diagnostics,
|
||||
"{project_name}: Expected <={max_diagnostics} diagnostics but got {diagnostics}:\n {details}",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setup_rayon();
|
||||
|
@ -544,7 +551,7 @@ fn bench_project(benchmark: &ProjectBenchmark, criterion: &mut Criterion) {
|
|||
group.bench_function(benchmark.project.config.name, |b| {
|
||||
b.iter_batched_ref(
|
||||
|| benchmark.setup_iteration(),
|
||||
|db| check_project(db, benchmark.max_diagnostics),
|
||||
|db| check_project(db, benchmark.project.config.name, benchmark.max_diagnostics),
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
|
@ -612,7 +619,7 @@ fn datetype(criterion: &mut Criterion) {
|
|||
max_dep_date: "2025-07-04",
|
||||
python_version: PythonVersion::PY313,
|
||||
},
|
||||
0,
|
||||
2,
|
||||
);
|
||||
|
||||
bench_project(&benchmark, criterion);
|
||||
|
|
141
crates/ty/docs/rules.md
generated
141
crates/ty/docs/rules.md
generated
|
@ -36,7 +36,7 @@ def test(): -> "int":
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20call-non-callable) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L99)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L100)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -58,7 +58,7 @@ Calling a non-callable object will raise a `TypeError` at runtime.
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-argument-forms) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L143)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L144)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -88,7 +88,7 @@ f(int) # error
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-declarations) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L169)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L170)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -117,7 +117,7 @@ a = 1
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-metaclass) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L194)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L195)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -147,7 +147,7 @@ class C(A, B): ...
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20cyclic-class-definition) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L220)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L221)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -177,7 +177,7 @@ class B(A): ...
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-base) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L264)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L286)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -202,7 +202,7 @@ class B(A, A): ...
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-kw-only) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L285)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L307)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -306,7 +306,7 @@ def test(): -> "Literal[5]":
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20inconsistent-mro) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L427)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L449)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -334,7 +334,7 @@ class C(A, B): ...
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20index-out-of-bounds) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L451)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L473)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -358,7 +358,7 @@ t[3] # IndexError: tuple index out of range
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20instance-layout-conflict) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L317)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L339)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -445,7 +445,7 @@ an atypical memory layout.
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-argument-type) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L471)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L493)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -470,7 +470,7 @@ func("foo") # error: [invalid-argument-type]
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-assignment) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L511)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L533)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -496,7 +496,7 @@ a: int = ''
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-attribute-access) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1515)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1537)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -528,7 +528,7 @@ C.instance_var = 3 # error: Cannot assign to instance variable
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-base) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L533)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L555)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -550,7 +550,7 @@ class A(42): ... # error: [invalid-base]
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-context-manager) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L584)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L606)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -575,7 +575,7 @@ with 1:
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-declaration) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L605)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L627)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -602,7 +602,7 @@ a: str
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-exception-caught) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L628)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L650)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -644,7 +644,7 @@ except ZeroDivisionError:
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-generic-class) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L664)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L686)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -675,7 +675,7 @@ class C[U](Generic[T]): ...
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-legacy-type-variable) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L690)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L712)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -708,7 +708,7 @@ def f(t: TypeVar("U")): ...
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-metaclass) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L739)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L761)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -740,7 +740,7 @@ class B(metaclass=f): ...
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-overload) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L766)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L788)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -788,7 +788,7 @@ def foo(x: int) -> int: ...
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-parameter-default) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L809)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L831)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -812,7 +812,7 @@ def f(a: int = ''): ...
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-protocol) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L399)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L421)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -844,7 +844,7 @@ TypeError: Protocols can only inherit from other protocols, got <class 'int'>
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-raise) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L829)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L851)
|
||||
</small>
|
||||
|
||||
Checks for `raise` statements that raise non-exceptions or use invalid
|
||||
|
@ -891,7 +891,7 @@ def g():
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-return-type) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L492)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L514)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -914,7 +914,7 @@ def func() -> int:
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-super-argument) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L872)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L894)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -968,7 +968,7 @@ TODO #14889
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-alias-type) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L718)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L740)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -993,7 +993,7 @@ NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name mus
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-checking-constant) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L911)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L933)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1021,7 +1021,7 @@ TYPE_CHECKING = ''
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-form) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L935)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L957)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1049,7 +1049,7 @@ b: Annotated[int] # `Annotated` expects at least two arguments
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-call) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L987)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1009)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1081,7 +1081,7 @@ f(10) # Error
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-definition) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L959)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L981)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1113,7 +1113,7 @@ class C:
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-variable-constraints) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1015)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1037)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1146,7 +1146,7 @@ T = TypeVar('T', bound=str) # valid bound TypeVar
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-argument) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1044)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1066)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1169,7 +1169,7 @@ func() # TypeError: func() missing 1 required positional argument: 'x'
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20no-matching-overload) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1063)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1085)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1196,7 +1196,7 @@ func("string") # error: [no-matching-overload]
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20non-subscriptable) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1086)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1108)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1218,7 +1218,7 @@ Subscripting an object that does not support it will raise a `TypeError` at runt
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20not-iterable) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1104)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1126)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1242,7 +1242,7 @@ for i in 34: # TypeError: 'int' object is not iterable
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20parameter-already-assigned) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1155)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1177)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1296,7 +1296,7 @@ def test(): -> "int":
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20static-assert-error) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1491)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1513)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1324,7 +1324,7 @@ static_assert(int(2.0 * 3.0) == 6) # error: does not have a statically known tr
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20subclass-of-final-class) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1246)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1268)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1351,7 +1351,7 @@ class B(A): ... # Error raised here
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20too-many-positional-arguments) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1291)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1313)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1376,7 +1376,7 @@ f("foo") # Error raised here
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20type-assertion-failure) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1269)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1291)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1402,7 +1402,7 @@ def _(x: int):
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unavailable-implicit-super-arguments) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1312)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1334)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1446,7 +1446,7 @@ class A:
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unknown-argument) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1369)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1391)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1471,7 +1471,7 @@ f(x=1, y=2) # Error raised here
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-attribute) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1390)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1412)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1497,7 +1497,7 @@ A().foo # AttributeError: 'A' object has no attribute 'foo'
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-import) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1412)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1434)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1520,7 +1520,7 @@ import foo # ModuleNotFoundError: No module named 'foo'
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-reference) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1431)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1453)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1543,7 +1543,7 @@ print(x) # NameError: name 'x' is not defined
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-bool-conversion) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1124)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1146)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1578,7 +1578,7 @@ b1 < b2 < b1 # exception raised here
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-operator) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1450)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1472)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1604,7 +1604,7 @@ A() + A() # TypeError: unsupported operand type(s) for +: 'A' and 'A'
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20zero-stepsize-in-slice) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1472)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1494)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1622,6 +1622,31 @@ l = list(range(10))
|
|||
l[1:10:0] # ValueError: slice step cannot be zero
|
||||
```
|
||||
|
||||
## `deprecated`
|
||||
|
||||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20deprecated) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L265)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
||||
Checks for uses of deprecated items
|
||||
|
||||
**Why is this bad?**
|
||||
|
||||
Deprecated items should no longer be used.
|
||||
|
||||
**Examples**
|
||||
|
||||
```python
|
||||
@warnings.deprecated("use new_func instead")
|
||||
def old_func(): ...
|
||||
|
||||
old_func() # emits [deprecated] diagnostic
|
||||
```
|
||||
|
||||
## `invalid-ignore-comment`
|
||||
|
||||
<small>
|
||||
|
@ -1655,7 +1680,7 @@ a = 20 / 0 # type: ignore
|
|||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-attribute) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1176)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1198)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1681,7 +1706,7 @@ A.c # AttributeError: type object 'A' has no attribute 'c'
|
|||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-implicit-call) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L117)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L118)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1711,7 +1736,7 @@ A()[0] # TypeError: 'A' object is not subscriptable
|
|||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-import) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1198)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1220)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1741,7 +1766,7 @@ from module import a # ImportError: cannot import name 'a' from 'module'
|
|||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20redundant-cast) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1543)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1565)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1766,7 +1791,7 @@ cast(int, f()) # Redundant
|
|||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20undefined-reveal) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1351)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1373)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1817,7 +1842,7 @@ a = 20 / 0 # ty: ignore[division-by-zero]
|
|||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-global) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1564)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1586)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1871,7 +1896,7 @@ def g():
|
|||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-base) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L551)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L573)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1908,7 +1933,7 @@ class D(C): ... # error: [unsupported-base]
|
|||
<small>
|
||||
Default level: [`ignore`](../rules.md#rule-levels "This lint has a default level of 'ignore'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20division-by-zero) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L246)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L247)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
@ -1930,7 +1955,7 @@ Dividing by zero raises a `ZeroDivisionError` at runtime.
|
|||
<small>
|
||||
Default level: [`ignore`](../rules.md#rule-levels "This lint has a default level of 'ignore'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unresolved-reference) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1224)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1246)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
355
crates/ty_python_semantic/resources/mdtest/deprecated.md
Normal file
355
crates/ty_python_semantic/resources/mdtest/deprecated.md
Normal file
|
@ -0,0 +1,355 @@
|
|||
# Tests for the `@deprecated` decorator
|
||||
|
||||
## Introduction
|
||||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
||||
The decorator `@deprecated("some message")` can be applied to functions, methods, overloads, and
|
||||
classes. Uses of these items should subsequently produce a warning.
|
||||
|
||||
```py
|
||||
from typing_extensions import deprecated
|
||||
|
||||
@deprecated("use OtherClass")
|
||||
def myfunc(): ...
|
||||
|
||||
myfunc() # error: [deprecated] "use OtherClass"
|
||||
```
|
||||
|
||||
```py
|
||||
from typing_extensions import deprecated
|
||||
|
||||
@deprecated("use BetterClass")
|
||||
class MyClass: ...
|
||||
|
||||
MyClass() # error: [deprecated] "use BetterClass"
|
||||
```
|
||||
|
||||
```py
|
||||
from typing_extensions import deprecated
|
||||
|
||||
class MyClass:
|
||||
@deprecated("use something else")
|
||||
def afunc(): ...
|
||||
@deprecated("don't use this!")
|
||||
def amethod(self): ...
|
||||
|
||||
MyClass.afunc() # error: [deprecated] "use something else"
|
||||
MyClass().amethod() # error: [deprecated] "don't use this!"
|
||||
```
|
||||
|
||||
## Syntax
|
||||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
||||
The typeshed declaration of the decorator is as follows:
|
||||
|
||||
```ignore
|
||||
class deprecated:
|
||||
message: LiteralString
|
||||
category: type[Warning] | None
|
||||
stacklevel: int
|
||||
def __init__(self, message: LiteralString, /, *, category: type[Warning] | None = ..., stacklevel: int = 1) -> None: ...
|
||||
def __call__(self, arg: _T, /) -> _T: ...
|
||||
```
|
||||
|
||||
Only the mandatory message string is of interest to static analysis, the other two affect only
|
||||
runtime behaviour.
|
||||
|
||||
```py
|
||||
from typing_extensions import deprecated
|
||||
|
||||
@deprecated # error: [invalid-argument-type] "LiteralString"
|
||||
def invalid_deco(): ...
|
||||
|
||||
invalid_deco() # error: [missing-argument]
|
||||
```
|
||||
|
||||
```py
|
||||
from typing_extensions import deprecated
|
||||
|
||||
@deprecated() # error: [missing-argument] "message"
|
||||
def invalid_deco(): ...
|
||||
|
||||
invalid_deco()
|
||||
```
|
||||
|
||||
The argument is supposed to be a LiteralString, and we can handle simple constant propagations like
|
||||
this:
|
||||
|
||||
```py
|
||||
from typing_extensions import deprecated
|
||||
|
||||
x = "message"
|
||||
|
||||
@deprecated(x)
|
||||
def invalid_deco(): ...
|
||||
|
||||
invalid_deco() # error: [deprecated] "message"
|
||||
```
|
||||
|
||||
However sufficiently opaque LiteralStrings we can't resolve, and so we lose the message:
|
||||
|
||||
```py
|
||||
from typing_extensions import deprecated, LiteralString
|
||||
|
||||
def opaque() -> LiteralString:
|
||||
return "message"
|
||||
|
||||
@deprecated(opaque())
|
||||
def valid_deco(): ...
|
||||
|
||||
valid_deco() # error: [deprecated]
|
||||
```
|
||||
|
||||
Fully dynamic strings are technically allowed at runtime, but typeshed mandates that the input is a
|
||||
LiteralString, so we can/should emit a diagnostic for this:
|
||||
|
||||
```py
|
||||
from typing_extensions import deprecated
|
||||
|
||||
def opaque() -> str:
|
||||
return "message"
|
||||
|
||||
@deprecated(opaque()) # error: [invalid-argument-type] "LiteralString"
|
||||
def dubious_deco(): ...
|
||||
|
||||
dubious_deco()
|
||||
```
|
||||
|
||||
Although we have no use for the other arguments, we should still error if they're wrong.
|
||||
|
||||
```py
|
||||
from typing_extensions import deprecated
|
||||
|
||||
@deprecated("some message", dsfsdf="whatever") # error: [unknown-argument] "dsfsdf"
|
||||
def invalid_deco(): ...
|
||||
|
||||
invalid_deco()
|
||||
```
|
||||
|
||||
And we should always handle correct ones fine.
|
||||
|
||||
```py
|
||||
from typing_extensions import deprecated
|
||||
|
||||
@deprecated("some message", category=DeprecationWarning, stacklevel=1)
|
||||
def valid_deco(): ...
|
||||
|
||||
valid_deco() # error: [deprecated] "some message"
|
||||
```
|
||||
|
||||
## Different Versions
|
||||
|
||||
There are 2 different sources of `@deprecated`: `warnings` and `typing_extensions`. The version in
|
||||
`warnings` was added in 3.13, the version in `typing_extensions` is a compatibility shim.
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.13"
|
||||
```
|
||||
|
||||
`main.py`:
|
||||
|
||||
```py
|
||||
import warnings
|
||||
import typing_extensions
|
||||
|
||||
@warnings.deprecated("nope")
|
||||
def func1(): ...
|
||||
@typing_extensions.deprecated("nada")
|
||||
def func2(): ...
|
||||
|
||||
func1() # error: [deprecated] "nope"
|
||||
func2() # error: [deprecated] "nada"
|
||||
```
|
||||
|
||||
## Imports
|
||||
|
||||
### Direct Import Deprecated
|
||||
|
||||
Importing a deprecated item should produce a warning. Subsequent uses of the deprecated item
|
||||
shouldn't produce a warning.
|
||||
|
||||
`module.py`:
|
||||
|
||||
```py
|
||||
from typing_extensions import deprecated
|
||||
|
||||
@deprecated("Use OtherType instead")
|
||||
class DeprType: ...
|
||||
|
||||
@deprecated("Use other_func instead")
|
||||
def depr_func(): ...
|
||||
```
|
||||
|
||||
`main.py`:
|
||||
|
||||
```py
|
||||
# error: [deprecated] "Use OtherType instead"
|
||||
# error: [deprecated] "Use other_func instead"
|
||||
from module import DeprType, depr_func
|
||||
|
||||
# TODO: these diagnostics ideally shouldn't fire since we warn on the import
|
||||
DeprType() # error: [deprecated] "Use OtherType instead"
|
||||
depr_func() # error: [deprecated] "Use other_func instead"
|
||||
|
||||
def higher_order(x): ...
|
||||
|
||||
# TODO: these diagnostics ideally shouldn't fire since we warn on the import
|
||||
higher_order(DeprType) # error: [deprecated] "Use OtherType instead"
|
||||
higher_order(depr_func) # error: [deprecated] "Use other_func instead"
|
||||
|
||||
# TODO: these diagnostics ideally shouldn't fire since we warn on the import
|
||||
DeprType.__str__ # error: [deprecated] "Use OtherType instead"
|
||||
depr_func.__str__ # error: [deprecated] "Use other_func instead"
|
||||
```
|
||||
|
||||
### Non-Import Deprecated
|
||||
|
||||
If the items aren't imported and instead referenced using `module.item` then each use should produce
|
||||
a warning.
|
||||
|
||||
`module.py`:
|
||||
|
||||
```py
|
||||
from typing_extensions import deprecated
|
||||
|
||||
@deprecated("Use OtherType instead")
|
||||
class DeprType: ...
|
||||
|
||||
@deprecated("Use other_func instead")
|
||||
def depr_func(): ...
|
||||
```
|
||||
|
||||
`main.py`:
|
||||
|
||||
```py
|
||||
import module
|
||||
|
||||
module.DeprType() # error: [deprecated] "Use OtherType instead"
|
||||
module.depr_func() # error: [deprecated] "Use other_func instead"
|
||||
|
||||
def higher_order(x): ...
|
||||
|
||||
higher_order(module.DeprType) # error: [deprecated] "Use OtherType instead"
|
||||
higher_order(module.depr_func) # error: [deprecated] "Use other_func instead"
|
||||
|
||||
module.DeprType.__str__ # error: [deprecated] "Use OtherType instead"
|
||||
module.depr_func.__str__ # error: [deprecated] "Use other_func instead"
|
||||
```
|
||||
|
||||
### Star Import Deprecated
|
||||
|
||||
If the items are instead star-imported, then the actual uses should warn.
|
||||
|
||||
`module.py`:
|
||||
|
||||
```py
|
||||
from typing_extensions import deprecated
|
||||
|
||||
@deprecated("Use OtherType instead")
|
||||
class DeprType: ...
|
||||
|
||||
@deprecated("Use other_func instead")
|
||||
def depr_func(): ...
|
||||
```
|
||||
|
||||
`main.py`:
|
||||
|
||||
```py
|
||||
from module import *
|
||||
|
||||
DeprType() # error: [deprecated] "Use OtherType instead"
|
||||
depr_func() # error: [deprecated] "Use other_func instead"
|
||||
|
||||
def higher_order(x): ...
|
||||
|
||||
higher_order(DeprType) # error: [deprecated] "Use OtherType instead"
|
||||
higher_order(depr_func) # error: [deprecated] "Use other_func instead"
|
||||
|
||||
DeprType.__str__ # error: [deprecated] "Use OtherType instead"
|
||||
depr_func.__str__ # error: [deprecated] "Use other_func instead"
|
||||
```
|
||||
|
||||
## Aliases
|
||||
|
||||
Ideally a deprecated warning shouldn't transitively follow assignments, as you already had to "name"
|
||||
the deprecated symbol to assign it to something else. These kinds of diagnostics would therefore be
|
||||
redundant and annoying.
|
||||
|
||||
```py
|
||||
from typing_extensions import deprecated
|
||||
|
||||
@deprecated("Use OtherType instead")
|
||||
class DeprType: ...
|
||||
|
||||
@deprecated("Use other_func instead")
|
||||
def depr_func(): ...
|
||||
|
||||
alias_func = depr_func # error: [deprecated] "Use other_func instead"
|
||||
AliasClass = DeprType # error: [deprecated] "Use OtherType instead"
|
||||
|
||||
# TODO: these diagnostics ideally shouldn't fire
|
||||
alias_func() # error: [deprecated] "Use other_func instead"
|
||||
AliasClass() # error: [deprecated] "Use OtherType instead"
|
||||
```
|
||||
|
||||
## Dunders
|
||||
|
||||
If a dunder like `__add__` is deprecated, then the equivalent syntactic sugar like `+` should fire a
|
||||
diagnostic.
|
||||
|
||||
```py
|
||||
from typing_extensions import deprecated
|
||||
|
||||
class MyInt:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
@deprecated("MyInt `+` support is broken")
|
||||
def __add__(self, other):
|
||||
return MyInt(self.val + other.val)
|
||||
|
||||
x = MyInt(1)
|
||||
y = MyInt(2)
|
||||
z = x + y # TODO error: [deprecated] "MyInt `+` support is broken"
|
||||
```
|
||||
|
||||
## Overloads
|
||||
|
||||
Overloads can be deprecated, but only trigger warnings when invoked.
|
||||
|
||||
```py
|
||||
from typing_extensions import deprecated
|
||||
from typing_extensions import overload
|
||||
|
||||
@overload
|
||||
@deprecated("strings are no longer supported")
|
||||
def f(x: str): ...
|
||||
@overload
|
||||
def f(x: int): ...
|
||||
def f(x):
|
||||
print(x)
|
||||
|
||||
f(1)
|
||||
f("hello") # TODO: error: [deprecated] "strings are no longer supported"
|
||||
```
|
||||
|
||||
If the actual impl is deprecated, the deprecation always fires.
|
||||
|
||||
```py
|
||||
from typing_extensions import deprecated
|
||||
from typing_extensions import overload
|
||||
|
||||
@overload
|
||||
def f(x: str): ...
|
||||
@overload
|
||||
def f(x: int): ...
|
||||
@deprecated("unusable")
|
||||
def f(x):
|
||||
print(x)
|
||||
|
||||
f(1) # error: [deprecated] "unusable"
|
||||
f("hello") # error: [deprecated] "unusable"
|
||||
```
|
|
@ -0,0 +1,93 @@
|
|||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
---
|
||||
mdtest name: deprecated.md - Tests for the `@deprecated` decorator - Introduction
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/deprecated.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | from typing_extensions import deprecated
|
||||
2 |
|
||||
3 | @deprecated("use OtherClass")
|
||||
4 | def myfunc(): ...
|
||||
5 |
|
||||
6 | myfunc() # error: [deprecated] "use OtherClass"
|
||||
7 | from typing_extensions import deprecated
|
||||
8 |
|
||||
9 | @deprecated("use BetterClass")
|
||||
10 | class MyClass: ...
|
||||
11 |
|
||||
12 | MyClass() # error: [deprecated] "use BetterClass"
|
||||
13 | from typing_extensions import deprecated
|
||||
14 |
|
||||
15 | class MyClass:
|
||||
16 | @deprecated("use something else")
|
||||
17 | def afunc(): ...
|
||||
18 | @deprecated("don't use this!")
|
||||
19 | def amethod(self): ...
|
||||
20 |
|
||||
21 | MyClass.afunc() # error: [deprecated] "use something else"
|
||||
22 | MyClass().amethod() # error: [deprecated] "don't use this!"
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
warning[deprecated]: The function `myfunc` is deprecated
|
||||
--> src/mdtest_snippet.py:6:1
|
||||
|
|
||||
4 | def myfunc(): ...
|
||||
5 |
|
||||
6 | myfunc() # error: [deprecated] "use OtherClass"
|
||||
| ^^^^^^ use OtherClass
|
||||
7 | from typing_extensions import deprecated
|
||||
|
|
||||
info: rule `deprecated` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
warning[deprecated]: The class `MyClass` is deprecated
|
||||
--> src/mdtest_snippet.py:12:1
|
||||
|
|
||||
10 | class MyClass: ...
|
||||
11 |
|
||||
12 | MyClass() # error: [deprecated] "use BetterClass"
|
||||
| ^^^^^^^ use BetterClass
|
||||
13 | from typing_extensions import deprecated
|
||||
|
|
||||
info: rule `deprecated` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
warning[deprecated]: The function `afunc` is deprecated
|
||||
--> src/mdtest_snippet.py:21:9
|
||||
|
|
||||
19 | def amethod(self): ...
|
||||
20 |
|
||||
21 | MyClass.afunc() # error: [deprecated] "use something else"
|
||||
| ^^^^^ use something else
|
||||
22 | MyClass().amethod() # error: [deprecated] "don't use this!"
|
||||
|
|
||||
info: rule `deprecated` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
warning[deprecated]: The function `amethod` is deprecated
|
||||
--> src/mdtest_snippet.py:22:11
|
||||
|
|
||||
21 | MyClass.afunc() # error: [deprecated] "use something else"
|
||||
22 | MyClass().amethod() # error: [deprecated] "don't use this!"
|
||||
| ^^^^^^^ don't use this!
|
||||
|
|
||||
info: rule `deprecated` is enabled by default
|
||||
|
||||
```
|
|
@ -0,0 +1,178 @@
|
|||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
---
|
||||
mdtest name: deprecated.md - Tests for the `@deprecated` decorator - Syntax
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/deprecated.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | from typing_extensions import deprecated
|
||||
2 |
|
||||
3 | @deprecated # error: [invalid-argument-type] "LiteralString"
|
||||
4 | def invalid_deco(): ...
|
||||
5 |
|
||||
6 | invalid_deco() # error: [missing-argument]
|
||||
7 | from typing_extensions import deprecated
|
||||
8 |
|
||||
9 | @deprecated() # error: [missing-argument] "message"
|
||||
10 | def invalid_deco(): ...
|
||||
11 |
|
||||
12 | invalid_deco()
|
||||
13 | from typing_extensions import deprecated
|
||||
14 |
|
||||
15 | x = "message"
|
||||
16 |
|
||||
17 | @deprecated(x)
|
||||
18 | def invalid_deco(): ...
|
||||
19 |
|
||||
20 | invalid_deco() # error: [deprecated] "message"
|
||||
21 | from typing_extensions import deprecated, LiteralString
|
||||
22 |
|
||||
23 | def opaque() -> LiteralString:
|
||||
24 | return "message"
|
||||
25 |
|
||||
26 | @deprecated(opaque())
|
||||
27 | def valid_deco(): ...
|
||||
28 |
|
||||
29 | valid_deco() # error: [deprecated]
|
||||
30 | from typing_extensions import deprecated
|
||||
31 |
|
||||
32 | def opaque() -> str:
|
||||
33 | return "message"
|
||||
34 |
|
||||
35 | @deprecated(opaque()) # error: [invalid-argument-type] "LiteralString"
|
||||
36 | def dubious_deco(): ...
|
||||
37 |
|
||||
38 | dubious_deco()
|
||||
39 | from typing_extensions import deprecated
|
||||
40 |
|
||||
41 | @deprecated("some message", dsfsdf="whatever") # error: [unknown-argument] "dsfsdf"
|
||||
42 | def invalid_deco(): ...
|
||||
43 |
|
||||
44 | invalid_deco()
|
||||
45 | from typing_extensions import deprecated
|
||||
46 |
|
||||
47 | @deprecated("some message", category=DeprecationWarning, stacklevel=1)
|
||||
48 | def valid_deco(): ...
|
||||
49 |
|
||||
50 | valid_deco() # error: [deprecated] "some message"
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error[invalid-argument-type]: Argument to class `deprecated` is incorrect
|
||||
--> src/mdtest_snippet.py:3:1
|
||||
|
|
||||
1 | from typing_extensions import deprecated
|
||||
2 |
|
||||
3 | @deprecated # error: [invalid-argument-type] "LiteralString"
|
||||
| ^^^^^^^^^^^ Expected `LiteralString`, found `def invalid_deco() -> Unknown`
|
||||
4 | def invalid_deco(): ...
|
||||
|
|
||||
info: rule `invalid-argument-type` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[missing-argument]: No argument provided for required parameter `arg` of bound method `__call__`
|
||||
--> src/mdtest_snippet.py:6:1
|
||||
|
|
||||
4 | def invalid_deco(): ...
|
||||
5 |
|
||||
6 | invalid_deco() # error: [missing-argument]
|
||||
| ^^^^^^^^^^^^^^
|
||||
7 | from typing_extensions import deprecated
|
||||
|
|
||||
info: rule `missing-argument` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[missing-argument]: No argument provided for required parameter `message` of class `deprecated`
|
||||
--> src/mdtest_snippet.py:9:2
|
||||
|
|
||||
7 | from typing_extensions import deprecated
|
||||
8 |
|
||||
9 | @deprecated() # error: [missing-argument] "message"
|
||||
| ^^^^^^^^^^^^
|
||||
10 | def invalid_deco(): ...
|
||||
|
|
||||
info: rule `missing-argument` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
warning[deprecated]: The function `invalid_deco` is deprecated
|
||||
--> src/mdtest_snippet.py:20:1
|
||||
|
|
||||
18 | def invalid_deco(): ...
|
||||
19 |
|
||||
20 | invalid_deco() # error: [deprecated] "message"
|
||||
| ^^^^^^^^^^^^ message
|
||||
21 | from typing_extensions import deprecated, LiteralString
|
||||
|
|
||||
info: rule `deprecated` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
warning[deprecated]: The function `valid_deco` is deprecated
|
||||
--> src/mdtest_snippet.py:29:1
|
||||
|
|
||||
27 | def valid_deco(): ...
|
||||
28 |
|
||||
29 | valid_deco() # error: [deprecated]
|
||||
| ^^^^^^^^^^
|
||||
30 | from typing_extensions import deprecated
|
||||
|
|
||||
info: rule `deprecated` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[invalid-argument-type]: Argument to class `deprecated` is incorrect
|
||||
--> src/mdtest_snippet.py:35:13
|
||||
|
|
||||
33 | return "message"
|
||||
34 |
|
||||
35 | @deprecated(opaque()) # error: [invalid-argument-type] "LiteralString"
|
||||
| ^^^^^^^^ Expected `LiteralString`, found `str`
|
||||
36 | def dubious_deco(): ...
|
||||
|
|
||||
info: rule `invalid-argument-type` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[unknown-argument]: Argument `dsfsdf` does not match any known parameter of class `deprecated`
|
||||
--> src/mdtest_snippet.py:41:29
|
||||
|
|
||||
39 | from typing_extensions import deprecated
|
||||
40 |
|
||||
41 | @deprecated("some message", dsfsdf="whatever") # error: [unknown-argument] "dsfsdf"
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
42 | def invalid_deco(): ...
|
||||
|
|
||||
info: rule `unknown-argument` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
warning[deprecated]: The function `valid_deco` is deprecated
|
||||
--> src/mdtest_snippet.py:50:1
|
||||
|
|
||||
48 | def valid_deco(): ...
|
||||
49 |
|
||||
50 | valid_deco() # error: [deprecated] "some message"
|
||||
| ^^^^^^^^^^ some message
|
||||
|
|
||||
info: rule `deprecated` is enabled by default
|
||||
|
||||
```
|
|
@ -275,6 +275,7 @@ pub enum KnownModule {
|
|||
UnittestMock,
|
||||
#[cfg(test)]
|
||||
Uuid,
|
||||
Warnings,
|
||||
}
|
||||
|
||||
impl KnownModule {
|
||||
|
@ -294,6 +295,7 @@ impl KnownModule {
|
|||
Self::TypeCheckerInternals => "_typeshed._type_checker_internals",
|
||||
Self::TyExtensions => "ty_extensions",
|
||||
Self::ImportLib => "importlib",
|
||||
Self::Warnings => "warnings",
|
||||
#[cfg(test)]
|
||||
Self::UnittestMock => "unittest.mock",
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -4187,6 +4187,45 @@ impl<'db> Type<'db> {
|
|||
.into()
|
||||
}
|
||||
|
||||
Some(KnownClass::Deprecated) => {
|
||||
// ```py
|
||||
// class deprecated:
|
||||
// def __new__(
|
||||
// cls,
|
||||
// message: LiteralString,
|
||||
// /,
|
||||
// *,
|
||||
// category: type[Warning] | None = ...,
|
||||
// stacklevel: int = 1
|
||||
// ) -> Self: ...
|
||||
// ```
|
||||
Binding::single(
|
||||
self,
|
||||
Signature::new(
|
||||
Parameters::new([
|
||||
Parameter::positional_only(Some(Name::new_static("message")))
|
||||
.with_annotated_type(Type::LiteralString),
|
||||
Parameter::keyword_only(Name::new_static("category"))
|
||||
.with_annotated_type(UnionType::from_elements(
|
||||
db,
|
||||
[
|
||||
// TODO: should be `type[Warning]`
|
||||
Type::any(),
|
||||
KnownClass::NoneType.to_instance(db),
|
||||
],
|
||||
))
|
||||
// TODO: should be `type[Warning]`
|
||||
.with_default_type(Type::any()),
|
||||
Parameter::keyword_only(Name::new_static("stacklevel"))
|
||||
.with_annotated_type(KnownClass::Int.to_instance(db))
|
||||
.with_default_type(Type::IntLiteral(1)),
|
||||
]),
|
||||
Some(KnownClass::Deprecated.to_instance(db)),
|
||||
),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
Some(KnownClass::TypeAliasType) => {
|
||||
// ```py
|
||||
// def __new__(
|
||||
|
@ -4450,8 +4489,11 @@ impl<'db> Type<'db> {
|
|||
|
||||
Type::EnumLiteral(enum_literal) => enum_literal.enum_class_instance(db).bindings(db),
|
||||
|
||||
Type::KnownInstance(known_instance) => {
|
||||
known_instance.instance_fallback(db).bindings(db)
|
||||
}
|
||||
|
||||
Type::PropertyInstance(_)
|
||||
| Type::KnownInstance(_)
|
||||
| Type::AlwaysFalsy
|
||||
| Type::AlwaysTruthy
|
||||
| Type::IntLiteral(_)
|
||||
|
@ -5016,6 +5058,10 @@ impl<'db> Type<'db> {
|
|||
Type::KnownInstance(known_instance) => match known_instance {
|
||||
KnownInstanceType::TypeAliasType(alias) => Ok(alias.value_type(db)),
|
||||
KnownInstanceType::TypeVar(typevar) => Ok(Type::TypeVar(*typevar)),
|
||||
KnownInstanceType::Deprecated(_) => Err(InvalidTypeExpressionError {
|
||||
invalid_expressions: smallvec::smallvec![InvalidTypeExpression::Deprecated],
|
||||
fallback_type: Type::unknown(),
|
||||
}),
|
||||
KnownInstanceType::SubscriptedProtocol(_) => Err(InvalidTypeExpressionError {
|
||||
invalid_expressions: smallvec::smallvec_inline![
|
||||
InvalidTypeExpression::Protocol
|
||||
|
@ -5861,6 +5907,9 @@ pub enum KnownInstanceType<'db> {
|
|||
|
||||
/// A single instance of `typing.TypeAliasType` (PEP 695 type alias)
|
||||
TypeAliasType(TypeAliasType<'db>),
|
||||
|
||||
/// A single instance of `warnings.deprecated` or `typing_extensions.deprecated`
|
||||
Deprecated(DeprecatedInstance<'db>),
|
||||
}
|
||||
|
||||
fn walk_known_instance_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||
|
@ -5879,6 +5928,9 @@ fn walk_known_instance_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
|||
KnownInstanceType::TypeAliasType(type_alias) => {
|
||||
visitor.visit_type_alias_type(db, type_alias);
|
||||
}
|
||||
KnownInstanceType::Deprecated(_) => {
|
||||
// Nothing to visit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5895,6 +5947,10 @@ impl<'db> KnownInstanceType<'db> {
|
|||
Self::TypeAliasType(type_alias) => {
|
||||
Self::TypeAliasType(type_alias.normalized_impl(db, visitor))
|
||||
}
|
||||
Self::Deprecated(deprecated) => {
|
||||
// Nothing to normalize
|
||||
Self::Deprecated(deprecated)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5903,6 +5959,7 @@ impl<'db> KnownInstanceType<'db> {
|
|||
Self::SubscriptedProtocol(_) | Self::SubscriptedGeneric(_) => KnownClass::SpecialForm,
|
||||
Self::TypeVar(_) => KnownClass::TypeVar,
|
||||
Self::TypeAliasType(_) => KnownClass::TypeAliasType,
|
||||
Self::Deprecated(_) => KnownClass::Deprecated,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5947,6 +6004,7 @@ impl<'db> KnownInstanceType<'db> {
|
|||
// it as an instance of `typing.TypeVar`. Inside of a generic class or function, we'll
|
||||
// have a `Type::TypeVar(_)`, which is rendered as the typevar's name.
|
||||
KnownInstanceType::TypeVar(_) => f.write_str("typing.TypeVar"),
|
||||
KnownInstanceType::Deprecated(_) => f.write_str("warnings.deprecated"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6135,6 +6193,8 @@ enum InvalidTypeExpression<'db> {
|
|||
Protocol,
|
||||
/// Same for `Generic`
|
||||
Generic,
|
||||
/// Same for `@deprecated`
|
||||
Deprecated,
|
||||
/// Type qualifiers are always invalid in *type expressions*,
|
||||
/// but these ones are okay with 0 arguments in *annotation expressions*
|
||||
TypeQualifier(SpecialFormType),
|
||||
|
@ -6176,6 +6236,9 @@ impl<'db> InvalidTypeExpression<'db> {
|
|||
InvalidTypeExpression::Generic => {
|
||||
f.write_str("`typing.Generic` is not allowed in type expressions")
|
||||
}
|
||||
InvalidTypeExpression::Deprecated => {
|
||||
f.write_str("`warnings.deprecated` is not allowed in type expressions")
|
||||
}
|
||||
InvalidTypeExpression::TypeQualifier(qualifier) => write!(
|
||||
f,
|
||||
"Type qualifier `{qualifier}` is not allowed in type expressions \
|
||||
|
@ -6231,6 +6294,17 @@ impl<'db> InvalidTypeExpression<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Data regarding a `warnings.deprecated` or `typing_extensions.deprecated` decorator.
|
||||
#[salsa::interned(debug)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct DeprecatedInstance<'db> {
|
||||
/// The message for the deprecation
|
||||
pub message: Option<StringLiteralType<'db>>,
|
||||
}
|
||||
|
||||
// The Salsa heap is tracked separately.
|
||||
impl get_size2::GetSize for DeprecatedInstance<'_> {}
|
||||
|
||||
/// Whether this typecar was created via the legacy `TypeVar` constructor, or using PEP 695 syntax.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum TypeVarKind {
|
||||
|
|
|
@ -848,6 +848,7 @@ impl<'db> Bindings<'db> {
|
|||
class_literal.name(db),
|
||||
class_literal.body_scope(db),
|
||||
class_literal.known(db),
|
||||
class_literal.deprecated(db),
|
||||
Some(params),
|
||||
class_literal.dataclass_transformer_params(db),
|
||||
)));
|
||||
|
|
|
@ -22,8 +22,9 @@ use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signatu
|
|||
use crate::types::tuple::TupleType;
|
||||
use crate::types::{
|
||||
BareTypeAliasType, Binding, BoundSuperError, BoundSuperType, CallableType, DataclassParams,
|
||||
DynamicType, KnownInstanceType, TypeAliasType, TypeMapping, TypeRelation, TypeTransformer,
|
||||
TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, infer_definition_types,
|
||||
DeprecatedInstance, DynamicType, KnownInstanceType, TypeAliasType, TypeMapping, TypeRelation,
|
||||
TypeTransformer, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind,
|
||||
infer_definition_types,
|
||||
};
|
||||
use crate::{
|
||||
Db, FxOrderSet, KnownModule, Program,
|
||||
|
@ -799,6 +800,9 @@ pub struct ClassLiteral<'db> {
|
|||
|
||||
pub(crate) known: Option<KnownClass>,
|
||||
|
||||
/// If this class is deprecated, this holds the deprecation message.
|
||||
pub(crate) deprecated: Option<DeprecatedInstance<'db>>,
|
||||
|
||||
pub(crate) dataclass_params: Option<DataclassParams>,
|
||||
pub(crate) dataclass_transformer_params: Option<DataclassTransformerParams>,
|
||||
}
|
||||
|
@ -2418,6 +2422,7 @@ pub enum KnownClass {
|
|||
NoneType, // Part of `types` for Python >= 3.10
|
||||
// Typing
|
||||
Any,
|
||||
Deprecated,
|
||||
StdlibAlias,
|
||||
SpecialForm,
|
||||
TypeVar,
|
||||
|
@ -2535,6 +2540,7 @@ impl KnownClass {
|
|||
| Self::NotImplementedType
|
||||
| Self::Staticmethod
|
||||
| Self::Classmethod
|
||||
| Self::Deprecated
|
||||
| Self::Field
|
||||
| Self::KwOnly
|
||||
| Self::NamedTupleFallback => Truthiness::Ambiguous,
|
||||
|
@ -2562,6 +2568,7 @@ impl KnownClass {
|
|||
| Self::Property
|
||||
| Self::Staticmethod
|
||||
| Self::Classmethod
|
||||
| Self::Deprecated
|
||||
| Self::Type
|
||||
| Self::ModuleType
|
||||
| Self::Super
|
||||
|
@ -2648,6 +2655,7 @@ impl KnownClass {
|
|||
| KnownClass::ExceptionGroup
|
||||
| KnownClass::Staticmethod
|
||||
| KnownClass::Classmethod
|
||||
| KnownClass::Deprecated
|
||||
| KnownClass::Super
|
||||
| KnownClass::Enum
|
||||
| KnownClass::Auto
|
||||
|
@ -2731,6 +2739,7 @@ impl KnownClass {
|
|||
| Self::ExceptionGroup
|
||||
| Self::Staticmethod
|
||||
| Self::Classmethod
|
||||
| Self::Deprecated
|
||||
| Self::GenericAlias
|
||||
| Self::GeneratorType
|
||||
| Self::AsyncGeneratorType
|
||||
|
@ -2797,6 +2806,7 @@ impl KnownClass {
|
|||
Self::ExceptionGroup => "ExceptionGroup",
|
||||
Self::Staticmethod => "staticmethod",
|
||||
Self::Classmethod => "classmethod",
|
||||
Self::Deprecated => "deprecated",
|
||||
Self::GenericAlias => "GenericAlias",
|
||||
Self::ModuleType => "ModuleType",
|
||||
Self::FunctionType => "FunctionType",
|
||||
|
@ -3071,6 +3081,7 @@ impl KnownClass {
|
|||
| Self::ParamSpec
|
||||
| Self::ParamSpecArgs
|
||||
| Self::ParamSpecKwargs
|
||||
| Self::Deprecated
|
||||
| Self::NewType => KnownModule::TypingExtensions,
|
||||
Self::NoDefaultType => {
|
||||
let python_version = Program::get(db).python_version(db);
|
||||
|
@ -3139,6 +3150,7 @@ impl KnownClass {
|
|||
| Self::ExceptionGroup
|
||||
| Self::Staticmethod
|
||||
| Self::Classmethod
|
||||
| Self::Deprecated
|
||||
| Self::GenericAlias
|
||||
| Self::ModuleType
|
||||
| Self::FunctionType
|
||||
|
@ -3226,6 +3238,7 @@ impl KnownClass {
|
|||
| Self::ExceptionGroup
|
||||
| Self::Staticmethod
|
||||
| Self::Classmethod
|
||||
| Self::Deprecated
|
||||
| Self::TypeVar
|
||||
| Self::ParamSpec
|
||||
| Self::ParamSpecArgs
|
||||
|
@ -3278,6 +3291,7 @@ impl KnownClass {
|
|||
"ExceptionGroup" => Self::ExceptionGroup,
|
||||
"staticmethod" => Self::Staticmethod,
|
||||
"classmethod" => Self::Classmethod,
|
||||
"deprecated" => Self::Deprecated,
|
||||
"GenericAlias" => Self::GenericAlias,
|
||||
"NoneType" => Self::NoneType,
|
||||
"ModuleType" => Self::ModuleType,
|
||||
|
@ -3397,6 +3411,8 @@ impl KnownClass {
|
|||
| Self::NamedTuple
|
||||
| Self::Iterable
|
||||
| Self::NewType => matches!(module, KnownModule::Typing | KnownModule::TypingExtensions),
|
||||
Self::Deprecated => matches!(module, KnownModule::Warnings | KnownModule::TypingExtensions),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3481,7 +3497,32 @@ impl KnownClass {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
KnownClass::Deprecated => {
|
||||
// Parsing something of the form:
|
||||
//
|
||||
// @deprecated("message")
|
||||
// @deprecated("message", caregory = DeprecationWarning, stacklevel = 1)
|
||||
//
|
||||
// "Static type checker behavior is not affected by the category and stacklevel arguments"
|
||||
// so we only need the message and can ignore everything else. The message is mandatory,
|
||||
// must be a LiteralString, and always comes first.
|
||||
//
|
||||
// We aren't guaranteed to know the static value of a LiteralString, so we need to
|
||||
// accept that sometimes we will fail to include the message.
|
||||
//
|
||||
// We don't do any serious validation/diagnostics here, as the signature for this
|
||||
// is included in `Type::bindings`.
|
||||
//
|
||||
// See: <https://typing.python.org/en/latest/spec/directives.html#deprecated>
|
||||
let [Some(message), ..] = overload.parameter_types() else {
|
||||
// Checking in Type::bindings will complain about this for us
|
||||
return;
|
||||
};
|
||||
|
||||
overload.set_return_type(Type::KnownInstance(KnownInstanceType::Deprecated(
|
||||
DeprecatedInstance::new(db, message.into_string_literal()),
|
||||
)));
|
||||
}
|
||||
KnownClass::TypeVar => {
|
||||
let assigned_to = index
|
||||
.try_expression(ast::ExprRef::from(call_expression))
|
||||
|
|
|
@ -163,7 +163,9 @@ impl<'db> ClassBase<'db> {
|
|||
Type::KnownInstance(known_instance) => match known_instance {
|
||||
KnownInstanceType::SubscriptedGeneric(_) => Some(Self::Generic),
|
||||
KnownInstanceType::SubscriptedProtocol(_) => Some(Self::Protocol),
|
||||
KnownInstanceType::TypeAliasType(_) | KnownInstanceType::TypeVar(_) => None,
|
||||
KnownInstanceType::TypeAliasType(_)
|
||||
| KnownInstanceType::TypeVar(_)
|
||||
| KnownInstanceType::Deprecated(_) => None,
|
||||
},
|
||||
|
||||
Type::SpecialForm(special_form) => match special_form {
|
||||
|
|
|
@ -288,7 +288,6 @@ impl LintDiagnosticGuard<'_, '_> {
|
|||
///
|
||||
/// Callers can add additional primary or secondary annotations via the
|
||||
/// `DerefMut` trait implementation to a `Diagnostic`.
|
||||
#[expect(dead_code)]
|
||||
pub(super) fn add_primary_tag(&mut self, tag: DiagnosticTag) {
|
||||
let ann = self.primary_annotation_mut().unwrap();
|
||||
ann.push_tag(tag);
|
||||
|
|
|
@ -34,6 +34,7 @@ pub(crate) fn register_lints(registry: &mut LintRegistryBuilder) {
|
|||
registry.register_lint(&CONFLICTING_DECLARATIONS);
|
||||
registry.register_lint(&CONFLICTING_METACLASS);
|
||||
registry.register_lint(&CYCLIC_CLASS_DEFINITION);
|
||||
registry.register_lint(&DEPRECATED);
|
||||
registry.register_lint(&DIVISION_BY_ZERO);
|
||||
registry.register_lint(&DUPLICATE_BASE);
|
||||
registry.register_lint(&DUPLICATE_KW_ONLY);
|
||||
|
@ -261,6 +262,27 @@ declare_lint! {
|
|||
}
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// ## What it does
|
||||
/// Checks for uses of deprecated items
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Deprecated items should no longer be used.
|
||||
///
|
||||
/// ## Examples
|
||||
/// ```python
|
||||
/// @warnings.deprecated("use new_func instead")
|
||||
/// def old_func(): ...
|
||||
///
|
||||
/// old_func() # emits [deprecated] diagnostic
|
||||
/// ```
|
||||
pub(crate) static DEPRECATED = {
|
||||
summary: "detects uses of deprecated items",
|
||||
status: LintStatus::preview("1.0.0"),
|
||||
default_level: Level::Warn,
|
||||
}
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// ## What it does
|
||||
/// Checks for class definitions with duplicate bases.
|
||||
|
|
|
@ -76,8 +76,8 @@ use crate::types::narrow::ClassInfoConstraintFunction;
|
|||
use crate::types::signatures::{CallableSignature, Signature};
|
||||
use crate::types::visitor::any_over_type;
|
||||
use crate::types::{
|
||||
BoundMethodType, CallableType, DynamicType, KnownClass, Type, TypeMapping, TypeRelation,
|
||||
TypeTransformer, TypeVarInstance, UnionBuilder, walk_type_mapping,
|
||||
BoundMethodType, CallableType, DeprecatedInstance, DynamicType, KnownClass, Type, TypeMapping,
|
||||
TypeRelation, TypeTransformer, TypeVarInstance, UnionBuilder, walk_type_mapping,
|
||||
};
|
||||
use crate::{Db, FxOrderSet, ModuleName, resolve_module};
|
||||
|
||||
|
@ -199,6 +199,9 @@ pub struct OverloadLiteral<'db> {
|
|||
/// A set of special decorators that were applied to this function
|
||||
pub(crate) decorators: FunctionDecorators,
|
||||
|
||||
/// If `Some` then contains the `@warnings.deprecated`
|
||||
pub(crate) deprecated: Option<DeprecatedInstance<'db>>,
|
||||
|
||||
/// The arguments to `dataclass_transformer`, if this function was annotated
|
||||
/// with `@dataclass_transformer(...)`.
|
||||
pub(crate) dataclass_transformer_params: Option<DataclassTransformerParams>,
|
||||
|
@ -220,6 +223,7 @@ impl<'db> OverloadLiteral<'db> {
|
|||
self.known(db),
|
||||
self.body_scope(db),
|
||||
self.decorators(db),
|
||||
self.deprecated(db),
|
||||
Some(params),
|
||||
)
|
||||
}
|
||||
|
@ -465,6 +469,14 @@ impl<'db> FunctionLiteral<'db> {
|
|||
.any(|overload| overload.decorators(db).contains(decorator))
|
||||
}
|
||||
|
||||
/// If the implementation of this function is deprecated, returns the `@warnings.deprecated`.
|
||||
///
|
||||
/// Checking if an overload is deprecated requires deeper call analysis.
|
||||
fn implementation_deprecated(self, db: &'db dyn Db) -> Option<DeprecatedInstance<'db>> {
|
||||
let (_overloads, implementation) = self.overloads_and_implementation(db);
|
||||
implementation.and_then(|overload| overload.deprecated(db))
|
||||
}
|
||||
|
||||
fn definition(self, db: &'db dyn Db) -> Definition<'db> {
|
||||
self.last_definition(db).definition(db)
|
||||
}
|
||||
|
@ -672,6 +684,16 @@ impl<'db> FunctionType<'db> {
|
|||
self.literal(db).has_known_decorator(db, decorator)
|
||||
}
|
||||
|
||||
/// If the implementation of this function is deprecated, returns the `@warnings.deprecated`.
|
||||
///
|
||||
/// Checking if an overload is deprecated requires deeper call analysis.
|
||||
pub(crate) fn implementation_deprecated(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
) -> Option<DeprecatedInstance<'db>> {
|
||||
self.literal(db).implementation_deprecated(db)
|
||||
}
|
||||
|
||||
/// Returns the [`Definition`] of the implementation or first overload of this function.
|
||||
///
|
||||
/// ## Warning
|
||||
|
|
|
@ -2298,6 +2298,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
|
||||
let mut decorator_types_and_nodes = Vec::with_capacity(decorator_list.len());
|
||||
let mut function_decorators = FunctionDecorators::empty();
|
||||
let mut deprecated = None;
|
||||
let mut dataclass_transformer_params = None;
|
||||
|
||||
for decorator in decorator_list {
|
||||
|
@ -2315,6 +2316,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
Type::KnownInstance(KnownInstanceType::Deprecated(deprecated_inst)) => {
|
||||
deprecated = Some(deprecated_inst);
|
||||
}
|
||||
Type::DataclassTransformer(params) => {
|
||||
dataclass_transformer_params = Some(params);
|
||||
}
|
||||
|
@ -2362,6 +2366,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
known_function,
|
||||
body_scope,
|
||||
function_decorators,
|
||||
deprecated,
|
||||
dataclass_transformer_params,
|
||||
);
|
||||
|
||||
|
@ -2624,6 +2629,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
body: _,
|
||||
} = class_node;
|
||||
|
||||
let mut deprecated = None;
|
||||
let mut dataclass_params = None;
|
||||
let mut dataclass_transformer_params = None;
|
||||
for decorator in decorator_list {
|
||||
|
@ -2641,6 +2647,13 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
continue;
|
||||
}
|
||||
|
||||
if let Type::KnownInstance(KnownInstanceType::Deprecated(deprecated_inst)) =
|
||||
decorator_ty
|
||||
{
|
||||
deprecated = Some(deprecated_inst);
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Type::FunctionLiteral(f) = decorator_ty {
|
||||
// We do not yet detect or flag `@dataclass_transform` applied to more than one
|
||||
// overload, or an overload and the implementation both. Nevertheless, this is not
|
||||
|
@ -2673,6 +2686,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
name.id.clone(),
|
||||
body_scope,
|
||||
maybe_known_class,
|
||||
deprecated,
|
||||
dataclass_params,
|
||||
dataclass_transformer_params,
|
||||
));
|
||||
|
@ -4358,7 +4372,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
|
||||
for alias in names {
|
||||
for definition in self.index.definitions(alias) {
|
||||
self.extend(infer_definition_types(self.db(), *definition));
|
||||
let inferred = infer_definition_types(self.db(), *definition);
|
||||
// Check non-star imports for deprecations
|
||||
if definition.kind(self.db()).as_star_import().is_none() {
|
||||
for ty in inferred.declarations.values() {
|
||||
self.check_deprecated(alias, ty.inner);
|
||||
}
|
||||
}
|
||||
self.extend(inferred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5562,6 +5583,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
| KnownClass::NamedTuple
|
||||
| KnownClass::TypeAliasType
|
||||
| KnownClass::Tuple
|
||||
| KnownClass::Deprecated
|
||||
)
|
||||
)
|
||||
// temporary special-casing for all subclasses of `enum.Enum`
|
||||
|
@ -5799,6 +5821,62 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
ty
|
||||
}
|
||||
|
||||
/// Check if the given ty is `@deprecated` or not
|
||||
fn check_deprecated<T: Ranged>(&self, ranged: T, ty: Type) {
|
||||
// First handle classes
|
||||
if let Type::ClassLiteral(class_literal) = ty {
|
||||
let Some(deprecated) = class_literal.deprecated(self.db()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(builder) = self
|
||||
.context
|
||||
.report_lint(&crate::types::diagnostic::DEPRECATED, ranged)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let class_name = class_literal.name(self.db());
|
||||
let mut diag =
|
||||
builder.into_diagnostic(format_args!(r#"The class `{class_name}` is deprecated"#));
|
||||
if let Some(message) = deprecated.message(self.db()) {
|
||||
diag.set_primary_message(message.value(self.db()));
|
||||
}
|
||||
diag.add_primary_tag(ruff_db::diagnostic::DiagnosticTag::Deprecated);
|
||||
return;
|
||||
}
|
||||
|
||||
// Next handle functions
|
||||
let function = match ty {
|
||||
Type::FunctionLiteral(function) => function,
|
||||
Type::BoundMethod(bound) => bound.function(self.db()),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// Currently we only check the final implementation for deprecation, as
|
||||
// that check can be done on any reference to the function. Analysis of
|
||||
// deprecated overloads needs to be done in places where we resolve the
|
||||
// actual overloads being used.
|
||||
let Some(deprecated) = function.implementation_deprecated(self.db()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(builder) = self
|
||||
.context
|
||||
.report_lint(&crate::types::diagnostic::DEPRECATED, ranged)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let func_name = function.name(self.db());
|
||||
let mut diag =
|
||||
builder.into_diagnostic(format_args!(r#"The function `{func_name}` is deprecated"#));
|
||||
if let Some(message) = deprecated.message(self.db()) {
|
||||
diag.set_primary_message(message.value(self.db()));
|
||||
}
|
||||
diag.add_primary_tag(ruff_db::diagnostic::DiagnosticTag::Deprecated);
|
||||
}
|
||||
|
||||
fn infer_name_load(&mut self, name_node: &ast::ExprName) -> Type<'db> {
|
||||
let ast::ExprName {
|
||||
range: _,
|
||||
|
@ -5811,6 +5889,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
|
||||
let (resolved, constraint_keys) =
|
||||
self.infer_place_load(&expr, ast::ExprRef::Name(name_node));
|
||||
|
||||
resolved
|
||||
// Not found in the module's explicitly declared global symbols?
|
||||
// Check the "implicit globals" such as `__doc__`, `__file__`, `__name__`, etc.
|
||||
|
@ -5892,6 +5971,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
|
||||
let use_id = expr_ref.scoped_use_id(db, scope);
|
||||
let place = place_from_bindings(db, use_def.bindings_at_use(use_id));
|
||||
|
||||
(place, Some(use_id))
|
||||
}
|
||||
}
|
||||
|
@ -6165,6 +6245,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
})
|
||||
});
|
||||
|
||||
if let Some(ty) = place.place.ignore_possibly_unbound() {
|
||||
self.check_deprecated(expr_ref, ty);
|
||||
}
|
||||
|
||||
(place, constraint_keys)
|
||||
}
|
||||
|
||||
|
@ -6368,6 +6452,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
}
|
||||
})
|
||||
.inner_type();
|
||||
|
||||
self.check_deprecated(attr, resolved_type);
|
||||
|
||||
// Even if we can obtain the attribute type based on the assignments, we still perform default type inference
|
||||
// (to report errors).
|
||||
assigned_type.unwrap_or(resolved_type)
|
||||
|
@ -9294,6 +9381,15 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
}
|
||||
Type::unknown()
|
||||
}
|
||||
KnownInstanceType::Deprecated(_) => {
|
||||
self.infer_type_expression(&subscript.slice);
|
||||
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
|
||||
builder.into_diagnostic(format_args!(
|
||||
"`warnings.deprecated` is not allowed in type expressions",
|
||||
));
|
||||
}
|
||||
Type::unknown()
|
||||
}
|
||||
KnownInstanceType::TypeVar(_) => {
|
||||
self.infer_type_expression(&subscript.slice);
|
||||
todo_type!("TypeVar annotations")
|
||||
|
|
10
ty.schema.json
generated
10
ty.schema.json
generated
|
@ -331,6 +331,16 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"deprecated": {
|
||||
"title": "detects uses of deprecated items",
|
||||
"description": "## What it does\nChecks for uses of deprecated items\n\n## Why is this bad?\nDeprecated items should no longer be used.\n\n## Examples\n```python\n@warnings.deprecated(\"use new_func instead\")\ndef old_func(): ...\n\nold_func() # emits [deprecated] diagnostic\n```",
|
||||
"default": "warn",
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Level"
|
||||
}
|
||||
]
|
||||
},
|
||||
"division-by-zero": {
|
||||
"title": "detects division by zero",
|
||||
"description": "## What it does\nIt detects division by zero.\n\n## Why is this bad?\nDividing by zero raises a `ZeroDivisionError` at runtime.\n\n## Examples\n```python\n5 / 0\n```",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue