build: cap function size, complexity and method surface - #226
Conversation
0ac57f8 to
1eead21
Compare
38c945a to
73f7a6b
Compare
1eead21 to
53cd85d
Compare
73f7a6b to
1632cd0
Compare
53cd85d to
627f6dc
Compare
1632cd0 to
10a5b23
Compare
|
Force-pushed after a deeper review pass. Splitting the reflect walk moved its cycle guard into a struct field, and nothing held that guard. Removing The rest of the PR was re-probed rather than re-read: |
627f6dc to
233cc84
Compare
10a5b23 to
2ccc7cd
Compare
233cc84 to
279cb9a
Compare
2ccc7cd to
a7adffa
Compare
279cb9a to
e5a931d
Compare
a7adffa to
6dbe9a4
Compare
e5a931d to
0670aa0
Compare
6dbe9a4 to
3c143ce
Compare
0670aa0 to
3d80f64
Compare
3c143ce to
0fdb553
Compare
3d80f64 to
aebda3e
Compare
0fdb553 to
f73c29c
Compare
aebda3e to
a072c1f
Compare
f73c29c to
860239c
Compare
a072c1f to
f71cddb
Compare
860239c to
dfdfdd6
Compare
Closes #83. Closes #178. Three shapes produced the god object, and none of them was measured while it was growing. This enforces all three. funlen adopts the styleguide's 70-line cap, which until now was declared and enforced by nothing. gocognit caps nesting-weighted complexity, which is the reason the line cap is 70 rather than 200. Both skip test files: a table-driven test is long because its table is long, and its table is data — splitting one to satisfy a size cap moves cases away from the assertion that reads them. The method cap is the one that would have caught the actual failure. No function in that package ever came near 70 lines; the surface that grew was the type's, from one method to 159, and no rule in force measured type surface at all. An AST rule in internal/archtest now caps methods per type under compilers/ at 20, against a finished tree whose widest type carries 14. gocognit's first finding was a real one: irverify's reflect walk scored 28, twice anything else in the repo. Adopting a cap above it would have encoded exactly the shape the cap exists to prevent, so the walk is split into a small value carrying the walk's state and three methods — visit, descend, and the per-kind children. Behaviour is unchanged; the tree's worst is now 14, and the cap is set at 20. Both caps are proven by planting, not by reading: a 75-line function and a type given a twenty-first method each fail, and the counter's own tests plant a package past the cap, two same-named types in different packages, and all three receiver spellings of one type. Splitting the walk moved its cycle guard into a struct field, and nothing held that guard: no document in the corpus shares a pointer, because a compiled Document is a flat registry referenced by ID, so removing the guard changed nothing any test observed. A planted document that does share one holds it — visiting once is also what makes the walk's paths deterministic, which invariant 7 rests on. What the method cap measures is now stated and pinned: methods declared on a type, not the type's method set. That is the dimension the failure grew in — 159 methods written across 11 files, one at a time — and it is what an AST rule can measure without a type checker. It also means a type can stay under the cap by embedding, which is the refactoring the cap exists to prompt rather than a way around it; leaving that unsaid would have let the first reader assume otherwise.
dfdfdd6 to
d943957
Compare
|
Rebased onto the new The method cap counts declared methods, not the method set, and nothing said so. A type declaring 15 methods and embedding another with 15 has a surface of 30 and passes. I planted exactly that to confirm it. That is the right measure, not a hole, and the const now says why: the dimension the failure actually grew in was declarations — 159 of them, written across 11 files, one at a time — and it is what an AST rule can measure without standing up a type checker, which is what #178 asked for. It also means the cap can be satisfied by embedding, and that is the refactoring it exists to prompt rather than a way around it: two types each under the cap, each with a coherent set, is the outcome you want. A type reaching for embedding to duck the number is visible in review in a way that one more method on one more struct never was. There is a planted case pinning the behaviour so the choice cannot change silently. What was already sound, probed rather than assumed:
Unrelated, filed on #59: the coverage gate reported two different statement counts for the same commit while I was running it repeatedly across this stack — |
Closes #83. Closes #178. Stacked on #225 → #223 → #222 → #221 → #220 → #219 → #218.
Summary
Three shapes produced the god object, and none of them was measured while it was growing. This enforces all three, now that the restructuring has finished and the caps can be calibrated against the shape they are meant to hold rather than the one that failed.
funlenadopts the styleguide's 70-line cap, which until now was declared inCLAUDE.mdand enforced by nothing.gocognitcaps nesting-weighted complexity — the reason the line cap is 70 and not 200. Both skip test files: a table-driven test is long because its table is long, and the table is data; splitting one to satisfy a size cap moves cases away from the assertion that reads them, which is the opposite of what the cap is for.The method cap is the one that would have caught the actual failure. No function in
compilers/openapiever came near 70 lines — #178 measured that when it was filed. What grew was type surface, from one method to 159, and nothing measured type surface. A new AST rule ininternal/archtestcaps methods per type undercompilers/at 20, against a finished tree whose widest type iscompile.Typesat 14.gocognit's first finding was a real one, and I took it rather than tuning it away.irverify's reflect walk scored 28 — twice anything else in the repo, the next being 14. Setting the bar at 30 would have made the tree pass while encoding exactly the shape the cap exists to prevent, which is the failure mode #178 names in its own sequencing note. So the walk is split: a smallvalueWalkvalue carrying the seen-set, the visitor and the truncation flag, withwalk,descendandchildrenas methods. Behaviour is unchanged; the tree's worst is now 14 and the cap sits at 20.Test plan
gofmt,go vet,golangci-lint run(0 issues),go build ./...,./scripts/check-coverage.sh— all pass. Statement count moves 4188 → 4185: the three closure assignments in the reflect walk became method declarations.mainover all 163 fixtures, and the whole suite passes, which is what holds the walk split to "behaviour unchanged".funlenat74 > 70. Givinglowering.Ctxa twenty-first method fails the new rule withlowering.Ctx has 21 methods. Both restored, both green again._test.gofile that must not count.requires a non-empty sweep, so a walk that stopped reachingcompilers/fails rather than passing vacuously.Notes
statementsis left off infunlen. Two limits on the same axis means the tighter one is the rule and the other is noise.