github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/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 GOVETTOOL (default:
    27  // cmd/vet) and accept any of those flags plus any flag valid for 'go
    28  // build'. The tool must support -flags, which prints a description of
    29  // its flags in JSON to stdout.
    30  
    31  // GOVETTOOL specifies the vet command to run.
    32  // This must be an environment variable because
    33  // we need it before flag processing, as we execute
    34  // $GOVETTOOL to discover the set of flags it supports.
    35  //
    36  // Using an environment variable also makes it easy for users to opt in
    37  // to (and later, opt out of) the new cmd/vet analysis driver during the
    38  // transition. It is also used by tests.
    39  var vetTool = os.Getenv("GOVETTOOL")
    40  
    41  // vetFlags processes the command line, splitting it at the first non-flag
    42  // into the list of flags and list of packages.
    43  func vetFlags(args []string) (passToVet, packageNames []string) {
    44  	// Query the vet command for its flags.
    45  	tool := vetTool
    46  	if tool != "" {
    47  		var err error
    48  		tool, err = filepath.Abs(tool)
    49  		if err != nil {
    50  			log.Fatal(err)
    51  		}
    52  	} else {
    53  		tool = base.Tool("vet")
    54  	}
    55  	out := new(bytes.Buffer)
    56  	vetcmd := exec.Command(tool, "-flags")
    57  	vetcmd.Stdout = out
    58  	if err := vetcmd.Run(); err != nil {
    59  		fmt.Fprintf(os.Stderr, "go vet: can't execute %s -flags: %v\n", tool, err)
    60  		os.Exit(2)
    61  	}
    62  	var analysisFlags []struct {
    63  		Name  string
    64  		Bool  bool
    65  		Usage string
    66  	}
    67  	if err := json.Unmarshal(out.Bytes(), &analysisFlags); err != nil {
    68  		fmt.Fprintf(os.Stderr, "go vet: can't unmarshal JSON from %s -flags: %v", tool, err)
    69  		os.Exit(2)
    70  	}
    71  
    72  	// Add vet's flags to vetflagDefn.
    73  	//
    74  	// Some flags, in particular -tags and -v, are known to vet but
    75  	// also defined as build flags. This works fine, so we don't
    76  	// define them here but use AddBuildFlags to init them.
    77  	// However some, like -x, are known to the build but not to vet.
    78  	var vetFlagDefn []*cmdflag.Defn
    79  	for _, f := range analysisFlags {
    80  		switch f.Name {
    81  		case "tags", "v":
    82  			continue
    83  		}
    84  		defn := &cmdflag.Defn{
    85  			Name:       f.Name,
    86  			PassToTest: true,
    87  		}
    88  		if f.Bool {
    89  			defn.BoolVar = new(bool)
    90  		}
    91  		vetFlagDefn = append(vetFlagDefn, defn)
    92  	}
    93  
    94  	// Add build flags to vetFlagDefn.
    95  	var cmd base.Command
    96  	work.AddBuildFlags(&cmd)
    97  	cmd.Flag.VisitAll(func(f *flag.Flag) {
    98  		vetFlagDefn = append(vetFlagDefn, &cmdflag.Defn{
    99  			Name:  f.Name,
   100  			Value: f.Value,
   101  		})
   102  	})
   103  
   104  	// Process args.
   105  	args = str.StringList(cmdflag.FindGOFLAGS(vetFlagDefn), args)
   106  	for i := 0; i < len(args); i++ {
   107  		if !strings.HasPrefix(args[i], "-") {
   108  			return args[:i], args[i:]
   109  		}
   110  
   111  		f, value, extraWord := cmdflag.Parse("vet", vetFlagDefn, args, i)
   112  		if f == nil {
   113  			fmt.Fprintf(os.Stderr, "vet: flag %q not defined\n", args[i])
   114  			fmt.Fprintf(os.Stderr, "Run \"go help vet\" for more information\n")
   115  			os.Exit(2)
   116  		}
   117  		if f.Value != nil {
   118  			if err := f.Value.Set(value); err != nil {
   119  				base.Fatalf("invalid flag argument for -%s: %v", f.Name, err)
   120  			}
   121  			keep := f.PassToTest
   122  			if !keep {
   123  				// A build flag, probably one we don't want to pass to vet.
   124  				// Can whitelist.
   125  				switch f.Name {
   126  				case "tags", "v":
   127  					keep = true
   128  				}
   129  			}
   130  			if !keep {
   131  				// Flags known to the build but not to vet, so must be dropped.
   132  				if extraWord {
   133  					args = append(args[:i], args[i+2:]...)
   134  					extraWord = false
   135  				} else {
   136  					args = append(args[:i], args[i+1:]...)
   137  				}
   138  				i--
   139  			}
   140  		}
   141  		if extraWord {
   142  			i++
   143  		}
   144  	}
   145  	return args, nil
   146  }