Skip to content

Commit a73672d

Browse files
committed
modfile: add support for go and toolchain lines
As part of the forward compatibility work, a new toolchain line is being added, and go lines are allowed to specify toolchain versions like "1.21.0" or "1.21rc1" now. (The lax RE has allowed this for quite some time; what's new here is allowing it in the main module.) For golang/go#57001. Change-Id: I1dc01289381fe080644a7a391b97a65158938f39 Reviewed-on: https://go-review.googlesource.com/c/mod/+/497397 TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Bryan Mills <[email protected]> Run-TryBot: Russ Cox <[email protected]>
1 parent e7bea8f commit a73672d

File tree

3 files changed

+86
-18
lines changed

3 files changed

+86
-18
lines changed

modfile/read_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -434,13 +434,13 @@ func TestGoVersion(t *testing.T) {
434434
{desc: "empty", input: "module m\ngo \n", ok: false},
435435
{desc: "one", input: "module m\ngo 1\n", ok: false},
436436
{desc: "two", input: "module m\ngo 1.22\n", ok: true},
437-
{desc: "three", input: "module m\ngo 1.22.333", ok: false},
437+
{desc: "three", input: "module m\ngo 1.22.333", ok: true},
438438
{desc: "before", input: "module m\ngo v1.2\n", ok: false},
439-
{desc: "after", input: "module m\ngo 1.2rc1\n", ok: false},
439+
{desc: "after", input: "module m\ngo 1.2rc1\n", ok: true},
440440
{desc: "space", input: "module m\ngo 1.2 3.4\n", ok: false},
441-
{desc: "alt1", input: "module m\ngo 1.2.3\n", ok: false, laxOK: true},
442-
{desc: "alt2", input: "module m\ngo 1.2rc1\n", ok: false, laxOK: true},
443-
{desc: "alt3", input: "module m\ngo 1.2beta1\n", ok: false, laxOK: true},
441+
{desc: "alt1", input: "module m\ngo 1.2.3\n", ok: true, laxOK: true},
442+
{desc: "alt2", input: "module m\ngo 1.2rc1\n", ok: true, laxOK: true},
443+
{desc: "alt3", input: "module m\ngo 1.2beta1\n", ok: true, laxOK: true},
444444
{desc: "alt4", input: "module m\ngo 1.2.beta1\n", ok: false, laxOK: true},
445445
{desc: "alt1", input: "module m\ngo v1.2.3\n", ok: false, laxOK: true},
446446
{desc: "alt2", input: "module m\ngo v1.2rc1\n", ok: false, laxOK: true},

modfile/rule.go

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,13 @@ import (
3535

3636
// A File is the parsed, interpreted form of a go.mod file.
3737
type File struct {
38-
Module *Module
39-
Go *Go
40-
Require []*Require
41-
Exclude []*Exclude
42-
Replace []*Replace
43-
Retract []*Retract
38+
Module *Module
39+
Go *Go
40+
Toolchain *Toolchain
41+
Require []*Require
42+
Exclude []*Exclude
43+
Replace []*Replace
44+
Retract []*Retract
4445

4546
Syntax *FileSyntax
4647
}
@@ -58,6 +59,12 @@ type Go struct {
5859
Syntax *Line
5960
}
6061

62+
// A Toolchain is the toolchain statement.
63+
type Toolchain struct {
64+
Name string // "go1.21rc1"
65+
Syntax *Line
66+
}
67+
6168
// An Exclude is a single exclude statement.
6269
type Exclude struct {
6370
Mod module.Version
@@ -296,9 +303,13 @@ func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (parse
296303
return f, nil
297304
}
298305

299-
var GoVersionRE = lazyregexp.New(`^([1-9][0-9]*)\.(0|[1-9][0-9]*)$`)
306+
var GoVersionRE = lazyregexp.New(`^([1-9][0-9]*)\.(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))?([a-z]+[0-9]+)?$`)
300307
var laxGoVersionRE = lazyregexp.New(`^v?(([1-9][0-9]*)\.(0|[1-9][0-9]*))([^0-9].*)$`)
301308

309+
// Toolchains must be named beginning with `go1` or containing `-go1` as a substring,
310+
// like "go1.20.3" or "gccgo-go1.20.3". As a special case, "local" is also permitted.
311+
var ToolchainRE = lazyregexp.New(`^local$|(^|-)go1`)
312+
302313
func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, args []string, fix VersionFixer, strict bool) {
303314
// If strict is false, this module is a dependency.
304315
// We ignore all unknown directives as well as main-module-only
@@ -926,7 +937,7 @@ func (f *File) Cleanup() {
926937

927938
func (f *File) AddGoStmt(version string) error {
928939
if !GoVersionRE.MatchString(version) {
929-
return fmt.Errorf("invalid language version string %q", version)
940+
return fmt.Errorf("invalid language version %q", version)
930941
}
931942
if f.Go == nil {
932943
var hint Expr
@@ -944,6 +955,28 @@ func (f *File) AddGoStmt(version string) error {
944955
return nil
945956
}
946957

958+
func (f *File) AddToolchainStmt(name string) error {
959+
if !ToolchainRE.MatchString(name) {
960+
return fmt.Errorf("invalid toolchain name %q", name)
961+
}
962+
if f.Toolchain == nil {
963+
var hint Expr
964+
if f.Go != nil && f.Go.Syntax != nil {
965+
hint = f.Go.Syntax
966+
} else if f.Module != nil && f.Module.Syntax != nil {
967+
hint = f.Module.Syntax
968+
}
969+
f.Toolchain = &Toolchain{
970+
Name: name,
971+
Syntax: f.Syntax.addLine(hint, "toolchain", name),
972+
}
973+
} else {
974+
f.Toolchain.Name = name
975+
f.Syntax.updateLine(f.Go.Syntax, "toolchain", name)
976+
}
977+
return nil
978+
}
979+
947980
// AddRequire sets the first require line for path to version vers,
948981
// preserving any existing comments for that line and removing all
949982
// other lines for path.

modfile/work.go

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import (
1212

1313
// A WorkFile is the parsed, interpreted form of a go.work file.
1414
type WorkFile struct {
15-
Go *Go
16-
Use []*Use
17-
Replace []*Replace
15+
Go *Go
16+
Toolchain *Toolchain
17+
Use []*Use
18+
Replace []*Replace
1819

1920
Syntax *FileSyntax
2021
}
@@ -109,15 +110,15 @@ func (f *WorkFile) Cleanup() {
109110

110111
func (f *WorkFile) AddGoStmt(version string) error {
111112
if !GoVersionRE.MatchString(version) {
112-
return fmt.Errorf("invalid language version string %q", version)
113+
return fmt.Errorf("invalid language version %q", version)
113114
}
114115
if f.Go == nil {
115116
stmt := &Line{Token: []string{"go", version}}
116117
f.Go = &Go{
117118
Version: version,
118119
Syntax: stmt,
119120
}
120-
// Find the first non-comment-only block that's and add
121+
// Find the first non-comment-only block and add
121122
// the go statement before it. That will keep file comments at the top.
122123
i := 0
123124
for i = 0; i < len(f.Syntax.Stmt); i++ {
@@ -133,6 +134,40 @@ func (f *WorkFile) AddGoStmt(version string) error {
133134
return nil
134135
}
135136

137+
func (f *WorkFile) AddToolchainStmt(name string) error {
138+
if !ToolchainRE.MatchString(name) {
139+
return fmt.Errorf("invalid toolchain name %q", name)
140+
}
141+
if f.Toolchain == nil {
142+
stmt := &Line{Token: []string{"toolchain", name}}
143+
f.Toolchain = &Toolchain{
144+
Name: name,
145+
Syntax: stmt,
146+
}
147+
// Find the go line and add the toolchain line after it.
148+
// Or else find the first non-comment-only block and add
149+
// the toolchain line before it. That will keep file comments at the top.
150+
i := 0
151+
for i = 0; i < len(f.Syntax.Stmt); i++ {
152+
if line, ok := f.Syntax.Stmt[i].(*Line); ok && len(line.Token) > 0 && line.Token[0] == "go" {
153+
i++
154+
goto Found
155+
}
156+
}
157+
for i = 0; i < len(f.Syntax.Stmt); i++ {
158+
if _, ok := f.Syntax.Stmt[i].(*CommentBlock); !ok {
159+
break
160+
}
161+
}
162+
Found:
163+
f.Syntax.Stmt = append(append(f.Syntax.Stmt[:i:i], stmt), f.Syntax.Stmt[i:]...)
164+
} else {
165+
f.Toolchain.Name = name
166+
f.Syntax.updateLine(f.Toolchain.Syntax, "toolchain", name)
167+
}
168+
return nil
169+
}
170+
136171
func (f *WorkFile) AddUse(diskPath, modulePath string) error {
137172
need := true
138173
for _, d := range f.Use {

0 commit comments

Comments
 (0)