Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions crates/ty/docs/rules.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## What it does

Checks for `ty: ignore` comments that don't specify which rules to ignore.

## Why is this bad?

A blanket `ty: ignore` comment suppresses every type-checking diagnostic on the
applicable line or file. Specifying rule codes documents which diagnostics are
expected and prevents the comment from silencing unrelated errors.

## Examples

```py
# error
value = unknown # ty: ignore
```

Use instead:

```py
value = unknown # ty: ignore[unresolved-reference]
```
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ this:

```py
try:
import optional_dependency # ty: ignore
import optional_dependency # ty: ignore[unresolved-import]
except ImportError:
optional_dependency = None

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Blanket `ty: ignore` comments

The optional `blanket-ignore-comment` rule requires `ty: ignore` comments to include specific rule
codes.

```toml
[rules]
blanket-ignore-comment = "error"
```

## Line-level ignores

```py
# error: [blanket-ignore-comment]
a = unresolved # ty: ignore

b = unresolved # ty: ignore[unresolved-reference]

# `blanket-ignore-comment` covers only `ty: ignore`; we can also add `blanket-type-ignore-comment`,
# parallel to `unused-ignore-comment` and `unused-type-ignore-comment`. In the meantime, blanket
# `type: ignore` can also be caught by Ruff's PGH003 rule.
c = unresolved # type: ignore
```

## Unused ignore comments

When `unused-ignore-comment` is enabled, an unused `ty: ignore` comment without rule codes triggers
both rules.

```py
# error: [unused-ignore-comment] "Unused `ty: ignore` without a code"
d = 1 # ty: ignore[]

# error: [blanket-ignore-comment]
# error: [unused-ignore-comment] "Unused blanket `ty: ignore` directive"
Comment thread
MichaReiser marked this conversation as resolved.
e = 1 # ty: ignore
```

## Suppression diagnostics

Suppression-related diagnostics are checked before `blanket-ignore-comment`. A blanket ignore that
suppresses an `ignore-comment-unknown-rule` or `invalid-ignore-comment` diagnostic therefore counts
as used:

```py
# The nested ignore contains an unknown rule.
# error: [blanket-ignore-comment]
a = 1 # ty: ignore # ty: ignore[not-a-rule]

# The nested ignore is invalid.
# error: [blanket-ignore-comment]
b = 1 # ty: ignore # ty: ignore[*]
```

## File-level ignores

The rule also detects file-level blanket ignores:

```py
# error: [blanket-ignore-comment]
# ty: ignore

a = unresolved
```

## Suppressing the rule

A blanket ignore can be suppressed by a code-specific ignore:

```py
a = unresolved # ty: ignore # ty: ignore[blanket-ignore-comment]
```
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# Suppressing errors with `ty: ignore`

Type check errors can be suppressed by a `ty: ignore` comment on the same line as the violation.
Type check errors can be suppressed by a `ty: ignore` comment on the same line as the violation. The
optional `blanket-ignore-comment` rule is tested separately so that these examples can exercise bare
ignore comments.

```toml
[rules]
blanket-ignore-comment = "ignore"
```

## Simple `ty: ignore`

Expand Down
4 changes: 3 additions & 1 deletion crates/ty_python_semantic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
)]
use crate::lint::{LintRegistry, LintRegistryBuilder};
use crate::suppression::{
IGNORE_COMMENT_UNKNOWN_RULE, INVALID_IGNORE_COMMENT, UNUSED_TYPE_IGNORE_COMMENT,
BLANKET_IGNORE_COMMENT, IGNORE_COMMENT_UNKNOWN_RULE, INVALID_IGNORE_COMMENT,
UNUSED_TYPE_IGNORE_COMMENT,
};
use crate::types::check_types;
pub use db::Db;
Expand Down Expand Up @@ -85,6 +86,7 @@ pub fn register_lints(registry: &mut LintRegistryBuilder) {
registry.register_lint(&UNUSED_TYPE_IGNORE_COMMENT);
registry.register_lint(&IGNORE_COMMENT_UNKNOWN_RULE);
registry.register_lint(&INVALID_IGNORE_COMMENT);
registry.register_lint(&BLANKET_IGNORE_COMMENT);
}

#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)]
Expand Down
37 changes: 37 additions & 0 deletions crates/ty_python_semantic/src/suppression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ declare_lint! {
}
}

declare_lint! {
#[doc = include_str!("../resources/lint_docs/blanket-ignore-comment.md")]
pub(crate) static BLANKET_IGNORE_COMMENT = {
summary: "detects blanket `ty: ignore` comments",
status: LintStatus::stable("0.0.57"),
default_level: Level::Ignore,
}
}

pub fn is_unused_ignore_comment_lint(name: LintName) -> bool {
name == UNUSED_IGNORE_COMMENT.name() || name == UNUSED_TYPE_IGNORE_COMMENT.name()
}
Expand Down Expand Up @@ -127,11 +136,39 @@ pub(crate) fn check_suppressions(

check_unknown_rule(&mut context);
check_invalid_suppression(&mut context);
check_blanket_suppressions(&mut context);
check_unused_suppressions(&mut context);

context.diagnostics.into_inner().into_diagnostics()
}

fn check_blanket_suppressions(context: &mut CheckSuppressionsContext) {
if context.is_lint_disabled(&BLANKET_IGNORE_COMMENT) {
return;
}

for suppression in context.suppressions.iter().filter(|suppression| {
suppression.kind == SuppressionKind::Ty && suppression.target == SuppressionTarget::All
}) {
// A blanket suppression cannot suppress its own diagnostic, but a code-specific
// suppression can.
if let Some(code_suppression) = context
.suppressions
.lint_suppressions(suppression.range, LintId::of(&BLANKET_IGNORE_COMMENT))
.find(|candidate| candidate.target.is_lint())
{
context
.diagnostics
.borrow_mut()
.mark_used(code_suppression.id());
} else if let Some(diag) =
context.report_unchecked(&BLANKET_IGNORE_COMMENT, suppression.range)
{
diag.into_diagnostic("Use specific rule codes in `ty: ignore`");
}
}
}

/// 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) {
Expand Down
10 changes: 10 additions & 0 deletions ty.schema.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading