Skip to content

Releases: dakimura/guff

v0.7.0

Choose a tag to compare

@github-actions github-actions released this 07 Sep 04:34
924e0f5

What's Changed

  • A cancel that never gets called, and a TLS config that skips its own check by @dakimura in #82
  • SA5008 was misreading struct tags before it ever looked at the JSON by @dakimura in #83
  • wsl_v5 was reading three of upstream's exemptions as violations by @dakimura in #84
  • G122 followed the walk callback into closures upstream never enters by @dakimura in #85
  • //lint:file-ignore U1000 stopped at the file; upstream's does not by @dakimura in #86
  • authelia's last three: wsl_v5's err check and a taint that never died by @dakimura in #87
  • Two modernizers shipped their messages bare, and the case that would have caught it by @dakimura in #88
  • minmax had one of upstream's two patterns, and the wrong idea of "equal" by @dakimura in #89
  • forcetypeassert pointed at the :=, and its whole fixture was six lines by @dakimura in #90
  • guff type-checked syncthing's lib/model as ill-typed, and no gate could say so by @dakimura in #91
  • SA4016 had one of upstream's two branches, and ^ rendered as ? by @dakimura in #92
  • gosec's taint is four rules on one engine, and its call graph is not every function by @dakimura in #93
  • T[A, B](v) is a conversion, and *p is addressable whatever p was by @dakimura in #94
  • Inference's "untyped" argument was tested for being a constant by @dakimura in #95
  • An untyped constant does not convert to an interface by being representable as one by @dakimura in #96
  • docs: the embedded-generic-instance failure narrows to one line of context by @dakimura in #97
  • A generic instance one struct down still needs its methods expanded by @dakimura in #98
  • package p_test imports the test variant of p, not the production one by @dakimura in #99
  • A typed constant is rounded after every operation, not where it is used by @dakimura in #100
  • errchkjson never matched (*encoding/json.Encoder).Encode by @dakimura in #101
  • gosec G705: the sink is a method on an interface, and the writer decides by @dakimura in #102
  • byte and rune are their own Basic values, not spellings of uint8 and int32 by @dakimura in #103
  • Recovering a doc comment's position by line number threw away the column by @dakimura in #104
  • godot: three defects — a missing collector, a wrong line, and a dropped blank line by @dakimura in #105
  • Three linters reporting the right finding the wrong way by @dakimura in #106
  • thanos no longer has an ill-typed package, so stop allowing it one by @dakimura in #107
  • The seed compiled one copy of a package and ordered it by another's imports by @dakimura in #108
  • 84 of 116 linters had never had a column or a severity checked by @dakimura in #109
  • Widening 18 thin fixtures found 7 defects, two of them in the type checker by @dakimura in #110
  • Widening 20 more fixtures: 4 defects, and one a unit test was demanding by @dakimura in #111
  • 33 more fixtures widened: 2 defects, both invisible at one finding by @dakimura in #112
  • Counting the Info.Defs holes instead of waiting to trip over them by @dakimura in #113
  • Seven private expr_string, three upstream renderers, seven defects by @dakimura in #114
  • The last three expr_string, and a fourth upstream renderer by @dakimura in #115
  • --isolate --linter X was overwriting the committed 116-target snapshot by @dakimura in #116
  • --fix was broken in three places, and fixing one moved nothing by @dakimura in #117
  • A tier that compares what --fix writes, and the one case guff wrote too much by @dakimura in #118
  • AddImport is one function ten checkers call, and File.imports was a different list by @dakimura in #119
  • Three --fix defects in modernize, and the two the syntax error was hiding by @dakimura in #120
  • importas renamed the alias and not the code that used it by @dakimura in #121
  • QF1012 checked *T and passed T, and one hunk it leaves is upstream's bug by @dakimura in #122
  • DeleteStmt and DeleteUnusedVars, and no guff-side broken tree left by @dakimura in #123
  • revive's ReplacementLine, and a slot for "upstream is wrong" by @dakimura in #124
  • govet's assign and unreachable, and a unit test that pinned the wrong answer by @dakimura in #125
  • govet's timeformat and sigchanyzer by @dakimura in #126
  • govet's stringintconv and composites by @dakimura in #127
  • govet's hostport by @dakimura in #128
  • godot's missing period, and an approximation not taken by @dakimura in #129
  • whitespace's blank lines, and gocritic's first three fixes by @dakimura in #130
  • staticcheck's S1002, S1004 and S1012, and the renderer that answers a different question by @dakimura in #131
  • staticcheck's S1003 and S1021, and the case that closes is not the one worked on by @dakimura in #132
  • staticcheck's S1016, S1028 and S1030, and two names for one type by @dakimura in #133
  • Five more S checks, and a fixer that wrote nothing when it should have written a formatted file by @dakimura in #134
  • Seven more S checks, and the three shapes a one-shape fixture was hiding by @dakimura in #135
  • S1001, and the three defects a seven-shape fixture found in one run by @dakimura in #136
  • goheader: the largest pending gap was two missing pieces by @dakimura in #137
  • testifylint: seven checkers that rewrite the name and the arguments together by @dakimura in #138
  • testifylint: the other nine checkers, and the case closes by @dakimura in #139
  • dupword's comments, an upstream bug reproduced, and one half left undone by @dakimura in #140
  • nlreturn and protogetter: "source not obtainable" was one command from false by @dakimura in #141
  • Write down why nolint's fixes are a design decision, not a port by @dakimura in #142
  • ginkgolinter: the message already carried the fix by @dakimura in #143
  • A path boundary in the fixer: an edit must land in its own issue's file by @dakimura in #144
  • SA1004: emitting more so that less is written by @dakimura in #145
  • SA4013: two rules meeting in one hunk by @dakimura in #146
  • SA4026 and SA1013: writing the rewrite that does not compile by @dakimura in #147
  • SA1006 and SA6005, and three cases closing at once by @dakimura in #148
  • staticcheck-sa closes: SA4029, SA1008, SA9004 by @dakimura in #149
  • gocritic's extras.go: 48 lines of diff, none of them written by a linter by @dakimura in #150
  • parens over-wrote by one hunk, and the ledger's refusal was one level too shallow by @dakimura in #151
  • QF1005: seven defects behind four differing lines, one of them deleting a call by @dakimura in #152
  • Add a Hindi README and put it in every language bar by @dakimura in #153
  • dupword closes: a linter did less because of where a file sat in the crate graph by @dakimura in #154
  • nolintlint gets its fixes: three cases close, and #142's dilemma was a false one by @dakimura in #155
  • gocritic: 13 ruleguard suggestions, and the last 9 lines belong to another subsystem by @dakimura in #156
  • wsl and wsl-v5 close, and the thin fixtures were hiding a reporting bug by @dakimura in #157
  • The Fixer belongs inside the pipeline, before dedup and the caps by @dakimura in #158
  • nakedret and embeddedstructfieldcheck: the inputs were already there by @dakimura in https://github.com/dakimura/guff/pull...
Read more

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 21 Aug 01:20
9605ddf

What's Changed

  • Make the gates binding: stop cancelling the tier that dates regressions by @dakimura in #16
  • Bail to go list when export data is asked for, instead of returning none by @dakimura in #18
  • Lint the files git has not been told about yet by @dakimura in #19
  • Report the integer conversions nothing has bounded (gosec G115) by @dakimura in #20
  • Spare the parameters a config told revive to spare (allowRegex) by @dakimura in #21
  • Peel the pointer off an elided composite literal (modernize atomictypes) by @dakimura in #22
  • Leave the switch that cannot become an if-then alone (unnecessary-stmt) by @dakimura in #23
  • Match gosec's call rules by syntax, the way gosec does (G404) by @dakimura in #24
  • Keep the excluded files a test variant still has (modernize atomictypes) by @dakimura in #25
  • Give testifylint a ground-truth gate, and fix the four things it found by @dakimura in #26
  • Let errors.As take a target of any (govet errorsas) by @dakimura in #27
  • Read protogetter's rules off protogetter, aliases and all by @dakimura in #28
  • Read uses the error off the call arguments, as nilerr does by @dakimura in #29
  • Print the type parameters a generic receiver has (unused) by @dakimura in #30
  • Follow the rename through the join, not just into the branches (SA5011) by @dakimura in #31
  • Stay out of a kubebuilder package (modernize omitzero) by @dakimura in #32
  • A test file is a filename, not a package name (revive IsTest) by @dakimura in #33
  • A nil is only a swallowed error where an error is returned (nilerr) by @dakimura in #34
  • Format like the gofumpt golangci-lint pins, and stop inventing a severity by @dakimura in #35
  • Name the path in a dupl message, and stop dereferencing an interface (gocritic underef) by @dakimura in #36
  • Honour revive's own disable directives by @dakimura in #37
  • Compare signatures, not arena ids (SA5010) by @dakimura in #39
  • Two places upstream is deliberately blind, and guff was not (composites, nilnil) by @dakimura in #38
  • Four gates gitea found missing (gocritic badCall/mapKey, revive identical-*/if-return) by @dakimura in #40
  • Three more of gitea's 33 (bodyclose, testifylint bool-compare, govet structtag) by @dakimura in #41
  • Compare the printed source, not an approximation of it (SA4000) by @dakimura in #42
  • Only a map read counts as a map read (SA6001) by @dakimura in #44
  • Two gates jaeger found missing (gocritic stringXbytes, revive max-control-nesting) by @dakimura in #43
  • httpresponse: the membership test is the signature, not the package by @dakimura in #45
  • contextcheck: a capturing closure had nowhere to report, and a bare one had no business reporting by @dakimura in #46
  • An import stub is not a package: fact producers were scheduled on metadata by @dakimura in #47
  • A labelled break jumped to the top of the loop it was leaving by @dakimura in #48
  • Two gocritic false positives argo-cd found (walker scope, regexpSimplify escapes) by @dakimura in #49
  • Two argo-cd false positives (thelper synctest, formatters crossing a module boundary) by @dakimura in #50
  • G122: the path stays the path after filepath.Clean by @dakimura in #51
  • usetesting: the function that owns the closure owns the finding by @dakimura in #52
  • Session log: the 2026-08-20 hunt pass, and what the next one should start with by @dakimura in #53
  • contextcheck: a callee built for an imported package has no SSA package to ask by @dakimura in #54
  • protogetter: a getter that returns a value cannot fill a pointer field by @dakimura in #55
  • spancheck: report on the assignment and on the return, and only when there is one by @dakimura in #56
  • unused: a generic interface draws no edge to the methods that implement it by @dakimura in #57
  • bodyclose: a response is one that came out of a call by @dakimura in #58
  • prealloc: the capacity is printed the way go/printer prints it by @dakimura in #59
  • gocritic: a block comment is its own group by @dakimura in #60
  • recvcheck: 2.12.2 excludes the encoding half, not the decoding half by @dakimura in #61
  • Session log, second half: eight more, and the version the comparison is against by @dakimura in #62
  • Record the bodyclose hypothesis that fit three shapes and broke on a repo by @dakimura in #63
  • gosec G202: SQL string concatenation by @dakimura in #64
  • One shared body for ST1023 and QF1011, and the right-hand side seen alone by @dakimura in #65
  • An index binds to the position that absorbs it (govet printf) by @dakimura in #66
  • A deferred call can assign to a named result, so don't lift it by @dakimura in #67
  • The receiver decides whether a call aborts (SA5011) by @dakimura in #68
  • return f() returns f's results, not f's tuple by @dakimura in #69
  • //lint:ignore U1000 marks an object used by @dakimura in #70
  • Three loops: where rangeint points, what it refuses, and what range costs by @dakimura in #71
  • Three dapr false positives: an else, a getter, and a stack index by @dakimura in #72
  • The package initializer is not a source function (gosec) by @dakimura in #73
  • G101's gate is a dictionary, not a character count (port zxcvbn) by @dakimura in #74
  • Three ways a response goes unclosed, and two that are not calls at all (bodyclose) by @dakimura in #75
  • unparam's other three families, and the results a range-over-func return was dropping by @dakimura in #76
  • A response stored into a captured variable, and the closure that closes it by @dakimura in #77
  • os-setenv is on by golangci's defaults, and a conversion is pure by @dakimura in #78
  • cargo test that never starts is a file count, not a slow test by @dakimura in #79
  • unused colours a graph from its roots, and go1.26's new takes a value by @dakimura in #80
  • Release 0.6.0: 64 commits of upstream parity, re-measured by @dakimura in #81

Full Changelog: v0.5.0...v0.6.0

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 17 Aug 01:39
aef610b

A performance release. Same linters, same findings, same config — less time and a third less memory.

Faster and lighter

Measured on prometheus ./... (Darwin arm64, 10 core), v0.4.1 → v0.5.0:

v0.4.1 v0.5.0
cold wall 2.33s 1.98s −15%
warm wall (one file changed) 1.51s 1.36s −10%
peak memory 2517 MiB 1717 MiB −32%
-j 1 wall 5.48s 4.90s −11%

Most of it came from one thing. Three enums that sit in million-element arrays were each sized by one or two oversized variants — a Rust enum costs what its largest variant costs, so a 24-byte expression was paying for a 192-byte one:

before after
Expr 192 B 80 B
Stmt 656 B 320 B
TypeData 112 B 72 B
ObjectData 104 B 64 B

Boxing the outliers took every AST node, type and object in the process down with them. Details and the measurements that sized each one: docs/PERF_TASKS_V9.md.

vs golangci-lint

Real repositories, their own .golangci.yml, cold cache, golangci-lint 2.12.2:

Repository golangci-lint guff Speedup
grafana 290.4s 17.8s 16×
consul 39.4s 4.7s
helm 17.4s 1.3s 13×
k9s 14.3s 2.1s
caddy 8.7s 0.91s 10×
containerd 4.1s 0.40s 10×
gin 3.9s 0.37s 11×
cobra 1.4s 0.22s

