// File is a wrapper for the state of a file used in the parser.
// The basic parse tree walker is a method of this type.
type File struct {
- fset *token.FileSet
- name string // Name of file.
- astFile *ast.File
- blocks []Block
- atomicPkg string // Package name for "sync/atomic" in this file.
+ fset *token.FileSet
+ name string // Name of file.
+ astFile *ast.File
+ blocks []Block
+ 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.
// to appear in syntactically incorrect places. //go: appears at the beginning of
// the line and is syntactically safe.
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)
+
+ // Mark compiler directive as handled.
+ f.directives[c] = true
}
}
n.List = list
if err != nil {
log.Fatalf("cover: %s: %s", name, err)
}
- // Remove comments. Or else they interfere with new AST.
- parsedFile.Comments = nil
file := &File{
- fset: fset,
- name: name,
- astFile: parsedFile,
+ fset: fset,
+ name: name,
+ astFile: parsedFile,
+ directives: map[*ast.Comment]bool{},
}
if *mode == "atomic" {
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)
fd := os.Stdout
if *output != "" {
}
}
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)
// 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.
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.
func (f *File) intLiteral(i int) *ast.BasicLit {
node := &ast.BasicLit{
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)
}
+ // "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.
c := ".*// This comment shouldn't appear in generated go code.*"
if got, err := regexp.MatchString(c, string(file)); err != nil || got {