-
-
Notifications
You must be signed in to change notification settings - Fork 112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
non-idempotent runs #132
Comments
Previously, RequestWith would ignore any return if an error was encountered. Now, RequestWith returns any value present even if there is an error -- basically a typed pass through. We also pipe the output through gofumpt twice now to ensure we get desired output: ref: mvdan/gofumpt#132
Thanks for raising this issue. I admit I've only been testing gofumpt on inputs which are reasonably gofmt-like. If your entire input is crammed into a single line, or has almost no whitespace between tokens, it's not surprising that gofumpt isn't idempotent straight away. We already do enforce gofmt's style, because under the hood this is a gofmt fork. However, gofumpt's own extra rules work at the level of the input go/ast, so our ability to insert newlines or move code around is relatively limited. I can go into more detail there if it sounds interesting. One option would be for gofumpt to first run gofmt on the entire input, and afterwards enforce its extra rules as a second pass. This could possibly make gofumpt somewhat slower, but also fix a lot of these idempotency edge cases. Another option is to try to fix all of these idempotency edge cases one by one. I can certainly try, but I worry that some would be extremely hard to fix, and it would be hard to tell when we're truly "done". |
I see what you did in your generator to run |
Makes sense. I don't fully understand how a repeated run is able to insert an extra newline, that would be interesting to know. I wonder if there's a more general way to ensure idempotence -- it's difficult for me to understand from an I-currently-know-nothing perspective what additional behavior triggers based on differences of input (in this case, an extra newline if things are mostly formatted). |
@mvdan I encountered another case where the output of gofumpt is not gofumpt-safe. Example:
Running gofumpt once gives:
Running it twice gives:
This came up when I ran the output of a protobuf code generator through gofumpt and found that I needed to run it twice before it stopped changing. Should I file a separate bug? To me, grouping and sorting the imports is really something that gofumpt ought to be able to do in a single pass. (In general I do think code formatters ought to always reach a fixed point after a single iteration, but I take from your previous comments that there are practical reasons about the way gofumpt is written which makes that difficult.) |
Using the same bug seems fine; thanks for reporting! I do intend to fix non-idempotency bugs, even if some might be tricky or require slowing down the tool a bit. |
The last example is very similar to #225. In both edge cases, moving imports around results in bad formatting. I just can't fix these with the current mechanism of modifying go/ast; I will need a different approach entirely. |
echo "package main; func (v *OffsetForLeaderEpochResponse) Throttle() (int32, bool) { return v.ThrottleMillis, v.Version >= 0 }; func (v *OffsetForLeaderEpochResponse) RequestKind() Request { return &OffsetForLeaderEpochRequest{Version: v.Version} }" | gofumpt
output:
If we pipe the expression through
gofumpt
twice, we get an additional newline betweenThrottle
andRequestKind
:echo "package main; func (v *OffsetForLeaderEpochResponse) Throttle() (int32, bool) { return v.ThrottleMillis, v.Version >= 0 }; func (v *OffsetForLeaderEpochResponse) RequestKind() Request { return &OffsetForLeaderEpochRequest{Version: v.Version} }" | gofumpt | gofumpt
output:
The text was updated successfully, but these errors were encountered: