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
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions crates/red_knot_python_semantic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ ruff_python_stdlib = { workspace = true }
ruff_source_file = { workspace = true }
ruff_text_size = { workspace = true }
ruff_python_literal = { workspace = true }
ruff_python_trivia = { workspace = true }

anyhow = { workspace = true }
bitflags = { workspace = true }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Suppressing errors with `knot: ignore`

Type check errors can be suppressed by a `knot: ignore` comment on the same line as the violation.

## Simple `knot: ignore`

```py
a = 4 + test # knot: ignore
```

## Suppressing a specific code

```py
a = 4 + test # knot: ignore[unresolved-reference]
```

## Useless suppression

TODO: Red Knot should emit an `unused-suppression` diagnostic for the
`possibly-unresolved-reference` suppression.

```py
test = 10
a = test + 3 # knot: ignore[possibly-unresolved-reference]
```

## Useless suppression if the error codes don't match

TODO: Red Knot should emit a `unused-suppression` diagnostic for the `possibly-unresolved-reference`
suppression because it doesn't match the actual `unresolved-reference` diagnostic.

```py
# error: [unresolved-reference]
a = test + 3 # knot: ignore[possibly-unresolved-reference]
```

## Multiple suppressions

```py
# fmt: off
def test(a: f"f-string type annotation", b: b"byte-string-type-annotation"): ... # knot: ignore[fstring-type-annotation, byte-string-type-annotation]
```

## Can't suppress syntax errors

<!-- blacken-docs:off -->

```py
# error: [invalid-syntax]
def test( # knot: ignore
```

<!-- blacken-docs:on -->

## Can't suppress `revealed-type` diagnostics

```py
a = 10
# revealed: Literal[10]
reveal_type(a) # knot: ignore
```

## Extra whitespace in type ignore comments is allowed

```py
a = 10 / 0 # knot : ignore
a = 10 / 0 # knot: ignore [ division-by-zero ]
```

## Whitespace is optional

```py
# fmt: off
a = 10 / 0 #knot:ignore[division-by-zero]
```

## Trailing codes comma

Trailing commas in the codes section are allowed:

```py
a = 10 / 0 # knot: ignore[division-by-zero,]
```

## Invalid characters in codes

```py
# error: [division-by-zero]
a = 10 / 0 # knot: ignore[*-*]
```

## Trailing whitespace

<!-- blacken-docs:off -->

```py
a = 10 / 0 # knot: ignore[division-by-zero]
# ^^^^^^ trailing whitespace
```

<!-- blacken-docs:on -->

## Missing comma

A missing comma results in an invalid suppression comment. We may want to recover from this in the
future.

```py
# error: [unresolved-reference]
a = x / 0 # knot: ignore[division-by-zero unresolved-reference]
```

## Empty codes

An empty codes array suppresses no-diagnostics and is always useless
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something that we should flag, or just let it pass silently?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch. I updated #15084 to correctly mark the suppression as unused


```py
# error: [division-by-zero]
a = 4 / 0 # knot: ignore[]
```
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,8 @@ a = test # type: ignore[name-defined]

## Nested comments

TODO: We should support this for better interopability with other suppression comments.

```py
# fmt: off
# TODO this error should be suppressed
# error: [unresolved-reference]
a = test \
+ 2 # fmt: skip # type: ignore

Expand Down
12 changes: 9 additions & 3 deletions crates/red_knot_python_semantic/src/db.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::lint::RuleSelection;
use crate::lint::{LintRegistry, RuleSelection};
use ruff_db::files::File;
use ruff_db::{Db as SourceDb, Upcast};

Expand All @@ -8,6 +8,8 @@ pub trait Db: SourceDb + Upcast<dyn SourceDb> {
fn is_file_open(&self, file: File) -> bool;

fn rule_selection(&self) -> &RuleSelection;

fn lint_registry(&self) -> &LintRegistry;
}

#[cfg(test)]
Expand All @@ -19,7 +21,7 @@ pub(crate) mod tests {
use crate::{default_lint_registry, ProgramSettings, PythonPlatform};

use super::Db;
use crate::lint::RuleSelection;
use crate::lint::{LintRegistry, RuleSelection};
use anyhow::Context;
use ruff_db::files::{File, Files};
use ruff_db::system::{DbWithTestSystem, System, SystemPathBuf, TestSystem};
Expand All @@ -45,7 +47,7 @@ pub(crate) mod tests {
vendored: red_knot_vendored::file_system().clone(),
events: Arc::default(),
files: Files::default(),
rule_selection: Arc::new(RuleSelection::from_registry(&default_lint_registry())),
rule_selection: Arc::new(RuleSelection::from_registry(default_lint_registry())),
}
}

Expand Down Expand Up @@ -112,6 +114,10 @@ pub(crate) mod tests {
fn rule_selection(&self) -> &RuleSelection {
&self.rule_selection
}

fn lint_registry(&self) -> &LintRegistry {
default_lint_registry()
}
}

#[salsa::db]
Expand Down
14 changes: 9 additions & 5 deletions crates/red_knot_python_semantic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,15 @@ mod visibility_constraints;

type FxOrderSet<V> = ordermap::set::OrderSet<V, BuildHasherDefault<FxHasher>>;

/// Creates a new registry with all known semantic lints.
pub fn default_lint_registry() -> LintRegistry {
let mut registry = LintRegistryBuilder::default();
register_lints(&mut registry);
registry.build()
/// Returns the default registry with all known semantic lints.
pub fn default_lint_registry() -> &'static LintRegistry {
static REGISTRY: std::sync::LazyLock<LintRegistry> = std::sync::LazyLock::new(|| {
let mut registry = LintRegistryBuilder::default();
register_lints(&mut registry);
registry.build()
});

&REGISTRY
}

/// Register all known semantic lints.
Expand Down
4 changes: 2 additions & 2 deletions crates/red_knot_python_semantic/src/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ impl LintRegistryBuilder {
}
}

#[derive(Default, Debug)]
#[derive(Default, Debug, Clone)]
pub struct LintRegistry {
lints: Vec<LintId>,
by_name: FxHashMap<&'static str, LintEntry>,
Expand Down Expand Up @@ -385,7 +385,7 @@ pub enum GetLintError {
Unknown(String),
}

#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum LintEntry {
/// An existing lint rule. Can be in preview, stable or deprecated.
Lint(LintId),
Expand Down
Loading