Pin the four tooling interface ids and inherit them from a mock - #119
Pin the four tooling interface ids and inherit them from a mock#119thedavidmeister wants to merge 4 commits into
Conversation
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 57 minutes Limit details: You’ve used all 1 included review currently available under your plan. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…cond mock ToolingMock declares all four tooling interfaces and overrides all five builders, so the compile-time conformance a separate mock was carrying is already there. The interface tests reach the interface type by assignment rather than by a cast through address, so the inheritance itself is what the compiler checks. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
@coderabbitai review |
|
Closes #74.
The four published tooling interfaces are implemented by contracts in other
repositories and probed over ERC-165 by contracts already deployed on chain, and
nothing in this repository held their ids.
src/interface/had no test file atall, so a rename or a signature change carried through every implementer moved
type(I…).interfaceIdoff what is deployed while every downstream ERC-165 testrecomputed the constant from the same source and stayed green.
test/src/interface/I*.t.sol— one file per interface, mirroringsrc/interface/under the placement rule — pins each id to a literal and holdsToolingMockagainst the interface it implements.IOpcodeToolingV10x514b5d4fIParserToolingV10x1a2c8eddISubParserToolingV10x336284d4IIntegrityToolingV10xb92d7553The four literals were not taken from the issue.
nix develop -c cast siggivesbuildOpcodeFunctionPointers()0x514b5d4f,buildSubParserWordParsers()0x336284d4,buildIntegrityFunctionPointers()0xb92d7553,buildLiteralParserFunctionPointers()0xccf44775andbuildOperandHandlerFunctionPointers()0xd6d8c9a8. The first three interfacesdeclare one function each, so their id is that selector;
IParserToolingV1declares two, and
0xccf44775 ^ 0xd6d8c9a8 = 0x1a2c8edd. All four agree with theissue's numbers, and none of them moved when #124 changed the builders to
view— state mutability is not part of a selector.
ConformingToolingMockis deleted and the tests holdToolingMockinsteadThis branch previously added a second mock. Its only reason to exist was that
IParserToolingV1andISubParserToolingV1declared their three builderspure, so no per-instance mock could implement them andToolingMockcould notinherit the interfaces at all. #124 has since landed: all five builders are
view, andToolingMockdeclaresis IOpcodeToolingV1, IParserToolingV1, ISubParserToolingV1, IIntegrityToolingV1withoverrideon all five. The reasonis gone, so the mock goes with it.
The case for keeping it was that a
pureimplementation conforming to aviewdeclaration is a distinct property from a storage-reading one. It is distinct,
but it is a property of the compiler rather than of this repository: an override
may only tighten state mutability, and
pureis the tightest, so apureimplementation conforms to every declaration that is not
payable. That makes itstrictly the weaker instrument, and this was measured rather than reasoned.
Restoring
ConformingToolingMocknext toToolingMockand tightening eachbuilder's declaration to
purein turn,forge buildcites:ToolingMockis named on all five.ConformingToolingMockis named on two, andis blind to the three it implements
pure— the three the issue was worriedabout. There is no mutation of the four interfaces it catches that
ToolingMockdoes not, and three that
ToolingMockcatches and it does not.Ledger for the deletion. Build cost is nil — a
git rmand four import lines.Carrying cost is a second 68-line mock kept in step with five builder signatures,
answering three of them from constants no test pins, and a standing question at
every future interface change about which of two mocks is the one that matters.
Cost of bringing it back is the same
git rmin reverse, and the condition thatwould call for it — an interface going back to declaring a builder
pure— makesToolingMockstop compiling and say so, which is the same signal by a shorterroute.
What replaces it is not an
addresscast. Each test reaches the interface typeby assignment —
IOpcodeToolingV1 tooling = mock;— which compiles only whileToolingMockactually inherits that interface. TheI…(address(mock))form theearlier revision used compiles whether or not anything inherits anything, which
is the hole the issue names: "tests cast a bare address to the interface at the
call site". Dropping an interface from
ToolingMock's inheritance list alongwith its
overrideand@inheritdoc— the shape a "simplify the mock" changetakes — leaves every
test/libcall site green, and is caught only by these fourlines.
Divergence from the issue's proposed fix
The issue sketches one
testToolingInterfaceIds()holding all four ids, plus anew
ConformingToolingMock. This ships four files rather than one, because theplacement rule keys
test/src/**on the subject's path; and it ships no new mockat all, because the mock the issue asks for now exists as
ToolingMock.QA
Merged
origin/main(b422d97, carrying Declare every tooling builderviewso an implementation can read state #124) into this branch asf6910ca.The merge is textually clean, and the full suite on the merge commit — before
any of this revision's changes — is
Ran 22 test suites in 2.99s (25.60s CPU time): 150 tests passed, 0 failed, 0 skipped (150 total tests), so the mergeis checked by a run rather than by the absence of conflict markers.
Full suite after the revision, on the flake toolchain:
Ran 22 test suites in 1.87s (19.21s CPU time): 150 tests passed, 0 failed, 0 skipped (150 total tests). The four interface suites are8 tests passed, 0 failed, each fuzztest at
runs: 2048.nix develop -c forge fmt --checkexits 0.Mutation matrix: 45 mutants, 45 killed, 0 survived. Every mutant was applied
to a committed-clean tree, scored by a full
forge testwithcache/fuzz/failuresdeleted first so no counterexample replay could stand infor a discovery, and reverted with
git checkout --after. The unmutated treewas scored through the same harness first and reported
rc=0withtotal_ran=150— which is what rules out a harness error reading as a cleanpass.
git statusis empty after the run.A. Interface mutants killed at compile time (30). For each of the five
builders across the four interfaces: rename, return type
bytes memory→bytes32, add auint256argument,view→pure,view→ nonpayable. Allrc=1,total_ran=0. The two mutability directions are caught by twodifferent mechanisms, both read out of the compiler:
A tightening is caught by the mock; a loosening is caught by
LibCodeGen's ownviewwrappers. Both directions are red, and neither needed a second mock.B. Adding a builder to each interface (4).
Error (3656): Contract "ToolingMock" should be marked as abstract.on all four.C. Flipping each pinned id literal by one nibble (4). All four compile and
run —
total_ran=150— and fail on the pin alone.D. Dropping each interface from
ToolingMock's inheritance, with itsoverrideand@inheritdoc(4). All four areError (9574): Type contract ToolingMock is not implicitly convertible to expected type contract I…, citedat the new assignment line —
IOpcodeToolingV1.t.sol:38,IParserToolingV1.t.sol:47,ISubParserToolingV1.t.sol:38,IIntegrityToolingV1.t.sol:38. Nothing else in the suite notices these.E. Coherent mutants, where the whole tree still compiles (8). These are what
the pin exists for: a change carried through every implementer and every caller
leaves the suite green otherwise. All eight run
total_ran=150and fail onlyon the id test.
buildOpcodeFunctionPointersrenamed acrosssrcandtest0xb00765840x514b5d4fbuildLiteralParserFunctionPointersrenamed acrosssrcandtest0xb1dc44210x1a2c8eddbuildSubParserWordParsersrenamed acrosssrcandtest0x89ff7ed10x336284d4buildIntegrityFunctionPointersrenamed acrosssrcandtest0x9cf38df80xb92d7553IOpcodeToolingV1and implemented onToolingMock0xebcf676f0x514b5d4fIParserToolingV1and implemented onToolingMock0xc53e923c0x1a2c8eddISubParserToolingV1and implemented onToolingMock0x41e370a00x336284d4IIntegrityToolingV1and implemented onToolingMock0xb563b4c40xb92d7553The last four are the issue's own scenario verbatim — a sixth builder lands
without minting a
V2, everything in every repository recompiles, and the onlything that goes red is the literal.
Oracle: the ABI encoding rules and ERC-165, not the source. Each pinned id was
derived from
cast sigover the five builder signatures and, forIParserToolingV1, an exclusive or done by hand — never read back fromtype(I…).interfaceId, which is the expression under test.Category check: the category is every interface published from
src/interface/.ls src/interfacereturns exactlyIIntegrityToolingV1.sol,IOpcodeToolingV1.sol,IParserToolingV1.sol,ISubParserToolingV1.sol, andfind test/src/interface -name '*.t.sol'returns exactly four matching files,so the set is closed on the rule rather than on the issue's list.
Not asserted, and deliberately: nothing pins which of the two selectors
IParserToolingV1's id exclusive-ors together carries which meaning, becausethe id genuinely cannot say. What holds the two builders apart is that each is
read back through its own name against its own value, both here and in
test/lib/LibCodeGen.*ConstantString.t.sol.