Skip to content

Commit 88a300b

Browse files
committed
require Go 1.22 and use go/version for Options.LangVersion
This is a Go API breaking change; where existing users could use strings such as "1.16" or "v1.16", they should now use "go1.16". Note that the "go" prefix is mandatory, following go/version. I considered a softer landing to manipulate the older string formats to conform to the new format, but given how go/version is strict and Go versions are now clearly defined differently from SemVer, it seems best to draw a line and follow go/version's notion of validity. Supporting "go1.16" alongside "v1.16" could also cause confusion in the long run, as Go versions are subtly different from SemVer in terms of validity and order. Requiring the "go" prefix avoids footguns. Most gofumpt CLI users shouldn't be using the -lang CLI flag directly, as by default it grabs its value from the go directive in go.mod. Hence, most gofumpt CLI users should not be affected by this breakage. Go API users, such as tool or IDE integration authors, are more likely to get broken as the API falls back to assuming a conservative Go 1.0. Fixing the breakage is rather simple, as shown by the changes below. While here, suggest `go list -m` rather than `go mod edit -json`, as the latter requires using `jq` to obtain just one field. We still mention the latter command as an alternative.
1 parent 41a9946 commit 88a300b

File tree

8 files changed

+40
-46
lines changed

8 files changed

+40
-46
lines changed

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ package p
651651
$ gofumpt f.go
652652
package p
653653
654-
//gofumpt:diagnose v0.1.1-0.20211103104632-bdfa3b02e50a -lang=v1.16
654+
//gofumpt:diagnose v0.1.1-0.20211103104632-bdfa3b02e50a -lang=go1.16
655655
```
656656

657657
### License

Diff for: format/format.go

+21-27
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"go/ast"
1313
"go/parser"
1414
"go/token"
15+
goversion "go/version"
1516
"os"
1617
"reflect"
1718
"regexp"
@@ -23,7 +24,6 @@ import (
2324
"unicode/utf8"
2425

2526
"github.com/google/go-cmp/cmp"
26-
"golang.org/x/mod/semver"
2727
"golang.org/x/tools/go/ast/astutil"
2828

2929
"mvdan.cc/gofumpt/internal/govendor/go/format"
@@ -32,26 +32,25 @@ import (
3232

3333
// Options is the set of formatting options which affect gofumpt.
3434
type Options struct {
35-
// TODO: link to the go/version docs once Go 1.22 is out.
36-
// The old semver docs said:
37-
//
38-
// LangVersion is treated as a semantic version, which may start with a "v"
39-
// prefix. Like Go versions, it may also be incomplete; "1.14" is equivalent
40-
// to "1.14.0". When empty, it is equivalent to "v1", to not use language
41-
// features which could break programs.
42-
4335
// LangVersion is the Go version a piece of code is written in.
4436
// The version is used to decide whether to apply formatting
4537
// rules which require new language features.
46-
// When inside a Go module, LangVersion should typically be:
38+
// When empty, a default of go1 is assumed.
39+
// Otherwise, the version must satisfy [go/version.IsValid].
40+
//
41+
// When formatting a Go module, LangVersion should typically be
4742
//
48-
// go mod edit -json | jq -r '.Go'
43+
// go list -m -f {{.GoVersion}}
44+
//
45+
// with a "go" prefix, or the equivalent from `go mod edit -json`.
4946
LangVersion string
5047

5148
// ModulePath corresponds to the Go module path which contains the source
52-
// code being formatted. When inside a Go module, ModulePath should be:
49+
// code being formatted. When formatting a Go module, ModulePath should be
50+
//
51+
// go list -m -f {{.Path}}
5352
//
54-
// go mod edit -json | jq -r '.Module.Path'
53+
// or the equivalent from `go mod edit -json`.
5554
//
5655
// ModulePath is used for formatting decisions like what import paths are
5756
// considered to be not part of the standard library. When empty, the source
@@ -86,26 +85,21 @@ func Source(src []byte, opts Options) ([]byte, error) {
8685
return buf.Bytes(), nil
8786
}
8887

89-
var rxGoVersionMajorMinor = regexp.MustCompile(`^(v|go)?([1-9]+)\.([0-9]+)`)
90-
9188
// File modifies a file and fset in place to follow gofumpt's format. The
9289
// changes might include manipulating adding or removing newlines in fset,
9390
// modifying the position of nodes, or modifying literal values.
9491
func File(fset *token.FileSet, file *ast.File, opts Options) {
9592
simplify(file)
9693

97-
// TODO: replace this hacky mess with go/version once we can rely on Go 1.22,
98-
// as well as replacing our uses of the semver package.
99-
// In particular, we likely want to allow any of 1.21, 1.21.2, or go1.21rc3,
100-
// but we can rely on go/version.Lang to validate and normalize.
10194
if opts.LangVersion == "" {
102-
opts.LangVersion = "v1.0"
103-
}
104-
m := rxGoVersionMajorMinor.FindStringSubmatch(opts.LangVersion)
105-
if m == nil {
106-
panic(fmt.Sprintf("invalid Go version: %q", opts.LangVersion))
95+
opts.LangVersion = "go1"
96+
} else {
97+
lang := goversion.Lang(opts.LangVersion)
98+
if lang == "" {
99+
panic(fmt.Sprintf("invalid Go version: %q", opts.LangVersion))
100+
}
101+
opts.LangVersion = lang
107102
}
108-
opts.LangVersion = "v" + m[2] + "." + m[3]
109103
f := &fumpter{
110104
file: fset.File(file.Pos()),
111105
fset: fset,
@@ -680,8 +674,8 @@ func (f *fumpter) applyPre(c *astutil.Cursor) {
680674
}
681675

682676
case *ast.BasicLit:
683-
// Octal number literals were introduced in 1.13.
684-
if semver.Compare(f.LangVersion, "v1.13") >= 0 {
677+
// Octal number literals were introduced in Go 1.13.
678+
if goversion.Compare(f.LangVersion, "go1.13") >= 0 {
685679
if node.Kind == token.INT && rxOctalInteger.MatchString(node.Value) {
686680
node.Value = "0o" + node.Value[1:]
687681
c.Replace(node)

Diff for: format/fuzz_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ func FuzzFormat(f *testing.F) {
2626
for _, file := range archive.Files {
2727
f.Logf("adding %s from %s", file.Name, path)
2828
if strings.HasSuffix(file.Name, ".go") || strings.Contains(file.Name, ".go.") {
29-
f.Add(string(file.Data), int8(18), false) // -lang=1.18
30-
f.Add(string(file.Data), int8(1), false) // -lang=1.1
31-
f.Add(string(file.Data), int8(18), true) // -lang=1.18 -extra
29+
f.Add(string(file.Data), int8(18), false) // -lang=go1.18
30+
f.Add(string(file.Data), int8(1), false) // -lang=go1.1
31+
f.Add(string(file.Data), int8(18), true) // -lang=go1.18 -extra
3232
}
3333
}
3434
}
@@ -40,7 +40,7 @@ func FuzzFormat(f *testing.F) {
4040
// TODO: also fuzz Options.ModulePath
4141
opts := Options{ExtraRules: extraRules}
4242
if majorVersion >= 0 {
43-
opts.LangVersion = fmt.Sprintf("1.%d", majorVersion)
43+
opts.LangVersion = fmt.Sprintf("go1.%d", majorVersion)
4444
}
4545

4646
orig := []byte(src)

Diff for: go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module mvdan.cc/gofumpt
22

3-
go 1.21
3+
go 1.22
44

55
require (
66
github.com/go-quicktest/qt v1.101.0

Diff for: gofmt.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func usage() {
9696
-w write result to (source) file instead of stdout
9797
-extra enable extra rules which should be vetted by a human
9898
99-
-lang str target Go version in the form "1.X" (default from go.mod)
99+
-lang str target Go version in the form "go1.X" (default from go.mod)
100100
-modpath str Go module path containing the source file (default from go.mod)
101101
`)
102102
}
@@ -298,7 +298,7 @@ func processFile(filename string, info fs.FileInfo, in io.Reader, r *reporter, e
298298
if ok && mod != nil {
299299
mod := mod.(*module)
300300
if lang == "" {
301-
lang = mod.Go
301+
lang = "go" + mod.Go
302302
}
303303
if modpath == "" {
304304
modpath = mod.Module.Path

Diff for: testdata/script/diagnose.txtar

+7-7
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ cmp stdout foo.go.golden
1414
exec gofumpt -extra foo.go
1515
cmp stdout foo.go.golden-extra
1616

17-
exec gofumpt -lang=1.0 foo.go
17+
exec gofumpt -lang=go1 foo.go
1818
cmp stdout foo.go.golden-lang
1919

2020
exec gofumpt -d nochange.go
@@ -79,24 +79,24 @@ package p
7979
-- foo.go.golden --
8080
package p
8181

82-
//gofumpt:diagnose version: v0.0.0-20220727155840-8dda8068d9f3 (go1.18.29) flags: -lang=v1.16 -modpath=test
82+
//gofumpt:diagnose version: v0.0.0-20220727155840-8dda8068d9f3 (go1.18.29) flags: -lang=go1.16 -modpath=test
8383
-- foo.go.golden-devel --
8484
package p
8585

86-
//gofumpt:diagnose version: (devel) (go1.18.29) flags: -lang=v1.16 -modpath=test
86+
//gofumpt:diagnose version: (devel) (go1.18.29) flags: -lang=go1.16 -modpath=test
8787
-- foo.go.golden-extra --
8888
package p
8989

90-
//gofumpt:diagnose version: v0.0.0-20220727155840-8dda8068d9f3 (go1.18.29) flags: -lang=v1.16 -modpath=test -extra
90+
//gofumpt:diagnose version: v0.0.0-20220727155840-8dda8068d9f3 (go1.18.29) flags: -lang=go1.16 -modpath=test -extra
9191
-- foo.go.golden-lang --
9292
package p
9393

94-
//gofumpt:diagnose version: v0.0.0-20220727155840-8dda8068d9f3 (go1.18.29) flags: -lang=v1.0 -modpath=test
94+
//gofumpt:diagnose version: v0.0.0-20220727155840-8dda8068d9f3 (go1.18.29) flags: -lang=go1 -modpath=test
9595
-- foo.go.golden-released --
9696
package p
9797

98-
//gofumpt:diagnose v0.3.2-0.20220627183521-8dda8068d9f3 -lang=v1.16 -modpath=test
98+
//gofumpt:diagnose v0.3.2-0.20220627183521-8dda8068d9f3 -lang=go1.16 -modpath=test
9999
-- foo.go.golden-external --
100100
package p
101101

102-
//gofumpt:diagnose (devel) -lang=v1.16 -modpath=
102+
//gofumpt:diagnose (devel) -lang=go1.16 -modpath=

Diff for: testdata/script/gomod.txtar

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Test various edge cases with go.mod files.
22

33
exec gofumpt toolchain-stable/a.go
4-
stdout '//gofumpt:diagnose.* -lang=v1.21'
4+
stdout '//gofumpt:diagnose.* -lang=go1.21'
55

66
exec gofumpt toolchain-unstable/a.go
7-
stdout '//gofumpt:diagnose.* -lang=v1.21'
7+
stdout '//gofumpt:diagnose.* -lang=go1.21'
88

99
-- toolchain-stable/go.mod --
1010
module a

Diff for: testdata/script/octal-literals.txtar

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ exec gofumpt -l .
33
! stdout .
44

55
# We can give an explicitly newer version.
6-
exec gofumpt -lang=1.13 -l .
6+
exec gofumpt -lang=go1.13 -l .
77
stdout -count=1 'foo\.go'
88
stdout -count=1 'nested[/\\]nested\.go'
99

@@ -20,7 +20,7 @@ exec gofumpt -d foo.go.golden
2020
! stdout .
2121

2222
# We can give an explicitly older version, too
23-
exec gofumpt -lang=1.0 -l .
23+
exec gofumpt -lang=go1.0 -l .
2424
! stdout .
2525

2626
-- go.mod --

0 commit comments

Comments
 (0)