Move gocovmerge as vendor (#10947)
* Move gocovmerge as vendor * Update Makefile Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									14c97c0501
								
							
						
					
					
						commit
						b264c30394
					
				
					 6 changed files with 382 additions and 4 deletions
				
			
		
							
								
								
									
										5
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								Makefile
									
									
									
									
									
								
							|  | @ -293,10 +293,7 @@ test\#%: | ||||||
| 
 | 
 | ||||||
| .PHONY: coverage | .PHONY: coverage | ||||||
| coverage: | coverage: | ||||||
| 	@hash gocovmerge > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
 | 	GO111MODULE=on $(GO) run -mod=vendor build/gocovmerge.go integration.coverage.out $(shell find . -type f -name "coverage.out") > coverage.all | ||||||
| 		$(GO) get -u github.com/wadey/gocovmerge; \
 |  | ||||||
| 	fi |  | ||||||
| 	gocovmerge integration.coverage.out $(shell find . -type f -name "coverage.out") > coverage.all;\
 |  | ||||||
| 
 | 
 | ||||||
| .PHONY: unit-test-coverage | .PHONY: unit-test-coverage | ||||||
| unit-test-coverage: | unit-test-coverage: | ||||||
|  |  | ||||||
							
								
								
									
										119
									
								
								build/gocovmerge.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								build/gocovmerge.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,119 @@ | ||||||
|  | // Copyright 2020 The Gitea Authors. All rights reserved.
 | ||||||
|  | // Copyright (c) 2015, Wade Simmons
 | ||||||
|  | // Use of this source code is governed by a MIT-style
 | ||||||
|  | // license that can be found in the LICENSE file.
 | ||||||
|  | 
 | ||||||
|  | // gocovmerge takes the results from multiple `go test -coverprofile` runs and
 | ||||||
|  | // merges them into one profile
 | ||||||
|  | 
 | ||||||
|  | // +build ignore
 | ||||||
|  | 
 | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"flag" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"log" | ||||||
|  | 	"os" | ||||||
|  | 	"sort" | ||||||
|  | 
 | ||||||
|  | 	"golang.org/x/tools/cover" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func mergeProfiles(p *cover.Profile, merge *cover.Profile) { | ||||||
|  | 	if p.Mode != merge.Mode { | ||||||
|  | 		log.Fatalf("cannot merge profiles with different modes") | ||||||
|  | 	} | ||||||
|  | 	// Since the blocks are sorted, we can keep track of where the last block
 | ||||||
|  | 	// was inserted and only look at the blocks after that as targets for merge
 | ||||||
|  | 	startIndex := 0 | ||||||
|  | 	for _, b := range merge.Blocks { | ||||||
|  | 		startIndex = mergeProfileBlock(p, b, startIndex) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func mergeProfileBlock(p *cover.Profile, pb cover.ProfileBlock, startIndex int) int { | ||||||
|  | 	sortFunc := func(i int) bool { | ||||||
|  | 		pi := p.Blocks[i+startIndex] | ||||||
|  | 		return pi.StartLine >= pb.StartLine && (pi.StartLine != pb.StartLine || pi.StartCol >= pb.StartCol) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	i := 0 | ||||||
|  | 	if sortFunc(i) != true { | ||||||
|  | 		i = sort.Search(len(p.Blocks)-startIndex, sortFunc) | ||||||
|  | 	} | ||||||
|  | 	i += startIndex | ||||||
|  | 	if i < len(p.Blocks) && p.Blocks[i].StartLine == pb.StartLine && p.Blocks[i].StartCol == pb.StartCol { | ||||||
|  | 		if p.Blocks[i].EndLine != pb.EndLine || p.Blocks[i].EndCol != pb.EndCol { | ||||||
|  | 			log.Fatalf("OVERLAP MERGE: %v %v %v", p.FileName, p.Blocks[i], pb) | ||||||
|  | 		} | ||||||
|  | 		switch p.Mode { | ||||||
|  | 		case "set": | ||||||
|  | 			p.Blocks[i].Count |= pb.Count | ||||||
|  | 		case "count", "atomic": | ||||||
|  | 			p.Blocks[i].Count += pb.Count | ||||||
|  | 		default: | ||||||
|  | 			log.Fatalf("unsupported covermode: '%s'", p.Mode) | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		if i > 0 { | ||||||
|  | 			pa := p.Blocks[i-1] | ||||||
|  | 			if pa.EndLine >= pb.EndLine && (pa.EndLine != pb.EndLine || pa.EndCol > pb.EndCol) { | ||||||
|  | 				log.Fatalf("OVERLAP BEFORE: %v %v %v", p.FileName, pa, pb) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if i < len(p.Blocks)-1 { | ||||||
|  | 			pa := p.Blocks[i+1] | ||||||
|  | 			if pa.StartLine <= pb.StartLine && (pa.StartLine != pb.StartLine || pa.StartCol < pb.StartCol) { | ||||||
|  | 				log.Fatalf("OVERLAP AFTER: %v %v %v", p.FileName, pa, pb) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		p.Blocks = append(p.Blocks, cover.ProfileBlock{}) | ||||||
|  | 		copy(p.Blocks[i+1:], p.Blocks[i:]) | ||||||
|  | 		p.Blocks[i] = pb | ||||||
|  | 	} | ||||||
|  | 	return i + 1 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func addProfile(profiles []*cover.Profile, p *cover.Profile) []*cover.Profile { | ||||||
|  | 	i := sort.Search(len(profiles), func(i int) bool { return profiles[i].FileName >= p.FileName }) | ||||||
|  | 	if i < len(profiles) && profiles[i].FileName == p.FileName { | ||||||
|  | 		mergeProfiles(profiles[i], p) | ||||||
|  | 	} else { | ||||||
|  | 		profiles = append(profiles, nil) | ||||||
|  | 		copy(profiles[i+1:], profiles[i:]) | ||||||
|  | 		profiles[i] = p | ||||||
|  | 	} | ||||||
|  | 	return profiles | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func dumpProfiles(profiles []*cover.Profile, out io.Writer) { | ||||||
|  | 	if len(profiles) == 0 { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	fmt.Fprintf(out, "mode: %s\n", profiles[0].Mode) | ||||||
|  | 	for _, p := range profiles { | ||||||
|  | 		for _, b := range p.Blocks { | ||||||
|  | 			fmt.Fprintf(out, "%s:%d.%d,%d.%d %d %d\n", p.FileName, b.StartLine, b.StartCol, b.EndLine, b.EndCol, b.NumStmt, b.Count) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func main() { | ||||||
|  | 	flag.Parse() | ||||||
|  | 
 | ||||||
|  | 	var merged []*cover.Profile | ||||||
|  | 
 | ||||||
|  | 	for _, file := range flag.Args() { | ||||||
|  | 		profiles, err := cover.ParseProfiles(file) | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Fatalf("failed to parse profiles: %v", err) | ||||||
|  | 		} | ||||||
|  | 		for _, p := range profiles { | ||||||
|  | 			merged = addProfile(merged, p) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	dumpProfiles(merged, os.Stdout) | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								build/vendor.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								build/vendor.go
									
									
									
									
										vendored
									
									
								
							|  | @ -15,4 +15,7 @@ import ( | ||||||
| 
 | 
 | ||||||
| 	// for embed
 | 	// for embed
 | ||||||
| 	_ "github.com/shurcooL/vfsgen" | 	_ "github.com/shurcooL/vfsgen" | ||||||
|  | 
 | ||||||
|  | 	// for cover merge
 | ||||||
|  | 	_ "golang.org/x/tools/cover" | ||||||
| ) | ) | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							|  | @ -111,6 +111,7 @@ require ( | ||||||
| 	golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 | 	golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 | ||||||
| 	golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 | 	golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 | ||||||
| 	golang.org/x/text v0.3.2 | 	golang.org/x/text v0.3.2 | ||||||
|  | 	golang.org/x/tools v0.0.0-20200225230052-807dcd883420 | ||||||
| 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect | 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect | ||||||
| 	gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect | 	gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect | ||||||
| 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df | 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df | ||||||
|  |  | ||||||
							
								
								
									
										256
									
								
								vendor/golang.org/x/tools/cover/profile.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								vendor/golang.org/x/tools/cover/profile.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,256 @@ | ||||||
|  | // Copyright 2013 The Go Authors. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by a BSD-style
 | ||||||
|  | // license that can be found in the LICENSE file.
 | ||||||
|  | 
 | ||||||
|  | // Package cover provides support for parsing coverage profiles
 | ||||||
|  | // generated by "go test -coverprofile=cover.out".
 | ||||||
|  | package cover // import "golang.org/x/tools/cover"
 | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bufio" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"math" | ||||||
|  | 	"os" | ||||||
|  | 	"sort" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Profile represents the profiling data for a specific file.
 | ||||||
|  | type Profile struct { | ||||||
|  | 	FileName string | ||||||
|  | 	Mode     string | ||||||
|  | 	Blocks   []ProfileBlock | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ProfileBlock represents a single block of profiling data.
 | ||||||
|  | type ProfileBlock struct { | ||||||
|  | 	StartLine, StartCol int | ||||||
|  | 	EndLine, EndCol     int | ||||||
|  | 	NumStmt, Count      int | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type byFileName []*Profile | ||||||
|  | 
 | ||||||
|  | func (p byFileName) Len() int           { return len(p) } | ||||||
|  | func (p byFileName) Less(i, j int) bool { return p[i].FileName < p[j].FileName } | ||||||
|  | func (p byFileName) Swap(i, j int)      { p[i], p[j] = p[j], p[i] } | ||||||
|  | 
 | ||||||
|  | // ParseProfiles parses profile data in the specified file and returns a
 | ||||||
|  | // Profile for each source file described therein.
 | ||||||
|  | func ParseProfiles(fileName string) ([]*Profile, error) { | ||||||
|  | 	pf, err := os.Open(fileName) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	defer pf.Close() | ||||||
|  | 
 | ||||||
|  | 	files := make(map[string]*Profile) | ||||||
|  | 	buf := bufio.NewReader(pf) | ||||||
|  | 	// First line is "mode: foo", where foo is "set", "count", or "atomic".
 | ||||||
|  | 	// Rest of file is in the format
 | ||||||
|  | 	//	encoding/base64/base64.go:34.44,37.40 3 1
 | ||||||
|  | 	// where the fields are: name.go:line.column,line.column numberOfStatements count
 | ||||||
|  | 	s := bufio.NewScanner(buf) | ||||||
|  | 	mode := "" | ||||||
|  | 	for s.Scan() { | ||||||
|  | 		line := s.Text() | ||||||
|  | 		if mode == "" { | ||||||
|  | 			const p = "mode: " | ||||||
|  | 			if !strings.HasPrefix(line, p) || line == p { | ||||||
|  | 				return nil, fmt.Errorf("bad mode line: %v", line) | ||||||
|  | 			} | ||||||
|  | 			mode = line[len(p):] | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		fn, b, err := parseLine(line) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, fmt.Errorf("line %q doesn't match expected format: %v", line, err) | ||||||
|  | 		} | ||||||
|  | 		p := files[fn] | ||||||
|  | 		if p == nil { | ||||||
|  | 			p = &Profile{ | ||||||
|  | 				FileName: fn, | ||||||
|  | 				Mode:     mode, | ||||||
|  | 			} | ||||||
|  | 			files[fn] = p | ||||||
|  | 		} | ||||||
|  | 		p.Blocks = append(p.Blocks, b) | ||||||
|  | 	} | ||||||
|  | 	if err := s.Err(); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	for _, p := range files { | ||||||
|  | 		sort.Sort(blocksByStart(p.Blocks)) | ||||||
|  | 		// Merge samples from the same location.
 | ||||||
|  | 		j := 1 | ||||||
|  | 		for i := 1; i < len(p.Blocks); i++ { | ||||||
|  | 			b := p.Blocks[i] | ||||||
|  | 			last := p.Blocks[j-1] | ||||||
|  | 			if b.StartLine == last.StartLine && | ||||||
|  | 				b.StartCol == last.StartCol && | ||||||
|  | 				b.EndLine == last.EndLine && | ||||||
|  | 				b.EndCol == last.EndCol { | ||||||
|  | 				if b.NumStmt != last.NumStmt { | ||||||
|  | 					return nil, fmt.Errorf("inconsistent NumStmt: changed from %d to %d", last.NumStmt, b.NumStmt) | ||||||
|  | 				} | ||||||
|  | 				if mode == "set" { | ||||||
|  | 					p.Blocks[j-1].Count |= b.Count | ||||||
|  | 				} else { | ||||||
|  | 					p.Blocks[j-1].Count += b.Count | ||||||
|  | 				} | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			p.Blocks[j] = b | ||||||
|  | 			j++ | ||||||
|  | 		} | ||||||
|  | 		p.Blocks = p.Blocks[:j] | ||||||
|  | 	} | ||||||
|  | 	// Generate a sorted slice.
 | ||||||
|  | 	profiles := make([]*Profile, 0, len(files)) | ||||||
|  | 	for _, profile := range files { | ||||||
|  | 		profiles = append(profiles, profile) | ||||||
|  | 	} | ||||||
|  | 	sort.Sort(byFileName(profiles)) | ||||||
|  | 	return profiles, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // parseLine parses a line from a coverage file.
 | ||||||
|  | // It is equivalent to the regex
 | ||||||
|  | // ^(.+):([0-9]+)\.([0-9]+),([0-9]+)\.([0-9]+) ([0-9]+) ([0-9]+)$
 | ||||||
|  | //
 | ||||||
|  | // However, it is much faster: https://golang.org/cl/179377
 | ||||||
|  | func parseLine(l string) (fileName string, block ProfileBlock, err error) { | ||||||
|  | 	end := len(l) | ||||||
|  | 
 | ||||||
|  | 	b := ProfileBlock{} | ||||||
|  | 	b.Count, end, err = seekBack(l, ' ', end, "Count") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", b, err | ||||||
|  | 	} | ||||||
|  | 	b.NumStmt, end, err = seekBack(l, ' ', end, "NumStmt") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", b, err | ||||||
|  | 	} | ||||||
|  | 	b.EndCol, end, err = seekBack(l, '.', end, "EndCol") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", b, err | ||||||
|  | 	} | ||||||
|  | 	b.EndLine, end, err = seekBack(l, ',', end, "EndLine") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", b, err | ||||||
|  | 	} | ||||||
|  | 	b.StartCol, end, err = seekBack(l, '.', end, "StartCol") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", b, err | ||||||
|  | 	} | ||||||
|  | 	b.StartLine, end, err = seekBack(l, ':', end, "StartLine") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", b, err | ||||||
|  | 	} | ||||||
|  | 	fn := l[0:end] | ||||||
|  | 	if fn == "" { | ||||||
|  | 		return "", b, errors.New("a FileName cannot be blank") | ||||||
|  | 	} | ||||||
|  | 	return fn, b, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // seekBack searches backwards from end to find sep in l, then returns the
 | ||||||
|  | // value between sep and end as an integer.
 | ||||||
|  | // If seekBack fails, the returned error will reference what.
 | ||||||
|  | func seekBack(l string, sep byte, end int, what string) (value int, nextSep int, err error) { | ||||||
|  | 	// Since we're seeking backwards and we know only ASCII is legal for these values,
 | ||||||
|  | 	// we can ignore the possibility of non-ASCII characters.
 | ||||||
|  | 	for start := end - 1; start >= 0; start-- { | ||||||
|  | 		if l[start] == sep { | ||||||
|  | 			i, err := strconv.Atoi(l[start+1 : end]) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return 0, 0, fmt.Errorf("couldn't parse %q: %v", what, err) | ||||||
|  | 			} | ||||||
|  | 			if i < 0 { | ||||||
|  | 				return 0, 0, fmt.Errorf("negative values are not allowed for %s, found %d", what, i) | ||||||
|  | 			} | ||||||
|  | 			return i, start, nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return 0, 0, fmt.Errorf("couldn't find a %s before %s", string(sep), what) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type blocksByStart []ProfileBlock | ||||||
|  | 
 | ||||||
|  | func (b blocksByStart) Len() int      { return len(b) } | ||||||
|  | func (b blocksByStart) Swap(i, j int) { b[i], b[j] = b[j], b[i] } | ||||||
|  | func (b blocksByStart) Less(i, j int) bool { | ||||||
|  | 	bi, bj := b[i], b[j] | ||||||
|  | 	return bi.StartLine < bj.StartLine || bi.StartLine == bj.StartLine && bi.StartCol < bj.StartCol | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Boundary represents the position in a source file of the beginning or end of a
 | ||||||
|  | // block as reported by the coverage profile. In HTML mode, it will correspond to
 | ||||||
|  | // the opening or closing of a <span> tag and will be used to colorize the source
 | ||||||
|  | type Boundary struct { | ||||||
|  | 	Offset int     // Location as a byte offset in the source file.
 | ||||||
|  | 	Start  bool    // Is this the start of a block?
 | ||||||
|  | 	Count  int     // Event count from the cover profile.
 | ||||||
|  | 	Norm   float64 // Count normalized to [0..1].
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Boundaries returns a Profile as a set of Boundary objects within the provided src.
 | ||||||
|  | func (p *Profile) Boundaries(src []byte) (boundaries []Boundary) { | ||||||
|  | 	// Find maximum count.
 | ||||||
|  | 	max := 0 | ||||||
|  | 	for _, b := range p.Blocks { | ||||||
|  | 		if b.Count > max { | ||||||
|  | 			max = b.Count | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// Divisor for normalization.
 | ||||||
|  | 	divisor := math.Log(float64(max)) | ||||||
|  | 
 | ||||||
|  | 	// boundary returns a Boundary, populating the Norm field with a normalized Count.
 | ||||||
|  | 	boundary := func(offset int, start bool, count int) Boundary { | ||||||
|  | 		b := Boundary{Offset: offset, Start: start, Count: count} | ||||||
|  | 		if !start || count == 0 { | ||||||
|  | 			return b | ||||||
|  | 		} | ||||||
|  | 		if max <= 1 { | ||||||
|  | 			b.Norm = 0.8 // Profile is in"set" mode; we want a heat map. Use cov8 in the CSS.
 | ||||||
|  | 		} else if count > 0 { | ||||||
|  | 			b.Norm = math.Log(float64(count)) / divisor | ||||||
|  | 		} | ||||||
|  | 		return b | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	line, col := 1, 2 // TODO: Why is this 2?
 | ||||||
|  | 	for si, bi := 0, 0; si < len(src) && bi < len(p.Blocks); { | ||||||
|  | 		b := p.Blocks[bi] | ||||||
|  | 		if b.StartLine == line && b.StartCol == col { | ||||||
|  | 			boundaries = append(boundaries, boundary(si, true, b.Count)) | ||||||
|  | 		} | ||||||
|  | 		if b.EndLine == line && b.EndCol == col || line > b.EndLine { | ||||||
|  | 			boundaries = append(boundaries, boundary(si, false, 0)) | ||||||
|  | 			bi++ | ||||||
|  | 			continue // Don't advance through src; maybe the next block starts here.
 | ||||||
|  | 		} | ||||||
|  | 		if src[si] == '\n' { | ||||||
|  | 			line++ | ||||||
|  | 			col = 0 | ||||||
|  | 		} | ||||||
|  | 		col++ | ||||||
|  | 		si++ | ||||||
|  | 	} | ||||||
|  | 	sort.Sort(boundariesByPos(boundaries)) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type boundariesByPos []Boundary | ||||||
|  | 
 | ||||||
|  | func (b boundariesByPos) Len() int      { return len(b) } | ||||||
|  | func (b boundariesByPos) Swap(i, j int) { b[i], b[j] = b[j], b[i] } | ||||||
|  | func (b boundariesByPos) Less(i, j int) bool { | ||||||
|  | 	if b[i].Offset == b[j].Offset { | ||||||
|  | 		return !b[i].Start && b[j].Start | ||||||
|  | 	} | ||||||
|  | 	return b[i].Offset < b[j].Offset | ||||||
|  | } | ||||||
							
								
								
									
										2
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							|  | @ -732,6 +732,8 @@ golang.org/x/text/unicode/bidi | ||||||
| golang.org/x/text/unicode/norm | golang.org/x/text/unicode/norm | ||||||
| golang.org/x/text/width | golang.org/x/text/width | ||||||
| # golang.org/x/tools v0.0.0-20200225230052-807dcd883420 | # golang.org/x/tools v0.0.0-20200225230052-807dcd883420 | ||||||
|  | ## explicit | ||||||
|  | golang.org/x/tools/cover | ||||||
| golang.org/x/tools/go/ast/astutil | golang.org/x/tools/go/ast/astutil | ||||||
| golang.org/x/tools/go/buildutil | golang.org/x/tools/go/buildutil | ||||||
| golang.org/x/tools/go/gcexportdata | golang.org/x/tools/go/gcexportdata | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue