From 80d656cde4dc251690b61560f93cd6137269e810 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 02:24:21 +0000 Subject: [PATCH 1/4] chore(ast-grep): pin ast-grep 0.45.3 --- .changeset/ast-grep-0-45-3.md | 5 ++ packages/cli/package.json | 16 +++--- pnpm-lock.yaml | 94 +++++++++++++++++------------------ 3 files changed, 60 insertions(+), 55 deletions(-) create mode 100644 .changeset/ast-grep-0-45-3.md diff --git a/.changeset/ast-grep-0-45-3.md b/.changeset/ast-grep-0-45-3.md new file mode 100644 index 00000000..b9dcdb4b --- /dev/null +++ b/.changeset/ast-grep-0-45-3.md @@ -0,0 +1,5 @@ +--- +"@taskless/cli": patch +--- + +Update the bundled ast-grep to 0.45.3 (from 0.45.2). diff --git a/packages/cli/package.json b/packages/cli/package.json index 72c36696..4e8e933e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -69,7 +69,7 @@ "zod": "^4.3.6" }, "devDependencies": { - "@ast-grep/cli": "0.45.2", + "@ast-grep/cli": "0.45.3", "@types/sprintf-js": "^1.1.4", "openapi-typescript": "^7.13.0", "prettier": "^3.8.1", @@ -82,13 +82,13 @@ "taskless": "./dist/index.js" }, "optionalDependencies": { - "@ast-grep/cli-darwin-arm64": "0.45.2", - "@ast-grep/cli-darwin-x64": "0.45.2", - "@ast-grep/cli-linux-arm64-gnu": "0.45.2", - "@ast-grep/cli-linux-x64-gnu": "0.45.2", - "@ast-grep/cli-win32-arm64-msvc": "0.45.2", - "@ast-grep/cli-win32-ia32-msvc": "0.45.2", - "@ast-grep/cli-win32-x64-msvc": "0.45.2", + "@ast-grep/cli-darwin-arm64": "0.45.3", + "@ast-grep/cli-darwin-x64": "0.45.3", + "@ast-grep/cli-linux-arm64-gnu": "0.45.3", + "@ast-grep/cli-linux-x64-gnu": "0.45.3", + "@ast-grep/cli-win32-arm64-msvc": "0.45.3", + "@ast-grep/cli-win32-ia32-msvc": "0.45.3", + "@ast-grep/cli-win32-x64-msvc": "0.45.3", "@taskless/vale-darwin-arm64": "3.20.0-20260907164938", "@taskless/vale-darwin-x64": "3.20.0-20260907164938", "@taskless/vale-linux-arm64": "3.20.0-20260907164938", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8dd3fba9..d32a6c82 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -109,8 +109,8 @@ importers: version: 4.3.6 devDependencies: '@ast-grep/cli': - specifier: 0.45.2 - version: 0.45.2 + specifier: 0.45.3 + version: 0.45.3 '@types/sprintf-js': specifier: ^1.1.4 version: 1.1.4 @@ -134,26 +134,26 @@ importers: version: 3.2.4(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) optionalDependencies: '@ast-grep/cli-darwin-arm64': - specifier: 0.45.2 - version: 0.45.2 + specifier: 0.45.3 + version: 0.45.3 '@ast-grep/cli-darwin-x64': - specifier: 0.45.2 - version: 0.45.2 + specifier: 0.45.3 + version: 0.45.3 '@ast-grep/cli-linux-arm64-gnu': - specifier: 0.45.2 - version: 0.45.2 + specifier: 0.45.3 + version: 0.45.3 '@ast-grep/cli-linux-x64-gnu': - specifier: 0.45.2 - version: 0.45.2 + specifier: 0.45.3 + version: 0.45.3 '@ast-grep/cli-win32-arm64-msvc': - specifier: 0.45.2 - version: 0.45.2 + specifier: 0.45.3 + version: 0.45.3 '@ast-grep/cli-win32-ia32-msvc': - specifier: 0.45.2 - version: 0.45.2 + specifier: 0.45.3 + version: 0.45.3 '@ast-grep/cli-win32-x64-msvc': - specifier: 0.45.2 - version: 0.45.2 + specifier: 0.45.3 + version: 0.45.3 '@taskless/vale-darwin-arm64': specifier: 3.20.0-20260907164938 version: 3.20.0-20260907164938 @@ -187,50 +187,50 @@ importers: packages: - '@ast-grep/cli-darwin-arm64@0.45.2': - resolution: {integrity: sha512-UeLO9dAyWVesDII8m545LEzooNLRmNigbv6Qo7bACkB3jYTqoVbLH96+PZFFbJcQRcabqiwOiDlxhodkCMkP5A==} + '@ast-grep/cli-darwin-arm64@0.45.3': + resolution: {integrity: sha512-6RZg4gRMJcSJtEJuaW5z33qfwXD7kRDGZ0T0dTxVxsLSw5BkeAjR8REysxUwPWZn+oOkzPyZ3y7/qNnSI/diIA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@ast-grep/cli-darwin-x64@0.45.2': - resolution: {integrity: sha512-Y9sUYEGx/jE/1e9bq8E11AIRDSTMj7ZCzfK862TVM2yc+jKWif+IcqPkDNX4rpz+erTxK+IsSVwoEXduB+zSPg==} + '@ast-grep/cli-darwin-x64@0.45.3': + resolution: {integrity: sha512-4z6ZknTMSlTirQuJ4xihXoLfG7YwAOxFCqkKuGAxAiP+K70zivib8R19vUoFJDy4s+rjyGNDdrm45/ClC5tITQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@ast-grep/cli-linux-arm64-gnu@0.45.2': - resolution: {integrity: sha512-hfxdLxxCcGobue++cWNf7O/VC+Ctj9FeFqgTkKUxxZbPjMawHLNvd2+uXbTimgOOZrAZqm2VsZ9CRxw8zrADIA==} + '@ast-grep/cli-linux-arm64-gnu@0.45.3': + resolution: {integrity: sha512-T/N+Fl/pMqNjuyPV9PbTSR2bTlZrLKiCyHzJax6QNaOOVviXjEscROrAc6c0lFfc+Z07gW7Rt3oZKZdLEExutQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@ast-grep/cli-linux-x64-gnu@0.45.2': - resolution: {integrity: sha512-Fh/evRRyJaH0Dctk+JMN4o7dz0tI5Pj3XJUDlp/aAwvXnZ0kuOsAMpO4tlYYq3By0/6GbKCr2Qau1t9cx1rMVQ==} + '@ast-grep/cli-linux-x64-gnu@0.45.3': + resolution: {integrity: sha512-HbxIy6tZa8zn4J2hG8KVIo9n4+INAgVB8l2DyHqYxGKxnod8u2B4hfTGxbMm+BjBvYxKW4oGLZtYqInMyxidyQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@ast-grep/cli-win32-arm64-msvc@0.45.2': - resolution: {integrity: sha512-wzlAX2K9255DNrHMpK9EpM+61ITYpTL14P47eZ4or2jYGUpB0bl1r7FjTC+eDXcwHLJozvu1b6f42iy0505JvA==} + '@ast-grep/cli-win32-arm64-msvc@0.45.3': + resolution: {integrity: sha512-X0+81Mgr8zsH6hu4Pqdr5h1IyAFUWbKS1PMkKT6awYiiTo/1uhVM/6WzJ+ohOQPmMTyt4poyyg46u2E4WzKECg==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@ast-grep/cli-win32-ia32-msvc@0.45.2': - resolution: {integrity: sha512-hgRpvkX2n8ih3Fywj6dn6ekLmKI0ytC2HdDlLvAQBDzxYzxVMpb6mydF+eR4lqPEGTSuGXmN7lsbDzRRFX7qqQ==} + '@ast-grep/cli-win32-ia32-msvc@0.45.3': + resolution: {integrity: sha512-W4H27lU/xzzIOvRo4vDTjzq91PioLCjupR7jLmLVkAqAkX2YiNRixf8NjMcQgzAgr33jUsMlyutiTElLgi1O9g==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@ast-grep/cli-win32-x64-msvc@0.45.2': - resolution: {integrity: sha512-IFV/nygDDDOecuyqaaHIRwhEgVu8+42jm9UIiiZ/P9zUxDMZa34Z440FFGqqRvf4Mnm+1X0jRsNRxGyWPpzPog==} + '@ast-grep/cli-win32-x64-msvc@0.45.3': + resolution: {integrity: sha512-UZrpVbjLQqQIRxWqeMcwyLSIhlDZyhYb8SinssM38Oo6mEB2jMfHCEoigay9UOZTfUR268n72BBV48nW2h+QwA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@ast-grep/cli@0.45.2': - resolution: {integrity: sha512-euUPqWTyWt6Ma9WBRCMM12UZXkdQAoUVyXFWnPXtFfIuiF3oJUhEWpzlcSaliiFcXLfzSuMu4rAhhtlGrTEaZA==} + '@ast-grep/cli@0.45.3': + resolution: {integrity: sha512-Cm07SHb8Q8dJfAQhrPOveAGuuzznmf6IjQa2+0Yvjt8Qfo+XIVbEIo0Prng+UDVMlaCS/FkF2eA8M3F6z4+0Wg==} engines: {node: '>= 12.0.0'} hasBin: true @@ -2510,38 +2510,38 @@ packages: snapshots: - '@ast-grep/cli-darwin-arm64@0.45.2': + '@ast-grep/cli-darwin-arm64@0.45.3': optional: true - '@ast-grep/cli-darwin-x64@0.45.2': + '@ast-grep/cli-darwin-x64@0.45.3': optional: true - '@ast-grep/cli-linux-arm64-gnu@0.45.2': + '@ast-grep/cli-linux-arm64-gnu@0.45.3': optional: true - '@ast-grep/cli-linux-x64-gnu@0.45.2': + '@ast-grep/cli-linux-x64-gnu@0.45.3': optional: true - '@ast-grep/cli-win32-arm64-msvc@0.45.2': + '@ast-grep/cli-win32-arm64-msvc@0.45.3': optional: true - '@ast-grep/cli-win32-ia32-msvc@0.45.2': + '@ast-grep/cli-win32-ia32-msvc@0.45.3': optional: true - '@ast-grep/cli-win32-x64-msvc@0.45.2': + '@ast-grep/cli-win32-x64-msvc@0.45.3': optional: true - '@ast-grep/cli@0.45.2': + '@ast-grep/cli@0.45.3': dependencies: detect-libc: 2.1.2 optionalDependencies: - '@ast-grep/cli-darwin-arm64': 0.45.2 - '@ast-grep/cli-darwin-x64': 0.45.2 - '@ast-grep/cli-linux-arm64-gnu': 0.45.2 - '@ast-grep/cli-linux-x64-gnu': 0.45.2 - '@ast-grep/cli-win32-arm64-msvc': 0.45.2 - '@ast-grep/cli-win32-ia32-msvc': 0.45.2 - '@ast-grep/cli-win32-x64-msvc': 0.45.2 + '@ast-grep/cli-darwin-arm64': 0.45.3 + '@ast-grep/cli-darwin-x64': 0.45.3 + '@ast-grep/cli-linux-arm64-gnu': 0.45.3 + '@ast-grep/cli-linux-x64-gnu': 0.45.3 + '@ast-grep/cli-win32-arm64-msvc': 0.45.3 + '@ast-grep/cli-win32-ia32-msvc': 0.45.3 + '@ast-grep/cli-win32-x64-msvc': 0.45.3 '@babel/code-frame@7.29.0': dependencies: From 9c3b89769a23237c0b92c4cbfe714cf8ef450945 Mon Sep 17 00:00:00 2001 From: Jakob Heuser Date: Tue, 15 Sep 2026 16:52:19 -0700 Subject: [PATCH 2/4] fix(ast-grep): bring the 0.45.3 pin's constants, schema and pins into line The pin bump alone left four things behind: - AST_GREP_VERSION still said 0.45.2, so engine-version-consistency and the vendor contract's version pin both failed, and the reconciliation marker recorded the wrong engine. - The vendored rule schema is fetched from the tagged upstream, so it is regenerated. The only change is the Severity enum's order: `off` now sorts first, because `--min-severity` compares severities as an ordered type. No consumer of ours reads the order. - The severity-vocabulary pin asserted the old order in ast-grep's error text. The vocabulary is unchanged; the pin records the reorder. - The reconciliation marker test's deliberate literal, which exists so an upgrade cannot refresh it silently, is refreshed by hand. --- .../cli/src/generated/ast-grep-rule-schema.json | 12 ++++++------ packages/cli/src/rules/capabilities.ts | 2 +- packages/cli/test/ast-grep-vendor-contract.test.ts | 13 +++++++++++-- .../cli/test/engine-version-consistency.test.ts | 2 +- packages/cli/test/reconcile-marker.test.ts | 2 +- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/packages/cli/src/generated/ast-grep-rule-schema.json b/packages/cli/src/generated/ast-grep-rule-schema.json index d5278fb7..30159355 100644 --- a/packages/cli/src/generated/ast-grep-rule-schema.json +++ b/packages/cli/src/generated/ast-grep-rule-schema.json @@ -661,6 +661,11 @@ }, "Severity": { "oneOf": [ + { + "description": "Turns off the rule.", + "type": "string", + "const": "off" + }, { "description": "A kind reminder for code with potential improvement.", "type": "string", @@ -680,11 +685,6 @@ "description": "An error that code produces bugs or has logic errors.", "type": "string", "const": "error" - }, - { - "description": "Turns off the rule.", - "type": "string", - "const": "off" } ] }, @@ -743,5 +743,5 @@ "additionalProperties": true } }, - "$comment": "Generated by fetch-ast-grep-schema.ts at 2026-08-26T03:59:55.871Z from ast-grep v0.45.2 (https://raw.githubusercontent.com/ast-grep/ast-grep/0.45.2/schemas/rule.json)" + "$comment": "Generated by fetch-ast-grep-schema.ts at 2026-09-15T23:43:40.361Z from ast-grep v0.45.3 (https://raw.githubusercontent.com/ast-grep/ast-grep/0.45.3/schemas/rule.json)" } diff --git a/packages/cli/src/rules/capabilities.ts b/packages/cli/src/rules/capabilities.ts index 4c9ebbae..d64a2bbd 100644 --- a/packages/cli/src/rules/capabilities.ts +++ b/packages/cli/src/rules/capabilities.ts @@ -39,7 +39,7 @@ * Pinned against the binary by `test/ast-grep-vendor-contract.test.ts` * ("engine capabilities" → "reports the pinned version"). */ -export const AST_GREP_VERSION = "0.45.2"; +export const AST_GREP_VERSION = "0.45.3"; /** * Every language ast-grep can parse, verbatim from diff --git a/packages/cli/test/ast-grep-vendor-contract.test.ts b/packages/cli/test/ast-grep-vendor-contract.test.ts index b30ef195..a7312a33 100644 --- a/packages/cli/test/ast-grep-vendor-contract.test.ts +++ b/packages/cli/test/ast-grep-vendor-contract.test.ts @@ -25,7 +25,7 @@ import { escapeRegExp } from "../src/util/regex"; * ast-grep's observable behaviour, pinned. * * Everything here is a property of a **vendored third-party binary**, exact- - * pinned at `0.45.2` in `packages/cli/package.json`. Our own tests assert that + * pinned at `0.45.3` in `packages/cli/package.json`. Our own tests assert that * our code behaves correctly *given* these; this file asserts the givens, so an * ast-grep bump that changes one fails here — naming the assumption and the * code that rests on it — instead of surfacing downstream. @@ -489,13 +489,22 @@ withSg("ast-grep vendor contract", () => { // `off` is accepted in a rule but disables it, so it can never appear in // output (asserted below). A rule at any other severity fails the parse // rather than reaching us, which is what keeps the four-value union safe. + // + // ORDER CHANGED AT 0.45.3, vocabulary unchanged. `off` moved from last + // to first, here and in the vendored schema's `Severity` enum alike: + // `--min-severity` (ast-grep/ast-grep#2917) compares severities as an + // ordered type, and `off` has to sort lowest for "off means no minimum" + // to fall out of that comparison. Nothing of ours reads the order — + // `verify` validates against the schema's `const` values, not their + // position — so the exact text is pinned to make the next reorder + // visible, not because anything depends on it. const cwd = project({ rules: { "no-eval": rule("no-eval", "catastrophe") }, sources: evalSource, }); const result = scan(cwd); expect(result.stderr).toContain( - "unknown variant `catastrophe`, expected one of `hint`, `info`, `warning`, `error`, `off`" + "unknown variant `catastrophe`, expected one of `off`, `hint`, `info`, `warning`, `error`" ); }); diff --git a/packages/cli/test/engine-version-consistency.test.ts b/packages/cli/test/engine-version-consistency.test.ts index 5a3fb29f..45832546 100644 --- a/packages/cli/test/engine-version-consistency.test.ts +++ b/packages/cli/test/engine-version-consistency.test.ts @@ -153,7 +153,7 @@ describe("a resolution says which tier answered", () => { /** * The third link: a pinned package contains the version its NAME claims. * - * The pin says `@ast-grep/cli-…: 0.45.2`, and nothing forced the file inside it + * The pin says `@ast-grep/cli-…: 0.45.3`, and nothing forced the file inside it * to be that. A mispublished or substituted package satisfies the pin, installs * cleanly, and answers `--version` with something else. * diff --git a/packages/cli/test/reconcile-marker.test.ts b/packages/cli/test/reconcile-marker.test.ts index 9938fbf4..30c6f819 100644 --- a/packages/cli/test/reconcile-marker.test.ts +++ b/packages/cli/test/reconcile-marker.test.ts @@ -86,7 +86,7 @@ describe("recording a rules reconciliation", () => { expect(rules?.reconciledTo).toBe(version); // Engine versions are the input a later differential needs. Recorded here // and nowhere else, so an upgrade cannot silently refresh them. - expect(rules?.engines).toEqual({ sg: "0.45.2", vale: "3.20.0" }); + expect(rules?.engines).toEqual({ sg: "0.45.3", vale: "3.20.0" }); }); it("reports the marker through info", async () => { From c50849ea20cc71e0a324b1e4ac2e8b280cedf4b5 Mon Sep 17 00:00:00 2001 From: Jakob Heuser Date: Tue, 15 Sep 2026 16:52:35 -0700 Subject: [PATCH 3/4] test(ast-grep): pin where an inline ast-grep-ignore comment has to sit ast-grep 0.45.3 (ast-grep/ast-grep#2909) stopped treating any comment that CONTAINS `ast-grep-ignore` as a directive; it now has to be the comment's first alphabetic text. Nothing of ours writes these, but `check` scans whatever code a project has, so the change reaches a user as a finding that appears under a prose comment, or an `unused-suppression` hint that vanishes, with nothing saying why. Measured against both binaries, swapped in place: the prose-mention, unused-hint and first-alphabetic cases all fail on 0.45.2 and pass on 0.45.3, and the two baseline cases (the directive works; a genuinely unused one is reported as a hint on the stream) fail when their fixture is broken. Not pinned, deliberately: `--min-severity`. `check` has no severity filter and `runAstGrepScan` passes no such flag, so there is no path by which it reaches a user. tree-sitter 0.27 moved nothing the existing language-alias, kind and pattern pins can see. --- .../cli/test/ast-grep-vendor-contract.test.ts | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/packages/cli/test/ast-grep-vendor-contract.test.ts b/packages/cli/test/ast-grep-vendor-contract.test.ts index a7312a33..040c47ad 100644 --- a/packages/cli/test/ast-grep-vendor-contract.test.ts +++ b/packages/cli/test/ast-grep-vendor-contract.test.ts @@ -326,6 +326,30 @@ const semanticsRule = (body: string) => "", ].join("\n"); +/** + * Every finding `rule("no-eval")` produces over one source file, in stream + * order — including the built-in `unused-suppression` rule's, which is why + * `ruleId` and `severity` are read rather than assumed. + */ +const findingsFor = (source: string) => + scan( + project({ + rules: { "no-eval": rule("no-eval") }, + sources: { "src/a.ts": source }, + }) + ) + .stdout.split("\n") + .filter((line) => line !== "") + .map( + (line) => + JSON.parse(line) as { + ruleId: string; + severity: string; + text: string; + note: unknown; + } + ); + /** A Markdown rule whose `rule:` body is given verbatim, already indented. */ const markdownRule = (body: string) => [ @@ -1095,6 +1119,132 @@ withSg("ast-grep vendor contract", () => { }); }); + /** + * Inline `ast-grep-ignore` comments in SCANNED code, and where the directive + * has to sit to count. CHANGED AT 0.45.3 (ast-grep/ast-grep#2909). + * + * Nothing of ours writes these or documents them, but `check` scans whatever + * source a project has, and ast-grep honours the comment wherever it finds + * one — so a user's code carries this behaviour into `taskless check` + * whether or not they wrote the comment for us. Through 0.45.2 the check was + * a substring search: any comment CONTAINING `ast-grep-ignore` was a live + * directive, so prose describing the mechanism above a flagged line + * suppressed the finding, and prose with nothing to suppress was reported as + * an unused directive. At 0.45.3 the directive must be the comment's first + * alphabetic text. + * + * Both halves of that are the quiet kind: a finding appears that did not + * before, or a hint stops appearing, and nothing says why. Measured against + * both binaries, so each case below records which side of the bump it is on. + */ + describe("inline ast-grep-ignore comments", () => { + it("suppresses the next line, bare or naming the rule", () => { + // The mechanism itself, unchanged across the bump and pinned so the + // cases that follow are read against a working baseline rather than a + // directive that stopped applying altogether. + expect( + findingsFor( + [ + "// ast-grep-ignore", + 'const a = eval("1");', + "// ast-grep-ignore: no-eval", + 'const b = eval("2");', + "", + ].join("\n") + ) + ).toEqual([]); + }); + + it("no longer suppresses from a comment that mentions the directive as prose", () => { + // CHANGED AT 0.45.3. At 0.45.2 this scan reported NOTHING: the + // substring match read the prose as a directive and swallowed the + // finding. A codebase whose comments discuss suppression now reports + // the findings those comments sat on, which a rule author sees as new + // errors from an unchanged rule. + const findings = findingsFor( + [ + "// see ast-grep-ignore: no-eval for how to suppress this", + 'const c = eval("3");', + "", + ].join("\n") + ); + expect(findings.map((finding) => finding.ruleId)).toEqual(["no-eval"]); + expect(findings[0]?.text).toBe('eval("3")'); + }); + + it("no longer reports a prose mention as an unused directive", () => { + // The other half of the same change, in the other direction: at 0.45.2 + // this scan produced an `unused-suppression` hint on the comment line. + // A project that had been carrying that hint sees it disappear. + expect( + findingsFor( + [ + "// This comment mentions ast-grep-ignore as prose", + "const g = 1;", + "", + ].join("\n") + ) + ).toEqual([]); + }); + + it("anchors on the first ALPHABETIC character, not the first character", () => { + // The exact boundary upstream chose: everything before the first letter + // is skipped, so the comment marker, extra whitespace, a block-comment + // opener and even a leading list number are not prose. A directive + // behind `// 1.` therefore still suppresses, which is the case an + // author would guess wrong about from "must be first". (This comment + // is itself scanned by the repo's own `check`, so no line of it may + // start with the token — a wrapped one did, and was reported.) + expect( + findingsFor( + [ + "/* ast-grep-ignore */", + 'const d = eval("4");', + "// ast-grep-ignore", + 'const e = eval("5");', + "// 1. ast-grep-ignore", + 'const f = eval("6");', + "", + ].join("\n") + ) + ).toEqual([]); + // And one letter before it is enough to make it prose again. + expect( + findingsFor( + ["// NOTE ast-grep-ignore", 'const h = eval("7");', ""].join("\n") + ).map((finding) => finding.text) + ).toEqual(['eval("7")']); + }); + + it("reports a genuinely unused directive on the stream as a hint", () => { + // How a user sees any of this at all. `unused-suppression` is a built-in + // rule, not one of ours, and it arrives on `--json=stream` like any + // finding: `ruleId` is the built-in's name, `severity` is `hint` (inside + // AstGrepMatch's union, so it renders), and `note` is `null` rather than + // absent — `check.md` documents that shape, and `format.ts` tests + // truthiness, so the null is tolerated rather than typed. A finding + // that scoped its rule to something else counts as unused too, and the + // finding it did not cover is reported beside it. + const findings = findingsFor( + [ + "// ast-grep-ignore", + "const h = 1;", + "// ast-grep-ignore: other-rule", + 'const i = eval("9");', + "", + ].join("\n") + ); + expect( + findings.map((finding) => [finding.ruleId, finding.severity]) + ).toEqual([ + ["no-eval", "error"], + ["unused-suppression", "hint"], + ["unused-suppression", "hint"], + ]); + expect(findings[1]?.note).toBeNull(); + }); + }); + describe("the `sg` alias prints a deprecation banner on stderr", () => { // DEPRECATED AT 0.45.0. `AST_GREP_BINARY.binaryNames` puts `ast-grep` // first, but the resolver reverses that list at its link-based tiers, so From 5b37146ac379f3f4a3e53e36ad2062da2cb56bcb Mon Sep 17 00:00:00 2001 From: Jakob Heuser Date: Tue, 15 Sep 2026 16:52:42 -0700 Subject: [PATCH 4/4] docs(ast-grep): tell a rule author what 0.45.3 moved --- .changeset/ast-grep-0-45-3.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.changeset/ast-grep-0-45-3.md b/.changeset/ast-grep-0-45-3.md index b9dcdb4b..bec6ec87 100644 --- a/.changeset/ast-grep-0-45-3.md +++ b/.changeset/ast-grep-0-45-3.md @@ -3,3 +3,9 @@ --- Update the bundled ast-grep to 0.45.3 (from 0.45.2). + +What a rule author sees in `taskless check` on ast-grep rules: + +- An inline `ast-grep-ignore` comment in scanned code now takes effect only when it is the comment's first alphabetic text. A comment that merely mentioned the directive as prose above a flagged line used to suppress the finding; it no longer does, so findings can appear that were hidden before. Move `ast-grep-ignore` to the start of the comment if the suppression was meant. +- The same prose mentions no longer produce `unused-suppression` hints. +- Rule files, `taskless verify`, and the language list are unchanged. Nothing installed under `.taskless/` needs migrating.