Reproduce with ./benchmarks/run.sh --oss --tier pr,nightly. Full detail: SCOREBOARD.md.

grafana's golangci-lint number is 290.4s here against 394.8s in the v0.4.1 README, so the ratio reads 16× where it used to read 17× — a different checkout, a different golangci-lint, a different day. Both sides of every row are this run's measurements.

No behaviour changes

Findings are byte-identical to v0.4.1 across the regression corpus, at -j 1 and -j N, with node-kind masks on and off. 114/114 golangci-lint v2 linters, compat golden 81/81, 3119 tests.

Also in this release

Claude Code is now called out alongside Cursor in the agent docs — put the lint block in CLAUDE.md and it applies every session. See docs/AGENTS.md.


What's Changed

  • Persist guff's analysis cache across CI runs by @dakimura in #1
  • Halve the analyze phase: an inspector that skips the 97% it used to read, and a mutex ten workers queued on by @dakimura in #2
  • The compat gate has never passed in CI: the runner's Go was two minors behind the baselines by @dakimura in #3
  • Two fixtures were linux-only, so the golden never compared them by @dakimura in #6
  • Comment linters were paying for object resolution none of them read by @dakimura in #5
  • The shape ledger was a laptop measurement the runner could not reproduce by @dakimura in #4
  • One question asked twenty-five times, and a speculation that had never once hit by @dakimura in #7
  • Every package's results stayed alive until every other package caught up by @dakimura in #8
  • -j 1 kept every package's results alive the way the wavefront used to by @dakimura in #9
  • Box the two Expr variants that were sizing every AST node (and finish PERF_TASKS_V8) by @dakimura in #10
  • Box the two TypeData variants that were sizing every arena slot by @dakimura in #11
  • Box the ObjectData variant that was sizing every object slot by @dakimura in #12
  • Correct the round's closing numbers, measured on merged main by @dakimura in #13
  • Re-lock both regress baselines on the round's numbers by @dakimura in #14
  • Release 0.5.0: re-measure the OSS benchmarks and refresh the docs by @dakimura in #15

New Contributors

Full Changelog: v0.4.1...v0.5.0

v0.4.1

Choose a tag to compare

@github-actions github-actions released this 07 Aug 01:56

Full Changelog: v0.4.0...v0.4.1

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 06 Aug 23:21

Full Changelog: v0.3.0...v0.4.0

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 06 Aug 23:26

Full Changelog: v0.2.0...v0.3.0

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 06 Aug 23:23

Full Changelog: v0.1.0...v0.2.0

v0.1.0

Choose a tag to compare

@github-actions github-actions released this 04 Aug 02:21