github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa1012/sa1012.go (about) 1 package sa1012 2 3 import ( 4 "go/ast" 5 "go/types" 6 7 "github.com/amarpal/go-tools/analysis/code" 8 "github.com/amarpal/go-tools/analysis/edit" 9 "github.com/amarpal/go-tools/analysis/lint" 10 "github.com/amarpal/go-tools/analysis/report" 11 "github.com/amarpal/go-tools/go/types/typeutil" 12 "github.com/amarpal/go-tools/pattern" 13 14 "golang.org/x/tools/go/analysis" 15 "golang.org/x/tools/go/analysis/passes/inspect" 16 ) 17 18 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 19 Analyzer: &analysis.Analyzer{ 20 Name: "SA1012", 21 Run: run, 22 Requires: []*analysis.Analyzer{inspect.Analyzer}, 23 }, 24 Doc: &lint.Documentation{ 25 Title: `A nil \'context.Context\' is being passed to a function, consider using \'context.TODO\' instead`, 26 Since: "2017.1", 27 Severity: lint.SeverityWarning, 28 MergeIf: lint.MergeIfAny, 29 }, 30 }) 31 32 var Analyzer = SCAnalyzer.Analyzer 33 34 var checkNilContextQ = pattern.MustParse(`(CallExpr fun@(Symbol _) (Builtin "nil"):_)`) 35 36 func run(pass *analysis.Pass) (interface{}, error) { 37 todo := &ast.CallExpr{ 38 Fun: edit.Selector("context", "TODO"), 39 } 40 bg := &ast.CallExpr{ 41 Fun: edit.Selector("context", "Background"), 42 } 43 fn := func(node ast.Node) { 44 m, ok := code.Match(pass, checkNilContextQ, node) 45 if !ok { 46 return 47 } 48 49 call := node.(*ast.CallExpr) 50 fun, ok := m.State["fun"].(*types.Func) 51 if !ok { 52 // it might also be a builtin 53 return 54 } 55 sig := fun.Type().(*types.Signature) 56 if sig.Params().Len() == 0 { 57 // Our CallExpr might've matched a method expression, like 58 // (*T).Foo(nil) – here, nil isn't the first argument of 59 // the Foo method, but the method receiver. 60 return 61 } 62 if !typeutil.IsType(sig.Params().At(0).Type(), "context.Context") { 63 return 64 } 65 report.Report(pass, call.Args[0], 66 "do not pass a nil Context, even if a function permits it; pass context.TODO if you are unsure about which Context to use", report.Fixes( 67 edit.Fix("use context.TODO", edit.ReplaceWithNode(pass.Fset, call.Args[0], todo)), 68 edit.Fix("use context.Background", edit.ReplaceWithNode(pass.Fset, call.Args[0], bg)))) 69 } 70 code.Preorder(pass, fn, (*ast.CallExpr)(nil)) 71 return nil, nil 72 }