Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9053923
test(type3clonecheck): rename p2ok to ok so G1 checks it; add write g…
liziye627-design Oct 23, 2026
84fe66c
fix(quality): keep Django and unittest hooks out of the dead-code lis…
llvm-x86 Oct 25, 2026
3d70c98
fix(kotlin): a trailing-lambda scope function never binds a Java meth…
llvm-x86 Oct 24, 2026
4438f15
test(qschemetrip): re-pin for the #155 placeholder versions
llvm-x86 Oct 25, 2026
4cb3f9b
test(quality): give the Python hook arm negatives that can fail (#437…
joyful-ii-V-I Oct 26, 2026
c64e76d
fix(kotlin): keep a Kotlin receiver's proof for trailing-lambda scope…
joyful-ii-V-I Oct 26, 2026
9f63bb5
refactor(kotlin): reuse isStrictlySortedTable and firstChildOfKind (#…
joyful-ii-V-I Oct 26, 2026
24d8554
chore(docs): drop the regenerated COMMANDS.md and showcase capture (#…
joyful-ii-V-I Oct 26, 2026
0a0589b
fix(quality): pytest xunit hooks count as framework entry points; one…
joyful-ii-V-I Oct 26, 2026
80dbb88
test(dead): pin setUpTestData and runTest as --dead-code runner roots…
joyful-ii-V-I Oct 26, 2026
6f37231
Merge PR #431 (@liziye627-design): type3clonecheck p2ok → ok
joyful-ii-V-I Oct 26, 2026
481c6f8
Merge PR #438 (@llvm-x86, Fixes #155): Kotlin scope functions (mainta…
joyful-ii-V-I Oct 26, 2026
ae9e549
Merge PR #437 (@llvm-x86, Fixes #228): Django/unittest hooks + closur…
joyful-ii-V-I Oct 26, 2026
70400c4
fix(showcase): scrub the repo root and scratch dir only as whole path…
joyful-ii-V-I Oct 26, 2026
185ba69
versions: kParserVer 231 (rich 232), kCacheVersion 43, kQSnapCacheSch…
joyful-ii-V-I Oct 26, 2026
79e6918
test(langidentity): lower the ceiling 335 -> 333 to the composed tree…
joyful-ii-V-I Oct 26, 2026
bd2aaa0
docs: showcase capture 2026-10-25 + COMMANDS.md regenerated on the tr…
joyful-ii-V-I Oct 26, 2026
6a98316
changelog.d: credit @llvm-x86 on the #437 and #438 fragments
joyful-ii-V-I Oct 26, 2026
a45f4da
test(showcase): pin the generator's scrub_root call site so a plain r…
joyful-ii-V-I Oct 26, 2026
7a5181f
changelog.d: the #228 fragment names the renumbered snapshot scheme (…
joyful-ii-V-I Oct 26, 2026
6df53cf
test(pyimportprecise): the cache walker's history names v43 and the t…
joyful-ii-V-I Oct 26, 2026
0712a51
Merge origin/integration/train-35 (harvested head 6580f358) into inte…
joyful-ii-V-I Oct 26, 2026
fb19c62
docs: showcase capture 2026-10-26 + COMMANDS.md regenerated on the me…
joyful-ii-V-I Oct 26, 2026
c477a88
fix(cache): a flag byte other than 0 or 1 refuses the record (CodeRab…
joyful-ii-V-I Oct 26, 2026
00f4aa8
docs(quality): the pytest-hook comment no longer says @pytest.fixture…
joyful-ii-V-I Oct 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .ripwire_quality_acks
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ ack duplication 5a94cb2859bf1601 71 now=71 was=0 prov=recon a probe TU deliberat
ack duplication 5ae632d0ded8f363 58 now=58 was=0 prov=recon facet=switch-name-table per-enumerator switches name every enumerator and carry no default: on purpose — that is what makes -Werror=switch a compile error at each one when an enumerator is appended; two such switches over one enum share the enumerator spelling by construction
ack duplication 5bfc60bf2eec4379 58 now=58 was=0 prov=recon readability-wave1 (clones.h scanCodeTokens extraction): normalizeTokens and objectKeys are both 'call a scanner, emplace_back each visit into a vector, return it' — the same 58-token visitor-to-vector adapter over two unrelated scanners in two different layers (the clone tokenizer in core vs the JSON top-level-key walker in the MCP layer). Neither can call the other; a shared generic adapter would be a cross-layer template earning six lines. Kept as written.
ack duplication 5c9174022e5a0734 44 DISCLOSE sinks: a reason-to-field switch is the shape every multi-reason sink shares; -Wswitch exhaustiveness over DisclosureWhy is the point (Diagnostics.h 4b)
ack duplication 5c98961b8ec17a64 35 now=35 was=0 p=src/docparse.h:160 #155 (pr438 B2): inSortedTable is the span form of the table lookup isStrictlySortedTable's tables share; isMarkdownGrammarExtension is the same one-line binary_search over a different table, and merging them would need docparse.h to include externalnames.h for no gain
ack duplication 5e4956ffb34ce360 36 now=36 was=0 prov=recon M1: a 36-token shape coincidence, not duplication. isMcpProtocolVersionSupported walks a version list and mcpVerbDeclaresLegend walks a field list; both are the C++ spelling of 'is X in this list', over different tables, different types and different call sites. Folding them into one generic helper would couple the protocol handshake to the legend family for no shared meaning.
ack duplication 5f01b66132b916bd 44 DISCLOSE sinks: a reason-to-field switch is the shape every multi-reason sink shares; -Wswitch exhaustiveness over DisclosureWhy is the point (Diagnostics.h 4b)
ack duplication 5fad9d85c2552890 32 now=32 was=0 p=src/receiverevidence.h:142 lane FE-B (receiver evidence): lexical clone pairs, not shared logic — the per-language predicates (implicitReceiverLang, classNameReceiverLang, memberNeverReachesFreeFunction, isReceiverEvidenceKind, callableKind, classLike, isThisRoot) are one-line OR-lists over different enum sets that share only the shape with quality.h/model.h/slice.h predicates asking other questions; keyOf/typeKey build different keys into a reused buffer; classAt (innermost class span by byte) vs flipimpact innermostAtLine (by line, other table); impactViaColumn vs lintrules dependencyCapableMask share a bitmask idiom; the JS fixture encode|stringify is a deliberate twin (test/receiverevidencefix: same-named near misses); pargates.py is unchanged since cd87e30e (a regrouped pre-existing pair). The one real clone, the enclosing-function walk, was merged into innermostEnclosingFn instead of acked
Expand Down
18 changes: 18 additions & 0 deletions changelog.d/155-kotlin-scope-functions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
### Fixed — Kotlin scope functions no longer bind Java methods (#438, thanks @llvm-x86)

A Kotlin call with a trailing lambda to `let`, `run`, `with`, `apply` or `also` now stays off the Java
method of the same name. Before this change, `s.run { }` bound `Job.run`, and `with( sb ) { }` bound
`Builder.with`. Ripwire now reads the call as the stdlib scope function. It drops the Java candidates and
keeps the Kotlin ones. A call with no Kotlin target reports `external=` with that proof.

Calls without a trailing lambda keep their edges. `j.run()` still binds `Job.run`, and `b.with( "x" )` still
binds `Builder.with`.

Known floors:

- A parenthesised lambda, such as `s.let( { } )`, is not a trailing lambda. Its edge is unchanged.
- A Java method called from Kotlin with a trailing lambda is dropped when its name is a scope-function name.
The spelling alone does not tell the two apart.
- A receiver of a Java type is not checked. A receiver whose type is an in-tree Kotlin class that defines the method
keeps its own binding (`t.run { }` on a `t: Task` binds `Task.run`).
- A qualified `kotlin.run { }` can still bind an in-tree Kotlin `run`, hedged.
21 changes: 21 additions & 0 deletions changelog.d/228-django-unittest-hooks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
### Fixed — Django and unittest hooks no longer read as dead code (#437, thanks @llvm-x86)

Before: `--quality-delta` reported a Python hook as dead when no call in the tree named it. unittest and Django call
`setUp`, `tearDown`, `setUpClass` and `setUpTestData` themselves. The runner calls each `test*` method. A closure inside a
method that called `self.hook()` also lost the call. Each such method appeared as dead code.

Now: the tool treats these names as called. It covers `setUpModule`, `tearDownModule`, `load_tests`, `runTest`, `asyncSetUp` and `asyncTearDown` too. The pytest xunit hooks `setup_module`, `teardown_module`,
`setup_function`, `teardown_function`, `setup_class`, `teardown_class`, `setup_method` and `teardown_method` count as called too. A `test*`
method (any name that starts with `test`) and an xunit hook count as called only in a test module: a path `isTestPath` accepts, or a Django `tests.py`. A closure
that calls `self.` or `cls.` keeps the overrides of the method it calls live. A closure that binds `self` or `cls` as its own
parameter does not.

The HEAD snapshot scheme moves from 22 to 24. Old cached snapshots are not used.

Not covered, still reported: pytest's conftest hooks such as `pytest_configure`. (`@pytest.fixture` functions are not reported as dead code here.)

The hook names live in one table per framework (`kUnittestHooks`, `kXunitHooks`, `kUnittestModuleHooks` in `quality.h`), shared with `--dead-code` and `--safe-delete`, which now also skip `setUpTestData` and `runTest` in a TestCase subclass.

Limit: the name check ignores types and scope. A method named `setUp`, `tearDown` or `load_tests` in application code is also treated as called. In a test module, any def named `test*` counts as called, nested defs and helper functions included. The tool errs toward live here.

A closure that binds `self` or `cls` as its own parameter does not count, even for `def cb(self=self)`.
12 changes: 7 additions & 5 deletions changelog.d/versions.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
### Changed — the versions this change moves, stated once

`kParserVer` 206 → 226 (file tag 227): the Rust written-type receivers carried 202 (203) on their branch and the struct-field
`kParserVer` 206 → 231 (file tag 232): the Rust written-type receivers carried 202 (203) on their branch and the struct-field
slot join 204 (205), and its fix rounds changed what 204 extracts, so a cache from that branch would replay the older slot
rows; the interface-dispatch records carried 210 and 212 on theirs; the JS/TS written-binding records carried 218 and the
Python rebind-withholding records a 224 placeholder on theirs; 226 sits above every live branch and pull-request number
(212 to 225). A cache written by 0.6.8 is re-parsed once. `kCacheVersion` 36 → 40: a Rust binding typed only through a trait
Python rebind-withholding records a 224 placeholder on theirs; train 34 took 226; the Kotlin trailing-lambda capture (#155)
carried a 228 placeholder and another branch holds 229, so 231 sits above every live branch and pull-request number. A cache
written by 0.6.8 is re-parsed once. `kCacheVersion` 36 → 43: a Rust binding typed only through a trait
(`&dyn T`, `impl T`, a bound), a Rust `impl T for X` block and a JS/TS binding written after its declaration are new binding
records, so a cache written before them is rejected and rebuilt (the branches that added them used 37, 38 and 39). The
quality-snapshot scheme stays 22. The session legend dictionary is `dictv=8c941c6c17d27549 entries=932`.
records, and a Kotlin call's trailing-lambda byte (#155) widens the reference record, so a cache written before them is
rejected and rebuilt (the branches that added them used 37, 38, 39 and 41). The quality-snapshot scheme moves 22 → 24 (#228: Django and
unittest hooks and closure self-dispatch change the dead set; old snapshots are not used; the branch used 23). The session legend dictionary is `dictv=8c941c6c17d27549 entries=932`.
12 changes: 12 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,18 @@ triple-quoted string that ends in an escaped `$`). `test/kotlincheck.sh`
covers extraction, both bridge directions in flat and split layouts, the Java-edge invariant, the
bodyless-type collapse, hostile nesting, metrics and determinism.

**Kotlin scope functions (#155).** A call with a trailing lambda to `let`, `run`, `with`, `apply` or `also`
(`s.run { }`, `with( sb ) { }`, `xs.also { }`) is the stdlib inline function, so FE-A (`FalseEdgeRules`, `graph.h`)
drops every Java candidate of that name and keeps the Kotlin ones; an emptied set is `external=` with that proof.
The fact is `Reference::trailingLambda`, set by `kotlinTrailingLambda` (`ingest_metrics.h`); the names are
`kKotlinScopeFunctionNames` (`externalnames.h`, regenerated by one `javap` command recorded there). A receiver rule
that already picked a Kotlin member (`t.run { }` on a `t: Task` whose class defines `run`) keeps its proof; a pick
holding a Java method is undone, so `s.run { }` is decided here. Stated floors: no receiver typing beyond that (a
receiver of a Java type is not checked); a qualified `kotlin.run { }` can still bind an in-tree Kotlin `run` (hedged); no SAM conversion; a parenthesised lambda (`s.let( { } )`) is not trailing and
keeps the ladder; and a Java method of one of these names called from Kotlin with a trailing lambda is dropped,
because the spelling alone cannot tell the two apart. `test/kotlincheck.sh` §15 and `test/falseedgecheck.sh` arm J
pin it.

<a id="clojure-extraction"></a>
**Clojure extraction.** The vendored Yogthos parser is a fork of Sogaiu's grammar. The grammar
rules match at the pinned revisions, while Yogthos' generated ABI-15 parser completed 4,930 of 4,933
Expand Down
Loading
Loading