notignored-cli 0.1.2__py3-none-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- notignored_cli-0.1.2.data/scripts/notignored.exe +0 -0
- notignored_cli-0.1.2.dist-info/METADATA +416 -0
- notignored_cli-0.1.2.dist-info/RECORD +6 -0
- notignored_cli-0.1.2.dist-info/WHEEL +4 -0
- notignored_cli-0.1.2.dist-info/licenses/LICENSE +21 -0
- notignored_cli-0.1.2.dist-info/sboms/notignored.cyclonedx.json +1724 -0
|
Binary file
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: notignored-cli
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Environment :: Console
|
|
6
|
+
Classifier: Intended Audience :: Developers
|
|
7
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Classifier: Programming Language :: Rust
|
|
10
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
11
|
+
Classifier: Topic :: Software Development :: Testing
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Summary: Extract lint and type-check suppression comments from source files natively and fast — no linter subprocesses.
|
|
14
|
+
Keywords: cli,lint,noqa,suppression,code-review
|
|
15
|
+
Home-Page: https://github.com/nickderobertis/notignored
|
|
16
|
+
Author: Nick DeRobertis
|
|
17
|
+
License: MIT
|
|
18
|
+
Requires-Python: >=3.9
|
|
19
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
20
|
+
Project-URL: Homepage, https://github.com/nickderobertis/notignored
|
|
21
|
+
Project-URL: Repository, https://github.com/nickderobertis/notignored
|
|
22
|
+
|
|
23
|
+
# notignored
|
|
24
|
+
|
|
25
|
+
Find every lint and type-check suppression comment in a codebase — natively, and
|
|
26
|
+
fast.
|
|
27
|
+
|
|
28
|
+
```console
|
|
29
|
+
$ notignored src/
|
|
30
|
+
src/app.py:3:12 ruff F401 (line) -- re-exported for the public API
|
|
31
|
+
src/app.py:5:58 ruff E501 (line) -- long wrapped URL
|
|
32
|
+
src/app.py:10:17 ruff * (line)
|
|
33
|
+
src/vendored.py:1:1 ruff E501 (file) -- vendored upstream, not ours to reformat
|
|
34
|
+
notignored: 4 ignores in 2 files
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Why
|
|
38
|
+
|
|
39
|
+
Suppression comments are where lint and type-check debt hides. A `# noqa` costs
|
|
40
|
+
one line to add and scrolls past review unremarked — especially in agent-written
|
|
41
|
+
code, where silencing a rule is often easier than fixing it.
|
|
42
|
+
|
|
43
|
+
`notignored` turns every suppression into a first-class, queryable record: which
|
|
44
|
+
tool, which rules, the stated reason, and exactly where it lives. That makes a
|
|
45
|
+
high-level review of a large change possible — you can see the bypasses and their
|
|
46
|
+
justifications without reading every line.
|
|
47
|
+
|
|
48
|
+
It parses the directives itself and **never invokes the tool whose rule is being
|
|
49
|
+
silenced**, so scanning a tree costs a read and a scan instead of ten linter
|
|
50
|
+
startups. That is what makes it cheap enough to run on every pull request.
|
|
51
|
+
|
|
52
|
+
## Install
|
|
53
|
+
|
|
54
|
+
```console
|
|
55
|
+
# From PyPI or npm — both ship the prebuilt binary, so no Rust toolchain is
|
|
56
|
+
# needed and nothing is compiled at install time:
|
|
57
|
+
pip install notignored-cli
|
|
58
|
+
npm install -g notignored-cli
|
|
59
|
+
|
|
60
|
+
# Or without installing at all:
|
|
61
|
+
npx notignored-cli src/
|
|
62
|
+
|
|
63
|
+
# Cross-platform, from source (Linux, macOS, Windows):
|
|
64
|
+
cargo install --git https://github.com/nickderobertis/notignored --locked
|
|
65
|
+
|
|
66
|
+
# Or a prebuilt binary (Linux, macOS, and Windows under a POSIX shell):
|
|
67
|
+
curl -fsSL https://raw.githubusercontent.com/nickderobertis/notignored/main/scripts/install.sh | sh
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
All four install the same `notignored` command. The `notignored-cli`
|
|
71
|
+
distributions carry the release binary for your platform — a wheel per platform
|
|
72
|
+
on PyPI, a package per platform on npm, picked automatically — so they are the
|
|
73
|
+
fastest path on a CI image with no Rust toolchain, and the only one that works
|
|
74
|
+
where github.com is blocked but the package registries are not. Prebuilt targets
|
|
75
|
+
are Linux (x64, arm64), macOS (x64, arm64), and Windows (x64); anywhere else,
|
|
76
|
+
`cargo install` builds from source.
|
|
77
|
+
|
|
78
|
+
The installer honours `NOTIGNORED_VERSION` / `NOTIGNORED_INSTALL_DIR` (or the
|
|
79
|
+
`--version` / `--to` flags), verifies the archive against the SHA-256 checksum
|
|
80
|
+
published beside it, and refuses to install a binary it cannot verify. Every
|
|
81
|
+
tagged release attaches per-platform archives built on native runners.
|
|
82
|
+
|
|
83
|
+
In CI there is nothing to install: the [GitHub Action](#on-a-pull-request-the-github-action)
|
|
84
|
+
fetches the release binary itself and posts what the pull request added.
|
|
85
|
+
|
|
86
|
+
```yaml
|
|
87
|
+
- uses: nickderobertis/notignored@main
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Try it
|
|
91
|
+
|
|
92
|
+
[`examples/`](examples) holds a handful of tiny files — Python, TypeScript,
|
|
93
|
+
shell, Rust — each carrying the kind of suppression, and the kind of reason,
|
|
94
|
+
real code collects. Point the binary at them:
|
|
95
|
+
|
|
96
|
+
```console
|
|
97
|
+
$ notignored examples/
|
|
98
|
+
examples/api_client.py:3:20 mypy import-untyped (line) -- the vendored SDK ships no type stubs
|
|
99
|
+
examples/api_client.py:4:28 ruff F401 (line) -- re-exported so callers can configure retries
|
|
100
|
+
examples/deploy.sh:8:1 shellcheck SC2086 (next-line) -- the flags file is ours, and has to split into separate arguments
|
|
101
|
+
examples/deploy.sh:13:3 llmlint tool_output_is_signal (file) -- example input the README quickstart scans, not a script this project runs
|
|
102
|
+
examples/retry.rs:6:1 rust dead_code (next-line) -- the scheduler starts calling this once backoff lands
|
|
103
|
+
examples/widget.ts:6:3 eslint no-console (next-line) -- the mount path is traced in production
|
|
104
|
+
examples/widget.ts:9:3 typescript * (next-line) -- the vendored analytics global is declared without its options bag
|
|
105
|
+
notignored: 7 ignores in 4 files
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Seven suppressions, seven tools, one pass over four files — no linter was run. Each
|
|
109
|
+
line is `path:line:column tool rules (scope) -- reason`; `--format json` gives
|
|
110
|
+
the same records as the envelope [below](#output), and `--format markdown` gives
|
|
111
|
+
the comment the action posts. That block is checked against the real binary by
|
|
112
|
+
`tests/e2e/examples.rs`, so it is output, not an illustration.
|
|
113
|
+
|
|
114
|
+
## Usage
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
notignored [PATHS...] [--format human|json|markdown] [--tool NAME]... [--fail-if-found]
|
|
118
|
+
[--diff [--diff-base REF]] [--github-repo OWNER/REPO] [--github-sha SHA]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
- `PATHS` — files and/or directories. Directories are walked recursively,
|
|
122
|
+
honouring `.gitignore`. Defaults to `.`.
|
|
123
|
+
- `--format` — `human` (default), `json`, or `markdown` (a pull-request comment
|
|
124
|
+
body; see the action below).
|
|
125
|
+
- `--tool` — only report this tool; repeat to allow several. Omit for all.
|
|
126
|
+
- `--fail-if-found` — exit 1 when any suppression is reported.
|
|
127
|
+
- `--diff` — report only the suppressions the change added (see below).
|
|
128
|
+
- `--diff-base` — the git revision or range `--diff` compares against.
|
|
129
|
+
- `--github-repo` / `--github-sha` — the `owner/repo` and commit the `markdown`
|
|
130
|
+
format builds its permalinks from.
|
|
131
|
+
|
|
132
|
+
### Reviewing a pull request
|
|
133
|
+
|
|
134
|
+
A reviewer cares about the suppressions a change *introduces*, not the
|
|
135
|
+
inventory it inherited. `--diff` reports only those: a directive is new when the
|
|
136
|
+
diff added at least one of the lines it occupies.
|
|
137
|
+
|
|
138
|
+
```console
|
|
139
|
+
$ notignored --diff --diff-base main --fail-if-found
|
|
140
|
+
src/app.py:42:20 ruff E501 (line) -- long wrapped URL
|
|
141
|
+
notignored: 1 ignore in 1 file
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
- Bare `--diff` compares the work tree — staged *and* unstaged — against `HEAD`.
|
|
145
|
+
- `--diff-base REF` takes any git revision or range. A **plain ref** is compared
|
|
146
|
+
from the **merge base**, the way a pull request's "Files changed" is, so
|
|
147
|
+
commits that landed on the base branch after this one forked are never
|
|
148
|
+
reported as this branch's own. An explicit `A..B` **range** is passed to git
|
|
149
|
+
as-is, two-dot semantics and all. These are llmlint's `--diff` / `--diff-base`
|
|
150
|
+
semantics exactly.
|
|
151
|
+
- `PATHS` still narrow the result: `notignored --diff --diff-base main src/`
|
|
152
|
+
reports the new suppressions under `src/` only. An empty intersection is a
|
|
153
|
+
clean exit 0.
|
|
154
|
+
- Only the files the change touched are read, so a diff run stays fast on a
|
|
155
|
+
large repository. Files the change deleted are skipped; a renamed file reports
|
|
156
|
+
what the change added to it, not the lines that merely moved.
|
|
157
|
+
- Git names a file in bytes and a report names it with a string, so a path that
|
|
158
|
+
is not valid UTF-8 has no faithful spelling here. It becomes an `errors` entry
|
|
159
|
+
(and exit 2) rather than a file quietly dropped from the review — the lossy
|
|
160
|
+
spelling would name a file that does not exist.
|
|
161
|
+
|
|
162
|
+
`--diff` shells out to `git` — infrastructure, not one of the linters whose
|
|
163
|
+
directives are parsed natively — so it needs `git` on `PATH` and a work tree.
|
|
164
|
+
|
|
165
|
+
### On a pull request: the GitHub Action
|
|
166
|
+
|
|
167
|
+
The action posts one sticky comment naming every suppression the pull request
|
|
168
|
+
added, with its stated reason and a link to the line. It edits that same comment
|
|
169
|
+
on each push instead of adding another, and on a pull request that adds none it
|
|
170
|
+
posts nothing at all.
|
|
171
|
+
|
|
172
|
+
```yaml
|
|
173
|
+
# .github/workflows/notignored.yml
|
|
174
|
+
name: notignored
|
|
175
|
+
|
|
176
|
+
on:
|
|
177
|
+
pull_request:
|
|
178
|
+
|
|
179
|
+
permissions:
|
|
180
|
+
contents: read
|
|
181
|
+
pull-requests: write
|
|
182
|
+
|
|
183
|
+
jobs:
|
|
184
|
+
suppressions:
|
|
185
|
+
runs-on: ubuntu-latest
|
|
186
|
+
steps:
|
|
187
|
+
- uses: actions/checkout@v4
|
|
188
|
+
with:
|
|
189
|
+
fetch-depth: 0 # the base branch has to be fetched to diff against it
|
|
190
|
+
- uses: nickderobertis/notignored@main
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
| Input | Default | Meaning |
|
|
194
|
+
| --- | --- | --- |
|
|
195
|
+
| `github-token` | `${{ github.token }}` | Token used to upsert the comment. Needs `pull-requests: write`. |
|
|
196
|
+
| `diff-base` | the pull request's base branch | Any git revision or range, as `--diff-base` takes. |
|
|
197
|
+
| `paths` | the whole repository | Whitespace-separated files and directories to scan. |
|
|
198
|
+
| `version` | `latest` | A release tag such as `v0.1.0`, or `local` to build the action's own source with `cargo`. |
|
|
199
|
+
|
|
200
|
+
It exposes `count` (how many suppressions the change added) and `report-path`
|
|
201
|
+
(the JSON report), so a later step can fail the build, upload the report, or
|
|
202
|
+
gate on a threshold:
|
|
203
|
+
|
|
204
|
+
```yaml
|
|
205
|
+
- uses: nickderobertis/notignored@main
|
|
206
|
+
id: notignored
|
|
207
|
+
- if: steps.notignored.outputs.count != '0'
|
|
208
|
+
run: echo "this change adds ${{ steps.notignored.outputs.count }} suppression(s)"
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
The steps are `bash`, so the action runs on the Linux and macOS runners; it needs
|
|
212
|
+
`jq` and the `gh` CLI (both preinstalled there), plus `cargo` when
|
|
213
|
+
`version: local`.
|
|
214
|
+
|
|
215
|
+
`--format markdown` renders exactly the body the action posts, so it can be
|
|
216
|
+
previewed locally:
|
|
217
|
+
|
|
218
|
+
```console
|
|
219
|
+
$ notignored --diff --diff-base main --format markdown \
|
|
220
|
+
--github-repo nickderobertis/notignored --github-sha "$(git rev-parse HEAD)"
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Both permalink flags are optional; without them each location renders as plain
|
|
224
|
+
`path:line` text. When a body names fewer than four suppressions, each one also
|
|
225
|
+
carries the source line with two lines of context on either side.
|
|
226
|
+
|
|
227
|
+
### Exit codes
|
|
228
|
+
|
|
229
|
+
| Code | Meaning |
|
|
230
|
+
| --- | --- |
|
|
231
|
+
| `0` | The scan completed. |
|
|
232
|
+
| `1` | `--fail-if-found` was given and at least one suppression was reported. |
|
|
233
|
+
| `2` | The scan could not complete — an unreadable path or file, or a bad argument. |
|
|
234
|
+
|
|
235
|
+
Findings go to stdout; the summary and any errors go to stderr, so
|
|
236
|
+
`notignored --format json > report.json` captures only the report. A downstream
|
|
237
|
+
consumer that stops reading (`| head`, `| grep -q`) is not an error: the scan's
|
|
238
|
+
own verdict still decides the exit code.
|
|
239
|
+
|
|
240
|
+
## Output
|
|
241
|
+
|
|
242
|
+
The `json` format emits the full report envelope:
|
|
243
|
+
|
|
244
|
+
```json
|
|
245
|
+
{
|
|
246
|
+
"version": 1,
|
|
247
|
+
"ignores": [
|
|
248
|
+
{
|
|
249
|
+
"tool": "ruff",
|
|
250
|
+
"scope": "line",
|
|
251
|
+
"rules": ["E501"],
|
|
252
|
+
"reason": "long wrapped URL",
|
|
253
|
+
"path": "src/app.py",
|
|
254
|
+
"line": 12,
|
|
255
|
+
"end_line": 12,
|
|
256
|
+
"column": 20,
|
|
257
|
+
"raw": "# noqa: E501 # long wrapped URL",
|
|
258
|
+
"suppressed": { "start_line": 12, "end_line": 12 }
|
|
259
|
+
}
|
|
260
|
+
],
|
|
261
|
+
"errors": []
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
- `scope` — `line`, `next-line`, `file`, or `block`.
|
|
266
|
+
- `rules` — rule names/codes exactly as written. `[]` means a blanket
|
|
267
|
+
suppression of every rule the tool would apply (rendered as `*` in the human
|
|
268
|
+
format).
|
|
269
|
+
- `reason` — the stated justification, or `null`. Taken from the tool's native
|
|
270
|
+
reason syntax where one exists, otherwise from the trailing comment on the
|
|
271
|
+
directive line; whitespace is collapsed to single spaces.
|
|
272
|
+
- `line` / `end_line` / `column` — 1-based.
|
|
273
|
+
- `suppressed` — the best-effort range the directive silences. `end_line` is
|
|
274
|
+
`null` when it runs to end-of-file or is unterminated.
|
|
275
|
+
- `errors` — files that could not be read. Never a panic.
|
|
276
|
+
|
|
277
|
+
`version` is the envelope version; it changes only when the shape does.
|
|
278
|
+
|
|
279
|
+
## Supported tools
|
|
280
|
+
|
|
281
|
+
| Tool | Directives |
|
|
282
|
+
| --- | --- |
|
|
283
|
+
| `eslint` | `// eslint-disable-line rule`, `// eslint-disable-next-line rule -- reason`, `/* eslint-disable rule -- reason */` … `/* eslint-enable rule */` |
|
|
284
|
+
| `biome` | `// biome-ignore lint/group/rule: reason`, `// biome-ignore-all lint/group/rule: reason`, `// biome-ignore-start lint/group/rule: reason` … `// biome-ignore-end lint/group/rule: reason` |
|
|
285
|
+
| `ruff` | `# noqa`, `# noqa: E501, F401`, `# ruff: noqa`, `# ruff: noqa: E501` |
|
|
286
|
+
| `typescript` | `// @ts-ignore`, `// @ts-expect-error reason`, `/* @ts-ignore */`, `// @ts-nocheck` |
|
|
287
|
+
| `mypy` | `# type: ignore`, `# type: ignore[arg-type, index]`, `# mypy: ignore-errors`, `# mypy: disable-error-code="arg-type"` |
|
|
288
|
+
| `pyright` | `# pyright: ignore`, `# pyright: ignore[reportArgumentType]`, `# pyright: reportMissingImports=false` |
|
|
289
|
+
| `ty` | `# ty: ignore`, `# ty: ignore[invalid-argument-type]` |
|
|
290
|
+
| `rust` | `#[allow(dead_code)]`, `#[allow(clippy::needless_collect, dead_code)]`, `#[expect(dead_code, reason = "…")]`, `#![allow(…)]`, `#![expect(…, reason = "…")]` |
|
|
291
|
+
| `shellcheck` | `# shellcheck disable=SC2086`, `# shellcheck disable=SC2086,SC2046`, `# shellcheck disable=SC2000-SC2100`, `# shellcheck disable=all`, `# shellcheck disable=SC2086 # reason` |
|
|
292
|
+
| `llmlint` | `ignore[rule, …] reason`, `ignore-file[rule, …] reason`, `ignore-block[rule, …] reason` … `ignore-end[rule, …]` — each written after the `llmlint` keyword and a colon, in the host language's comment syntax |
|
|
293
|
+
|
|
294
|
+
Scope follows each tool's own rules, not a house convention:
|
|
295
|
+
|
|
296
|
+
- **rust** — an outer attribute is `next-line` and its `suppressed` range runs
|
|
297
|
+
through the end of the item it annotates; an inner `#![…]` is `file`. A
|
|
298
|
+
`reason = "…"` is the record's reason, even when the string wraps.
|
|
299
|
+
- **shellcheck** — a directive above the first command is `file`; anywhere else
|
|
300
|
+
it is `next-line`. A directive ShellCheck itself rejects (trailing prose with
|
|
301
|
+
no `#`, or one placed after a command) is reported by neither tool.
|
|
302
|
+
- **typescript** — the parity claim is pinned to one compiler: the `typescript`
|
|
303
|
+
version in `tests/js-toolchain/package.json` — **7.0.2**, the Go port — which
|
|
304
|
+
is what `tests/e2e/typescript_parity.rs` drives. The 5.x compiler is a
|
|
305
|
+
separate implementation of the same directives and is not guaranteed to read
|
|
306
|
+
every form the same way, so the claim here is parity with the pinned compiler
|
|
307
|
+
rather than with every `tsc` ever shipped.
|
|
308
|
+
- **llmlint** — `ignore` is `line`, `ignore-file` is `file`, and
|
|
309
|
+
`ignore-block` … `ignore-end` is one `block` record spanning both directives.
|
|
310
|
+
A block left unclosed keeps a null `suppressed.end_line` and adds an `errors`
|
|
311
|
+
entry.
|
|
312
|
+
|
|
313
|
+
Each tool's own reason syntax is what gets captured: ESLint's ` -- description`,
|
|
314
|
+
Biome's mandatory `: explanation`, ruff's trailing `# comment`, and — for
|
|
315
|
+
TypeScript, which defines no separator — whatever text trails the directive. A
|
|
316
|
+
directive that lists no rules is a blanket suppression (`rules: []`).
|
|
317
|
+
|
|
318
|
+
Scope follows the tool rather than the syntax. `// eslint-disable-line` is
|
|
319
|
+
`line`; `// eslint-disable-next-line`, `// biome-ignore` and
|
|
320
|
+
`// @ts-expect-error` are `next-line`; `# ruff: noqa`, `// biome-ignore-all` and
|
|
321
|
+
`// @ts-nocheck` are `file`; and the delimited pairs
|
|
322
|
+
(`/* eslint-disable */` … `/* eslint-enable */`,
|
|
323
|
+
`// biome-ignore-start` … `// biome-ignore-end`) are `block`, running to
|
|
324
|
+
end-of-file with `suppressed.end_line: null` when they are never closed.
|
|
325
|
+
|
|
326
|
+
Adding one is four touch points: a module under `src/tools/`, one line in
|
|
327
|
+
`src/tools/mod.rs::registry()`, one row above, and a directive in
|
|
328
|
+
`tests/fixtures/polyglot/`. `tests/tools_contract.rs` fails the build if a row
|
|
329
|
+
here and the registered parsers disagree, and `tests/e2e/polyglot.rs` fails it
|
|
330
|
+
if a registered tool is missing from that fixture tree.
|
|
331
|
+
|
|
332
|
+
## Where a directive reaches, and who honours it
|
|
333
|
+
|
|
334
|
+
Every `# type: ignore` and `# pyright: ignore` is `line`-scoped; the module-wide
|
|
335
|
+
forms are mypy's two `# mypy:` config comments and pyright's rule override; for
|
|
336
|
+
ty, where the comment sits is the scope; and a Rust attribute reaches to the end
|
|
337
|
+
of the item it annotates:
|
|
338
|
+
|
|
339
|
+
| Source | Reported as |
|
|
340
|
+
| --- | --- |
|
|
341
|
+
| `f(x) # type: ignore` | `mypy`, `line` |
|
|
342
|
+
| `# mypy: ignore-errors` on its own line | `mypy`, `file` |
|
|
343
|
+
| `# mypy: disable-error-code="arg-type"` on its own line | `mypy`, `file` |
|
|
344
|
+
| `f(x) # pyright: ignore` | `pyright`, `line` |
|
|
345
|
+
| `# pyright: reportMissingImports=false` | `pyright`, `file` |
|
|
346
|
+
| `f(x) # ty: ignore` | `ty`, `line` |
|
|
347
|
+
| `# ty: ignore` above every statement | `ty`, `file` |
|
|
348
|
+
| `# ty: ignore` on its own line in the body | `ty`, `next-line` |
|
|
349
|
+
| `#[allow(dead_code)]` above an item | `rust`, `next-line`, `suppressed` through the item's last line |
|
|
350
|
+
| `#[expect(dead_code, reason = "…")]` above an item | `rust`, `next-line`, `reason` from the attribute |
|
|
351
|
+
| `#![allow(dead_code)]` at the top of the file | `rust`, `file` |
|
|
352
|
+
|
|
353
|
+
A Rust attribute's `scope` is `next-line` — that is where the item it annotates
|
|
354
|
+
starts, and where a reviewer has to look — while its `suppressed` range covers
|
|
355
|
+
the whole item, however many lines that item runs to. An inner `#![…]` attribute
|
|
356
|
+
exempts the file it opens.
|
|
357
|
+
|
|
358
|
+
Pyright's `<rule>=<value>` override is reported only for the two values that
|
|
359
|
+
switch a rule off, `false` and `none`; `true`, `error`, `warning`, and
|
|
360
|
+
`information` turn a rule on or move its severity, so they are configuration
|
|
361
|
+
rather than suppression. Pyright reads the rest of that line as its own item
|
|
362
|
+
list, so the form can carry no reason — and a comment it refuses (a trailing
|
|
363
|
+
`# why`, a value outside those six, or a directive that does not open the
|
|
364
|
+
comment) silences nothing and is not reported.
|
|
365
|
+
|
|
366
|
+
Several tools honour a directive they did not invent: pyright and ty both act on
|
|
367
|
+
mypy's `# type: ignore`, and ruff, pyright, and ty all act on one that does not
|
|
368
|
+
open its comment (mypy does not). A directive is reported **once, under the tool
|
|
369
|
+
whose syntax it is** — `# type: ignore` is one `mypy` record, not three — so a
|
|
370
|
+
count of records is a count of suppressions written, not of checkers affected.
|
|
371
|
+
|
|
372
|
+
One line can still carry directives for several tools, and each record covers
|
|
373
|
+
**its own directive only**. Given
|
|
374
|
+
|
|
375
|
+
```python
|
|
376
|
+
import legacy # type: ignore[import-not-found] # no stubs published # noqa: F401 # imported for its side effects
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
the `mypy` record's `raw` stops at `# no stubs published` and its `reason` is
|
|
380
|
+
`"no stubs published"`; the `ruff` record's `raw` starts at `# noqa: F401` and
|
|
381
|
+
its `reason` is `"imported for its side effects"`. A record's `raw` and `reason`
|
|
382
|
+
always end where the next tool's directive begins, so one tool's live suppression
|
|
383
|
+
can never be filed as another's justification.
|
|
384
|
+
|
|
385
|
+
`# pyright: basic` and `# pyright: strict` switch pyright's type-checking mode
|
|
386
|
+
rather than silencing a diagnostic, and are deliberately not reported.
|
|
387
|
+
|
|
388
|
+
## How it works
|
|
389
|
+
|
|
390
|
+
Source is scanned once per file by a language-aware comment extractor
|
|
391
|
+
(`src/comments.rs`) that understands `#` comments, `//` line comments, multi-line
|
|
392
|
+
`/* … */` blocks (nested, for Rust), Rust attributes, and the punctuation that
|
|
393
|
+
delimits a Rust item — and that knows a string literal when it sees one, so
|
|
394
|
+
`MESSAGE = "# noqa: E501"` is never reported. Tool parsers consume that
|
|
395
|
+
extraction; they never re-scan raw lines.
|
|
396
|
+
|
|
397
|
+
## Development
|
|
398
|
+
|
|
399
|
+
```console
|
|
400
|
+
just bootstrap # from a clean clone
|
|
401
|
+
just check # the full gate: format, clippy, tests + coverage, docs
|
|
402
|
+
just --list # everything else
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
`just check` runs the end-to-end suite, which drives the compiled binary as a
|
|
406
|
+
subprocess and the **real, pinned** tools — `ruff`, `mypy`, `pyright`, `ty`,
|
|
407
|
+
`shellcheck`, and `llmlint` (see the `.<tool>-version` files), `eslint` /
|
|
408
|
+
`biome` / `tsc` (see `tests/js-toolchain/package.json`), plus the pinned
|
|
409
|
+
toolchain's own `rustc` and `clippy-driver` — to prove that what `notignored`
|
|
410
|
+
reports is what those tools actually suppress. `just bootstrap` installs them
|
|
411
|
+
all under `.dev/`; it needs `uv` and Node.js 20+ on `PATH`.
|
|
412
|
+
|
|
413
|
+
## License
|
|
414
|
+
|
|
415
|
+
MIT — see [LICENSE](LICENSE).
|
|
416
|
+
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
notignored_cli-0.1.2.data/scripts/notignored.exe,sha256=gdpmo7OWJ9H9NM3-TuWD-Fbu-uxG8I_psDl9ECpYEbA,2377216
|
|
2
|
+
notignored_cli-0.1.2.dist-info/METADATA,sha256=7RWlBW4iPlmTIktsngGpYEHTjZRcCxgNWYP4M-HJubg,19703
|
|
3
|
+
notignored_cli-0.1.2.dist-info/WHEEL,sha256=2zDlIYIdD4m4N3p5DVEG3iJhGLdhsBQgdH-FqVkAur8,94
|
|
4
|
+
notignored_cli-0.1.2.dist-info/licenses/LICENSE,sha256=JeBGdcXIUkoTZEHhkKd-FmKgdgumkH509Ob1QWJomVE,1072
|
|
5
|
+
notignored_cli-0.1.2.dist-info/sboms/notignored.cyclonedx.json,sha256=zPK7cw78freeESThSQYQF4ws8SkCFssvooQd6XwAAE4,53304
|
|
6
|
+
notignored_cli-0.1.2.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nick DeRobertis
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|