golang.org/x/tools/gopls@v0.15.3/internal/hooks/gofumpt_120.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build go1.20 6 // +build go1.20 7 8 package hooks 9 10 import ( 11 "context" 12 "fmt" 13 14 "golang.org/x/tools/gopls/internal/settings" 15 "mvdan.cc/gofumpt/format" 16 ) 17 18 func updateGofumpt(options *settings.Options) { 19 options.GofumptFormat = func(ctx context.Context, langVersion, modulePath string, src []byte) ([]byte, error) { 20 fixedVersion, err := fixLangVersion(langVersion) 21 if err != nil { 22 return nil, err 23 } 24 return format.Source(src, format.Options{ 25 LangVersion: fixedVersion, 26 ModulePath: modulePath, 27 }) 28 } 29 } 30 31 // fixLangVersion function cleans the input so that gofumpt doesn't panic. It is 32 // rather permissive, and accepts version strings that aren't technically valid 33 // in a go.mod file. 34 // 35 // More specifically, it looks for an optional 'v' followed by 1-3 36 // '.'-separated numbers. The resulting string is stripped of any suffix beyond 37 // this expected version number pattern. 38 // 39 // See also golang/go#61692: gofumpt does not accept the new language versions 40 // appearing in go.mod files (e.g. go1.21rc3). 41 func fixLangVersion(input string) (string, error) { 42 bad := func() (string, error) { 43 return "", fmt.Errorf("invalid language version syntax %q", input) 44 } 45 if input == "" { 46 return input, nil 47 } 48 i := 0 49 if input[0] == 'v' { // be flexible about 'v' 50 i++ 51 } 52 // takeDigits consumes ascii numerals 0-9 and reports if at least one was 53 // consumed. 54 takeDigits := func() bool { 55 found := false 56 for ; i < len(input) && '0' <= input[i] && input[i] <= '9'; i++ { 57 found = true 58 } 59 return found 60 } 61 if !takeDigits() { // versions must start with at least one number 62 return bad() 63 } 64 65 // Accept optional minor and patch versions. 66 for n := 0; n < 2; n++ { 67 if i < len(input) && input[i] == '.' { 68 // Look for minor/patch version. 69 i++ 70 if !takeDigits() { 71 i-- 72 break 73 } 74 } 75 } 76 // Accept any suffix. 77 return input[:i], nil 78 }