github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-internal/vet/vetflag.go (about)

     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  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/base"
    19  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/cmdflag"
    20  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/str"
    21  	"github.com/gagliardetto/golang-go/cmd/go/not-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, work.DefaultBuildFlags)
   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  	goflags := cmdflag.FindGOFLAGS(vetFlagDefn)
   130  	args = str.StringList(goflags, args)
   131  	for i := 0; i < len(args); i++ {
   132  		if !strings.HasPrefix(args[i], "-") {
   133  			return args[:i], args[i:]
   134  		}
   135  
   136  		f, value, extraWord := cmdflag.Parse("vet", usage, vetFlagDefn, args, i)
   137  		if f == nil {
   138  			fmt.Fprintf(os.Stderr, "vet: flag %q not defined\n", args[i])
   139  			fmt.Fprintf(os.Stderr, "Run \"go help vet\" for more information\n")
   140  			base.SetExitStatus(2)
   141  			base.Exit()
   142  		}
   143  		if i < len(goflags) {
   144  			f.Present = false // Not actually present on the command line.
   145  		}
   146  		if f.Value != nil {
   147  			if err := f.Value.Set(value); err != nil {
   148  				base.Fatalf("invalid flag argument for -%s: %v", f.Name, err)
   149  			}
   150  			keep := f.PassToTest
   151  			if !keep {
   152  				// A build flag, probably one we don't want to pass to vet.
   153  				// Can whitelist.
   154  				switch f.Name {
   155  				case "tags", "v":
   156  					keep = true
   157  				}
   158  			}
   159  			if !keep {
   160  				// Flags known to the build but not to vet, so must be dropped.
   161  				if extraWord {
   162  					args = append(args[:i], args[i+2:]...)
   163  					extraWord = false
   164  				} else {
   165  					args = append(args[:i], args[i+1:]...)
   166  				}
   167  				i--
   168  			}
   169  		}
   170  		if extraWord {
   171  			i++
   172  		}
   173  	}
   174  	return args, nil
   175  }
   176  
   177  var vetUsage func()
   178  
   179  func init() { vetUsage = usage } // break initialization cycle
   180  
   181  func usage() {
   182  	fmt.Fprintf(os.Stderr, "usage: %s\n", CmdVet.UsageLine)
   183  	fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", CmdVet.LongName())
   184  
   185  	// This part is additional to what (*Command).Usage does:
   186  	cmd := "go tool vet"
   187  	if vetTool != "" {
   188  		cmd = vetTool
   189  	}
   190  	fmt.Fprintf(os.Stderr, "Run '%s -help' for the vet tool's flags.\n", cmd)
   191  
   192  	base.SetExitStatus(2)
   193  	base.Exit()
   194  }