cmd/cover: cover funcs in if, for, switch clauses

This peculiar case arose in range statements but there are other contexts
and one turned up in the auto-generated translation of the compiler.
Take care of it always.

	for i := 0; i < 0; func() {i++; q=q.Link}() { ... }

That code has been given the obvious rewrite but we should still handle it.

Odd but easy to fix (tricky to test).

Fixes #10269.

Change-Id: I66e1404eb24da15a24be7f67403e19ed66fba0a7
Reviewed-on: https://go-review.googlesource.com/8284
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Rob Pike 2015-03-31 10:47:12 -07:00
Родитель dce4131cda
Коммит 65b5a8eca7
2 изменённых файлов: 41 добавлений и 5 удалений

Просмотреть файл

@ -503,6 +503,9 @@ func (f *File) addCounters(pos, blockEnd token.Pos, list []ast.Stmt, extendToClo
// Therefore we draw a line at the start of the body of the first function literal we find. // Therefore we draw a line at the start of the body of the first function literal we find.
// TODO: what if there's more than one? Probably doesn't matter much. // TODO: what if there's more than one? Probably doesn't matter much.
func hasFuncLiteral(n ast.Node) (bool, token.Pos) { func hasFuncLiteral(n ast.Node) (bool, token.Pos) {
if n == nil {
return false, 0
}
var literal funcLitFinder var literal funcLitFinder
ast.Walk(&literal, n) ast.Walk(&literal, n)
return literal.found(), token.Pos(literal) return literal.found(), token.Pos(literal)
@ -517,24 +520,54 @@ func (f *File) statementBoundary(s ast.Stmt) token.Pos {
// Treat blocks like basic blocks to avoid overlapping counters. // Treat blocks like basic blocks to avoid overlapping counters.
return s.Lbrace return s.Lbrace
case *ast.IfStmt: case *ast.IfStmt:
found, pos := hasFuncLiteral(s.Init)
if found {
return pos
}
found, pos = hasFuncLiteral(s.Cond)
if found {
return pos
}
return s.Body.Lbrace return s.Body.Lbrace
case *ast.ForStmt: case *ast.ForStmt:
found, pos := hasFuncLiteral(s.Init)
if found {
return pos
}
found, pos = hasFuncLiteral(s.Cond)
if found {
return pos
}
found, pos = hasFuncLiteral(s.Post)
if found {
return pos
}
return s.Body.Lbrace return s.Body.Lbrace
case *ast.LabeledStmt: case *ast.LabeledStmt:
return f.statementBoundary(s.Stmt) return f.statementBoundary(s.Stmt)
case *ast.RangeStmt: case *ast.RangeStmt:
// Ranges might loop over things with function literals.: for _ = range []func(){ ... } {.
// TODO: There are a few other such possibilities, but they're extremely unlikely.
found, pos := hasFuncLiteral(s.X) found, pos := hasFuncLiteral(s.X)
if found { if found {
return pos return pos
} }
return s.Body.Lbrace return s.Body.Lbrace
case *ast.SwitchStmt: case *ast.SwitchStmt:
found, pos := hasFuncLiteral(s.Init)
if found {
return pos
}
found, pos = hasFuncLiteral(s.Tag)
if found {
return pos
}
return s.Body.Lbrace return s.Body.Lbrace
case *ast.SelectStmt: case *ast.SelectStmt:
return s.Body.Lbrace return s.Body.Lbrace
case *ast.TypeSwitchStmt: case *ast.TypeSwitchStmt:
found, pos := hasFuncLiteral(s.Init)
if found {
return pos
}
return s.Body.Lbrace return s.Body.Lbrace
} }
// If not a control flow statement, it is a declaration, expression, call, etc. and it may have a function literal. // If not a control flow statement, it is a declaration, expression, call, etc. and it may have a function literal.

9
cmd/cover/testdata/test.go поставляемый
Просмотреть файл

@ -86,10 +86,13 @@ func testIf() {
check(LINE, 0) check(LINE, 0)
} }
} }
if func(a, b int) bool { return a < b }(3, 4) {
check(LINE, 1)
}
} }
func testFor() { func testFor() {
for i := 0; i < 10; i++ { for i := 0; i < 10; func() { i++; check(LINE, 10) }() {
check(LINE, 10) check(LINE, 10)
} }
} }
@ -122,7 +125,7 @@ func testBlockRun() {
} }
func testSwitch() { func testSwitch() {
for i := 0; i < 5; i++ { for i := 0; i < 5; func() { i++; check(LINE, 5) }() {
switch i { switch i {
case 0: case 0:
check(LINE, 1) check(LINE, 1)
@ -139,7 +142,7 @@ func testSwitch() {
func testTypeSwitch() { func testTypeSwitch() {
var x = []interface{}{1, 2.0, "hi"} var x = []interface{}{1, 2.0, "hi"}
for _, v := range x { for _, v := range x {
switch v.(type) { switch func() { check(LINE, 3) }(); v.(type) {
case int: case int:
check(LINE, 1) check(LINE, 1)
case float64: case float64: