Skip to content

Commit

Permalink
format: support Go versions from newer go.mod files
Browse files Browse the repository at this point in the history
The 'go' directive in go.mod files can now contain strings such as
"go1.22rc1" or "go1.21.1", and our old semver logic didn't support
some of those.

Upstream is providing new API via go/version in Go 1.22,
but until that is out, we implement our own workaround to avoid panics.

Fixes #280.
  • Loading branch information
mvdan committed Dec 27, 2023
1 parent 4ac1be2 commit 13743a4
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 14 deletions.
31 changes: 20 additions & 11 deletions format/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,20 @@ import (

// Options is the set of formatting options which affect gofumpt.
type Options struct {
// LangVersion corresponds to the Go language version a piece of code is
// written in. The version is used to decide whether to apply formatting
// rules which require new language features. When inside a Go module,
// LangVersion should be:
//
// go mod edit -json | jq -r '.Go'
// TODO: link to the go/version docs once Go 1.22 is out.
// The old semver docs said:
//
// LangVersion is treated as a semantic version, which may start with a "v"
// prefix. Like Go versions, it may also be incomplete; "1.14" is equivalent
// to "1.14.0". When empty, it is equivalent to "v1", to not use language
// features which could break programs.

// LangVersion is the Go version a piece of code is written in.
// The version is used to decide whether to apply formatting
// rules which require new language features.
// When inside a Go module, LangVersion should typically be:
//
// go mod edit -json | jq -r '.Go'
LangVersion string

// ModulePath corresponds to the Go module path which contains the source
Expand Down Expand Up @@ -82,20 +85,26 @@ func Source(src []byte, opts Options) ([]byte, error) {
return buf.Bytes(), nil
}

var rxGoVersionMajorMinor = regexp.MustCompile(`^(v|go)?([1-9]+)\.([0-9]+)`)

// File modifies a file and fset in place to follow gofumpt's format. The
// changes might include manipulating adding or removing newlines in fset,
// modifying the position of nodes, or modifying literal values.
func File(fset *token.FileSet, file *ast.File, opts Options) {
simplify(file)

// TODO: replace this hacky mess with go/version once we can rely on Go 1.22,
// as well as replacing our uses of the semver package.
// In particular, we likely want to allow any of 1.21, 1.21.2, or go1.21rc3,
// but we can rely on go/version.Lang to validate and normalize.
if opts.LangVersion == "" {
opts.LangVersion = "v1"
} else if opts.LangVersion[0] != 'v' {
opts.LangVersion = "v" + opts.LangVersion
opts.LangVersion = "v1.0"
}
if !semver.IsValid(opts.LangVersion) {
panic(fmt.Sprintf("invalid semver string: %q", opts.LangVersion))
m := rxGoVersionMajorMinor.FindStringSubmatch(opts.LangVersion)
if m == nil {
panic(fmt.Sprintf("invalid Go version: %q", opts.LangVersion))
}
opts.LangVersion = "v" + m[2] + "." + m[3]
f := &fumpter{
File: fset.File(file.Pos()),
fset: fset,
Expand Down
4 changes: 2 additions & 2 deletions testdata/script/diagnose.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ cmp stdout foo.go.golden
exec gofumpt -extra foo.go
cmp stdout foo.go.golden-extra

exec gofumpt -lang=v1 foo.go
exec gofumpt -lang=1.0 foo.go
cmp stdout foo.go.golden-lang

exec gofumpt -d nochange.go
Expand Down Expand Up @@ -91,7 +91,7 @@ package p
-- foo.go.golden-lang --
package p

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

Expand Down
25 changes: 25 additions & 0 deletions testdata/script/gomod.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Test various edge cases with go.mod files.

exec gofumpt toolchain-stable/a.go
stdout '//gofumpt:diagnose.* -lang=v1.21'

exec gofumpt toolchain-unstable/a.go
stdout '//gofumpt:diagnose.* -lang=v1.21'

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

go 1.21.2
-- toolchain-stable/a.go --
package a

//gofumpt:diagnose

-- toolchain-unstable/go.mod --
module a

go 1.21rc3
-- toolchain-unstable/a.go --
package a

//gofumpt:diagnose
2 changes: 1 addition & 1 deletion testdata/script/octal-literals.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ exec gofumpt -d foo.go.golden
! stdout .

# We can give an explicitly older version, too
exec gofumpt -lang=v1 -l .
exec gofumpt -lang=1.0 -l .
! stdout .

-- go.mod --
Expand Down

0 comments on commit 13743a4

Please sign in to comment.