...

Source file src/pkg/cmd/go/internal/vet/vetflag.go

     1	// Copyright 2017 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	
     5	package vet
     6	
     7	import (
     8		"bytes"
     9		"encoding/json"
    10		"flag"
    11		"fmt"
    12		"log"
    13		"os"
    14		"os/exec"
    15		"path/filepath"
    16		"strings"
    17	
    18		"cmd/go/internal/base"
    19		"cmd/go/internal/cmdflag"
    20		"cmd/go/internal/str"
    21		"cmd/go/internal/work"
    22	)
    23	
    24	// go vet flag processing
    25	//
    26	// We query the flags of the tool specified by -vettool and accept any
    27	// of those flags plus any flag valid for 'go build'. The tool must
    28	// support -flags, which prints a description of its flags in JSON to
    29	// stdout.
    30	
    31	// vetTool specifies the vet command to run.
    32	// Any tool that supports the (still unpublished) vet
    33	// command-line protocol may be supplied; see
    34	// golang.org/x/tools/go/analysis/unitchecker for one
    35	// implementation. It is also used by tests.
    36	//
    37	// The default behavior (vetTool=="") runs 'go tool vet'.
    38	//
    39	var vetTool string // -vettool
    40	
    41	func init() {
    42		// Extract -vettool by ad hoc flag processing:
    43		// its value is needed even before we can declare
    44		// the flags available during main flag processing.
    45		for i, arg := range os.Args {
    46			if arg == "-vettool" || arg == "--vettool" {
    47				if i+1 >= len(os.Args) {
    48					log.Fatalf("%s requires a filename", arg)
    49				}
    50				vetTool = os.Args[i+1]
    51				break
    52			} else if strings.HasPrefix(arg, "-vettool=") ||
    53				strings.HasPrefix(arg, "--vettool=") {
    54				vetTool = arg[strings.IndexByte(arg, '=')+1:]
    55				break
    56			}
    57		}
    58	}
    59	
    60	// vetFlags processes the command line, splitting it at the first non-flag
    61	// into the list of flags and list of packages.
    62	func vetFlags(usage func(), args []string) (passToVet, packageNames []string) {
    63		// Query the vet command for its flags.
    64		tool := vetTool
    65		if tool != "" {
    66			var err error
    67			tool, err = filepath.Abs(tool)
    68			if err != nil {
    69				log.Fatal(err)
    70			}
    71		} else {
    72			tool = base.Tool("vet")
    73		}
    74		out := new(bytes.Buffer)
    75		vetcmd := exec.Command(tool, "-flags")
    76		vetcmd.Stdout = out
    77		if err := vetcmd.Run(); err != nil {
    78			fmt.Fprintf(os.Stderr, "go vet: can't execute %s -flags: %v\n", tool, err)
    79			base.SetExitStatus(2)
    80			base.Exit()
    81		}
    82		var analysisFlags []struct {
    83			Name  string
    84			Bool  bool
    85			Usage string
    86		}
    87		if err := json.Unmarshal(out.Bytes(), &analysisFlags); err != nil {
    88			fmt.Fprintf(os.Stderr, "go vet: can't unmarshal JSON from %s -flags: %v", tool, err)
    89			base.SetExitStatus(2)
    90			base.Exit()
    91		}
    92	
    93		// Add vet's flags to vetflagDefn.
    94		//
    95		// Some flags, in particular -tags and -v, are known to vet but
    96		// also defined as build flags. This works fine, so we don't
    97		// define them here but use AddBuildFlags to init them.
    98		// However some, like -x, are known to the build but not to vet.
    99		var vetFlagDefn []*cmdflag.Defn
   100		for _, f := range analysisFlags {
   101			switch f.Name {
   102			case "tags", "v":
   103				continue
   104			}
   105			defn := &cmdflag.Defn{
   106				Name:       f.Name,
   107				PassToTest: true,
   108			}
   109			if f.Bool {
   110				defn.BoolVar = new(bool)
   111			}
   112			vetFlagDefn = append(vetFlagDefn, defn)
   113		}
   114	
   115		// Add build flags to vetFlagDefn.
   116		var cmd base.Command
   117		work.AddBuildFlags(&cmd)
   118		// This flag declaration is a placeholder:
   119		// -vettool is actually parsed by the init function above.
   120		cmd.Flag.StringVar(new(string), "vettool", "", "path to vet tool binary")
   121		cmd.Flag.VisitAll(func(f *flag.Flag) {
   122			vetFlagDefn = append(vetFlagDefn, &cmdflag.Defn{
   123				Name:  f.Name,
   124				Value: f.Value,
   125			})
   126		})
   127	
   128		// Process args.
   129		args = str.StringList(cmdflag.FindGOFLAGS(vetFlagDefn), args)
   130		for i := 0; i < len(args); i++ {
   131			if !strings.HasPrefix(args[i], "-") {
   132				return args[:i], args[i:]
   133			}
   134	
   135			f, value, extraWord := cmdflag.Parse("vet", usage, vetFlagDefn, args, i)
   136			if f == nil {
   137				fmt.Fprintf(os.Stderr, "vet: flag %q not defined\n", args[i])
   138				fmt.Fprintf(os.Stderr, "Run \"go help vet\" for more information\n")
   139				base.SetExitStatus(2)
   140				base.Exit()
   141			}
   142			if f.Value != nil {
   143				if err := f.Value.Set(value); err != nil {
   144					base.Fatalf("invalid flag argument for -%s: %v", f.Name, err)
   145				}
   146				keep := f.PassToTest
   147				if !keep {
   148					// A build flag, probably one we don't want to pass to vet.
   149					// Can whitelist.
   150					switch f.Name {
   151					case "tags", "v":
   152						keep = true
   153					}
   154				}
   155				if !keep {
   156					// Flags known to the build but not to vet, so must be dropped.
   157					if extraWord {
   158						args = append(args[:i], args[i+2:]...)
   159						extraWord = false
   160					} else {
   161						args = append(args[:i], args[i+1:]...)
   162					}
   163					i--
   164				}
   165			}
   166			if extraWord {
   167				i++
   168			}
   169		}
   170		return args, nil
   171	}
   172	
   173	var vetUsage func()
   174	
   175	func init() { vetUsage = usage } // break initialization cycle
   176	
   177	func usage() {
   178		fmt.Fprintf(os.Stderr, "usage: %s\n", CmdVet.UsageLine)
   179		fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", CmdVet.LongName())
   180	
   181		// This part is additional to what (*Command).Usage does:
   182		cmd := "go tool vet"
   183		if vetTool != "" {
   184			cmd = vetTool
   185		}
   186		fmt.Fprintf(os.Stderr, "Run '%s -help' for the vet tool's flags.\n", cmd)
   187	
   188		base.SetExitStatus(2)
   189		base.Exit()
   190	}
   191	

View as plain text