diff --git a/.github/mypy-primer-ty.toml b/.github/mypy-primer-ty.toml
index a64ea72eba..c0bcd88695 100644
--- a/.github/mypy-primer-ty.toml
+++ b/.github/mypy-primer-ty.toml
@@ -5,5 +5,4 @@
[rules]
possibly-unresolved-reference = "warn"
possibly-missing-import = "warn"
-unused-ignore-comment = "warn"
division-by-zero = "warn"
diff --git a/crates/ruff_benchmark/benches/ty.rs b/crates/ruff_benchmark/benches/ty.rs
index 0252d5c75b..1270800206 100644
--- a/crates/ruff_benchmark/benches/ty.rs
+++ b/crates/ruff_benchmark/benches/ty.rs
@@ -15,7 +15,7 @@ use ruff_db::files::{File, system_path_to_file};
use ruff_db::source::source_text;
use ruff_db::system::{InMemorySystem, MemoryFileSystem, SystemPath, SystemPathBuf, TestSystem};
use ruff_python_ast::PythonVersion;
-use ty_project::metadata::options::{EnvironmentOptions, Options};
+use ty_project::metadata::options::{AnalysisOptions, EnvironmentOptions, Options};
use ty_project::metadata::value::{RangedValue, RelativePathBuf};
use ty_project::watch::{ChangeEvent, ChangedKind};
use ty_project::{CheckMode, Db, ProjectDatabase, ProjectMetadata};
@@ -67,6 +67,7 @@ fn tomllib_path(file: &TestFile) -> SystemPathBuf {
SystemPathBuf::from("src").join(file.name())
}
+#[expect(clippy::needless_update)]
fn setup_tomllib_case() -> Case {
let system = TestSystem::default();
let fs = system.memory_file_system().clone();
@@ -85,6 +86,10 @@ fn setup_tomllib_case() -> Case {
python_version: Some(RangedValue::cli(PythonVersion::PY312)),
..EnvironmentOptions::default()
}),
+ analysis: Some(AnalysisOptions {
+ respect_type_ignore_comments: Some(false),
+ ..AnalysisOptions::default()
+ }),
..Options::default()
});
@@ -755,7 +760,7 @@ fn datetype(criterion: &mut Criterion) {
max_dep_date: "2025-07-04",
python_version: PythonVersion::PY313,
},
- 2,
+ 4,
);
bench_project(&benchmark, criterion);
diff --git a/crates/ruff_benchmark/benches/ty_walltime.rs b/crates/ruff_benchmark/benches/ty_walltime.rs
index d6391b9465..e1ba047eba 100644
--- a/crates/ruff_benchmark/benches/ty_walltime.rs
+++ b/crates/ruff_benchmark/benches/ty_walltime.rs
@@ -71,6 +71,8 @@ impl Display for Benchmark<'_> {
}
}
+#[track_caller]
+#[expect(clippy::cast_precision_loss)]
fn check_project(db: &ProjectDatabase, project_name: &str, max_diagnostics: usize) {
let result = db.check();
let diagnostics = result.len();
@@ -79,6 +81,12 @@ fn check_project(db: &ProjectDatabase, project_name: &str, max_diagnostics: usiz
diagnostics > 1 && diagnostics <= max_diagnostics,
"Expected between 1 and {max_diagnostics} diagnostics on project '{project_name}' but got {diagnostics}",
);
+
+ if (max_diagnostics - diagnostics) as f64 / max_diagnostics as f64 > 0.10 {
+ tracing::warn!(
+ "The expected diagnostics for project `{project_name}` can be reduced: expected {max_diagnostics} but got {diagnostics}"
+ );
+ }
}
static ALTAIR: Benchmark = Benchmark::new(
@@ -101,7 +109,7 @@ static ALTAIR: Benchmark = Benchmark::new(
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
- 1000,
+ 850,
);
static COLOUR_SCIENCE: Benchmark = Benchmark::new(
@@ -120,7 +128,7 @@ static COLOUR_SCIENCE: Benchmark = Benchmark::new(
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY310,
},
- 1070,
+ 350,
);
static FREQTRADE: Benchmark = Benchmark::new(
@@ -163,7 +171,7 @@ static PANDAS: Benchmark = Benchmark::new(
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
- 4000,
+ 3800,
);
static PYDANTIC: Benchmark = Benchmark::new(
@@ -181,7 +189,7 @@ static PYDANTIC: Benchmark = Benchmark::new(
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY39,
},
- 7000,
+ 3200,
);
static SYMPY: Benchmark = Benchmark::new(
@@ -194,7 +202,7 @@ static SYMPY: Benchmark = Benchmark::new(
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
- 13116,
+ 13400,
);
static TANJUN: Benchmark = Benchmark::new(
@@ -207,7 +215,7 @@ static TANJUN: Benchmark = Benchmark::new(
max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312,
},
- 320,
+ 110,
);
static STATIC_FRAME: Benchmark = Benchmark::new(
@@ -223,7 +231,7 @@ static STATIC_FRAME: Benchmark = Benchmark::new(
max_dep_date: "2025-08-09",
python_version: PythonVersion::PY311,
},
- 1100,
+ 1657,
);
#[track_caller]
diff --git a/crates/ty/docs/rules.md b/crates/ty/docs/rules.md
index eb6d62eb85..4a15c1c29b 100644
--- a/crates/ty/docs/rules.md
+++ b/crates/ty/docs/rules.md
@@ -467,7 +467,7 @@ def test(): -> "int":
Default level: warn ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1047,7 +1047,7 @@ class D(Generic[U, T]): ...
Default level: warn ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2895,7 +2895,7 @@ A() + A() # TypeError: unsupported operand type(s) for +: 'A' and 'A'
## `unused-ignore-comment`
-Default level: ignore ·
+Default level: warn ·
Added in 0.0.1-alpha.1 ·
Related issues ·
View source
@@ -2904,11 +2904,11 @@ Added in 0.
**What it does**
-Checks for `type: ignore` or `ty: ignore` directives that are no longer applicable.
+Checks for `ty: ignore` or `type: ignore` directives that are no longer applicable.
**Why is this bad?**
-A `type: ignore` directive that no longer matches any diagnostic violations is likely
+A `ty: ignore` directive that no longer matches any diagnostic violations is likely
included by mistake, and should be removed to avoid confusion.
**Examples**
@@ -2923,6 +2923,11 @@ Use instead:
a = 20 / 2
```
+**Options**
+
+Set [`analysis.respect-type-ignore-comments`](https://docs.astral.sh/ty/reference/configuration/#respect-type-ignore-comments)
+to `false` to prevent this rule from reporting unused `type: ignore` comments.
+
## `useless-overload-body`
diff --git a/crates/ty_project/src/metadata/options.rs b/crates/ty_project/src/metadata/options.rs
index a3cc86ef34..a250baa982 100644
--- a/crates/ty_project/src/metadata/options.rs
+++ b/crates/ty_project/src/metadata/options.rs
@@ -1292,7 +1292,7 @@ pub struct AnalysisOptions {
respect-type-ignore-comments = false
"#
)]
- respect_type_ignore_comments: Option,
+ pub respect_type_ignore_comments: Option,
}
impl AnalysisOptions {
diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/mro.md_-_Method_Resolution_Or…_-_`__bases__`_lists_wi…_(ea7ebc83ec359b54).snap b/crates/ty_python_semantic/resources/mdtest/snapshots/mro.md_-_Method_Resolution_Or…_-_`__bases__`_lists_wi…_(ea7ebc83ec359b54).snap
index 18f5ecedf7..eb6d577069 100644
--- a/crates/ty_python_semantic/resources/mdtest/snapshots/mro.md_-_Method_Resolution_Or…_-_`__bases__`_lists_wi…_(ea7ebc83ec359b54).snap
+++ b/crates/ty_python_semantic/resources/mdtest/snapshots/mro.md_-_Method_Resolution_Or…_-_`__bases__`_lists_wi…_(ea7ebc83ec359b54).snap
@@ -304,7 +304,7 @@ info: rule `duplicate-base` is enabled by default
```
```
-info[unused-ignore-comment]: Unused blanket `type: ignore` directive
+warning[unused-ignore-comment]: Unused blanket `type: ignore` directive
--> src/mdtest_snippet.py:72:9
|
70 | A,
@@ -356,7 +356,7 @@ info: rule `duplicate-base` is enabled by default
```
```
-info[unused-ignore-comment]: Unused blanket `type: ignore` directive
+warning[unused-ignore-comment]: Unused blanket `type: ignore` directive
--> src/mdtest_snippet.py:81:13
|
79 | ):
diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/ty_ignore.md_-_Suppressing_errors_w…_-_Multiple_unused_comm…_(7cbe4a1d9893a05).snap b/crates/ty_python_semantic/resources/mdtest/snapshots/ty_ignore.md_-_Suppressing_errors_w…_-_Multiple_unused_comm…_(7cbe4a1d9893a05).snap
index 5299372d53..9bd24985d0 100644
--- a/crates/ty_python_semantic/resources/mdtest/snapshots/ty_ignore.md_-_Suppressing_errors_w…_-_Multiple_unused_comm…_(7cbe4a1d9893a05).snap
+++ b/crates/ty_python_semantic/resources/mdtest/snapshots/ty_ignore.md_-_Suppressing_errors_w…_-_Multiple_unused_comm…_(7cbe4a1d9893a05).snap
@@ -27,7 +27,7 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/suppressions/ty_ignore.m
# Diagnostics
```
-info[unused-ignore-comment]: Unused `ty: ignore` directive
+warning[unused-ignore-comment]: Unused `ty: ignore` directive
--> src/mdtest_snippet.py:2:13
|
1 | # error: [unused-ignore-comment] "Unused `ty: ignore` directive"
@@ -47,7 +47,7 @@ help: Remove the unused suppression comment
```
```
-info[unused-ignore-comment]: Unused `ty: ignore` directive: 'invalid-assignment'
+warning[unused-ignore-comment]: Unused `ty: ignore` directive: 'invalid-assignment'
--> src/mdtest_snippet.py:6:26
|
4 | # error: [unused-ignore-comment] "Unused `ty: ignore` directive: 'invalid-assignment'"
@@ -70,7 +70,7 @@ help: Remove the unused suppression code
```
```
-info[unused-ignore-comment]: Unused `ty: ignore` directive: 'unresolved-reference'
+warning[unused-ignore-comment]: Unused `ty: ignore` directive: 'unresolved-reference'
--> src/mdtest_snippet.py:6:64
|
4 | # error: [unused-ignore-comment] "Unused `ty: ignore` directive: 'invalid-assignment'"
@@ -93,7 +93,7 @@ help: Remove the unused suppression code
```
```
-info[unused-ignore-comment]: Unused `ty: ignore` directive: 'invalid-assignment', 'unresolved-reference'
+warning[unused-ignore-comment]: Unused `ty: ignore` directive: 'invalid-assignment', 'unresolved-reference'
--> src/mdtest_snippet.py:9:26
|
8 | # error: [unused-ignore-comment] "Unused `ty: ignore` directive: 'invalid-assignment', 'unresolved-reference'"
diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/type_ignore.md_-_Suppressing_errors_w…_-_Nested_comments_(6e4dc67270e388d2).snap b/crates/ty_python_semantic/resources/mdtest/snapshots/type_ignore.md_-_Suppressing_errors_w…_-_Nested_comments_(6e4dc67270e388d2).snap
index ffe10ed75e..5e8ac905fa 100644
--- a/crates/ty_python_semantic/resources/mdtest/snapshots/type_ignore.md_-_Suppressing_errors_w…_-_Nested_comments_(6e4dc67270e388d2).snap
+++ b/crates/ty_python_semantic/resources/mdtest/snapshots/type_ignore.md_-_Suppressing_errors_w…_-_Nested_comments_(6e4dc67270e388d2).snap
@@ -32,7 +32,7 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/suppressions/type_ignore
# Diagnostics
```
-info[unused-ignore-comment]: Unused `ty: ignore` directive
+warning[unused-ignore-comment]: Unused `ty: ignore` directive
--> src/mdtest_snippet.py:10:9
|
8 | a = (3
@@ -55,7 +55,7 @@ help: Remove the unused suppression comment
```
```
-info[unused-ignore-comment]: Unused `ty: ignore` directive
+warning[unused-ignore-comment]: Unused `ty: ignore` directive
--> src/mdtest_snippet.py:14:21
|
12 | a = (3
diff --git a/crates/ty_python_semantic/src/suppression.rs b/crates/ty_python_semantic/src/suppression.rs
index 7b9794031c..5161c65922 100644
--- a/crates/ty_python_semantic/src/suppression.rs
+++ b/crates/ty_python_semantic/src/suppression.rs
@@ -24,10 +24,10 @@ use crate::{Db, declare_lint, lint::LintId};
declare_lint! {
/// ## What it does
- /// Checks for `type: ignore` or `ty: ignore` directives that are no longer applicable.
+ /// Checks for `ty: ignore` or `type: ignore` directives that are no longer applicable.
///
/// ## Why is this bad?
- /// A `type: ignore` directive that no longer matches any diagnostic violations is likely
+ /// A `ty: ignore` directive that no longer matches any diagnostic violations is likely
/// included by mistake, and should be removed to avoid confusion.
///
/// ## Examples
@@ -40,10 +40,14 @@ declare_lint! {
/// ```py
/// a = 20 / 2
/// ```
+ ///
+ /// ## Options
+ /// Set [`analysis.respect-type-ignore-comments`](https://docs.astral.sh/ty/reference/configuration/#respect-type-ignore-comments)
+ /// to `false` to prevent this rule from reporting unused `type: ignore` comments.
pub static UNUSED_IGNORE_COMMENT = {
- summary: "detects unused `type: ignore` comments",
+ summary: "detects unused `ty: ignore` and `type: ignore` comments",
status: LintStatus::stable("0.0.1-alpha.1"),
- default_level: Level::Ignore,
+ default_level: Level::Warn,
}
}
diff --git a/ty.schema.json b/ty.schema.json
index 7e1f86ac65..458cb9c2af 100644
--- a/ty.schema.json
+++ b/ty.schema.json
@@ -1171,9 +1171,9 @@
]
},
"unused-ignore-comment": {
- "title": "detects unused `type: ignore` comments",
- "description": "## What it does\nChecks for `type: ignore` or `ty: ignore` directives that are no longer applicable.\n\n## Why is this bad?\nA `type: ignore` directive that no longer matches any diagnostic violations is likely\nincluded by mistake, and should be removed to avoid confusion.\n\n## Examples\n```py\na = 20 / 2 # ty: ignore[division-by-zero]\n```\n\nUse instead:\n\n```py\na = 20 / 2\n```",
- "default": "ignore",
+ "title": "detects unused `ty: ignore` and `type: ignore` comments",
+ "description": "## What it does\nChecks for `ty: ignore` or `type: ignore` directives that are no longer applicable.\n\n## Why is this bad?\nA `ty: ignore` directive that no longer matches any diagnostic violations is likely\nincluded by mistake, and should be removed to avoid confusion.\n\n## Examples\n```py\na = 20 / 2 # ty: ignore[division-by-zero]\n```\n\nUse instead:\n\n```py\na = 20 / 2\n```\n\n## Options\nSet [`analysis.respect-type-ignore-comments`](https://docs.astral.sh/ty/reference/configuration/#respect-type-ignore-comments)\nto `false` to prevent this rule from reporting unused `type: ignore` comments.",
+ "default": "warn",
"oneOf": [
{
"$ref": "#/definitions/Level"