1
0
mirror of https://github.com/golang/go synced 2024-11-23 16:30:06 -07:00

cmd/cover: retain un-attached compiler directives

Parser doesn't attach some compiler directives to anything in the tree.
We have to explicitely retain them in the generated code. This change,
makes cover explicitely print out any compiler directive that wasn't
handled in the ast.Visitor.

Fixes #18285.

Change-Id: Ib60f253815e92d7fc85051a7f663a61116e40a91
Reviewed-on: https://go-review.googlesource.com/34563
Run-TryBot: Rob Pike <r@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
Dhananjay Nakrani 2016-12-18 19:25:37 -08:00 committed by Rob Pike
parent a0667be8ef
commit 7eee512773
3 changed files with 50 additions and 11 deletions

View File

@ -156,6 +156,7 @@ type File struct {
astFile *ast.File astFile *ast.File
blocks []Block blocks []Block
atomicPkg string // Package name for "sync/atomic" in this file. atomicPkg string // Package name for "sync/atomic" in this file.
directives map[*ast.Comment]bool // Map of compiler directives to whether it's processed in ast.Visitor or not.
} }
// Visit implements the ast.Visitor interface. // Visit implements the ast.Visitor interface.
@ -247,8 +248,11 @@ func (f *File) Visit(node ast.Node) ast.Visitor {
// to appear in syntactically incorrect places. //go: appears at the beginning of // to appear in syntactically incorrect places. //go: appears at the beginning of
// the line and is syntactically safe. // the line and is syntactically safe.
for _, c := range n.List { for _, c := range n.List {
if strings.HasPrefix(c.Text, "//go:") && f.fset.Position(c.Slash).Column == 1 { if f.isDirective(c) {
list = append(list, c) list = append(list, c)
// Mark compiler directive as handled.
f.directives[c] = true
} }
} }
n.List = list n.List = list
@ -360,17 +364,27 @@ func annotate(name string) {
if err != nil { if err != nil {
log.Fatalf("cover: %s: %s", name, err) log.Fatalf("cover: %s: %s", name, err)
} }
// Remove comments. Or else they interfere with new AST.
parsedFile.Comments = nil
file := &File{ file := &File{
fset: fset, fset: fset,
name: name, name: name,
astFile: parsedFile, astFile: parsedFile,
directives: map[*ast.Comment]bool{},
} }
if *mode == "atomic" { if *mode == "atomic" {
file.atomicPkg = file.addImport(atomicPackagePath) file.atomicPkg = file.addImport(atomicPackagePath)
} }
for _, cg := range parsedFile.Comments {
for _, c := range cg.List {
if file.isDirective(c) {
file.directives[c] = false
}
}
}
// Remove comments. Or else they interfere with new AST.
parsedFile.Comments = nil
ast.Walk(file, file.astFile) ast.Walk(file, file.astFile)
fd := os.Stdout fd := os.Stdout
if *output != "" { if *output != "" {
@ -381,6 +395,17 @@ func annotate(name string) {
} }
} }
fd.Write(initialComments(content)) // Retain '// +build' directives. fd.Write(initialComments(content)) // Retain '// +build' directives.
// Retain compiler directives that are not processed in ast.Visitor.
// Some compiler directives like "go:linkname" and "go:cgo_"
// can be not attached to anything in the tree and hence will not be printed by printer.
// So, we have to explicitely print them here.
for cd, handled := range file.directives {
if !handled {
fmt.Fprintln(fd, cd.Text)
}
}
file.print(fd) file.print(fd)
// After printing the source tree, add some declarations for the counters etc. // After printing the source tree, add some declarations for the counters etc.
// We could do this by adding to the tree, but it's easier just to print the text. // We could do this by adding to the tree, but it's easier just to print the text.
@ -391,6 +416,11 @@ func (f *File) print(w io.Writer) {
printer.Fprint(w, f.fset, f.astFile) printer.Fprint(w, f.fset, f.astFile)
} }
// isDirective reports whether a comment is a compiler directive.
func (f *File) isDirective(c *ast.Comment) bool {
return strings.HasPrefix(c.Text, "//go:") && f.fset.Position(c.Slash).Column == 1
}
// intLiteral returns an ast.BasicLit representing the integer value. // intLiteral returns an ast.BasicLit representing the integer value.
func (f *File) intLiteral(i int) *ast.BasicLit { func (f *File) intLiteral(i int) *ast.BasicLit {
node := &ast.BasicLit{ node := &ast.BasicLit{

View File

@ -90,6 +90,11 @@ func TestCover(t *testing.T) {
if got, err := regexp.MatchString(".*\n//go:nosplit\nfunc someFunction().*", string(file)); err != nil || !got { if got, err := regexp.MatchString(".*\n//go:nosplit\nfunc someFunction().*", string(file)); err != nil || !got {
t.Errorf("misplaced compiler directive: got=(%v, %v); want=(true; nil)", got, err) t.Errorf("misplaced compiler directive: got=(%v, %v); want=(true; nil)", got, err)
} }
// "go:linkname" compiler directive should be present.
if got, err := regexp.MatchString(`.*go\:linkname some\_name some\_name.*`, string(file)); err != nil || !got {
t.Errorf("'go:linkname' compiler directive not found: got=(%v, %v); want=(true; nil)", got, err)
}
// No other comments should be present in generated code. // No other comments should be present in generated code.
c := ".*// This comment shouldn't appear in generated go code.*" c := ".*// This comment shouldn't appear in generated go code.*"
if got, err := regexp.MatchString(c, string(file)); err != nil || got { if got, err := regexp.MatchString(c, string(file)); err != nil || got {

View File

@ -10,6 +10,10 @@
package main package main
import _ "unsafe" // for go:linkname
//go:linkname some_name some_name
const anything = 1e9 // Just some unlikely value that means "we got here, don't care how often" const anything = 1e9 // Just some unlikely value that means "we got here, don't care how often"
func testAll() { func testAll() {