--- /dev/null
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +builder // ERROR "possible malformed \+build comment"
+// +build !ignore
+
+package main
+
+// +build toolate // ERROR "build comment appears too late in file"
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "strings"
+ "unicode"
+)
+
+var (
+ nl = []byte("\n")
+ slashSlash = []byte("//")
+ plusBuild = []byte("+build")
+)
+
+// checkBuildTag checks that build tags are in the correct location and well-formed.
+func checkBuildTag(name string, data []byte) {
+ if !*vetBuildTags && !*vetAll {
+ return
+ }
+ lines := bytes.SplitAfter(data, nl)
+
+ // Determine cutpoint where +build comments are no longer valid.
+ // They are valid in leading // comments in the file followed by
+ // a blank line.
+ var cutoff int
+ for i, line := range lines {
+ line = bytes.TrimSpace(line)
+ if len(line) == 0 {
+ cutoff = i
+ continue
+ }
+ if bytes.HasPrefix(line, slashSlash) {
+ continue
+ }
+ break
+ }
+
+ for i, line := range lines {
+ line = bytes.TrimSpace(line)
+ if !bytes.HasPrefix(line, slashSlash) {
+ continue
+ }
+ text := bytes.TrimSpace(line[2:])
+ if bytes.HasPrefix(text, plusBuild) {
+ fields := bytes.Fields(text)
+ if !bytes.Equal(fields[0], plusBuild) {
+ // Comment is something like +buildasdf not +build.
+ fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1)
+ continue
+ }
+ if i >= cutoff {
+ fmt.Fprintf(os.Stderr, "%s:%d: +build comment appears too late in file\n", name, i+1)
+ setExit(1)
+ continue
+ }
+ // Check arguments.
+ Args:
+ for _, arg := range fields[1:] {
+ for _, elem := range strings.Split(string(arg), ",") {
+ if strings.HasPrefix(elem, "!!") {
+ fmt.Fprintf(os.Stderr, "%s:%d: invalid double negative in build constraint: %s\n", name, i+1, arg)
+ setExit(1)
+ break Args
+ }
+ if strings.HasPrefix(elem, "!") {
+ elem = elem[1:]
+ }
+ for _, c := range elem {
+ if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' {
+ fmt.Fprintf(os.Stderr, "%s:%d: invalid non-alphanumeric build constraint: %s\n", name, i+1, arg)
+ setExit(1)
+ break Args
+ }
+ }
+ }
+ }
+ continue
+ }
+ // Comment with +build but not at beginning.
+ if bytes.Contains(line, plusBuild) && i < cutoff {
+ fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1)
+ continue
+ }
+ }
+}
"go/printer"
"go/token"
"io"
+ "io/ioutil"
"os"
"path/filepath"
"strconv"
// Flags to control which checks to perform
var (
vetAll = flag.Bool("all", true, "check everything; disabled if any explicit check is requested")
+ vetAtomic = flag.Bool("atomic", false, "check for common mistaken usages of the sync/atomic package")
+ vetBuildTags = flag.Bool("buildtags", false, "check that +build tags are valid")
vetMethods = flag.Bool("methods", false, "check that canonically named methods are canonically defined")
vetPrintf = flag.Bool("printf", false, "check printf-like invocations")
vetStructTags = flag.Bool("structtags", false, "check that struct field tags have canonical format")
- vetUntaggedLiteral = flag.Bool("composites", false, "check that composite literals used type-tagged elements")
vetRangeLoops = flag.Bool("rangeloops", false, "check that range loop variables are used correctly")
- vetAtomic = flag.Bool("atomic", false, "check for common mistaken usages of the sync/atomic package")
+ vetUntaggedLiteral = flag.Bool("composites", false, "check that composite literals used type-tagged elements")
)
// setExit sets the value for os.Exit when it is called, later. It
// doFile analyzes one file. If the reader is nil, the source code is read from the
// named file.
func doFile(name string, reader io.Reader) {
+ if reader == nil {
+ f, err := os.Open(name)
+ if err != nil {
+ errorf("%s: %s", name, err)
+ return
+ }
+ defer f.Close()
+ reader = f
+ }
+ data, err := ioutil.ReadAll(reader)
+ if err != nil {
+ errorf("%s: %s", name, err)
+ return
+ }
+ checkBuildTag(name, data)
fs := token.NewFileSet()
- parsedFile, err := parser.ParseFile(fs, name, reader, 0)
+ parsedFile, err := parser.ParseFile(fs, name, bytes.NewReader(data), 0)
if err != nil {
errorf("%s: %s", name, err)
return