[release-branch.go1.17] modfile: in SetRequireSeparateIndirect, convert lines to blocks When reading go.mod, SetRequireSeparateIndirect will insert new requirements into the last uncommented direct-only or indirect-only block OR line. If the last such statement is a line, SetRequireSeparateIndirect converts it to a block before inserting new requirements. Cleanup will convert it back to a line later if no requirements are inserted. For golang/go#47756 Change-Id: Id8ee3b0ce2d005488ddb3d9a5349115bd93938e7 Reviewed-on: https://go-review.googlesource.com/c/mod/+/348576 Trust: Jay Conrod <jayconrod@google.com> Run-TryBot: Jay Conrod <jayconrod@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Bryan C. Mills <bcmills@google.com> Reviewed-by: Michael Matloob <matloob@golang.org> (cherry picked from commit 37dd6891021a152661f432ba5349c6527c243c02) Reviewed-on: https://go-review.googlesource.com/c/mod/+/351318
diff --git a/modfile/rule.go b/modfile/rule.go index d53d7a8..ca03e70 100644 --- a/modfile/rule.go +++ b/modfile/rule.go
@@ -1009,9 +1009,8 @@ // We may insert new requirements into the last uncommented // direct-only and indirect-only blocks. We may also move requirements // to the opposite block if their indirect markings change. - lastDirectBlock, lastIndirectBlock *LineBlock - lastDirectBlockIndex = -1 - lastIndirectBlockIndex = -1 + lastDirectIndex = -1 + lastIndirectIndex = -1 // If there are no direct-only or indirect-only blocks, a new block may // be inserted after the last require line or block. @@ -1033,6 +1032,13 @@ } lastRequireIndex = i requireLineOrBlockCount++ + if !hasComments(stmt.Comments) { + if isIndirect(stmt) { + lastIndirectIndex = i + } else { + lastDirectIndex = i + } + } case *LineBlock: if len(stmt.Token) == 0 || stmt.Token[0] != "require" { @@ -1054,12 +1060,10 @@ } } if allDirect { - lastDirectBlock = stmt - lastDirectBlockIndex = i + lastDirectIndex = i } if allIndirect { - lastIndirectBlock = stmt - lastIndirectBlockIndex = i + lastIndirectIndex = i } } } @@ -1067,33 +1071,56 @@ oneFlatUncommentedBlock := requireLineOrBlockCount == 1 && !hasComments(*f.Syntax.Stmt[lastRequireIndex].Comment()) - // Create direct and indirect blocks if needed. It's okay if they're empty. - // Cleanup will remove them and convert one-line blocks to lines. - if lastDirectBlock == nil { - lastDirectBlock = &LineBlock{Token: []string{"require"}} - i := len(f.Syntax.Stmt) - if lastIndirectBlockIndex >= 0 { - i = lastIndirectBlockIndex - lastIndirectBlockIndex++ - } else if lastRequireIndex >= 0 { - i = lastRequireIndex + 1 - } + // Create direct and indirect blocks if needed. Convert lines into blocks + // if needed. If we end up with an empty block or a one-line block, + // Cleanup will delete it or convert it to a line later. + insertBlock := func(i int) *LineBlock { + block := &LineBlock{Token: []string{"require"}} f.Syntax.Stmt = append(f.Syntax.Stmt, nil) copy(f.Syntax.Stmt[i+1:], f.Syntax.Stmt[i:]) - f.Syntax.Stmt[i] = lastDirectBlock - lastDirectBlockIndex = i + f.Syntax.Stmt[i] = block + return block } - if lastIndirectBlock == nil { - lastIndirectBlock = &LineBlock{Token: []string{"require"}} - i := len(f.Syntax.Stmt) - if lastDirectBlockIndex >= 0 { - i = lastDirectBlockIndex + 1 + ensureBlock := func(i int) *LineBlock { + switch stmt := f.Syntax.Stmt[i].(type) { + case *LineBlock: + return stmt + case *Line: + block := &LineBlock{ + Token: []string{"require"}, + Line: []*Line{stmt}, + } + stmt.Token = stmt.Token[1:] // remove "require" + stmt.InBlock = true + f.Syntax.Stmt[i] = block + return block + default: + panic(fmt.Sprintf("unexpected statement: %v", stmt)) } - f.Syntax.Stmt = append(f.Syntax.Stmt, nil) - copy(f.Syntax.Stmt[i+1:], f.Syntax.Stmt[i:]) - f.Syntax.Stmt[i] = lastIndirectBlock - lastIndirectBlockIndex = i + } + + var lastDirectBlock *LineBlock + if lastDirectIndex < 0 { + if lastIndirectIndex >= 0 { + lastDirectIndex = lastIndirectIndex + lastIndirectIndex++ + } else if lastRequireIndex >= 0 { + lastDirectIndex = lastRequireIndex + 1 + } else { + lastDirectIndex = len(f.Syntax.Stmt) + } + lastDirectBlock = insertBlock(lastDirectIndex) + } else { + lastDirectBlock = ensureBlock(lastDirectIndex) + } + + var lastIndirectBlock *LineBlock + if lastIndirectIndex < 0 { + lastIndirectIndex = lastDirectIndex + 1 + lastIndirectBlock = insertBlock(lastIndirectIndex) + } else { + lastIndirectBlock = ensureBlock(lastIndirectIndex) } // Delete requirements we don't want anymore.
diff --git a/modfile/rule_test.go b/modfile/rule_test.go index 93dfae0..21cea13 100644 --- a/modfile/rule_test.go +++ b/modfile/rule_test.go
@@ -340,6 +340,28 @@ `, }, { + `existing_line`, + `module m + require x.y/a v1.0.0 + require x.y/c v1.0.0 // indirect + `, + []require{ + {"x.y/a", "v1.2.3", false}, + {"x.y/b", "v1.2.3", false}, + {"x.y/c", "v1.2.3", true}, + {"x.y/d", "v1.2.3", true}, + }, + `module m + require ( + x.y/a v1.2.3 + x.y/b v1.2.3 + ) + require ( + x.y/c v1.2.3 // indirect + x.y/d v1.2.3 // indirect + )`, + }, + { `existing_multi`, `module m require x.y/a v1.2.3 @@ -366,7 +388,10 @@ {"x.y/g", "v1.2.3", false}, }, `module m - require x.y/a v1.2.3 + require ( + x.y/a v1.2.3 + x.y/h v1.2.3 + ) require x.y/b v1.2.3 // indirect; demoted to indirect require x.y/c v1.2.3 // not v1.2.3! require x.y/d v1.2.3 // comment kept @@ -376,7 +401,6 @@ require x.y/g v1.2.3 require x.y/i v1.2.3 // indirect require x.y/j v1.2.3 // indirect - require x.y/h v1.2.3 `, }, {