Skip to content

feat(res-to-affine): walker parity for raw-js, untyped-exception, mutable-global (Phase 2c, Refs #57) - #385

Merged
hyperpolymath merged 1 commit into
mainfrom
feat/issue-57-phase2c-walker-parity
May 27, 2026
Merged

feat(res-to-affine): walker parity for raw-js, untyped-exception, mutable-global (Phase 2c, Refs #57)#385
hyperpolymath merged 1 commit into
mainfrom
feat/issue-57-phase2c-walker-parity

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Summary

Brings the tree-sitter AST walker (Phase 2b, #322) to feature parity with the Phase-1 line-regex scanner across all four anti-patterns. Walker and scanner now emit the same (kind, line) pairs on the synthetic fixture, locked in by a new alcotest.

Pattern Phase 1 (scanner) Phase 2b Phase 2c (this PR)
side-effect-import ✅ regex ✅ AST
raw-js ✅ regex AST (new)
untyped-exception ✅ regex AST (new)
mutable-global ✅ regex AST (new)

New walker detectors

  • raw_jsextension_expression whose extension_identifier is raw or bs.raw. Reaches inside ERROR subtrees so the older [%bs.raw ...] syntax (which the pinned grammar can't parse cleanly) is still flagged.

  • untyped_exception — three structural triggers:

    1. any try_expression (try/catch block);
    2. value_identifier_path OR module_identifier_path whose surface text starts with Js.Exn or Promise.catch (catches the path in both expression and pattern position — line 22 of sample.res | Js.Exn.Error(_) => is the latter);
    3. call_expression whose function child is the bare identifier raise.
  • mutable_globalmutation_expression that lives directly inside an expression_statement at module top level (ancestor chain is expression_statement :: source_file or expression_statement :: block :: module_declaration). In-function x := y against a local ref is correctly not flagged — the structurally-stronger version of the column-0 regex anchor introduced in docs(res-to-affine): corpus run + regex precision fixes (Refs #57) #319.

Shared helper push_finding consolidates the slice/trim/truncate/cons boilerplate that all four detectors now use. The DFS is one pass with all detectors invoked per node.

Phase 2c follow-ups remaining (not in this PR)

  • The two anti-patterns deferred from Phase 1 (inline lambda callback records, oversized 4+ tuple matches) — both need new Scanner.kind variants and emitter guidance.
  • Flipping the CLI default from --engine=scanner to --engine=walker — a behavior change deserving its own PR.
  • The two precision-pass deferrals in CORPUS-RUN.md (module-block nested side effects, top-level let x = ref(...) declarations).

Test plan

  • dune build tools/res-to-affine clean.
  • dune test tools/res-to-affine/9/9 (3 existing emitter + 6 walker cases, 4 new in this PR).
  • dune build + dune test clean repo-wide (327/327 tests pass).
  • Spot-checked end-to-end against tools/res-to-affine/test/fixtures/sample.res:
    walker emits 6 findings, same (kind, line) set as scanner.
  • CI on this PR.

Refs #57 (multi-phase; this is Phase 2c parity port).

🤖 Generated with Claude Code

…able-global (Phase 2c, Refs #57)

Brings the tree-sitter AST walker (Phase 2b, #322) to feature parity with
the Phase-1 line-regex scanner across all four anti-patterns. Walker and
scanner now emit the same (kind, line) pairs on the synthetic fixture,
locked in by a new `walker / scanner parity` alcotest.

New walker detectors:

- `raw_js` — extension_expression whose extension_identifier is "raw" or
  "bs.raw". Reaches inside ERROR subtrees so the older `[%bs.raw ...]`
  syntax (which the pinned grammar can't parse cleanly) is still flagged.

- `untyped_exception` — three structural triggers:
    1. any try_expression (try/catch block);
    2. value_identifier_path OR module_identifier_path whose surface
       text starts with `Js.Exn` or `Promise.catch` (catches the path in
       both expression and pattern position — line 22 of sample.res
       `| Js.Exn.Error(_) =>` is the latter);
    3. call_expression whose function child is the bare identifier
       `raise`.

- `mutable_global` — mutation_expression that lives directly inside an
  expression_statement at module top level (ancestor chain is
  `expression_statement :: source_file` or
  `expression_statement :: block :: module_declaration`). In-function
  `x := y` against a local ref is correctly NOT flagged — the
  structurally-stronger version of the column-0 regex anchor introduced
  in #319.

Shared helper `push_finding` consolidates the slice/trim/truncate/cons
boilerplate that all four detectors now use. The DFS is one pass with
all detectors invoked per node.

Phase 2c follow-ups remaining (not in this PR):

- Two anti-patterns deferred from Phase 1 (inline lambda callback
  records, oversized 4+ tuple matches) — both need new Scanner.kind
  variants and emitter guidance.
- Flipping the CLI default from --engine=scanner to --engine=walker
  (a behavior change deserving its own PR).
- The two precision-pass deferrals in CORPUS-RUN.md (module-block
  nested side effects, top-level `let x = ref(...)` declarations).

Test plan:
- `dune build tools/res-to-affine` clean.
- `dune test tools/res-to-affine/` — 9/9 (3 existing emitter + 6 walker
  cases, 4 of them new in this PR).
- `dune build` + `dune test` clean repo-wide (327/327 tests pass).
- Spot-checked end-to-end: walker emits 6 findings on sample.res, same
  (kind, line) set as scanner.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hyperpolymath
hyperpolymath enabled auto-merge (squash) May 26, 2026 14:02
@github-actions

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 43 issues detected

Severity Count
🔴 Critical 15
🟠 High 16
🟡 Medium 12

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Action actions/checkout@v6 needs attention",
    "type": "unpinned_action",
    "file": "publish-jsr.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action denoland/setup-deno@v2 needs attention",
    "type": "unpinned_action",
    "file": "publish-jsr.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/example/smoke_driver.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/cli.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/lib/compile.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/lib/runner.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/lib/discover.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/affinescript/affinescript/packages/affine-js/types.d.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath
hyperpolymath merged commit 2395c76 into main May 27, 2026
21 checks passed
@hyperpolymath
hyperpolymath deleted the feat/issue-57-phase2c-walker-parity branch May 27, 2026 12:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant