diff --git a/README.md b/README.md index 7a021d2..2b2c8a9 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/packages/cli/package.json b/packages/cli/package.json index 5e68413..960e849 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -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", diff --git a/packages/cli/src/main.ts b/packages/cli/src/main.ts index e84542d..a1694ca 100644 --- a/packages/cli/src/main.ts +++ b/packages/cli/src/main.ts @@ -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 Check all files from the given tsconfig project @@ -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; } diff --git a/packages/core/package.json b/packages/core/package.json index f7fa0ee..1ba913a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -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", diff --git a/packages/core/src/analyzer.ts b/packages/core/src/analyzer.ts index e19e328..2b65247 100644 --- a/packages/core/src/analyzer.ts +++ b/packages/core/src/analyzer.ts @@ -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 { @@ -117,7 +118,8 @@ export function collectMutedLines(sourceFile: ts.SourceFile): Set { 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(); diff --git a/packages/core/test/fixtures/muted.ts b/packages/core/test/fixtures/muted.ts index a2f7c89..a044b2d 100644 --- a/packages/core/test/fixtures/muted.ts +++ b/packages/core/test/fixtures/muted.ts @@ -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"); } diff --git a/packages/core/test/mute-fix.test.ts b/packages/core/test/mute-fix.test.ts index 7f2d649..a655dad 100644 --- a/packages/core/test/mute-fix.test.ts +++ b/packages/core/test/mute-fix.test.ts @@ -26,6 +26,7 @@ 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); @@ -33,6 +34,10 @@ test("@nothrow directives mute throw sites and diagnostics", () => { 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"]);