sa1012.go raw
1 package sa1012
2
3 import (
4 "go/ast"
5 "go/types"
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/go/types/typeutil"
12 "honnef.co/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.RawDocumentation{
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.IsTypeWithName(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 }
73