Skip to content

Commit 22458ad

Browse files
author
Bryan C. Mills
committed
modfile: factor out methods for changing Require properties
For golang/go#45965 Change-Id: I331d068d115b145239933da0d8341a1627124935 Reviewed-on: https://go-review.googlesource.com/c/mod/+/325970 Trust: Bryan C. Mills <bcmills@google.com> Run-TryBot: Bryan C. Mills <bcmills@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Michael Matloob <matloob@golang.org>
1 parent 9eb10b1 commit 22458ad

File tree

1 file changed

+109
-98
lines changed

1 file changed

+109
-98
lines changed

modfile/rule.go

+109-98
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,6 @@ type Go struct {
5858
Syntax *Line
5959
}
6060

61-
// A Require is a single require statement.
62-
type Require struct {
63-
Mod module.Version
64-
Indirect bool // has "// indirect" comment
65-
Syntax *Line
66-
}
67-
6861
// An Exclude is a single exclude statement.
6962
type Exclude struct {
7063
Mod module.Version
@@ -93,6 +86,93 @@ type VersionInterval struct {
9386
Low, High string
9487
}
9588

89+
// A Require is a single require statement.
90+
type Require struct {
91+
Mod module.Version
92+
Indirect bool // has "// indirect" comment
93+
Syntax *Line
94+
}
95+
96+
func (r *Require) markRemoved() {
97+
r.Syntax.markRemoved()
98+
*r = Require{}
99+
}
100+
101+
func (r *Require) setVersion(v string) {
102+
r.Mod.Version = v
103+
104+
if line := r.Syntax; len(line.Token) > 0 {
105+
if line.InBlock {
106+
// If the line is preceded by an empty line, remove it; see
107+
// https://golang.org/issue/33779.
108+
if len(line.Comments.Before) == 1 && len(line.Comments.Before[0].Token) == 0 {
109+
line.Comments.Before = line.Comments.Before[:0]
110+
}
111+
if len(line.Token) >= 2 { // example.com v1.2.3
112+
line.Token[1] = v
113+
}
114+
} else {
115+
if len(line.Token) >= 3 { // require example.com v1.2.3
116+
line.Token[2] = v
117+
}
118+
}
119+
}
120+
}
121+
122+
// setIndirect sets line to have (or not have) a "// indirect" comment.
123+
func (r *Require) setIndirect(indirect bool) {
124+
r.Indirect = indirect
125+
line := r.Syntax
126+
if isIndirect(line) == indirect {
127+
return
128+
}
129+
if indirect {
130+
// Adding comment.
131+
if len(line.Suffix) == 0 {
132+
// New comment.
133+
line.Suffix = []Comment{{Token: "// indirect", Suffix: true}}
134+
return
135+
}
136+
137+
com := &line.Suffix[0]
138+
text := strings.TrimSpace(strings.TrimPrefix(com.Token, string(slashSlash)))
139+
if text == "" {
140+
// Empty comment.
141+
com.Token = "// indirect"
142+
return
143+
}
144+
145+
// Insert at beginning of existing comment.
146+
com.Token = "// indirect; " + text
147+
return
148+
}
149+
150+
// Removing comment.
151+
f := strings.TrimSpace(strings.TrimPrefix(line.Suffix[0].Token, string(slashSlash)))
152+
if f == "indirect" {
153+
// Remove whole comment.
154+
line.Suffix = nil
155+
return
156+
}
157+
158+
// Remove comment prefix.
159+
com := &line.Suffix[0]
160+
i := strings.Index(com.Token, "indirect;")
161+
com.Token = "//" + com.Token[i+len("indirect;"):]
162+
}
163+
164+
// isIndirect reports whether line has a "// indirect" comment,
165+
// meaning it is in go.mod only for its effect on indirect dependencies,
166+
// so that it can be dropped entirely once the effective version of the
167+
// indirect dependency reaches the given minimum version.
168+
func isIndirect(line *Line) bool {
169+
if len(line.Suffix) == 0 {
170+
return false
171+
}
172+
f := strings.Fields(strings.TrimPrefix(line.Suffix[0].Token, string(slashSlash)))
173+
return (len(f) == 1 && f[0] == "indirect" || len(f) > 1 && f[0] == "indirect;")
174+
}
175+
96176
func (f *File) AddModuleStmt(path string) error {
97177
if f.Syntax == nil {
98178
f.Syntax = new(FileSyntax)
@@ -476,58 +556,6 @@ func (f *File) fixRetract(fix VersionFixer, errs *ErrorList) {
476556
}
477557
}
478558

479-
// isIndirect reports whether line has a "// indirect" comment,
480-
// meaning it is in go.mod only for its effect on indirect dependencies,
481-
// so that it can be dropped entirely once the effective version of the
482-
// indirect dependency reaches the given minimum version.
483-
func isIndirect(line *Line) bool {
484-
if len(line.Suffix) == 0 {
485-
return false
486-
}
487-
f := strings.Fields(strings.TrimPrefix(line.Suffix[0].Token, string(slashSlash)))
488-
return (len(f) == 1 && f[0] == "indirect" || len(f) > 1 && f[0] == "indirect;")
489-
}
490-
491-
// setIndirect sets line to have (or not have) a "// indirect" comment.
492-
func setIndirect(line *Line, indirect bool) {
493-
if isIndirect(line) == indirect {
494-
return
495-
}
496-
if indirect {
497-
// Adding comment.
498-
if len(line.Suffix) == 0 {
499-
// New comment.
500-
line.Suffix = []Comment{{Token: "// indirect", Suffix: true}}
501-
return
502-
}
503-
504-
com := &line.Suffix[0]
505-
text := strings.TrimSpace(strings.TrimPrefix(com.Token, string(slashSlash)))
506-
if text == "" {
507-
// Empty comment.
508-
com.Token = "// indirect"
509-
return
510-
}
511-
512-
// Insert at beginning of existing comment.
513-
com.Token = "// indirect; " + text
514-
return
515-
}
516-
517-
// Removing comment.
518-
f := strings.TrimSpace(strings.TrimPrefix(line.Suffix[0].Token, string(slashSlash)))
519-
if f == "indirect" {
520-
// Remove whole comment.
521-
line.Suffix = nil
522-
return
523-
}
524-
525-
// Remove comment prefix.
526-
com := &line.Suffix[0]
527-
i := strings.Index(com.Token, "indirect;")
528-
com.Token = "//" + com.Token[i+len("indirect;"):]
529-
}
530-
531559
// IsDirectoryPath reports whether the given path should be interpreted
532560
// as a directory path. Just like on the go command line, relative paths
533561
// and rooted paths are directory paths; the rest are module paths.
@@ -866,8 +894,12 @@ func (f *File) AddRequire(path, vers string) error {
866894
// the last require block, regardless of any existing require lines for path.
867895
func (f *File) AddNewRequire(path, vers string, indirect bool) {
868896
line := f.Syntax.addLine(nil, "require", AutoQuote(path), vers)
869-
setIndirect(line, indirect)
870-
f.Require = append(f.Require, &Require{module.Version{Path: path, Version: vers}, indirect, line})
897+
r := &Require{
898+
Mod: module.Version{Path: path, Version: vers},
899+
Syntax: line,
900+
}
901+
r.setIndirect(indirect)
902+
f.Require = append(f.Require, r)
871903
}
872904

873905
// SetRequire updates the requirements of f to contain exactly req, preserving
@@ -885,49 +917,28 @@ func (f *File) AddNewRequire(path, vers string, indirect bool) {
885917
// If any existing requirements may be removed, the caller should call Cleanup
886918
// after all edits are complete.
887919
func (f *File) SetRequire(req []*Require) {
888-
need := make(map[string]string)
889-
indirect := make(map[string]bool)
920+
type elem struct {
921+
version string
922+
indirect bool
923+
}
924+
need := make(map[string]elem)
890925
for _, r := range req {
891-
if prev, dup := need[r.Mod.Path]; dup && prev != r.Mod.Version {
892-
panic(fmt.Errorf("SetRequire called with conflicting versions for path %s (%s and %s)", r.Mod.Path, prev, r.Mod.Version))
926+
if prev, dup := need[r.Mod.Path]; dup && prev.version != r.Mod.Version {
927+
panic(fmt.Errorf("SetRequire called with conflicting versions for path %s (%s and %s)", r.Mod.Path, prev.version, r.Mod.Version))
893928
}
894-
need[r.Mod.Path] = r.Mod.Version
895-
indirect[r.Mod.Path] = r.Indirect
929+
need[r.Mod.Path] = elem{r.Mod.Version, r.Indirect}
896930
}
897931

898932
// Update or delete the existing Require entries to preserve
899933
// only the first for each module path in req.
900934
for _, r := range f.Require {
901-
v, ok := need[r.Mod.Path]
902-
if !ok {
903-
// This line is redundant or its path is no longer required at all.
904-
// Mark the requirement for deletion in Cleanup.
905-
r.Syntax.markRemoved()
906-
*r = Require{}
907-
}
908-
909-
r.Mod.Version = v
910-
r.Indirect = indirect[r.Mod.Path]
911-
912-
if line := r.Syntax; line != nil && len(line.Token) > 0 {
913-
if line.InBlock {
914-
// If the line is preceded by an empty line, remove it; see
915-
// https://golang.org/issue/33779.
916-
if len(line.Comments.Before) == 1 && len(line.Comments.Before[0].Token) == 0 {
917-
line.Comments.Before = line.Comments.Before[:0]
918-
}
919-
if len(line.Token) >= 2 { // example.com v1.2.3
920-
line.Token[1] = v
921-
}
922-
} else {
923-
if len(line.Token) >= 3 { // require example.com v1.2.3
924-
line.Token[2] = v
925-
}
926-
}
927-
928-
setIndirect(line, r.Indirect)
935+
e, ok := need[r.Mod.Path]
936+
if ok {
937+
r.setVersion(e.version)
938+
r.setIndirect(e.indirect)
939+
} else {
940+
r.markRemoved()
929941
}
930-
931942
delete(need, r.Mod.Path)
932943
}
933944

@@ -936,8 +947,8 @@ func (f *File) SetRequire(req []*Require) {
936947
//
937948
// This step is nondeterministic, but the final result will be deterministic
938949
// because we will sort the block.
939-
for path, vers := range need {
940-
f.AddNewRequire(path, vers, indirect[path])
950+
for path, e := range need {
951+
f.AddNewRequire(path, e.version, e.indirect)
941952
}
942953

943954
f.SortBlocks()

0 commit comments

Comments
 (0)