62 lines
2.1 KiB
Go
62 lines
2.1 KiB
Go
|
|
package sa1029
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"go/types"
|
||
|
|
|
||
|
|
"honnef.co/go/tools/analysis/callcheck"
|
||
|
|
"honnef.co/go/tools/analysis/lint"
|
||
|
|
"honnef.co/go/tools/internal/passes/buildir"
|
||
|
|
|
||
|
|
"golang.org/x/tools/go/analysis"
|
||
|
|
)
|
||
|
|
|
||
|
|
var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
|
||
|
|
Analyzer: &analysis.Analyzer{
|
||
|
|
Name: "SA1029",
|
||
|
|
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||
|
|
Run: callcheck.Analyzer(checkWithValueKeyRules),
|
||
|
|
},
|
||
|
|
Doc: &lint.RawDocumentation{
|
||
|
|
Title: `Inappropriate key in call to \'context.WithValue\'`,
|
||
|
|
Text: `The provided key must be comparable and should not be
|
||
|
|
of type \'string\' or any other built-in type to avoid collisions between
|
||
|
|
packages using context. Users of \'WithValue\' should define their own
|
||
|
|
types for keys.
|
||
|
|
|
||
|
|
To avoid allocating when assigning to an \'interface{}\',
|
||
|
|
context keys often have concrete type \'struct{}\'. Alternatively,
|
||
|
|
exported context key variables' static type should be a pointer or
|
||
|
|
interface.`,
|
||
|
|
Since: "2020.1",
|
||
|
|
Severity: lint.SeverityWarning,
|
||
|
|
MergeIf: lint.MergeIfAny,
|
||
|
|
},
|
||
|
|
})
|
||
|
|
|
||
|
|
var Analyzer = SCAnalyzer.Analyzer
|
||
|
|
|
||
|
|
var checkWithValueKeyRules = map[string]callcheck.Check{
|
||
|
|
"context.WithValue": checkWithValueKey,
|
||
|
|
}
|
||
|
|
|
||
|
|
func checkWithValueKey(call *callcheck.Call) {
|
||
|
|
arg := call.Args[1]
|
||
|
|
T := arg.Value.Value.Type()
|
||
|
|
if typ, ok := types.Unalias(T).(*types.Basic); ok {
|
||
|
|
if _, ok := T.(*types.Alias); ok {
|
||
|
|
arg.Invalid(
|
||
|
|
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))))
|
||
|
|
} else {
|
||
|
|
arg.Invalid(
|
||
|
|
fmt.Sprintf("should not use built-in type %s as key for value; define your own type to avoid collisions", typ))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// TODO(dh): we should probably flag all anonymous structs, as they all risk collisions
|
||
|
|
if s, ok := T.(*types.Struct); ok && s.NumFields() == 0 {
|
||
|
|
arg.Invalid("should not use empty anonymous struct as key for value; define your own type to avoid collisions")
|
||
|
|
} else if !types.Comparable(T) {
|
||
|
|
arg.Invalid(fmt.Sprintf("keys used with context.WithValue must be comparable, but type %s is not comparable", T))
|
||
|
|
}
|
||
|
|
}
|