From fa1ba4269bda724bb9f01ec381fbbaf031e45833 Mon Sep 17 00:00:00 2001 From: Aofei Sheng Date: Sat, 4 Nov 2023 19:02:03 +0800 Subject: [PATCH 1/7] sumdb: replace globsMatchPath with module.MatchPrefixPatterns In CL 239797, src/cmd/go/internal/str.GlobsMatchPath was replicated as module.MatchPrefixPatterns. This redundancy eliminates the need for globsMatchPath. This CL replaces calls to globsMatchPath with module.MatchPrefixPatterns and removes the now redundant globsMatchPath. Change-Id: Idd6fc10e7cf24d7b9603fa17edb2460d50b2e4aa Reviewed-on: https://go-review.googlesource.com/c/mod/+/539815 Auto-Submit: Bryan Mills Reviewed-by: Cherry Mui Reviewed-by: Bryan Mills LUCI-TryBot-Result: Go LUCI --- sumdb/client.go | 47 +---------------------------------------------- 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/sumdb/client.go b/sumdb/client.go index aecdc68..04c6e24 100644 --- a/sumdb/client.go +++ b/sumdb/client.go @@ -8,7 +8,6 @@ import ( "bytes" "errors" "fmt" - "path" "strings" "sync" "sync/atomic" @@ -193,51 +192,7 @@ func (c *Client) SetGONOSUMDB(list string) { var ErrGONOSUMDB = errors.New("skipped (listed in GONOSUMDB)") func (c *Client) skip(target string) bool { - return globsMatchPath(c.nosumdb, target) -} - -// globsMatchPath reports whether any path prefix of target -// matches one of the glob patterns (as defined by path.Match) -// in the comma-separated globs list. -// It ignores any empty or malformed patterns in the list. -func globsMatchPath(globs, target string) bool { - for globs != "" { - // Extract next non-empty glob in comma-separated list. - var glob string - if i := strings.Index(globs, ","); i >= 0 { - glob, globs = globs[:i], globs[i+1:] - } else { - glob, globs = globs, "" - } - if glob == "" { - continue - } - - // A glob with N+1 path elements (N slashes) needs to be matched - // against the first N+1 path elements of target, - // which end just before the N+1'th slash. - n := strings.Count(glob, "/") - prefix := target - // Walk target, counting slashes, truncating at the N+1'th slash. - for i := 0; i < len(target); i++ { - if target[i] == '/' { - if n == 0 { - prefix = target[:i] - break - } - n-- - } - } - if n > 0 { - // Not enough prefix elements. - continue - } - matched, _ := path.Match(glob, prefix) - if matched { - return true - } - } - return false + return module.MatchPrefixPatterns(c.nosumdb, target) } // Lookup returns the go.sum lines for the given module path and version. From 766dc5df63e3e3e5cd6b1682f522a01c99723beb Mon Sep 17 00:00:00 2001 From: Sam Thanawalla Date: Fri, 2 Feb 2024 20:14:19 +0000 Subject: [PATCH 2/7] modfile: use new go version string format in WorkFile.add error For golang/go#61888 Tested: Ran go test ./modfile Change-Id: I254fe559f25ac643e842a935954d18744ae87b0b Reviewed-on: https://go-review.googlesource.com/c/mod/+/560875 Reviewed-by: Bryan Mills Run-TryBot: Sam Thanawalla TryBot-Result: Gopher Robot --- modfile/rule.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modfile/rule.go b/modfile/rule.go index 35fd1f5..26acaa5 100644 --- a/modfile/rule.go +++ b/modfile/rule.go @@ -308,6 +308,7 @@ var laxGoVersionRE = lazyregexp.New(`^v?(([1-9][0-9]*)\.(0|[1-9][0-9]*))([^0-9]. // Toolchains must be named beginning with `go1`, // like "go1.20.3" or "go1.20.3-gccgo". As a special case, "default" is also permitted. +// TODO(samthanawalla): Replace regex with https://pkg.go.dev/go/version#IsValid in 1.23+ var ToolchainRE = lazyregexp.New(`^default$|^go1($|\.)`) func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, args []string, fix VersionFixer, strict bool) { @@ -384,7 +385,7 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a 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.0 or local", args[0]) + errorf("invalid toolchain version '%s': must match format go1.23.0 or default", args[0]) return } f.Toolchain = &Toolchain{Syntax: line} @@ -630,7 +631,7 @@ func (f *WorkFile) add(errs *ErrorList, line *Line, verb string, args []string, errorf("go directive expects exactly one argument") return } else if !GoVersionRE.MatchString(args[0]) { - errorf("invalid go version '%s': must match format 1.23", args[0]) + errorf("invalid go version '%s': must match format 1.23.0", args[0]) return } @@ -646,7 +647,7 @@ func (f *WorkFile) add(errs *ErrorList, line *Line, verb string, args []string, 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]) + errorf("invalid toolchain version '%s': must match format go1.23.0 or default", args[0]) return } From 18d3f5618645fbebec703867364c3f2ac1102f1d Mon Sep 17 00:00:00 2001 From: crazybolillo Date: Fri, 8 Mar 2024 12:44:38 -0300 Subject: [PATCH 3/7] modfile: fix crash on AddGoStmt in empty File AddGoStmt uses File.Syntax without checking whether it is nil or not. This causes crashes when using it on empty files that have not had their Syntax member initialized to a valid pointer. This change fixes it by ensuring File.Syntax is a valid pointer before proceeding. Fixes golang/go#62457. Change-Id: Iab02039f79e73d939ca5d3e48b29faa5e0a9a5ec Reviewed-on: https://go-review.googlesource.com/c/mod/+/570115 Reviewed-by: Michael Knyszek Auto-Submit: Bryan Mills Reviewed-by: Bryan Mills LUCI-TryBot-Result: Go LUCI --- modfile/rule.go | 2 ++ modfile/rule_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/modfile/rule.go b/modfile/rule.go index 26acaa5..0e7b7e2 100644 --- a/modfile/rule.go +++ b/modfile/rule.go @@ -975,6 +975,8 @@ func (f *File) AddGoStmt(version string) error { var hint Expr if f.Module != nil && f.Module.Syntax != nil { hint = f.Module.Syntax + } else if f.Syntax == nil { + f.Syntax = new(FileSyntax) } f.Go = &Go{ Version: version, diff --git a/modfile/rule_test.go b/modfile/rule_test.go index 57c8be6..96e0bfe 100644 --- a/modfile/rule_test.go +++ b/modfile/rule_test.go @@ -1549,6 +1549,20 @@ var fixVersionTests = []struct { }, } +var modifyEmptyFilesTests = []struct { + desc string + operations func(f *File) + want string +}{ + { + desc: `addGoStmt`, + operations: func(f *File) { + f.AddGoStmt("1.20") + }, + want: `go 1.20`, + }, +} + func fixV(path, version string) (string, error) { if path != "example.com/m" { return "", fmt.Errorf("module path must be example.com/m") @@ -1846,3 +1860,29 @@ func TestFixVersion(t *testing.T) { }) } } + +func TestAddOnEmptyFile(t *testing.T) { + for _, tt := range modifyEmptyFilesTests { + t.Run(tt.desc, func(t *testing.T) { + f := &File{} + tt.operations(f) + + expect, err := Parse("out", []byte(tt.want), nil) + if err != nil { + t.Fatal(err) + } + golden, err := expect.Format() + if err != nil { + t.Fatal(err) + } + got, err := f.Format() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(got, golden) { + t.Fatalf("got:\n%s\nwant:\n%s", got, golden) + } + }) + } +} From 87140ecd02dd794a1acc173c48f0c8c52cd3a339 Mon Sep 17 00:00:00 2001 From: Filippo Valsorda Date: Sat, 9 Mar 2024 10:06:07 +0100 Subject: [PATCH 4/7] sumdb/tlog: make NewTiles only generate strictly necessary tiles Currently, NewTiles returns tiles for every partial tree size from oldTreeSize to newTreeSize. However, if a log is only publishing checkpoints at oldTreeSize and newTreeSize, the trees of sizes oldTreeSize+1 to newTreeSize-1 are unverifiable, so those tiles are unnecessary. Also, NewTiles currently returns tiles that already exists as part of oldTreeSize, which are not new. This has a significant performance and cost difference when uploading tiles individually to e.g. object storage. Change-Id: I92a5d76bc54e7022991e51997e793356ab5e7d5c Reviewed-on: https://go-review.googlesource.com/c/mod/+/570295 Auto-Submit: Filippo Valsorda LUCI-TryBot-Result: Go LUCI Reviewed-by: Russ Cox Reviewed-by: Cherry Mui --- sumdb/tlog/tile.go | 10 ++++------ sumdb/tlog/tile_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/sumdb/tlog/tile.go b/sumdb/tlog/tile.go index 857d487..37771c5 100644 --- a/sumdb/tlog/tile.go +++ b/sumdb/tlog/tile.go @@ -115,16 +115,14 @@ func NewTiles(h int, oldTreeSize, newTreeSize int64) []Tile { for level := uint(0); newTreeSize>>(H*level) > 0; level++ { oldN := oldTreeSize >> (H * level) newN := newTreeSize >> (H * level) + if oldN == newN { + continue + } for n := oldN >> H; n < newN>>H; n++ { tiles = append(tiles, Tile{H: h, L: int(level), N: n, W: 1 << H}) } n := newN >> H - maxW := int(newN - n< n< 0 { tiles = append(tiles, Tile{H: h, L: int(level), N: n, W: w}) } } diff --git a/sumdb/tlog/tile_test.go b/sumdb/tlog/tile_test.go index e451a63..62b50b7 100644 --- a/sumdb/tlog/tile_test.go +++ b/sumdb/tlog/tile_test.go @@ -5,6 +5,7 @@ package tlog import ( + "fmt" "testing" ) @@ -22,3 +23,28 @@ func FuzzParseTilePath(f *testing.F) { ParseTilePath(path) }) } + +func TestNewTilesForSize(t *testing.T) { + for _, tt := range []struct { + old, new int64 + want int + }{ + {1, 1, 0}, + {100, 101, 1}, + {1023, 1025, 3}, + {1024, 1030, 1}, + {1030, 2000, 1}, + {1030, 10000, 10}, + {49516517, 49516586, 3}, + } { + t.Run(fmt.Sprintf("%d-%d", tt.old, tt.new), func(t *testing.T) { + tiles := NewTiles(10, tt.old, tt.new) + if got := len(tiles); got != tt.want { + t.Errorf("got %d, want %d", got, tt.want) + for _, tile := range tiles { + t.Logf("%+v", tile) + } + } + }) + } +} From aa51b25a4485b19ca64f578bad6fa40229e75984 Mon Sep 17 00:00:00 2001 From: Quan Tong Date: Mon, 13 Nov 2023 12:14:57 +0700 Subject: [PATCH 5/7] modfile: do not collapse if there are unattached comments within blocks The existing implementation drops unattached comments when collapsing block into single line. Fixes #33280 Change-Id: I388e1468657d461cba771e90d5e328746d927ae2 Reviewed-on: https://go-review.googlesource.com/c/mod/+/541815 Reviewed-by: Cherry Mui Reviewed-by: Bryan Mills Auto-Submit: Bryan Mills LUCI-TryBot-Result: Go LUCI --- modfile/read.go | 2 +- modfile/rule_test.go | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/modfile/read.go b/modfile/read.go index 5b5bb5e..2205682 100644 --- a/modfile/read.go +++ b/modfile/read.go @@ -225,7 +225,7 @@ func (x *FileSyntax) Cleanup() { if ww == 0 { continue } - if ww == 1 { + if ww == 1 && len(stmt.RParen.Comments.Before) == 0 { // Collapse block into single line. line := &Line{ Comments: Comments{ diff --git a/modfile/rule_test.go b/modfile/rule_test.go index 96e0bfe..ca11d17 100644 --- a/modfile/rule_test.go +++ b/modfile/rule_test.go @@ -90,6 +90,24 @@ var addRequireTests = []struct { ) `, }, + { + `unattached_comments`, + ` + module m + require ( + foo v0.0.0-00010101000000-000000000000 + // bar v0.0.0-00010101000000-000000000000 + ) + `, + "foo", "v0.0.0-00010101000000-000000000000", + ` + module m + require ( + foo v0.0.0-00010101000000-000000000000 + // bar v0.0.0-00010101000000-000000000000 + ) + `, + }, } type require struct { From 6686f416970d4b8e2f54f521955dee89e6763c4b Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Tue, 7 May 2024 16:01:29 -0400 Subject: [PATCH 6/7] module: add COM0 and LPT0 to badWindowsNames They have been added to the list of file names that are disallowed on Windows that's referenced in the comment to badWindowsNames, so add them to badWindowsNames. For golang/go#67238 For golang/go#66625 Change-Id: I82e5d70f33330f746783fd22090a3ebaf9408dfc Reviewed-on: https://go-review.googlesource.com/c/mod/+/583836 LUCI-TryBot-Result: Go LUCI Reviewed-by: Sam Thanawalla --- module/module.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/module/module.go b/module/module.go index 2a364b2..cac1a89 100644 --- a/module/module.go +++ b/module/module.go @@ -506,6 +506,7 @@ var badWindowsNames = []string{ "PRN", "AUX", "NUL", + "COM0", "COM1", "COM2", "COM3", @@ -515,6 +516,7 @@ var badWindowsNames = []string{ "COM7", "COM8", "COM9", + "LPT0", "LPT1", "LPT2", "LPT3", From c0bdc7bd01c96a3472df419bc2b082e06b09a219 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 8 May 2024 20:33:49 -0400 Subject: [PATCH 7/7] modfile: add API for godebug lines For golang/go#65573 Change-Id: I5c1be8833f70b0b5a7257bd5216fa6a89bd2665f Reviewed-on: https://go-review.googlesource.com/c/mod/+/584300 LUCI-TryBot-Result: Go LUCI Auto-Submit: Russ Cox Reviewed-by: Michael Matloob Reviewed-by: Sam Thanawalla --- modfile/rule.go | 106 ++++++++++++++++++++++++++++- modfile/rule_test.go | 157 +++++++++++++++++++++++++++++++++++++++++++ modfile/work.go | 52 +++++++++++++- modfile/work_test.go | 28 ++++++++ 4 files changed, 339 insertions(+), 4 deletions(-) diff --git a/modfile/rule.go b/modfile/rule.go index 0e7b7e2..66dcaf9 100644 --- a/modfile/rule.go +++ b/modfile/rule.go @@ -38,6 +38,7 @@ type File struct { Module *Module Go *Go Toolchain *Toolchain + Godebug []*Godebug Require []*Require Exclude []*Exclude Replace []*Replace @@ -65,6 +66,13 @@ type Toolchain struct { Syntax *Line } +// A Godebug is a single godebug key=value statement. +type Godebug struct { + Key string + Value string + Syntax *Line +} + // An Exclude is a single exclude statement. type Exclude struct { Mod module.Version @@ -289,7 +297,7 @@ func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (parse }) } continue - case "module", "require", "exclude", "replace", "retract": + case "module", "godebug", "require", "exclude", "replace", "retract": for _, l := range x.Line { f.add(&errs, x, l, x.Token[0], l.Token, fix, strict) } @@ -308,7 +316,9 @@ var laxGoVersionRE = lazyregexp.New(`^v?(([1-9][0-9]*)\.(0|[1-9][0-9]*))([^0-9]. // Toolchains must be named beginning with `go1`, // like "go1.20.3" or "go1.20.3-gccgo". As a special case, "default" is also permitted. -// TODO(samthanawalla): Replace regex with https://pkg.go.dev/go/version#IsValid in 1.23+ +// Note that this regexp is a much looser condition than go/version.IsValid, +// for forward compatibility. +// (This code has to be work to identify new toolchains even if we tweak the syntax in the future.) var ToolchainRE = lazyregexp.New(`^default$|^go1($|\.)`) func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, args []string, fix VersionFixer, strict bool) { @@ -384,7 +394,7 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a if len(args) != 1 { errorf("toolchain directive expects exactly one argument") return - } else if strict && !ToolchainRE.MatchString(args[0]) { + } else if !ToolchainRE.MatchString(args[0]) { errorf("invalid toolchain version '%s': must match format go1.23.0 or default", args[0]) return } @@ -412,6 +422,22 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a } f.Module.Mod = module.Version{Path: s} + case "godebug": + if len(args) != 1 || strings.ContainsAny(args[0], "\"`',") { + errorf("usage: godebug key=value") + return + } + key, value, ok := strings.Cut(args[0], "=") + if !ok { + errorf("usage: godebug key=value") + return + } + f.Godebug = append(f.Godebug, &Godebug{ + Key: key, + Value: value, + Syntax: line, + }) + case "require", "exclude": if len(args) != 2 { errorf("usage: %s module/path v1.2.3", verb) @@ -654,6 +680,22 @@ func (f *WorkFile) add(errs *ErrorList, line *Line, verb string, args []string, f.Toolchain = &Toolchain{Syntax: line} f.Toolchain.Name = args[0] + case "godebug": + if len(args) != 1 || strings.ContainsAny(args[0], "\"`',") { + errorf("usage: godebug key=value") + return + } + key, value, ok := strings.Cut(args[0], "=") + if !ok { + errorf("usage: godebug key=value") + return + } + f.Godebug = append(f.Godebug, &Godebug{ + Key: key, + Value: value, + Syntax: line, + }) + case "use": if len(args) != 1 { errorf("usage: %s local/dir", verb) @@ -929,6 +971,15 @@ func (f *File) Format() ([]byte, error) { // Cleanup cleans out all the cleared entries. func (f *File) Cleanup() { w := 0 + for _, g := range f.Godebug { + if g.Key != "" { + f.Godebug[w] = g + w++ + } + } + f.Godebug = f.Godebug[:w] + + w = 0 for _, r := range f.Require { if r.Mod.Path != "" { f.Require[w] = r @@ -1027,6 +1078,45 @@ func (f *File) AddToolchainStmt(name string) error { return nil } +// AddGodebug sets the first godebug line for key to value, +// preserving any existing comments for that line and removing all +// other godebug lines for key. +// +// If no line currently exists for key, AddGodebug adds a new line +// at the end of the last godebug block. +func (f *File) AddGodebug(key, value string) error { + need := true + for _, g := range f.Godebug { + if g.Key == key { + if need { + g.Value = value + f.Syntax.updateLine(g.Syntax, "godebug", key+"="+value) + need = false + } else { + g.Syntax.markRemoved() + *g = Godebug{} + } + } + } + + if need { + f.addNewGodebug(key, value) + } + return nil +} + +// addNewGodebug adds a new godebug key=value line at the end +// of the last godebug block, regardless of any existing godebug lines for key. +func (f *File) addNewGodebug(key, value string) { + line := f.Syntax.addLine(nil, "godebug", key+"="+value) + g := &Godebug{ + Key: key, + Value: value, + Syntax: line, + } + f.Godebug = append(f.Godebug, g) +} + // 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. @@ -1334,6 +1424,16 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) { f.SortBlocks() } +func (f *File) DropGodebug(key string) error { + for _, g := range f.Godebug { + if g.Key == key { + g.Syntax.markRemoved() + *g = Godebug{} + } + } + return nil +} + func (f *File) DropRequire(path string) error { for _, r := range f.Require { if r.Mod.Path == path { diff --git a/modfile/rule_test.go b/modfile/rule_test.go index ca11d17..4d0d12a 100644 --- a/modfile/rule_test.go +++ b/modfile/rule_test.go @@ -1581,6 +1581,139 @@ var modifyEmptyFilesTests = []struct { }, } +var addGodebugTests = []struct { + desc string + in string + key string + value string + out string +}{ + { + `existing`, + ` + module m + godebug key=old + `, + "key", "new", + ` + module m + godebug key=new + `, + }, + { + `existing2`, + ` + module m + godebug ( + key=first // first + other=first-a // first-a + ) + godebug key=second // second + godebug ( + key=third // third + other=third-a // third-a + ) + `, + "key", "new", + ` + module m + + godebug ( + key=new // first + other=first-a// first-a + ) + + godebug other=third-a // third-a + `, + }, + { + `new`, + ` + module m + godebug other=foo + `, + "key", "new", + ` + module m + godebug ( + other=foo + key=new + ) + `, + }, + { + `new2`, + ` + module m + godebug first=1 + godebug second=2 + `, + "third", "3", + ` + module m + godebug first=1 + godebug ( + second=2 + third=3 + ) + `, + }, +} + +var dropGodebugTests = []struct { + desc string + in string + key string + out string +}{ + { + `existing`, + ` + module m + godebug key=old + `, + "key", + ` + module m + `, + }, + { + `existing2`, + ` + module m + godebug ( + key=first // first + other=first-a // first-a + ) + godebug key=second // second + godebug ( + key=third // third + other=third-a // third-a + ) + `, + "key", + ` + module m + + godebug other=first-a// first-a + + godebug other=third-a // third-a + `, + }, + { + `new`, + ` + module m + godebug other=foo + `, + "key", + ` + module m + godebug other=foo + `, + }, +} + func fixV(path, version string) (string, error) { if path != "example.com/m" { return "", fmt.Errorf("module path must be example.com/m") @@ -1600,6 +1733,18 @@ func TestAddRequire(t *testing.T) { } } +func TestAddGodebug(t *testing.T) { + for _, tt := range addGodebugTests { + t.Run(tt.desc, func(t *testing.T) { + testEdit(t, tt.in, tt.out, true, func(f *File) error { + err := f.AddGodebug(tt.key, tt.value) + f.Cleanup() + return err + }) + }) + } +} + func TestSetRequire(t *testing.T) { for _, tt := range setRequireTests { t.Run(tt.desc, func(t *testing.T) { @@ -1696,6 +1841,18 @@ func TestDropToolchain(t *testing.T) { } } +func TestDropGodebug(t *testing.T) { + for _, tt := range dropGodebugTests { + t.Run(tt.desc, func(t *testing.T) { + testEdit(t, tt.in, tt.out, true, func(f *File) error { + f.DropGodebug(tt.key) + f.Cleanup() + return nil + }) + }) + } +} + func TestAddExclude(t *testing.T) { for _, tt := range addExcludeTests { t.Run(tt.desc, func(t *testing.T) { diff --git a/modfile/work.go b/modfile/work.go index d7b9937..8f54897 100644 --- a/modfile/work.go +++ b/modfile/work.go @@ -14,6 +14,7 @@ import ( type WorkFile struct { Go *Go Toolchain *Toolchain + Godebug []*Godebug Use []*Use Replace []*Replace @@ -68,7 +69,7 @@ func ParseWork(file string, data []byte, fix VersionFixer) (*WorkFile, error) { Err: fmt.Errorf("unknown block type: %s", strings.Join(x.Token, " ")), }) continue - case "use", "replace": + case "godebug", "use", "replace": for _, l := range x.Line { f.add(&errs, l, x.Token[0], l.Token, fix) } @@ -184,6 +185,55 @@ func (f *WorkFile) DropToolchainStmt() { } } +// AddGodebug sets the first godebug line for key to value, +// preserving any existing comments for that line and removing all +// other godebug lines for key. +// +// If no line currently exists for key, AddGodebug adds a new line +// at the end of the last godebug block. +func (f *WorkFile) AddGodebug(key, value string) error { + need := true + for _, g := range f.Godebug { + if g.Key == key { + if need { + g.Value = value + f.Syntax.updateLine(g.Syntax, "godebug", key+"="+value) + need = false + } else { + g.Syntax.markRemoved() + *g = Godebug{} + } + } + } + + if need { + f.addNewGodebug(key, value) + } + return nil +} + +// addNewGodebug adds a new godebug key=value line at the end +// of the last godebug block, regardless of any existing godebug lines for key. +func (f *WorkFile) addNewGodebug(key, value string) { + line := f.Syntax.addLine(nil, "godebug", key+"="+value) + g := &Godebug{ + Key: key, + Value: value, + Syntax: line, + } + f.Godebug = append(f.Godebug, g) +} + +func (f *WorkFile) DropGodebug(key string) error { + for _, g := range f.Godebug { + if g.Key == key { + g.Syntax.markRemoved() + *g = Godebug{} + } + } + return nil +} + func (f *WorkFile) AddUse(diskPath, modulePath string) error { need := true for _, d := range f.Use { diff --git a/modfile/work_test.go b/modfile/work_test.go index dcc0810..b4b4e7e 100644 --- a/modfile/work_test.go +++ b/modfile/work_test.go @@ -352,6 +352,34 @@ func TestWorkSortBlocks(t *testing.T) { } } +func TestWorkAddGodebug(t *testing.T) { + for _, tt := range addGodebugTests { + t.Run(tt.desc, func(t *testing.T) { + in := strings.ReplaceAll(tt.in, "module m", "use foo") + out := strings.ReplaceAll(tt.out, "module m", "use foo") + testWorkEdit(t, in, out, func(f *WorkFile) error { + err := f.AddGodebug(tt.key, tt.value) + f.Cleanup() + return err + }) + }) + } +} + +func TestWorkDropGodebug(t *testing.T) { + for _, tt := range dropGodebugTests { + t.Run(tt.desc, func(t *testing.T) { + in := strings.ReplaceAll(tt.in, "module m", "use foo") + out := strings.ReplaceAll(tt.out, "module m", "use foo") + testWorkEdit(t, in, out, func(f *WorkFile) error { + f.DropGodebug(tt.key) + f.Cleanup() + return nil + }) + }) + } +} + // Test that when files in the testdata directory are parsed // and printed and parsed again, we get the same parse tree // both times.