Skip to content

Add --transitive: settle on what only dead code references - #68

Closed
Komoszek wants to merge 2 commits into
claude/issue-58-implementation-rcw0sgfrom
claude/issue-58-transitive
Closed

Komoszek wants to merge 2 commits into
claude/issue-58-implementation-rcw0sgfrom
claude/issue-58-transitive

Conversation

@Komoszek

@Komoszek Komoszek commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Closes #58. Stacked on #70 (the finder split), so the rounds land inside settler.dart; retarget once it merges.

The text a removal deletes keeps nothing alive. With --transitive each round drops the references inside the previous round's removable findings and classifies again, until nothing changes. One run, no new reference queries (the cross-library probe covers only newly emptied names; override and superclass lookups are memoized), works in a plain report too. Default output is unchanged.

  • dead_spans.dart: the deleted text as a per-file span index. Only non-removalBlocked findings contribute, so the set the report assumes deleted is exactly what --remove deletes.
  • settler.dart: settle loops _round until the span set is stable (cap 16; the set only grows); _liveRefs, _onlyReferencedFrom, memoized overrides and probe names.
  • CrossLibraryReferences.merged / .where.
  • A finding dead only because its container is goes unreported with the container.
  • UnusedDeclaration.onlyReferencedFrom: every dead declaration it hangs on, in source order (text and -f github show the first plus a count; -f json the list).
  • --[no-]transitive, transitive: in ciach.yaml. Off by default.
  • Fixture scenarios/transitive.dart + tests, including: after --remove on a copy, a second run has nothing left to remove; a helper two dead roots call names both. A mutually recursive pair stays — Report dead reference cycles (reachability from live roots) #65.

🤖 Generated with Claude Code

https://claude.ai/code/session_011fqaUyaQtY7AkuTYBjAUJv

@Komoszek
Komoszek marked this pull request as draft September 21, 2026 06:16
@Komoszek
Komoszek force-pushed the claude/issue-58-implementation-rcw0sg branch from d4169d6 to 77780ea Compare September 21, 2026 06:19
@Komoszek
Komoszek force-pushed the claude/issue-58-transitive branch 3 times, most recently from 8b80a5f to a28f619 Compare September 21, 2026 09:00
@Komoszek
Komoszek added this pull request to stack #69 September 21, 2026 09:41
Comment thread lib/src/models.dart Outdated

/// With [FinderOptions.transitive], the finding whose removal deletes this
/// declaration's last reference, as `qualifiedName (file:line)`.
final String? onlyReferencedFrom;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Couldn't transitively dead declaration be referenced by multiple dead declarations?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It could, and the field was wrong about it — fixed in 46be3f7.

_onlyReferencedFrom returned on the first reference that landed in a dead span, so with several dead referrers it named whichever one the analysis server happened to list first: arbitrary, and the name asserted it was the only one.

It is now the list of all of them, deduplicated and sorted by source position (the server's order isn't). Rendering: the text and GitHub reports show the first plus a count, the JSON carries the whole list.

24:6  function  _sharedByDeadRoots  (private)  (only referenced from dead _deadRoot (…:6) and 1 more)
"onlyReferencedFrom": ["_deadRoot (lib/scenarios/transitive.dart:6)",
                       "_secondDeadRoot (lib/scenarios/transitive.dart:20)"]

The fixture gained _sharedByDeadRoots, called from two dead roots, and the test asserts both are named in source order.


Generated by Claude Code

Ciach ran the server session and also collected candidates, fetched
what the verdict reads, decided each finding, and spelled it: 1000
lines with five jobs. Each is now a collaborator, and Ciach keeps the
session and the pipeline.

- CandidateCollector: which declarations get checked — the symbol walk,
  the option filters, entry points and their containers.
- ReferenceFetch: the server round trips — references, tokens, syntax
  nodes — into the SourceIndex.
- Settler: from references to sorted findings.
- Verdict: what becomes of one unused candidate — suppressed,
  report-only, or a finding — and its hints.

A move, not a change: the tests and the verbose narration are the same.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011fqaUyaQtY7AkuTYBjAUJv
The text a removal deletes keeps nothing alive. With `--transitive`,
the settler runs in rounds: each drops the references inside the
previous round's removable findings and classifies again, until
nothing changes — one run, no new reference queries: the cross-library
probe covers only newly emptied names, override and superclass lookups
are memoized. Round one is the plain result, so the default output is
unchanged.

Only findings `--remove` would delete count as gone, so the set the
report assumes deleted is the set removed. A finding dead only because
its container is goes unreported with the container. Each finding this
adds names every dead declaration it hangs on (`onlyReferencedFrom`),
in source order — several may reference the same one.

Off by default: a false positive takes everything only it referenced
with it. A cycle keeps itself alive; that is #65.

Closes #58.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011fqaUyaQtY7AkuTYBjAUJv

Copy link
Copy Markdown
Contributor Author

Closed in favour of #72: the stack was reordered so the finder split (#71) comes first and --transitive lands on the split code, and GitHub does not allow moving the base of a PR that is part of a stack. Same end state; the multiple-referrers thread is addressed there (onlyReferencedFrom is a list).


Generated by Claude Code

@Komoszek
Komoszek removed this pull request from stack #69 September 22, 2026 06:20
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.

ciach should report (and remove) transitively dead code

2 participants