Skip to content

fix(ownts): не принимать options-объект за блок cleanup (false-positive OWN001)#148

Merged
PhysShell merged 2 commits into
mainfrom
claude/ownts-cleanup-span-fix
Jun 27, 2026
Merged

fix(ownts): не принимать options-объект за блок cleanup (false-positive OWN001)#148
PhysShell merged 2 commits into
mainfrom
claude/ownts-cleanup-span-fix

Conversation

@PhysShell

@PhysShell PhysShell commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Что и зачем

Follow-up к смерженному #145 (#145 уехал в main ровно когда я дорабатывал последнее замечание CodeRabbit). Для expression-bodied cleanup, чей removeEventListener передаёт options-объект —

return () => el.removeEventListener("x", h, {capture: true})

_cleanup_span делал find("{") и попадал на { объекта {capture: true}: _match_block обрывал диапазон на } опций и срезал хвостовую ). Получившийся cleanup переставал парситься через _LISTENER, _listener_call возвращал None, и корректно снятый listener報ался как ложная утечка OWN001.

Теперь cleanup считается block-bodied только если первый непробельный символ после => — это {; иначе это выражение, заканчивающееся на переводе строки (целиком сохраняя вызов с options-объектом и закрывающей )).

Тип изменения

  • feat — новая возможность
  • fix — исправление бага
  • docs — документация
  • refactor / chore / test / ci — без изменения поведения

Как проверено

  • python tests/run_tests.py — зелёный (effects 31/31, ownir 194/194)
  • ruff check . и mypy (--strict по ownlang) — чисто
  • селфтест: python frontend/ownts/test_ownts.py (новая фикстура EffectExprCleanup.tsx → 0 находок)
  • Демо: python frontend/ownts/ownts.py frontend/ownts/examples/EffectExprCleanup.tsx --check

Связанные issue

Follow-up к #145 (OwnTS parser hardening). Closes — нет.

Чеклист

  • изменение покрыто тестом/селфтестом (EffectExprCleanup.tsx + CI-шаг)
  • README/docs обновлены при необходимости (поведение зафиксировано тестами)
  • коммиты в conventional-commit стиле (fix:)

🤖 Generated with Claude Code


Generated by Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved effect cleanup analysis so expression-bodied useEffect cleanups that pass listener options (e.g., { capture: true }) are correctly recognized and no longer trigger false leak/instability diagnostics.
  • Tests

    • Added a new React example component demonstrating the cleanup pattern and asserting the analysis returns no findings.
    • Expanded the CI regression coverage to fail if any related leak/instability findings are detected.

…deRabbit)

Follow-up to the merged #145. For an EXPRESSION-bodied effect cleanup whose
removeEventListener passes an options object —
  return () => el.removeEventListener("x", h, {capture: true})
_cleanup_span did `find("{")`, which landed on the `{` of `{capture: true}`,
so _match_block ended the span at the options `}` and truncated the trailing `)`.
The resulting cleanup no longer parsed via _LISTENER, so _listener_call returned
None and a correctly-released listener was reported as a false OWN001 leak.

Now a cleanup is treated as block-bodied only when the first non-whitespace
character after `=>` is `{`; otherwise it is an expression body ending at the
line break (which keeps the whole call, options object and closing `)` included).

New fixture EffectExprCleanup.tsx (expect zero findings), pinned in test_ownts.py
and a CI step. Full suite green (effects 31/31, ownir 194/194); ruff + mypy
--strict clean; all existing fixtures unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PVeuchch67CtjSbizYm8gi
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 11a119b6-35e0-46e6-b478-b1a306af7495

📥 Commits

Reviewing files that changed from the base of the PR and between 23c89f2 and 49294f1.

📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • frontend/ownts/examples/EffectExprCleanup.tsx
  • frontend/ownts/ownts.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/ownts/examples/EffectExprCleanup.tsx
  • frontend/ownts/ownts.py
  • .github/workflows/ci.yml

📝 Walkthrough

Walkthrough

This PR updates OwnTS cleanup-span parsing for expression-bodied useEffect cleanups, adds a React regression example with an inline options object, and extends test and CI coverage to assert that the analysis stays silent.

Changes

Expression-bodied cleanup parsing

