From 7e09842e2b8d007d9ea30883b822fa61e542c01a Mon Sep 17 00:00:00 2001 From: Aleksandr Razumov Date: Sun, 29 Sep 2019 00:56:21 +0300 Subject: [PATCH] govet: add more analyzers --- pkg/golinters/govet.go | 16 ++ .../passes/deepequalerrors/deepequalerrors.go | 115 ++++++++ .../go/analysis/passes/findcall/findcall.go | 80 ++++++ .../go/analysis/passes/nilness/nilness.go | 271 ++++++++++++++++++ .../go/analysis/passes/pkgfact/pkgfact.go | 127 ++++++++ .../go/analysis/passes/sortslice/analyzer.go | 123 ++++++++ vendor/modules.txt | 5 + 7 files changed, 737 insertions(+) create mode 100644 vendor/golang.org/x/tools/go/analysis/passes/deepequalerrors/deepequalerrors.go create mode 100644 vendor/golang.org/x/tools/go/analysis/passes/findcall/findcall.go create mode 100644 vendor/golang.org/x/tools/go/analysis/passes/nilness/nilness.go create mode 100644 vendor/golang.org/x/tools/go/analysis/passes/pkgfact/pkgfact.go create mode 100644 vendor/golang.org/x/tools/go/analysis/passes/sortslice/analyzer.go diff --git a/pkg/golinters/govet.go b/pkg/golinters/govet.go index b3b7b61a..a24af46d 100644 --- a/pkg/golinters/govet.go +++ b/pkg/golinters/govet.go @@ -13,18 +13,26 @@ import ( "golang.org/x/tools/go/analysis/passes/atomic" "golang.org/x/tools/go/analysis/passes/atomicalign" "golang.org/x/tools/go/analysis/passes/bools" + "golang.org/x/tools/go/analysis/passes/buildssa" "golang.org/x/tools/go/analysis/passes/buildtag" "golang.org/x/tools/go/analysis/passes/cgocall" "golang.org/x/tools/go/analysis/passes/composite" "golang.org/x/tools/go/analysis/passes/copylock" + "golang.org/x/tools/go/analysis/passes/ctrlflow" + "golang.org/x/tools/go/analysis/passes/deepequalerrors" "golang.org/x/tools/go/analysis/passes/errorsas" + "golang.org/x/tools/go/analysis/passes/findcall" "golang.org/x/tools/go/analysis/passes/httpresponse" + "golang.org/x/tools/go/analysis/passes/inspect" "golang.org/x/tools/go/analysis/passes/loopclosure" "golang.org/x/tools/go/analysis/passes/lostcancel" "golang.org/x/tools/go/analysis/passes/nilfunc" + "golang.org/x/tools/go/analysis/passes/nilness" + "golang.org/x/tools/go/analysis/passes/pkgfact" "golang.org/x/tools/go/analysis/passes/printf" "golang.org/x/tools/go/analysis/passes/shadow" "golang.org/x/tools/go/analysis/passes/shift" + "golang.org/x/tools/go/analysis/passes/sortslice" "golang.org/x/tools/go/analysis/passes/stdmethods" "golang.org/x/tools/go/analysis/passes/structtag" "golang.org/x/tools/go/analysis/passes/tests" @@ -41,18 +49,26 @@ func getAllAnalyzers() []*analysis.Analyzer { atomic.Analyzer, atomicalign.Analyzer, bools.Analyzer, + buildssa.Analyzer, buildtag.Analyzer, cgocall.Analyzer, composite.Analyzer, copylock.Analyzer, + ctrlflow.Analyzer, + deepequalerrors.Analyzer, errorsas.Analyzer, + findcall.Analyzer, httpresponse.Analyzer, + inspect.Analyzer, loopclosure.Analyzer, lostcancel.Analyzer, nilfunc.Analyzer, + nilness.Analyzer, + pkgfact.Analyzer, printf.Analyzer, shadow.Analyzer, shift.Analyzer, + sortslice.Analyzer, stdmethods.Analyzer, structtag.Analyzer, tests.Analyzer, diff --git a/vendor/golang.org/x/tools/go/analysis/passes/deepequalerrors/deepequalerrors.go b/vendor/golang.org/x/tools/go/analysis/passes/deepequalerrors/deepequalerrors.go new file mode 100644 index 00000000..067e91a2 --- /dev/null +++ b/vendor/golang.org/x/tools/go/analysis/passes/deepequalerrors/deepequalerrors.go @@ -0,0 +1,115 @@ +// Copyright 2019 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 deepequalerrors defines an Analyzer that checks for the use +// of reflect.DeepEqual with error values. +package deepequalerrors + +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 = `check for calls of reflect.DeepEqual on error values + +The deepequalerrors checker looks for calls of the form: + + reflect.DeepEqual(err1, err2) + +where err1 and err2 are errors. Using reflect.DeepEqual to compare +errors is discouraged.` + +var Analyzer = &analysis.Analyzer{ + Name: "deepequalerrors", + Doc: Doc, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: run, +} + +func run(pass *analysis.Pass) (interface{}, error) { + 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, ok := typeutil.Callee(pass.TypesInfo, call).(*types.Func) + if !ok { + return + } + if fn.FullName() == "reflect.DeepEqual" && hasError(pass, call.Args[0]) && hasError(pass, call.Args[1]) { + pass.Reportf(call.Pos(), "avoid using reflect.DeepEqual with errors") + } + }) + return nil, nil +} + +var errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface) + +// hasError reports whether the type of e contains the type error. +// See containsError, below, for the meaning of "contains". +func hasError(pass *analysis.Pass, e ast.Expr) bool { + tv, ok := pass.TypesInfo.Types[e] + if !ok { // no type info, assume good + return false + } + return containsError(tv.Type) +} + +// Report whether any type that typ could store and that could be compared is the +// error type. This includes typ itself, as well as the types of struct field, slice +// and array elements, map keys and elements, and pointers. It does not include +// channel types (incomparable), arg and result types of a Signature (not stored), or +// methods of a named or interface type (not stored). +func containsError(typ types.Type) bool { + // Track types being processed, to avoid infinite recursion. + // Using types as keys here is OK because we are checking for the identical pointer, not + // type identity. See analysis/passes/printf/types.go. + inProgress := make(map[types.Type]bool) + + var check func(t types.Type) bool + check = func(t types.Type) bool { + if t == errorType { + return true + } + if inProgress[t] { + return false + } + inProgress[t] = true + switch t := t.(type) { + case *types.Pointer: + return check(t.Elem()) + case *types.Slice: + return check(t.Elem()) + case *types.Array: + return check(t.Elem()) + case *types.Map: + return check(t.Key()) || check(t.Elem()) + case *types.Struct: + for i := 0; i < t.NumFields(); i++ { + if check(t.Field(i).Type()) { + return true + } + } + case *types.Named: + return check(t.Underlying()) + + // We list the remaining valid type kinds for completeness. + case *types.Basic: + case *types.Chan: // channels store values, but they are not comparable + case *types.Signature: + case *types.Tuple: // tuples are only part of signatures + case *types.Interface: + } + return false + } + + return check(typ) +} diff --git a/vendor/golang.org/x/tools/go/analysis/passes/findcall/findcall.go b/vendor/golang.org/x/tools/go/analysis/passes/findcall/findcall.go new file mode 100644 index 00000000..2a4e3a47 --- /dev/null +++ b/vendor/golang.org/x/tools/go/analysis/passes/findcall/findcall.go @@ -0,0 +1,80 @@ +// 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 findcall package defines an Analyzer that serves as a trivial +// example and test of the Analysis API. It reports a diagnostic for +// every call to a function or method of the name specified by its +// -name flag. +package findcall + +import ( + "go/ast" + "go/types" + + "golang.org/x/tools/go/analysis" +) + +const Doc = `find calls to a particular function + +The findcall analysis reports calls to functions or methods +of a particular name.` + +var Analyzer = &analysis.Analyzer{ + Name: "findcall", + Doc: Doc, + Run: run, + RunDespiteErrors: true, + FactTypes: []analysis.Fact{new(foundFact)}, +} + +var name string // -name flag + +func init() { + Analyzer.Flags.StringVar(&name, "name", name, "name of the function to find") +} + +func run(pass *analysis.Pass) (interface{}, error) { + for _, f := range pass.Files { + ast.Inspect(f, func(n ast.Node) bool { + if call, ok := n.(*ast.CallExpr); ok { + var id *ast.Ident + switch fun := call.Fun.(type) { + case *ast.Ident: + id = fun + case *ast.SelectorExpr: + id = fun.Sel + } + if id != nil && !pass.TypesInfo.Types[id].IsType() && id.Name == name { + pass.Reportf(call.Lparen, "call of %s(...)", id.Name) + } + } + return true + }) + } + + // Export a fact for each matching function. + // + // These facts are produced only to test the testing + // infrastructure in the analysistest package. + // They are not consumed by the findcall Analyzer + // itself, as would happen in a more realistic example. + for _, f := range pass.Files { + for _, decl := range f.Decls { + if decl, ok := decl.(*ast.FuncDecl); ok && decl.Name.Name == name { + if obj, ok := pass.TypesInfo.Defs[decl.Name].(*types.Func); ok { + pass.ExportObjectFact(obj, new(foundFact)) + } + } + } + } + + return nil, nil +} + +// foundFact is a fact associated with functions that match -name. +// We use it to exercise the fact machinery in tests. +type foundFact struct{} + +func (*foundFact) String() string { return "found" } +func (*foundFact) AFact() {} diff --git a/vendor/golang.org/x/tools/go/analysis/passes/nilness/nilness.go b/vendor/golang.org/x/tools/go/analysis/passes/nilness/nilness.go new file mode 100644 index 00000000..5fe3f267 --- /dev/null +++ b/vendor/golang.org/x/tools/go/analysis/passes/nilness/nilness.go @@ -0,0 +1,271 @@ +// 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. + +// Package nilness inspects the control-flow graph of an SSA function +// and reports errors such as nil pointer dereferences and degenerate +// nil pointer comparisons. +package nilness + +import ( + "fmt" + "go/token" + "go/types" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/buildssa" + "golang.org/x/tools/go/ssa" +) + +const Doc = `check for redundant or impossible nil comparisons + +The nilness checker inspects the control-flow graph of each function in +a package and reports nil pointer dereferences and degenerate nil +pointers. A degenerate comparison is of the form x==nil or x!=nil where x +is statically known to be nil or non-nil. These are often a mistake, +especially in control flow related to errors. + +This check reports conditions such as: + + if f == nil { // impossible condition (f is a function) + } + +and: + + p := &v + ... + if p != nil { // tautological condition + } + +and: + + if p == nil { + print(*p) // nil dereference + } +` + +var Analyzer = &analysis.Analyzer{ + Name: "nilness", + Doc: Doc, + Run: run, + Requires: []*analysis.Analyzer{buildssa.Analyzer}, +} + +func run(pass *analysis.Pass) (interface{}, error) { + ssainput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) + for _, fn := range ssainput.SrcFuncs { + runFunc(pass, fn) + } + return nil, nil +} + +func runFunc(pass *analysis.Pass, fn *ssa.Function) { + reportf := func(category string, pos token.Pos, format string, args ...interface{}) { + pass.Report(analysis.Diagnostic{ + Pos: pos, + Category: category, + Message: fmt.Sprintf(format, args...), + }) + } + + // notNil reports an error if v is provably nil. + notNil := func(stack []fact, instr ssa.Instruction, v ssa.Value, descr string) { + if nilnessOf(stack, v) == isnil { + reportf("nilderef", instr.Pos(), "nil dereference in "+descr) + } + } + + // visit visits reachable blocks of the CFG in dominance order, + // maintaining a stack of dominating nilness facts. + // + // By traversing the dom tree, we can pop facts off the stack as + // soon as we've visited a subtree. Had we traversed the CFG, + // we would need to retain the set of facts for each block. + seen := make([]bool, len(fn.Blocks)) // seen[i] means visit should ignore block i + var visit func(b *ssa.BasicBlock, stack []fact) + visit = func(b *ssa.BasicBlock, stack []fact) { + if seen[b.Index] { + return + } + seen[b.Index] = true + + // Report nil dereferences. + for _, instr := range b.Instrs { + switch instr := instr.(type) { + case ssa.CallInstruction: + notNil(stack, instr, instr.Common().Value, + instr.Common().Description()) + case *ssa.FieldAddr: + notNil(stack, instr, instr.X, "field selection") + case *ssa.IndexAddr: + notNil(stack, instr, instr.X, "index operation") + case *ssa.MapUpdate: + notNil(stack, instr, instr.Map, "map update") + case *ssa.Slice: + // A nilcheck occurs in ptr[:] iff ptr is a pointer to an array. + if _, ok := instr.X.Type().Underlying().(*types.Pointer); ok { + notNil(stack, instr, instr.X, "slice operation") + } + case *ssa.Store: + notNil(stack, instr, instr.Addr, "store") + case *ssa.TypeAssert: + notNil(stack, instr, instr.X, "type assertion") + case *ssa.UnOp: + if instr.Op == token.MUL { // *X + notNil(stack, instr, instr.X, "load") + } + } + } + + // For nil comparison blocks, report an error if the condition + // is degenerate, and push a nilness fact on the stack when + // visiting its true and false successor blocks. + if binop, tsucc, fsucc := eq(b); binop != nil { + xnil := nilnessOf(stack, binop.X) + ynil := nilnessOf(stack, binop.Y) + + if ynil != unknown && xnil != unknown && (xnil == isnil || ynil == isnil) { + // Degenerate condition: + // the nilness of both operands is known, + // and at least one of them is nil. + var adj string + if (xnil == ynil) == (binop.Op == token.EQL) { + adj = "tautological" + } else { + adj = "impossible" + } + reportf("cond", binop.Pos(), "%s condition: %s %s %s", adj, xnil, binop.Op, ynil) + + // If tsucc's or fsucc's sole incoming edge is impossible, + // it is unreachable. Prune traversal of it and + // all the blocks it dominates. + // (We could be more precise with full dataflow + // analysis of control-flow joins.) + var skip *ssa.BasicBlock + if xnil == ynil { + skip = fsucc + } else { + skip = tsucc + } + for _, d := range b.Dominees() { + if d == skip && len(d.Preds) == 1 { + continue + } + visit(d, stack) + } + return + } + + // "if x == nil" or "if nil == y" condition; x, y are unknown. + if xnil == isnil || ynil == isnil { + var f fact + if xnil == isnil { + // x is nil, y is unknown: + // t successor learns y is nil. + f = fact{binop.Y, isnil} + } else { + // x is nil, y is unknown: + // t successor learns x is nil. + f = fact{binop.X, isnil} + } + + for _, d := range b.Dominees() { + // Successor blocks learn a fact + // only at non-critical edges. + // (We could do be more precise with full dataflow + // analysis of control-flow joins.) + s := stack + if len(d.Preds) == 1 { + if d == tsucc { + s = append(s, f) + } else if d == fsucc { + s = append(s, f.negate()) + } + } + visit(d, s) + } + return + } + } + + for _, d := range b.Dominees() { + visit(d, stack) + } + } + + // Visit the entry block. No need to visit fn.Recover. + if fn.Blocks != nil { + visit(fn.Blocks[0], make([]fact, 0, 20)) // 20 is plenty + } +} + +// A fact records that a block is dominated +// by the condition v == nil or v != nil. +type fact struct { + value ssa.Value + nilness nilness +} + +func (f fact) negate() fact { return fact{f.value, -f.nilness} } + +type nilness int + +const ( + isnonnil = -1 + unknown nilness = 0 + isnil = 1 +) + +var nilnessStrings = []string{"non-nil", "unknown", "nil"} + +func (n nilness) String() string { return nilnessStrings[n+1] } + +// nilnessOf reports whether v is definitely nil, definitely not nil, +// or unknown given the dominating stack of facts. +func nilnessOf(stack []fact, v ssa.Value) nilness { + // Is value intrinsically nil or non-nil? + switch v := v.(type) { + case *ssa.Alloc, + *ssa.FieldAddr, + *ssa.FreeVar, + *ssa.Function, + *ssa.Global, + *ssa.IndexAddr, + *ssa.MakeChan, + *ssa.MakeClosure, + *ssa.MakeInterface, + *ssa.MakeMap, + *ssa.MakeSlice: + return isnonnil + case *ssa.Const: + if v.IsNil() { + return isnil + } else { + return isnonnil + } + } + + // Search dominating control-flow facts. + for _, f := range stack { + if f.value == v { + return f.nilness + } + } + return unknown +} + +// If b ends with an equality comparison, eq returns the operation and +// its true (equal) and false (not equal) successors. +func eq(b *ssa.BasicBlock) (op *ssa.BinOp, tsucc, fsucc *ssa.BasicBlock) { + if If, ok := b.Instrs[len(b.Instrs)-1].(*ssa.If); ok { + if binop, ok := If.Cond.(*ssa.BinOp); ok { + switch binop.Op { + case token.EQL: + return binop, b.Succs[0], b.Succs[1] + case token.NEQ: + return binop, b.Succs[1], b.Succs[0] + } + } + } + return nil, nil, nil +} diff --git a/vendor/golang.org/x/tools/go/analysis/passes/pkgfact/pkgfact.go b/vendor/golang.org/x/tools/go/analysis/passes/pkgfact/pkgfact.go new file mode 100644 index 00000000..e0530867 --- /dev/null +++ b/vendor/golang.org/x/tools/go/analysis/passes/pkgfact/pkgfact.go @@ -0,0 +1,127 @@ +// 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 pkgfact package is a demonstration and test of the package fact +// mechanism. +// +// The output of the pkgfact analysis is a set of key/values pairs +// gathered from the analyzed package and its imported dependencies. +// Each key/value pair comes from a top-level constant declaration +// whose name starts and ends with "_". For example: +// +// package p +// +// const _greeting_ = "hello" +// const _audience_ = "world" +// +// the pkgfact analysis output for package p would be: +// +// {"greeting": "hello", "audience": "world"}. +// +// In addition, the analysis reports a diagnostic at each import +// showing which key/value pairs it contributes. +package pkgfact + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + "reflect" + "sort" + "strings" + + "golang.org/x/tools/go/analysis" +) + +var Analyzer = &analysis.Analyzer{ + Name: "pkgfact", + Doc: "gather name/value pairs from constant declarations", + Run: run, + FactTypes: []analysis.Fact{new(pairsFact)}, + ResultType: reflect.TypeOf(map[string]string{}), +} + +// A pairsFact is a package-level fact that records +// an set of key=value strings accumulated from constant +// declarations in this package and its dependencies. +// Elements are ordered by keys, which are unique. +type pairsFact []string + +func (f *pairsFact) AFact() {} +func (f *pairsFact) String() string { return "pairs(" + strings.Join(*f, ", ") + ")" } + +func run(pass *analysis.Pass) (interface{}, error) { + result := make(map[string]string) + + // At each import, print the fact from the imported + // package and accumulate its information into the result. + // (Warning: accumulation leads to quadratic growth of work.) + doImport := func(spec *ast.ImportSpec) { + pkg := imported(pass.TypesInfo, spec) + var fact pairsFact + if pass.ImportPackageFact(pkg, &fact) { + for _, pair := range fact { + eq := strings.IndexByte(pair, '=') + result[pair[:eq]] = pair[1+eq:] + } + pass.Reportf(spec.Pos(), "%s", strings.Join(fact, " ")) + } + } + + // At each "const _name_ = value", add a fact into env. + doConst := func(spec *ast.ValueSpec) { + if len(spec.Names) == len(spec.Values) { + for i := range spec.Names { + name := spec.Names[i].Name + if strings.HasPrefix(name, "_") && strings.HasSuffix(name, "_") { + + if key := strings.Trim(name, "_"); key != "" { + value := pass.TypesInfo.Types[spec.Values[i]].Value.String() + result[key] = value + } + } + } + } + } + + for _, f := range pass.Files { + for _, decl := range f.Decls { + if decl, ok := decl.(*ast.GenDecl); ok { + for _, spec := range decl.Specs { + switch decl.Tok { + case token.IMPORT: + doImport(spec.(*ast.ImportSpec)) + case token.CONST: + doConst(spec.(*ast.ValueSpec)) + } + } + } + } + } + + // Sort/deduplicate the result and save it as a package fact. + keys := make([]string, 0, len(result)) + for key := range result { + keys = append(keys, key) + } + sort.Strings(keys) + var fact pairsFact + for _, key := range keys { + fact = append(fact, fmt.Sprintf("%s=%s", key, result[key])) + } + if len(fact) > 0 { + pass.ExportPackageFact(&fact) + } + + return result, nil +} + +func imported(info *types.Info, spec *ast.ImportSpec) *types.Package { + obj, ok := info.Implicits[spec] + if !ok { + obj = info.Defs[spec.Name] // renaming import + } + return obj.(*types.PkgName).Imported() +} diff --git a/vendor/golang.org/x/tools/go/analysis/passes/sortslice/analyzer.go b/vendor/golang.org/x/tools/go/analysis/passes/sortslice/analyzer.go new file mode 100644 index 00000000..69a67939 --- /dev/null +++ b/vendor/golang.org/x/tools/go/analysis/passes/sortslice/analyzer.go @@ -0,0 +1,123 @@ +// Copyright 2019 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 sortslice defines an Analyzer that checks for calls +// to sort.Slice that do not use a slice type as first argument. +package sortslice + +import ( + "bytes" + "fmt" + "go/ast" + "go/format" + "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 = `check the argument type of sort.Slice + +sort.Slice requires an argument of a slice type. Check that +the interface{} value passed to sort.Slice is actually a slice.` + +var Analyzer = &analysis.Analyzer{ + Name: "sortslice", + Doc: Doc, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: run, +} + +func run(pass *analysis.Pass) (interface{}, error) { + 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.Callee(pass.TypesInfo, call).(*types.Func) + if fn == nil { + return + } + + if fn.FullName() != "sort.Slice" { + return + } + + arg := call.Args[0] + typ := pass.TypesInfo.Types[arg].Type + switch typ.Underlying().(type) { + case *types.Slice, *types.Interface: + return + } + + var fixes []analysis.SuggestedFix + switch v := typ.Underlying().(type) { + case *types.Array: + var buf bytes.Buffer + format.Node(&buf, pass.Fset, &ast.SliceExpr{ + X: arg, + Slice3: false, + Lbrack: arg.End() + 1, + Rbrack: arg.End() + 3, + }) + fixes = append(fixes, analysis.SuggestedFix{ + Message: "Get a slice of the full array", + TextEdits: []analysis.TextEdit{{ + Pos: arg.Pos(), + End: arg.End(), + NewText: buf.Bytes(), + }}, + }) + case *types.Pointer: + _, ok := v.Elem().Underlying().(*types.Slice) + if !ok { + break + } + var buf bytes.Buffer + format.Node(&buf, pass.Fset, &ast.StarExpr{ + X: arg, + }) + fixes = append(fixes, analysis.SuggestedFix{ + Message: "Dereference the pointer to the slice", + TextEdits: []analysis.TextEdit{{ + Pos: arg.Pos(), + End: arg.End(), + NewText: buf.Bytes(), + }}, + }) + case *types.Signature: + if v.Params().Len() != 0 || v.Results().Len() != 1 { + break + } + if _, ok := v.Results().At(0).Type().Underlying().(*types.Slice); !ok { + break + } + var buf bytes.Buffer + format.Node(&buf, pass.Fset, &ast.CallExpr{ + Fun: arg, + }) + fixes = append(fixes, analysis.SuggestedFix{ + Message: "Call the function", + TextEdits: []analysis.TextEdit{{ + Pos: arg.Pos(), + End: arg.End(), + NewText: buf.Bytes(), + }}, + }) + } + + pass.Report(analysis.Diagnostic{ + Pos: call.Pos(), + End: call.End(), + Message: fmt.Sprintf("sort.Slice's argument must be a slice; is called with %s", typ.String()), + SuggestedFixes: fixes, + }) + }) + return nil, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index d1986fbd..12d9439f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -194,16 +194,21 @@ golang.org/x/tools/go/analysis/passes/cgocall golang.org/x/tools/go/analysis/passes/composite golang.org/x/tools/go/analysis/passes/copylock golang.org/x/tools/go/analysis/passes/ctrlflow +golang.org/x/tools/go/analysis/passes/deepequalerrors golang.org/x/tools/go/analysis/passes/errorsas +golang.org/x/tools/go/analysis/passes/findcall golang.org/x/tools/go/analysis/passes/httpresponse golang.org/x/tools/go/analysis/passes/inspect golang.org/x/tools/go/analysis/passes/internal/analysisutil golang.org/x/tools/go/analysis/passes/loopclosure golang.org/x/tools/go/analysis/passes/lostcancel golang.org/x/tools/go/analysis/passes/nilfunc +golang.org/x/tools/go/analysis/passes/nilness +golang.org/x/tools/go/analysis/passes/pkgfact golang.org/x/tools/go/analysis/passes/printf golang.org/x/tools/go/analysis/passes/shadow golang.org/x/tools/go/analysis/passes/shift +golang.org/x/tools/go/analysis/passes/sortslice golang.org/x/tools/go/analysis/passes/stdmethods golang.org/x/tools/go/analysis/passes/structtag golang.org/x/tools/go/analysis/passes/tests