1 // Package staticcheck contains a linter for Go source code.
2 package staticcheck // import "honnef.co/go/tools/staticcheck"
10 htmltemplate "html/template"
18 texttemplate "text/template"
21 . "honnef.co/go/tools/arg"
22 "honnef.co/go/tools/code"
23 "honnef.co/go/tools/deprecated"
24 "honnef.co/go/tools/edit"
25 "honnef.co/go/tools/facts"
26 "honnef.co/go/tools/functions"
27 "honnef.co/go/tools/internal/passes/buildir"
28 "honnef.co/go/tools/internal/sharedcheck"
29 "honnef.co/go/tools/ir"
30 "honnef.co/go/tools/ir/irutil"
31 "honnef.co/go/tools/lint"
32 . "honnef.co/go/tools/lint/lintdsl"
33 "honnef.co/go/tools/pattern"
34 "honnef.co/go/tools/printf"
35 "honnef.co/go/tools/report"
37 "golang.org/x/tools/go/analysis"
38 "golang.org/x/tools/go/analysis/passes/inspect"
39 "golang.org/x/tools/go/ast/astutil"
40 "golang.org/x/tools/go/ast/inspector"
41 "golang.org/x/tools/go/types/typeutil"
44 func checkSortSlice(call *Call) {
45 c := call.Instr.Common().StaticCallee()
48 T := arg.Value.Value.Type().Underlying()
50 case *types.Interface:
52 // TODO(dh): if the value is a phi node we can look at its edges
53 if k, ok := arg.Value.Value.(*ir.Const); ok && k.Value == nil {
54 // literal nil, e.g. sort.Sort(nil, ...)
55 arg.Invalid(fmt.Sprintf("cannot call %s on nil literal", c))
61 arg.Invalid(fmt.Sprintf("%s must only be called on slices, was called on %s", c, T))
65 func validRegexp(call *Call) {
67 err := ValidateRegexp(arg.Value)
69 arg.Invalid(err.Error())
75 func (rs runeSlice) Len() int { return len(rs) }
76 func (rs runeSlice) Less(i int, j int) bool { return rs[i] < rs[j] }
77 func (rs runeSlice) Swap(i int, j int) { rs[i], rs[j] = rs[j], rs[i] }
79 func utf8Cutset(call *Call) {
81 if InvalidUTF8(arg.Value) {
82 arg.Invalid(MsgInvalidUTF8)
86 func uniqueCutset(call *Call) {
88 if !UniqueStringCutset(arg.Value) {
89 arg.Invalid(MsgNonUniqueCutset)
93 func unmarshalPointer(name string, arg int) CallCheck {
94 return func(call *Call) {
95 if !Pointer(call.Args[arg].Value) {
96 call.Args[arg].Invalid(fmt.Sprintf("%s expects to unmarshal into a pointer, but the provided value is not a pointer", name))
101 func pointlessIntMath(call *Call) {
102 if ConvertedFromInt(call.Args[0].Value) {
103 call.Invalid(fmt.Sprintf("calling %s on a converted integer is pointless", code.CallName(call.Instr.Common())))
107 func checkValidHostPort(arg int) CallCheck {
108 return func(call *Call) {
109 if !ValidHostPort(call.Args[arg].Value) {
110 call.Args[arg].Invalid(MsgInvalidHostPort)
116 checkRegexpRules = map[string]CallCheck{
117 "regexp.MustCompile": validRegexp,
118 "regexp.Compile": validRegexp,
119 "regexp.Match": validRegexp,
120 "regexp.MatchReader": validRegexp,
121 "regexp.MatchString": validRegexp,
124 checkTimeParseRules = map[string]CallCheck{
125 "time.Parse": func(call *Call) {
126 arg := call.Args[Arg("time.Parse.layout")]
127 err := ValidateTimeLayout(arg.Value)
129 arg.Invalid(err.Error())
134 checkEncodingBinaryRules = map[string]CallCheck{
135 "encoding/binary.Write": func(call *Call) {
136 arg := call.Args[Arg("encoding/binary.Write.data")]
137 if !CanBinaryMarshal(call.Pass, arg.Value) {
138 arg.Invalid(fmt.Sprintf("value of type %s cannot be used with binary.Write", arg.Value.Value.Type()))
143 checkURLsRules = map[string]CallCheck{
144 "net/url.Parse": func(call *Call) {
145 arg := call.Args[Arg("net/url.Parse.rawurl")]
146 err := ValidateURL(arg.Value)
148 arg.Invalid(err.Error())
153 checkSyncPoolValueRules = map[string]CallCheck{
154 "(*sync.Pool).Put": func(call *Call) {
155 arg := call.Args[Arg("(*sync.Pool).Put.x")]
156 typ := arg.Value.Value.Type()
157 if !code.IsPointerLike(typ) {
158 arg.Invalid("argument should be pointer-like to avoid allocations")
163 checkRegexpFindAllRules = map[string]CallCheck{
164 "(*regexp.Regexp).FindAll": RepeatZeroTimes("a FindAll method", 1),
165 "(*regexp.Regexp).FindAllIndex": RepeatZeroTimes("a FindAll method", 1),
166 "(*regexp.Regexp).FindAllString": RepeatZeroTimes("a FindAll method", 1),
167 "(*regexp.Regexp).FindAllStringIndex": RepeatZeroTimes("a FindAll method", 1),
168 "(*regexp.Regexp).FindAllStringSubmatch": RepeatZeroTimes("a FindAll method", 1),
169 "(*regexp.Regexp).FindAllStringSubmatchIndex": RepeatZeroTimes("a FindAll method", 1),
170 "(*regexp.Regexp).FindAllSubmatch": RepeatZeroTimes("a FindAll method", 1),
171 "(*regexp.Regexp).FindAllSubmatchIndex": RepeatZeroTimes("a FindAll method", 1),
174 checkUTF8CutsetRules = map[string]CallCheck{
175 "strings.IndexAny": utf8Cutset,
176 "strings.LastIndexAny": utf8Cutset,
177 "strings.ContainsAny": utf8Cutset,
178 "strings.Trim": utf8Cutset,
179 "strings.TrimLeft": utf8Cutset,
180 "strings.TrimRight": utf8Cutset,
183 checkUniqueCutsetRules = map[string]CallCheck{
184 "strings.Trim": uniqueCutset,
185 "strings.TrimLeft": uniqueCutset,
186 "strings.TrimRight": uniqueCutset,
189 checkUnmarshalPointerRules = map[string]CallCheck{
190 "encoding/xml.Unmarshal": unmarshalPointer("xml.Unmarshal", 1),
191 "(*encoding/xml.Decoder).Decode": unmarshalPointer("Decode", 0),
192 "(*encoding/xml.Decoder).DecodeElement": unmarshalPointer("DecodeElement", 0),
193 "encoding/json.Unmarshal": unmarshalPointer("json.Unmarshal", 1),
194 "(*encoding/json.Decoder).Decode": unmarshalPointer("Decode", 0),
197 checkUnbufferedSignalChanRules = map[string]CallCheck{
198 "os/signal.Notify": func(call *Call) {
199 arg := call.Args[Arg("os/signal.Notify.c")]
200 if UnbufferedChannel(arg.Value) {
201 arg.Invalid("the channel used with signal.Notify should be buffered")
206 checkMathIntRules = map[string]CallCheck{
207 "math.Ceil": pointlessIntMath,
208 "math.Floor": pointlessIntMath,
209 "math.IsNaN": pointlessIntMath,
210 "math.Trunc": pointlessIntMath,
211 "math.IsInf": pointlessIntMath,
214 checkStringsReplaceZeroRules = map[string]CallCheck{
215 "strings.Replace": RepeatZeroTimes("strings.Replace", 3),
216 "bytes.Replace": RepeatZeroTimes("bytes.Replace", 3),
219 checkListenAddressRules = map[string]CallCheck{
220 "net/http.ListenAndServe": checkValidHostPort(0),
221 "net/http.ListenAndServeTLS": checkValidHostPort(0),
224 checkBytesEqualIPRules = map[string]CallCheck{
225 "bytes.Equal": func(call *Call) {
226 if ConvertedFrom(call.Args[Arg("bytes.Equal.a")].Value, "net.IP") &&
227 ConvertedFrom(call.Args[Arg("bytes.Equal.b")].Value, "net.IP") {
228 call.Invalid("use net.IP.Equal to compare net.IPs, not bytes.Equal")
233 checkRegexpMatchLoopRules = map[string]CallCheck{
234 "regexp.Match": loopedRegexp("regexp.Match"),
235 "regexp.MatchReader": loopedRegexp("regexp.MatchReader"),
236 "regexp.MatchString": loopedRegexp("regexp.MatchString"),
239 checkNoopMarshal = map[string]CallCheck{
240 // TODO(dh): should we really flag XML? Even an empty struct
241 // produces a non-zero amount of data, namely its type name.
242 // Let's see if we encounter any false positives.
244 // Also, should we flag gob?
245 "encoding/json.Marshal": checkNoopMarshalImpl(Arg("json.Marshal.v"), "MarshalJSON", "MarshalText"),
246 "encoding/xml.Marshal": checkNoopMarshalImpl(Arg("xml.Marshal.v"), "MarshalXML", "MarshalText"),
247 "(*encoding/json.Encoder).Encode": checkNoopMarshalImpl(Arg("(*encoding/json.Encoder).Encode.v"), "MarshalJSON", "MarshalText"),
248 "(*encoding/xml.Encoder).Encode": checkNoopMarshalImpl(Arg("(*encoding/xml.Encoder).Encode.v"), "MarshalXML", "MarshalText"),
250 "encoding/json.Unmarshal": checkNoopMarshalImpl(Arg("json.Unmarshal.v"), "UnmarshalJSON", "UnmarshalText"),
251 "encoding/xml.Unmarshal": checkNoopMarshalImpl(Arg("xml.Unmarshal.v"), "UnmarshalXML", "UnmarshalText"),
252 "(*encoding/json.Decoder).Decode": checkNoopMarshalImpl(Arg("(*encoding/json.Decoder).Decode.v"), "UnmarshalJSON", "UnmarshalText"),
253 "(*encoding/xml.Decoder).Decode": checkNoopMarshalImpl(Arg("(*encoding/xml.Decoder).Decode.v"), "UnmarshalXML", "UnmarshalText"),
256 checkUnsupportedMarshal = map[string]CallCheck{
257 "encoding/json.Marshal": checkUnsupportedMarshalImpl(Arg("json.Marshal.v"), "json", "MarshalJSON", "MarshalText"),
258 "encoding/xml.Marshal": checkUnsupportedMarshalImpl(Arg("xml.Marshal.v"), "xml", "MarshalXML", "MarshalText"),
259 "(*encoding/json.Encoder).Encode": checkUnsupportedMarshalImpl(Arg("(*encoding/json.Encoder).Encode.v"), "json", "MarshalJSON", "MarshalText"),
260 "(*encoding/xml.Encoder).Encode": checkUnsupportedMarshalImpl(Arg("(*encoding/xml.Encoder).Encode.v"), "xml", "MarshalXML", "MarshalText"),
263 checkAtomicAlignment = map[string]CallCheck{
264 "sync/atomic.AddInt64": checkAtomicAlignmentImpl,
265 "sync/atomic.AddUint64": checkAtomicAlignmentImpl,
266 "sync/atomic.CompareAndSwapInt64": checkAtomicAlignmentImpl,
267 "sync/atomic.CompareAndSwapUint64": checkAtomicAlignmentImpl,
268 "sync/atomic.LoadInt64": checkAtomicAlignmentImpl,
269 "sync/atomic.LoadUint64": checkAtomicAlignmentImpl,
270 "sync/atomic.StoreInt64": checkAtomicAlignmentImpl,
271 "sync/atomic.StoreUint64": checkAtomicAlignmentImpl,
272 "sync/atomic.SwapInt64": checkAtomicAlignmentImpl,
273 "sync/atomic.SwapUint64": checkAtomicAlignmentImpl,
276 // TODO(dh): detect printf wrappers
277 checkPrintfRules = map[string]CallCheck{
278 "fmt.Errorf": func(call *Call) { checkPrintfCall(call, 0, 1) },
279 "fmt.Printf": func(call *Call) { checkPrintfCall(call, 0, 1) },
280 "fmt.Sprintf": func(call *Call) { checkPrintfCall(call, 0, 1) },
281 "fmt.Fprintf": func(call *Call) { checkPrintfCall(call, 1, 2) },
282 "golang.org/x/xerrors.Errorf": func(call *Call) { checkPrintfCall(call, 0, 1) },
285 checkSortSliceRules = map[string]CallCheck{
286 "sort.Slice": checkSortSlice,
287 "sort.SliceIsSorted": checkSortSlice,
288 "sort.SliceStable": checkSortSlice,
291 checkWithValueKeyRules = map[string]CallCheck{
292 "context.WithValue": checkWithValueKey,
296 func checkPrintfCall(call *Call, fIdx, vIdx int) {
299 switch v := call.Args[vIdx].Value.Value.(type) {
302 args, ok = irutil.Vararg(v)
304 // We don't know what the actual arguments to the function are
308 // nil, i.e. no arguments
310 // We don't know what the actual arguments to the function are
313 checkPrintfCallImpl(f, f.Value.Value, args)
319 isInt verbFlag = 1 << iota
324 // Verbs that accept "pseudo pointers" will sometimes dereference
325 // non-nil pointers. For example, %x on a non-nil *struct will print the
326 // individual fields, but on a nil pointer it will print the address.
333 var verbs = [...]verbFlag{
334 'b': isPseudoPointer | isInt | isFP,
336 'd': isPseudoPointer | isInt,
343 'o': isPseudoPointer | isInt,
344 'O': isPseudoPointer | isInt,
345 'p': isSlice | isPointer | noRecurse,
346 'q': isInt | isString,
352 'X': isPseudoPointer | isInt | isFP | isString,
353 'x': isPseudoPointer | isInt | isFP | isString,
356 func checkPrintfCallImpl(carg *Argument, f ir.Value, args []ir.Value) {
357 var msCache *typeutil.MethodSetCache
358 if f.Parent() != nil {
359 msCache = &f.Parent().Prog.MethodSets
362 elem := func(T types.Type, verb rune) ([]types.Type, bool) {
363 if verbs[verb]&noRecurse != 0 {
364 return []types.Type{T}, false
366 switch T := T.(type) {
368 if verbs[verb]&isSlice != 0 {
369 return []types.Type{T}, false
371 if verbs[verb]&isString != 0 && code.IsType(T.Elem().Underlying(), "byte") {
372 return []types.Type{T}, false
374 return []types.Type{T.Elem()}, true
378 return []types.Type{key, val}, true
380 out := make([]types.Type, 0, T.NumFields())
381 for i := 0; i < T.NumFields(); i++ {
382 out = append(out, T.Field(i).Type())
386 return []types.Type{T.Elem()}, true
388 return []types.Type{T}, false
391 isInfo := func(T types.Type, info types.BasicInfo) bool {
392 basic, ok := T.Underlying().(*types.Basic)
393 return ok && basic.Info()&info != 0
396 isStringer := func(T types.Type, ms *types.MethodSet) bool {
397 sel := ms.Lookup(nil, "String")
401 fn, ok := sel.Obj().(*types.Func)
403 // should be unreachable
406 sig := fn.Type().(*types.Signature)
407 if sig.Params().Len() != 0 {
410 if sig.Results().Len() != 1 {
413 if !code.IsType(sig.Results().At(0).Type(), "string") {
418 isError := func(T types.Type, ms *types.MethodSet) bool {
419 sel := ms.Lookup(nil, "Error")
423 fn, ok := sel.Obj().(*types.Func)
425 // should be unreachable
428 sig := fn.Type().(*types.Signature)
429 if sig.Params().Len() != 0 {
432 if sig.Results().Len() != 1 {
435 if !code.IsType(sig.Results().At(0).Type(), "string") {
441 isFormatter := func(T types.Type, ms *types.MethodSet) bool {
442 sel := ms.Lookup(nil, "Format")
446 fn, ok := sel.Obj().(*types.Func)
448 // should be unreachable
451 sig := fn.Type().(*types.Signature)
452 if sig.Params().Len() != 2 {
455 // TODO(dh): check the types of the arguments for more
457 if sig.Results().Len() != 0 {
463 seen := map[types.Type]bool{}
464 var checkType func(verb rune, T types.Type, top bool) bool
465 checkType = func(verb rune, T types.Type, top bool) bool {
467 for k := range seen {
475 if int(verb) >= len(verbs) {
486 ms := msCache.MethodSet(T)
487 if isFormatter(T, ms) {
488 // the value is responsible for formatting itself
492 if flags&isString != 0 && (isStringer(T, ms) || isError(T, ms)) {
493 // Check for stringer early because we're about to dereference
498 if flags&(isPointer|isPseudoPointer) == 0 && top {
499 T = code.Dereference(T)
501 if flags&isPseudoPointer != 0 && top {
502 t := code.Dereference(T)
503 if _, ok := t.Underlying().(*types.Struct); ok {
508 if _, ok := T.(*types.Interface); ok {
509 // We don't know what's in the interface
513 var info types.BasicInfo
514 if flags&isInt != 0 {
515 info |= types.IsInteger
517 if flags&isBool != 0 {
518 info |= types.IsBoolean
521 info |= types.IsFloat | types.IsComplex
523 if flags&isString != 0 {
524 info |= types.IsString
527 if info != 0 && isInfo(T, info) {
531 if flags&isString != 0 && (code.IsType(T, "[]byte") || isStringer(T, ms) || isError(T, ms)) {
535 if flags&isPointer != 0 && code.IsPointerLike(T) {
538 if flags&isPseudoPointer != 0 {
539 switch U := T.Underlying().(type) {
545 if _, ok := U.Elem().Underlying().(*types.Struct); !ok {
546 // TODO(dh): can this condition ever be false? For
547 // *T, if T is a struct, we'll already have
548 // dereferenced it, meaning the *types.Pointer
549 // branch couldn't have been taken. For T that
550 // aren't structs, this condition will always
554 case *types.Chan, *types.Signature:
555 // Channels and functions are always treated as
556 // pointers and never recursed into.
559 if U.Kind() == types.UnsafePointer {
562 case *types.Interface:
563 // we will already have bailed if the type is an
567 // other pointer-like types, such as maps or slices,
568 // will be printed element-wise.
572 if flags&isSlice != 0 {
573 if _, ok := T.(*types.Slice); ok {
578 if flags&isAny != 0 {
582 elems, ok := elem(T.Underlying(), verb)
586 for _, elem := range elems {
587 if !checkType(verb, elem, false) {
595 k, ok := f.(*ir.Const)
599 actions, err := printf.Parse(constant.StringVal(k.Value))
601 carg.Invalid("couldn't parse format string")
608 checkStar := func(verb printf.Verb, star printf.Argument) bool {
609 if star, ok := star.(printf.Star); ok {
611 if star.Index == -1 {
620 carg.Invalid(fmt.Sprintf("Printf format %s reads invalid arg 0; indices are 1-based", verb.Raw))
625 fmt.Sprintf("Printf format %s reads arg #%d, but call has only %d args",
626 verb.Raw, idx, len(args)))
629 if arg, ok := args[idx-1].(*ir.MakeInterface); ok {
630 if !isInfo(arg.X.Type(), types.IsInteger) {
631 carg.Invalid(fmt.Sprintf("Printf format %s reads non-int arg #%d as argument of *", verb.Raw, idx))
638 // We only report one problem per format string. Making a
639 // mistake with an index tends to invalidate all future
641 for _, action := range actions {
642 verb, ok := action.(printf.Verb)
647 if !checkStar(verb, verb.Width) || !checkStar(verb, verb.Precision) {
652 if verb.Value != -1 {
658 fmt.Sprintf("Printf format %s reads arg #%d, but call has only %d args",
659 verb.Raw, off, len(args)))
661 } else if verb.Value == 0 && verb.Letter != '%' {
662 carg.Invalid(fmt.Sprintf("Printf format %s reads invalid arg 0; indices are 1-based", verb.Raw))
665 arg, ok := args[off-1].(*ir.MakeInterface)
667 if !checkType(verb.Letter, arg.X.Type(), true) {
668 carg.Invalid(fmt.Sprintf("Printf format %s has arg #%d of wrong type %s",
669 verb.Raw, ptr, args[ptr-1].(*ir.MakeInterface).X.Type()))
677 // Consume next argument
680 // Don't consume any arguments
686 if !hasExplicit && ptr <= len(args) {
687 carg.Invalid(fmt.Sprintf("Printf call needs %d args but has %d args", ptr-1, len(args)))
691 func checkAtomicAlignmentImpl(call *Call) {
692 sizes := call.Pass.TypesSizes
693 if sizes.Sizeof(types.Typ[types.Uintptr]) != 4 {
694 // Not running on a 32-bit platform
697 v, ok := call.Args[0].Value.Value.(*ir.FieldAddr)
699 // TODO(dh): also check indexing into arrays and slices
702 T := v.X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct)
703 fields := make([]*types.Var, 0, T.NumFields())
704 for i := 0; i < T.NumFields() && i <= v.Field; i++ {
705 fields = append(fields, T.Field(i))
708 off := sizes.Offsetsof(fields)[v.Field]
710 msg := fmt.Sprintf("address of non 64-bit aligned field %s passed to %s",
711 T.Field(v.Field).Name(),
712 code.CallName(call.Instr.Common()))
717 func checkNoopMarshalImpl(argN int, meths ...string) CallCheck {
718 return func(call *Call) {
719 if code.IsGenerated(call.Pass, call.Instr.Pos()) {
722 arg := call.Args[argN]
723 T := arg.Value.Value.Type()
724 Ts, ok := code.Dereference(T).Underlying().(*types.Struct)
728 if Ts.NumFields() == 0 {
731 fields := code.FlattenFields(Ts)
732 for _, field := range fields {
733 if field.Var.Exported() {
737 // OPT(dh): we could use a method set cache here
738 ms := call.Instr.Parent().Prog.MethodSets.MethodSet(T)
739 // TODO(dh): we're not checking the signature, which can cause false negatives.
740 // This isn't a huge problem, however, since vet complains about incorrect signatures.
741 for _, meth := range meths {
742 if ms.Lookup(nil, meth) != nil {
746 arg.Invalid("struct doesn't have any exported fields, nor custom marshaling")
750 func checkUnsupportedMarshalImpl(argN int, tag string, meths ...string) CallCheck {
751 // TODO(dh): flag slices and maps of unsupported types
752 return func(call *Call) {
753 msCache := &call.Instr.Parent().Prog.MethodSets
755 arg := call.Args[argN]
756 T := arg.Value.Value.Type()
757 Ts, ok := code.Dereference(T).Underlying().(*types.Struct)
761 ms := msCache.MethodSet(T)
762 // TODO(dh): we're not checking the signature, which can cause false negatives.
763 // This isn't a huge problem, however, since vet complains about incorrect signatures.
764 for _, meth := range meths {
765 if ms.Lookup(nil, meth) != nil {
769 fields := code.FlattenFields(Ts)
770 for _, field := range fields {
771 if !(field.Var.Exported()) {
774 if reflect.StructTag(field.Tag).Get(tag) == "-" {
777 ms := msCache.MethodSet(field.Var.Type())
778 // TODO(dh): we're not checking the signature, which can cause false negatives.
779 // This isn't a huge problem, however, since vet complains about incorrect signatures.
780 for _, meth := range meths {
781 if ms.Lookup(nil, meth) != nil {
785 switch field.Var.Type().Underlying().(type) {
786 case *types.Chan, *types.Signature:
787 arg.Invalid(fmt.Sprintf("trying to marshal chan or func value, field %s", fieldPath(T, field.Path)))
793 func fieldPath(start types.Type, indices []int) string {
795 for _, idx := range indices {
796 field := code.Dereference(start).Underlying().(*types.Struct).Field(idx)
798 p += "." + field.Name()
803 func isInLoop(b *ir.BasicBlock) bool {
804 sets := functions.FindLoops(b.Parent())
805 for _, set := range sets {
813 func CheckUntrappableSignal(pass *analysis.Pass) (interface{}, error) {
814 fn := func(node ast.Node) {
815 call := node.(*ast.CallExpr)
816 if !code.IsCallToAnyAST(pass, call,
817 "os/signal.Ignore", "os/signal.Notify", "os/signal.Reset") {
822 for _, arg := range call.Args {
823 if conv, ok := arg.(*ast.CallExpr); ok && isName(pass, conv.Fun, "os.Signal") {
827 if isName(pass, arg, "syscall.SIGTERM") {
833 for i, arg := range call.Args {
834 if conv, ok := arg.(*ast.CallExpr); ok && isName(pass, conv.Fun, "os.Signal") {
838 if isName(pass, arg, "os.Kill") || isName(pass, arg, "syscall.SIGKILL") {
839 var fixes []analysis.SuggestedFix
841 nargs := make([]ast.Expr, len(call.Args))
842 for j, a := range call.Args {
844 nargs[j] = Selector("syscall", "SIGTERM")
851 fixes = append(fixes, edit.Fix(fmt.Sprintf("use syscall.SIGTERM instead of %s", report.Render(pass, arg)), edit.ReplaceWithNode(pass.Fset, call, &ncall)))
853 nargs := make([]ast.Expr, 0, len(call.Args))
854 for j, a := range call.Args {
858 nargs = append(nargs, a)
862 fixes = append(fixes, edit.Fix(fmt.Sprintf("remove %s from list of arguments", report.Render(pass, arg)), edit.ReplaceWithNode(pass.Fset, call, &ncall)))
863 report.Report(pass, arg, fmt.Sprintf("%s cannot be trapped (did you mean syscall.SIGTERM?)", report.Render(pass, arg)), report.Fixes(fixes...))
865 if isName(pass, arg, "syscall.SIGSTOP") {
866 nargs := make([]ast.Expr, 0, len(call.Args)-1)
867 for j, a := range call.Args {
871 nargs = append(nargs, a)
875 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))))
879 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
883 func CheckTemplate(pass *analysis.Pass) (interface{}, error) {
884 fn := func(node ast.Node) {
885 call := node.(*ast.CallExpr)
887 switch code.CallNameAST(pass, call) {
888 case "(*text/template.Template).Parse":
890 case "(*html/template.Template).Parse":
895 sel := call.Fun.(*ast.SelectorExpr)
896 if !code.IsCallToAnyAST(pass, sel.X, "text/template.New", "html/template.New") {
897 // TODO(dh): this is a cheap workaround for templates with
898 // different delims. A better solution with less false
899 // negatives would use data flow analysis to see where the
900 // template comes from and where it has been
903 s, ok := code.ExprToString(pass, call.Args[Arg("(*text/template.Template).Parse.text")])
910 _, err = texttemplate.New("").Parse(s)
912 _, err = htmltemplate.New("").Parse(s)
915 // TODO(dominikh): whitelist other parse errors, if any
916 if strings.Contains(err.Error(), "unexpected") {
917 report.Report(pass, call.Args[Arg("(*text/template.Template).Parse.text")], err.Error())
921 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
926 checkTimeSleepConstantPatternRns = pattern.MustParse(`(BinaryExpr duration "*" (SelectorExpr (Ident "time") (Ident "Nanosecond")))`)
927 checkTimeSleepConstantPatternRs = pattern.MustParse(`(BinaryExpr duration "*" (SelectorExpr (Ident "time") (Ident "Second")))`)
930 func CheckTimeSleepConstant(pass *analysis.Pass) (interface{}, error) {
931 fn := func(node ast.Node) {
932 call := node.(*ast.CallExpr)
933 if !code.IsCallToAST(pass, call, "time.Sleep") {
936 lit, ok := call.Args[Arg("time.Sleep.d")].(*ast.BasicLit)
940 n, err := strconv.Atoi(lit.Value)
944 if n == 0 || n > 120 {
945 // time.Sleep(0) is a seldom used pattern in concurrency
946 // tests. >120 might be intentional. 120 was chosen
947 // because the user could've meant 2 minutes.
951 report.Report(pass, lit,
952 fmt.Sprintf("sleeping for %d nanoseconds is probably a bug; be explicit if it isn't", n), report.Fixes(
953 edit.Fix("explicitly use nanoseconds", edit.ReplaceWithPattern(pass, checkTimeSleepConstantPatternRns, pattern.State{"duration": lit}, lit)),
954 edit.Fix("use seconds", edit.ReplaceWithPattern(pass, checkTimeSleepConstantPatternRs, pattern.State{"duration": lit}, lit))))
956 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
960 var checkWaitgroupAddQ = pattern.MustParse(`
965 call@(CallExpr (Function "(*sync.WaitGroup).Add") _):_) _))`)
967 func CheckWaitgroupAdd(pass *analysis.Pass) (interface{}, error) {
968 fn := func(node ast.Node) {
969 if m, ok := Match(pass, checkWaitgroupAddQ, node); ok {
970 call := m.State["call"].(ast.Node)
971 report.Report(pass, call, fmt.Sprintf("should call %s before starting the goroutine to avoid a race", report.Render(pass, call)))
974 code.Preorder(pass, fn, (*ast.GoStmt)(nil))
978 func CheckInfiniteEmptyLoop(pass *analysis.Pass) (interface{}, error) {
979 fn := func(node ast.Node) {
980 loop := node.(*ast.ForStmt)
981 if len(loop.Body.List) != 0 || loop.Post != nil {
985 if loop.Init != nil {
986 // TODO(dh): this isn't strictly necessary, it just makes
990 // An empty loop is bad news in two cases: 1) The loop has no
991 // condition. In that case, it's just a loop that spins
992 // forever and as fast as it can, keeping a core busy. 2) The
993 // loop condition only consists of variable or field reads and
994 // operators on those. The only way those could change their
995 // value is with unsynchronised access, which constitutes a
998 // If the condition contains any function calls, its behaviour
999 // is dynamic and the loop might terminate. Similarly for
1000 // channel receives.
1002 if loop.Cond != nil {
1003 if code.MayHaveSideEffects(pass, loop.Cond, nil) {
1006 if ident, ok := loop.Cond.(*ast.Ident); ok {
1007 if k, ok := pass.TypesInfo.ObjectOf(ident).(*types.Const); ok {
1008 if !constant.BoolVal(k.Val()) {
1009 // don't flag `for false {}` loops. They're a debug aid.
1014 report.Report(pass, loop, "loop condition never changes or has a race condition")
1016 report.Report(pass, loop, "this loop will spin, using 100%% CPU", report.ShortRange())
1018 code.Preorder(pass, fn, (*ast.ForStmt)(nil))
1022 func CheckDeferInInfiniteLoop(pass *analysis.Pass) (interface{}, error) {
1023 fn := func(node ast.Node) {
1025 var defers []ast.Stmt
1026 loop := node.(*ast.ForStmt)
1027 if loop.Cond != nil {
1030 fn2 := func(node ast.Node) bool {
1031 switch stmt := node.(type) {
1032 case *ast.ReturnStmt:
1035 case *ast.BranchStmt:
1036 // TODO(dominikh): if this sees a break in a switch or
1037 // select, it doesn't check if it breaks the loop or
1038 // just the select/switch. This causes some false
1040 if stmt.Tok == token.BREAK {
1044 case *ast.DeferStmt:
1045 defers = append(defers, stmt)
1047 // Don't look into function bodies
1052 ast.Inspect(loop.Body, fn2)
1056 for _, stmt := range defers {
1057 report.Report(pass, stmt, "defers in this infinite loop will never run")
1060 code.Preorder(pass, fn, (*ast.ForStmt)(nil))
1064 func CheckDubiousDeferInChannelRangeLoop(pass *analysis.Pass) (interface{}, error) {
1065 fn := func(node ast.Node) {
1066 loop := node.(*ast.RangeStmt)
1067 typ := pass.TypesInfo.TypeOf(loop.X)
1068 _, ok := typ.Underlying().(*types.Chan)
1072 fn2 := func(node ast.Node) bool {
1073 switch stmt := node.(type) {
1074 case *ast.DeferStmt:
1075 report.Report(pass, stmt, "defers in this range loop won't run unless the channel gets closed")
1077 // Don't look into function bodies
1082 ast.Inspect(loop.Body, fn2)
1084 code.Preorder(pass, fn, (*ast.RangeStmt)(nil))
1088 func CheckTestMainExit(pass *analysis.Pass) (interface{}, error) {
1095 fn := func(node ast.Node, push bool) bool {
1097 if fnmain != nil && node == fnmain {
1098 if !callsExit && callsRun {
1099 report.Report(pass, fnmain, "TestMain should call os.Exit to set exit code")
1109 switch node := node.(type) {
1114 if !isTestMain(pass, node) {
1118 arg = pass.TypesInfo.ObjectOf(node.Type.Params.List[0].Names[0])
1121 if code.IsCallToAST(pass, node, "os.Exit") {
1125 sel, ok := node.Fun.(*ast.SelectorExpr)
1129 ident, ok := sel.X.(*ast.Ident)
1133 if arg != pass.TypesInfo.ObjectOf(ident) {
1136 if sel.Sel.Name == "Run" {
1142 ExhaustiveTypeSwitch(node)
1146 pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.FuncDecl)(nil), (*ast.CallExpr)(nil)}, fn)
1150 func isTestMain(pass *analysis.Pass, decl *ast.FuncDecl) bool {
1151 if decl.Name.Name != "TestMain" {
1154 if len(decl.Type.Params.List) != 1 {
1157 arg := decl.Type.Params.List[0]
1158 if len(arg.Names) != 1 {
1161 return code.IsOfType(pass, arg.Type, "*testing.M")
1164 func CheckExec(pass *analysis.Pass) (interface{}, error) {
1165 fn := func(node ast.Node) {
1166 call := node.(*ast.CallExpr)
1167 if !code.IsCallToAST(pass, call, "os/exec.Command") {
1170 val, ok := code.ExprToString(pass, call.Args[Arg("os/exec.Command.name")])
1174 if !strings.Contains(val, " ") || strings.Contains(val, `\`) || strings.Contains(val, "/") {
1177 report.Report(pass, call.Args[Arg("os/exec.Command.name")],
1178 "first argument to exec.Command looks like a shell command, but a program name or path are expected")
1180 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
1184 func CheckLoopEmptyDefault(pass *analysis.Pass) (interface{}, error) {
1185 fn := func(node ast.Node) {
1186 loop := node.(*ast.ForStmt)
1187 if len(loop.Body.List) != 1 || loop.Cond != nil || loop.Init != nil {
1190 sel, ok := loop.Body.List[0].(*ast.SelectStmt)
1194 for _, c := range sel.Body.List {
1195 // FIXME this leaves behind an empty line, and possibly
1196 // comments in the default branch. We can't easily fix
1198 if comm, ok := c.(*ast.CommClause); ok && comm.Comm == nil && len(comm.Body) == 0 {
1199 report.Report(pass, comm, "should not have an empty default case in a for+select loop; the loop will spin",
1200 report.Fixes(edit.Fix("remove empty default branch", edit.Delete(comm))))
1201 // there can only be one default case
1206 code.Preorder(pass, fn, (*ast.ForStmt)(nil))
1210 func CheckLhsRhsIdentical(pass *analysis.Pass) (interface{}, error) {
1211 var isFloat func(T types.Type) bool
1212 isFloat = func(T types.Type) bool {
1213 switch T := T.Underlying().(type) {
1216 return kind == types.Float32 || kind == types.Float64
1218 return isFloat(T.Elem())
1220 for i := 0; i < T.NumFields(); i++ {
1221 if !isFloat(T.Field(i).Type()) {
1231 // TODO(dh): this check ignores the existence of side-effects and
1232 // happily flags fn() == fn() – so far, we've had nobody complain
1233 // about a false positive, and it's caught several bugs in real
1235 fn := func(node ast.Node) {
1236 op := node.(*ast.BinaryExpr)
1238 case token.EQL, token.NEQ:
1239 if isFloat(pass.TypesInfo.TypeOf(op.X)) {
1240 // f == f and f != f might be used to check for NaN
1243 case token.SUB, token.QUO, token.AND, token.REM, token.OR, token.XOR, token.AND_NOT,
1244 token.LAND, token.LOR, token.LSS, token.GTR, token.LEQ, token.GEQ:
1246 // For some ops, such as + and *, it can make sense to
1247 // have identical operands
1251 if reflect.TypeOf(op.X) != reflect.TypeOf(op.Y) {
1254 if report.Render(pass, op.X) != report.Render(pass, op.Y) {
1257 l1, ok1 := op.X.(*ast.BasicLit)
1258 l2, ok2 := op.Y.(*ast.BasicLit)
1259 if ok1 && ok2 && l1.Kind == token.INT && l2.Kind == l1.Kind && l1.Value == "0" && l2.Value == l1.Value && code.IsGenerated(pass, l1.Pos()) {
1260 // cgo generates the following function call:
1261 // _cgoCheckPointer(_cgoBase0, 0 == 0) – it uses 0 == 0
1262 // instead of true in case the user shadowed the
1263 // identifier. Ideally we'd restrict this exception to
1264 // calls of _cgoCheckPointer, but it's not worth the
1265 // hassle of keeping track of the stack. <lit> <op> <lit>
1266 // are very rare to begin with, and we're mostly checking
1267 // for them to catch typos such as 1 == 1 where the user
1268 // meant to type i == 1. The odds of a false negative for
1272 report.Report(pass, op, fmt.Sprintf("identical expressions on the left and right side of the '%s' operator", op.Op))
1274 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil))
1278 func CheckScopedBreak(pass *analysis.Pass) (interface{}, error) {
1279 fn := func(node ast.Node) {
1280 var body *ast.BlockStmt
1281 switch node := node.(type) {
1284 case *ast.RangeStmt:
1287 ExhaustiveTypeSwitch(node)
1289 for _, stmt := range body.List {
1290 var blocks [][]ast.Stmt
1291 switch stmt := stmt.(type) {
1292 case *ast.SwitchStmt:
1293 for _, c := range stmt.Body.List {
1294 blocks = append(blocks, c.(*ast.CaseClause).Body)
1296 case *ast.SelectStmt:
1297 for _, c := range stmt.Body.List {
1298 blocks = append(blocks, c.(*ast.CommClause).Body)
1304 for _, body := range blocks {
1308 lasts := []ast.Stmt{body[len(body)-1]}
1309 // TODO(dh): unfold all levels of nested block
1310 // statements, not just a single level if statement
1311 if ifs, ok := lasts[0].(*ast.IfStmt); ok {
1312 if len(ifs.Body.List) == 0 {
1315 lasts[0] = ifs.Body.List[len(ifs.Body.List)-1]
1317 if block, ok := ifs.Else.(*ast.BlockStmt); ok {
1318 if len(block.List) != 0 {
1319 lasts = append(lasts, block.List[len(block.List)-1])
1323 for _, last := range lasts {
1324 branch, ok := last.(*ast.BranchStmt)
1325 if !ok || branch.Tok != token.BREAK || branch.Label != nil {
1328 report.Report(pass, branch, "ineffective break statement. Did you mean to break out of the outer loop?")
1333 code.Preorder(pass, fn, (*ast.ForStmt)(nil), (*ast.RangeStmt)(nil))
1337 func CheckUnsafePrintf(pass *analysis.Pass) (interface{}, error) {
1338 fn := func(node ast.Node) {
1339 call := node.(*ast.CallExpr)
1340 name := code.CallNameAST(pass, call)
1344 case "fmt.Printf", "fmt.Sprintf", "log.Printf":
1345 arg = Arg("fmt.Printf.format")
1347 arg = Arg("fmt.Fprintf.format")
1351 if len(call.Args) != arg+1 {
1354 switch call.Args[arg].(type) {
1355 case *ast.CallExpr, *ast.Ident:
1360 alt := name[:len(name)-1]
1361 report.Report(pass, call,
1362 "printf-style function with dynamic format string and no further arguments should use print-style function instead",
1363 report.Fixes(edit.Fix(fmt.Sprintf("use %s instead of %s", alt, name), edit.ReplaceWithString(pass.Fset, call.Fun, alt))))
1365 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
1369 func CheckEarlyDefer(pass *analysis.Pass) (interface{}, error) {
1370 fn := func(node ast.Node) {
1371 block := node.(*ast.BlockStmt)
1372 if len(block.List) < 2 {
1375 for i, stmt := range block.List {
1376 if i == len(block.List)-1 {
1379 assign, ok := stmt.(*ast.AssignStmt)
1383 if len(assign.Rhs) != 1 {
1386 if len(assign.Lhs) < 2 {
1389 if lhs, ok := assign.Lhs[len(assign.Lhs)-1].(*ast.Ident); ok && lhs.Name == "_" {
1392 call, ok := assign.Rhs[0].(*ast.CallExpr)
1396 sig, ok := pass.TypesInfo.TypeOf(call.Fun).(*types.Signature)
1400 if sig.Results().Len() < 2 {
1403 last := sig.Results().At(sig.Results().Len() - 1)
1404 // FIXME(dh): check that it's error from universe, not
1405 // another type of the same name
1406 if last.Type().String() != "error" {
1409 lhs, ok := assign.Lhs[0].(*ast.Ident)
1413 def, ok := block.List[i+1].(*ast.DeferStmt)
1417 sel, ok := def.Call.Fun.(*ast.SelectorExpr)
1421 ident, ok := selectorX(sel).(*ast.Ident)
1425 if ident.Obj != lhs.Obj {
1428 if sel.Sel.Name != "Close" {
1431 report.Report(pass, def, fmt.Sprintf("should check returned error before deferring %s", report.Render(pass, def.Call)))
1434 code.Preorder(pass, fn, (*ast.BlockStmt)(nil))
1438 func selectorX(sel *ast.SelectorExpr) ast.Node {
1439 switch x := sel.X.(type) {
1440 case *ast.SelectorExpr:
1447 func CheckEmptyCriticalSection(pass *analysis.Pass) (interface{}, error) {
1448 if pass.Pkg.Path() == "sync_test" {
1449 // exception for the sync package's tests
1453 // Initially it might seem like this check would be easier to
1454 // implement using IR. After all, we're only checking for two
1455 // consecutive method calls. In reality, however, there may be any
1456 // number of other instructions between the lock and unlock, while
1457 // still constituting an empty critical section. For example,
1458 // given `m.x().Lock(); m.x().Unlock()`, there will be a call to
1459 // x(). In the AST-based approach, this has a tiny potential for a
1460 // false positive (the second call to x might be doing work that
1461 // is protected by the mutex). In an IR-based approach, however,
1462 // it would miss a lot of real bugs.
1464 mutexParams := func(s ast.Stmt) (x ast.Expr, funcName string, ok bool) {
1465 expr, ok := s.(*ast.ExprStmt)
1467 return nil, "", false
1469 call, ok := expr.X.(*ast.CallExpr)
1471 return nil, "", false
1473 sel, ok := call.Fun.(*ast.SelectorExpr)
1475 return nil, "", false
1478 fn, ok := pass.TypesInfo.ObjectOf(sel.Sel).(*types.Func)
1480 return nil, "", false
1482 sig := fn.Type().(*types.Signature)
1483 if sig.Params().Len() != 0 || sig.Results().Len() != 0 {
1484 return nil, "", false
1487 return sel.X, fn.Name(), true
1490 fn := func(node ast.Node) {
1491 block := node.(*ast.BlockStmt)
1492 if len(block.List) < 2 {
1495 for i := range block.List[:len(block.List)-1] {
1496 sel1, method1, ok1 := mutexParams(block.List[i])
1497 sel2, method2, ok2 := mutexParams(block.List[i+1])
1499 if !ok1 || !ok2 || report.Render(pass, sel1) != report.Render(pass, sel2) {
1502 if (method1 == "Lock" && method2 == "Unlock") ||
1503 (method1 == "RLock" && method2 == "RUnlock") {
1504 report.Report(pass, block.List[i+1], "empty critical section")
1508 code.Preorder(pass, fn, (*ast.BlockStmt)(nil))
1513 // cgo produces code like fn(&*_Cvar_kSomeCallbacks) which we don't
1515 cgoIdent = regexp.MustCompile(`^_C(func|var)_.+$`)
1516 checkIneffectiveCopyQ1 = pattern.MustParse(`(UnaryExpr "&" (StarExpr obj))`)
1517 checkIneffectiveCopyQ2 = pattern.MustParse(`(StarExpr (UnaryExpr "&" _))`)
1520 func CheckIneffectiveCopy(pass *analysis.Pass) (interface{}, error) {
1521 fn := func(node ast.Node) {
1522 if m, ok := Match(pass, checkIneffectiveCopyQ1, node); ok {
1523 if ident, ok := m.State["obj"].(*ast.Ident); !ok || !cgoIdent.MatchString(ident.Name) {
1524 report.Report(pass, node, "&*x will be simplified to x. It will not copy x.")
1526 } else if _, ok := Match(pass, checkIneffectiveCopyQ2, node); ok {
1527 report.Report(pass, node, "*&x will be simplified to x. It will not copy x.")
1530 code.Preorder(pass, fn, (*ast.UnaryExpr)(nil), (*ast.StarExpr)(nil))
1534 func CheckCanonicalHeaderKey(pass *analysis.Pass) (interface{}, error) {
1535 fn := func(node ast.Node, push bool) bool {
1539 assign, ok := node.(*ast.AssignStmt)
1541 // TODO(dh): This risks missing some Header reads, for
1542 // example in `h1["foo"] = h2["foo"]` – these edge
1543 // cases are probably rare enough to ignore for now.
1544 for _, expr := range assign.Lhs {
1545 op, ok := expr.(*ast.IndexExpr)
1549 if code.IsOfType(pass, op.X, "net/http.Header") {
1555 op, ok := node.(*ast.IndexExpr)
1559 if !code.IsOfType(pass, op.X, "net/http.Header") {
1562 s, ok := code.ExprToString(pass, op.Index)
1566 canonical := http.CanonicalHeaderKey(s)
1570 var fix analysis.SuggestedFix
1571 switch op.Index.(type) {
1573 fix = edit.Fix("canonicalize header key", edit.ReplaceWithString(pass.Fset, op.Index, strconv.Quote(canonical)))
1575 call := &ast.CallExpr{
1576 Fun: Selector("http", "CanonicalHeaderKey"),
1577 Args: []ast.Expr{op.Index},
1579 fix = edit.Fix("wrap in http.CanonicalHeaderKey", edit.ReplaceWithNode(pass.Fset, op.Index, call))
1581 msg := fmt.Sprintf("keys in http.Header are canonicalized, %q is not canonical; fix the constant or use http.CanonicalHeaderKey", s)
1582 if fix.Message != "" {
1583 report.Report(pass, op, msg, report.Fixes(fix))
1585 report.Report(pass, op, msg)
1589 pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.AssignStmt)(nil), (*ast.IndexExpr)(nil)}, fn)
1593 func CheckBenchmarkN(pass *analysis.Pass) (interface{}, error) {
1594 fn := func(node ast.Node) {
1595 assign := node.(*ast.AssignStmt)
1596 if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 {
1599 sel, ok := assign.Lhs[0].(*ast.SelectorExpr)
1603 if sel.Sel.Name != "N" {
1606 if !code.IsOfType(pass, sel.X, "*testing.B") {
1609 report.Report(pass, assign, fmt.Sprintf("should not assign to %s", report.Render(pass, sel)))
1611 code.Preorder(pass, fn, (*ast.AssignStmt)(nil))
1615 func CheckUnreadVariableValues(pass *analysis.Pass) (interface{}, error) {
1616 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
1617 if code.IsExample(fn) {
1624 if gen, ok := code.Generator(pass, node.Pos()); ok && gen == facts.Goyacc {
1625 // Don't flag unused values in code generated by goyacc.
1626 // There may be hundreds of those due to the way the state
1627 // machine is constructed.
1631 switchTags := map[ir.Value]struct{}{}
1632 ast.Inspect(node, func(node ast.Node) bool {
1633 s, ok := node.(*ast.SwitchStmt)
1637 v, _ := fn.ValueForExpr(s.Tag)
1638 switchTags[v] = struct{}{}
1642 // OPT(dh): don't use a map, possibly use a bitset
1643 var hasUse func(v ir.Value, seen map[ir.Value]struct{}) bool
1644 hasUse = func(v ir.Value, seen map[ir.Value]struct{}) bool {
1645 if _, ok := seen[v]; ok {
1648 if _, ok := switchTags[v]; ok {
1651 refs := v.Referrers()
1653 // TODO investigate why refs can be nil
1656 for _, ref := range *refs {
1657 switch ref := ref.(type) {
1661 seen = map[ir.Value]struct{}{}
1663 seen[v] = struct{}{}
1664 if hasUse(ref, seen) {
1669 seen = map[ir.Value]struct{}{}
1671 seen[v] = struct{}{}
1672 if hasUse(ref, seen) {
1682 ast.Inspect(node, func(node ast.Node) bool {
1683 assign, ok := node.(*ast.AssignStmt)
1687 if len(assign.Lhs) > 1 && len(assign.Rhs) == 1 {
1688 // Either a function call with multiple return values,
1689 // or a comma-ok assignment
1691 val, _ := fn.ValueForExpr(assign.Rhs[0])
1695 refs := val.Referrers()
1699 for _, ref := range *refs {
1700 ex, ok := ref.(*ir.Extract)
1704 if !hasUse(ex, nil) {
1705 lhs := assign.Lhs[ex.Index]
1706 if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" {
1709 report.Report(pass, assign, fmt.Sprintf("this value of %s is never used", lhs))
1714 for i, lhs := range assign.Lhs {
1715 rhs := assign.Rhs[i]
1716 if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" {
1719 val, _ := fn.ValueForExpr(rhs)
1724 if _, ok := val.(*ir.Const); ok {
1725 // a zero-valued constant, for example in 'foo := []string(nil)'
1728 if !hasUse(val, nil) {
1729 report.Report(pass, assign, fmt.Sprintf("this value of %s is never used", lhs))
1738 func CheckPredeterminedBooleanExprs(pass *analysis.Pass) (interface{}, error) {
1739 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
1740 for _, block := range fn.Blocks {
1741 for _, ins := range block.Instrs {
1742 binop, ok := ins.(*ir.BinOp)
1747 case token.GTR, token.LSS, token.EQL, token.NEQ, token.LEQ, token.GEQ:
1752 xs, ok1 := consts(binop.X, nil, nil)
1753 ys, ok2 := consts(binop.Y, nil, nil)
1754 if !ok1 || !ok2 || len(xs) == 0 || len(ys) == 0 {
1759 for _, x := range xs {
1760 for _, y := range ys {
1767 if constant.Compare(x.Value, binop.Op, y.Value) {
1773 if trues == 0 || trues == len(xs)*len(ys) {
1774 report.Report(pass, binop, fmt.Sprintf("binary expression is always %t for all possible values (%s %s %s)", b, xs, binop.Op, ys))
1782 func CheckNilMaps(pass *analysis.Pass) (interface{}, error) {
1783 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
1784 for _, block := range fn.Blocks {
1785 for _, ins := range block.Instrs {
1786 mu, ok := ins.(*ir.MapUpdate)
1790 c, ok := mu.Map.(*ir.Const)
1797 report.Report(pass, mu, "assignment to nil map")
1804 func CheckExtremeComparison(pass *analysis.Pass) (interface{}, error) {
1805 isobj := func(expr ast.Expr, name string) bool {
1806 sel, ok := expr.(*ast.SelectorExpr)
1810 return code.IsObject(pass.TypesInfo.ObjectOf(sel.Sel), name)
1813 fn := func(node ast.Node) {
1814 expr := node.(*ast.BinaryExpr)
1815 tx := pass.TypesInfo.TypeOf(expr.X)
1816 basic, ok := tx.Underlying().(*types.Basic)
1824 switch basic.Kind() {
1826 max = "math.MaxUint8"
1828 max = "math.MaxUint16"
1830 max = "math.MaxUint32"
1832 max = "math.MaxUint64"
1834 max = "math.MaxUint64"
1837 min = "math.MinInt8"
1838 max = "math.MaxInt8"
1840 min = "math.MinInt16"
1841 max = "math.MaxInt16"
1843 min = "math.MinInt32"
1844 max = "math.MaxInt32"
1846 min = "math.MinInt64"
1847 max = "math.MaxInt64"
1849 min = "math.MinInt64"
1850 max = "math.MaxInt64"
1853 if (expr.Op == token.GTR || expr.Op == token.GEQ) && isobj(expr.Y, max) ||
1854 (expr.Op == token.LSS || expr.Op == token.LEQ) && isobj(expr.X, max) {
1855 report.Report(pass, expr, fmt.Sprintf("no value of type %s is greater than %s", basic, max))
1857 if expr.Op == token.LEQ && isobj(expr.Y, max) ||
1858 expr.Op == token.GEQ && isobj(expr.X, max) {
1859 report.Report(pass, expr, fmt.Sprintf("every value of type %s is <= %s", basic, max))
1862 if (basic.Info() & types.IsUnsigned) != 0 {
1863 if (expr.Op == token.LSS && code.IsIntLiteral(expr.Y, "0")) ||
1864 (expr.Op == token.GTR && code.IsIntLiteral(expr.X, "0")) {
1865 report.Report(pass, expr, fmt.Sprintf("no value of type %s is less than 0", basic))
1867 if expr.Op == token.GEQ && code.IsIntLiteral(expr.Y, "0") ||
1868 expr.Op == token.LEQ && code.IsIntLiteral(expr.X, "0") {
1869 report.Report(pass, expr, fmt.Sprintf("every value of type %s is >= 0", basic))
1872 if (expr.Op == token.LSS || expr.Op == token.LEQ) && isobj(expr.Y, min) ||
1873 (expr.Op == token.GTR || expr.Op == token.GEQ) && isobj(expr.X, min) {
1874 report.Report(pass, expr, fmt.Sprintf("no value of type %s is less than %s", basic, min))
1876 if expr.Op == token.GEQ && isobj(expr.Y, min) ||
1877 expr.Op == token.LEQ && isobj(expr.X, min) {
1878 report.Report(pass, expr, fmt.Sprintf("every value of type %s is >= %s", basic, min))
1883 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil))
1887 func consts(val ir.Value, out []*ir.Const, visitedPhis map[string]bool) ([]*ir.Const, bool) {
1888 if visitedPhis == nil {
1889 visitedPhis = map[string]bool{}
1892 switch val := val.(type) {
1894 if visitedPhis[val.Name()] {
1897 visitedPhis[val.Name()] = true
1898 vals := val.Operands(nil)
1899 for _, phival := range vals {
1900 out, ok = consts(*phival, out, visitedPhis)
1906 out = append(out, val)
1908 out, ok = consts(val.X, out, visitedPhis)
1918 uniq := []*ir.Const{out[0]}
1919 for _, val := range out[1:] {
1920 if val.Value == uniq[len(uniq)-1].Value {
1923 uniq = append(uniq, val)
1928 func CheckLoopCondition(pass *analysis.Pass) (interface{}, error) {
1929 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
1930 cb := func(node ast.Node) bool {
1931 loop, ok := node.(*ast.ForStmt)
1935 if loop.Init == nil || loop.Cond == nil || loop.Post == nil {
1938 init, ok := loop.Init.(*ast.AssignStmt)
1939 if !ok || len(init.Lhs) != 1 || len(init.Rhs) != 1 {
1942 cond, ok := loop.Cond.(*ast.BinaryExpr)
1946 x, ok := cond.X.(*ast.Ident)
1950 lhs, ok := init.Lhs[0].(*ast.Ident)
1954 if x.Obj != lhs.Obj {
1957 if _, ok := loop.Post.(*ast.IncDecStmt); !ok {
1961 v, isAddr := fn.ValueForExpr(cond.X)
1962 if v == nil || isAddr {
1965 switch v := v.(type) {
1967 ops := v.Operands(nil)
1971 _, ok := (*ops[0]).(*ir.Const)
1975 sigma, ok := (*ops[1]).(*ir.Sigma)
1985 report.Report(pass, cond, "variable in loop condition never changes")
1989 Inspect(fn.Source(), cb)
1994 func CheckArgOverwritten(pass *analysis.Pass) (interface{}, error) {
1995 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
1996 cb := func(node ast.Node) bool {
1997 var typ *ast.FuncType
1998 var body *ast.BlockStmt
1999 switch fn := node.(type) {
2010 if len(typ.Params.List) == 0 {
2013 for _, field := range typ.Params.List {
2014 for _, arg := range field.Names {
2015 obj := pass.TypesInfo.ObjectOf(arg)
2016 var irobj *ir.Parameter
2017 for _, param := range fn.Params {
2018 if param.Object() == obj {
2026 refs := irobj.Referrers()
2030 if len(code.FilterDebug(*refs)) != 0 {
2034 var assignment ast.Node
2035 ast.Inspect(body, func(node ast.Node) bool {
2036 if assignment != nil {
2039 assign, ok := node.(*ast.AssignStmt)
2043 for _, lhs := range assign.Lhs {
2044 ident, ok := lhs.(*ast.Ident)
2048 if pass.TypesInfo.ObjectOf(ident) == obj {
2055 if assignment != nil {
2056 report.Report(pass, arg, fmt.Sprintf("argument %s is overwritten before first use", arg),
2057 report.Related(assignment, fmt.Sprintf("assignment to %s", arg)))
2063 Inspect(fn.Source(), cb)
2068 func CheckIneffectiveLoop(pass *analysis.Pass) (interface{}, error) {
2069 // This check detects some, but not all unconditional loop exits.
2070 // We give up in the following cases:
2072 // - a goto anywhere in the loop. The goto might skip over our
2073 // return, and we don't check that it doesn't.
2075 // - any nested, unlabelled continue, even if it is in another
2077 fn := func(node ast.Node) {
2078 var body *ast.BlockStmt
2079 switch fn := node.(type) {
2085 ExhaustiveTypeSwitch(node)
2090 labels := map[*ast.Object]ast.Stmt{}
2091 ast.Inspect(body, func(node ast.Node) bool {
2092 label, ok := node.(*ast.LabeledStmt)
2096 labels[label.Label.Obj] = label.Stmt
2100 ast.Inspect(body, func(node ast.Node) bool {
2102 var body *ast.BlockStmt
2103 switch node := node.(type) {
2107 case *ast.RangeStmt:
2108 typ := pass.TypesInfo.TypeOf(node.X)
2109 if _, ok := typ.Underlying().(*types.Map); ok {
2110 // looping once over a map is a valid pattern for
2111 // getting an arbitrary element.
2119 if len(body.List) < 2 {
2120 // avoid flagging the somewhat common pattern of using
2121 // a range loop to get the first element in a slice,
2122 // or the first rune in a string.
2125 var unconditionalExit ast.Node
2126 hasBranching := false
2127 for _, stmt := range body.List {
2128 switch stmt := stmt.(type) {
2129 case *ast.BranchStmt:
2132 if stmt.Label == nil || labels[stmt.Label.Obj] == loop {
2133 unconditionalExit = stmt
2135 case token.CONTINUE:
2136 if stmt.Label == nil || labels[stmt.Label.Obj] == loop {
2137 unconditionalExit = nil
2141 case *ast.ReturnStmt:
2142 unconditionalExit = stmt
2143 case *ast.IfStmt, *ast.ForStmt, *ast.RangeStmt, *ast.SwitchStmt, *ast.SelectStmt:
2147 if unconditionalExit == nil || !hasBranching {
2150 ast.Inspect(body, func(node ast.Node) bool {
2151 if branch, ok := node.(*ast.BranchStmt); ok {
2155 unconditionalExit = nil
2157 case token.CONTINUE:
2158 if branch.Label != nil && labels[branch.Label.Obj] != loop {
2161 unconditionalExit = nil
2167 if unconditionalExit != nil {
2168 report.Report(pass, unconditionalExit, "the surrounding loop is unconditionally terminated")
2173 code.Preorder(pass, fn, (*ast.FuncDecl)(nil), (*ast.FuncLit)(nil))
2177 var checkNilContextQ = pattern.MustParse(`(CallExpr fun@(Function _) (Builtin "nil"):_)`)
2179 func CheckNilContext(pass *analysis.Pass) (interface{}, error) {
2180 todo := &ast.CallExpr{
2181 Fun: Selector("context", "TODO"),
2183 bg := &ast.CallExpr{
2184 Fun: Selector("context", "Background"),
2186 fn := func(node ast.Node) {
2187 m, ok := Match(pass, checkNilContextQ, node)
2192 call := node.(*ast.CallExpr)
2193 fun, ok := m.State["fun"].(*types.Func)
2195 // it might also be a builtin
2198 sig := fun.Type().(*types.Signature)
2199 if sig.Params().Len() == 0 {
2200 // Our CallExpr might've matched a method expression, like
2201 // (*T).Foo(nil) – here, nil isn't the first argument of
2202 // the Foo method, but the method receiver.
2205 if !code.IsType(sig.Params().At(0).Type(), "context.Context") {
2208 report.Report(pass, call.Args[0],
2209 "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(
2210 edit.Fix("use context.TODO", edit.ReplaceWithNode(pass.Fset, call.Args[0], todo)),
2211 edit.Fix("use context.Background", edit.ReplaceWithNode(pass.Fset, call.Args[0], bg))))
2213 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
2218 checkSeekerQ = pattern.MustParse(`(CallExpr fun@(SelectorExpr _ (Ident "Seek")) [arg1@(SelectorExpr (Ident "io") (Ident (Or "SeekStart" "SeekCurrent" "SeekEnd"))) arg2])`)
2219 checkSeekerR = pattern.MustParse(`(CallExpr fun [arg2 arg1])`)
2222 func CheckSeeker(pass *analysis.Pass) (interface{}, error) {
2223 fn := func(node ast.Node) {
2224 if _, edits, ok := MatchAndEdit(pass, checkSeekerQ, checkSeekerR, node); ok {
2225 report.Report(pass, node, "the first argument of io.Seeker is the offset, but an io.Seek* constant is being used instead",
2226 report.Fixes(edit.Fix("swap arguments", edits...)))
2229 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
2233 func CheckIneffectiveAppend(pass *analysis.Pass) (interface{}, error) {
2234 isAppend := func(ins ir.Value) bool {
2235 call, ok := ins.(*ir.Call)
2239 if call.Call.IsInvoke() {
2242 if builtin, ok := call.Call.Value.(*ir.Builtin); !ok || builtin.Name() != "append" {
2248 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2249 for _, block := range fn.Blocks {
2250 for _, ins := range block.Instrs {
2251 val, ok := ins.(ir.Value)
2252 if !ok || !isAppend(val) {
2257 visited := map[ir.Instruction]bool{}
2258 var walkRefs func(refs []ir.Instruction)
2259 walkRefs = func(refs []ir.Instruction) {
2261 for _, ref := range refs {
2266 if _, ok := ref.(*ir.DebugRef); ok {
2269 switch ref := ref.(type) {
2271 walkRefs(*ref.Referrers())
2273 walkRefs(*ref.Referrers())
2278 walkRefs(*ref.Referrers())
2280 case ir.Instruction:
2287 refs := val.Referrers()
2294 report.Report(pass, ins, "this result of append is never used, except maybe in other appends")
2302 func CheckConcurrentTesting(pass *analysis.Pass) (interface{}, error) {
2303 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2304 for _, block := range fn.Blocks {
2305 for _, ins := range block.Instrs {
2306 gostmt, ok := ins.(*ir.Go)
2311 switch val := gostmt.Call.Value.(type) {
2314 case *ir.MakeClosure:
2315 fn = val.Fn.(*ir.Function)
2319 if fn.Blocks == nil {
2322 for _, block := range fn.Blocks {
2323 for _, ins := range block.Instrs {
2324 call, ok := ins.(*ir.Call)
2328 if call.Call.IsInvoke() {
2331 callee := call.Call.StaticCallee()
2335 recv := callee.Signature.Recv()
2339 if !code.IsType(recv.Type(), "*testing.common") {
2342 fn, ok := call.Call.StaticCallee().Object().(*types.Func)
2348 case "FailNow", "Fatal", "Fatalf", "SkipNow", "Skip", "Skipf":
2352 // TODO(dh): don't report multiple diagnostics
2353 // for multiple calls to T.Fatal, but do
2354 // collect all of them as related information
2355 report.Report(pass, gostmt, fmt.Sprintf("the goroutine calls T.%s, which must be called in the same goroutine as the test", name),
2356 report.Related(call, fmt.Sprintf("call to T.%s", name)))
2365 func eachCall(fn *ir.Function, cb func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function)) {
2366 for _, b := range fn.Blocks {
2367 for _, instr := range b.Instrs {
2368 if site, ok := instr.(ir.CallInstruction); ok {
2369 if g := site.Common().StaticCallee(); g != nil {
2377 func CheckCyclicFinalizer(pass *analysis.Pass) (interface{}, error) {
2378 cb := func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function) {
2379 if callee.RelString(nil) != "runtime.SetFinalizer" {
2382 arg0 := site.Common().Args[Arg("runtime.SetFinalizer.obj")]
2383 if iface, ok := arg0.(*ir.MakeInterface); ok {
2386 load, ok := arg0.(*ir.Load)
2390 v, ok := load.X.(*ir.Alloc)
2394 arg1 := site.Common().Args[Arg("runtime.SetFinalizer.finalizer")]
2395 if iface, ok := arg1.(*ir.MakeInterface); ok {
2398 mc, ok := arg1.(*ir.MakeClosure)
2402 for _, b := range mc.Bindings {
2404 pos := lint.DisplayPosition(pass.Fset, mc.Fn.Pos())
2405 report.Report(pass, site, fmt.Sprintf("the finalizer closes over the object, preventing the finalizer from ever running (at %s)", pos))
2409 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2416 func CheckSliceOutOfBounds(pass *analysis.Pass) (interface{}, error) {
2417 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2418 for _, block := range fn.Blocks {
2419 for _, ins := range block.Instrs {
2420 ia, ok := ins.(*ir.IndexAddr)
2424 if _, ok := ia.X.Type().Underlying().(*types.Slice); !ok {
2427 sr, ok1 := c.funcDescs.Get(fn).Ranges[ia.X].(vrp.SliceInterval)
2428 idxr, ok2 := c.funcDescs.Get(fn).Ranges[ia.Index].(vrp.IntInterval)
2429 if !ok1 || !ok2 || !sr.IsKnown() || !idxr.IsKnown() || sr.Length.Empty() || idxr.Empty() {
2432 if idxr.Lower.Cmp(sr.Length.Upper) >= 0 {
2433 report.Nodef(pass, ia, "index out of bounds")
2442 func CheckDeferLock(pass *analysis.Pass) (interface{}, error) {
2443 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2444 for _, block := range fn.Blocks {
2445 instrs := code.FilterDebug(block.Instrs)
2446 if len(instrs) < 2 {
2449 for i, ins := range instrs[:len(instrs)-1] {
2450 call, ok := ins.(*ir.Call)
2454 if !code.IsCallToAny(call.Common(), "(*sync.Mutex).Lock", "(*sync.RWMutex).RLock") {
2457 nins, ok := instrs[i+1].(*ir.Defer)
2461 if !code.IsCallToAny(&nins.Call, "(*sync.Mutex).Lock", "(*sync.RWMutex).RLock") {
2464 if call.Common().Args[0] != nins.Call.Args[0] {
2467 name := shortCallName(call.Common())
2475 report.Report(pass, nins, fmt.Sprintf("deferring %s right after having locked already; did you mean to defer %s?", name, alt))
2482 func CheckNaNComparison(pass *analysis.Pass) (interface{}, error) {
2483 isNaN := func(v ir.Value) bool {
2484 call, ok := v.(*ir.Call)
2488 return code.IsCallTo(call.Common(), "math.NaN")
2490 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2491 for _, block := range fn.Blocks {
2492 for _, ins := range block.Instrs {
2493 ins, ok := ins.(*ir.BinOp)
2497 if isNaN(ins.X) || isNaN(ins.Y) {
2498 report.Report(pass, ins, "no value is equal to NaN, not even NaN itself")
2506 func CheckInfiniteRecursion(pass *analysis.Pass) (interface{}, error) {
2507 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2508 eachCall(fn, func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function) {
2512 if _, ok := site.(*ir.Go); ok {
2513 // Recursively spawning goroutines doesn't consume
2514 // stack space infinitely, so don't flag it.
2518 block := site.Block()
2520 for _, b := range fn.Blocks {
2521 if block.Dominates(b) {
2524 if len(b.Instrs) == 0 {
2527 if _, ok := b.Control().(*ir.Return); ok {
2535 report.Report(pass, site, "infinite recursive call")
2541 func objectName(obj types.Object) string {
2546 if obj.Pkg() != nil && obj.Pkg().Scope().Lookup(obj.Name()) == obj {
2547 s := obj.Pkg().Path()
2556 func isName(pass *analysis.Pass, expr ast.Expr, name string) bool {
2557 var obj types.Object
2558 switch expr := expr.(type) {
2560 obj = pass.TypesInfo.ObjectOf(expr)
2561 case *ast.SelectorExpr:
2562 obj = pass.TypesInfo.ObjectOf(expr.Sel)
2564 return objectName(obj) == name
2567 func CheckLeakyTimeTick(pass *analysis.Pass) (interface{}, error) {
2568 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2569 if code.IsMainLike(pass) || code.IsInTest(pass, fn) {
2572 for _, block := range fn.Blocks {
2573 for _, ins := range block.Instrs {
2574 call, ok := ins.(*ir.Call)
2575 if !ok || !code.IsCallTo(call.Common(), "time.Tick") {
2578 if !functions.Terminates(call.Parent()) {
2581 report.Report(pass, call, "using time.Tick leaks the underlying ticker, consider using it only in endless functions, tests and the main package, and use time.NewTicker here")
2588 var checkDoubleNegationQ = pattern.MustParse(`(UnaryExpr "!" single@(UnaryExpr "!" x))`)
2590 func CheckDoubleNegation(pass *analysis.Pass) (interface{}, error) {
2591 fn := func(node ast.Node) {
2592 if m, ok := Match(pass, checkDoubleNegationQ, node); ok {
2593 report.Report(pass, node, "negating a boolean twice has no effect; is this a typo?", report.Fixes(
2594 edit.Fix("turn into single negation", edit.ReplaceWithNode(pass.Fset, node, m.State["single"].(ast.Node))),
2595 edit.Fix("remove double negation", edit.ReplaceWithNode(pass.Fset, node, m.State["x"].(ast.Node)))))
2598 code.Preorder(pass, fn, (*ast.UnaryExpr)(nil))
2602 func CheckRepeatedIfElse(pass *analysis.Pass) (interface{}, error) {
2603 seen := map[ast.Node]bool{}
2605 var collectConds func(ifstmt *ast.IfStmt, conds []ast.Expr) ([]ast.Expr, bool)
2606 collectConds = func(ifstmt *ast.IfStmt, conds []ast.Expr) ([]ast.Expr, bool) {
2608 // Bail if any if-statement has an Init statement or side effects in its condition
2609 if ifstmt.Init != nil {
2612 if code.MayHaveSideEffects(pass, ifstmt.Cond, nil) {
2616 conds = append(conds, ifstmt.Cond)
2617 if elsestmt, ok := ifstmt.Else.(*ast.IfStmt); ok {
2618 return collectConds(elsestmt, conds)
2622 fn := func(node ast.Node) {
2623 ifstmt := node.(*ast.IfStmt)
2625 // this if-statement is part of an if/else-if chain that we've already processed
2628 if ifstmt.Else == nil {
2629 // there can be at most one condition
2632 conds, ok := collectConds(ifstmt, nil)
2639 counts := map[string]int{}
2640 for _, cond := range conds {
2641 s := report.Render(pass, cond)
2644 report.Report(pass, cond, "this condition occurs multiple times in this if/else if chain")
2648 code.Preorder(pass, fn, (*ast.IfStmt)(nil))
2652 func CheckSillyBitwiseOps(pass *analysis.Pass) (interface{}, error) {
2653 // FIXME(dh): what happened here?
2655 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2656 for _, block := range fn.Blocks {
2657 for _, ins := range block.Instrs {
2658 ins, ok := ins.(*ir.BinOp)
2663 if c, ok := ins.Y.(*ir.Const); !ok || c.Value == nil || c.Value.Kind() != constant.Int || c.Uint64() != 0 {
2667 case token.AND, token.OR, token.XOR:
2669 // we do not flag shifts because too often, x<<0 is part
2670 // of a pattern, x<<0, x<<8, x<<16, ...
2673 path, _ := astutil.PathEnclosingInterval(code.File(pass, ins), ins.Pos(), ins.Pos())
2678 if node, ok := path[0].(*ast.BinaryExpr); !ok || !code.IsIntLiteral(node.Y, "0") {
2684 report.Report(pass, ins, "x & 0 always equals 0")
2685 case token.OR, token.XOR:
2686 report.Report(pass, ins, fmt.Sprintf("x %s 0 always equals x", ins.Op))
2692 fn := func(node ast.Node) {
2693 binop := node.(*ast.BinaryExpr)
2694 b, ok := pass.TypesInfo.TypeOf(binop).Underlying().(*types.Basic)
2698 if (b.Info() & types.IsInteger) == 0 {
2702 case token.AND, token.OR, token.XOR:
2704 // we do not flag shifts because too often, x<<0 is part
2705 // of a pattern, x<<0, x<<8, x<<16, ...
2708 switch y := binop.Y.(type) {
2710 obj, ok := pass.TypesInfo.ObjectOf(y).(*types.Const)
2714 if v, _ := constant.Int64Val(obj.Val()); v != 0 {
2717 path, _ := astutil.PathEnclosingInterval(code.File(pass, obj), obj.Pos(), obj.Pos())
2721 spec, ok := path[1].(*ast.ValueSpec)
2725 if len(spec.Names) != 1 || len(spec.Values) != 1 {
2726 // TODO(dh): we could support this
2729 ident, ok := spec.Values[0].(*ast.Ident)
2733 if !isIota(pass.TypesInfo.ObjectOf(ident)) {
2738 report.Report(pass, node,
2739 fmt.Sprintf("%s always equals 0; %s is defined as iota and has value 0, maybe %s is meant to be 1 << iota?", report.Render(pass, binop), report.Render(pass, binop.Y), report.Render(pass, binop.Y)))
2740 case token.OR, token.XOR:
2741 report.Report(pass, node,
2742 fmt.Sprintf("%s always equals %s; %s is defined as iota and has value 0, maybe %s is meant to be 1 << iota?", report.Render(pass, binop), report.Render(pass, binop.X), report.Render(pass, binop.Y), report.Render(pass, binop.Y)))
2745 if !code.IsIntLiteral(binop.Y, "0") {
2750 report.Report(pass, node, fmt.Sprintf("%s always equals 0", report.Render(pass, binop)))
2751 case token.OR, token.XOR:
2752 report.Report(pass, node, fmt.Sprintf("%s always equals %s", report.Render(pass, binop), report.Render(pass, binop.X)))
2758 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil))
2762 func isIota(obj types.Object) bool {
2763 if obj.Name() != "iota" {
2766 c, ok := obj.(*types.Const)
2770 return c.Pkg() == nil
2773 func CheckNonOctalFileMode(pass *analysis.Pass) (interface{}, error) {
2774 fn := func(node ast.Node) {
2775 call := node.(*ast.CallExpr)
2776 sig, ok := pass.TypesInfo.TypeOf(call.Fun).(*types.Signature)
2780 n := sig.Params().Len()
2781 for i := 0; i < n; i++ {
2782 typ := sig.Params().At(i).Type()
2783 if !code.IsType(typ, "os.FileMode") {
2787 lit, ok := call.Args[i].(*ast.BasicLit)
2791 if len(lit.Value) == 3 &&
2792 lit.Value[0] != '0' &&
2793 lit.Value[0] >= '0' && lit.Value[0] <= '7' &&
2794 lit.Value[1] >= '0' && lit.Value[1] <= '7' &&
2795 lit.Value[2] >= '0' && lit.Value[2] <= '7' {
2797 v, err := strconv.ParseInt(lit.Value, 10, 64)
2801 report.Report(pass, call.Args[i], fmt.Sprintf("file mode '%s' evaluates to %#o; did you mean '0%s'?", lit.Value, v, lit.Value),
2802 report.Fixes(edit.Fix("fix octal literal", edit.ReplaceWithString(pass.Fset, call.Args[i], "0"+lit.Value))))
2806 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
2810 func CheckPureFunctions(pass *analysis.Pass) (interface{}, error) {
2811 pure := pass.ResultOf[facts.Purity].(facts.PurityResult)
2814 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2815 if code.IsInTest(pass, fn) {
2816 params := fn.Signature.Params()
2817 for i := 0; i < params.Len(); i++ {
2818 param := params.At(i)
2819 if code.IsType(param.Type(), "*testing.B") {
2820 // Ignore discarded pure functions in code related
2821 // to benchmarks. Instead of matching BenchmarkFoo
2822 // functions, we match any function accepting a
2823 // *testing.B. Benchmarks sometimes call generic
2824 // functions for doing the actual work, and
2825 // checking for the parameter is a lot easier and
2826 // faster than analyzing call trees.
2832 for _, b := range fn.Blocks {
2833 for _, ins := range b.Instrs {
2834 ins, ok := ins.(*ir.Call)
2838 refs := ins.Referrers()
2839 if refs == nil || len(code.FilterDebug(*refs)) > 0 {
2843 callee := ins.Common().StaticCallee()
2847 if callee.Object() == nil {
2848 // TODO(dh): support anonymous functions
2851 if _, ok := pure[callee.Object().(*types.Func)]; ok {
2852 if pass.Pkg.Path() == "fmt_test" && callee.Object().(*types.Func).FullName() == "fmt.Sprintf" {
2853 // special case for benchmarks in the fmt package
2856 report.Report(pass, ins, fmt.Sprintf("%s is a pure function but its return value is ignored", callee.Name()))
2864 func CheckDeprecated(pass *analysis.Pass) (interface{}, error) {
2865 deprs := pass.ResultOf[facts.Deprecated].(facts.DeprecatedResult)
2867 // Selectors can appear outside of function literals, e.g. when
2868 // declaring package level variables.
2870 var tfn types.Object
2872 fn := func(node ast.Node, push bool) bool {
2881 if fn, ok := node.(*ast.FuncDecl); ok {
2882 tfn = pass.TypesInfo.ObjectOf(fn.Name)
2884 sel, ok := node.(*ast.SelectorExpr)
2889 obj := pass.TypesInfo.ObjectOf(sel.Sel)
2890 if obj.Pkg() == nil {
2893 if pass.Pkg == obj.Pkg() || obj.Pkg().Path()+"_test" == pass.Pkg.Path() {
2894 // Don't flag stuff in our own package
2897 if depr, ok := deprs.Objects[obj]; ok {
2898 // Look for the first available alternative, not the first
2899 // version something was deprecated in. If a function was
2900 // deprecated in Go 1.6, an alternative has been available
2901 // already in 1.0, and we're targeting 1.2, it still
2902 // makes sense to use the alternative from 1.0, to be
2904 minVersion := deprecated.Stdlib[code.SelectorName(pass, sel)].AlternativeAvailableSince
2905 if !code.IsGoVersion(pass, minVersion) {
2910 if _, ok := deprs.Objects[tfn]; ok {
2911 // functions that are deprecated may use deprecated
2916 report.Report(pass, sel, fmt.Sprintf("%s is deprecated: %s", report.Render(pass, sel), depr.Msg))
2922 fn2 := func(node ast.Node) {
2923 spec := node.(*ast.ImportSpec)
2924 var imp *types.Package
2925 if spec.Name != nil {
2926 imp = pass.TypesInfo.ObjectOf(spec.Name).(*types.PkgName).Imported()
2928 imp = pass.TypesInfo.Implicits[spec].(*types.PkgName).Imported()
2931 p := spec.Path.Value
2932 path := p[1 : len(p)-1]
2933 if depr, ok := deprs.Packages[imp]; ok {
2934 if path == "github.com/golang/protobuf/proto" {
2935 gen, ok := code.Generator(pass, spec.Path.Pos())
2936 if ok && gen == facts.ProtocGenGo {
2940 report.Report(pass, spec, fmt.Sprintf("package %s is deprecated: %s", path, depr.Msg))
2943 pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes(nil, fn)
2944 code.Preorder(pass, fn2, (*ast.ImportSpec)(nil))
2948 func callChecker(rules map[string]CallCheck) func(pass *analysis.Pass) (interface{}, error) {
2949 return func(pass *analysis.Pass) (interface{}, error) {
2950 return checkCalls(pass, rules)
2954 func checkCalls(pass *analysis.Pass, rules map[string]CallCheck) (interface{}, error) {
2955 cb := func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function) {
2956 obj, ok := callee.Object().(*types.Func)
2961 r, ok := rules[lint.FuncName(obj)]
2965 var args []*Argument
2966 irargs := site.Common().Args
2967 if callee.Signature.Recv() != nil {
2970 for _, arg := range irargs {
2971 if iarg, ok := arg.(*ir.MakeInterface); ok {
2974 args = append(args, &Argument{Value: Value{arg}})
2980 Parent: site.Parent(),
2983 path, _ := astutil.PathEnclosingInterval(code.File(pass, site), site.Pos(), site.Pos())
2984 var astcall *ast.CallExpr
2985 for _, el := range path {
2986 if expr, ok := el.(*ast.CallExpr); ok {
2991 for idx, arg := range call.Args {
2992 for _, e := range arg.invalids {
2994 report.Report(pass, astcall.Args[idx], e)
2996 report.Report(pass, site, e)
3000 for _, e := range call.invalids {
3001 report.Report(pass, call.Instr, e)
3004 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
3010 func shortCallName(call *ir.CallCommon) string {
3011 if call.IsInvoke() {
3014 switch v := call.Value.(type) {
3016 fn, ok := v.Object().(*types.Func)
3027 func CheckWriterBufferModified(pass *analysis.Pass) (interface{}, error) {
3028 // TODO(dh): this might be a good candidate for taint analysis.
3029 // Taint the argument as MUST_NOT_MODIFY, then propagate that
3030 // through functions like bytes.Split
3032 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
3034 if fn.Name() != "Write" || sig.Recv() == nil || sig.Params().Len() != 1 || sig.Results().Len() != 2 {
3037 tArg, ok := sig.Params().At(0).Type().(*types.Slice)
3041 if basic, ok := tArg.Elem().(*types.Basic); !ok || basic.Kind() != types.Byte {
3044 if basic, ok := sig.Results().At(0).Type().(*types.Basic); !ok || basic.Kind() != types.Int {
3047 if named, ok := sig.Results().At(1).Type().(*types.Named); !ok || !code.IsType(named, "error") {
3051 for _, block := range fn.Blocks {
3052 for _, ins := range block.Instrs {
3053 switch ins := ins.(type) {
3055 addr, ok := ins.Addr.(*ir.IndexAddr)
3059 if addr.X != fn.Params[1] {
3062 report.Report(pass, ins, "io.Writer.Write must not modify the provided buffer, not even temporarily")
3064 if !code.IsCallTo(ins.Common(), "append") {
3067 if ins.Common().Args[0] != fn.Params[1] {
3070 report.Report(pass, ins, "io.Writer.Write must not modify the provided buffer, not even temporarily")
3078 func loopedRegexp(name string) CallCheck {
3079 return func(call *Call) {
3080 if len(extractConsts(call.Args[0].Value.Value)) == 0 {
3083 if !isInLoop(call.Instr.Block()) {
3086 call.Invalid(fmt.Sprintf("calling %s in a loop has poor performance, consider using regexp.Compile", name))
3090 func CheckEmptyBranch(pass *analysis.Pass) (interface{}, error) {
3091 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
3092 if fn.Source() == nil {
3095 if code.IsExample(fn) {
3098 cb := func(node ast.Node) bool {
3099 ifstmt, ok := node.(*ast.IfStmt)
3103 if ifstmt.Else != nil {
3104 b, ok := ifstmt.Else.(*ast.BlockStmt)
3105 if !ok || len(b.List) != 0 {
3108 report.Report(pass, ifstmt.Else, "empty branch", report.FilterGenerated(), report.ShortRange())
3110 if len(ifstmt.Body.List) != 0 {
3113 report.Report(pass, ifstmt, "empty branch", report.FilterGenerated(), report.ShortRange())
3116 Inspect(fn.Source(), cb)
3121 func CheckMapBytesKey(pass *analysis.Pass) (interface{}, error) {
3122 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
3123 for _, b := range fn.Blocks {
3125 for _, ins := range b.Instrs {
3126 // find []byte -> string conversions
3127 conv, ok := ins.(*ir.Convert)
3128 if !ok || conv.Type() != types.Universe.Lookup("string").Type() {
3131 if s, ok := conv.X.Type().(*types.Slice); !ok || s.Elem() != types.Universe.Lookup("byte").Type() {
3134 refs := conv.Referrers()
3135 // need at least two (DebugRef) references: the
3136 // conversion and the *ast.Ident
3137 if refs == nil || len(*refs) < 2 {
3141 // skip first reference, that's the conversion itself
3142 for _, ref := range (*refs)[1:] {
3143 switch ref := ref.(type) {
3145 if _, ok := ref.Expr.(*ast.Ident); !ok {
3146 // the string seems to be used somewhere
3147 // unexpected; the default branch should
3148 // catch this already, but be safe
3155 // the string is used somewhere else than a
3161 // the result of the conversion wasn't assigned to an
3166 report.Report(pass, conv, "m[string(key)] would be more efficient than k := string(key); m[k]")
3173 func CheckRangeStringRunes(pass *analysis.Pass) (interface{}, error) {
3174 return sharedcheck.CheckRangeStringRunes(pass)
3177 func CheckSelfAssignment(pass *analysis.Pass) (interface{}, error) {
3178 pure := pass.ResultOf[facts.Purity].(facts.PurityResult)
3180 fn := func(node ast.Node) {
3181 assign := node.(*ast.AssignStmt)
3182 if assign.Tok != token.ASSIGN || len(assign.Lhs) != len(assign.Rhs) {
3185 for i, lhs := range assign.Lhs {
3186 rhs := assign.Rhs[i]
3187 if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) {
3190 if code.MayHaveSideEffects(pass, lhs, pure) || code.MayHaveSideEffects(pass, rhs, pure) {
3194 rlh := report.Render(pass, lhs)
3195 rrh := report.Render(pass, rhs)
3197 report.Report(pass, assign, fmt.Sprintf("self-assignment of %s to %s", rrh, rlh), report.FilterGenerated())
3201 code.Preorder(pass, fn, (*ast.AssignStmt)(nil))
3205 func buildTagsIdentical(s1, s2 []string) bool {
3206 if len(s1) != len(s2) {
3209 s1s := make([]string, len(s1))
3212 s2s := make([]string, len(s2))
3215 for i, s := range s1s {
3223 func CheckDuplicateBuildConstraints(pass *analysis.Pass) (interface{}, error) {
3224 for _, f := range pass.Files {
3225 constraints := buildTags(f)
3226 for i, constraint1 := range constraints {
3227 for j, constraint2 := range constraints {
3231 if buildTagsIdentical(constraint1, constraint2) {
3232 msg := fmt.Sprintf("identical build constraints %q and %q",
3233 strings.Join(constraint1, " "),
3234 strings.Join(constraint2, " "))
3235 report.Report(pass, f, msg, report.FilterGenerated(), report.ShortRange())
3243 func CheckSillyRegexp(pass *analysis.Pass) (interface{}, error) {
3244 // We could use the rule checking engine for this, but the
3245 // arguments aren't really invalid.
3246 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
3247 for _, b := range fn.Blocks {
3248 for _, ins := range b.Instrs {
3249 call, ok := ins.(*ir.Call)
3253 if !code.IsCallToAny(call.Common(), "regexp.MustCompile", "regexp.Compile", "regexp.Match", "regexp.MatchReader", "regexp.MatchString") {
3256 c, ok := call.Common().Args[0].(*ir.Const)
3260 s := constant.StringVal(c.Value)
3261 re, err := syntax.Parse(s, 0)
3265 if re.Op != syntax.OpLiteral && re.Op != syntax.OpEmptyMatch {
3268 report.Report(pass, call, "regular expression does not contain any meta characters")
3275 func CheckMissingEnumTypesInDeclaration(pass *analysis.Pass) (interface{}, error) {
3276 fn := func(node ast.Node) {
3277 decl := node.(*ast.GenDecl)
3278 if !decl.Lparen.IsValid() {
3281 if decl.Tok != token.CONST {
3285 groups := code.GroupSpecs(pass.Fset, decl.Specs)
3287 for _, group := range groups {
3291 if group[0].(*ast.ValueSpec).Type == nil {
3292 // first constant doesn't have a type
3295 for i, spec := range group {
3296 spec := spec.(*ast.ValueSpec)
3297 if len(spec.Names) != 1 || len(spec.Values) != 1 {
3300 switch v := spec.Values[0].(type) {
3302 case *ast.UnaryExpr:
3303 if _, ok := v.X.(*ast.BasicLit); !ok {
3307 // if it's not a literal it might be typed, such as
3308 // time.Microsecond = 1000 * Nanosecond
3314 if spec.Type != nil {
3318 var edits []analysis.TextEdit
3319 typ := group[0].(*ast.ValueSpec).Type
3320 for _, spec := range group[1:] {
3321 nspec := *spec.(*ast.ValueSpec)
3323 edits = append(edits, edit.ReplaceWithNode(pass.Fset, spec, &nspec))
3325 report.Report(pass, group[0], "only the first constant in this group has an explicit type", report.Fixes(edit.Fix("add type to all constants in group", edits...)))
3328 code.Preorder(pass, fn, (*ast.GenDecl)(nil))
3332 func CheckTimerResetReturnValue(pass *analysis.Pass) (interface{}, error) {
3333 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
3334 for _, block := range fn.Blocks {
3335 for _, ins := range block.Instrs {
3336 call, ok := ins.(*ir.Call)
3340 if !code.IsCallTo(call.Common(), "(*time.Timer).Reset") {
3343 refs := call.Referrers()
3347 for _, ref := range code.FilterDebug(*refs) {
3348 ifstmt, ok := ref.(*ir.If)
3354 for _, succ := range ifstmt.Block().Succs {
3355 if len(succ.Preds) != 1 {
3356 // Merge point, not a branch in the
3357 // syntactical sense.
3359 // FIXME(dh): this is broken for if
3360 // statements a la "if x || y"
3363 irutil.Walk(succ, func(b *ir.BasicBlock) bool {
3364 if !succ.Dominates(b) {
3365 // We've reached the end of the branch
3368 for _, ins := range b.Instrs {
3369 // TODO(dh): we should check that
3370 // we're receiving from the channel of
3371 // a time.Timer to further reduce
3372 // false positives. Not a key
3373 // priority, considering the rarity of
3374 // Reset and the tiny likeliness of a
3376 if ins, ok := ins.(*ir.Recv); ok && code.IsType(ins.Chan.Type(), "<-chan time.Time") {
3386 report.Report(pass, call, "it is not possible to use Reset's return value correctly, as there is a race condition between draining the channel and the new timer expiring")
3396 checkToLowerToUpperComparisonQ = pattern.MustParse(`
3398 (CallExpr fun@(Function (Or "strings.ToLower" "strings.ToUpper")) [a])
3400 (CallExpr fun [b]))`)
3401 checkToLowerToUpperComparisonR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "strings") (Ident "EqualFold")) [a b])`)
3404 func CheckToLowerToUpperComparison(pass *analysis.Pass) (interface{}, error) {
3405 fn := func(node ast.Node) {
3406 m, ok := Match(pass, checkToLowerToUpperComparisonQ, node)
3410 rn := pattern.NodeToAST(checkToLowerToUpperComparisonR.Root, m.State).(ast.Expr)
3411 if m.State["tok"].(token.Token) == token.NEQ {
3412 rn = &ast.UnaryExpr{
3418 report.Report(pass, node, "should use strings.EqualFold instead", report.Fixes(edit.Fix("replace with strings.EqualFold", edit.ReplaceWithNode(pass.Fset, node, rn))))
3421 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil))
3425 func CheckUnreachableTypeCases(pass *analysis.Pass) (interface{}, error) {
3426 // Check if T subsumes V in a type switch. T subsumes V if T is an interface and T's method set is a subset of V's method set.
3427 subsumes := func(T, V types.Type) bool {
3428 tIface, ok := T.Underlying().(*types.Interface)
3433 return types.Implements(V, tIface)
3436 subsumesAny := func(Ts, Vs []types.Type) (types.Type, types.Type, bool) {
3437 for _, T := range Ts {
3438 for _, V := range Vs {
3445 return nil, nil, false
3448 fn := func(node ast.Node) {
3449 tsStmt := node.(*ast.TypeSwitchStmt)
3451 type ccAndTypes struct {
3456 // All asserted types in the order of case clauses.
3457 ccs := make([]ccAndTypes, 0, len(tsStmt.Body.List))
3458 for _, stmt := range tsStmt.Body.List {
3459 cc, _ := stmt.(*ast.CaseClause)
3461 // Exclude the 'default' case.
3462 if len(cc.List) == 0 {
3466 Ts := make([]types.Type, len(cc.List))
3467 for i, expr := range cc.List {
3468 Ts[i] = pass.TypesInfo.TypeOf(expr)
3471 ccs = append(ccs, ccAndTypes{cc: cc, types: Ts})
3475 // Zero or one case clauses, nothing to check.
3479 // Check if case clauses following cc have types that are subsumed by cc.
3480 for i, cc := range ccs[:len(ccs)-1] {
3481 for _, next := range ccs[i+1:] {
3482 if T, V, yes := subsumesAny(cc.types, next.types); yes {
3483 report.Report(pass, next.cc, fmt.Sprintf("unreachable case clause: %s will always match before %s", T.String(), V.String()),
3484 report.ShortRange())
3490 code.Preorder(pass, fn, (*ast.TypeSwitchStmt)(nil))
3494 var checkSingleArgAppendQ = pattern.MustParse(`(CallExpr (Builtin "append") [_])`)
3496 func CheckSingleArgAppend(pass *analysis.Pass) (interface{}, error) {
3497 fn := func(node ast.Node) {
3498 _, ok := Match(pass, checkSingleArgAppendQ, node)
3502 report.Report(pass, node, "x = append(y) is equivalent to x = y", report.FilterGenerated())
3504 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
3508 func CheckStructTags(pass *analysis.Pass) (interface{}, error) {
3509 importsGoFlags := false
3511 // we use the AST instead of (*types.Package).Imports to work
3512 // around vendored packages in GOPATH mode. A vendored package's
3513 // path will include the vendoring subtree as a prefix.
3514 for _, f := range pass.Files {
3515 for _, imp := range f.Imports {
3517 if v[1:len(v)-1] == "github.com/jessevdk/go-flags" {
3518 importsGoFlags = true
3524 fn := func(node ast.Node) {
3525 for _, field := range node.(*ast.StructType).Fields.List {
3526 if field.Tag == nil {
3529 tags, err := parseStructTag(field.Tag.Value[1 : len(field.Tag.Value)-1])
3531 report.Report(pass, field.Tag, fmt.Sprintf("unparseable struct tag: %s", err))
3534 for k, v := range tags {
3536 isGoFlagsTag := importsGoFlags &&
3537 (k == "choice" || k == "optional-value" || k == "default")
3539 report.Report(pass, field.Tag, fmt.Sprintf("duplicate struct tag %q", k))
3545 checkJSONTag(pass, field, v[0])
3547 checkXMLTag(pass, field, v[0])
3552 code.Preorder(pass, fn, (*ast.StructType)(nil))
3556 func checkJSONTag(pass *analysis.Pass, field *ast.Field, tag string) {
3557 if pass.Pkg.Path() == "encoding/json" || pass.Pkg.Path() == "encoding/json_test" {
3558 // don't flag malformed JSON tags in the encoding/json
3559 // package; it knows what it is doing, and it is testing
3563 //lint:ignore SA9003 TODO(dh): should we flag empty tags?
3566 fields := strings.Split(tag, ",")
3567 for _, r := range fields[0] {
3568 if !unicode.IsLetter(r) && !unicode.IsDigit(r) && !strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", r) {
3569 report.Report(pass, field.Tag, fmt.Sprintf("invalid JSON field name %q", fields[0]))
3573 for _, s := range fields[1:] {
3578 // allow stuff like "-,"
3581 // only for string, floating point, integer and bool
3582 T := code.Dereference(pass.TypesInfo.TypeOf(field.Type).Underlying()).Underlying()
3583 basic, ok := T.(*types.Basic)
3584 if !ok || (basic.Info()&(types.IsBoolean|types.IsInteger|types.IsFloat|types.IsString)) == 0 {
3585 report.Report(pass, field.Tag, "the JSON string option only applies to fields of type string, floating point, integer or bool, or pointers to those")
3590 report.Report(pass, field.Tag, fmt.Sprintf("unknown JSON option %q", s))
3594 report.Report(pass, field.Tag, `duplicate JSON option "omitempty"`)
3597 report.Report(pass, field.Tag, `duplicate JSON option "string"`)
3600 report.Report(pass, field.Tag, `duplicate JSON option "inline"`)
3604 func checkXMLTag(pass *analysis.Pass, field *ast.Field, tag string) {
3605 //lint:ignore SA9003 TODO(dh): should we flag empty tags?
3608 fields := strings.Split(tag, ",")
3609 counts := map[string]int{}
3610 var exclusives []string
3611 for _, s := range fields[1:] {
3613 case "attr", "chardata", "cdata", "innerxml", "comment":
3616 exclusives = append(exclusives, s)
3618 case "omitempty", "any":
3622 report.Report(pass, field.Tag, fmt.Sprintf("unknown XML option %q", s))
3625 for k, v := range counts {
3627 report.Report(pass, field.Tag, fmt.Sprintf("duplicate XML option %q", k))
3630 if len(exclusives) > 1 {
3631 report.Report(pass, field.Tag, fmt.Sprintf("XML options %s are mutually exclusive", strings.Join(exclusives, " and ")))
3635 func CheckImpossibleTypeAssertion(pass *analysis.Pass) (interface{}, error) {
3640 msc := &pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg.Prog.MethodSets
3641 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
3642 for _, b := range fn.Blocks {
3643 for _, instr := range b.Instrs {
3644 assert, ok := instr.(*ir.TypeAssert)
3649 left := assert.X.Type()
3650 right := assert.AssertedType
3651 righti, ok := right.Underlying().(*types.Interface)
3654 // We only care about interface->interface
3655 // assertions. The Go compiler already catches
3656 // impossible interface->concrete assertions.
3660 ms := msc.MethodSet(left)
3661 for i := 0; i < righti.NumMethods(); i++ {
3662 mr := righti.Method(i)
3663 sel := ms.Lookup(mr.Pkg(), mr.Name())
3667 ml := sel.Obj().(*types.Func)
3668 if types.AssignableTo(ml.Type(), mr.Type()) {
3672 wrong = append(wrong, entry{ml, mr})
3675 if len(wrong) != 0 {
3676 s := fmt.Sprintf("impossible type assertion; %s and %s contradict each other:",
3677 types.TypeString(left, types.RelativeTo(pass.Pkg)),
3678 types.TypeString(right, types.RelativeTo(pass.Pkg)))
3679 for _, e := range wrong {
3680 s += fmt.Sprintf("\n\twrong type for %s method", e.l.Name())
3681 s += fmt.Sprintf("\n\t\thave %s", e.l.Type())
3682 s += fmt.Sprintf("\n\t\twant %s", e.r.Type())
3684 report.Report(pass, assert, s)
3692 func checkWithValueKey(call *Call) {
3694 T := arg.Value.Value.Type()
3695 if T, ok := T.(*types.Basic); ok {
3697 fmt.Sprintf("should not use built-in type %s as key for value; define your own type to avoid collisions", T))
3699 if !types.Comparable(T) {
3700 arg.Invalid(fmt.Sprintf("keys used with context.WithValue must be comparable, but type %s is not comparable", T))
3704 func CheckMaybeNil(pass *analysis.Pass) (interface{}, error) {
3705 // This is an extremely trivial check that doesn't try to reason
3706 // about control flow. That is, phis and sigmas do not propagate
3707 // any information. As such, we can flag this:
3710 // if x == nil { return }
3712 // but we cannot flag this:
3714 // if x == nil { println(x) }
3717 // nor many other variations of conditional uses of or assignments to x.
3719 // However, even this trivial implementation finds plenty of
3720 // real-world bugs, such as dereference before nil pointer check,
3721 // or using t.Error instead of t.Fatal when encountering nil
3724 // On the flip side, our naive implementation avoids false positives in branches, such as
3726 // if x != nil { _ = *x }
3728 // due to the same lack of propagating information through sigma
3729 // nodes. x inside the branch will be independent of the x in the
3730 // nil pointer check.
3733 // We could implement a more powerful check, but then we'd be
3734 // getting false positives instead of false negatives because
3735 // we're incapable of deducing relationships between variables.
3736 // For example, a function might return a pointer and an error,
3737 // and the error being nil guarantees that the pointer is not nil.
3738 // Depending on the surrounding code, the pointer may still end up
3739 // being checked against nil in one place, and guarded by a check
3740 // on the error in another, which would lead to us marking some
3743 // Unfortunately, simply hard-coding the relationship between
3744 // return values wouldn't eliminate all false positives, either.
3745 // Many other more subtle relationships exist. An abridged example
3748 // if a == nil && b == nil { return }
3750 // if c != "" { _ = *a }
3752 // where `fn` is guaranteed to return a non-empty string if a
3755 // We choose to err on the side of false negatives.
3757 isNilConst := func(v ir.Value) bool {
3758 if code.IsPointerLike(v.Type()) {
3759 if k, ok := v.(*ir.Const); ok {
3766 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
3767 maybeNil := map[ir.Value]ir.Instruction{}
3768 for _, b := range fn.Blocks {
3769 for _, instr := range b.Instrs {
3770 if instr, ok := instr.(*ir.BinOp); ok {
3772 if isNilConst(instr.X) {
3774 } else if isNilConst(instr.Y) {
3777 maybeNil[ptr] = instr
3782 for _, b := range fn.Blocks {
3783 for _, instr := range b.Instrs {
3785 switch instr := instr.(type) {
3797 case *ir.Alloc, *ir.FieldAddr, *ir.IndexAddr:
3798 // these cannot be nil
3801 if r, ok := maybeNil[ptr]; ok {
3802 report.Report(pass, instr, "possible nil pointer dereference",
3803 report.Related(r, "this check suggests that the pointer can be nil"))
3813 var checkAddressIsNilQ = pattern.MustParse(
3819 func CheckAddressIsNil(pass *analysis.Pass) (interface{}, error) {
3820 fn := func(node ast.Node) {
3821 _, ok := Match(pass, checkAddressIsNilQ, node)
3825 report.Report(pass, node, "the address of a variable cannot be nil")
3827 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil))