1  
     2  
     3  
     4  
     5  package vet
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/json"
    10  	"errors"
    11  	"flag"
    12  	"fmt"
    13  	"log"
    14  	"os"
    15  	"os/exec"
    16  	"path/filepath"
    17  	"strings"
    18  
    19  	"cmd/go/internal/base"
    20  	"cmd/go/internal/cmdflag"
    21  	"cmd/go/internal/work"
    22  )
    23  
    24  
    25  
    26  
    27  
    28  
    29  
    30  
    31  
    32  
    33  
    34  
    35  
    36  
    37  
    38  var vetTool string 
    39  
    40  func init() {
    41  	
    42  	
    43  	
    44  	work.AddBuildFlags(CmdVet, work.OmitJSONFlag)
    45  	CmdVet.Flag.StringVar(&vetTool, "vettool", "", "")
    46  }
    47  
    48  func parseVettoolFlag(args []string) {
    49  	
    50  	
    51  	
    52  	for i, arg := range args {
    53  		if arg == "-vettool" || arg == "--vettool" {
    54  			if i+1 >= len(args) {
    55  				log.Fatalf("%s requires a filename", arg)
    56  			}
    57  			vetTool = args[i+1]
    58  			return
    59  		} else if strings.HasPrefix(arg, "-vettool=") ||
    60  			strings.HasPrefix(arg, "--vettool=") {
    61  			vetTool = arg[strings.IndexByte(arg, '=')+1:]
    62  			return
    63  		}
    64  	}
    65  }
    66  
    67  
    68  
    69  func vetFlags(args []string) (passToVet, packageNames []string) {
    70  	parseVettoolFlag(args)
    71  
    72  	
    73  	var tool string
    74  	if vetTool == "" {
    75  		tool = base.Tool("vet")
    76  	} else {
    77  		var err error
    78  		tool, err = filepath.Abs(vetTool)
    79  		if err != nil {
    80  			log.Fatal(err)
    81  		}
    82  	}
    83  	out := new(bytes.Buffer)
    84  	vetcmd := exec.Command(tool, "-flags")
    85  	vetcmd.Stdout = out
    86  	if err := vetcmd.Run(); err != nil {
    87  		fmt.Fprintf(os.Stderr, "go: can't execute %s -flags: %v\n", tool, err)
    88  		base.SetExitStatus(2)
    89  		base.Exit()
    90  	}
    91  	var analysisFlags []struct {
    92  		Name  string
    93  		Bool  bool
    94  		Usage string
    95  	}
    96  	if err := json.Unmarshal(out.Bytes(), &analysisFlags); err != nil {
    97  		fmt.Fprintf(os.Stderr, "go: can't unmarshal JSON from %s -flags: %v", tool, err)
    98  		base.SetExitStatus(2)
    99  		base.Exit()
   100  	}
   101  
   102  	
   103  	
   104  	
   105  	
   106  	
   107  	isVetFlag := make(map[string]bool, len(analysisFlags))
   108  	cf := CmdVet.Flag
   109  	for _, f := range analysisFlags {
   110  		isVetFlag[f.Name] = true
   111  		if cf.Lookup(f.Name) == nil {
   112  			if f.Bool {
   113  				cf.Bool(f.Name, false, "")
   114  			} else {
   115  				cf.String(f.Name, "", "")
   116  			}
   117  		}
   118  	}
   119  
   120  	
   121  	
   122  	base.SetFromGOFLAGS(&CmdVet.Flag)
   123  	addFromGOFLAGS := map[string]bool{}
   124  	CmdVet.Flag.Visit(func(f *flag.Flag) {
   125  		if isVetFlag[f.Name] {
   126  			addFromGOFLAGS[f.Name] = true
   127  		}
   128  	})
   129  
   130  	explicitFlags := make([]string, 0, len(args))
   131  	for len(args) > 0 {
   132  		f, remainingArgs, err := cmdflag.ParseOne(&CmdVet.Flag, args)
   133  
   134  		if errors.Is(err, flag.ErrHelp) {
   135  			exitWithUsage()
   136  		}
   137  
   138  		if errors.Is(err, cmdflag.ErrFlagTerminator) {
   139  			
   140  			
   141  			packageNames = remainingArgs
   142  			break
   143  		}
   144  
   145  		if nf := (cmdflag.NonFlagError{}); errors.As(err, &nf) {
   146  			
   147  			
   148  			packageNames = args
   149  			break
   150  		}
   151  
   152  		if err != nil {
   153  			fmt.Fprintln(os.Stderr, err)
   154  			exitWithUsage()
   155  		}
   156  
   157  		if isVetFlag[f.Name] {
   158  			
   159  			
   160  			explicitFlags = append(explicitFlags, args[:len(args)-len(remainingArgs)]...)
   161  
   162  			
   163  			
   164  			delete(addFromGOFLAGS, f.Name)
   165  		}
   166  
   167  		args = remainingArgs
   168  	}
   169  
   170  	
   171  	CmdVet.Flag.Visit(func(f *flag.Flag) {
   172  		if addFromGOFLAGS[f.Name] {
   173  			passToVet = append(passToVet, fmt.Sprintf("-%s=%s", f.Name, f.Value))
   174  		}
   175  	})
   176  	passToVet = append(passToVet, explicitFlags...)
   177  	return passToVet, packageNames
   178  }
   179  
   180  func exitWithUsage() {
   181  	fmt.Fprintf(os.Stderr, "usage: %s\n", CmdVet.UsageLine)
   182  	fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", CmdVet.LongName())
   183  
   184  	
   185  	cmd := "go tool vet"
   186  	if vetTool != "" {
   187  		cmd = vetTool
   188  	}
   189  	fmt.Fprintf(os.Stderr, "Run '%s help' for a full list of flags and analyzers.\n", cmd)
   190  	fmt.Fprintf(os.Stderr, "Run '%s -help' for an overview.\n", cmd)
   191  
   192  	base.SetExitStatus(2)
   193  	base.Exit()
   194  }
   195  
View as plain text