From 9f76e5c58b59280894d8f3a0c64460b6375f7fe7 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Thu, 18 Oct 2018 22:26:36 -0400 Subject: [PATCH] go/analysis/passes/vet: delete All the passes have been moved into their own packages. The README file has been saved for the new cmd/analyze command, which will shortly be renamed to vet. Change-Id: I68c765a4da2f8d5a2b0161b462bd81483b5ceed5 Reviewed-on: https://go-review.googlesource.com/c/143301 Reviewed-by: Michael Matloob Run-TryBot: Michael Matloob TryBot-Result: Gobot Gobot --- .../{passes/vet => cmd/analyze}/README | 0 go/analysis/passes/vet/REVISION | 1 - go/analysis/passes/vet/doc.go | 221 ----- go/analysis/passes/vet/main.go | 753 ------------------ .../passes/vet/testdata/tagtest/file1.go | 10 - .../passes/vet/testdata/tagtest/file2.go | 10 - go/analysis/passes/vet/vet_test.go | 441 ---------- 7 files changed, 1436 deletions(-) rename go/analysis/{passes/vet => cmd/analyze}/README (100%) delete mode 100644 go/analysis/passes/vet/REVISION delete mode 100644 go/analysis/passes/vet/doc.go delete mode 100644 go/analysis/passes/vet/main.go delete mode 100644 go/analysis/passes/vet/testdata/tagtest/file1.go delete mode 100644 go/analysis/passes/vet/testdata/tagtest/file2.go delete mode 100644 go/analysis/passes/vet/vet_test.go diff --git a/go/analysis/passes/vet/README b/go/analysis/cmd/analyze/README similarity index 100% rename from go/analysis/passes/vet/README rename to go/analysis/cmd/analyze/README diff --git a/go/analysis/passes/vet/REVISION b/go/analysis/passes/vet/REVISION deleted file mode 100644 index dbd98e1cd46..00000000000 --- a/go/analysis/passes/vet/REVISION +++ /dev/null @@ -1 +0,0 @@ -cmd/vet@31d19c0 \ No newline at end of file diff --git a/go/analysis/passes/vet/doc.go b/go/analysis/passes/vet/doc.go deleted file mode 100644 index 0b970d089fe..00000000000 --- a/go/analysis/passes/vet/doc.go +++ /dev/null @@ -1,221 +0,0 @@ -// +build ignore - -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* - -Vet examines Go source code and reports suspicious constructs, such as Printf -calls whose arguments do not align with the format string. Vet uses heuristics -that do not guarantee all reports are genuine problems, but it can find errors -not caught by the compilers. - -Vet is normally invoked using the go command by running "go vet": - - go vet -vets the package in the current directory. - - go vet package/path/name -vets the package whose path is provided. - -Use "go help packages" to see other ways of specifying which packages to vet. - -Vet's exit code is 2 for erroneous invocation of the tool, 1 if a -problem was reported, and 0 otherwise. Note that the tool does not -check every possible problem and depends on unreliable heuristics -so it should be used as guidance only, not as a firm indicator of -program correctness. - -By default the -all flag is set so all checks are performed. -If any flags are explicitly set to true, only those tests are run. Conversely, if -any flag is explicitly set to false, only those tests are disabled. Thus -printf=true -runs the printf check, -printf=false runs all checks except the printf check. - -By default vet uses the object files generated by 'go install some/pkg' to typecheck the code. -If the -source flag is provided, vet uses only source code. - -Available checks: - -Assembly declarations - -Flag: -asmdecl - -Mismatches between assembly files and Go function declarations. - -Useless assignments - -Flag: -assign - -Check for useless assignments. - -Atomic mistakes - -Flag: -atomic - -Common mistaken usages of the sync/atomic package. - -Boolean conditions - -Flag: -bool - -Mistakes involving boolean operators. - -Build tags - -Flag: -buildtags - -Badly formed or misplaced +build tags. - -Invalid uses of cgo - -Flag: -cgocall - -Detect some violations of the cgo pointer passing rules. - -Unkeyed composite literals - -Flag: -composites - -Composite struct literals that do not use the field-keyed syntax. - -Copying locks - -Flag: -copylocks - -Locks that are erroneously passed by value. - -HTTP responses used incorrectly - -Flag: -httpresponse - -Mistakes deferring a function call on an HTTP response before -checking whether the error returned with the response was nil. - -Failure to call the cancelation function returned by WithCancel - -Flag: -lostcancel - -The cancelation function returned by context.WithCancel, WithTimeout, -and WithDeadline must be called or the new context will remain live -until its parent context is cancelled. -(The background context is never cancelled.) - -Methods - -Flag: -methods - -Non-standard signatures for methods with familiar names, including: - Format GobEncode GobDecode MarshalJSON MarshalXML - Peek ReadByte ReadFrom ReadRune Scan Seek - UnmarshalJSON UnreadByte UnreadRune WriteByte - WriteTo - -Nil function comparison - -Flag: -nilfunc - -Comparisons between functions and nil. - -Printf family - -Flag: -printf - -Suspicious calls to fmt.Print, fmt.Printf, and related functions. -The check applies to known functions (for example, those in package fmt) -as well as any detected wrappers of known functions. - -The -printfuncs flag specifies a comma-separated list of names of -additional known formatting functions. Each name can be of the form -pkg.Name or pkg.Type.Name, where pkg is a complete import path, -or else can be a case-insensitive unqualified identifier like "errorf". -If a listed name ends in f, the function is assumed to be Printf-like, -taking a format string before the argument list. Otherwise it is -assumed to be Print-like, taking a list of arguments with no format string. - -Range loop variables - -Flag: -rangeloops - -Incorrect uses of range loop variables in closures. - -Shadowed variables - -Flag: -shadow=false (experimental; must be set explicitly) - -Variables that may have been unintentionally shadowed. - -Shifts - -Flag: -shift - -Shifts equal to or longer than the variable's length. - -Struct tags - -Flag: -structtags - -Struct tags that do not follow the format understood by reflect.StructTag.Get. -Well-known encoding struct tags (json, xml) used with unexported fields. - -Tests and documentation examples - -Flag: -tests - -Mistakes involving tests including functions with incorrect names or signatures -and example tests that document identifiers not in the package. - -Unreachable code - -Flag: -unreachable - -Unreachable code. - -Misuse of unsafe Pointers - -Flag: -unsafeptr - -Likely incorrect uses of unsafe.Pointer to convert integers to pointers. -A conversion from uintptr to unsafe.Pointer is invalid if it implies that -there is a uintptr-typed word in memory that holds a pointer value, -because that word will be invisible to stack copying and to the garbage -collector. - -Unused result of certain function calls - -Flag: -unusedresult - -Calls to well-known functions and methods that return a value that is -discarded. By default, this includes functions like fmt.Errorf and -fmt.Sprintf and methods like String and Error. The flags -unusedfuncs -and -unusedstringmethods control the set. - -Other flags - -These flags configure the behavior of vet: - - -all (default true) - Enable all non-experimental checks. - -v - Verbose mode - -printfuncs - A comma-separated list of print-like function names - to supplement the standard list. - For more information, see the discussion of the -printf flag. - -shadowstrict - Whether to be strict about shadowing; can be noisy. - -Using vet directly - -For testing and debugging vet can be run directly by invoking -"go tool vet" or just running the binary. Run this way, vet might not -have up to date information for imported packages. - - go tool vet source/directory/*.go -vets the files named, all of which must be in the same package. - - go tool vet source/directory -recursively descends the directory, vetting each package it finds. - -*/ -package main diff --git a/go/analysis/passes/vet/main.go b/go/analysis/passes/vet/main.go deleted file mode 100644 index 60114c5c169..00000000000 --- a/go/analysis/passes/vet/main.go +++ /dev/null @@ -1,753 +0,0 @@ -// +build ignore - -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Vet is a simple checker for static errors in Go source code. -// See doc.go for more information. - -package main - -import ( - "bytes" - "encoding/gob" - "encoding/json" - "flag" - "fmt" - "go/ast" - "go/build" - "go/importer" - "go/parser" - "go/printer" - "go/token" - "go/types" - "io" - "io/ioutil" - "os" - "path/filepath" - "sort" - "strconv" - "strings" - - "cmd/internal/objabi" -) - -// Important! If you add flags here, make sure to update cmd/go/internal/vet/vetflag.go. - -var ( - verbose = flag.Bool("v", false, "verbose") - source = flag.Bool("source", false, "import from source instead of compiled object files") - tags = flag.String("tags", "", "space-separated list of build tags to apply when parsing") - tagList = []string{} // exploded version of tags flag; set in main - - vcfg vetConfig - mustTypecheck bool -) - -var exitCode = 0 - -// "-all" flag enables all non-experimental checks -var all = triStateFlag("all", unset, "enable all non-experimental checks") - -// Flags to control which individual checks to perform. -var report = map[string]*triState{ - // Only unusual checks are written here. - // Most checks that operate during the AST walk are added by register. - "asmdecl": triStateFlag("asmdecl", unset, "check assembly against Go declarations"), - "buildtags": triStateFlag("buildtags", unset, "check that +build tags are valid"), -} - -// experimental records the flags enabling experimental features. These must be -// requested explicitly; they are not enabled by -all. -var experimental = map[string]bool{} - -// setTrueCount record how many flags are explicitly set to true. -var setTrueCount int - -// dirsRun and filesRun indicate whether the vet is applied to directory or -// file targets. The distinction affects which checks are run. -var dirsRun, filesRun bool - -// includesNonTest indicates whether the vet is applied to non-test targets. -// Certain checks are relevant only if they touch both test and non-test files. -var includesNonTest bool - -// A triState is a boolean that knows whether it has been set to either true or false. -// It is used to identify if a flag appears; the standard boolean flag cannot -// distinguish missing from unset. It also satisfies flag.Value. -type triState int - -const ( - unset triState = iota - setTrue - setFalse -) - -func triStateFlag(name string, value triState, usage string) *triState { - flag.Var(&value, name, usage) - return &value -} - -// triState implements flag.Value, flag.Getter, and flag.boolFlag. -// They work like boolean flags: we can say vet -printf as well as vet -printf=true -func (ts *triState) Get() interface{} { - return *ts == setTrue -} - -func (ts triState) isTrue() bool { - return ts == setTrue -} - -func (ts *triState) Set(value string) error { - b, err := strconv.ParseBool(value) - if err != nil { - return err - } - if b { - *ts = setTrue - setTrueCount++ - } else { - *ts = setFalse - } - return nil -} - -func (ts *triState) String() string { - switch *ts { - case unset: - return "true" // An unset flag will be set by -all, so defaults to true. - case setTrue: - return "true" - case setFalse: - return "false" - } - panic("not reached") -} - -func (ts triState) IsBoolFlag() bool { - return true -} - -// vet tells whether to report errors for the named check, a flag name. -func vet(name string) bool { - return report[name].isTrue() -} - -// setExit sets the value for os.Exit when it is called, later. It -// remembers the highest value. -func setExit(err int) { - if err > exitCode { - exitCode = err - } -} - -var ( - // Each of these vars has a corresponding case in (*File).Visit. - assignStmt *ast.AssignStmt - binaryExpr *ast.BinaryExpr - callExpr *ast.CallExpr - compositeLit *ast.CompositeLit - exprStmt *ast.ExprStmt - forStmt *ast.ForStmt - funcDecl *ast.FuncDecl - funcLit *ast.FuncLit - genDecl *ast.GenDecl - interfaceType *ast.InterfaceType - rangeStmt *ast.RangeStmt - returnStmt *ast.ReturnStmt - structType *ast.StructType - - // checkers is a two-level map. - // The outer level is keyed by a nil pointer, one of the AST vars above. - // The inner level is keyed by checker name. - checkers = make(map[ast.Node]map[string]func(*File, ast.Node)) - pkgCheckers = make(map[string]func(*Package)) - exporters = make(map[string]func() interface{}) -) - -// The exporters data as written to the vetx output file. -type vetxExport struct { - Name string - Data interface{} -} - -// Vet can provide its own "export information" -// about package A to future invocations of vet -// on packages importing A. If B imports A, -// then running "go vet B" actually invokes vet twice: -// first, it runs vet on A, in "vetx-only" mode, which -// skips most checks and only computes export data -// describing A. Then it runs vet on B, making A's vetx -// data available for consultation. The vet of B -// computes vetx data for B in addition to its -// usual vet checks. - -// register registers the named check function, -// to be called with AST nodes of the given types. -// The registered functions are not called in vetx-only mode. -func register(name, usage string, fn func(*File, ast.Node), types ...ast.Node) { - report[name] = triStateFlag(name, unset, usage) - for _, typ := range types { - m := checkers[typ] - if m == nil { - m = make(map[string]func(*File, ast.Node)) - checkers[typ] = m - } - m[name] = fn - } -} - -// registerPkgCheck registers a package-level checking function, -// to be invoked with the whole package being vetted -// before any of the per-node handlers. -// The registered function fn is called even in vetx-only mode -// (see comment above), so fn must take care not to report -// errors when vcfg.VetxOnly is true. -func registerPkgCheck(name string, fn func(*Package)) { - pkgCheckers[name] = fn -} - -// registerExport registers a function to return vetx export data -// that should be saved and provided to future invocations of vet -// when checking packages importing this one. -// The value returned by fn should be nil or else valid to encode using gob. -// Typically a registerExport call is paired with a call to gob.Register. -func registerExport(name string, fn func() interface{}) { - exporters[name] = fn -} - -// Usage is a replacement usage function for the flags package. -func Usage() { - fmt.Fprintf(os.Stderr, "Usage of vet:\n") - fmt.Fprintf(os.Stderr, "\tvet [flags] directory...\n") - fmt.Fprintf(os.Stderr, "\tvet [flags] files... # Must be a single package\n") - fmt.Fprintf(os.Stderr, "By default, -all is set and all non-experimental checks are run.\n") - fmt.Fprintf(os.Stderr, "For more information run\n") - fmt.Fprintf(os.Stderr, "\tgo doc cmd/vet\n\n") - fmt.Fprintf(os.Stderr, "Flags:\n") - flag.PrintDefaults() - os.Exit(2) -} - -// File is a wrapper for the state of a file used in the parser. -// The parse tree walkers are all methods of this type. -type File struct { - pkg *Package - fset *token.FileSet - name string - content []byte - file *ast.File - b bytes.Buffer // for use by methods - - // Parsed package "foo" when checking package "foo_test" - basePkg *Package - - // The keys are the objects that are receivers of a "String() - // string" method. The value reports whether the method has a - // pointer receiver. - // This is used by the recursiveStringer method in print.go. - stringerPtrs map[*ast.Object]bool - - // Registered checkers to run. - checkers map[ast.Node][]func(*File, ast.Node) - - // Unreachable nodes; can be ignored in shift check. - dead map[ast.Node]bool -} - -func main() { - objabi.AddVersionFlag() - flag.Usage = Usage - flag.Parse() - - // If any flag is set, we run only those checks requested. - // If all flag is set true or if no flags are set true, set all the non-experimental ones - // not explicitly set (in effect, set the "-all" flag). - if setTrueCount == 0 || *all == setTrue { - for name, setting := range report { - if *setting == unset && !experimental[name] { - *setting = setTrue - } - } - } - - // Accept space-separated tags because that matches - // the go command's other subcommands. - // Accept commas because go tool vet traditionally has. - tagList = strings.Fields(strings.ReplaceAll(*tags, ",", " ")) - - initPrintFlags() - initUnusedFlags() - - if flag.NArg() == 0 { - Usage() - } - - // Special case for "go vet" passing an explicit configuration: - // single argument ending in vet.cfg. - // Once we have a more general mechanism for obtaining this - // information from build tools like the go command, - // vet should be changed to use it. This vet.cfg hack is an - // experiment to learn about what form that information should take. - if flag.NArg() == 1 && strings.HasSuffix(flag.Arg(0), "vet.cfg") { - doPackageCfg(flag.Arg(0)) - os.Exit(exitCode) - } - - for _, name := range flag.Args() { - // Is it a directory? - fi, err := os.Stat(name) - if err != nil { - warnf("error walking tree: %s", err) - continue - } - if fi.IsDir() { - dirsRun = true - } else { - filesRun = true - if !strings.HasSuffix(name, "_test.go") { - includesNonTest = true - } - } - } - if dirsRun && filesRun { - Usage() - } - if dirsRun { - for _, name := range flag.Args() { - walkDir(name) - } - os.Exit(exitCode) - } - if doPackage(flag.Args(), nil) == nil { - warnf("no files checked") - } - os.Exit(exitCode) -} - -// prefixDirectory places the directory name on the beginning of each name in the list. -func prefixDirectory(directory string, names []string) { - if directory != "." { - for i, name := range names { - names[i] = filepath.Join(directory, name) - } - } -} - -// vetConfig is the JSON config struct prepared by the Go command. -type vetConfig struct { - Compiler string - Dir string - ImportPath string - GoFiles []string - ImportMap map[string]string - PackageFile map[string]string - Standard map[string]bool - PackageVetx map[string]string // map from import path to vetx data file - VetxOnly bool // only compute vetx output; don't run ordinary checks - VetxOutput string // file where vetx output should be written - - SucceedOnTypecheckFailure bool - - imp types.Importer -} - -func (v *vetConfig) Import(path string) (*types.Package, error) { - if v.imp == nil { - v.imp = importer.For(v.Compiler, v.openPackageFile) - } - if path == "unsafe" { - return v.imp.Import("unsafe") - } - p := v.ImportMap[path] - if p == "" { - return nil, fmt.Errorf("unknown import path %q", path) - } - if v.PackageFile[p] == "" { - if v.Compiler == "gccgo" && v.Standard[path] { - // gccgo doesn't have sources for standard library packages, - // but the importer will do the right thing. - return v.imp.Import(path) - } - return nil, fmt.Errorf("unknown package file for import %q", path) - } - return v.imp.Import(p) -} - -func (v *vetConfig) openPackageFile(path string) (io.ReadCloser, error) { - file := v.PackageFile[path] - if file == "" { - if v.Compiler == "gccgo" && v.Standard[path] { - // The importer knows how to handle this. - return nil, nil - } - // Note that path here has been translated via v.ImportMap, - // unlike in the error in Import above. We prefer the error in - // Import, but it's worth diagnosing this one too, just in case. - return nil, fmt.Errorf("unknown package file for %q", path) - } - f, err := os.Open(file) - if err != nil { - return nil, err - } - return f, nil -} - -// doPackageCfg analyzes a single package described in a config file. -func doPackageCfg(cfgFile string) { - js, err := ioutil.ReadFile(cfgFile) - if err != nil { - errorf("%v", err) - } - if err := json.Unmarshal(js, &vcfg); err != nil { - errorf("parsing vet config %s: %v", cfgFile, err) - } - stdImporter = &vcfg - inittypes() - mustTypecheck = true - doPackage(vcfg.GoFiles, nil) - if vcfg.VetxOutput != "" { - out := make([]vetxExport, 0, len(exporters)) - for name, fn := range exporters { - out = append(out, vetxExport{ - Name: name, - Data: fn(), - }) - } - // Sort the data so that it is consistent across builds. - sort.Slice(out, func(i, j int) bool { - return out[i].Name < out[j].Name - }) - var buf bytes.Buffer - if err := gob.NewEncoder(&buf).Encode(out); err != nil { - errorf("encoding vet output: %v", err) - return - } - if err := ioutil.WriteFile(vcfg.VetxOutput, buf.Bytes(), 0666); err != nil { - errorf("saving vet output: %v", err) - return - } - } -} - -// doPackageDir analyzes the single package found in the directory, if there is one, -// plus a test package, if there is one. -func doPackageDir(directory string) { - context := build.Default - if len(context.BuildTags) != 0 { - warnf("build tags %s previously set", context.BuildTags) - } - context.BuildTags = append(tagList, context.BuildTags...) - - pkg, err := context.ImportDir(directory, 0) - if err != nil { - // If it's just that there are no go source files, that's fine. - if _, nogo := err.(*build.NoGoError); nogo { - return - } - // Non-fatal: we are doing a recursive walk and there may be other directories. - warnf("cannot process directory %s: %s", directory, err) - return - } - var names []string - names = append(names, pkg.GoFiles...) - names = append(names, pkg.CgoFiles...) - names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package. - names = append(names, pkg.SFiles...) - prefixDirectory(directory, names) - basePkg := doPackage(names, nil) - // Is there also a "foo_test" package? If so, do that one as well. - if len(pkg.XTestGoFiles) > 0 { - names = pkg.XTestGoFiles - prefixDirectory(directory, names) - doPackage(names, basePkg) - } -} - -type Package struct { - path string - defs map[*ast.Ident]types.Object - uses map[*ast.Ident]types.Object - implicits map[ast.Node]types.Object - selectors map[*ast.SelectorExpr]*types.Selection - types map[ast.Expr]types.TypeAndValue - spans map[types.Object]Span - files []*File - typesPkg *types.Package -} - -// doPackage analyzes the single package constructed from the named files. -// It returns the parsed Package or nil if none of the files have been checked. -func doPackage(names []string, basePkg *Package) *Package { - var files []*File - var astFiles []*ast.File - fs := token.NewFileSet() - for _, name := range names { - data, err := ioutil.ReadFile(name) - if err != nil { - // Warn but continue to next package. - warnf("%s: %s", name, err) - return nil - } - var parsedFile *ast.File - if strings.HasSuffix(name, ".go") { - parsedFile, err = parser.ParseFile(fs, name, data, parser.ParseComments) - if err != nil { - warnf("%s: %s", name, err) - return nil - } - astFiles = append(astFiles, parsedFile) - } - file := &File{ - fset: fs, - content: data, - name: name, - file: parsedFile, - dead: make(map[ast.Node]bool), - } - files = append(files, file) - } - if len(astFiles) == 0 { - return nil - } - pkg := new(Package) - pkg.path = astFiles[0].Name.Name - pkg.files = files - // Type check the package. - errs := pkg.check(fs, astFiles) - if errs != nil { - if vcfg.SucceedOnTypecheckFailure { - os.Exit(0) - } - if *verbose || mustTypecheck { - for _, err := range errs { - fmt.Fprintf(os.Stderr, "%v\n", err) - } - if mustTypecheck { - // This message could be silenced, and we could just exit, - // but it might be helpful at least at first to make clear that the - // above errors are coming from vet and not the compiler - // (they often look like compiler errors, such as "declared but not used"). - errorf("typecheck failures") - } - } - } - - // Check. - for _, file := range files { - file.pkg = pkg - file.basePkg = basePkg - } - for name, fn := range pkgCheckers { - if vet(name) { - fn(pkg) - } - } - if vcfg.VetxOnly { - return pkg - } - - chk := make(map[ast.Node][]func(*File, ast.Node)) - for typ, set := range checkers { - for name, fn := range set { - if vet(name) { - chk[typ] = append(chk[typ], fn) - } - } - } - for _, file := range files { - checkBuildTag(file) - file.checkers = chk - if file.file != nil { - file.walkFile(file.name, file.file) - } - } - return pkg -} - -func visit(path string, f os.FileInfo, err error) error { - if err != nil { - warnf("walk error: %s", err) - return err - } - // One package per directory. Ignore the files themselves. - if !f.IsDir() { - return nil - } - doPackageDir(path) - return nil -} - -func (pkg *Package) hasFileWithSuffix(suffix string) bool { - for _, f := range pkg.files { - if strings.HasSuffix(f.name, suffix) { - return true - } - } - return false -} - -// walkDir recursively walks the tree looking for Go packages. -func walkDir(root string) { - filepath.Walk(root, visit) -} - -// errorf formats the error to standard error, adding program -// identification and a newline, and exits. -func errorf(format string, args ...interface{}) { - fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...) - os.Exit(2) -} - -// warnf formats the error to standard error, adding program -// identification and a newline, but does not exit. -func warnf(format string, args ...interface{}) { - fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...) - setExit(1) -} - -// Println is fmt.Println guarded by -v. -func Println(args ...interface{}) { - if !*verbose { - return - } - fmt.Println(args...) -} - -// Printf is fmt.Printf guarded by -v. -func Printf(format string, args ...interface{}) { - if !*verbose { - return - } - fmt.Printf(format+"\n", args...) -} - -// Bad reports an error and sets the exit code.. -func (f *File) Bad(pos token.Pos, args ...interface{}) { - f.Warn(pos, args...) - setExit(1) -} - -// Badf reports a formatted error and sets the exit code. -func (f *File) Badf(pos token.Pos, format string, args ...interface{}) { - f.Warnf(pos, format, args...) - setExit(1) -} - -// loc returns a formatted representation of the position. -func (f *File) loc(pos token.Pos) string { - if pos == token.NoPos { - return "" - } - // Do not print columns. Because the pos often points to the start of an - // expression instead of the inner part with the actual error, the - // precision can mislead. - posn := f.fset.Position(pos) - return fmt.Sprintf("%s:%d", posn.Filename, posn.Line) -} - -// locPrefix returns a formatted representation of the position for use as a line prefix. -func (f *File) locPrefix(pos token.Pos) string { - if pos == token.NoPos { - return "" - } - return fmt.Sprintf("%s: ", f.loc(pos)) -} - -// Warn reports an error but does not set the exit code. -func (f *File) Warn(pos token.Pos, args ...interface{}) { - fmt.Fprintf(os.Stderr, "%s%s", f.locPrefix(pos), fmt.Sprintln(args...)) -} - -// Warnf reports a formatted error but does not set the exit code. -func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) { - fmt.Fprintf(os.Stderr, "%s%s\n", f.locPrefix(pos), fmt.Sprintf(format, args...)) -} - -// walkFile walks the file's tree. -func (f *File) walkFile(name string, file *ast.File) { - Println("Checking file", name) - ast.Walk(f, file) -} - -// Visit implements the ast.Visitor interface. -func (f *File) Visit(node ast.Node) ast.Visitor { - f.updateDead(node) - var key ast.Node - switch node.(type) { - case *ast.AssignStmt: - key = assignStmt - case *ast.BinaryExpr: - key = binaryExpr - case *ast.CallExpr: - key = callExpr - case *ast.CompositeLit: - key = compositeLit - case *ast.ExprStmt: - key = exprStmt - case *ast.ForStmt: - key = forStmt - case *ast.FuncDecl: - key = funcDecl - case *ast.FuncLit: - key = funcLit - case *ast.GenDecl: - key = genDecl - case *ast.InterfaceType: - key = interfaceType - case *ast.RangeStmt: - key = rangeStmt - case *ast.ReturnStmt: - key = returnStmt - case *ast.StructType: - key = structType - } - for _, fn := range f.checkers[key] { - fn(f, node) - } - return f -} - -// gofmt returns a string representation of the expression. -func (f *File) gofmt(x ast.Expr) string { - f.b.Reset() - printer.Fprint(&f.b, f.fset, x) - return f.b.String() -} - -// imported[path][key] is previously written export data. -var imported = make(map[string]map[string]interface{}) - -// readVetx reads export data written by a previous -// invocation of vet on an imported package (path). -// The key is the name passed to registerExport -// when the data was originally generated. -// readVetx returns nil if the data is unavailable. -func readVetx(path, key string) interface{} { - if path == "unsafe" || vcfg.ImportPath == "" { - return nil - } - m := imported[path] - if m == nil { - file := vcfg.PackageVetx[path] - if file == "" { - return nil - } - data, err := ioutil.ReadFile(file) - if err != nil { - return nil - } - var out []vetxExport - err = gob.NewDecoder(bytes.NewReader(data)).Decode(&out) - if err != nil { - return nil - } - m = make(map[string]interface{}) - for _, x := range out { - m[x.Name] = x.Data - } - imported[path] = m - } - return m[key] -} diff --git a/go/analysis/passes/vet/testdata/tagtest/file1.go b/go/analysis/passes/vet/testdata/tagtest/file1.go deleted file mode 100644 index 22a1509acc0..00000000000 --- a/go/analysis/passes/vet/testdata/tagtest/file1.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build testtag - -package main - -func main() { -} diff --git a/go/analysis/passes/vet/testdata/tagtest/file2.go b/go/analysis/passes/vet/testdata/tagtest/file2.go deleted file mode 100644 index ba7dd91bbd8..00000000000 --- a/go/analysis/passes/vet/testdata/tagtest/file2.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !testtag - -package main - -func ignore() { -} diff --git a/go/analysis/passes/vet/vet_test.go b/go/analysis/passes/vet/vet_test.go deleted file mode 100644 index 0d0c439b7c7..00000000000 --- a/go/analysis/passes/vet/vet_test.go +++ /dev/null @@ -1,441 +0,0 @@ -// +build ignore - -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main_test - -import ( - "bytes" - "errors" - "fmt" - "internal/testenv" - "io/ioutil" - "log" - "os" - "os/exec" - "path/filepath" - "regexp" - "runtime" - "strconv" - "strings" - "sync" - "testing" -) - -const ( - dataDir = "testdata" - binary = "./testvet.exe" -) - -// We implement TestMain so remove the test binary when all is done. -func TestMain(m *testing.M) { - result := m.Run() - os.Remove(binary) - os.Exit(result) -} - -var ( - buildMu sync.Mutex // guards following - built = false // We have built the binary. - failed = false // We have failed to build the binary, don't try again. -) - -func Build(t *testing.T) { - buildMu.Lock() - defer buildMu.Unlock() - if built { - return - } - if failed { - t.Skip("cannot run on this environment") - } - testenv.MustHaveGoBuild(t) - cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", binary) - output, err := cmd.CombinedOutput() - if err != nil { - failed = true - fmt.Fprintf(os.Stderr, "%s\n", output) - t.Fatal(err) - } - built = true -} - -func Vet(t *testing.T, files []string) { - flags := []string{ - "-printfuncs=Warn:1,Warnf:1", - "-all", - "-shadow", - } - cmd := exec.Command(binary, append(flags, files...)...) - errchk(cmd, files, t) -} - -// TestVet is equivalent to running this: -// go build -o ./testvet -// errorCheck the output of ./testvet -shadow -printfuncs='Warn:1,Warnf:1' testdata/*.go testdata/*.s -// rm ./testvet -// - -// TestVet tests self-contained files in testdata/*.go. -// -// If a file contains assembly or has inter-dependencies, it should be -// in its own test, like TestVetAsm, TestDivergentPackagesExamples, -// etc below. -func TestVet(t *testing.T) { - Build(t) - t.Parallel() - - gos, err := filepath.Glob(filepath.Join(dataDir, "*.go")) - if err != nil { - t.Fatal(err) - } - wide := runtime.GOMAXPROCS(0) - if wide > len(gos) { - wide = len(gos) - } - batch := make([][]string, wide) - for i, file := range gos { - // The print.go test is run by TestVetPrint. - if strings.HasSuffix(file, "print.go") { - continue - } - batch[i%wide] = append(batch[i%wide], file) - } - for i, files := range batch { - if len(files) == 0 { - continue - } - files := files - t.Run(fmt.Sprint(i), func(t *testing.T) { - t.Parallel() - t.Logf("files: %q", files) - Vet(t, files) - }) - } -} - -func TestVetPrint(t *testing.T) { - Build(t) - file := filepath.Join("testdata", "print.go") - cmd := exec.Command( - "go", "vet", "-vettool="+binary, - "-printf", - "-printfuncs=Warn:1,Warnf:1", - file, - ) - errchk(cmd, []string{file}, t) -} - -func TestVetAsm(t *testing.T) { - Build(t) - - asmDir := filepath.Join(dataDir, "asm") - gos, err := filepath.Glob(filepath.Join(asmDir, "*.go")) - if err != nil { - t.Fatal(err) - } - asms, err := filepath.Glob(filepath.Join(asmDir, "*.s")) - if err != nil { - t.Fatal(err) - } - - t.Parallel() - Vet(t, append(gos, asms...)) -} - -func TestVetDirs(t *testing.T) { - t.Parallel() - Build(t) - for _, dir := range []string{ - "testingpkg", - "divergent", - "buildtag", - "incomplete", // incomplete examples - "cgo", - } { - dir := dir - t.Run(dir, func(t *testing.T) { - t.Parallel() - gos, err := filepath.Glob(filepath.Join("testdata", dir, "*.go")) - if err != nil { - t.Fatal(err) - } - Vet(t, gos) - }) - } -} - -func errchk(c *exec.Cmd, files []string, t *testing.T) { - output, err := c.CombinedOutput() - if _, ok := err.(*exec.ExitError); !ok { - t.Logf("vet output:\n%s", output) - t.Fatal(err) - } - fullshort := make([]string, 0, len(files)*2) - for _, f := range files { - fullshort = append(fullshort, f, filepath.Base(f)) - } - err = errorCheck(string(output), false, fullshort...) - if err != nil { - t.Errorf("error check failed: %s", err) - } -} - -// TestTags verifies that the -tags argument controls which files to check. -func TestTags(t *testing.T) { - t.Parallel() - Build(t) - for _, tag := range []string{"testtag", "x testtag y", "x,testtag,y"} { - tag := tag - t.Run(tag, func(t *testing.T) { - t.Parallel() - t.Logf("-tags=%s", tag) - args := []string{ - "-tags=" + tag, - "-v", // We're going to look at the files it examines. - "testdata/tagtest", - } - cmd := exec.Command(binary, args...) - output, err := cmd.CombinedOutput() - if err != nil { - t.Fatal(err) - } - // file1 has testtag and file2 has !testtag. - if !bytes.Contains(output, []byte(filepath.Join("tagtest", "file1.go"))) { - t.Error("file1 was excluded, should be included") - } - if bytes.Contains(output, []byte(filepath.Join("tagtest", "file2.go"))) { - t.Error("file2 was included, should be excluded") - } - }) - } -} - -// Issue #21188. -func TestVetVerbose(t *testing.T) { - t.Parallel() - Build(t) - cmd := exec.Command(binary, "-v", "-all", "testdata/cgo/cgo3.go") - out, err := cmd.CombinedOutput() - if err != nil { - t.Logf("%s", out) - t.Error(err) - } -} - -// All declarations below were adapted from test/run.go. - -// errorCheck matches errors in outStr against comments in source files. -// For each line of the source files which should generate an error, -// there should be a comment of the form // ERROR "regexp". -// If outStr has an error for a line which has no such comment, -// this function will report an error. -// Likewise if outStr does not have an error for a line which has a comment, -// or if the error message does not match the . -// The syntax is Perl but its best to stick to egrep. -// -// Sources files are supplied as fullshort slice. -// It consists of pairs: full path to source file and it's base name. -func errorCheck(outStr string, wantAuto bool, fullshort ...string) (err error) { - var errs []error - out := splitOutput(outStr, wantAuto) - // Cut directory name. - for i := range out { - for j := 0; j < len(fullshort); j += 2 { - full, short := fullshort[j], fullshort[j+1] - out[i] = strings.ReplaceAll(out[i], full, short) - } - } - - var want []wantedError - for j := 0; j < len(fullshort); j += 2 { - full, short := fullshort[j], fullshort[j+1] - want = append(want, wantedErrors(full, short)...) - } - for _, we := range want { - var errmsgs []string - if we.auto { - errmsgs, out = partitionStrings("", out) - } else { - errmsgs, out = partitionStrings(we.prefix, out) - } - if len(errmsgs) == 0 { - errs = append(errs, fmt.Errorf("%s:%d: missing error %q", we.file, we.lineNum, we.reStr)) - continue - } - matched := false - n := len(out) - for _, errmsg := range errmsgs { - // Assume errmsg says "file:line: foo". - // Cut leading "file:line: " to avoid accidental matching of file name instead of message. - text := errmsg - if i := strings.Index(text, " "); i >= 0 { - text = text[i+1:] - } - if we.re.MatchString(text) { - matched = true - } else { - out = append(out, errmsg) - } - } - if !matched { - errs = append(errs, fmt.Errorf("%s:%d: no match for %#q in:\n\t%s", we.file, we.lineNum, we.reStr, strings.Join(out[n:], "\n\t"))) - continue - } - } - - if len(out) > 0 { - errs = append(errs, fmt.Errorf("Unmatched Errors:")) - for _, errLine := range out { - errs = append(errs, fmt.Errorf("%s", errLine)) - } - } - - if len(errs) == 0 { - return nil - } - if len(errs) == 1 { - return errs[0] - } - var buf bytes.Buffer - fmt.Fprintf(&buf, "\n") - for _, err := range errs { - fmt.Fprintf(&buf, "%s\n", err.Error()) - } - return errors.New(buf.String()) -} - -func splitOutput(out string, wantAuto bool) []string { - // gc error messages continue onto additional lines with leading tabs. - // Split the output at the beginning of each line that doesn't begin with a tab. - // lines are impossible to match so those are filtered out. - var res []string - for _, line := range strings.Split(out, "\n") { - line = strings.TrimSuffix(line, "\r") // normalize Windows output - if strings.HasPrefix(line, "\t") { - res[len(res)-1] += "\n" + line - } else if strings.HasPrefix(line, "go tool") || strings.HasPrefix(line, "#") || !wantAuto && strings.HasPrefix(line, "") { - continue - } else if strings.TrimSpace(line) != "" { - res = append(res, line) - } - } - return res -} - -// matchPrefix reports whether s starts with file name prefix followed by a :, -// and possibly preceded by a directory name. -func matchPrefix(s, prefix string) bool { - i := strings.Index(s, ":") - if i < 0 { - return false - } - j := strings.LastIndex(s[:i], "/") - s = s[j+1:] - if len(s) <= len(prefix) || s[:len(prefix)] != prefix { - return false - } - if s[len(prefix)] == ':' { - return true - } - return false -} - -func partitionStrings(prefix string, strs []string) (matched, unmatched []string) { - for _, s := range strs { - if matchPrefix(s, prefix) { - matched = append(matched, s) - } else { - unmatched = append(unmatched, s) - } - } - return -} - -type wantedError struct { - reStr string - re *regexp.Regexp - lineNum int - auto bool // match line - file string - prefix string -} - -var ( - errRx = regexp.MustCompile(`// (?:GC_)?ERROR (.*)`) - errAutoRx = regexp.MustCompile(`// (?:GC_)?ERRORAUTO (.*)`) - errQuotesRx = regexp.MustCompile(`"([^"]*)"`) - lineRx = regexp.MustCompile(`LINE(([+-])([0-9]+))?`) -) - -// wantedErrors parses expected errors from comments in a file. -func wantedErrors(file, short string) (errs []wantedError) { - cache := make(map[string]*regexp.Regexp) - - src, err := ioutil.ReadFile(file) - if err != nil { - log.Fatal(err) - } - for i, line := range strings.Split(string(src), "\n") { - lineNum := i + 1 - if strings.Contains(line, "////") { - // double comment disables ERROR - continue - } - var auto bool - m := errAutoRx.FindStringSubmatch(line) - if m != nil { - auto = true - } else { - m = errRx.FindStringSubmatch(line) - } - if m == nil { - continue - } - all := m[1] - mm := errQuotesRx.FindAllStringSubmatch(all, -1) - if mm == nil { - log.Fatalf("%s:%d: invalid errchk line: %s", file, lineNum, line) - } - for _, m := range mm { - replacedOnce := false - rx := lineRx.ReplaceAllStringFunc(m[1], func(m string) string { - if replacedOnce { - return m - } - replacedOnce = true - n := lineNum - if strings.HasPrefix(m, "LINE+") { - delta, _ := strconv.Atoi(m[5:]) - n += delta - } else if strings.HasPrefix(m, "LINE-") { - delta, _ := strconv.Atoi(m[5:]) - n -= delta - } - return fmt.Sprintf("%s:%d", short, n) - }) - re := cache[rx] - if re == nil { - var err error - re, err = regexp.Compile(rx) - if err != nil { - log.Fatalf("%s:%d: invalid regexp \"%#q\" in ERROR line: %v", file, lineNum, rx, err) - } - cache[rx] = re - } - prefix := fmt.Sprintf("%s:%d", short, lineNum) - errs = append(errs, wantedError{ - reStr: rx, - re: re, - prefix: prefix, - auto: auto, - lineNum: lineNum, - file: short, - }) - } - } - - return -}