Home | History | Annotate | Download | only in cover
      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 
      5 package main
      6 
      7 import (
      8 	"bytes"
      9 	"flag"
     10 	"fmt"
     11 	"go/ast"
     12 	"go/parser"
     13 	"go/token"
     14 	"io"
     15 	"io/ioutil"
     16 	"log"
     17 	"os"
     18 	"sort"
     19 	"strconv"
     20 
     21 	"cmd/internal/edit"
     22 	"cmd/internal/objabi"
     23 )
     24 
     25 const usageMessage = "" +
     26 	`Usage of 'go tool cover':
     27 Given a coverage profile produced by 'go test':
     28 	go test -coverprofile=c.out
     29 
     30 Open a web browser displaying annotated source code:
     31 	go tool cover -html=c.out
     32 
     33 Write out an HTML file instead of launching a web browser:
     34 	go tool cover -html=c.out -o coverage.html
     35 
     36 Display coverage percentages to stdout for each function:
     37 	go tool cover -func=c.out
     38 
     39 Finally, to generate modified source code with coverage annotations
     40 (what go test -cover does):
     41 	go tool cover -mode=set -var=CoverageVariableName program.go
     42 `
     43 
     44 func usage() {
     45 	fmt.Fprintln(os.Stderr, usageMessage)
     46 	fmt.Fprintln(os.Stderr, "Flags:")
     47 	flag.PrintDefaults()
     48 	fmt.Fprintln(os.Stderr, "\n  Only one of -html, -func, or -mode may be set.")
     49 	os.Exit(2)
     50 }
     51 
     52 var (
     53 	mode    = flag.String("mode", "", "coverage mode: set, count, atomic")
     54 	varVar  = flag.String("var", "GoCover", "name of coverage variable to generate")
     55 	output  = flag.String("o", "", "file for output; default: stdout")
     56 	htmlOut = flag.String("html", "", "generate HTML representation of coverage profile")
     57 	funcOut = flag.String("func", "", "output coverage profile information for each function")
     58 )
     59 
     60 var profile string // The profile to read; the value of -html or -func
     61 
     62 var counterStmt func(*File, string) string
     63 
     64 const (
     65 	atomicPackagePath = "sync/atomic"
     66 	atomicPackageName = "_cover_atomic_"
     67 )
     68 
     69 func main() {
     70 	objabi.AddVersionFlag()
     71 	flag.Usage = usage
     72 	flag.Parse()
     73 
     74 	// Usage information when no arguments.
     75 	if flag.NFlag() == 0 && flag.NArg() == 0 {
     76 		flag.Usage()
     77 	}
     78 
     79 	err := parseFlags()
     80 	if err != nil {
     81 		fmt.Fprintln(os.Stderr, err)
     82 		fmt.Fprintln(os.Stderr, `For usage information, run "go tool cover -help"`)
     83 		os.Exit(2)
     84 	}
     85 
     86 	// Generate coverage-annotated source.
     87 	if *mode != "" {
     88 		annotate(flag.Arg(0))
     89 		return
     90 	}
     91 
     92 	// Output HTML or function coverage information.
     93 	if *htmlOut != "" {
     94 		err = htmlOutput(profile, *output)
     95 	} else {
     96 		err = funcOutput(profile, *output)
     97 	}
     98 
     99 	if err != nil {
    100 		fmt.Fprintf(os.Stderr, "cover: %v\n", err)
    101 		os.Exit(2)
    102 	}
    103 }
    104 
    105 // parseFlags sets the profile and counterStmt globals and performs validations.
    106 func parseFlags() error {
    107 	profile = *htmlOut
    108 	if *funcOut != "" {
    109 		if profile != "" {
    110 			return fmt.Errorf("too many options")
    111 		}
    112 		profile = *funcOut
    113 	}
    114 
    115 	// Must either display a profile or rewrite Go source.
    116 	if (profile == "") == (*mode == "") {
    117 		return fmt.Errorf("too many options")
    118 	}
    119 
    120 	if *mode != "" {
    121 		switch *mode {
    122 		case "set":
    123 			counterStmt = setCounterStmt
    124 		case "count":
    125 			counterStmt = incCounterStmt
    126 		case "atomic":
    127 			counterStmt = atomicCounterStmt
    128 		default:
    129 			return fmt.Errorf("unknown -mode %v", *mode)
    130 		}
    131 
    132 		if flag.NArg() == 0 {
    133 			return fmt.Errorf("missing source file")
    134 		} else if flag.NArg() == 1 {
    135 			return nil
    136 		}
    137 	} else if flag.NArg() == 0 {
    138 		return nil
    139 	}
    140 	return fmt.Errorf("too many arguments")
    141 }
    142 
    143 // Block represents the information about a basic block to be recorded in the analysis.
    144 // Note: Our definition of basic block is based on control structures; we don't break
    145 // apart && and ||. We could but it doesn't seem important enough to bother.
    146 type Block struct {
    147 	startByte token.Pos
    148 	endByte   token.Pos
    149 	numStmt   int
    150 }
    151 
    152 // File is a wrapper for the state of a file used in the parser.
    153 // The basic parse tree walker is a method of this type.
    154 type File struct {
    155 	fset    *token.FileSet
    156 	name    string // Name of file.
    157 	astFile *ast.File
    158 	blocks  []Block
    159 	content []byte
    160 	edit    *edit.Buffer
    161 }
    162 
    163 // findText finds text in the original source, starting at pos.
    164 // It correctly skips over comments and assumes it need not
    165 // handle quoted strings.
    166 // It returns a byte offset within f.src.
    167 func (f *File) findText(pos token.Pos, text string) int {
    168 	b := []byte(text)
    169 	start := f.offset(pos)
    170 	i := start
    171 	s := f.content
    172 	for i < len(s) {
    173 		if bytes.HasPrefix(s[i:], b) {
    174 			return i
    175 		}
    176 		if i+2 <= len(s) && s[i] == '/' && s[i+1] == '/' {
    177 			for i < len(s) && s[i] != '\n' {
    178 				i++
    179 			}
    180 			continue
    181 		}
    182 		if i+2 <= len(s) && s[i] == '/' && s[i+1] == '*' {
    183 			for i += 2; ; i++ {
    184 				if i+2 > len(s) {
    185 					return 0
    186 				}
    187 				if s[i] == '*' && s[i+1] == '/' {
    188 					i += 2
    189 					break
    190 				}
    191 			}
    192 			continue
    193 		}
    194 		i++
    195 	}
    196 	return -1
    197 }
    198 
    199 // Visit implements the ast.Visitor interface.
    200 func (f *File) Visit(node ast.Node) ast.Visitor {
    201 	switch n := node.(type) {
    202 	case *ast.BlockStmt:
    203 		// If it's a switch or select, the body is a list of case clauses; don't tag the block itself.
    204 		if len(n.List) > 0 {
    205 			switch n.List[0].(type) {
    206 			case *ast.CaseClause: // switch
    207 				for _, n := range n.List {
    208 					clause := n.(*ast.CaseClause)
    209 					f.addCounters(clause.Colon+1, clause.Colon+1, clause.End(), clause.Body, false)
    210 				}
    211 				return f
    212 			case *ast.CommClause: // select
    213 				for _, n := range n.List {
    214 					clause := n.(*ast.CommClause)
    215 					f.addCounters(clause.Colon+1, clause.Colon+1, clause.End(), clause.Body, false)
    216 				}
    217 				return f
    218 			}
    219 		}
    220 		f.addCounters(n.Lbrace, n.Lbrace+1, n.Rbrace+1, n.List, true) // +1 to step past closing brace.
    221 	case *ast.IfStmt:
    222 		if n.Init != nil {
    223 			ast.Walk(f, n.Init)
    224 		}
    225 		ast.Walk(f, n.Cond)
    226 		ast.Walk(f, n.Body)
    227 		if n.Else == nil {
    228 			return nil
    229 		}
    230 		// The elses are special, because if we have
    231 		//	if x {
    232 		//	} else if y {
    233 		//	}
    234 		// we want to cover the "if y". To do this, we need a place to drop the counter,
    235 		// so we add a hidden block:
    236 		//	if x {
    237 		//	} else {
    238 		//		if y {
    239 		//		}
    240 		//	}
    241 		f.edit.Insert(f.offset(n.Body.End()), "else{")
    242 		elseOffset := f.findText(n.Body.End(), "else")
    243 		if elseOffset < 0 {
    244 			panic("lost else")
    245 		}
    246 		f.edit.Delete(elseOffset, elseOffset+4)
    247 		f.edit.Insert(f.offset(n.Else.End()), "}")
    248 		switch stmt := n.Else.(type) {
    249 		case *ast.IfStmt:
    250 			block := &ast.BlockStmt{
    251 				Lbrace: n.Body.End(), // Start at end of the "if" block so the covered part looks like it starts at the "else".
    252 				List:   []ast.Stmt{stmt},
    253 				Rbrace: stmt.End(),
    254 			}
    255 			n.Else = block
    256 		case *ast.BlockStmt:
    257 			stmt.Lbrace = n.Body.End() // Start at end of the "if" block so the covered part looks like it starts at the "else".
    258 		default:
    259 			panic("unexpected node type in if")
    260 		}
    261 		ast.Walk(f, n.Else)
    262 		return nil
    263 	case *ast.SelectStmt:
    264 		// Don't annotate an empty select - creates a syntax error.
    265 		if n.Body == nil || len(n.Body.List) == 0 {
    266 			return nil
    267 		}
    268 	case *ast.SwitchStmt:
    269 		// Don't annotate an empty switch - creates a syntax error.
    270 		if n.Body == nil || len(n.Body.List) == 0 {
    271 			if n.Init != nil {
    272 				ast.Walk(f, n.Init)
    273 			}
    274 			if n.Tag != nil {
    275 				ast.Walk(f, n.Tag)
    276 			}
    277 			return nil
    278 		}
    279 	case *ast.TypeSwitchStmt:
    280 		// Don't annotate an empty type switch - creates a syntax error.
    281 		if n.Body == nil || len(n.Body.List) == 0 {
    282 			if n.Init != nil {
    283 				ast.Walk(f, n.Init)
    284 			}
    285 			ast.Walk(f, n.Assign)
    286 			return nil
    287 		}
    288 	}
    289 	return f
    290 }
    291 
    292 // unquote returns the unquoted string.
    293 func unquote(s string) string {
    294 	t, err := strconv.Unquote(s)
    295 	if err != nil {
    296 		log.Fatalf("cover: improperly quoted string %q\n", s)
    297 	}
    298 	return t
    299 }
    300 
    301 var slashslash = []byte("//")
    302 
    303 func annotate(name string) {
    304 	fset := token.NewFileSet()
    305 	content, err := ioutil.ReadFile(name)
    306 	if err != nil {
    307 		log.Fatalf("cover: %s: %s", name, err)
    308 	}
    309 	parsedFile, err := parser.ParseFile(fset, name, content, parser.ParseComments)
    310 	if err != nil {
    311 		log.Fatalf("cover: %s: %s", name, err)
    312 	}
    313 
    314 	file := &File{
    315 		fset:    fset,
    316 		name:    name,
    317 		content: content,
    318 		edit:    edit.NewBuffer(content),
    319 		astFile: parsedFile,
    320 	}
    321 	if *mode == "atomic" {
    322 		// Add import of sync/atomic immediately after package clause.
    323 		// We do this even if there is an existing import, because the
    324 		// existing import may be shadowed at any given place we want
    325 		// to refer to it, and our name (_cover_atomic_) is less likely to
    326 		// be shadowed.
    327 		file.edit.Insert(file.offset(file.astFile.Name.End()),
    328 			fmt.Sprintf("; import %s %q", atomicPackageName, atomicPackagePath))
    329 	}
    330 
    331 	ast.Walk(file, file.astFile)
    332 	newContent := file.edit.Bytes()
    333 
    334 	fd := os.Stdout
    335 	if *output != "" {
    336 		var err error
    337 		fd, err = os.Create(*output)
    338 		if err != nil {
    339 			log.Fatalf("cover: %s", err)
    340 		}
    341 	}
    342 
    343 	fmt.Fprintf(fd, "//line %s:1\n", name)
    344 	fd.Write(newContent)
    345 
    346 	// After printing the source tree, add some declarations for the counters etc.
    347 	// We could do this by adding to the tree, but it's easier just to print the text.
    348 	file.addVariables(fd)
    349 }
    350 
    351 // setCounterStmt returns the expression: __count[23] = 1.
    352 func setCounterStmt(f *File, counter string) string {
    353 	return fmt.Sprintf("%s = 1", counter)
    354 }
    355 
    356 // incCounterStmt returns the expression: __count[23]++.
    357 func incCounterStmt(f *File, counter string) string {
    358 	return fmt.Sprintf("%s++", counter)
    359 }
    360 
    361 // atomicCounterStmt returns the expression: atomic.AddUint32(&__count[23], 1)
    362 func atomicCounterStmt(f *File, counter string) string {
    363 	return fmt.Sprintf("%s.AddUint32(&%s, 1)", atomicPackageName, counter)
    364 }
    365 
    366 // newCounter creates a new counter expression of the appropriate form.
    367 func (f *File) newCounter(start, end token.Pos, numStmt int) string {
    368 	stmt := counterStmt(f, fmt.Sprintf("%s.Count[%d]", *varVar, len(f.blocks)))
    369 	f.blocks = append(f.blocks, Block{start, end, numStmt})
    370 	return stmt
    371 }
    372 
    373 // addCounters takes a list of statements and adds counters to the beginning of
    374 // each basic block at the top level of that list. For instance, given
    375 //
    376 //	S1
    377 //	if cond {
    378 //		S2
    379 // 	}
    380 //	S3
    381 //
    382 // counters will be added before S1 and before S3. The block containing S2
    383 // will be visited in a separate call.
    384 // TODO: Nested simple blocks get unnecessary (but correct) counters
    385 func (f *File) addCounters(pos, insertPos, blockEnd token.Pos, list []ast.Stmt, extendToClosingBrace bool) {
    386 	// Special case: make sure we add a counter to an empty block. Can't do this below
    387 	// or we will add a counter to an empty statement list after, say, a return statement.
    388 	if len(list) == 0 {
    389 		f.edit.Insert(f.offset(insertPos), f.newCounter(insertPos, blockEnd, 0)+";")
    390 		return
    391 	}
    392 	// We have a block (statement list), but it may have several basic blocks due to the
    393 	// appearance of statements that affect the flow of control.
    394 	for {
    395 		// Find first statement that affects flow of control (break, continue, if, etc.).
    396 		// It will be the last statement of this basic block.
    397 		var last int
    398 		end := blockEnd
    399 		for last = 0; last < len(list); last++ {
    400 			stmt := list[last]
    401 			end = f.statementBoundary(stmt)
    402 			if f.endsBasicSourceBlock(stmt) {
    403 				// If it is a labeled statement, we need to place a counter between
    404 				// the label and its statement because it may be the target of a goto
    405 				// and thus start a basic block. That is, given
    406 				//	foo: stmt
    407 				// we need to create
    408 				//	foo: ; stmt
    409 				// and mark the label as a block-terminating statement.
    410 				// The result will then be
    411 				//	foo: COUNTER[n]++; stmt
    412 				// However, we can't do this if the labeled statement is already
    413 				// a control statement, such as a labeled for.
    414 				if label, isLabel := stmt.(*ast.LabeledStmt); isLabel && !f.isControl(label.Stmt) {
    415 					newLabel := *label
    416 					newLabel.Stmt = &ast.EmptyStmt{
    417 						Semicolon: label.Stmt.Pos(),
    418 						Implicit:  true,
    419 					}
    420 					end = label.Pos() // Previous block ends before the label.
    421 					list[last] = &newLabel
    422 					// Open a gap and drop in the old statement, now without a label.
    423 					list = append(list, nil)
    424 					copy(list[last+1:], list[last:])
    425 					list[last+1] = label.Stmt
    426 				}
    427 				last++
    428 				extendToClosingBrace = false // Block is broken up now.
    429 				break
    430 			}
    431 		}
    432 		if extendToClosingBrace {
    433 			end = blockEnd
    434 		}
    435 		if pos != end { // Can have no source to cover if e.g. blocks abut.
    436 			f.edit.Insert(f.offset(insertPos), f.newCounter(pos, end, last)+";")
    437 		}
    438 		list = list[last:]
    439 		if len(list) == 0 {
    440 			break
    441 		}
    442 		pos = list[0].Pos()
    443 		insertPos = pos
    444 	}
    445 }
    446 
    447 // hasFuncLiteral reports the existence and position of the first func literal
    448 // in the node, if any. If a func literal appears, it usually marks the termination
    449 // of a basic block because the function body is itself a block.
    450 // Therefore we draw a line at the start of the body of the first function literal we find.
    451 // TODO: what if there's more than one? Probably doesn't matter much.
    452 func hasFuncLiteral(n ast.Node) (bool, token.Pos) {
    453 	if n == nil {
    454 		return false, 0
    455 	}
    456 	var literal funcLitFinder
    457 	ast.Walk(&literal, n)
    458 	return literal.found(), token.Pos(literal)
    459 }
    460 
    461 // statementBoundary finds the location in s that terminates the current basic
    462 // block in the source.
    463 func (f *File) statementBoundary(s ast.Stmt) token.Pos {
    464 	// Control flow statements are easy.
    465 	switch s := s.(type) {
    466 	case *ast.BlockStmt:
    467 		// Treat blocks like basic blocks to avoid overlapping counters.
    468 		return s.Lbrace
    469 	case *ast.IfStmt:
    470 		found, pos := hasFuncLiteral(s.Init)
    471 		if found {
    472 			return pos
    473 		}
    474 		found, pos = hasFuncLiteral(s.Cond)
    475 		if found {
    476 			return pos
    477 		}
    478 		return s.Body.Lbrace
    479 	case *ast.ForStmt:
    480 		found, pos := hasFuncLiteral(s.Init)
    481 		if found {
    482 			return pos
    483 		}
    484 		found, pos = hasFuncLiteral(s.Cond)
    485 		if found {
    486 			return pos
    487 		}
    488 		found, pos = hasFuncLiteral(s.Post)
    489 		if found {
    490 			return pos
    491 		}
    492 		return s.Body.Lbrace
    493 	case *ast.LabeledStmt:
    494 		return f.statementBoundary(s.Stmt)
    495 	case *ast.RangeStmt:
    496 		found, pos := hasFuncLiteral(s.X)
    497 		if found {
    498 			return pos
    499 		}
    500 		return s.Body.Lbrace
    501 	case *ast.SwitchStmt:
    502 		found, pos := hasFuncLiteral(s.Init)
    503 		if found {
    504 			return pos
    505 		}
    506 		found, pos = hasFuncLiteral(s.Tag)
    507 		if found {
    508 			return pos
    509 		}
    510 		return s.Body.Lbrace
    511 	case *ast.SelectStmt:
    512 		return s.Body.Lbrace
    513 	case *ast.TypeSwitchStmt:
    514 		found, pos := hasFuncLiteral(s.Init)
    515 		if found {
    516 			return pos
    517 		}
    518 		return s.Body.Lbrace
    519 	}
    520 	// If not a control flow statement, it is a declaration, expression, call, etc. and it may have a function literal.
    521 	// If it does, that's tricky because we want to exclude the body of the function from this block.
    522 	// Draw a line at the start of the body of the first function literal we find.
    523 	// TODO: what if there's more than one? Probably doesn't matter much.
    524 	found, pos := hasFuncLiteral(s)
    525 	if found {
    526 		return pos
    527 	}
    528 	return s.End()
    529 }
    530 
    531 // endsBasicSourceBlock reports whether s changes the flow of control: break, if, etc.,
    532 // or if it's just problematic, for instance contains a function literal, which will complicate
    533 // accounting due to the block-within-an expression.
    534 func (f *File) endsBasicSourceBlock(s ast.Stmt) bool {
    535 	switch s := s.(type) {
    536 	case *ast.BlockStmt:
    537 		// Treat blocks like basic blocks to avoid overlapping counters.
    538 		return true
    539 	case *ast.BranchStmt:
    540 		return true
    541 	case *ast.ForStmt:
    542 		return true
    543 	case *ast.IfStmt:
    544 		return true
    545 	case *ast.LabeledStmt:
    546 		return true // A goto may branch here, starting a new basic block.
    547 	case *ast.RangeStmt:
    548 		return true
    549 	case *ast.SwitchStmt:
    550 		return true
    551 	case *ast.SelectStmt:
    552 		return true
    553 	case *ast.TypeSwitchStmt:
    554 		return true
    555 	case *ast.ExprStmt:
    556 		// Calls to panic change the flow.
    557 		// We really should verify that "panic" is the predefined function,
    558 		// but without type checking we can't and the likelihood of it being
    559 		// an actual problem is vanishingly small.
    560 		if call, ok := s.X.(*ast.CallExpr); ok {
    561 			if ident, ok := call.Fun.(*ast.Ident); ok && ident.Name == "panic" && len(call.Args) == 1 {
    562 				return true
    563 			}
    564 		}
    565 	}
    566 	found, _ := hasFuncLiteral(s)
    567 	return found
    568 }
    569 
    570 // isControl reports whether s is a control statement that, if labeled, cannot be
    571 // separated from its label.
    572 func (f *File) isControl(s ast.Stmt) bool {
    573 	switch s.(type) {
    574 	case *ast.ForStmt, *ast.RangeStmt, *ast.SwitchStmt, *ast.SelectStmt, *ast.TypeSwitchStmt:
    575 		return true
    576 	}
    577 	return false
    578 }
    579 
    580 // funcLitFinder implements the ast.Visitor pattern to find the location of any
    581 // function literal in a subtree.
    582 type funcLitFinder token.Pos
    583 
    584 func (f *funcLitFinder) Visit(node ast.Node) (w ast.Visitor) {
    585 	if f.found() {
    586 		return nil // Prune search.
    587 	}
    588 	switch n := node.(type) {
    589 	case *ast.FuncLit:
    590 		*f = funcLitFinder(n.Body.Lbrace)
    591 		return nil // Prune search.
    592 	}
    593 	return f
    594 }
    595 
    596 func (f *funcLitFinder) found() bool {
    597 	return token.Pos(*f) != token.NoPos
    598 }
    599 
    600 // Sort interface for []block1; used for self-check in addVariables.
    601 
    602 type block1 struct {
    603 	Block
    604 	index int
    605 }
    606 
    607 type blockSlice []block1
    608 
    609 func (b blockSlice) Len() int           { return len(b) }
    610 func (b blockSlice) Less(i, j int) bool { return b[i].startByte < b[j].startByte }
    611 func (b blockSlice) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
    612 
    613 // offset translates a token position into a 0-indexed byte offset.
    614 func (f *File) offset(pos token.Pos) int {
    615 	return f.fset.Position(pos).Offset
    616 }
    617 
    618 // addVariables adds to the end of the file the declarations to set up the counter and position variables.
    619 func (f *File) addVariables(w io.Writer) {
    620 	// Self-check: Verify that the instrumented basic blocks are disjoint.
    621 	t := make([]block1, len(f.blocks))
    622 	for i := range f.blocks {
    623 		t[i].Block = f.blocks[i]
    624 		t[i].index = i
    625 	}
    626 	sort.Sort(blockSlice(t))
    627 	for i := 1; i < len(t); i++ {
    628 		if t[i-1].endByte > t[i].startByte {
    629 			fmt.Fprintf(os.Stderr, "cover: internal error: block %d overlaps block %d\n", t[i-1].index, t[i].index)
    630 			// Note: error message is in byte positions, not token positions.
    631 			fmt.Fprintf(os.Stderr, "\t%s:#%d,#%d %s:#%d,#%d\n",
    632 				f.name, f.offset(t[i-1].startByte), f.offset(t[i-1].endByte),
    633 				f.name, f.offset(t[i].startByte), f.offset(t[i].endByte))
    634 		}
    635 	}
    636 
    637 	// Declare the coverage struct as a package-level variable.
    638 	fmt.Fprintf(w, "\nvar %s = struct {\n", *varVar)
    639 	fmt.Fprintf(w, "\tCount     [%d]uint32\n", len(f.blocks))
    640 	fmt.Fprintf(w, "\tPos       [3 * %d]uint32\n", len(f.blocks))
    641 	fmt.Fprintf(w, "\tNumStmt   [%d]uint16\n", len(f.blocks))
    642 	fmt.Fprintf(w, "} {\n")
    643 
    644 	// Initialize the position array field.
    645 	fmt.Fprintf(w, "\tPos: [3 * %d]uint32{\n", len(f.blocks))
    646 
    647 	// A nice long list of positions. Each position is encoded as follows to reduce size:
    648 	// - 32-bit starting line number
    649 	// - 32-bit ending line number
    650 	// - (16 bit ending column number << 16) | (16-bit starting column number).
    651 	for i, block := range f.blocks {
    652 		start := f.fset.Position(block.startByte)
    653 		end := f.fset.Position(block.endByte)
    654 		fmt.Fprintf(w, "\t\t%d, %d, %#x, // [%d]\n", start.Line, end.Line, (end.Column&0xFFFF)<<16|(start.Column&0xFFFF), i)
    655 	}
    656 
    657 	// Close the position array.
    658 	fmt.Fprintf(w, "\t},\n")
    659 
    660 	// Initialize the position array field.
    661 	fmt.Fprintf(w, "\tNumStmt: [%d]uint16{\n", len(f.blocks))
    662 
    663 	// A nice long list of statements-per-block, so we can give a conventional
    664 	// valuation of "percent covered". To save space, it's a 16-bit number, so we
    665 	// clamp it if it overflows - won't matter in practice.
    666 	for i, block := range f.blocks {
    667 		n := block.numStmt
    668 		if n > 1<<16-1 {
    669 			n = 1<<16 - 1
    670 		}
    671 		fmt.Fprintf(w, "\t\t%d, // %d\n", n, i)
    672 	}
    673 
    674 	// Close the statements-per-block array.
    675 	fmt.Fprintf(w, "\t},\n")
    676 
    677 	// Close the struct initialization.
    678 	fmt.Fprintf(w, "}\n")
    679 
    680 	// Emit a reference to the atomic package to avoid
    681 	// import and not used error when there's no code in a file.
    682 	if *mode == "atomic" {
    683 		fmt.Fprintf(w, "var _ = %s.LoadUint32\n", atomicPackageName)
    684 	}
    685 }
    686