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 }