Layer / File(s) Summary
Cleanup span handling
frontend/ownts/ownts.py
_cleanup_span now parses single-expression cleanups by tracking nested structure instead of searching for a later block start.
Regression coverage
frontend/ownts/examples/EffectExprCleanup.tsx, frontend/ownts/test_ownts.py, .github/workflows/ci.yml
A new ExprCleanup fixture, a local assertion, and a CI step run the options-object cleanup case and assert that OwnTS/OwnIR produce no findings.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • PhysShell/Own.NET#144 — Shares the same OwnTS React effect parsing path and cleanup detection area.
  • PhysShell/Own.NET#145 — Also changes effect parsing and cleanup handling around listener options and release matching.

Poem

🐇 I hopped through braces, neat and small,
With capture true, I checked them all.
The cleanup whispered, “newline’s near,”
And silence bloomed—no leaks to fear.
✨ A carrot crisp, a parser sound!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the bug fix in OwnTS cleanup parsing and matches the main change set.
Description check ✅ Passed The description follows the required template and fills the key sections with relevant details, testing, and links.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/ownts-cleanup-span-fix

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 1093-1099: The silent-case check is masking failures by forcing
`ownlang ownir` to succeed even when it crashes, so the zero-findings assertion
can pass on an empty result. Update the regression test in the CI workflow to
call `ownlang ownir` directly and let it fail normally, then assert zero
findings only on a successful run using the `expr` capture and grep check. Keep
the existing `expr`/grep-based check, but remove the `|| true` fallback so
parser/core regressions are not hidden.

In `@frontend/ownts/ownts.py`:
- Around line 398-403: The expression-bodied cleanup parsing in the cleanup
extractor is stopping too early by using the first newline after the arrow,
which truncates formatted multi-line cleanups and breaks _listener_call
detection. Update the cleanup span logic in the expression-bodied branch of the
parser in ownts.py so it keeps consuming the full cleanup expression across line
breaks, including cases like return () => followed by an indented call or
multiline options objects, and only stops when the actual expression ends.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 242c3764-66c0-410c-bc1f-1c00a0f33683

📥 Commits

Reviewing files that changed from the base of the PR and between 3703b05 and 23c89f2.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • frontend/ownts/examples/EffectExprCleanup.tsx
  • frontend/ownts/ownts.py
  • frontend/ownts/test_ownts.py

Comment thread .github/workflows/ci.yml
Comment thread frontend/ownts/ownts.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 23c89f2ff8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml Outdated
run: |
python frontend/ownts/ownts.py frontend/ownts/examples/EffectExprCleanup.tsx \
-o "$RUNNER_TEMP/expr.facts.json"
expr=$(python -m ownlang ownir "$RUNNER_TEMP/expr.facts.json" || true)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fail this silent-fixture check on OwnIR errors

In the new Expression-bodied cleanup with an options object is silent workflow section, || true hides every non-zero status from python -m ownlang ownir. I checked ownlang/__main__.py: cmd_ownir returns 2 for malformed/drifted OwnIR facts and returns 0 when there are no leaks, so for this expected-silent fixture an OwnIR load/schema error or crash that emits no [OWN...] line would still leave the grep -cE count at 0 and let the regression step pass without validating the generated facts. Preserve/check the exit code instead of unconditionally swallowing it.

Useful? React with 👍 / 👎.

Two follow-ups from review (CodeRabbit + Codex) on the cleanup-span fix:

- Expression-bodied cleanups were cut at the FIRST newline after `=>`, so a
  formatted cleanup like
    return () =>
      el.removeEventListener("resize", onResize);
  left an empty cleanup and reintroduced the false OWN001. _cleanup_span now
  consumes the whole expression across line breaks, stopping only at a top-level
  `;` or the effect body's own closing brace (depth-aware over the masked body).

- The silent-case CI step used `|| true`, which would turn an OwnIR load/schema
  crash (rc 2, no findings printed) into a false-green zero-count. Dropped it so a
  parser/core regression fails the step; the other steps keep `|| true` because
  they legitimately expect findings (rc 1).

EffectExprCleanup.tsx gains a multi-line cleanup case. Full suite green
(effects 31/31, ownir 194/194); ruff + mypy --strict clean; all fixtures
unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PVeuchch67CtjSbizYm8gi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants