diff options
author | Ian Lance Taylor <iant@golang.org> | 2019-09-06 18:12:46 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2019-09-06 18:12:46 +0000 |
commit | aa8901e9bb0399d2c16f988ba2fe46eb0c0c5d13 (patch) | |
tree | 7e63b06d1eec92beec6997c9d3ab47a5d6a835be /libgo/go/golang.org/x/tools | |
parent | 920ea3b8ba3164b61ac9490dfdfceb6936eda6dd (diff) | |
download | gcc-aa8901e9bb0399d2c16f988ba2fe46eb0c0c5d13.tar.gz |
libgo: update to Go 1.13beta1 release
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/193497
From-SVN: r275473
Diffstat (limited to 'libgo/go/golang.org/x/tools')
21 files changed, 383 insertions, 138 deletions
diff --git a/libgo/go/golang.org/x/tools/go/analysis/analysis.go b/libgo/go/golang.org/x/tools/go/analysis/analysis.go index 21baa02a8de..19e1e421a38 100644 --- a/libgo/go/golang.org/x/tools/go/analysis/analysis.go +++ b/libgo/go/golang.org/x/tools/go/analysis/analysis.go @@ -87,6 +87,7 @@ type Pass struct { OtherFiles []string // names of non-Go files of this package Pkg *types.Package // type information about the package TypesInfo *types.Info // type information about the syntax trees + TypesSizes types.Sizes // function for computing sizes of types // Report reports a Diagnostic, a finding about a specific location // in the analyzed source code such as a potential mistake. @@ -127,10 +128,32 @@ type Pass struct { // See comments for ExportObjectFact. ExportPackageFact func(fact Fact) + // AllPackageFacts returns a new slice containing all package facts in unspecified order. + // WARNING: This is an experimental API and may change in the future. + AllPackageFacts func() []PackageFact + + // AllObjectFacts returns a new slice containing all object facts in unspecified order. + // WARNING: This is an experimental API and may change in the future. + AllObjectFacts func() []ObjectFact + /* Further fields may be added in future. */ // For example, suggested or applied refactorings. } +// PackageFact is a package together with an associated fact. +// WARNING: This is an experimental API and may change in the future. +type PackageFact struct { + Package *types.Package + Fact Fact +} + +// ObjectFact is an object together with an associated fact. +// WARNING: This is an experimental API and may change in the future. +type ObjectFact struct { + Object types.Object + Fact Fact +} + // Reportf is a helper function that reports a Diagnostic using the // specified position and formatted error message. func (pass *Pass) Reportf(pos token.Pos, format string, args ...interface{}) { @@ -138,6 +161,15 @@ func (pass *Pass) Reportf(pos token.Pos, format string, args ...interface{}) { pass.Report(Diagnostic{Pos: pos, Message: msg}) } +// reportNodef is a helper function that reports a Diagnostic using the +// range denoted by the AST node. +// +// WARNING: This is an experimental API and may change in the future. +func (pass *Pass) reportNodef(node ast.Node, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + pass.Report(Diagnostic{Pos: node.Pos(), End: node.End(), Message: msg}) +} + func (pass *Pass) String() string { return fmt.Sprintf("%s@%s", pass.Analyzer.Name, pass.Pkg.Path()) } @@ -180,13 +212,17 @@ type Fact interface { AFact() // dummy method to avoid type errors } -// A Diagnostic is a message associated with a source location. +// A Diagnostic is a message associated with a source location or range. // // An Analyzer may return a variety of diagnostics; the optional Category, // which should be a constant, may be used to classify them. // It is primarily intended to make it easy to look up documentation. +// +// If End is provided, the diagnostic is specified to apply to the range between +// Pos and End. type Diagnostic struct { Pos token.Pos - Category string // optional + End token.Pos // optional + Category string // optional Message string } diff --git a/libgo/go/golang.org/x/tools/go/analysis/doc.go b/libgo/go/golang.org/x/tools/go/analysis/doc.go index f925849ab50..2d44b0458a9 100644 --- a/libgo/go/golang.org/x/tools/go/analysis/doc.go +++ b/libgo/go/golang.org/x/tools/go/analysis/doc.go @@ -246,7 +246,7 @@ An Analyzer that uses facts must declare their types: var Analyzer = &analysis.Analyzer{ Name: "printf", - FactTypes: []reflect.Type{reflect.TypeOf(new(isWrapper))}, + FactTypes: []analysis.Fact{new(isWrapper)}, ... } diff --git a/libgo/go/golang.org/x/tools/go/analysis/internal/analysisflags/flags.go b/libgo/go/golang.org/x/tools/go/analysis/internal/analysisflags/flags.go index 729ac3b4176..a3c2f096300 100644 --- a/libgo/go/golang.org/x/tools/go/analysis/internal/analysisflags/flags.go +++ b/libgo/go/golang.org/x/tools/go/analysis/internal/analysisflags/flags.go @@ -8,6 +8,7 @@ package analysisflags import ( "crypto/sha256" + "encoding/gob" "encoding/json" "flag" "fmt" @@ -32,6 +33,14 @@ var ( // including (in multi mode) a flag named after the analyzer, // parses the flags, then filters and returns the list of // analyzers enabled by flags. +// +// The result is intended to be passed to unitchecker.Run or checker.Run. +// Use in unitchecker.Run will gob.Register all fact types for the returned +// graph of analyzers but of course not the ones only reachable from +// dropped analyzers. To avoid inconsistency about which gob types are +// registered from run to run, Parse itself gob.Registers all the facts +// only reachable from dropped analyzers. +// This is not a particularly elegant API, but this is an internal package. func Parse(analyzers []*analysis.Analyzer, multi bool) []*analysis.Analyzer { // Connect each analysis flag to the command line as -analysis.flag. enabled := make(map[*analysis.Analyzer]*triState) @@ -88,6 +97,8 @@ func Parse(analyzers []*analysis.Analyzer, multi bool) []*analysis.Analyzer { os.Exit(0) } + everything := expand(analyzers) + // If any -NAME flag is true, run only those analyzers. Otherwise, // if any -NAME flag is false, run all but those analyzers. if multi { @@ -119,9 +130,35 @@ func Parse(analyzers []*analysis.Analyzer, multi bool) []*analysis.Analyzer { } } + // Register fact types of skipped analyzers + // in case we encounter them in imported files. + kept := expand(analyzers) + for a := range everything { + if !kept[a] { + for _, f := range a.FactTypes { + gob.Register(f) + } + } + } + return analyzers } +func expand(analyzers []*analysis.Analyzer) map[*analysis.Analyzer]bool { + seen := make(map[*analysis.Analyzer]bool) + var visitAll func([]*analysis.Analyzer) + visitAll = func(analyzers []*analysis.Analyzer) { + for _, a := range analyzers { + if !seen[a] { + seen[a] = true + visitAll(a.Requires) + } + } + } + visitAll(analyzers) + return seen +} + func printFlags() { type jsonFlag struct { Name string @@ -152,12 +189,13 @@ func printFlags() { // addVersionFlag registers a -V flag that, if set, // prints the executable version and exits 0. // -// It is a variable not a function to permit easy -// overriding in the copy vendored in $GOROOT/src/cmd/vet: -// -// func init() { addVersionFlag = objabi.AddVersionFlag } -var addVersionFlag = func() { - flag.Var(versionFlag{}, "V", "print version and exit") +// If the -V flag already exists — for example, because it was already +// registered by a call to cmd/internal/objabi.AddVersionFlag — then +// addVersionFlag does nothing. +func addVersionFlag() { + if flag.Lookup("V") == nil { + flag.Var(versionFlag{}, "V", "print version and exit") + } } // versionFlag minimally complies with the -V protocol required by "go vet". @@ -285,9 +323,14 @@ func PrintPlain(fset *token.FileSet, diag analysis.Diagnostic) { // -c=N: show offending line plus N lines of context. if Context >= 0 { + posn := fset.Position(diag.Pos) + end := fset.Position(diag.End) + if !end.IsValid() { + end = posn + } data, _ := ioutil.ReadFile(posn.Filename) lines := strings.Split(string(data), "\n") - for i := posn.Line - Context; i <= posn.Line+Context; i++ { + for i := posn.Line - Context; i <= end.Line+Context; i++ { if 1 <= i && i <= len(lines) { fmt.Fprintf(os.Stderr, "%d\t%s\n", i, lines[i-1]) } @@ -315,6 +358,8 @@ func (tree JSONTree) Add(fset *token.FileSet, id, name string, diags []analysis. Message string `json:"message"` } var diagnostics []jsonDiagnostic + // TODO(matloob): Should the JSON diagnostics contain ranges? + // If so, how should they be formatted? for _, f := range diags { diagnostics = append(diagnostics, jsonDiagnostic{ Category: f.Category, diff --git a/libgo/go/golang.org/x/tools/go/analysis/internal/analysisflags/help.go b/libgo/go/golang.org/x/tools/go/analysis/internal/analysisflags/help.go index 043b97896dd..c5a70f3b7d6 100644 --- a/libgo/go/golang.org/x/tools/go/analysis/internal/analysisflags/help.go +++ b/libgo/go/golang.org/x/tools/go/analysis/internal/analysisflags/help.go @@ -4,6 +4,7 @@ import ( "flag" "fmt" "log" + "os" "sort" "strings" @@ -47,6 +48,7 @@ func Help(progname string, analyzers []*analysis.Analyzer, args []string) { fs.Var(f.Value, f.Name, f.Usage) } }) + fs.SetOutput(os.Stdout) fs.PrintDefaults() fmt.Printf("\nTo see details and flags of a specific analyzer, run '%s help name'.\n", progname) @@ -75,6 +77,7 @@ outer: } fs.Var(f.Value, a.Name+"."+f.Name, f.Usage) }) + fs.SetOutput(os.Stdout) fs.PrintDefaults() if len(paras) > 1 { diff --git a/libgo/go/golang.org/x/tools/go/analysis/internal/analysisflags/patch.go b/libgo/go/golang.org/x/tools/go/analysis/internal/analysisflags/patch.go deleted file mode 100644 index 8f9741055cb..00000000000 --- a/libgo/go/golang.org/x/tools/go/analysis/internal/analysisflags/patch.go +++ /dev/null @@ -1,7 +0,0 @@ -package analysisflags - -import "cmd/internal/objabi" - -// This additional file changes the behavior of the vendored code. - -func init() { addVersionFlag = objabi.AddVersionFlag } diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go b/libgo/go/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go index dce1ef7bd5e..d41c4e97e32 100644 --- a/libgo/go/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go +++ b/libgo/go/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go @@ -114,7 +114,8 @@ func init() { // library we cannot assume types.SizesFor is consistent with arches. // For now, assume 64-bit norms and print a warning. // But this warning should really be deferred until we attempt to use - // arch, which is very unlikely. + // arch, which is very unlikely. Better would be + // to defer size computation until we have Pass.TypesSizes. arch.sizes = types.SizesFor("gc", "amd64") log.Printf("unknown architecture %s", arch.name) } @@ -129,7 +130,7 @@ var ( asmPlusBuild = re(`//\s+\+build\s+([^\n]+)`) asmTEXT = re(`\bTEXT\b(.*)·([^\(]+)\(SB\)(?:\s*,\s*([0-9A-Z|+()]+))?(?:\s*,\s*\$(-?[0-9]+)(?:-([0-9]+))?)?`) asmDATA = re(`\b(DATA|GLOBL)\b`) - asmNamedFP = re(`([a-zA-Z0-9_\xFF-\x{10FFFF}]+)(?:\+([0-9]+))\(FP\)`) + asmNamedFP = re(`\$?([a-zA-Z0-9_\xFF-\x{10FFFF}]+)(?:\+([0-9]+))\(FP\)`) asmUnnamedFP = re(`[^+\-0-9](([0-9]+)\(FP\))`) asmSP = re(`[^+\-0-9](([0-9]+)\(([A-Z0-9]+)\))`) asmOpcode = re(`^\s*(?:[A-Z0-9a-z_]+:)?\s*([A-Z]+)\s*([^,]*)(?:,\s*(.*))?`) @@ -183,6 +184,7 @@ Files: fnName string localSize, argSize int wroteSP bool + noframe bool haveRetArg bool retLine []int ) @@ -230,6 +232,11 @@ Files: } } + // Ignore comments and commented-out code. + if i := strings.Index(line, "//"); i >= 0 { + line = line[:i] + } + if m := asmTEXT.FindStringSubmatch(line); m != nil { flushRet() if arch == "" { @@ -253,7 +260,7 @@ Files: // identifiers to represent the directory separator. pkgPath = strings.Replace(pkgPath, "∕", "/", -1) if pkgPath != pass.Pkg.Path() { - log.Printf("%s:%d: [%s] cannot check cross-package assembly function: %s is in package %s", fname, lineno, arch, fnName, pkgPath) + // log.Printf("%s:%d: [%s] cannot check cross-package assembly function: %s is in package %s", fname, lineno, arch, fnName, pkgPath) fn = nil fnName = "" continue @@ -274,7 +281,8 @@ Files: localSize += archDef.intSize } argSize, _ = strconv.Atoi(m[5]) - if fn == nil && !strings.Contains(fnName, "<>") { + noframe = strings.Contains(flag, "NOFRAME") + if fn == nil && !strings.Contains(fnName, "<>") && !noframe { badf("function %s missing Go declaration", fnName) } wroteSP = false @@ -304,13 +312,18 @@ Files: continue } - if strings.Contains(line, ", "+archDef.stack) || strings.Contains(line, ",\t"+archDef.stack) { + if strings.Contains(line, ", "+archDef.stack) || strings.Contains(line, ",\t"+archDef.stack) || strings.Contains(line, "NOP "+archDef.stack) || strings.Contains(line, "NOP\t"+archDef.stack) { wroteSP = true continue } + if arch == "wasm" && strings.Contains(line, "CallImport") { + // CallImport is a call out to magic that can write the result. + haveRetArg = true + } + for _, m := range asmSP.FindAllStringSubmatch(line, -1) { - if m[3] != archDef.stack || wroteSP { + if m[3] != archDef.stack || wroteSP || noframe { continue } off := 0 @@ -370,7 +383,7 @@ Files: } continue } - asmCheckVar(badf, fn, line, m[0], off, v) + asmCheckVar(badf, fn, line, m[0], off, v, archDef) } } flushRet() @@ -588,7 +601,7 @@ func asmParseDecl(pass *analysis.Pass, decl *ast.FuncDecl) map[string]*asmFunc { } // asmCheckVar checks a single variable reference. -func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr string, off int, v *asmVar) { +func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr string, off int, v *asmVar, archDef *asmArch) { m := asmOpcode.FindStringSubmatch(line) if m == nil { if !strings.HasPrefix(strings.TrimSpace(line), "//") { @@ -597,6 +610,8 @@ func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr stri return } + addr := strings.HasPrefix(expr, "$") + // Determine operand sizes from instruction. // Typically the suffix suffices, but there are exceptions. var src, dst, kind asmKind @@ -616,10 +631,13 @@ func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr stri // They just take the address of it. case "386.LEAL": dst = 4 + addr = true case "amd64.LEAQ": dst = 8 + addr = true case "amd64p32.LEAL": dst = 4 + addr = true default: switch fn.arch.name { case "386", "amd64": @@ -724,6 +742,11 @@ func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr stri vs = v.inner[0].size vt = v.inner[0].typ } + if addr { + vk = asmKind(archDef.ptrSize) + vs = archDef.ptrSize + vt = "address" + } if off != v.off { var inner bytes.Buffer diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/bools/bools.go b/libgo/go/golang.org/x/tools/go/analysis/passes/bools/bools.go index 833c9d7aae1..c82d3675b95 100644 --- a/libgo/go/golang.org/x/tools/go/analysis/passes/bools/bools.go +++ b/libgo/go/golang.org/x/tools/go/analysis/passes/bools/bools.go @@ -30,8 +30,13 @@ func run(pass *analysis.Pass) (interface{}, error) { nodeFilter := []ast.Node{ (*ast.BinaryExpr)(nil), } + seen := make(map[*ast.BinaryExpr]bool) inspect.Preorder(nodeFilter, func(n ast.Node) { e := n.(*ast.BinaryExpr) + if seen[e] { + // Already processed as a subexpression of an earlier node. + return + } var op boolOp switch e.Op { @@ -43,10 +48,7 @@ func run(pass *analysis.Pass) (interface{}, error) { return } - // TODO(adonovan): this reports n(n-1)/2 errors for an - // expression e||...||e of depth n. Fix. - // See https://golang.org/issue/28086. - comm := op.commutativeSets(pass.TypesInfo, e) + comm := op.commutativeSets(pass.TypesInfo, e, seen) for _, exprs := range comm { op.checkRedundant(pass, exprs) op.checkSuspect(pass, exprs) @@ -70,8 +72,9 @@ var ( // expressions in e that are connected by op. // For example, given 'a || b || f() || c || d' with the or op, // commutativeSets returns {{b, a}, {d, c}}. -func (op boolOp) commutativeSets(info *types.Info, e *ast.BinaryExpr) [][]ast.Expr { - exprs := op.split(e) +// commutativeSets adds any expanded BinaryExprs to seen. +func (op boolOp) commutativeSets(info *types.Info, e *ast.BinaryExpr, seen map[*ast.BinaryExpr]bool) [][]ast.Expr { + exprs := op.split(e, seen) // Partition the slice of expressions into commutative sets. i := 0 @@ -188,11 +191,13 @@ func hasSideEffects(info *types.Info, e ast.Expr) bool { // split returns a slice of all subexpressions in e that are connected by op. // For example, given 'a || (b || c) || d' with the or op, // split returns []{d, c, b, a}. -func (op boolOp) split(e ast.Expr) (exprs []ast.Expr) { +// seen[e] is already true; any newly processed exprs are added to seen. +func (op boolOp) split(e ast.Expr, seen map[*ast.BinaryExpr]bool) (exprs []ast.Expr) { for { e = unparen(e) if b, ok := e.(*ast.BinaryExpr); ok && b.Op == op.tok { - exprs = append(exprs, op.split(b.Y)...) + seen[b] = true + exprs = append(exprs, op.split(b.Y, seen)...) e = b.X } else { exprs = append(exprs, e) diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/cgocall/cgocall.go b/libgo/go/golang.org/x/tools/go/analysis/passes/cgocall/cgocall.go index a6e76a192de..42a690dbab6 100644 --- a/libgo/go/golang.org/x/tools/go/analysis/passes/cgocall/cgocall.go +++ b/libgo/go/golang.org/x/tools/go/analysis/passes/cgocall/cgocall.go @@ -9,7 +9,6 @@ package cgocall import ( "fmt" "go/ast" - "go/build" "go/format" "go/parser" "go/token" @@ -46,7 +45,7 @@ func run(pass *analysis.Pass) (interface{}, error) { return nil, nil // doesn't use cgo } - cgofiles, info, err := typeCheckCgoSourceFiles(pass.Fset, pass.Pkg, pass.Files, pass.TypesInfo) + cgofiles, info, err := typeCheckCgoSourceFiles(pass.Fset, pass.Pkg, pass.Files, pass.TypesInfo, pass.TypesSizes) if err != nil { return nil, err } @@ -172,7 +171,7 @@ func checkCgo(fset *token.FileSet, f *ast.File, info *types.Info, reportf func(t // limited ourselves here to preserving function bodies and initializer // expressions since that is all that the cgocall analyzer needs. // -func typeCheckCgoSourceFiles(fset *token.FileSet, pkg *types.Package, files []*ast.File, info *types.Info) ([]*ast.File, *types.Info, error) { +func typeCheckCgoSourceFiles(fset *token.FileSet, pkg *types.Package, files []*ast.File, info *types.Info, sizes types.Sizes) ([]*ast.File, *types.Info, error) { const thispkg = "·this·" // Which files are cgo files? @@ -270,8 +269,7 @@ func typeCheckCgoSourceFiles(fset *token.FileSet, pkg *types.Package, files []*a Importer: importerFunc(func(path string) (*types.Package, error) { return importMap[path], nil }), - // TODO(adonovan): Sizes should probably be provided by analysis.Pass. - Sizes: types.SizesFor("gccgo", build.Default.GOARCH), + Sizes: sizes, Error: func(error) {}, // ignore errors (e.g. unused import) } diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/composite/composite.go b/libgo/go/golang.org/x/tools/go/analysis/passes/composite/composite.go index 9cca7781d00..2abe7c6d51e 100644 --- a/libgo/go/golang.org/x/tools/go/analysis/passes/composite/composite.go +++ b/libgo/go/golang.org/x/tools/go/analysis/passes/composite/composite.go @@ -21,7 +21,16 @@ const Doc = `check for unkeyed composite literals This analyzer reports a diagnostic for composite literals of struct types imported from another package that do not use the field-keyed syntax. Such literals are fragile because the addition of a new field -(even if unexported) to the struct will cause compilation to fail.` +(even if unexported) to the struct will cause compilation to fail. + +As an example, + + err = &net.DNSConfigError{err} + +should be replaced by: + + err = &net.DNSConfigError{Err: err} +` var Analyzer = &analysis.Analyzer{ Name: "composites", diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/errorsas/errorsas.go b/libgo/go/golang.org/x/tools/go/analysis/passes/errorsas/errorsas.go new file mode 100644 index 00000000000..c411466c28e --- /dev/null +++ b/libgo/go/golang.org/x/tools/go/analysis/passes/errorsas/errorsas.go @@ -0,0 +1,75 @@ +// Copyright 2018 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. + +// The errorsas package defines an Analyzer that checks that the second arugment to +// errors.As is a pointer to a type implementing error. +package errorsas + +import ( + "go/ast" + "go/types" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/go/types/typeutil" +) + +const doc = `report passing non-pointer or non-error values to errors.As + +The errorsas analysis reports calls to errors.As where the type +of the second argument is not a pointer to a type implementing error.` + +var Analyzer = &analysis.Analyzer{ + Name: "errorsas", + Doc: doc, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: run, +} + +func run(pass *analysis.Pass) (interface{}, error) { + switch pass.Pkg.Path() { + case "errors", "errors_test": + // These packages know how to use their own APIs. + // Sometimes they are testing what happens to incorrect programs. + return nil, nil + } + + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + + nodeFilter := []ast.Node{ + (*ast.CallExpr)(nil), + } + inspect.Preorder(nodeFilter, func(n ast.Node) { + call := n.(*ast.CallExpr) + fn := typeutil.StaticCallee(pass.TypesInfo, call) + if fn == nil { + return // not a static call + } + if len(call.Args) < 2 { + return // not enough arguments, e.g. called with return values of another function + } + if fn.FullName() == "errors.As" && !pointerToInterfaceOrError(pass, call.Args[1]) { + pass.Reportf(call.Pos(), "second argument to errors.As must be a pointer to an interface or a type implementing error") + } + }) + return nil, nil +} + +var errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface) + +// pointerToInterfaceOrError reports whether the type of e is a pointer to an interface or a type implementing error, +// or is the empty interface. +func pointerToInterfaceOrError(pass *analysis.Pass, e ast.Expr) bool { + t := pass.TypesInfo.Types[e].Type + if it, ok := t.Underlying().(*types.Interface); ok && it.NumMethods() == 0 { + return true + } + pt, ok := t.Underlying().(*types.Pointer) + if !ok { + return false + } + _, ok = pt.Elem().Underlying().(*types.Interface) + return ok || types.Implements(pt.Elem(), errorType) +} diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/inspect/inspect.go b/libgo/go/golang.org/x/tools/go/analysis/passes/inspect/inspect.go index bd06549984a..8213f633135 100644 --- a/libgo/go/golang.org/x/tools/go/analysis/passes/inspect/inspect.go +++ b/libgo/go/golang.org/x/tools/go/analysis/passes/inspect/inspect.go @@ -8,7 +8,11 @@ // // Example of use in another analysis: // -// import "golang.org/x/tools/go/analysis/passes/inspect" +// import ( +// "golang.org/x/tools/go/analysis" +// "golang.org/x/tools/go/analysis/passes/inspect" +// "golang.org/x/tools/go/ast/inspector" +// ) // // var Analyzer = &analysis.Analyzer{ // ... diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel.go b/libgo/go/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel.go index b5161836a57..e88cf57d8f7 100644 --- a/libgo/go/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel.go +++ b/libgo/go/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel.go @@ -45,6 +45,8 @@ var contextPackage = "context" // control-flow path from the call to a return statement and that path // does not "use" the cancel function. Any reference to the variable // counts as a use, even within a nested function literal. +// If the variable's scope is larger than the function +// containing the assignment, we assume that other uses exist. // // checkLostCancel analyzes a single named or literal function. func run(pass *analysis.Pass) (interface{}, error) { @@ -66,6 +68,15 @@ func run(pass *analysis.Pass) (interface{}, error) { } func runFunc(pass *analysis.Pass, node ast.Node) { + // Find scope of function node + var funcScope *types.Scope + switch v := node.(type) { + case *ast.FuncLit: + funcScope = pass.TypesInfo.Scopes[v.Type] + case *ast.FuncDecl: + funcScope = pass.TypesInfo.Scopes[v.Type] + } + // Maps each cancel variable to its defining ValueSpec/AssignStmt. cancelvars := make(map[*types.Var]ast.Node) @@ -114,7 +125,11 @@ func runFunc(pass *analysis.Pass, node ast.Node) { "the cancel function returned by context.%s should be called, not discarded, to avoid a context leak", n.(*ast.SelectorExpr).Sel.Name) } else if v, ok := pass.TypesInfo.Uses[id].(*types.Var); ok { - cancelvars[v] = stmt + // If the cancel variable is defined outside function scope, + // do not analyze it. + if funcScope.Contains(v.Pos()) { + cancelvars[v] = stmt + } } else if v, ok := pass.TypesInfo.Defs[id].(*types.Var); ok { cancelvars[v] = stmt } diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/printf/printf.go b/libgo/go/golang.org/x/tools/go/analysis/passes/printf/printf.go index c0265aafeee..f59e95dc219 100644 --- a/libgo/go/golang.org/x/tools/go/analysis/passes/printf/printf.go +++ b/libgo/go/golang.org/x/tools/go/analysis/passes/printf/printf.go @@ -453,15 +453,23 @@ func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func, } // isFormatter reports whether t satisfies fmt.Formatter. -// Unlike fmt.Stringer, it's impossible to satisfy fmt.Formatter without importing fmt. -func isFormatter(pass *analysis.Pass, t types.Type) bool { - for _, imp := range pass.Pkg.Imports() { - if imp.Path() == "fmt" { - formatter := imp.Scope().Lookup("Formatter").Type().Underlying().(*types.Interface) - return types.Implements(t, formatter) - } +// The only interface method to look for is "Format(State, rune)". +func isFormatter(typ types.Type) bool { + obj, _, _ := types.LookupFieldOrMethod(typ, false, nil, "Format") + fn, ok := obj.(*types.Func) + if !ok { + return false } - return false + sig := fn.Type().(*types.Signature) + return sig.Params().Len() == 2 && + sig.Results().Len() == 0 && + isNamed(sig.Params().At(0).Type(), "fmt", "State") && + types.Identical(sig.Params().At(1).Type(), types.Typ[types.Rune]) +} + +func isNamed(T types.Type, pkgpath, name string) bool { + named, ok := T.(*types.Named) + return ok && named.Obj().Pkg().Path() == pkgpath && named.Obj().Name() == name } // formatState holds the parsed representation of a printf directive such as "%3.*[4]d". @@ -731,6 +739,7 @@ var printVerbs = []printVerb{ {'T', "-", anyType}, {'U', "-#", argRune | argInt}, {'v', allFlags, anyType}, + {'w', noFlag, anyType}, {'x', sharpNumFlag, argRune | argInt | argString | argPointer}, {'X', sharpNumFlag, argRune | argInt | argString | argPointer}, } @@ -753,7 +762,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o formatter := false if state.argNum < len(call.Args) { if tv, ok := pass.TypesInfo.Types[call.Args[state.argNum]]; ok { - formatter = isFormatter(pass, tv.Type) + formatter = isFormatter(tv.Type) } } @@ -831,7 +840,7 @@ func recursiveStringer(pass *analysis.Pass, e ast.Expr) bool { typ := pass.TypesInfo.Types[e].Type // It's unlikely to be a recursive stringer if it has a Format method. - if isFormatter(pass, typ) { + if isFormatter(typ) { return false } @@ -847,20 +856,28 @@ func recursiveStringer(pass *analysis.Pass, e ast.Expr) bool { return false } - // Is it the receiver r, or &r? - recv := stringMethod.Type().(*types.Signature).Recv() - if recv == nil { + sig := stringMethod.Type().(*types.Signature) + if !isStringer(sig) { return false } + + // Is it the receiver r, or &r? if u, ok := e.(*ast.UnaryExpr); ok && u.Op == token.AND { e = u.X // strip off & from &r } if id, ok := e.(*ast.Ident); ok { - return pass.TypesInfo.Uses[id] == recv + return pass.TypesInfo.Uses[id] == sig.Recv() } return false } +// isStringer reports whether the method signature matches the String() definition in fmt.Stringer. +func isStringer(sig *types.Signature) bool { + return sig.Params().Len() == 0 && + sig.Results().Len() == 1 && + sig.Results().At(0).Type() == types.Typ[types.String] +} + // isFunctionValue reports whether the expression is a function as opposed to a function call. // It is almost always a mistake to print a function value. func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool { diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/printf/types.go b/libgo/go/golang.org/x/tools/go/analysis/passes/printf/types.go index 87523a19c67..12286fd5df5 100644 --- a/libgo/go/golang.org/x/tools/go/analysis/passes/printf/types.go +++ b/libgo/go/golang.org/x/tools/go/analysis/passes/printf/types.go @@ -2,7 +2,6 @@ package printf import ( "go/ast" - "go/build" "go/types" "golang.org/x/tools/go/analysis" @@ -39,7 +38,7 @@ func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type, } } // If the type implements fmt.Formatter, we have nothing to check. - if isFormatter(pass, typ) { + if isFormatter(typ) { return true } // If we can use a string, might arg (dynamically) implement the Stringer or Error interface? @@ -235,5 +234,3 @@ func matchStructArgType(pass *analysis.Pass, t printfArgType, typ *types.Struct, } return true } - -var archSizes = types.SizesFor("gccgo", build.Default.GOARCH) diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/shift/shift.go b/libgo/go/golang.org/x/tools/go/analysis/passes/shift/shift.go index 4142ac342ad..39f54573c9f 100644 --- a/libgo/go/golang.org/x/tools/go/analysis/passes/shift/shift.go +++ b/libgo/go/golang.org/x/tools/go/analysis/passes/shift/shift.go @@ -12,10 +12,8 @@ package shift import ( "go/ast" - "go/build" "go/constant" "go/token" - "go/types" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" @@ -93,36 +91,9 @@ func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) { if t == nil { return } - b, ok := t.Underlying().(*types.Basic) - if !ok { - return - } - var size int64 - switch b.Kind() { - case types.Uint8, types.Int8: - size = 8 - case types.Uint16, types.Int16: - size = 16 - case types.Uint32, types.Int32: - size = 32 - case types.Uint64, types.Int64: - size = 64 - case types.Int, types.Uint: - size = uintBitSize - case types.Uintptr: - size = uintptrBitSize - default: - return - } + size := 8 * pass.TypesSizes.Sizeof(t) if amt >= size { ident := analysisutil.Format(pass.Fset, x) pass.Reportf(node.Pos(), "%s (%d bits) too small for shift of %d", ident, size, amt) } } - -var ( - uintBitSize = 8 * archSizes.Sizeof(types.Typ[types.Uint]) - uintptrBitSize = 8 * archSizes.Sizeof(types.Typ[types.Uintptr]) -) - -var archSizes = types.SizesFor("gccgo", build.Default.GOARCH) diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/stdmethods/stdmethods.go b/libgo/go/golang.org/x/tools/go/analysis/passes/stdmethods/stdmethods.go index 83495112243..bc1db7e4c2e 100644 --- a/libgo/go/golang.org/x/tools/go/analysis/passes/stdmethods/stdmethods.go +++ b/libgo/go/golang.org/x/tools/go/analysis/passes/stdmethods/stdmethods.go @@ -8,7 +8,6 @@ package stdmethods import ( "go/ast" - "go/token" "go/types" "strings" @@ -117,6 +116,13 @@ func canonicalMethod(pass *analysis.Pass, id *ast.Ident) { args := sign.Params() results := sign.Results() + // Special case: WriteTo with more than one argument, + // not trying at all to implement io.WriterTo, + // comes up often enough to skip. + if id.Name == "WriteTo" && args.Len() > 1 { + return + } + // Do the =s (if any) all match? if !matchParams(pass, expect.args, args, "=") || !matchParams(pass, expect.results, results, "=") { return @@ -163,7 +169,7 @@ func matchParams(pass *analysis.Pass, expect []string, actual *types.Tuple, pref if i >= actual.Len() { return false } - if !matchParamType(pass.Fset, pass.Pkg, x, actual.At(i).Type()) { + if !matchParamType(x, actual.At(i).Type()) { return false } } @@ -174,13 +180,8 @@ func matchParams(pass *analysis.Pass, expect []string, actual *types.Tuple, pref } // Does this one type match? -func matchParamType(fset *token.FileSet, pkg *types.Package, expect string, actual types.Type) bool { +func matchParamType(expect string, actual types.Type) bool { expect = strings.TrimPrefix(expect, "=") - // Strip package name if we're in that package. - if n := len(pkg.Name()); len(expect) > n && expect[:n] == pkg.Name() && expect[n] == '.' { - expect = expect[n+1:] - } - // Overkill but easy. return typeString(actual) == expect } diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/structtag/structtag.go b/libgo/go/golang.org/x/tools/go/analysis/passes/structtag/structtag.go index 2b67c376bab..acc6e6c770d 100644 --- a/libgo/go/golang.org/x/tools/go/analysis/passes/structtag/structtag.go +++ b/libgo/go/golang.org/x/tools/go/analysis/passes/structtag/structtag.go @@ -41,7 +41,7 @@ func run(pass *analysis.Pass) (interface{}, error) { } inspect.Preorder(nodeFilter, func(n ast.Node) { styp := pass.TypesInfo.Types[n.(*ast.StructType)].Type.(*types.Struct) - var seen map[[2]string]token.Pos + var seen namesSeen for i := 0; i < styp.NumFields(); i++ { field := styp.Field(i) tag := styp.Tag(i) @@ -51,13 +51,47 @@ func run(pass *analysis.Pass) (interface{}, error) { return nil, nil } +// namesSeen keeps track of encoding tags by their key, name, and nested level +// from the initial struct. The level is taken into account because equal +// encoding key names only conflict when at the same level; otherwise, the lower +// level shadows the higher level. +type namesSeen map[uniqueName]token.Pos + +type uniqueName struct { + key string // "xml" or "json" + name string // the encoding name + level int // anonymous struct nesting level +} + +func (s *namesSeen) Get(key, name string, level int) (token.Pos, bool) { + if *s == nil { + *s = make(map[uniqueName]token.Pos) + } + pos, ok := (*s)[uniqueName{key, name, level}] + return pos, ok +} + +func (s *namesSeen) Set(key, name string, level int, pos token.Pos) { + if *s == nil { + *s = make(map[uniqueName]token.Pos) + } + (*s)[uniqueName{key, name, level}] = pos +} + var checkTagDups = []string{"json", "xml"} var checkTagSpaces = map[string]bool{"json": true, "xml": true, "asn1": true} // checkCanonicalFieldTag checks a single struct field tag. -func checkCanonicalFieldTag(pass *analysis.Pass, field *types.Var, tag string, seen *map[[2]string]token.Pos) { +func checkCanonicalFieldTag(pass *analysis.Pass, field *types.Var, tag string, seen *namesSeen) { + switch pass.Pkg.Path() { + case "encoding/json", "encoding/xml": + // These packages know how to use their own APIs. + // Sometimes they are testing what happens to incorrect programs. + return + } + for _, key := range checkTagDups { - checkTagDuplicates(pass, tag, key, field, field, seen) + checkTagDuplicates(pass, tag, key, field, field, seen, 1) } if err := validateStructTag(tag); err != nil { @@ -88,28 +122,29 @@ func checkCanonicalFieldTag(pass *analysis.Pass, field *types.Var, tag string, s // checkTagDuplicates checks a single struct field tag to see if any tags are // duplicated. nearest is the field that's closest to the field being checked, // while still being part of the top-level struct type. -func checkTagDuplicates(pass *analysis.Pass, tag, key string, nearest, field *types.Var, seen *map[[2]string]token.Pos) { +func checkTagDuplicates(pass *analysis.Pass, tag, key string, nearest, field *types.Var, seen *namesSeen, level int) { val := reflect.StructTag(tag).Get(key) if val == "-" { // Ignored, even if the field is anonymous. return } if val == "" || val[0] == ',' { - if field.Anonymous() { - typ, ok := field.Type().Underlying().(*types.Struct) - if !ok { - return - } - for i := 0; i < typ.NumFields(); i++ { - field := typ.Field(i) - if !field.Exported() { - continue - } - tag := typ.Tag(i) - checkTagDuplicates(pass, tag, key, nearest, field, seen) + if !field.Anonymous() { + // Ignored if the field isn't anonymous. + return + } + typ, ok := field.Type().Underlying().(*types.Struct) + if !ok { + return + } + for i := 0; i < typ.NumFields(); i++ { + field := typ.Field(i) + if !field.Exported() { + continue } + tag := typ.Tag(i) + checkTagDuplicates(pass, tag, key, nearest, field, seen, level+1) } - // Ignored if the field isn't anonymous. return } if key == "xml" && field.Name() == "XMLName" { @@ -132,10 +167,7 @@ func checkTagDuplicates(pass *analysis.Pass, tag, key string, nearest, field *ty } val = val[:i] } - if *seen == nil { - *seen = map[[2]string]token.Pos{} - } - if pos, ok := (*seen)[[2]string{key, val}]; ok { + if pos, ok := seen.Get(key, val, level); ok { alsoPos := pass.Fset.Position(pos) alsoPos.Column = 0 @@ -154,7 +186,7 @@ func checkTagDuplicates(pass *analysis.Pass, tag, key string, nearest, field *ty pass.Reportf(nearest.Pos(), "struct field %s repeats %s tag %q also at %s", field.Name(), key, val, alsoPos) } else { - (*seen)[[2]string{key, val}] = field.Pos() + seen.Set(key, val, level, field.Pos()) } } diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/tests/tests.go b/libgo/go/golang.org/x/tools/go/analysis/passes/tests/tests.go index 35b0a3e7cc2..8232276186a 100644 --- a/libgo/go/golang.org/x/tools/go/analysis/passes/tests/tests.go +++ b/libgo/go/golang.org/x/tools/go/analysis/passes/tests/tests.go @@ -20,7 +20,10 @@ const Doc = `check for common mistaken usages of tests and examples The tests checker walks Test, Benchmark and Example functions checking malformed names, wrong signatures and examples documenting non-existent -identifiers.` +identifiers. + +Please see the documentation for package testing in golang.org/pkg/testing +for the conventions that are enforced for Tests, Benchmarks, and Examples.` var Analyzer = &analysis.Analyzer{ Name: "tests", @@ -84,23 +87,25 @@ func isTestParam(typ ast.Expr, wantType string) bool { return false } -func lookup(pkg *types.Package, name string) types.Object { +func lookup(pkg *types.Package, name string) []types.Object { if o := pkg.Scope().Lookup(name); o != nil { - return o - } - - // If this package is ".../foo_test" and it imports a package - // ".../foo", try looking in the latter package. - // This heuristic should work even on build systems that do not - // record any special link between the packages. - if basePath := strings.TrimSuffix(pkg.Path(), "_test"); basePath != pkg.Path() { - for _, imp := range pkg.Imports() { - if imp.Path() == basePath { - return imp.Scope().Lookup(name) - } + return []types.Object{o} + } + + var ret []types.Object + // Search through the imports to see if any of them define name. + // It's hard to tell in general which package is being tested, so + // for the purposes of the analysis, allow the object to appear + // in any of the imports. This guarantees there are no false positives + // because the example needs to use the object so it must be defined + // in the package or one if its imports. On the other hand, false + // negatives are possible, but should be rare. + for _, imp := range pkg.Imports() { + if obj := imp.Scope().Lookup(name); obj != nil { + ret = append(ret, obj) } } - return nil + return ret } func checkExample(pass *analysis.Pass, fn *ast.FuncDecl) { @@ -121,9 +126,9 @@ func checkExample(pass *analysis.Pass, fn *ast.FuncDecl) { exName = strings.TrimPrefix(fnName, "Example") elems = strings.SplitN(exName, "_", 3) ident = elems[0] - obj = lookup(pass.Pkg, ident) + objs = lookup(pass.Pkg, ident) ) - if ident != "" && obj == nil { + if ident != "" && len(objs) == 0 { // Check ExampleFoo and ExampleBadFoo. pass.Reportf(fn.Pos(), "%s refers to unknown identifier: %s", fnName, ident) // Abort since obj is absent and no subsequent checks can be performed. @@ -145,7 +150,15 @@ func checkExample(pass *analysis.Pass, fn *ast.FuncDecl) { mmbr := elems[1] if !isExampleSuffix(mmbr) { // Check ExampleFoo_Method and ExampleFoo_BadMethod. - if obj, _, _ := types.LookupFieldOrMethod(obj.Type(), true, obj.Pkg(), mmbr); obj == nil { + found := false + // Check if Foo.Method exists in this package or its imports. + for _, obj := range objs { + if obj, _, _ := types.LookupFieldOrMethod(obj.Type(), true, obj.Pkg(), mmbr); obj != nil { + found = true + break + } + } + if !found { pass.Reportf(fn.Pos(), "%s refers to unknown field or method: %s.%s", fnName, ident, mmbr) } } diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go b/libgo/go/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go index 6cf4358ab9a..d019ecef15a 100644 --- a/libgo/go/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go +++ b/libgo/go/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go @@ -29,6 +29,13 @@ var Analyzer = &analysis.Analyzer{ } func run(pass *analysis.Pass) (interface{}, error) { + switch pass.Pkg.Path() { + case "encoding/gob", "encoding/json", "encoding/xml": + // These packages know how to use their own APIs. + // Sometimes they are testing what happens to incorrect programs. + return nil, nil + } + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) nodeFilter := []ast.Node{ diff --git a/libgo/go/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go b/libgo/go/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go index 5943c99e13b..b4fda19ecaa 100644 --- a/libgo/go/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go +++ b/libgo/go/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go @@ -329,6 +329,7 @@ func run(fset *token.FileSet, cfg *Config, analyzers []*analysis.Analyzer) ([]re OtherFiles: cfg.NonGoFiles, Pkg: pkg, TypesInfo: info, + TypesSizes: tc.Sizes, ResultOf: inputs, Report: func(d analysis.Diagnostic) { act.diagnostics = append(act.diagnostics, d) }, ImportObjectFact: facts.ImportObjectFact, diff --git a/libgo/go/golang.org/x/tools/go/ast/inspector/inspector.go b/libgo/go/golang.org/x/tools/go/ast/inspector/inspector.go index db88a951090..ddbdd3f08fc 100644 --- a/libgo/go/golang.org/x/tools/go/ast/inspector/inspector.go +++ b/libgo/go/golang.org/x/tools/go/ast/inspector/inspector.go @@ -14,7 +14,7 @@ // Experiments suggest the inspector's traversals are about 2.5x faster // than ast.Inspect, but it may take around 5 traversals for this // benefit to amortize the inspector's construction cost. -// If efficiency is the primary concern, do not use use Inspector for +// If efficiency is the primary concern, do not use Inspector for // one-off traversals. package inspector |