blob: 9c8529f7eb600ab5e0457595343a3464aae44004 [file] [log] [blame]
Dan Willemsen09eb3b12015-09-16 14:34:17 -07001// 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 "flag"
10 "fmt"
11 "go/ast"
12 "go/parser"
Dan Willemsen09eb3b12015-09-16 14:34:17 -070013 "go/token"
14 "io"
Dan Willemsen09eb3b12015-09-16 14:34:17 -070015 "log"
16 "os"
Dan Willemsen09eb3b12015-09-16 14:34:17 -070017 "sort"
Dan Willemsena3223282018-02-27 19:41:43 -080018
19 "cmd/internal/edit"
20 "cmd/internal/objabi"
Dan Willemsen09eb3b12015-09-16 14:34:17 -070021)
22
23const usageMessage = "" +
24 `Usage of 'go tool cover':
25Given a coverage profile produced by 'go test':
26 go test -coverprofile=c.out
27
28Open a web browser displaying annotated source code:
29 go tool cover -html=c.out
30
31Write out an HTML file instead of launching a web browser:
32 go tool cover -html=c.out -o coverage.html
33
34Display coverage percentages to stdout for each function:
35 go tool cover -func=c.out
36
37Finally, to generate modified source code with coverage annotations
38(what go test -cover does):
39 go tool cover -mode=set -var=CoverageVariableName program.go
40`
41
42func usage() {
Dan Willemsenbc60c3c2021-12-15 01:09:00 -080043 fmt.Fprint(os.Stderr, usageMessage)
44 fmt.Fprintln(os.Stderr, "\nFlags:")
Dan Willemsen09eb3b12015-09-16 14:34:17 -070045 flag.PrintDefaults()
46 fmt.Fprintln(os.Stderr, "\n Only one of -html, -func, or -mode may be set.")
47 os.Exit(2)
48}
49
50var (
51 mode = flag.String("mode", "", "coverage mode: set, count, atomic")
52 varVar = flag.String("var", "GoCover", "name of coverage variable to generate")
53 output = flag.String("o", "", "file for output; default: stdout")
54 htmlOut = flag.String("html", "", "generate HTML representation of coverage profile")
55 funcOut = flag.String("func", "", "output coverage profile information for each function")
56)
57
58var profile string // The profile to read; the value of -html or -func
59
Dan Willemsena3223282018-02-27 19:41:43 -080060var counterStmt func(*File, string) string
Dan Willemsen09eb3b12015-09-16 14:34:17 -070061
62const (
63 atomicPackagePath = "sync/atomic"
64 atomicPackageName = "_cover_atomic_"
65)
66
67func main() {
Dan Willemsena3223282018-02-27 19:41:43 -080068 objabi.AddVersionFlag()
Dan Willemsen09eb3b12015-09-16 14:34:17 -070069 flag.Usage = usage
70 flag.Parse()
71
72 // Usage information when no arguments.
73 if flag.NFlag() == 0 && flag.NArg() == 0 {
74 flag.Usage()
75 }
76
77 err := parseFlags()
78 if err != nil {
79 fmt.Fprintln(os.Stderr, err)
80 fmt.Fprintln(os.Stderr, `For usage information, run "go tool cover -help"`)
81 os.Exit(2)
82 }
83
84 // Generate coverage-annotated source.
85 if *mode != "" {
86 annotate(flag.Arg(0))
87 return
88 }
89
90 // Output HTML or function coverage information.
91 if *htmlOut != "" {
92 err = htmlOutput(profile, *output)
93 } else {
94 err = funcOutput(profile, *output)
95 }
96
97 if err != nil {
98 fmt.Fprintf(os.Stderr, "cover: %v\n", err)
99 os.Exit(2)
100 }
101}
102
103// parseFlags sets the profile and counterStmt globals and performs validations.
104func parseFlags() error {
105 profile = *htmlOut
106 if *funcOut != "" {
107 if profile != "" {
108 return fmt.Errorf("too many options")
109 }
110 profile = *funcOut
111 }
112
113 // Must either display a profile or rewrite Go source.
114 if (profile == "") == (*mode == "") {
115 return fmt.Errorf("too many options")
116 }
117
Colin Cross430342c2019-09-07 08:36:04 -0700118 if *varVar != "" && !token.IsIdentifier(*varVar) {
Colin Crossd9c6b802019-03-19 21:10:31 -0700119 return fmt.Errorf("-var: %q is not a valid identifier", *varVar)
120 }
121
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700122 if *mode != "" {
123 switch *mode {
124 case "set":
125 counterStmt = setCounterStmt
126 case "count":
127 counterStmt = incCounterStmt
128 case "atomic":
129 counterStmt = atomicCounterStmt
130 default:
131 return fmt.Errorf("unknown -mode %v", *mode)
132 }
133
134 if flag.NArg() == 0 {
135 return fmt.Errorf("missing source file")
136 } else if flag.NArg() == 1 {
137 return nil
138 }
139 } else if flag.NArg() == 0 {
140 return nil
141 }
142 return fmt.Errorf("too many arguments")
143}
144
145// Block represents the information about a basic block to be recorded in the analysis.
146// Note: Our definition of basic block is based on control structures; we don't break
147// apart && and ||. We could but it doesn't seem important enough to bother.
148type Block struct {
149 startByte token.Pos
150 endByte token.Pos
151 numStmt int
152}
153
154// File is a wrapper for the state of a file used in the parser.
155// The basic parse tree walker is a method of this type.
156type File struct {
Dan Willemsena3223282018-02-27 19:41:43 -0800157 fset *token.FileSet
158 name string // Name of file.
159 astFile *ast.File
160 blocks []Block
161 content []byte
162 edit *edit.Buffer
163}
164
165// findText finds text in the original source, starting at pos.
166// It correctly skips over comments and assumes it need not
167// handle quoted strings.
168// It returns a byte offset within f.src.
169func (f *File) findText(pos token.Pos, text string) int {
170 b := []byte(text)
171 start := f.offset(pos)
172 i := start
173 s := f.content
174 for i < len(s) {
175 if bytes.HasPrefix(s[i:], b) {
176 return i
177 }
178 if i+2 <= len(s) && s[i] == '/' && s[i+1] == '/' {
179 for i < len(s) && s[i] != '\n' {
180 i++
181 }
182 continue
183 }
184 if i+2 <= len(s) && s[i] == '/' && s[i+1] == '*' {
185 for i += 2; ; i++ {
186 if i+2 > len(s) {
187 return 0
188 }
189 if s[i] == '*' && s[i+1] == '/' {
190 i += 2
191 break
192 }
193 }
194 continue
195 }
196 i++
197 }
198 return -1
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700199}
200
201// Visit implements the ast.Visitor interface.
202func (f *File) Visit(node ast.Node) ast.Visitor {
203 switch n := node.(type) {
204 case *ast.BlockStmt:
205 // If it's a switch or select, the body is a list of case clauses; don't tag the block itself.
206 if len(n.List) > 0 {
207 switch n.List[0].(type) {
208 case *ast.CaseClause: // switch
209 for _, n := range n.List {
210 clause := n.(*ast.CaseClause)
Dan Willemsena3223282018-02-27 19:41:43 -0800211 f.addCounters(clause.Colon+1, clause.Colon+1, clause.End(), clause.Body, false)
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700212 }
213 return f
214 case *ast.CommClause: // select
215 for _, n := range n.List {
216 clause := n.(*ast.CommClause)
Dan Willemsena3223282018-02-27 19:41:43 -0800217 f.addCounters(clause.Colon+1, clause.Colon+1, clause.End(), clause.Body, false)
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700218 }
219 return f
220 }
221 }
Dan Willemsena3223282018-02-27 19:41:43 -0800222 f.addCounters(n.Lbrace, n.Lbrace+1, n.Rbrace+1, n.List, true) // +1 to step past closing brace.
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700223 case *ast.IfStmt:
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700224 if n.Init != nil {
225 ast.Walk(f, n.Init)
226 }
227 ast.Walk(f, n.Cond)
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700228 ast.Walk(f, n.Body)
229 if n.Else == nil {
230 return nil
231 }
232 // The elses are special, because if we have
233 // if x {
234 // } else if y {
235 // }
236 // we want to cover the "if y". To do this, we need a place to drop the counter,
237 // so we add a hidden block:
238 // if x {
239 // } else {
240 // if y {
241 // }
242 // }
Dan Willemsena3223282018-02-27 19:41:43 -0800243 elseOffset := f.findText(n.Body.End(), "else")
244 if elseOffset < 0 {
245 panic("lost else")
246 }
Dan Willemsenc7413322018-08-27 23:21:26 -0700247 f.edit.Insert(elseOffset+4, "{")
Dan Willemsena3223282018-02-27 19:41:43 -0800248 f.edit.Insert(f.offset(n.Else.End()), "}")
Dan Willemsenc7413322018-08-27 23:21:26 -0700249
250 // We just created a block, now walk it.
251 // Adjust the position of the new block to start after
252 // the "else". That will cause it to follow the "{"
253 // we inserted above.
254 pos := f.fset.File(n.Body.End()).Pos(elseOffset + 4)
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700255 switch stmt := n.Else.(type) {
256 case *ast.IfStmt:
257 block := &ast.BlockStmt{
Dan Willemsenc7413322018-08-27 23:21:26 -0700258 Lbrace: pos,
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700259 List: []ast.Stmt{stmt},
260 Rbrace: stmt.End(),
261 }
262 n.Else = block
263 case *ast.BlockStmt:
Dan Willemsenc7413322018-08-27 23:21:26 -0700264 stmt.Lbrace = pos
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700265 default:
266 panic("unexpected node type in if")
267 }
268 ast.Walk(f, n.Else)
269 return nil
270 case *ast.SelectStmt:
271 // Don't annotate an empty select - creates a syntax error.
272 if n.Body == nil || len(n.Body.List) == 0 {
273 return nil
274 }
275 case *ast.SwitchStmt:
276 // Don't annotate an empty switch - creates a syntax error.
277 if n.Body == nil || len(n.Body.List) == 0 {
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700278 if n.Init != nil {
279 ast.Walk(f, n.Init)
280 }
281 if n.Tag != nil {
282 ast.Walk(f, n.Tag)
283 }
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700284 return nil
285 }
286 case *ast.TypeSwitchStmt:
287 // Don't annotate an empty type switch - creates a syntax error.
288 if n.Body == nil || len(n.Body.List) == 0 {
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700289 if n.Init != nil {
290 ast.Walk(f, n.Init)
291 }
292 ast.Walk(f, n.Assign)
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700293 return nil
294 }
Patrice Arruda748609c2020-06-25 12:12:21 -0700295 case *ast.FuncDecl:
296 // Don't annotate functions with blank names - they cannot be executed.
297 if n.Name.Name == "_" {
298 return nil
299 }
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700300 }
301 return f
302}
303
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700304func annotate(name string) {
305 fset := token.NewFileSet()
Colin Cross1f805522021-05-14 11:10:59 -0700306 content, err := os.ReadFile(name)
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700307 if err != nil {
308 log.Fatalf("cover: %s: %s", name, err)
309 }
310 parsedFile, err := parser.ParseFile(fset, name, content, parser.ParseComments)
311 if err != nil {
312 log.Fatalf("cover: %s: %s", name, err)
313 }
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700314
315 file := &File{
Dan Willemsena3223282018-02-27 19:41:43 -0800316 fset: fset,
317 name: name,
318 content: content,
319 edit: edit.NewBuffer(content),
320 astFile: parsedFile,
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700321 }
322 if *mode == "atomic" {
Dan Willemsena3223282018-02-27 19:41:43 -0800323 // Add import of sync/atomic immediately after package clause.
324 // We do this even if there is an existing import, because the
325 // existing import may be shadowed at any given place we want
326 // to refer to it, and our name (_cover_atomic_) is less likely to
327 // be shadowed.
328 file.edit.Insert(file.offset(file.astFile.Name.End()),
329 fmt.Sprintf("; import %s %q", atomicPackageName, atomicPackagePath))
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700330 }
Dan Willemsenebae3022017-01-13 23:01:08 -0800331
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700332 ast.Walk(file, file.astFile)
Dan Willemsena3223282018-02-27 19:41:43 -0800333 newContent := file.edit.Bytes()
334
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700335 fd := os.Stdout
336 if *output != "" {
337 var err error
338 fd, err = os.Create(*output)
339 if err != nil {
340 log.Fatalf("cover: %s", err)
341 }
342 }
Dan Willemsenebae3022017-01-13 23:01:08 -0800343
Dan Willemsena3223282018-02-27 19:41:43 -0800344 fmt.Fprintf(fd, "//line %s:1\n", name)
345 fd.Write(newContent)
Dan Willemsenebae3022017-01-13 23:01:08 -0800346
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700347 // After printing the source tree, add some declarations for the counters etc.
348 // We could do this by adding to the tree, but it's easier just to print the text.
349 file.addVariables(fd)
350}
351
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700352// setCounterStmt returns the expression: __count[23] = 1.
Dan Willemsena3223282018-02-27 19:41:43 -0800353func setCounterStmt(f *File, counter string) string {
354 return fmt.Sprintf("%s = 1", counter)
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700355}
356
357// incCounterStmt returns the expression: __count[23]++.
Dan Willemsena3223282018-02-27 19:41:43 -0800358func incCounterStmt(f *File, counter string) string {
359 return fmt.Sprintf("%s++", counter)
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700360}
361
362// atomicCounterStmt returns the expression: atomic.AddUint32(&__count[23], 1)
Dan Willemsena3223282018-02-27 19:41:43 -0800363func atomicCounterStmt(f *File, counter string) string {
364 return fmt.Sprintf("%s.AddUint32(&%s, 1)", atomicPackageName, counter)
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700365}
366
367// newCounter creates a new counter expression of the appropriate form.
Dan Willemsena3223282018-02-27 19:41:43 -0800368func (f *File) newCounter(start, end token.Pos, numStmt int) string {
369 stmt := counterStmt(f, fmt.Sprintf("%s.Count[%d]", *varVar, len(f.blocks)))
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700370 f.blocks = append(f.blocks, Block{start, end, numStmt})
371 return stmt
372}
373
374// addCounters takes a list of statements and adds counters to the beginning of
375// each basic block at the top level of that list. For instance, given
376//
377// S1
378// if cond {
379// S2
380// }
381// S3
382//
383// counters will be added before S1 and before S3. The block containing S2
384// will be visited in a separate call.
385// TODO: Nested simple blocks get unnecessary (but correct) counters
Dan Willemsena3223282018-02-27 19:41:43 -0800386func (f *File) addCounters(pos, insertPos, blockEnd token.Pos, list []ast.Stmt, extendToClosingBrace bool) {
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700387 // Special case: make sure we add a counter to an empty block. Can't do this below
388 // or we will add a counter to an empty statement list after, say, a return statement.
389 if len(list) == 0 {
Dan Willemsena3223282018-02-27 19:41:43 -0800390 f.edit.Insert(f.offset(insertPos), f.newCounter(insertPos, blockEnd, 0)+";")
391 return
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700392 }
Colin Cross430342c2019-09-07 08:36:04 -0700393 // Make a copy of the list, as we may mutate it and should leave the
394 // existing list intact.
395 list = append([]ast.Stmt(nil), list...)
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700396 // We have a block (statement list), but it may have several basic blocks due to the
397 // appearance of statements that affect the flow of control.
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700398 for {
399 // Find first statement that affects flow of control (break, continue, if, etc.).
400 // It will be the last statement of this basic block.
401 var last int
402 end := blockEnd
403 for last = 0; last < len(list); last++ {
Dan Willemsenebae3022017-01-13 23:01:08 -0800404 stmt := list[last]
405 end = f.statementBoundary(stmt)
406 if f.endsBasicSourceBlock(stmt) {
407 // If it is a labeled statement, we need to place a counter between
408 // the label and its statement because it may be the target of a goto
409 // and thus start a basic block. That is, given
410 // foo: stmt
411 // we need to create
412 // foo: ; stmt
413 // and mark the label as a block-terminating statement.
414 // The result will then be
415 // foo: COUNTER[n]++; stmt
416 // However, we can't do this if the labeled statement is already
417 // a control statement, such as a labeled for.
418 if label, isLabel := stmt.(*ast.LabeledStmt); isLabel && !f.isControl(label.Stmt) {
419 newLabel := *label
420 newLabel.Stmt = &ast.EmptyStmt{
421 Semicolon: label.Stmt.Pos(),
422 Implicit: true,
423 }
424 end = label.Pos() // Previous block ends before the label.
425 list[last] = &newLabel
426 // Open a gap and drop in the old statement, now without a label.
427 list = append(list, nil)
428 copy(list[last+1:], list[last:])
429 list[last+1] = label.Stmt
430 }
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700431 last++
Dan Willemsenebae3022017-01-13 23:01:08 -0800432 extendToClosingBrace = false // Block is broken up now.
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700433 break
434 }
435 }
436 if extendToClosingBrace {
437 end = blockEnd
438 }
439 if pos != end { // Can have no source to cover if e.g. blocks abut.
Dan Willemsena3223282018-02-27 19:41:43 -0800440 f.edit.Insert(f.offset(insertPos), f.newCounter(pos, end, last)+";")
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700441 }
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700442 list = list[last:]
443 if len(list) == 0 {
444 break
445 }
446 pos = list[0].Pos()
Dan Willemsena3223282018-02-27 19:41:43 -0800447 insertPos = pos
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700448 }
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700449}
450
451// hasFuncLiteral reports the existence and position of the first func literal
452// in the node, if any. If a func literal appears, it usually marks the termination
453// of a basic block because the function body is itself a block.
454// Therefore we draw a line at the start of the body of the first function literal we find.
455// TODO: what if there's more than one? Probably doesn't matter much.
456func hasFuncLiteral(n ast.Node) (bool, token.Pos) {
457 if n == nil {
458 return false, 0
459 }
460 var literal funcLitFinder
461 ast.Walk(&literal, n)
462 return literal.found(), token.Pos(literal)
463}
464
465// statementBoundary finds the location in s that terminates the current basic
466// block in the source.
467func (f *File) statementBoundary(s ast.Stmt) token.Pos {
468 // Control flow statements are easy.
469 switch s := s.(type) {
470 case *ast.BlockStmt:
471 // Treat blocks like basic blocks to avoid overlapping counters.
472 return s.Lbrace
473 case *ast.IfStmt:
474 found, pos := hasFuncLiteral(s.Init)
475 if found {
476 return pos
477 }
478 found, pos = hasFuncLiteral(s.Cond)
479 if found {
480 return pos
481 }
482 return s.Body.Lbrace
483 case *ast.ForStmt:
484 found, pos := hasFuncLiteral(s.Init)
485 if found {
486 return pos
487 }
488 found, pos = hasFuncLiteral(s.Cond)
489 if found {
490 return pos
491 }
492 found, pos = hasFuncLiteral(s.Post)
493 if found {
494 return pos
495 }
496 return s.Body.Lbrace
497 case *ast.LabeledStmt:
498 return f.statementBoundary(s.Stmt)
499 case *ast.RangeStmt:
500 found, pos := hasFuncLiteral(s.X)
501 if found {
502 return pos
503 }
504 return s.Body.Lbrace
505 case *ast.SwitchStmt:
506 found, pos := hasFuncLiteral(s.Init)
507 if found {
508 return pos
509 }
510 found, pos = hasFuncLiteral(s.Tag)
511 if found {
512 return pos
513 }
514 return s.Body.Lbrace
515 case *ast.SelectStmt:
516 return s.Body.Lbrace
517 case *ast.TypeSwitchStmt:
518 found, pos := hasFuncLiteral(s.Init)
519 if found {
520 return pos
521 }
522 return s.Body.Lbrace
523 }
524 // If not a control flow statement, it is a declaration, expression, call, etc. and it may have a function literal.
525 // If it does, that's tricky because we want to exclude the body of the function from this block.
526 // Draw a line at the start of the body of the first function literal we find.
527 // TODO: what if there's more than one? Probably doesn't matter much.
528 found, pos := hasFuncLiteral(s)
529 if found {
530 return pos
531 }
532 return s.End()
533}
534
535// endsBasicSourceBlock reports whether s changes the flow of control: break, if, etc.,
536// or if it's just problematic, for instance contains a function literal, which will complicate
537// accounting due to the block-within-an expression.
538func (f *File) endsBasicSourceBlock(s ast.Stmt) bool {
539 switch s := s.(type) {
540 case *ast.BlockStmt:
541 // Treat blocks like basic blocks to avoid overlapping counters.
542 return true
543 case *ast.BranchStmt:
544 return true
545 case *ast.ForStmt:
546 return true
547 case *ast.IfStmt:
548 return true
549 case *ast.LabeledStmt:
Dan Willemsenebae3022017-01-13 23:01:08 -0800550 return true // A goto may branch here, starting a new basic block.
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700551 case *ast.RangeStmt:
552 return true
553 case *ast.SwitchStmt:
554 return true
555 case *ast.SelectStmt:
556 return true
557 case *ast.TypeSwitchStmt:
558 return true
559 case *ast.ExprStmt:
560 // Calls to panic change the flow.
561 // We really should verify that "panic" is the predefined function,
562 // but without type checking we can't and the likelihood of it being
563 // an actual problem is vanishingly small.
564 if call, ok := s.X.(*ast.CallExpr); ok {
565 if ident, ok := call.Fun.(*ast.Ident); ok && ident.Name == "panic" && len(call.Args) == 1 {
566 return true
567 }
568 }
569 }
570 found, _ := hasFuncLiteral(s)
571 return found
572}
573
Dan Willemsenebae3022017-01-13 23:01:08 -0800574// isControl reports whether s is a control statement that, if labeled, cannot be
575// separated from its label.
576func (f *File) isControl(s ast.Stmt) bool {
577 switch s.(type) {
578 case *ast.ForStmt, *ast.RangeStmt, *ast.SwitchStmt, *ast.SelectStmt, *ast.TypeSwitchStmt:
579 return true
580 }
581 return false
582}
583
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700584// funcLitFinder implements the ast.Visitor pattern to find the location of any
585// function literal in a subtree.
586type funcLitFinder token.Pos
587
588func (f *funcLitFinder) Visit(node ast.Node) (w ast.Visitor) {
589 if f.found() {
590 return nil // Prune search.
591 }
592 switch n := node.(type) {
593 case *ast.FuncLit:
594 *f = funcLitFinder(n.Body.Lbrace)
595 return nil // Prune search.
596 }
597 return f
598}
599
600func (f *funcLitFinder) found() bool {
601 return token.Pos(*f) != token.NoPos
602}
603
604// Sort interface for []block1; used for self-check in addVariables.
605
606type block1 struct {
607 Block
608 index int
609}
610
611type blockSlice []block1
612
613func (b blockSlice) Len() int { return len(b) }
614func (b blockSlice) Less(i, j int) bool { return b[i].startByte < b[j].startByte }
615func (b blockSlice) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
616
617// offset translates a token position into a 0-indexed byte offset.
618func (f *File) offset(pos token.Pos) int {
619 return f.fset.Position(pos).Offset
620}
621
622// addVariables adds to the end of the file the declarations to set up the counter and position variables.
623func (f *File) addVariables(w io.Writer) {
624 // Self-check: Verify that the instrumented basic blocks are disjoint.
625 t := make([]block1, len(f.blocks))
626 for i := range f.blocks {
627 t[i].Block = f.blocks[i]
628 t[i].index = i
629 }
630 sort.Sort(blockSlice(t))
631 for i := 1; i < len(t); i++ {
632 if t[i-1].endByte > t[i].startByte {
633 fmt.Fprintf(os.Stderr, "cover: internal error: block %d overlaps block %d\n", t[i-1].index, t[i].index)
634 // Note: error message is in byte positions, not token positions.
635 fmt.Fprintf(os.Stderr, "\t%s:#%d,#%d %s:#%d,#%d\n",
636 f.name, f.offset(t[i-1].startByte), f.offset(t[i-1].endByte),
637 f.name, f.offset(t[i].startByte), f.offset(t[i].endByte))
638 }
639 }
640
641 // Declare the coverage struct as a package-level variable.
642 fmt.Fprintf(w, "\nvar %s = struct {\n", *varVar)
643 fmt.Fprintf(w, "\tCount [%d]uint32\n", len(f.blocks))
644 fmt.Fprintf(w, "\tPos [3 * %d]uint32\n", len(f.blocks))
645 fmt.Fprintf(w, "\tNumStmt [%d]uint16\n", len(f.blocks))
646 fmt.Fprintf(w, "} {\n")
647
648 // Initialize the position array field.
649 fmt.Fprintf(w, "\tPos: [3 * %d]uint32{\n", len(f.blocks))
650
651 // A nice long list of positions. Each position is encoded as follows to reduce size:
652 // - 32-bit starting line number
653 // - 32-bit ending line number
654 // - (16 bit ending column number << 16) | (16-bit starting column number).
655 for i, block := range f.blocks {
656 start := f.fset.Position(block.startByte)
657 end := f.fset.Position(block.endByte)
Colin Crossd9c6b802019-03-19 21:10:31 -0700658
Colin Cross430342c2019-09-07 08:36:04 -0700659 start, end = dedup(start, end)
Colin Crossd9c6b802019-03-19 21:10:31 -0700660
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700661 fmt.Fprintf(w, "\t\t%d, %d, %#x, // [%d]\n", start.Line, end.Line, (end.Column&0xFFFF)<<16|(start.Column&0xFFFF), i)
662 }
663
664 // Close the position array.
665 fmt.Fprintf(w, "\t},\n")
666
667 // Initialize the position array field.
668 fmt.Fprintf(w, "\tNumStmt: [%d]uint16{\n", len(f.blocks))
669
670 // A nice long list of statements-per-block, so we can give a conventional
671 // valuation of "percent covered". To save space, it's a 16-bit number, so we
672 // clamp it if it overflows - won't matter in practice.
673 for i, block := range f.blocks {
674 n := block.numStmt
675 if n > 1<<16-1 {
676 n = 1<<16 - 1
677 }
678 fmt.Fprintf(w, "\t\t%d, // %d\n", n, i)
679 }
680
681 // Close the statements-per-block array.
682 fmt.Fprintf(w, "\t},\n")
683
684 // Close the struct initialization.
685 fmt.Fprintf(w, "}\n")
Dan Willemsena3223282018-02-27 19:41:43 -0800686
687 // Emit a reference to the atomic package to avoid
688 // import and not used error when there's no code in a file.
689 if *mode == "atomic" {
690 fmt.Fprintf(w, "var _ = %s.LoadUint32\n", atomicPackageName)
691 }
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700692}
Colin Crossd9c6b802019-03-19 21:10:31 -0700693
Colin Cross430342c2019-09-07 08:36:04 -0700694// It is possible for positions to repeat when there is a line
695// directive that does not specify column information and the input
696// has not been passed through gofmt.
697// See issues #27530 and #30746.
698// Tests are TestHtmlUnformatted and TestLineDup.
699// We use a map to avoid duplicates.
700
701// pos2 is a pair of token.Position values, used as a map key type.
702type pos2 struct {
703 p1, p2 token.Position
Colin Crossd9c6b802019-03-19 21:10:31 -0700704}
705
Colin Cross430342c2019-09-07 08:36:04 -0700706// seenPos2 tracks whether we have seen a token.Position pair.
707var seenPos2 = make(map[pos2]bool)
708
709// dedup takes a token.Position pair and returns a pair that does not
710// duplicate any existing pair. The returned pair will have the Offset
711// fields cleared.
712func dedup(p1, p2 token.Position) (r1, r2 token.Position) {
713 key := pos2{
714 p1: p1,
715 p2: p2,
716 }
717
718 // We want to ignore the Offset fields in the map,
719 // since cover uses only file/line/column.
720 key.p1.Offset = 0
721 key.p2.Offset = 0
722
723 for seenPos2[key] {
724 key.p2.Column++
725 }
726 seenPos2[key] = true
727
728 return key.p1, key.p2
Colin Crossd9c6b802019-03-19 21:10:31 -0700729}