src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

sa1016.go (3328B)


      1 package sa1016
      2 
      3 import (
      4 	"fmt"
      5 	"go/ast"
      6 
      7 	"honnef.co/go/tools/analysis/code"
      8 	"honnef.co/go/tools/analysis/edit"
      9 	"honnef.co/go/tools/analysis/lint"
     10 	"honnef.co/go/tools/analysis/report"
     11 	"honnef.co/go/tools/pattern"
     12 
     13 	"golang.org/x/tools/go/analysis"
     14 )
     15 
     16 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
     17 	Analyzer: &analysis.Analyzer{
     18 		Name:     "SA1016",
     19 		Run:      run,
     20 		Requires: code.RequiredAnalyzers,
     21 	},
     22 	Doc: &lint.RawDocumentation{
     23 		Title: `Trapping a signal that cannot be trapped`,
     24 		Text: `Not all signals can be intercepted by a process. Specifically, on
     25 UNIX-like systems, the \'syscall.SIGKILL\' and \'syscall.SIGSTOP\' signals are
     26 never passed to the process, but instead handled directly by the
     27 kernel. It is therefore pointless to try and handle these signals.`,
     28 		Since:    "2017.1",
     29 		Severity: lint.SeverityWarning,
     30 		MergeIf:  lint.MergeIfAny,
     31 	},
     32 })
     33 
     34 var Analyzer = SCAnalyzer.Analyzer
     35 
     36 var query = pattern.MustParse(`
     37 	(CallExpr
     38 		(Symbol
     39 			(Or
     40 				"os/signal.Ignore"
     41 				"os/signal.Notify"
     42 				"os/signal.Reset"))
     43 		_)`)
     44 
     45 func run(pass *analysis.Pass) (any, error) {
     46 	isSignal := func(pass *analysis.Pass, expr ast.Expr, name string) bool {
     47 		if expr, ok := expr.(*ast.SelectorExpr); ok {
     48 			return code.SelectorName(pass, expr) == name
     49 		} else {
     50 			return false
     51 		}
     52 	}
     53 
     54 	for node := range code.Matches(pass, query) {
     55 		call := node.(*ast.CallExpr)
     56 		hasSigterm := false
     57 		for _, arg := range call.Args {
     58 			if conv, ok := arg.(*ast.CallExpr); ok && isSignal(pass, conv.Fun, "os.Signal") {
     59 				arg = conv.Args[0]
     60 			}
     61 
     62 			if isSignal(pass, arg, "syscall.SIGTERM") {
     63 				hasSigterm = true
     64 				break
     65 			}
     66 
     67 		}
     68 		for i, arg := range call.Args {
     69 			if conv, ok := arg.(*ast.CallExpr); ok && isSignal(pass, conv.Fun, "os.Signal") {
     70 				arg = conv.Args[0]
     71 			}
     72 
     73 			if isSignal(pass, arg, "os.Kill") || isSignal(pass, arg, "syscall.SIGKILL") {
     74 				var fixes []analysis.SuggestedFix
     75 				if !hasSigterm {
     76 					nargs := make([]ast.Expr, len(call.Args))
     77 					for j, a := range call.Args {
     78 						if i == j {
     79 							nargs[j] = edit.Selector("syscall", "SIGTERM")
     80 						} else {
     81 							nargs[j] = a
     82 						}
     83 					}
     84 					ncall := *call
     85 					ncall.Args = nargs
     86 					fixes = append(fixes, edit.Fix(fmt.Sprintf("Use syscall.SIGTERM instead of %s", report.Render(pass, arg)), edit.ReplaceWithNode(pass.Fset, call, &ncall)))
     87 				}
     88 				nargs := make([]ast.Expr, 0, len(call.Args))
     89 				for j, a := range call.Args {
     90 					if i == j {
     91 						continue
     92 					}
     93 					nargs = append(nargs, a)
     94 				}
     95 				ncall := *call
     96 				ncall.Args = nargs
     97 				fixes = append(fixes, edit.Fix(fmt.Sprintf("Remove %s from list of arguments", report.Render(pass, arg)), edit.ReplaceWithNode(pass.Fset, call, &ncall)))
     98 				report.Report(pass, arg, fmt.Sprintf("%s cannot be trapped (did you mean syscall.SIGTERM?)", report.Render(pass, arg)), report.Fixes(fixes...))
     99 			}
    100 			if isSignal(pass, arg, "syscall.SIGSTOP") {
    101 				nargs := make([]ast.Expr, 0, len(call.Args)-1)
    102 				for j, a := range call.Args {
    103 					if i == j {
    104 						continue
    105 					}
    106 					nargs = append(nargs, a)
    107 				}
    108 				ncall := *call
    109 				ncall.Args = nargs
    110 				report.Report(pass, arg, "syscall.SIGSTOP cannot be trapped", report.Fixes(edit.Fix("Remove syscall.SIGSTOP from list of arguments", edit.ReplaceWithNode(pass.Fset, call, &ncall))))
    111 			}
    112 		}
    113 	}
    114 	return nil, nil
    115 }