mirror of
https://github.com/astral-sh/ruff.git
synced 2026-05-06 08:56:57 -04:00
[ty] Support type:ignore[ty:code] suppressions (#24096)
This commit is contained in:
@@ -989,7 +989,7 @@ fn datetype(criterion: &mut Criterion) {
|
||||
max_dep_date: "2025-07-04",
|
||||
python_version: PythonVersion::PY313,
|
||||
},
|
||||
4,
|
||||
10,
|
||||
);
|
||||
|
||||
bench_project(&benchmark, criterion);
|
||||
|
||||
@@ -109,7 +109,7 @@ static ALTAIR: Benchmark = Benchmark::new(
|
||||
max_dep_date: "2025-06-17",
|
||||
python_version: PythonVersion::PY312,
|
||||
},
|
||||
860,
|
||||
897,
|
||||
);
|
||||
|
||||
static COLOUR_SCIENCE: Benchmark = Benchmark::new(
|
||||
|
||||
Generated
+2
-2
@@ -742,11 +742,11 @@ Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.
|
||||
|
||||
**What it does**
|
||||
|
||||
Checks for `ty: ignore[code]` where `code` isn't a known lint rule.
|
||||
Checks for `ty: ignore[code]` or `type: ignore[ty:code]` comments where `code` isn't a known lint rule.
|
||||
|
||||
**Why is this bad?**
|
||||
|
||||
A `ty: ignore[code]` directive with a `code` that doesn't match
|
||||
A `ty: ignore[code]` or a `type:ignore[ty:code] directive with a `code` that doesn't match
|
||||
any known rule will not suppress any type errors, and is probably a mistake.
|
||||
|
||||
**Examples**
|
||||
|
||||
@@ -161,6 +161,75 @@ mod tests {
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_code_existing_type_ignore() {
|
||||
let test = CodeActionTest::with_source(
|
||||
r#"
|
||||
b = <START>a<END> / 0 # type:ignore[ty:division-by-zero]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.code_actions(&UNRESOLVED_REFERENCE), @"
|
||||
info[code-action]: Ignore 'unresolved-reference' for this line
|
||||
--> main.py:2:5
|
||||
|
|
||||
2 | b = a / 0 # type:ignore[ty:division-by-zero]
|
||||
| ^
|
||||
|
|
||||
1 |
|
||||
- b = a / 0 # type:ignore[ty:division-by-zero]
|
||||
2 + b = a / 0 # type:ignore[ty:division-by-zero, ty:unresolved-reference]
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_code_existing_type_ignore_without_any_ty_code() {
|
||||
let test = CodeActionTest::with_source(
|
||||
r#"
|
||||
b = <START>a<END> / 0 # type:ignore[mypy-code]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.code_actions(&UNRESOLVED_REFERENCE), @"
|
||||
info[code-action]: Ignore 'unresolved-reference' for this line
|
||||
--> main.py:2:5
|
||||
|
|
||||
2 | b = a / 0 # type:ignore[mypy-code]
|
||||
| ^
|
||||
|
|
||||
1 |
|
||||
- b = a / 0 # type:ignore[mypy-code]
|
||||
2 + b = a / 0 # type:ignore[mypy-code] # ty:ignore[unresolved-reference]
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_ignore_existing_file_level_ignore() {
|
||||
let test = CodeActionTest::with_source(
|
||||
r#"
|
||||
# ty:ignore[division-by-zero]
|
||||
|
||||
b = <START>a<END> / 0
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.code_actions(&UNRESOLVED_REFERENCE), @"
|
||||
info[code-action]: Ignore 'unresolved-reference' for this line
|
||||
--> main.py:4:5
|
||||
|
|
||||
2 | # ty:ignore[division-by-zero]
|
||||
3 |
|
||||
4 | b = a / 0
|
||||
| ^
|
||||
|
|
||||
1 |
|
||||
2 | # ty:ignore[division-by-zero]
|
||||
3 |
|
||||
- b = a / 0
|
||||
4 + b = a / 0 # ty:ignore[unresolved-reference]
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_code_existing_ignore_trailing_comma() {
|
||||
let test = CodeActionTest::with_source(
|
||||
|
||||
@@ -507,7 +507,7 @@ the class "header":
|
||||
|
||||
class A: ...
|
||||
|
||||
class B( # type: ignore[duplicate-base]
|
||||
class B( # type: ignore[ty:duplicate-base]
|
||||
A,
|
||||
A,
|
||||
): ...
|
||||
@@ -515,7 +515,7 @@ class B( # type: ignore[duplicate-base]
|
||||
class C(
|
||||
A,
|
||||
A
|
||||
): # type: ignore[duplicate-base]
|
||||
): # type: ignore[ty:duplicate-base]
|
||||
x: int
|
||||
|
||||
# fmt: on
|
||||
@@ -532,7 +532,7 @@ exception at runtime, not a sub-expression in the class's bases list.
|
||||
class D(
|
||||
A,
|
||||
# error: [unused-type-ignore-comment]
|
||||
A, # type: ignore[duplicate-base]
|
||||
A, # type: ignore[ty:duplicate-base]
|
||||
): ...
|
||||
|
||||
# error: [duplicate-base]
|
||||
@@ -541,7 +541,7 @@ class E(
|
||||
A
|
||||
):
|
||||
# error: [unused-type-ignore-comment]
|
||||
x: int # type: ignore[duplicate-base]
|
||||
x: int # type: ignore[ty:duplicate-base]
|
||||
|
||||
# fmt: on
|
||||
```
|
||||
|
||||
+15
-15
@@ -66,7 +66,7 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/mro.md
|
||||
51 |
|
||||
52 | class A: ...
|
||||
53 |
|
||||
54 | class B( # type: ignore[duplicate-base]
|
||||
54 | class B( # type: ignore[ty:duplicate-base]
|
||||
55 | A,
|
||||
56 | A,
|
||||
57 | ): ...
|
||||
@@ -74,7 +74,7 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/mro.md
|
||||
59 | class C(
|
||||
60 | A,
|
||||
61 | A
|
||||
62 | ): # type: ignore[duplicate-base]
|
||||
62 | ): # type: ignore[ty:duplicate-base]
|
||||
63 | x: int
|
||||
64 |
|
||||
65 | # fmt: on
|
||||
@@ -84,7 +84,7 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/mro.md
|
||||
69 | class D(
|
||||
70 | A,
|
||||
71 | # error: [unused-type-ignore-comment]
|
||||
72 | A, # type: ignore[duplicate-base]
|
||||
72 | A, # type: ignore[ty:duplicate-base]
|
||||
73 | ): ...
|
||||
74 |
|
||||
75 | # error: [duplicate-base]
|
||||
@@ -93,7 +93,7 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/mro.md
|
||||
78 | A
|
||||
79 | ):
|
||||
80 | # error: [unused-type-ignore-comment]
|
||||
81 | x: int # type: ignore[duplicate-base]
|
||||
81 | x: int # type: ignore[ty:duplicate-base]
|
||||
82 |
|
||||
83 | # fmt: on
|
||||
```
|
||||
@@ -281,7 +281,7 @@ error[duplicate-base]: Duplicate base class `A`
|
||||
| _______^
|
||||
70 | | A,
|
||||
71 | | # error: [unused-type-ignore-comment]
|
||||
72 | | A, # type: ignore[duplicate-base]
|
||||
72 | | A, # type: ignore[ty:duplicate-base]
|
||||
73 | | ): ...
|
||||
| |_^
|
||||
74 |
|
||||
@@ -295,7 +295,7 @@ info: The definition of class `D` will raise `TypeError` at runtime
|
||||
70 | A,
|
||||
| - Class `A` first included in bases list here
|
||||
71 | # error: [unused-type-ignore-comment]
|
||||
72 | A, # type: ignore[duplicate-base]
|
||||
72 | A, # type: ignore[ty:duplicate-base]
|
||||
| ^ Class `A` later repeated here
|
||||
73 | ): ...
|
||||
|
|
||||
@@ -304,20 +304,20 @@ info: rule `duplicate-base` is enabled by default
|
||||
```
|
||||
|
||||
```
|
||||
warning[unused-type-ignore-comment]: Unused blanket `type: ignore` directive
|
||||
warning[unused-type-ignore-comment]: Unused `type: ignore` directive
|
||||
--> src/mdtest_snippet.py:72:9
|
||||
|
|
||||
70 | A,
|
||||
71 | # error: [unused-type-ignore-comment]
|
||||
72 | A, # type: ignore[duplicate-base]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
72 | A, # type: ignore[ty:duplicate-base]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
73 | ): ...
|
||||
|
|
||||
help: Remove the unused suppression comment
|
||||
69 | class D(
|
||||
70 | A,
|
||||
71 | # error: [unused-type-ignore-comment]
|
||||
- A, # type: ignore[duplicate-base]
|
||||
- A, # type: ignore[ty:duplicate-base]
|
||||
72 + A,
|
||||
73 | ): ...
|
||||
74 |
|
||||
@@ -337,7 +337,7 @@ error[duplicate-base]: Duplicate base class `A`
|
||||
79 | | ):
|
||||
| |_^
|
||||
80 | # error: [unused-type-ignore-comment]
|
||||
81 | x: int # type: ignore[duplicate-base]
|
||||
81 | x: int # type: ignore[ty:duplicate-base]
|
||||
|
|
||||
info: The definition of class `E` will raise `TypeError` at runtime
|
||||
--> src/mdtest_snippet.py:77:5
|
||||
@@ -356,13 +356,13 @@ info: rule `duplicate-base` is enabled by default
|
||||
```
|
||||
|
||||
```
|
||||
warning[unused-type-ignore-comment]: Unused blanket `type: ignore` directive
|
||||
warning[unused-type-ignore-comment]: Unused `type: ignore` directive
|
||||
--> src/mdtest_snippet.py:81:13
|
||||
|
|
||||
79 | ):
|
||||
80 | # error: [unused-type-ignore-comment]
|
||||
81 | x: int # type: ignore[duplicate-base]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
81 | x: int # type: ignore[ty:duplicate-base]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
82 |
|
||||
83 | # fmt: on
|
||||
|
|
||||
@@ -370,7 +370,7 @@ help: Remove the unused suppression comment
|
||||
78 | A
|
||||
79 | ):
|
||||
80 | # error: [unused-type-ignore-comment]
|
||||
- x: int # type: ignore[duplicate-base]
|
||||
- x: int # type: ignore[ty:duplicate-base]
|
||||
81 + x: int
|
||||
82 |
|
||||
83 | # fmt: on
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
|
||||
---
|
||||
mdtest name: type_ignore.md - Suppressing errors with `type: ignore` - Unused ignore comment mixed with mypy comments
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/suppressions/type_ignore.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | # error: [unused-type-ignore-comment] "Unused `type: ignore` directive: 'division-by-zero'"
|
||||
2 | a = 10 / 2 # type: ignore[mypy-code, ty:division-by-zero]
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
warning[unused-type-ignore-comment]: Unused `type: ignore` directive: 'division-by-zero'
|
||||
--> src/mdtest_snippet.py:2:39
|
||||
|
|
||||
1 | # error: [unused-type-ignore-comment] "Unused `type: ignore` directive: 'division-by-zero'"
|
||||
2 | a = 10 / 2 # type: ignore[mypy-code, ty:division-by-zero]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: Remove the unused suppression code
|
||||
1 | # error: [unused-type-ignore-comment] "Unused `type: ignore` directive: 'division-by-zero'"
|
||||
- a = 10 / 2 # type: ignore[mypy-code, ty:division-by-zero]
|
||||
2 + a = 10 / 2 # type: ignore[mypy-code]
|
||||
|
||||
```
|
||||
@@ -167,15 +167,13 @@ a = 4 / 0 # ty: ignore[]
|
||||
|
||||
## File-level suppression comments
|
||||
|
||||
File level suppression comments are currently intentionally unsupported because we've yet to decide
|
||||
if they should use a different syntax that also supports enabling rules or changing the rule's
|
||||
severity: `ty: possibly-undefined-reference=error`
|
||||
File level suppression comments suppress all errors in a file with a given code.
|
||||
|
||||
```py
|
||||
# error: [unused-ignore-comment]
|
||||
# ty: ignore[division-by-zero]
|
||||
|
||||
a = 4 / 0 # error: [division-by-zero]
|
||||
a = 4 / 0
|
||||
b = a + c # error: [unresolved-reference]
|
||||
```
|
||||
|
||||
## Unknown rule
|
||||
|
||||
@@ -132,11 +132,19 @@ a = f"""
|
||||
|
||||
## Codes
|
||||
|
||||
Mypy supports `type: ignore[code]`. ty doesn't understand mypy's rule names. Therefore, ignore the
|
||||
codes and suppress all errors.
|
||||
Similar to mypy support `type: ignore[codes]` comments. But unlike mypy, ty only respects codes
|
||||
starting with `ty:` to avoid ambiguity with suppression comments from mypy and other type checkers.
|
||||
|
||||
```py
|
||||
a = test # type: ignore[name-defined]
|
||||
a = test # type: ignore[name-defined, ty:unresolved-reference]
|
||||
```
|
||||
|
||||
## Unknown codes starting with `ty`
|
||||
|
||||
```py
|
||||
# error: [unresolved-reference]
|
||||
# error: [ignore-comment-unknown-rule]
|
||||
a = test # type: ignore[ty:name-defined]
|
||||
```
|
||||
|
||||
## Nested comments
|
||||
@@ -190,6 +198,15 @@ a = 10 / 0
|
||||
b = a / 0
|
||||
```
|
||||
|
||||
## File level suppression with code
|
||||
|
||||
```py
|
||||
# type: ignore[ty:division-by-zero]
|
||||
|
||||
a = 10 / 0
|
||||
b = a + c # error: [unresolved-reference]
|
||||
```
|
||||
|
||||
## File level suppression with leading shebang
|
||||
|
||||
```py
|
||||
@@ -242,3 +259,26 @@ ty doesn't report invalid `type: ignore` comments:
|
||||
```py
|
||||
a = 10 + 4 # type: ignoreee
|
||||
```
|
||||
|
||||
## Unused ignore comment mixed with mypy comments
|
||||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
||||
```py
|
||||
# error: [unused-type-ignore-comment] "Unused `type: ignore` directive: 'division-by-zero'"
|
||||
a = 10 / 2 # type: ignore[mypy-code, ty:division-by-zero]
|
||||
```
|
||||
|
||||
## Unused ignore comment
|
||||
|
||||
```py
|
||||
# error: [unused-type-ignore-comment] "Unused `type: ignore` directive"
|
||||
a = 10 / 2 # type: ignore[ty:division-by-zero]
|
||||
```
|
||||
|
||||
## Unknown ignore code
|
||||
|
||||
```py
|
||||
# error: [ignore-comment-unknown-rule] "Unknown rule `division-by`. Did you mean"
|
||||
a = 10 / 2 # type: ignore[ty:division-by]
|
||||
```
|
||||
|
||||
@@ -83,10 +83,10 @@ declare_lint! {
|
||||
|
||||
declare_lint! {
|
||||
/// ## What it does
|
||||
/// Checks for `ty: ignore[code]` where `code` isn't a known lint rule.
|
||||
/// Checks for `ty: ignore[code]` or `type: ignore[ty:code]` comments where `code` isn't a known lint rule.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// A `ty: ignore[code]` directive with a `code` that doesn't match
|
||||
/// A `ty: ignore[code]` or a `type:ignore[ty:code] directive with a `code` that doesn't match
|
||||
/// any known rule will not suppress any type errors, and is probably a mistake.
|
||||
///
|
||||
/// ## Examples
|
||||
@@ -204,7 +204,7 @@ pub(crate) fn check_suppressions(
|
||||
context.diagnostics.into_inner().into_diagnostics()
|
||||
}
|
||||
|
||||
/// Checks for `ty: ignore` comments that reference unknown rules.
|
||||
/// Checks for `ty: ignore` and `type: ignore[ty:<code>]` comments that reference unknown rules.
|
||||
fn check_unknown_rule(context: &mut CheckSuppressionsContext) {
|
||||
if context.is_lint_disabled(&IGNORE_COMMENT_UNKNOWN_RULE) {
|
||||
return;
|
||||
@@ -339,8 +339,6 @@ pub(crate) struct Suppressions {
|
||||
///
|
||||
/// The suppressions are sorted by [`Suppression::comment_range`] and the [`Suppression::suppressed_range`]
|
||||
/// spans the entire file.
|
||||
///
|
||||
/// For now, this is limited to `type: ignore` comments.
|
||||
file: SmallVec<[Suppression; 1]>,
|
||||
|
||||
/// Suppressions that apply to a specific line (or lines).
|
||||
@@ -531,7 +529,7 @@ struct SuppressionsBuilder<'a> {
|
||||
lint_registry: &'a LintRegistry,
|
||||
source: &'a str,
|
||||
|
||||
/// `type: ignore` comments at the top of the file before any non-trivia code apply to the entire file.
|
||||
/// Ignore comments at the top of the file before any non-trivia code apply to the entire file.
|
||||
/// This boolean tracks if there has been any non trivia token.
|
||||
seen_non_trivia_token: bool,
|
||||
|
||||
@@ -574,13 +572,13 @@ impl<'a> SuppressionsBuilder<'a> {
|
||||
|
||||
#[expect(clippy::needless_pass_by_value)]
|
||||
fn add_comment(&mut self, comment: SuppressionComment, line_range: TextRange) {
|
||||
// `type: ignore` comments at the start of the file apply to the entire range.
|
||||
// ignore comments at the start of the file apply to the entire range.
|
||||
// > A # type: ignore comment on a line by itself at the top of a file, before any docstrings,
|
||||
// > imports, or other executable code, silences all errors in the file.
|
||||
// > Blank lines and other comments, such as shebang lines and coding cookies,
|
||||
// > may precede the # type: ignore comment.
|
||||
// > https://typing.python.org/en/latest/spec/directives.html#type-ignore-comments
|
||||
let is_file_suppression = comment.kind().is_type_ignore() && !self.seen_non_trivia_token;
|
||||
let is_file_suppression = !self.seen_non_trivia_token;
|
||||
|
||||
let suppressed_range = if is_file_suppression {
|
||||
TextRange::new(0.into(), self.source.text_len())
|
||||
@@ -588,7 +586,7 @@ impl<'a> SuppressionsBuilder<'a> {
|
||||
line_range
|
||||
};
|
||||
|
||||
let mut push_type_ignore_suppression = |suppression: Suppression| {
|
||||
let mut push_ignore_suppression = |suppression: Suppression| {
|
||||
if is_file_suppression {
|
||||
self.file.push(suppression);
|
||||
} else {
|
||||
@@ -599,7 +597,7 @@ impl<'a> SuppressionsBuilder<'a> {
|
||||
match comment.codes() {
|
||||
// `type: ignore`
|
||||
None => {
|
||||
push_type_ignore_suppression(Suppression {
|
||||
push_ignore_suppression(Suppression {
|
||||
target: SuppressionTarget::All,
|
||||
kind: comment.kind(),
|
||||
comment_range: comment.range(),
|
||||
@@ -608,22 +606,9 @@ impl<'a> SuppressionsBuilder<'a> {
|
||||
});
|
||||
}
|
||||
|
||||
// `type: ignore[..]`
|
||||
// The suppression applies to all lints if it is a `type: ignore`
|
||||
// comment. `type: ignore` apply to all lints for better mypy compatibility.
|
||||
Some(_) if comment.kind().is_type_ignore() => {
|
||||
push_type_ignore_suppression(Suppression {
|
||||
target: SuppressionTarget::All,
|
||||
kind: comment.kind(),
|
||||
comment_range: comment.range(),
|
||||
range: comment.range(),
|
||||
suppressed_range,
|
||||
});
|
||||
}
|
||||
|
||||
// `ty: ignore[]`
|
||||
// `ty: ignore[]` or `type: ignore[]`
|
||||
Some([]) => {
|
||||
self.line.push(Suppression {
|
||||
push_ignore_suppression(Suppression {
|
||||
target: SuppressionTarget::Empty,
|
||||
kind: comment.kind(),
|
||||
range: comment.range(),
|
||||
@@ -632,14 +617,25 @@ impl<'a> SuppressionsBuilder<'a> {
|
||||
});
|
||||
}
|
||||
|
||||
// `ty: ignore[a, b]`
|
||||
// `ty: ignore[a, b]` or `type: ignore[a, b]`
|
||||
Some(codes) => {
|
||||
for &code_range in codes {
|
||||
let code = &self.source[code_range];
|
||||
|
||||
// For `type:ignore`, ignore codes that don't start with `ty:`.
|
||||
let code = if comment.kind().is_type_ignore() {
|
||||
if let Some(prefix) = code.strip_prefix("ty:") {
|
||||
prefix
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
code
|
||||
};
|
||||
|
||||
match self.lint_registry.get(code) {
|
||||
Ok(lint) => {
|
||||
self.line.push(Suppression {
|
||||
push_ignore_suppression(Suppression {
|
||||
target: SuppressionTarget::Lint(lint),
|
||||
kind: comment.kind(),
|
||||
range: code_range,
|
||||
|
||||
@@ -14,7 +14,7 @@ use smallvec::SmallVec;
|
||||
|
||||
use crate::Db;
|
||||
use crate::lint::LintId;
|
||||
use crate::suppression::{SuppressionTarget, Suppressions, suppressions};
|
||||
use crate::suppression::{SuppressionKind, SuppressionTarget, Suppressions, suppressions};
|
||||
|
||||
/// Creates fixes to suppress all violations in `ids_with_range`.
|
||||
///
|
||||
@@ -183,7 +183,10 @@ fn append_to_existing_or_add_end_of_line_suppression(
|
||||
up_to_line_end.trim_end_matches(|c| !matches!(c, '\n' | '\r') && c.is_whitespace());
|
||||
let trailing_whitespace_len = up_to_line_end.text_len() - up_to_first_content.text_len();
|
||||
|
||||
let insertion = format!(" # ty:ignore[{codes}]", codes = Codes(codes));
|
||||
let insertion = format!(
|
||||
" # ty:ignore[{codes}]",
|
||||
codes = Codes(SuppressionKind::Ty, codes)
|
||||
);
|
||||
|
||||
Fix::safe_edit(if trailing_whitespace_len == TextSize::ZERO {
|
||||
Edit::insertion(insertion, line_end)
|
||||
@@ -218,9 +221,9 @@ fn add_to_existing_suppression(
|
||||
let up_to_last_code = before_closing_paren.trim_end();
|
||||
|
||||
let insertion = if up_to_last_code.ends_with(',') {
|
||||
format!(" {codes}", codes = Codes(codes))
|
||||
format!(" {codes}", codes = Codes(existing.kind, codes))
|
||||
} else {
|
||||
format!(", {codes}", codes = Codes(codes))
|
||||
format!(", {codes}", codes = Codes(existing.kind, codes))
|
||||
};
|
||||
|
||||
let relative_offset_from_end = comment_text.text_len() - up_to_last_code.text_len();
|
||||
@@ -231,10 +234,18 @@ fn add_to_existing_suppression(
|
||||
)))
|
||||
}
|
||||
|
||||
struct Codes<'a>(&'a [LintName]);
|
||||
struct Codes<'a>(SuppressionKind, &'a [LintName]);
|
||||
|
||||
impl std::fmt::Display for Codes<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.join(", ").entries(self.0).finish()
|
||||
let mut joiner = f.join(", ");
|
||||
|
||||
let namespace = if self.0.is_type_ignore() { "ty:" } else { "" };
|
||||
|
||||
for item in self.1 {
|
||||
joiner.entry(&format_args!("{namespace}{item}"));
|
||||
}
|
||||
|
||||
joiner.finish()
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,4 +23,4 @@ def snake_case(name: str) -> str:
|
||||
|
||||
|
||||
def get_indent(line: str) -> str:
|
||||
return re.match(r"^\s*", line).group() # type: ignore[union-attr]
|
||||
return re.match(r"^\s*", line).group() # type: ignore[union-attr, ty:unresolved-attribute]
|
||||
|
||||
Generated
+1
-1
@@ -596,7 +596,7 @@
|
||||
},
|
||||
"ignore-comment-unknown-rule": {
|
||||
"title": "detects `ty: ignore` comments that reference unknown rules",
|
||||
"description": "## What it does\nChecks for `ty: ignore[code]` where `code` isn't a known lint rule.\n\n## Why is this bad?\nA `ty: ignore[code]` directive with a `code` that doesn't match\nany known rule will not suppress any type errors, and is probably a mistake.\n\n## Examples\n```py\na = 20 / 0 # ty: ignore[division-by-zer]\n```\n\nUse instead:\n\n```py\na = 20 / 0 # ty: ignore[division-by-zero]\n```",
|
||||
"description": "## What it does\nChecks for `ty: ignore[code]` or `type: ignore[ty:code]` comments where `code` isn't a known lint rule.\n\n## Why is this bad?\nA `ty: ignore[code]` or a `type:ignore[ty:code] directive with a `code` that doesn't match\nany known rule will not suppress any type errors, and is probably a mistake.\n\n## Examples\n```py\na = 20 / 0 # ty: ignore[division-by-zer]\n```\n\nUse instead:\n\n```py\na = 20 / 0 # ty: ignore[division-by-zero]\n```",
|
||||
"default": "warn",
|
||||
"oneOf": [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user