Skip to content

Render mutant copies by splicing text instead of rebuilding trees - #574

Open
sknigh wants to merge 1 commit into
boxed:mainfrom
sknigh:perf/generation-splice
Open

sknigh wants to merge 1 commit into
boxed:mainfrom
sknigh:perf/generation-splice

Conversation

@sknigh

@sknigh sknigh commented Sep 8, 2026

Copy link
Copy Markdown

In my testing, the mutant generation phase ran about 3x faster with text splicing. There is a small compatibility risk because this implementation relies on an internal libCST API, which won't have the same compatibility SLA as public facing parts. On the other hand, this internal class is small (25 lines), has been stable for years, and a lot of libCST is built on top of it. As explained later, the tree build method remains available.


A mutant is a copy of its function with one node replaced. Generation rebuilt the function tree (deep_replace) and rendered it once per mutant.

Now the function is rendered once through a CodegenState subclass that records the output span of its name and of every mutation target, with the indentation in effect there. A mutant's text is the function's text with those two spans replaced; the replacement node is rendered with the recorded indentation, so the result matches rendering the rebuilt tree. A PrerenderedFunction node carries the text through module assembly and emits it verbatim, so line spans and the rest of the pipeline are unchanged.

A mutant whose target was rendered more than once, or precedes the function name, falls back to the tree rebuild. CodegenState is libcst internal API, but libcst's own node classes are built on it.

The test generates the generation corpus, plus sources with nested blocks, decorators, multi-line expressions, tabs and continuation lines, both ways and compares code, mutant names, line spans and hashes.

A mutant is a copy of its function with one node replaced. Generation
rebuilt the function tree (deep_replace) and rendered it once per mutant.

Now the function is rendered once through a CodegenState subclass that
records the output span of its name and of every mutation target, with
the indentation in effect there. A mutant's text is the function's text
with those two spans replaced; the replacement node is rendered with the
recorded indentation, so the result matches rendering the rebuilt tree.
A PrerenderedFunction node carries the text through module assembly and
emits it verbatim, so line spans and the rest of the pipeline are
unchanged.

A mutant whose target was rendered more than once, or precedes the
function name, falls back to the tree rebuild. CodegenState is libcst
internal API, but libcst's own node classes are built on it.

The test generates the generation corpus, plus sources with nested
blocks, decorators, multi-line expressions, tabs and continuation lines,
both ways and compares code, mutant names, line spans and hashes.
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.

1 participant