Skip to content

Commit

Permalink
modfile: use semantic sort for exclude blocks
Browse files Browse the repository at this point in the history
For golang/go#60028.

Change-Id: I4c7a726a900fc7c4b34816eba5cfd0361c45315f
Reviewed-on: https://go-review.googlesource.com/c/mod/+/492990
Run-TryBot: Dmitri Shuralyov <[email protected]>
TryBot-Result: Gopher Robot <[email protected]>
Auto-Submit: Dmitri Shuralyov <[email protected]>
Reviewed-by: Dmitri Shuralyov <[email protected]>
Reviewed-by: Bryan Mills <[email protected]>
  • Loading branch information
dmitshur authored and gopherbot committed May 17, 2023
1 parent ad6fd61 commit e7bea8f
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 1 deletion.
26 changes: 25 additions & 1 deletion modfile/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -1387,13 +1387,21 @@ func (f *File) DropRetract(vi VersionInterval) error {
func (f *File) SortBlocks() {
f.removeDups() // otherwise sorting is unsafe

// semanticSortForExcludeVersionV is the Go version (plus leading "v") at which
// lines in exclude blocks start to use semantic sort instead of lexicographic sort.
// See go.dev/issue/60028.
const semanticSortForExcludeVersionV = "v1.21"
useSemanticSortForExclude := f.Go != nil && semver.Compare("v"+f.Go.Version, semanticSortForExcludeVersionV) >= 0

for _, stmt := range f.Syntax.Stmt {
block, ok := stmt.(*LineBlock)
if !ok {
continue
}
less := lineLess
if block.Token[0] == "retract" {
if block.Token[0] == "exclude" && useSemanticSortForExclude {
less = lineExcludeLess
} else if block.Token[0] == "retract" {
less = lineRetractLess
}
sort.SliceStable(block.Line, func(i, j int) bool {
Expand Down Expand Up @@ -1496,6 +1504,22 @@ func lineLess(li, lj *Line) bool {
return len(li.Token) < len(lj.Token)
}

// lineExcludeLess reports whether li should be sorted before lj for lines in
// an "exclude" block.
func lineExcludeLess(li, lj *Line) bool {
if len(li.Token) != 2 || len(lj.Token) != 2 {
// Not a known exclude specification.
// Fall back to sorting lexicographically.
return lineLess(li, lj)
}
// An exclude specification has two tokens: ModulePath and Version.
// Compare module path by string order and version by semver rules.
if pi, pj := li.Token[0], lj.Token[0]; pi != pj {
return pi < pj
}
return semver.Compare(li.Token[1], lj.Token[1]) < 0
}

// lineRetractLess returns whether li should be sorted before lj for lines in
// a "retract" block. It treats each line as a version interval. Single versions
// are compared as if they were intervals with the same low and high version.
Expand Down
46 changes: 46 additions & 0 deletions modfile/rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1224,6 +1224,52 @@ var sortBlocksTests = []struct {
`,
false,
},
// Exclude blocks are sorted using semver in ascending order
// in go.mod files that opt in to Go version 1.21 or newer.
{
`sort_exclude_go121_semver`,
`module m
go 1.21
exclude (
b.example/m v0.9.0
a.example/m v1.0.0
b.example/m v0.10.0
c.example/m v1.1.0
b.example/m v0.11.0
)`,
`module m
go 1.21
exclude (
a.example/m v1.0.0
b.example/m v0.9.0
b.example/m v0.10.0
b.example/m v0.11.0
c.example/m v1.1.0
)
`,
true,
},
{
`sort_exclude_!go121_lexicographically`, // Maintain the previous (less featureful) behavior to avoid unnecessary churn.
`module m
exclude (
b.example/m v0.9.0
a.example/m v1.0.0
b.example/m v0.10.0
c.example/m v1.1.0
b.example/m v0.11.0
)`,
`module m
exclude (
a.example/m v1.0.0
b.example/m v0.10.0
b.example/m v0.11.0
b.example/m v0.9.0
c.example/m v1.1.0
)
`,
true,
},
}

var addRetractValidateVersionTests = []struct {
Expand Down

0 comments on commit e7bea8f

Please sign in to comment.