Uh oh!
There was an error while loading. Please reload this page.
Unname every return under src/, and stop the generators emitting named ones - #140
Conversation
Each function loses the name from its returns clause, gains an explicit return on every path, and keeps its @return tag without the name. AddressRegistry.get is one of them, so the contract's runtime code is a byte longer and its candidate deploy address and code hash move with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The released-suites emitter writes an unnamed returns clause, declares the array as a local and returns it. Both generated libs are regenerated from it, so the next script/Build.sol run reproduces them byte for byte. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
deployToNetworks proves deployedAddress equals expectedAddress on every path through the loop, so the deploy branch keeps a local for its own comparison and the function returns expectedAddress. newestFrozenTag refuses an empty record up front and seeds from the first path. Both were what slither's uninitialized-local detector named. Seeding them with the value the declaration already defaults to would be a line no test can kill. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…unname-returns # Conflicts: # src/abstract/RegistryDeploySuites.sol
generatedContractNames and aggregateImportBlock lose their names, and the aggregate emitter writes an unnamed clause with a local and a trailing return. LibReleasedSuites is regenerated from it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The name list the freeze and the aggregate read reaches both only through forge script, so nothing asserted it against generatedContracts(). A list that lost an entry froze one contract fewer and emitted an aggregate that declared that contract's releases as nothing, with the suite green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Warning Review limit reached
Next review available in:40 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 ignored due to path filters (1)
📒 Files selected for processing (14)
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 |
Uh oh!
There was an error while loading. Please reload this page.
…buildscript-base #140 swept named returns out of the repo (#129: this repo does not name return values), and #139 pinned the name list against the declaration. Both landed on the functions this branch renames. Conflicts, and how they were taken: * script/Build.sol - main unnamed generatedContractNames()'s return; this branch renamed that function to snapshotContractNames() as BuildScript's hook. Kept the rename, took main's style: unnamed `returns (string[] memory)` with an explicit `return names;`. generatedContracts() was already main's unnamed form and came through the automatic merge unchanged. * test/concrete/BuildHarness.sol - main's externalGeneratedContractNames() wrapper and this branch's externalSnapshotContractNames() are the same seam onto the renamed function. Kept this branch's, dropped main's: the function it called no longer exists. * test/script/Build.t.sol - merged without markers, but main's testGeneratedContractNamesAreTheDeclarationInOrder called the wrapper that went. Repointed at externalSnapshotContractNames() and renamed to match, so main's positional assertion is not lost. Its doc reference, and the one in testTheCommittedAggregateIsInDeclarationOrder, follow the rename. test/concrete/BuildScriptHarness.sol is not a conflict but its snapshotContractNames() is added by this branch with a named return, so it is unnamed here too, with an explicit return. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`#139` and `#140` landed under this branch, and `#126`'s aggregate writer came with them. Four files conflicted. - `foundry.toml`: both sides add the SAME `./fixture-lib` fs_permissions entry, predicted by this PR's "Not in this PR" note. One entry, with a comment covering all three writers that are pointed there. - `.soldeerignore`: both sides add `/fixture-lib`, in different positions. Deduped, keeping the sorted one. - `src/lib/LibRainDeploySnapshot.sol`: main introduced `LIB_DIR`, `pathForLib` and `releasedLibraryName` for the aggregate writer, so this branch's own `LIB_DIR` is a second declaration of main's and its hand-concatenated paths are main's helper spelled twice. Main's are kept, and the two writers this PR parameterises now build their path with `pathForLib(libDir, libraryName)`. The aggregate writer's NatSpec claimed the other two writers "take no directory and are always `LIB_DIR`", which this PR makes false; it now says they take the same `libDir`. - `test/script/Build.t.sol`: both sides reworded one paragraph. This branch's reason (both entry points rewrite committed files other contracts read) with main's conclusion (nothing below writes anything, which is what main's new reading tests made true). - `test/src/lib/LibRainDeploySnapshot.t.sol`: main added `generatedFilePrefix()` and the aggregate emitter tests where this branch deleted `testWriteReleasedSuitesLibWritesTheLibAtItsPath`. All of main's is kept and the deletion stands — that test wrote the committed `src/lib/LibAddressRegistryReleased.sol`, which is the race this PR removes, and `testTheCommittedReleasedLibIsWhatTheGeneratorEmits` carries its staleness half without writing. The three tests this PR added spell the generated header out literally; they use `generatedFilePrefix()` now. `testTheCommittedAggregateIsWhatTheGeneratorEmits` cited the deleted test by name and now cites its replacement. Return style is main's throughout: unnamed `returns (...)` with an explicit `return`, per #129 and the #140 sweep. Nothing added here names a return, and `forge build --force` emits no solc warning at all. `nix develop -c forge test`: 275 passed / 52 failed, every failure `*_RPC_URL not found`; `grep '[FAIL' | grep -vc '_RPC_URL. not found'` = 0. `git status` clean after the run and `fixture-lib/` empty, so no test writes a committed file. `nix develop -c forge fmt --check` exit 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Closes#129
Premise checked first
Every named return the issue lists at
369cfe9was still there when this branched offed91bfa, after #131 and #134. Nothing was already fixed.mainthen moved again — #136 landed the generated aggregate — and that merge is in here, so the sweep covers the codemainhas now rather than the code the issue was filed against.What changed
Each function loses the name from its
returnsclause, gains an explicitreturnon every path, and keeps its@returntag with the name dropped from the tag text — the formzoltuAddressalready used.src/abstract/RainDeploySuitesBase.solcheckedCandidateSuitescandidatessrc/abstract/RainDeploySuitesBase.solallSuitessuitessrc/abstract/RainDeploySuitesBase.solsuiteNamesnamessrc/abstract/RegistryDeploySuites.solcandidateSuitescandidatessrc/abstract/RainDeployVerifyBase.solderiveDeploymentderivedsrc/abstract/RainDeployVerifyBase.solderiveDeploymentsderivedsrc/lib/LibRainDeploy.solisStartBlockisStartsrc/lib/LibRainDeploy.solfindDeployBlockdeployBlocksrc/lib/LibRainDeploy.soldeployZoltudeployedAddresssrc/lib/LibRainDeploy.soldeployToNetworksdeployedAddresssrc/lib/LibRainDeploy.soldeployAndBroadcastdeployedAddresssrc/lib/LibRainDeploySnapshot.solfrozenSnapshotPathspathssrc/lib/LibRainDeploySnapshot.solsortedRecordPathssortedsrc/lib/LibRainDeploySnapshot.solreleasedImportBlockimportssrc/lib/LibRainDeploySnapshot.solnewestFrozenTagnewestsrc/lib/LibRainDeploySnapshot.solaggregateImportBlockimportssrc/interface/IAddressRegistryV1.solgetaccountsrc/concrete/AddressRegistry.solgetaccountscript/Build.solgeneratedContractscontractsscript/Build.solgeneratedContractNamesnamesRegistryDeploySuites.releasedSuitesis on the issue's list but is not in this diff: #136 replaced its body withreturn LibReleasedSuites.releasedSuites();, which is already unnamed.No named return is left under
src/orscript/.test/is out of scope per the issue.The two the issue calls the hard case
deployToNetworksassigneddeployedAddressonly inside theif/elsearms of the loop overnetworks, safe solely becauseNoNetworksguarantees an iteration. Enumerating the paths shows both arms end withdeployedAddress == expectedAddress— the deploy arm revertsUnexpectedDeployedAddressunless they are equal, the skip arm assigns it outright — so the accumulator carried nothing the caller had not already handed in. The deploy arm keeps a local for its own comparison, and the function returnsexpectedAddressafter the loop.newestFrozenTagusedbytes(newest).length == 0as an is-this-the-first-iteration flag on the default-initialised named return. It now refuses an empty record up front, seeds frompaths[0]and scans from 1.Both are also what slither's
uninitialized-localdetector named once the return was unnamed. Seeding either with the value its declaration already defaults to would be a line no test can kill — what the issue closed#103 over — so they are restructured rather than seeded, and no suppression is added. M21 and M22 in the table below are there because the restructure moved a live refusal's operand: they delete each refusal in turn to show it still fails a test.The generators
Two emitters wrote named returns as string literals — the per-contract released lib and #136's aggregate over them. Both now emit an unnamed clause, a local declaration and a trailing
return suites;, and the aggregate's empty-repo branch emitsreturn new DeploySuite[](0);.src/lib/LibAddressRegistryReleased.sol,src/lib/LibMigrationRegistryReleased.solandsrc/lib/LibReleasedSuites.solare the output offorge script script/Build.solagainst them, so the next build reproduces them rather than reverting this diff.The deploy address moves
AddressRegistry.getis one of the unnamed functions, so the contract's runtime code changes — one byte longer, two jump destinations shifted. The rolling candidate snapshot is regenerated with it:BYTECODE_HASH0xef8355…4ea73→0x6c37a6…10097DEPLOYED_ADDRESS0x25aC2b82915f191dbE64e65BAeDDD68b97b68fe1→0x8cACfbD5d78b6D87080cE0839708ac2dA5461F78src/generated/holds no frozen tag directory, so no released suite and nothing on chain is contradicted by the move.checkCandidatesAnchoredToSourceis what makes the regeneration mandatory rather than optional.Beyond the issue's table
script/Build.solis neithersrc/nor thetest/the issue excludes. Its two named returns are unnamed here rather than left as the only counterexamples to a ruling that says this repo does not name return values.Suite
mainated91bfa, no RPC env*_RPC_URL not found;grep '[FAIL' | grep -vc '_RPC_URL. not found'= 0testjob is greenforge fmt --checkrainixtest,legalandstatic(slither + fmt + single-contract) all greenEvery mutation below runs against that configuration, so the fork-dependent functions in
LibRainDeployare measured rather than skipped. Two earlier runs against rate-limited public endpoints reported failures that were HTTP 429s rather than the mutation; those are re-run and the table counts only runs whose failures are assertions.Mutation pass
One mutation at a time, each deleting a
returnthis diff ADDS or emits, against the full suite with live RPC.tests runis the suite's ownN total testsline, so a run that compiled nothing or filtered to nothing is visible rather than counted as a kill.infra failurescounts RPC transport errors in the run and is 0 everywhere, so no verdict rests on a fork that failed to open.RainDeploySuitesBase.checkedCandidateSuitesreturn candidates;testAllSuitesIsReleasedThenCandidates,testCandidatesPresentAnswersRainDeploySuitesBase.allSuitesreturn suites;testAllSuitesIsReleasedThenCandidates,testDerivationLeavesNoCodeBehindRainDeploySuitesBase.suiteNamesreturn names;testEmptySuiteIsUnknown,testRunRefusesToBroadcastACandidateThatIsNotItsSourceRegistryDeploySuites.candidateSuitesreturn candidates;testEveryCandidateArtifactPathResolves,testEveryCandidateHasASnapshotRainDeployVerifyBase.deriveDeploymentreturn derived;testChainCodeHashMismatchReverts,testChainIgnoresAnUndeployedCandidateRainDeployVerifyBase.deriveDeploymentsreturn derived;testChainCodeHashMismatchReverts,testChainMatrixReachesTheLastSupportedNetworkLibRainDeploy.isStartBlockreturn isStart;testFindDeployBlockZoltuFactory,testIsStartBlockAtDeployBlockLibRainDeploy.findDeployBlockreturn low;testFindDeployBlockExactZoltuBaseDeployBlock,testFindDeployBlockZoltuFactoryLibRainDeploy.deployZoltureturn deployedAddress;testChainCodeHashMismatchReverts,testChainIgnoresAnUndeployedCandidateLibRainDeploy.deployToNetworksreturn expectedAddress;testDeployAndBroadcastHappyPath,testDeployToNetworksEveryDependencyPresentDeploysLibRainDeploy.deployAndBroadcastreturnfrom the tail calltestDeployAndBroadcastHappyPathLibRainDeploySnapshot.frozenSnapshotPathsreturn paths;testCheckReleaseFollowsRecordAcceptsExactlyTheStrictlyGreater,testCheckReleaseFollowsRecordRefusesATagBelowTheNewestReleaseLibRainDeploySnapshot.sortedRecordPathsreturn sorted;testRecordPathsForContractSelectsOneContractInTagOrder,testSortedRecordPathsOrdersTagsAsVersionsLibRainDeploySnapshot.releasedImportBlockreturn imports;testReleasedImportBlockAliasesEveryRecordLibRainDeploySnapshot.newestFrozenTagreturn newest;testCheckReleaseFollowsRecordAcceptsExactlyTheStrictlyGreater,testCheckReleaseFollowsRecordRefusesATagBelowTheNewestReleaseLibRainDeploySnapshot.releasedLibraryBlockreturn suites;testReleasedLibraryBlockDeclaresEveryRecordAddressRegistry.getreturn account;testCandidatesPresentAnswers,testCheckResolvedAddressesChecksEveryReadBuild.generatedContractsreturn contracts;testGeneratedContractsAreExactlyTheDeclaredCandidates,testTheCommittedAggregateIsInDeclarationOrderLibRainDeploySnapshot.newestFrozenTagreturn "";guardtestFreezeCopiesTheRegeneratedRollingSnapshot,testFreezeCutsEveryNamedContractLibRainDeploy.deployToNetworksUnexpectedDeployedAddressrefusaltestDeployToNetworksFactoryReportsOtherAddressRevertsLibRainDeploy.deployToNetworksUnexpectedDeployedCodeHashrefusaltestUnexpectedDeployedCodeHashRevertsBuild.generatedContractNamesreturn names;testGeneratedContractNamesAreTheDeclarationInOrderLibRainDeploySnapshot.aggregateImportBlockreturn imports;testAggregateImportBlockImportsEveryReleasedLib,testTheCommittedAggregateIsWhatTheGeneratorEmitsLibRainDeploySnapshot.aggregateLibraryBlockreturn suites;testAggregateLibraryBlockConcatenatesEveryReleasedLib,testTheCommittedAggregateIsWhatTheGeneratorEmitsLibRainDeploySnapshot.aggregateLibraryBlocknew DeploySuite[](1)for an empty repotestAggregateLibraryBlockDeclaresNothingForNoContractsAddressRegistry.get's mutant (M18) is the one where the source-anchor tests fire for free: any edit to that contract's source breakstestSnapshotMatchesSourcewhatever the edit does. Its kill does not rest on them —testGetReturnsRegistered,testGetOpaqueNames,testResolveRegisteredandtestResolveUnregisteredare among the 24, and each of those reads a binding back rather than a pin.M23 survived the first run and is the reason
testGeneratedContractNamesAreTheDeclarationInOrderis in this PR.generatedContractNames()is the listcutReleasefreezes and the list the aggregate is emitted from, and both reach it only throughforge script, so nothing in the suite could tell it from an empty array. The new test asserts it positionally againstgeneratedContracts()and now kills the mutant.QA
testGeneratedContractNamesAreTheDeclarationInOrder— new, fails on the M23 mutant (320 tests ran, it is the only failure) and passes on this branch.testReleasedLibraryBlockDeclaresEveryRecordandtestAggregateLibraryBlockConcatenatesEveryReleasedLib— existing, expectations updated to the new emitted text; each fails against the pre-change emitter, verified by M17 and M25 which revert exactly that literal.returnthis diff adds or emits, deleted one at a time against the full suite with live RPC — 25 killed, 0 survived, 0 runs with an RPC transport failure. Full table above with the killing test for each.MigrationRegistrypins check.src/unnamed with an explicit return on every path, (b)@returnkept with the name dropped, (c) the generator changed so it stops emitting them, (d) the generated files regenerated from it. All four are covered, including the aggregate generator Move the release version under foundry's [external] section #136 added after the issue was filed and the two named returns inscript/Build.sol.test/is the issue's own exclusion and is untouched apart from the expectations that pin generated text and the one new test.