1 package sa1029
2 3 import (
4 "fmt"
5 "go/types"
6 7 "honnef.co/go/tools/analysis/callcheck"
8 "honnef.co/go/tools/analysis/lint"
9 "honnef.co/go/tools/internal/passes/buildir"
10 11 "golang.org/x/tools/go/analysis"
12 )
13 14 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
15 Analyzer: &analysis.Analyzer{
16 Name: "SA1029",
17 Requires: []*analysis.Analyzer{buildir.Analyzer},
18 Run: callcheck.Analyzer(checkWithValueKeyRules),
19 },
20 Doc: &lint.RawDocumentation{
21 Title: `Inappropriate key in call to \'context.WithValue\'`,
22 Text: `The provided key must be comparable and should not be
23 of type \'string\' or any other built-in type to avoid collisions between
24 packages using context. Users of \'WithValue\' should define their own
25 types for keys.
26 27 To avoid allocating when assigning to an \'interface{}\',
28 context keys often have concrete type \'struct{}\'. Alternatively,
29 exported context key variables' static type should be a pointer or
30 interface.`,
31 Since: "2020.1",
32 Severity: lint.SeverityWarning,
33 MergeIf: lint.MergeIfAny,
34 },
35 })
36 37 var Analyzer = SCAnalyzer.Analyzer
38 39 var checkWithValueKeyRules = map[string]callcheck.Check{
40 "context.WithValue": checkWithValueKey,
41 }
42 43 func checkWithValueKey(call *callcheck.Call) {
44 arg := call.Args[1]
45 T := arg.Value.Value.Type()
46 if typ, ok := types.Unalias(T).(*types.Basic); ok {
47 if _, ok := T.(*types.Alias); ok {
48 arg.Invalid(
49 fmt.Sprintf("should not use built-in type %s (via alias %s) as key for value; define your own type to avoid collisions", typ, types.TypeString(T, types.RelativeTo(call.Pass.Pkg))))
50 } else {
51 arg.Invalid(
52 fmt.Sprintf("should not use built-in type %s as key for value; define your own type to avoid collisions", typ))
53 }
54 }
55 // TODO(dh): we should probably flag all anonymous structs, as they all risk collisions
56 if s, ok := T.(*types.Struct); ok && s.NumFields() == 0 {
57 arg.Invalid("should not use empty anonymous struct as key for value; define your own type to avoid collisions")
58 } else if !types.Comparable(T) {
59 arg.Invalid(fmt.Sprintf("keys used with context.WithValue must be comparable, but type %s is not comparable", T))
60 }
61 }
62