require (
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26
golang.org/x/arch v0.2.1-0.20230208145055-40c19ba4a7c5
- golang.org/x/mod v0.10.1-0.20230517154618-e7bea8f1d64f
+ golang.org/x/mod v0.10.1-0.20230523205221-fc83a8faf993
golang.org/x/sync v0.2.0
golang.org/x/sys v0.8.0
golang.org/x/term v0.5.0
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
golang.org/x/arch v0.2.1-0.20230208145055-40c19ba4a7c5 h1:UFbINK7+lzLJEIqCXPlzx05ivYhLQeXCkxW3SSH3f8Q=
golang.org/x/arch v0.2.1-0.20230208145055-40c19ba4a7c5/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
-golang.org/x/mod v0.10.1-0.20230517154618-e7bea8f1d64f h1:ghNt+qaUoQ453QdEj40jEN5kYz71m4aDEkk767JfeR0=
-golang.org/x/mod v0.10.1-0.20230517154618-e7bea8f1d64f/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.10.1-0.20230523205221-fc83a8faf993 h1:hr4tBhz2ITxV3Dj4xUzXUVtuRnMSzdWcTF8JYcrbdGs=
+golang.org/x/mod v0.10.1-0.20230523205221-fc83a8faf993/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
-- m2/go.mod --
module example.com/m2
-go 1.21
+go 1.19
require example.com/generics v1.0.0
-- m2/q/q.go --
func Format(f *FileSyntax) []byte {
pr := &printer{}
pr.file(f)
- return pr.Bytes()
+
+ // remove trailing blank lines
+ b := pr.Bytes()
+ for len(b) > 0 && b[len(b)-1] == '\n' && (len(b) == 1 || b[len(b)-2] == '\n') {
+ b = b[:len(b)-1]
+ }
+ return b
}
// A printer collects the state during printing of a file or expression.
}
p.trim()
- p.printf("\n")
+ if b := p.Bytes(); len(b) == 0 || (len(b) >= 2 && b[len(b)-1] == '\n' && b[len(b)-2] == '\n') {
+ // skip the blank line at top of file or after a blank line
+ } else {
+ p.printf("\n")
+ }
for i := 0; i < p.margin; i++ {
p.printf("\t")
}
// A File is the parsed, interpreted form of a go.mod file.
type File struct {
- Module *Module
- Go *Go
- Require []*Require
- Exclude []*Exclude
- Replace []*Replace
- Retract []*Retract
+ Module *Module
+ Go *Go
+ Toolchain *Toolchain
+ Require []*Require
+ Exclude []*Exclude
+ Replace []*Replace
+ Retract []*Retract
Syntax *FileSyntax
}
Syntax *Line
}
+// A Toolchain is the toolchain statement.
+type Toolchain struct {
+ Name string // "go1.21rc1"
+ Syntax *Line
+}
+
// An Exclude is a single exclude statement.
type Exclude struct {
Mod module.Version
return f, nil
}
-var GoVersionRE = lazyregexp.New(`^([1-9][0-9]*)\.(0|[1-9][0-9]*)$`)
+var GoVersionRE = lazyregexp.New(`^([1-9][0-9]*)\.(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))?([a-z]+[0-9]+)?$`)
var laxGoVersionRE = lazyregexp.New(`^v?(([1-9][0-9]*)\.(0|[1-9][0-9]*))([^0-9].*)$`)
+// Toolchains must be named beginning with `go1` or containing `-go1` as a substring,
+// like "go1.20.3" or "gccgo-go1.20.3". As a special case, "local" is also permitted.
+var ToolchainRE = lazyregexp.New(`^local$|(^|-)go1`)
+
func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, args []string, fix VersionFixer, strict bool) {
// If strict is false, this module is a dependency.
// We ignore all unknown directives as well as main-module-only
f.Go = &Go{Syntax: line}
f.Go.Version = args[0]
+ case "toolchain":
+ if f.Toolchain != nil {
+ errorf("repeated toolchain statement")
+ return
+ }
+ if len(args) != 1 {
+ errorf("toolchain directive expects exactly one argument")
+ return
+ } else if strict && !ToolchainRE.MatchString(args[0]) {
+ errorf("invalid toolchain version '%s': must match format go1.23 or local", args[0])
+ return
+ }
+ f.Toolchain = &Toolchain{Syntax: line}
+ f.Toolchain.Name = args[0]
+
case "module":
if f.Module != nil {
errorf("repeated module statement")
f.Go = &Go{Syntax: line}
f.Go.Version = args[0]
+ case "toolchain":
+ if f.Toolchain != nil {
+ errorf("repeated toolchain statement")
+ return
+ }
+ if len(args) != 1 {
+ errorf("toolchain directive expects exactly one argument")
+ return
+ } else if !ToolchainRE.MatchString(args[0]) {
+ errorf("invalid toolchain version '%s': must match format go1.23 or local", args[0])
+ return
+ }
+
+ f.Toolchain = &Toolchain{Syntax: line}
+ f.Toolchain.Name = args[0]
+
case "use":
if len(args) != 1 {
errorf("usage: %s local/dir", verb)
func (f *File) AddGoStmt(version string) error {
if !GoVersionRE.MatchString(version) {
- return fmt.Errorf("invalid language version string %q", version)
+ return fmt.Errorf("invalid language version %q", version)
}
if f.Go == nil {
var hint Expr
return nil
}
+// DropGoStmt deletes the go statement from the file.
+func (f *File) DropGoStmt() {
+ if f.Go != nil {
+ f.Go.Syntax.markRemoved()
+ f.Go = nil
+ }
+}
+
+// DropToolchainStmt deletes the toolchain statement from the file.
+func (f *File) DropToolchainStmt() {
+ if f.Toolchain != nil {
+ f.Toolchain.Syntax.markRemoved()
+ f.Toolchain = nil
+ }
+}
+
+func (f *File) AddToolchainStmt(name string) error {
+ if !ToolchainRE.MatchString(name) {
+ return fmt.Errorf("invalid toolchain name %q", name)
+ }
+ if f.Toolchain == nil {
+ var hint Expr
+ if f.Go != nil && f.Go.Syntax != nil {
+ hint = f.Go.Syntax
+ } else if f.Module != nil && f.Module.Syntax != nil {
+ hint = f.Module.Syntax
+ }
+ f.Toolchain = &Toolchain{
+ Name: name,
+ Syntax: f.Syntax.addLine(hint, "toolchain", name),
+ }
+ } else {
+ f.Toolchain.Name = name
+ f.Syntax.updateLine(f.Toolchain.Syntax, "toolchain", name)
+ }
+ return nil
+}
+
// AddRequire sets the first require line for path to version vers,
// preserving any existing comments for that line and removing all
// other lines for path.
// A WorkFile is the parsed, interpreted form of a go.work file.
type WorkFile struct {
- Go *Go
- Use []*Use
- Replace []*Replace
+ Go *Go
+ Toolchain *Toolchain
+ Use []*Use
+ Replace []*Replace
Syntax *FileSyntax
}
func (f *WorkFile) AddGoStmt(version string) error {
if !GoVersionRE.MatchString(version) {
- return fmt.Errorf("invalid language version string %q", version)
+ return fmt.Errorf("invalid language version %q", version)
}
if f.Go == nil {
stmt := &Line{Token: []string{"go", version}}
Version: version,
Syntax: stmt,
}
- // Find the first non-comment-only block that's and add
+ // Find the first non-comment-only block and add
// the go statement before it. That will keep file comments at the top.
i := 0
for i = 0; i < len(f.Syntax.Stmt); i++ {
return nil
}
+func (f *WorkFile) AddToolchainStmt(name string) error {
+ if !ToolchainRE.MatchString(name) {
+ return fmt.Errorf("invalid toolchain name %q", name)
+ }
+ if f.Toolchain == nil {
+ stmt := &Line{Token: []string{"toolchain", name}}
+ f.Toolchain = &Toolchain{
+ Name: name,
+ Syntax: stmt,
+ }
+ // Find the go line and add the toolchain line after it.
+ // Or else find the first non-comment-only block and add
+ // the toolchain line before it. That will keep file comments at the top.
+ i := 0
+ for i = 0; i < len(f.Syntax.Stmt); i++ {
+ if line, ok := f.Syntax.Stmt[i].(*Line); ok && len(line.Token) > 0 && line.Token[0] == "go" {
+ i++
+ goto Found
+ }
+ }
+ for i = 0; i < len(f.Syntax.Stmt); i++ {
+ if _, ok := f.Syntax.Stmt[i].(*CommentBlock); !ok {
+ break
+ }
+ }
+ Found:
+ f.Syntax.Stmt = append(append(f.Syntax.Stmt[:i:i], stmt), f.Syntax.Stmt[i:]...)
+ } else {
+ f.Toolchain.Name = name
+ f.Syntax.updateLine(f.Toolchain.Syntax, "toolchain", name)
+ }
+ return nil
+}
+
+// DropGoStmt deletes the go statement from the file.
+func (f *WorkFile) DropGoStmt() {
+ if f.Go != nil {
+ f.Go.Syntax.markRemoved()
+ f.Go = nil
+ }
+}
+
+// DropToolchainStmt deletes the toolchain statement from the file.
+func (f *WorkFile) DropToolchainStmt() {
+ if f.Toolchain != nil {
+ f.Toolchain.Syntax.markRemoved()
+ f.Toolchain = nil
+ }
+}
+
func (f *WorkFile) AddUse(diskPath, modulePath string) error {
need := true
for _, d := range f.Use {
golang.org/x/arch/arm64/arm64asm
golang.org/x/arch/ppc64/ppc64asm
golang.org/x/arch/x86/x86asm
-# golang.org/x/mod v0.10.1-0.20230517154618-e7bea8f1d64f
+# golang.org/x/mod v0.10.1-0.20230523205221-fc83a8faf993
## explicit; go 1.17
golang.org/x/mod/internal/lazyregexp
golang.org/x/mod/modfile