]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/vet: bring in sigchanyzer to report unbuffered channels to signal.Notify
authorEmmanuel T Odeke <emmanuel@orijtech.com>
Sun, 7 Mar 2021 07:57:33 +0000 (23:57 -0800)
committerEmmanuel Odeke <emmanuel@orijtech.com>
Mon, 8 Mar 2021 23:13:52 +0000 (23:13 +0000)
Brings in the static analyzer "sigchanyzer", that we created at
Orijtech, Inc, and already submitted in CL 274352, as

    golang.org/x/tools/go/analysis/passes/sigchanyzer

and add it to cmd/vet as one of the passes.

Fixes #9399

Change-Id: I83708b8ea5ca8ede5ee31efab55cbce7419434ab
Reviewed-on: https://go-review.googlesource.com/c/go/+/299532
Run-TryBot: Emmanuel Odeke <emmanuel@orijtech.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Trust: Bryan C. Mills <bcmills@google.com>

src/cmd/vendor/golang.org/x/tools/go/analysis/passes/sigchanyzer/sigchanyzer.go [new file with mode: 0644]
src/cmd/vendor/modules.txt
src/cmd/vet/main.go

diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/sigchanyzer/sigchanyzer.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/sigchanyzer/sigchanyzer.go
new file mode 100644 (file)
index 0000000..3d89061
--- /dev/null
@@ -0,0 +1,129 @@
+// Copyright 2020 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 sigchanyzer defines an Analyzer that detects
+// misuse of unbuffered signal as argument to signal.Notify.
+package sigchanyzer
+
+import (
+       "bytes"
+       "go/ast"
+       "go/format"
+       "go/token"
+       "go/types"
+
+       "golang.org/x/tools/go/analysis"
+       "golang.org/x/tools/go/analysis/passes/inspect"
+       "golang.org/x/tools/go/ast/inspector"
+)
+
+const Doc = `check for unbuffered channel of os.Signal
+
+This checker reports call expression of the form signal.Notify(c <-chan os.Signal, sig ...os.Signal),
+where c is an unbuffered channel, which can be at risk of missing the signal.`
+
+// Analyzer describes sigchanyzer analysis function detector.
+var Analyzer = &analysis.Analyzer{
+       Name:     "sigchanyzer",
+       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)
+               if !isSignalNotify(pass.TypesInfo, call) {
+                       return
+               }
+               var chanDecl *ast.CallExpr
+               switch arg := call.Args[0].(type) {
+               case *ast.Ident:
+                       if decl, ok := findDecl(arg).(*ast.CallExpr); ok {
+                               chanDecl = decl
+                       }
+               case *ast.CallExpr:
+                       chanDecl = arg
+               }
+               if chanDecl == nil || len(chanDecl.Args) != 1 {
+                       return
+               }
+               chanDecl.Args = append(chanDecl.Args, &ast.BasicLit{
+                       Kind:  token.INT,
+                       Value: "1",
+               })
+               var buf bytes.Buffer
+               if err := format.Node(&buf, token.NewFileSet(), chanDecl); err != nil {
+                       return
+               }
+               pass.Report(analysis.Diagnostic{
+                       Pos:     call.Pos(),
+                       End:     call.End(),
+                       Message: "misuse of unbuffered os.Signal channel as argument to signal.Notify",
+                       SuggestedFixes: []analysis.SuggestedFix{{
+                               Message: "Change to buffer channel",
+                               TextEdits: []analysis.TextEdit{{
+                                       Pos:     chanDecl.Pos(),
+                                       End:     chanDecl.End(),
+                                       NewText: buf.Bytes(),
+                               }},
+                       }},
+               })
+       })
+       return nil, nil
+}
+
+func isSignalNotify(info *types.Info, call *ast.CallExpr) bool {
+       check := func(id *ast.Ident) bool {
+               obj := info.ObjectOf(id)
+               return obj.Name() == "Notify" && obj.Pkg().Path() == "os/signal"
+       }
+       switch fun := call.Fun.(type) {
+       case *ast.SelectorExpr:
+               return check(fun.Sel)
+       case *ast.Ident:
+               if fun, ok := findDecl(fun).(*ast.SelectorExpr); ok {
+                       return check(fun.Sel)
+               }
+               return false
+       default:
+               return false
+       }
+}
+
+func findDecl(arg *ast.Ident) ast.Node {
+       if arg.Obj == nil {
+               return nil
+       }
+       switch as := arg.Obj.Decl.(type) {
+       case *ast.AssignStmt:
+               if len(as.Lhs) != len(as.Rhs) {
+                       return nil
+               }
+               for i, lhs := range as.Lhs {
+                       lid, ok := lhs.(*ast.Ident)
+                       if !ok {
+                               continue
+                       }
+                       if lid.Obj == arg.Obj {
+                               return as.Rhs[i]
+                       }
+               }
+       case *ast.ValueSpec:
+               if len(as.Names) != len(as.Values) {
+                       return nil
+               }
+               for i, name := range as.Names {
+                       if name.Obj == arg.Obj {
+                               return as.Values[i]
+                       }
+               }
+       }
+       return nil
+}
index e4dfd32315ba6318c6b90abd63c045f479017952..b1a2c67581c3f1bc52137af323d92cd2ee013dde 100644 (file)
@@ -69,6 +69,7 @@ golang.org/x/tools/go/analysis/passes/lostcancel
 golang.org/x/tools/go/analysis/passes/nilfunc
 golang.org/x/tools/go/analysis/passes/printf
 golang.org/x/tools/go/analysis/passes/shift
+golang.org/x/tools/go/analysis/passes/sigchanyzer
 golang.org/x/tools/go/analysis/passes/stdmethods
 golang.org/x/tools/go/analysis/passes/stringintconv
 golang.org/x/tools/go/analysis/passes/structtag
index d50c45d691ee82fa64bb8cf110f68ed3f19c2380..a33bba2466611809093f57dc00b272733d4c3172 100644 (file)
@@ -22,6 +22,7 @@ import (
        "golang.org/x/tools/go/analysis/passes/nilfunc"
        "golang.org/x/tools/go/analysis/passes/printf"
        "golang.org/x/tools/go/analysis/passes/shift"
+       "golang.org/x/tools/go/analysis/passes/sigchanyzer"
        "golang.org/x/tools/go/analysis/passes/stdmethods"
        "golang.org/x/tools/go/analysis/passes/stringintconv"
        "golang.org/x/tools/go/analysis/passes/structtag"
@@ -54,6 +55,7 @@ func main() {
                nilfunc.Analyzer,
                printf.Analyzer,
                shift.Analyzer,
+               sigchanyzer.Analyzer,
                stdmethods.Analyzer,
                stringintconv.Analyzer,
                structtag.Analyzer,