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
23 changes: 16 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,30 @@ Unused `@throws` tags are reported as warnings (disable with `--no-unused`).
Like eslint's disable comments, individual lines can be muted:

```ts
throw new CacheMissError(key); // @nothrow — mutes this line
throw new CacheMissError(key); // @nothrow-line — same thing, explicit
throw new CacheMissError(key); // @nothrow — mutes this line (and the next)

// @nothrow-next-line
throw new CacheMissError(key); // — muted by the line above
// @nothrow — works on its own line above the statement too
throw new CacheMissError(key);

throw new CacheMissError(key); // @nothrow-line — explicit: only this line

// @nothrow-next-line — explicit: only the following line
throw new CacheMissError(key);
```

The bare `@nothrow` covers both its own line and the next, so it works
trailing a statement or on its own line above one. Use the explicit `-line` /
`-next-line` forms when a bare directive would spill onto a neighbouring line
you want checked.

What a muted line means depends on what is on it:

- a `throw`, a propagating call, or a `Promise.reject` — that throw site is
ignored (it no longer needs documenting, and no longer counts toward a
documented tag being "used")
- the function declaration itself (put `// @nothrow-next-line` directly above
the declaration, below any JSDoc) — the whole function's missing-`@throws`
report is muted
- the function declaration itself (put `// @nothrow` or `// @nothrow-next-line`
directly above the declaration, below any JSDoc) — the whole function's
missing-`@throws` report is muted
- a `@throws` tag line inside a JSDoc block — the unused-tag warning for that
tag is muted

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "throwscript",
"version": "0.2.0",
"version": "0.3.0",
"description": "Linter-like checker: every function that can throw must document it with JSDoc @throws",
"type": "module",
"license": "MIT",
Expand Down
8 changes: 4 additions & 4 deletions packages/cli/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ functions are handled implicitly: a rejection must be documented with the same
\`@throws {ErrorType}\` tag as a synchronous throw.

Muting (eslint-style):
// @nothrow trailing comment mutes the throw/call on that line
// @nothrow-line same as @nothrow
// @nothrow-next-line mutes the following line
// @nothrow mutes this line AND the next line
// @nothrow-line mutes only this line
// @nothrow-next-line mutes only the following line

Options:
-p, --project <tsconfig> Check all files from the given tsconfig project
Expand Down Expand Up @@ -137,7 +137,7 @@ function run(): number {
return 0;
}
if (parsed === "version") {
console.log("throwscript 0.2.0");
console.log("throwscript 0.3.0");
return 0;
Comment on lines 139 to 141
}

Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@throwscript/core",
"version": "0.2.0",
"version": "0.3.0",
"description": "Checker that asserts every function that can throw documents it with a JSDoc @throws tag",
"type": "module",
"license": "MIT",
Expand Down
12 changes: 7 additions & 5 deletions packages/core/src/analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,12 @@ const MUTE_DIRECTIVE = /@nothrow(-next-line|-line)?\b/g;
* Collect 0-based line numbers muted by `@nothrow` directives in comments,
* mirroring eslint's disable comments:
*
* - `// @nothrow` or `// @nothrow-line` mutes the line the directive is on
* (use it trailing a `throw`, a call, a declaration, or a `@throws` tag)
* - `// @nothrow-next-line` mutes the following line
* - `// @nothrow` mutes the line the directive is on AND the following line,
* so it works both trailing a statement and on its own line above one
* - `// @nothrow-line` mutes only the line the directive is on
* - `// @nothrow-next-line` mutes only the following line
*
* Both forms also work inside block comments; inside a multi-line comment the
* All forms also work inside block comments; inside a multi-line comment the
* directive applies relative to the line it is written on.
*/
export function collectMutedLines(sourceFile: ts.SourceFile): Set<number> {
Expand All @@ -117,7 +118,8 @@ export function collectMutedLines(sourceFile: ts.SourceFile): Set<number> {
const directiveLine = sourceFile.getLineAndCharacterOfPosition(
commentStart + match.index,
).line;
muted.add(match[1] === "-next-line" ? directiveLine + 1 : directiveLine);
if (match[1] !== "-next-line") muted.add(directiveLine);
if (match[1] !== "-line") muted.add(directiveLine + 1);
}
}
token = scanner.scan();
Expand Down
25 changes: 20 additions & 5 deletions packages/core/test/fixtures/muted.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,31 @@ export function mutedFunction(): void {
throw new MutedError("x");
}

// BAD: a directive only mutes its own line; the throw on the next line is
// still reported.
export function notMuted(): void {
// GOOD: bare @nothrow also covers the next line, so it works on its own line
// above the throw.
export function mutedAboveLine(): void {
// @nothrow
throw new MutedError("x");
}

// BAD: muting one throw does not mute the others.
// BAD: the explicit -line variant only mutes its own line; the throw on the
// next line is still reported.
export function notMuted(): void {
// @nothrow-line
throw new MutedError("x");
}

// BAD: the explicit -next-line variant does not mute its own line; a throw
// trailing it is still reported.
export function notMutedTrailing(): void {
throw new MutedError("x"); // @nothrow-next-line
}

// BAD: muting one throw does not mute the others. Note the explicit -line
// form: a bare @nothrow here would spill onto the next line and mute the
// OtherError throw too.
export function partiallyMuted(kind: number): void {
if (kind === 1) throw new MutedError("muted"); // @nothrow
if (kind === 1) throw new MutedError("muted"); // @nothrow-line
throw new OtherError("reported");
}

Expand Down
5 changes: 5 additions & 0 deletions packages/core/test/mute-fix.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,18 @@ test("@nothrow directives mute throw sites and diagnostics", () => {
assert.equal(errorsFor(diagnostics, "mutedSameLine").length, 0);
assert.equal(errorsFor(diagnostics, "mutedLineSuffix").length, 0);
assert.equal(errorsFor(diagnostics, "mutedNextLine").length, 0);
assert.equal(errorsFor(diagnostics, "mutedAboveLine").length, 0);
assert.equal(errorsFor(diagnostics, "mutedFunction").length, 0);
assert.equal(errorsFor(diagnostics, "mutedCall").length, 0);

const notMuted = errorsFor(diagnostics, "notMuted");
assert.equal(notMuted.length, 1);
assert.deepEqual(notMuted[0]?.types, ["MutedError"]);

const notMutedTrailing = errorsFor(diagnostics, "notMutedTrailing");
assert.equal(notMutedTrailing.length, 1);
assert.deepEqual(notMutedTrailing[0]?.types, ["MutedError"]);

const partial = errorsFor(diagnostics, "partiallyMuted");
assert.equal(partial.length, 1);
assert.deepEqual(partial[0]?.types, ["OtherError"]);
Expand Down