1// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package main
6
7import (
8	"bytes"
9	"cmd/internal/cov/covcmd"
10	"encoding/json"
11	"flag"
12	"fmt"
13	"go/ast"
14	"go/parser"
15	"go/token"
16	"internal/coverage"
17	"internal/coverage/encodemeta"
18	"internal/coverage/slicewriter"
19	"io"
20	"log"
21	"os"
22	"path/filepath"
23	"sort"
24	"strconv"
25	"strings"
26
27	"cmd/internal/edit"
28	"cmd/internal/objabi"
29	"cmd/internal/telemetry/counter"
30)
31
32const usageMessage = "" +
33	`Usage of 'go tool cover':
34Given a coverage profile produced by 'go test':
35	go test -coverprofile=c.out
36
37Open a web browser displaying annotated source code:
38	go tool cover -html=c.out
39
40Write out an HTML file instead of launching a web browser:
41	go tool cover -html=c.out -o coverage.html
42
43Display coverage percentages to stdout for each function:
44	go tool cover -func=c.out
45
46Finally, to generate modified source code with coverage annotations
47for a package (what go test -cover does):
48	go tool cover -mode=set -var=CoverageVariableName \
49		-pkgcfg=<config> -outfilelist=<file> file1.go ... fileN.go
50
51where -pkgcfg points to a file containing the package path,
52package name, module path, and related info from "go build",
53and -outfilelist points to a file containing the filenames
54of the instrumented output files (one per input file).
55See https://pkg.go.dev/cmd/internal/cov/covcmd#CoverPkgConfig for
56more on the package config.
57`
58
59func usage() {
60	fmt.Fprint(os.Stderr, usageMessage)
61	fmt.Fprintln(os.Stderr, "\nFlags:")
62	flag.PrintDefaults()
63	fmt.Fprintln(os.Stderr, "\n  Only one of -html, -func, or -mode may be set.")
64	os.Exit(2)
65}
66
67var (
68	mode             = flag.String("mode", "", "coverage mode: set, count, atomic")
69	varVar           = flag.String("var", "GoCover", "name of coverage variable to generate")
70	output           = flag.String("o", "", "file for output")
71	outfilelist      = flag.String("outfilelist", "", "file containing list of output files (one per line) if -pkgcfg is in use")
72	htmlOut          = flag.String("html", "", "generate HTML representation of coverage profile")
73	funcOut          = flag.String("func", "", "output coverage profile information for each function")
74	pkgcfg           = flag.String("pkgcfg", "", "enable full-package instrumentation mode using params from specified config file")
75	pkgconfig        covcmd.CoverPkgConfig
76	outputfiles      []string // list of *.cover.go instrumented outputs to write, one per input (set when -pkgcfg is in use)
77	profile          string   // The profile to read; the value of -html or -func
78	counterStmt      func(*File, string) string
79	covervarsoutfile string // an additional Go source file into which we'll write definitions of coverage counter variables + meta data variables (set when -pkgcfg is in use).
80	cmode            coverage.CounterMode
81	cgran            coverage.CounterGranularity
82)
83
84const (
85	atomicPackagePath = "sync/atomic"
86	atomicPackageName = "_cover_atomic_"
87)
88
89func main() {
90	counter.Open()
91
92	objabi.AddVersionFlag()
93	flag.Usage = usage
94	objabi.Flagparse(usage)
95	counter.Inc("cover/invocations")
96	counter.CountFlags("cover/flag:", *flag.CommandLine)
97
98	// Usage information when no arguments.
99	if flag.NFlag() == 0 && flag.NArg() == 0 {
100		flag.Usage()
101	}
102
103	err := parseFlags()
104	if err != nil {
105		fmt.Fprintln(os.Stderr, err)
106		fmt.Fprintln(os.Stderr, `For usage information, run "go tool cover -help"`)
107		os.Exit(2)
108	}
109
110	// Generate coverage-annotated source.
111	if *mode != "" {
112		annotate(flag.Args())
113		return
114	}
115
116	// Output HTML or function coverage information.
117	if *htmlOut != "" {
118		err = htmlOutput(profile, *output)
119	} else {
120		err = funcOutput(profile, *output)
121	}
122
123	if err != nil {
124		fmt.Fprintf(os.Stderr, "cover: %v\n", err)
125		os.Exit(2)
126	}
127}
128
129// parseFlags sets the profile and counterStmt globals and performs validations.
130func parseFlags() error {
131	profile = *htmlOut
132	if *funcOut != "" {
133		if profile != "" {
134			return fmt.Errorf("too many options")
135		}
136		profile = *funcOut
137	}
138
139	// Must either display a profile or rewrite Go source.
140	if (profile == "") == (*mode == "") {
141		return fmt.Errorf("too many options")
142	}
143
144	if *varVar != "" && !token.IsIdentifier(*varVar) {
145		return fmt.Errorf("-var: %q is not a valid identifier", *varVar)
146	}
147
148	if *mode != "" {
149		switch *mode {
150		case "set":
151			counterStmt = setCounterStmt
152			cmode = coverage.CtrModeSet
153		case "count":
154			counterStmt = incCounterStmt
155			cmode = coverage.CtrModeCount
156		case "atomic":
157			counterStmt = atomicCounterStmt
158			cmode = coverage.CtrModeAtomic
159		case "regonly":
160			counterStmt = nil
161			cmode = coverage.CtrModeRegOnly
162		case "testmain":
163			counterStmt = nil
164			cmode = coverage.CtrModeTestMain
165		default:
166			return fmt.Errorf("unknown -mode %v", *mode)
167		}
168
169		if flag.NArg() == 0 {
170			return fmt.Errorf("missing source file(s)")
171		} else {
172			if *pkgcfg != "" {
173				if *output != "" {
174					return fmt.Errorf("please use '-outfilelist' flag instead of '-o'")
175				}
176				var err error
177				if outputfiles, err = readOutFileList(*outfilelist); err != nil {
178					return err
179				}
180				covervarsoutfile = outputfiles[0]
181				outputfiles = outputfiles[1:]
182				numInputs := len(flag.Args())
183				numOutputs := len(outputfiles)
184				if numOutputs != numInputs {
185					return fmt.Errorf("number of output files (%d) not equal to number of input files (%d)", numOutputs, numInputs)
186				}
187				if err := readPackageConfig(*pkgcfg); err != nil {
188					return err
189				}
190				return nil
191			} else {
192				if *outfilelist != "" {
193					return fmt.Errorf("'-outfilelist' flag applicable only when -pkgcfg used")
194				}
195			}
196			if flag.NArg() == 1 {
197				return nil
198			}
199		}
200	} else if flag.NArg() == 0 {
201		return nil
202	}
203	return fmt.Errorf("too many arguments")
204}
205
206func readOutFileList(path string) ([]string, error) {
207	data, err := os.ReadFile(path)
208	if err != nil {
209		return nil, fmt.Errorf("error reading -outfilelist file %q: %v", path, err)
210	}
211	return strings.Split(strings.TrimSpace(string(data)), "\n"), nil
212}
213
214func readPackageConfig(path string) error {
215	data, err := os.ReadFile(path)
216	if err != nil {
217		return fmt.Errorf("error reading pkgconfig file %q: %v", path, err)
218	}
219	if err := json.Unmarshal(data, &pkgconfig); err != nil {
220		return fmt.Errorf("error reading pkgconfig file %q: %v", path, err)
221	}
222	switch pkgconfig.Granularity {
223	case "perblock":
224		cgran = coverage.CtrGranularityPerBlock
225	case "perfunc":
226		cgran = coverage.CtrGranularityPerFunc
227	default:
228		return fmt.Errorf(`%s: pkgconfig requires perblock/perfunc value`, path)
229	}
230	return nil
231}
232
233// Block represents the information about a basic block to be recorded in the analysis.
234// Note: Our definition of basic block is based on control structures; we don't break
235// apart && and ||. We could but it doesn't seem important enough to bother.
236type Block struct {
237	startByte token.Pos
238	endByte   token.Pos
239	numStmt   int
240}
241
242// Package holds package-specific state.
243type Package struct {
244	mdb            *encodemeta.CoverageMetaDataBuilder
245	counterLengths []int
246}
247
248// Function holds func-specific state.
249type Func struct {
250	units      []coverage.CoverableUnit
251	counterVar string
252}
253
254// File is a wrapper for the state of a file used in the parser.
255// The basic parse tree walker is a method of this type.
256type File struct {
257	fset    *token.FileSet
258	name    string // Name of file.
259	astFile *ast.File
260	blocks  []Block
261	content []byte
262	edit    *edit.Buffer
263	mdb     *encodemeta.CoverageMetaDataBuilder
264	fn      Func
265	pkg     *Package
266}
267
268// findText finds text in the original source, starting at pos.
269// It correctly skips over comments and assumes it need not
270// handle quoted strings.
271// It returns a byte offset within f.src.
272func (f *File) findText(pos token.Pos, text string) int {
273	b := []byte(text)
274	start := f.offset(pos)
275	i := start
276	s := f.content
277	for i < len(s) {
278		if bytes.HasPrefix(s[i:], b) {
279			return i
280		}
281		if i+2 <= len(s) && s[i] == '/' && s[i+1] == '/' {
282			for i < len(s) && s[i] != '\n' {
283				i++
284			}
285			continue
286		}
287		if i+2 <= len(s) && s[i] == '/' && s[i+1] == '*' {
288			for i += 2; ; i++ {
289				if i+2 > len(s) {
290					return 0
291				}
292				if s[i] == '*' && s[i+1] == '/' {
293					i += 2
294					break
295				}
296			}
297			continue
298		}
299		i++
300	}
301	return -1
302}
303
304// Visit implements the ast.Visitor interface.
305func (f *File) Visit(node ast.Node) ast.Visitor {
306	switch n := node.(type) {
307	case *ast.BlockStmt:
308		// If it's a switch or select, the body is a list of case clauses; don't tag the block itself.
309		if len(n.List) > 0 {
310			switch n.List[0].(type) {
311			case *ast.CaseClause: // switch
312				for _, n := range n.List {
313					clause := n.(*ast.CaseClause)
314					f.addCounters(clause.Colon+1, clause.Colon+1, clause.End(), clause.Body, false)
315				}
316				return f
317			case *ast.CommClause: // select
318				for _, n := range n.List {
319					clause := n.(*ast.CommClause)
320					f.addCounters(clause.Colon+1, clause.Colon+1, clause.End(), clause.Body, false)
321				}
322				return f
323			}
324		}
325		f.addCounters(n.Lbrace, n.Lbrace+1, n.Rbrace+1, n.List, true) // +1 to step past closing brace.
326	case *ast.IfStmt:
327		if n.Init != nil {
328			ast.Walk(f, n.Init)
329		}
330		ast.Walk(f, n.Cond)
331		ast.Walk(f, n.Body)
332		if n.Else == nil {
333			return nil
334		}
335		// The elses are special, because if we have
336		//	if x {
337		//	} else if y {
338		//	}
339		// we want to cover the "if y". To do this, we need a place to drop the counter,
340		// so we add a hidden block:
341		//	if x {
342		//	} else {
343		//		if y {
344		//		}
345		//	}
346		elseOffset := f.findText(n.Body.End(), "else")
347		if elseOffset < 0 {
348			panic("lost else")
349		}
350		f.edit.Insert(elseOffset+4, "{")
351		f.edit.Insert(f.offset(n.Else.End()), "}")
352
353		// We just created a block, now walk it.
354		// Adjust the position of the new block to start after
355		// the "else". That will cause it to follow the "{"
356		// we inserted above.
357		pos := f.fset.File(n.Body.End()).Pos(elseOffset + 4)
358		switch stmt := n.Else.(type) {
359		case *ast.IfStmt:
360			block := &ast.BlockStmt{
361				Lbrace: pos,
362				List:   []ast.Stmt{stmt},
363				Rbrace: stmt.End(),
364			}
365			n.Else = block
366		case *ast.BlockStmt:
367			stmt.Lbrace = pos
368		default:
369			panic("unexpected node type in if")
370		}
371		ast.Walk(f, n.Else)
372		return nil
373	case *ast.SelectStmt:
374		// Don't annotate an empty select - creates a syntax error.
375		if n.Body == nil || len(n.Body.List) == 0 {
376			return nil
377		}
378	case *ast.SwitchStmt:
379		// Don't annotate an empty switch - creates a syntax error.
380		if n.Body == nil || len(n.Body.List) == 0 {
381			if n.Init != nil {
382				ast.Walk(f, n.Init)
383			}
384			if n.Tag != nil {
385				ast.Walk(f, n.Tag)
386			}
387			return nil
388		}
389	case *ast.TypeSwitchStmt:
390		// Don't annotate an empty type switch - creates a syntax error.
391		if n.Body == nil || len(n.Body.List) == 0 {
392			if n.Init != nil {
393				ast.Walk(f, n.Init)
394			}
395			ast.Walk(f, n.Assign)
396			return nil
397		}
398	case *ast.FuncDecl:
399		// Don't annotate functions with blank names - they cannot be executed.
400		// Similarly for bodyless funcs.
401		if n.Name.Name == "_" || n.Body == nil {
402			return nil
403		}
404		fname := n.Name.Name
405		// Skip AddUint32 and StoreUint32 if we're instrumenting
406		// sync/atomic itself in atomic mode (out of an abundance of
407		// caution), since as part of the instrumentation process we
408		// add calls to AddUint32/StoreUint32, and we don't want to
409		// somehow create an infinite loop.
410		//
411		// Note that in the current implementation (Go 1.20) both
412		// routines are assembly stubs that forward calls to the
413		// internal/runtime/atomic equivalents, hence the infinite
414		// loop scenario is purely theoretical (maybe if in some
415		// future implementation one of these functions might be
416		// written in Go). See #57445 for more details.
417		if atomicOnAtomic() && (fname == "AddUint32" || fname == "StoreUint32") {
418			return nil
419		}
420		// Determine proper function or method name.
421		if r := n.Recv; r != nil && len(r.List) == 1 {
422			t := r.List[0].Type
423			star := ""
424			if p, _ := t.(*ast.StarExpr); p != nil {
425				t = p.X
426				star = "*"
427			}
428			if p, _ := t.(*ast.Ident); p != nil {
429				fname = star + p.Name + "." + fname
430			}
431		}
432		walkBody := true
433		if *pkgcfg != "" {
434			f.preFunc(n, fname)
435			if pkgconfig.Granularity == "perfunc" {
436				walkBody = false
437			}
438		}
439		if walkBody {
440			ast.Walk(f, n.Body)
441		}
442		if *pkgcfg != "" {
443			flit := false
444			f.postFunc(n, fname, flit, n.Body)
445		}
446		return nil
447	case *ast.FuncLit:
448		// For function literals enclosed in functions, just glom the
449		// code for the literal in with the enclosing function (for now).
450		if f.fn.counterVar != "" {
451			return f
452		}
453
454		// Hack: function literals aren't named in the go/ast representation,
455		// and we don't know what name the compiler will choose. For now,
456		// just make up a descriptive name.
457		pos := n.Pos()
458		p := f.fset.File(pos).Position(pos)
459		fname := fmt.Sprintf("func.L%d.C%d", p.Line, p.Column)
460		if *pkgcfg != "" {
461			f.preFunc(n, fname)
462		}
463		if pkgconfig.Granularity != "perfunc" {
464			ast.Walk(f, n.Body)
465		}
466		if *pkgcfg != "" {
467			flit := true
468			f.postFunc(n, fname, flit, n.Body)
469		}
470		return nil
471	}
472	return f
473}
474
475func mkCounterVarName(idx int) string {
476	return fmt.Sprintf("%s_%d", *varVar, idx)
477}
478
479func mkPackageIdVar() string {
480	return *varVar + "P"
481}
482
483func mkMetaVar() string {
484	return *varVar + "M"
485}
486
487func mkPackageIdExpression() string {
488	ppath := pkgconfig.PkgPath
489	if hcid := coverage.HardCodedPkgID(ppath); hcid != -1 {
490		return fmt.Sprintf("uint32(%d)", uint32(hcid))
491	}
492	return mkPackageIdVar()
493}
494
495func (f *File) preFunc(fn ast.Node, fname string) {
496	f.fn.units = f.fn.units[:0]
497
498	// create a new counter variable for this function.
499	cv := mkCounterVarName(len(f.pkg.counterLengths))
500	f.fn.counterVar = cv
501}
502
503func (f *File) postFunc(fn ast.Node, funcname string, flit bool, body *ast.BlockStmt) {
504
505	// Tack on single counter write if we are in "perfunc" mode.
506	singleCtr := ""
507	if pkgconfig.Granularity == "perfunc" {
508		singleCtr = "; " + f.newCounter(fn.Pos(), fn.Pos(), 1)
509	}
510
511	// record the length of the counter var required.
512	nc := len(f.fn.units) + coverage.FirstCtrOffset
513	f.pkg.counterLengths = append(f.pkg.counterLengths, nc)
514
515	// FIXME: for windows, do we want "\" and not "/"? Need to test here.
516	// Currently filename is formed as packagepath + "/" + basename.
517	fnpos := f.fset.Position(fn.Pos())
518	ppath := pkgconfig.PkgPath
519	filename := ppath + "/" + filepath.Base(fnpos.Filename)
520
521	// The convention for cmd/cover is that if the go command that
522	// kicks off coverage specifies a local import path (e.g. "go test
523	// -cover ./thispackage"), the tool will capture full pathnames
524	// for source files instead of relative paths, which tend to work
525	// more smoothly for "go tool cover -html". See also issue #56433
526	// for more details.
527	if pkgconfig.Local {
528		filename = f.name
529	}
530
531	// Hand off function to meta-data builder.
532	fd := coverage.FuncDesc{
533		Funcname: funcname,
534		Srcfile:  filename,
535		Units:    f.fn.units,
536		Lit:      flit,
537	}
538	funcId := f.mdb.AddFunc(fd)
539
540	hookWrite := func(cv string, which int, val string) string {
541		return fmt.Sprintf("%s[%d] = %s", cv, which, val)
542	}
543	if *mode == "atomic" {
544		hookWrite = func(cv string, which int, val string) string {
545			return fmt.Sprintf("%sStoreUint32(&%s[%d], %s)",
546				atomicPackagePrefix(), cv, which, val)
547		}
548	}
549
550	// Generate the registration hook sequence for the function. This
551	// sequence looks like
552	//
553	//   counterVar[0] = <num_units>
554	//   counterVar[1] = pkgId
555	//   counterVar[2] = fnId
556	//
557	cv := f.fn.counterVar
558	regHook := hookWrite(cv, 0, strconv.Itoa(len(f.fn.units))) + " ; " +
559		hookWrite(cv, 1, mkPackageIdExpression()) + " ; " +
560		hookWrite(cv, 2, strconv.Itoa(int(funcId))) + singleCtr
561
562	// Insert the registration sequence into the function. We want this sequence to
563	// appear before any counter updates, so use a hack to ensure that this edit
564	// applies before the edit corresponding to the prolog counter update.
565
566	boff := f.offset(body.Pos())
567	ipos := f.fset.File(body.Pos()).Pos(boff)
568	ip := f.offset(ipos)
569	f.edit.Replace(ip, ip+1, string(f.content[ipos-1])+regHook+" ; ")
570
571	f.fn.counterVar = ""
572}
573
574func annotate(names []string) {
575	var p *Package
576	if *pkgcfg != "" {
577		pp := pkgconfig.PkgPath
578		pn := pkgconfig.PkgName
579		mp := pkgconfig.ModulePath
580		mdb, err := encodemeta.NewCoverageMetaDataBuilder(pp, pn, mp)
581		if err != nil {
582			log.Fatalf("creating coverage meta-data builder: %v\n", err)
583		}
584		p = &Package{
585			mdb: mdb,
586		}
587	}
588	// TODO: process files in parallel here if it matters.
589	for k, name := range names {
590		if strings.ContainsAny(name, "\r\n") {
591			// annotateFile uses '//line' directives, which don't permit newlines.
592			log.Fatalf("cover: input path contains newline character: %q", name)
593		}
594
595		fd := os.Stdout
596		isStdout := true
597		if *pkgcfg != "" {
598			var err error
599			fd, err = os.Create(outputfiles[k])
600			if err != nil {
601				log.Fatalf("cover: %s", err)
602			}
603			isStdout = false
604		} else if *output != "" {
605			var err error
606			fd, err = os.Create(*output)
607			if err != nil {
608				log.Fatalf("cover: %s", err)
609			}
610			isStdout = false
611		}
612		p.annotateFile(name, fd)
613		if !isStdout {
614			if err := fd.Close(); err != nil {
615				log.Fatalf("cover: %s", err)
616			}
617		}
618	}
619
620	if *pkgcfg != "" {
621		fd, err := os.Create(covervarsoutfile)
622		if err != nil {
623			log.Fatalf("cover: %s", err)
624		}
625		p.emitMetaData(fd)
626		if err := fd.Close(); err != nil {
627			log.Fatalf("cover: %s", err)
628		}
629	}
630}
631
632func (p *Package) annotateFile(name string, fd io.Writer) {
633	fset := token.NewFileSet()
634	content, err := os.ReadFile(name)
635	if err != nil {
636		log.Fatalf("cover: %s: %s", name, err)
637	}
638	parsedFile, err := parser.ParseFile(fset, name, content, parser.ParseComments)
639	if err != nil {
640		log.Fatalf("cover: %s: %s", name, err)
641	}
642
643	file := &File{
644		fset:    fset,
645		name:    name,
646		content: content,
647		edit:    edit.NewBuffer(content),
648		astFile: parsedFile,
649	}
650	if p != nil {
651		file.mdb = p.mdb
652		file.pkg = p
653	}
654
655	if *mode == "atomic" {
656		// Add import of sync/atomic immediately after package clause.
657		// We do this even if there is an existing import, because the
658		// existing import may be shadowed at any given place we want
659		// to refer to it, and our name (_cover_atomic_) is less likely to
660		// be shadowed. The one exception is if we're visiting the
661		// sync/atomic package itself, in which case we can refer to
662		// functions directly without an import prefix. See also #57445.
663		if pkgconfig.PkgPath != "sync/atomic" {
664			file.edit.Insert(file.offset(file.astFile.Name.End()),
665				fmt.Sprintf("; import %s %q", atomicPackageName, atomicPackagePath))
666		}
667	}
668	if pkgconfig.PkgName == "main" {
669		file.edit.Insert(file.offset(file.astFile.Name.End()),
670			"; import _ \"runtime/coverage\"")
671	}
672
673	if counterStmt != nil {
674		ast.Walk(file, file.astFile)
675	}
676	newContent := file.edit.Bytes()
677
678	if strings.ContainsAny(name, "\r\n") {
679		// This should have been checked by the caller already, but we double check
680		// here just to be sure we haven't missed a caller somewhere.
681		panic(fmt.Sprintf("annotateFile: name contains unexpected newline character: %q", name))
682	}
683	fmt.Fprintf(fd, "//line %s:1:1\n", name)
684	fd.Write(newContent)
685
686	// After printing the source tree, add some declarations for the
687	// counters etc. We could do this by adding to the tree, but it's
688	// easier just to print the text.
689	file.addVariables(fd)
690
691	// Emit a reference to the atomic package to avoid
692	// import and not used error when there's no code in a file.
693	if *mode == "atomic" {
694		fmt.Fprintf(fd, "\nvar _ = %sLoadUint32\n", atomicPackagePrefix())
695	}
696}
697
698// setCounterStmt returns the expression: __count[23] = 1.
699func setCounterStmt(f *File, counter string) string {
700	return fmt.Sprintf("%s = 1", counter)
701}
702
703// incCounterStmt returns the expression: __count[23]++.
704func incCounterStmt(f *File, counter string) string {
705	return fmt.Sprintf("%s++", counter)
706}
707
708// atomicCounterStmt returns the expression: atomic.AddUint32(&__count[23], 1)
709func atomicCounterStmt(f *File, counter string) string {
710	return fmt.Sprintf("%sAddUint32(&%s, 1)", atomicPackagePrefix(), counter)
711}
712
713// newCounter creates a new counter expression of the appropriate form.
714func (f *File) newCounter(start, end token.Pos, numStmt int) string {
715	var stmt string
716	if *pkgcfg != "" {
717		slot := len(f.fn.units) + coverage.FirstCtrOffset
718		if f.fn.counterVar == "" {
719			panic("internal error: counter var unset")
720		}
721		stmt = counterStmt(f, fmt.Sprintf("%s[%d]", f.fn.counterVar, slot))
722		stpos := f.fset.Position(start)
723		enpos := f.fset.Position(end)
724		stpos, enpos = dedup(stpos, enpos)
725		unit := coverage.CoverableUnit{
726			StLine:  uint32(stpos.Line),
727			StCol:   uint32(stpos.Column),
728			EnLine:  uint32(enpos.Line),
729			EnCol:   uint32(enpos.Column),
730			NxStmts: uint32(numStmt),
731		}
732		f.fn.units = append(f.fn.units, unit)
733	} else {
734		stmt = counterStmt(f, fmt.Sprintf("%s.Count[%d]", *varVar,
735			len(f.blocks)))
736		f.blocks = append(f.blocks, Block{start, end, numStmt})
737	}
738	return stmt
739}
740
741// addCounters takes a list of statements and adds counters to the beginning of
742// each basic block at the top level of that list. For instance, given
743//
744//	S1
745//	if cond {
746//		S2
747//	}
748//	S3
749//
750// counters will be added before S1 and before S3. The block containing S2
751// will be visited in a separate call.
752// TODO: Nested simple blocks get unnecessary (but correct) counters
753func (f *File) addCounters(pos, insertPos, blockEnd token.Pos, list []ast.Stmt, extendToClosingBrace bool) {
754	// Special case: make sure we add a counter to an empty block. Can't do this below
755	// or we will add a counter to an empty statement list after, say, a return statement.
756	if len(list) == 0 {
757		f.edit.Insert(f.offset(insertPos), f.newCounter(insertPos, blockEnd, 0)+";")
758		return
759	}
760	// Make a copy of the list, as we may mutate it and should leave the
761	// existing list intact.
762	list = append([]ast.Stmt(nil), list...)
763	// We have a block (statement list), but it may have several basic blocks due to the
764	// appearance of statements that affect the flow of control.
765	for {
766		// Find first statement that affects flow of control (break, continue, if, etc.).
767		// It will be the last statement of this basic block.
768		var last int
769		end := blockEnd
770		for last = 0; last < len(list); last++ {
771			stmt := list[last]
772			end = f.statementBoundary(stmt)
773			if f.endsBasicSourceBlock(stmt) {
774				// If it is a labeled statement, we need to place a counter between
775				// the label and its statement because it may be the target of a goto
776				// and thus start a basic block. That is, given
777				//	foo: stmt
778				// we need to create
779				//	foo: ; stmt
780				// and mark the label as a block-terminating statement.
781				// The result will then be
782				//	foo: COUNTER[n]++; stmt
783				// However, we can't do this if the labeled statement is already
784				// a control statement, such as a labeled for.
785				if label, isLabel := stmt.(*ast.LabeledStmt); isLabel && !f.isControl(label.Stmt) {
786					newLabel := *label
787					newLabel.Stmt = &ast.EmptyStmt{
788						Semicolon: label.Stmt.Pos(),
789						Implicit:  true,
790					}
791					end = label.Pos() // Previous block ends before the label.
792					list[last] = &newLabel
793					// Open a gap and drop in the old statement, now without a label.
794					list = append(list, nil)
795					copy(list[last+1:], list[last:])
796					list[last+1] = label.Stmt
797				}
798				last++
799				extendToClosingBrace = false // Block is broken up now.
800				break
801			}
802		}
803		if extendToClosingBrace {
804			end = blockEnd
805		}
806		if pos != end { // Can have no source to cover if e.g. blocks abut.
807			f.edit.Insert(f.offset(insertPos), f.newCounter(pos, end, last)+";")
808		}
809		list = list[last:]
810		if len(list) == 0 {
811			break
812		}
813		pos = list[0].Pos()
814		insertPos = pos
815	}
816}
817
818// hasFuncLiteral reports the existence and position of the first func literal
819// in the node, if any. If a func literal appears, it usually marks the termination
820// of a basic block because the function body is itself a block.
821// Therefore we draw a line at the start of the body of the first function literal we find.
822// TODO: what if there's more than one? Probably doesn't matter much.
823func hasFuncLiteral(n ast.Node) (bool, token.Pos) {
824	if n == nil {
825		return false, 0
826	}
827	var literal funcLitFinder
828	ast.Walk(&literal, n)
829	return literal.found(), token.Pos(literal)
830}
831
832// statementBoundary finds the location in s that terminates the current basic
833// block in the source.
834func (f *File) statementBoundary(s ast.Stmt) token.Pos {
835	// Control flow statements are easy.
836	switch s := s.(type) {
837	case *ast.BlockStmt:
838		// Treat blocks like basic blocks to avoid overlapping counters.
839		return s.Lbrace
840	case *ast.IfStmt:
841		found, pos := hasFuncLiteral(s.Init)
842		if found {
843			return pos
844		}
845		found, pos = hasFuncLiteral(s.Cond)
846		if found {
847			return pos
848		}
849		return s.Body.Lbrace
850	case *ast.ForStmt:
851		found, pos := hasFuncLiteral(s.Init)
852		if found {
853			return pos
854		}
855		found, pos = hasFuncLiteral(s.Cond)
856		if found {
857			return pos
858		}
859		found, pos = hasFuncLiteral(s.Post)
860		if found {
861			return pos
862		}
863		return s.Body.Lbrace
864	case *ast.LabeledStmt:
865		return f.statementBoundary(s.Stmt)
866	case *ast.RangeStmt:
867		found, pos := hasFuncLiteral(s.X)
868		if found {
869			return pos
870		}
871		return s.Body.Lbrace
872	case *ast.SwitchStmt:
873		found, pos := hasFuncLiteral(s.Init)
874		if found {
875			return pos
876		}
877		found, pos = hasFuncLiteral(s.Tag)
878		if found {
879			return pos
880		}
881		return s.Body.Lbrace
882	case *ast.SelectStmt:
883		return s.Body.Lbrace
884	case *ast.TypeSwitchStmt:
885		found, pos := hasFuncLiteral(s.Init)
886		if found {
887			return pos
888		}
889		return s.Body.Lbrace
890	}
891	// If not a control flow statement, it is a declaration, expression, call, etc. and it may have a function literal.
892	// If it does, that's tricky because we want to exclude the body of the function from this block.
893	// Draw a line at the start of the body of the first function literal we find.
894	// TODO: what if there's more than one? Probably doesn't matter much.
895	found, pos := hasFuncLiteral(s)
896	if found {
897		return pos
898	}
899	return s.End()
900}
901
902// endsBasicSourceBlock reports whether s changes the flow of control: break, if, etc.,
903// or if it's just problematic, for instance contains a function literal, which will complicate
904// accounting due to the block-within-an expression.
905func (f *File) endsBasicSourceBlock(s ast.Stmt) bool {
906	switch s := s.(type) {
907	case *ast.BlockStmt:
908		// Treat blocks like basic blocks to avoid overlapping counters.
909		return true
910	case *ast.BranchStmt:
911		return true
912	case *ast.ForStmt:
913		return true
914	case *ast.IfStmt:
915		return true
916	case *ast.LabeledStmt:
917		return true // A goto may branch here, starting a new basic block.
918	case *ast.RangeStmt:
919		return true
920	case *ast.SwitchStmt:
921		return true
922	case *ast.SelectStmt:
923		return true
924	case *ast.TypeSwitchStmt:
925		return true
926	case *ast.ExprStmt:
927		// Calls to panic change the flow.
928		// We really should verify that "panic" is the predefined function,
929		// but without type checking we can't and the likelihood of it being
930		// an actual problem is vanishingly small.
931		if call, ok := s.X.(*ast.CallExpr); ok {
932			if ident, ok := call.Fun.(*ast.Ident); ok && ident.Name == "panic" && len(call.Args) == 1 {
933				return true
934			}
935		}
936	}
937	found, _ := hasFuncLiteral(s)
938	return found
939}
940
941// isControl reports whether s is a control statement that, if labeled, cannot be
942// separated from its label.
943func (f *File) isControl(s ast.Stmt) bool {
944	switch s.(type) {
945	case *ast.ForStmt, *ast.RangeStmt, *ast.SwitchStmt, *ast.SelectStmt, *ast.TypeSwitchStmt:
946		return true
947	}
948	return false
949}
950
951// funcLitFinder implements the ast.Visitor pattern to find the location of any
952// function literal in a subtree.
953type funcLitFinder token.Pos
954
955func (f *funcLitFinder) Visit(node ast.Node) (w ast.Visitor) {
956	if f.found() {
957		return nil // Prune search.
958	}
959	switch n := node.(type) {
960	case *ast.FuncLit:
961		*f = funcLitFinder(n.Body.Lbrace)
962		return nil // Prune search.
963	}
964	return f
965}
966
967func (f *funcLitFinder) found() bool {
968	return token.Pos(*f) != token.NoPos
969}
970
971// Sort interface for []block1; used for self-check in addVariables.
972
973type block1 struct {
974	Block
975	index int
976}
977
978type blockSlice []block1
979
980func (b blockSlice) Len() int           { return len(b) }
981func (b blockSlice) Less(i, j int) bool { return b[i].startByte < b[j].startByte }
982func (b blockSlice) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
983
984// offset translates a token position into a 0-indexed byte offset.
985func (f *File) offset(pos token.Pos) int {
986	return f.fset.Position(pos).Offset
987}
988
989// addVariables adds to the end of the file the declarations to set up the counter and position variables.
990func (f *File) addVariables(w io.Writer) {
991	if *pkgcfg != "" {
992		return
993	}
994	// Self-check: Verify that the instrumented basic blocks are disjoint.
995	t := make([]block1, len(f.blocks))
996	for i := range f.blocks {
997		t[i].Block = f.blocks[i]
998		t[i].index = i
999	}
1000	sort.Sort(blockSlice(t))
1001	for i := 1; i < len(t); i++ {
1002		if t[i-1].endByte > t[i].startByte {
1003			fmt.Fprintf(os.Stderr, "cover: internal error: block %d overlaps block %d\n", t[i-1].index, t[i].index)
1004			// Note: error message is in byte positions, not token positions.
1005			fmt.Fprintf(os.Stderr, "\t%s:#%d,#%d %s:#%d,#%d\n",
1006				f.name, f.offset(t[i-1].startByte), f.offset(t[i-1].endByte),
1007				f.name, f.offset(t[i].startByte), f.offset(t[i].endByte))
1008		}
1009	}
1010
1011	// Declare the coverage struct as a package-level variable.
1012	fmt.Fprintf(w, "\nvar %s = struct {\n", *varVar)
1013	fmt.Fprintf(w, "\tCount     [%d]uint32\n", len(f.blocks))
1014	fmt.Fprintf(w, "\tPos       [3 * %d]uint32\n", len(f.blocks))
1015	fmt.Fprintf(w, "\tNumStmt   [%d]uint16\n", len(f.blocks))
1016	fmt.Fprintf(w, "} {\n")
1017
1018	// Initialize the position array field.
1019	fmt.Fprintf(w, "\tPos: [3 * %d]uint32{\n", len(f.blocks))
1020
1021	// A nice long list of positions. Each position is encoded as follows to reduce size:
1022	// - 32-bit starting line number
1023	// - 32-bit ending line number
1024	// - (16 bit ending column number << 16) | (16-bit starting column number).
1025	for i, block := range f.blocks {
1026		start := f.fset.Position(block.startByte)
1027		end := f.fset.Position(block.endByte)
1028
1029		start, end = dedup(start, end)
1030
1031		fmt.Fprintf(w, "\t\t%d, %d, %#x, // [%d]\n", start.Line, end.Line, (end.Column&0xFFFF)<<16|(start.Column&0xFFFF), i)
1032	}
1033
1034	// Close the position array.
1035	fmt.Fprintf(w, "\t},\n")
1036
1037	// Initialize the position array field.
1038	fmt.Fprintf(w, "\tNumStmt: [%d]uint16{\n", len(f.blocks))
1039
1040	// A nice long list of statements-per-block, so we can give a conventional
1041	// valuation of "percent covered". To save space, it's a 16-bit number, so we
1042	// clamp it if it overflows - won't matter in practice.
1043	for i, block := range f.blocks {
1044		n := block.numStmt
1045		if n > 1<<16-1 {
1046			n = 1<<16 - 1
1047		}
1048		fmt.Fprintf(w, "\t\t%d, // %d\n", n, i)
1049	}
1050
1051	// Close the statements-per-block array.
1052	fmt.Fprintf(w, "\t},\n")
1053
1054	// Close the struct initialization.
1055	fmt.Fprintf(w, "}\n")
1056}
1057
1058// It is possible for positions to repeat when there is a line
1059// directive that does not specify column information and the input
1060// has not been passed through gofmt.
1061// See issues #27530 and #30746.
1062// Tests are TestHtmlUnformatted and TestLineDup.
1063// We use a map to avoid duplicates.
1064
1065// pos2 is a pair of token.Position values, used as a map key type.
1066type pos2 struct {
1067	p1, p2 token.Position
1068}
1069
1070// seenPos2 tracks whether we have seen a token.Position pair.
1071var seenPos2 = make(map[pos2]bool)
1072
1073// dedup takes a token.Position pair and returns a pair that does not
1074// duplicate any existing pair. The returned pair will have the Offset
1075// fields cleared.
1076func dedup(p1, p2 token.Position) (r1, r2 token.Position) {
1077	key := pos2{
1078		p1: p1,
1079		p2: p2,
1080	}
1081
1082	// We want to ignore the Offset fields in the map,
1083	// since cover uses only file/line/column.
1084	key.p1.Offset = 0
1085	key.p2.Offset = 0
1086
1087	for seenPos2[key] {
1088		key.p2.Column++
1089	}
1090	seenPos2[key] = true
1091
1092	return key.p1, key.p2
1093}
1094
1095func (p *Package) emitMetaData(w io.Writer) {
1096	if *pkgcfg == "" {
1097		return
1098	}
1099
1100	// If the "EmitMetaFile" path has been set, invoke a helper
1101	// that will write out a pre-cooked meta-data file for this package
1102	// to the specified location, in effect simulating the execution
1103	// of a test binary that doesn't do any testing to speak of.
1104	if pkgconfig.EmitMetaFile != "" {
1105		p.emitMetaFile(pkgconfig.EmitMetaFile)
1106	}
1107
1108	// Something went wrong if regonly/testmain mode is in effect and
1109	// we have instrumented functions.
1110	if counterStmt == nil && len(p.counterLengths) != 0 {
1111		panic("internal error: seen functions with regonly/testmain")
1112	}
1113
1114	// Emit package name.
1115	fmt.Fprintf(w, "\npackage %s\n\n", pkgconfig.PkgName)
1116
1117	// Emit package ID var.
1118	fmt.Fprintf(w, "\nvar %sP uint32\n", *varVar)
1119
1120	// Emit all of the counter variables.
1121	for k := range p.counterLengths {
1122		cvn := mkCounterVarName(k)
1123		fmt.Fprintf(w, "var %s [%d]uint32\n", cvn, p.counterLengths[k])
1124	}
1125
1126	// Emit encoded meta-data.
1127	var sws slicewriter.WriteSeeker
1128	digest, err := p.mdb.Emit(&sws)
1129	if err != nil {
1130		log.Fatalf("encoding meta-data: %v", err)
1131	}
1132	p.mdb = nil
1133	fmt.Fprintf(w, "var %s = [...]byte{\n", mkMetaVar())
1134	payload := sws.BytesWritten()
1135	for k, b := range payload {
1136		fmt.Fprintf(w, " 0x%x,", b)
1137		if k != 0 && k%8 == 0 {
1138			fmt.Fprintf(w, "\n")
1139		}
1140	}
1141	fmt.Fprintf(w, "}\n")
1142
1143	fixcfg := covcmd.CoverFixupConfig{
1144		Strategy:           "normal",
1145		MetaVar:            mkMetaVar(),
1146		MetaLen:            len(payload),
1147		MetaHash:           fmt.Sprintf("%x", digest),
1148		PkgIdVar:           mkPackageIdVar(),
1149		CounterPrefix:      *varVar,
1150		CounterGranularity: pkgconfig.Granularity,
1151		CounterMode:        *mode,
1152	}
1153	fixdata, err := json.Marshal(fixcfg)
1154	if err != nil {
1155		log.Fatalf("marshal fixupcfg: %v", err)
1156	}
1157	if err := os.WriteFile(pkgconfig.OutConfig, fixdata, 0666); err != nil {
1158		log.Fatalf("error writing %s: %v", pkgconfig.OutConfig, err)
1159	}
1160}
1161
1162// atomicOnAtomic returns true if we're instrumenting
1163// the sync/atomic package AND using atomic mode.
1164func atomicOnAtomic() bool {
1165	return *mode == "atomic" && pkgconfig.PkgPath == "sync/atomic"
1166}
1167
1168// atomicPackagePrefix returns the import path prefix used to refer to
1169// our special import of sync/atomic; this is either set to the
1170// constant atomicPackageName plus a dot or the empty string if we're
1171// instrumenting the sync/atomic package itself.
1172func atomicPackagePrefix() string {
1173	if atomicOnAtomic() {
1174		return ""
1175	}
1176	return atomicPackageName + "."
1177}
1178
1179func (p *Package) emitMetaFile(outpath string) {
1180	// Open output file.
1181	of, err := os.OpenFile(outpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
1182	if err != nil {
1183		log.Fatalf("opening covmeta %s: %v", outpath, err)
1184	}
1185
1186	if len(p.counterLengths) == 0 {
1187		// This corresponds to the case where we have no functions
1188		// in the package to instrument. Leave the file empty file if
1189		// this happens.
1190		if err = of.Close(); err != nil {
1191			log.Fatalf("closing meta-data file: %v", err)
1192		}
1193		return
1194	}
1195
1196	// Encode meta-data.
1197	var sws slicewriter.WriteSeeker
1198	digest, err := p.mdb.Emit(&sws)
1199	if err != nil {
1200		log.Fatalf("encoding meta-data: %v", err)
1201	}
1202	payload := sws.BytesWritten()
1203	blobs := [][]byte{payload}
1204
1205	// Write meta-data file directly.
1206	mfw := encodemeta.NewCoverageMetaFileWriter(outpath, of)
1207	err = mfw.Write(digest, blobs, cmode, cgran)
1208	if err != nil {
1209		log.Fatalf("writing meta-data file: %v", err)
1210	}
1211	if err = of.Close(); err != nil {
1212		log.Fatalf("closing meta-data file: %v", err)
1213	}
1214}
1215