github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/src/cmd/go/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  	"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  		os.Exit(2)
    80  	}
    81  	var analysisFlags []struct {
    82  		Name  string
    83  		Bool  bool
    84  		Usage string
    85  	}
    86  	if err := json.Unmarshal(out.Bytes(), &analysisFlags); err != nil {
    87  		fmt.Fprintf(os.Stderr, "go vet: can't unmarshal JSON from %s -flags: %v", tool, err)
    88  		os.Exit(2)
    89  	}
    90  
    91  	// Add vet's flags to vetflagDefn.
    92  	//
    93  	// Some flags, in particular -tags and -v, are known to vet but
    94  	// also defined as build flags. This works fine, so we don't
    95  	// define them here but use AddBuildFlags to init them.
    96  	// However some, like -x, are known to the build but not to vet.
    97  	var vetFlagDefn []*cmdflag.Defn
    98  	for _, f := range analysisFlags {
    99  		switch f.Name {
   100  		case "tags", "v":
   101  			continue
   102  		}
   103  		defn := &cmdflag.Defn{
   104  			Name:       f.Name,
   105  			PassToTest: true,
   106  		}
   107  		if f.Bool {
   108  			defn.BoolVar = new(bool)
   109  		}
   110  		vetFlagDefn = append(vetFlagDefn, defn)
   111  	}
   112  
   113  	// Add build flags to vetFlagDefn.
   114  	var cmd base.Command
   115  	work.AddBuildFlags(&cmd)
   116  	// This flag declaration is a placeholder:
   117  	// -vettool is actually parsed by the init function above.
   118  	cmd.Flag.StringVar(new(string), "vettool", "", "path to vet tool binary")
   119  	cmd.Flag.VisitAll(func(f *flag.Flag) {
   120  		vetFlagDefn = append(vetFlagDefn, &cmdflag.Defn{
   121  			Name:  f.Name,
   122  			Value: f.Value,
   123  		})
   124  	})
   125  
   126  	// Process args.
   127  	args = str.StringList(cmdflag.FindGOFLAGS(vetFlagDefn), args)
   128  	for i := 0; i < len(args); i++ {
   129  		if !strings.HasPrefix(args[i], "-") {
   130  			return args[:i], args[i:]
   131  		}
   132  
   133  		f, value, extraWord := cmdflag.Parse("vet", usage, vetFlagDefn, args, i)
   134  		if f == nil {
   135  			fmt.Fprintf(os.Stderr, "vet: flag %q not defined\n", args[i])
   136  			fmt.Fprintf(os.Stderr, "Run \"go help vet\" for more information\n")
   137  			os.Exit(2)
   138  		}
   139  		if f.Value != nil {
   140  			if err := f.Value.Set(value); err != nil {
   141  				base.Fatalf("invalid flag argument for -%s: %v", f.Name, err)
   142  			}
   143  			keep := f.PassToTest
   144  			if !keep {
   145  				// A build flag, probably one we don't want to pass to vet.
   146  				// Can whitelist.
   147  				switch f.Name {
   148  				case "tags", "v":
   149  					keep = true
   150  				}
   151  			}
   152  			if !keep {
   153  				// Flags known to the build but not to vet, so must be dropped.
   154  				if extraWord {
   155  					args = append(args[:i], args[i+2:]...)
   156  					extraWord = false
   157  				} else {
   158  					args = append(args[:i], args[i+1:]...)
   159  				}
   160  				i--
   161  			}
   162  		}
   163  		if extraWord {
   164  			i++
   165  		}
   166  	}
   167  	return args, nil
   168  }
   169  
   170  var vetUsage func()
   171  
   172  func init() { vetUsage = usage } // break initialization cycle
   173  
   174  func usage() {
   175  	fmt.Fprintf(os.Stderr, "usage: %s\n", CmdVet.UsageLine)
   176  	fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", CmdVet.LongName())
   177  
   178  	// This part is additional to what (*Command).Usage does:
   179  	cmd := "go tool vet"
   180  	if vetTool != "" {
   181  		cmd = vetTool
   182  	}
   183  	fmt.Fprintf(os.Stderr, "Run '%s -help' for the vet tool's flags.\n", cmd)
   184  
   185  	os.Exit(2)
   186  }