1 // Package staticcheck contains a linter for Go source code.
10 htmltemplate "html/template"
18 texttemplate "text/template"
21 "honnef.co/go/tools/analysis/code"
22 "honnef.co/go/tools/analysis/edit"
23 "honnef.co/go/tools/analysis/facts"
24 "honnef.co/go/tools/analysis/facts/nilness"
25 "honnef.co/go/tools/analysis/facts/typedness"
26 "honnef.co/go/tools/analysis/lint"
27 "honnef.co/go/tools/analysis/report"
28 "honnef.co/go/tools/go/ast/astutil"
29 "honnef.co/go/tools/go/ir"
30 "honnef.co/go/tools/go/ir/irutil"
31 "honnef.co/go/tools/go/types/typeutil"
32 "honnef.co/go/tools/internal/passes/buildir"
33 "honnef.co/go/tools/internal/sharedcheck"
34 "honnef.co/go/tools/knowledge"
35 "honnef.co/go/tools/pattern"
36 "honnef.co/go/tools/printf"
38 "golang.org/x/tools/go/analysis"
39 "golang.org/x/tools/go/analysis/passes/inspect"
40 goastutil "golang.org/x/tools/go/ast/astutil"
41 "golang.org/x/tools/go/ast/inspector"
42 gotypeutil "golang.org/x/tools/go/types/typeutil"
45 func checkSortSlice(call *Call) {
46 c := call.Instr.Common().StaticCallee()
49 T := arg.Value.Value.Type().Underlying()
51 case *types.Interface:
53 // TODO(dh): if the value is a phi node we can look at its edges
54 if k, ok := arg.Value.Value.(*ir.Const); ok && k.Value == nil {
55 // literal nil, e.g. sort.Sort(nil, ...)
56 arg.Invalid(fmt.Sprintf("cannot call %s on nil literal", c))
62 arg.Invalid(fmt.Sprintf("%s must only be called on slices, was called on %s", c, T))
66 func validRegexp(call *Call) {
68 err := ValidateRegexp(arg.Value)
70 arg.Invalid(err.Error())
76 func (rs runeSlice) Len() int { return len(rs) }
77 func (rs runeSlice) Less(i int, j int) bool { return rs[i] < rs[j] }
78 func (rs runeSlice) Swap(i int, j int) { rs[i], rs[j] = rs[j], rs[i] }
80 func utf8Cutset(call *Call) {
82 if InvalidUTF8(arg.Value) {
83 arg.Invalid(MsgInvalidUTF8)
87 func uniqueCutset(call *Call) {
89 if !UniqueStringCutset(arg.Value) {
90 arg.Invalid(MsgNonUniqueCutset)
94 func unmarshalPointer(name string, arg int) CallCheck {
95 return func(call *Call) {
96 if !Pointer(call.Args[arg].Value) {
97 call.Args[arg].Invalid(fmt.Sprintf("%s expects to unmarshal into a pointer, but the provided value is not a pointer", name))
102 func pointlessIntMath(call *Call) {
103 if ConvertedFromInt(call.Args[0].Value) {
104 call.Invalid(fmt.Sprintf("calling %s on a converted integer is pointless", irutil.CallName(call.Instr.Common())))
108 func checkValidHostPort(arg int) CallCheck {
109 return func(call *Call) {
110 if !ValidHostPort(call.Args[arg].Value) {
111 call.Args[arg].Invalid(MsgInvalidHostPort)
117 checkRegexpRules = map[string]CallCheck{
118 "regexp.MustCompile": validRegexp,
119 "regexp.Compile": validRegexp,
120 "regexp.Match": validRegexp,
121 "regexp.MatchReader": validRegexp,
122 "regexp.MatchString": validRegexp,
125 checkTimeParseRules = map[string]CallCheck{
126 "time.Parse": func(call *Call) {
127 arg := call.Args[knowledge.Arg("time.Parse.layout")]
128 err := ValidateTimeLayout(arg.Value)
130 arg.Invalid(err.Error())
135 checkEncodingBinaryRules = map[string]CallCheck{
136 "encoding/binary.Write": func(call *Call) {
137 arg := call.Args[knowledge.Arg("encoding/binary.Write.data")]
138 if !CanBinaryMarshal(call.Pass, arg.Value) {
139 arg.Invalid(fmt.Sprintf("value of type %s cannot be used with binary.Write", arg.Value.Value.Type()))
144 checkURLsRules = map[string]CallCheck{
145 "net/url.Parse": func(call *Call) {
146 arg := call.Args[knowledge.Arg("net/url.Parse.rawurl")]
147 err := ValidateURL(arg.Value)
149 arg.Invalid(err.Error())
154 checkSyncPoolValueRules = map[string]CallCheck{
155 "(*sync.Pool).Put": func(call *Call) {
156 arg := call.Args[knowledge.Arg("(*sync.Pool).Put.x")]
157 typ := arg.Value.Value.Type()
158 _, isSlice := typ.Underlying().(*types.Slice)
159 if !typeutil.IsPointerLike(typ) || isSlice {
160 arg.Invalid("argument should be pointer-like to avoid allocations")
165 checkRegexpFindAllRules = map[string]CallCheck{
166 "(*regexp.Regexp).FindAll": RepeatZeroTimes("a FindAll method", 1),
167 "(*regexp.Regexp).FindAllIndex": RepeatZeroTimes("a FindAll method", 1),
168 "(*regexp.Regexp).FindAllString": RepeatZeroTimes("a FindAll method", 1),
169 "(*regexp.Regexp).FindAllStringIndex": RepeatZeroTimes("a FindAll method", 1),
170 "(*regexp.Regexp).FindAllStringSubmatch": RepeatZeroTimes("a FindAll method", 1),
171 "(*regexp.Regexp).FindAllStringSubmatchIndex": RepeatZeroTimes("a FindAll method", 1),
172 "(*regexp.Regexp).FindAllSubmatch": RepeatZeroTimes("a FindAll method", 1),
173 "(*regexp.Regexp).FindAllSubmatchIndex": RepeatZeroTimes("a FindAll method", 1),
176 checkUTF8CutsetRules = map[string]CallCheck{
177 "strings.IndexAny": utf8Cutset,
178 "strings.LastIndexAny": utf8Cutset,
179 "strings.ContainsAny": utf8Cutset,
180 "strings.Trim": utf8Cutset,
181 "strings.TrimLeft": utf8Cutset,
182 "strings.TrimRight": utf8Cutset,
185 checkUniqueCutsetRules = map[string]CallCheck{
186 "strings.Trim": uniqueCutset,
187 "strings.TrimLeft": uniqueCutset,
188 "strings.TrimRight": uniqueCutset,
191 checkUnmarshalPointerRules = map[string]CallCheck{
192 "encoding/xml.Unmarshal": unmarshalPointer("xml.Unmarshal", 1),
193 "(*encoding/xml.Decoder).Decode": unmarshalPointer("Decode", 0),
194 "(*encoding/xml.Decoder).DecodeElement": unmarshalPointer("DecodeElement", 0),
195 "encoding/json.Unmarshal": unmarshalPointer("json.Unmarshal", 1),
196 "(*encoding/json.Decoder).Decode": unmarshalPointer("Decode", 0),
199 checkUnbufferedSignalChanRules = map[string]CallCheck{
200 "os/signal.Notify": func(call *Call) {
201 arg := call.Args[knowledge.Arg("os/signal.Notify.c")]
202 if UnbufferedChannel(arg.Value) {
203 arg.Invalid("the channel used with signal.Notify should be buffered")
208 checkMathIntRules = map[string]CallCheck{
209 "math.Ceil": pointlessIntMath,
210 "math.Floor": pointlessIntMath,
211 "math.IsNaN": pointlessIntMath,
212 "math.Trunc": pointlessIntMath,
213 "math.IsInf": pointlessIntMath,
216 checkStringsReplaceZeroRules = map[string]CallCheck{
217 "strings.Replace": RepeatZeroTimes("strings.Replace", 3),
218 "bytes.Replace": RepeatZeroTimes("bytes.Replace", 3),
221 checkListenAddressRules = map[string]CallCheck{
222 "net/http.ListenAndServe": checkValidHostPort(0),
223 "net/http.ListenAndServeTLS": checkValidHostPort(0),
226 checkBytesEqualIPRules = map[string]CallCheck{
227 "bytes.Equal": func(call *Call) {
228 if ConvertedFrom(call.Args[knowledge.Arg("bytes.Equal.a")].Value, "net.IP") &&
229 ConvertedFrom(call.Args[knowledge.Arg("bytes.Equal.b")].Value, "net.IP") {
230 call.Invalid("use net.IP.Equal to compare net.IPs, not bytes.Equal")
235 checkRegexpMatchLoopRules = map[string]CallCheck{
236 "regexp.Match": loopedRegexp("regexp.Match"),
237 "regexp.MatchReader": loopedRegexp("regexp.MatchReader"),
238 "regexp.MatchString": loopedRegexp("regexp.MatchString"),
241 checkNoopMarshal = map[string]CallCheck{
242 // TODO(dh): should we really flag XML? Even an empty struct
243 // produces a non-zero amount of data, namely its type name.
244 // Let's see if we encounter any false positives.
246 // Also, should we flag gob?
247 "encoding/json.Marshal": checkNoopMarshalImpl(knowledge.Arg("json.Marshal.v"), "MarshalJSON", "MarshalText"),
248 "encoding/xml.Marshal": checkNoopMarshalImpl(knowledge.Arg("xml.Marshal.v"), "MarshalXML", "MarshalText"),
249 "(*encoding/json.Encoder).Encode": checkNoopMarshalImpl(knowledge.Arg("(*encoding/json.Encoder).Encode.v"), "MarshalJSON", "MarshalText"),
250 "(*encoding/xml.Encoder).Encode": checkNoopMarshalImpl(knowledge.Arg("(*encoding/xml.Encoder).Encode.v"), "MarshalXML", "MarshalText"),
252 "encoding/json.Unmarshal": checkNoopMarshalImpl(knowledge.Arg("json.Unmarshal.v"), "UnmarshalJSON", "UnmarshalText"),
253 "encoding/xml.Unmarshal": checkNoopMarshalImpl(knowledge.Arg("xml.Unmarshal.v"), "UnmarshalXML", "UnmarshalText"),
254 "(*encoding/json.Decoder).Decode": checkNoopMarshalImpl(knowledge.Arg("(*encoding/json.Decoder).Decode.v"), "UnmarshalJSON", "UnmarshalText"),
255 "(*encoding/xml.Decoder).Decode": checkNoopMarshalImpl(knowledge.Arg("(*encoding/xml.Decoder).Decode.v"), "UnmarshalXML", "UnmarshalText"),
258 checkUnsupportedMarshal = map[string]CallCheck{
259 "encoding/json.Marshal": checkUnsupportedMarshalImpl(knowledge.Arg("json.Marshal.v"), "json", "MarshalJSON", "MarshalText"),
260 "encoding/xml.Marshal": checkUnsupportedMarshalImpl(knowledge.Arg("xml.Marshal.v"), "xml", "MarshalXML", "MarshalText"),
261 "(*encoding/json.Encoder).Encode": checkUnsupportedMarshalImpl(knowledge.Arg("(*encoding/json.Encoder).Encode.v"), "json", "MarshalJSON", "MarshalText"),
262 "(*encoding/xml.Encoder).Encode": checkUnsupportedMarshalImpl(knowledge.Arg("(*encoding/xml.Encoder).Encode.v"), "xml", "MarshalXML", "MarshalText"),
265 checkAtomicAlignment = map[string]CallCheck{
266 "sync/atomic.AddInt64": checkAtomicAlignmentImpl,
267 "sync/atomic.AddUint64": checkAtomicAlignmentImpl,
268 "sync/atomic.CompareAndSwapInt64": checkAtomicAlignmentImpl,
269 "sync/atomic.CompareAndSwapUint64": checkAtomicAlignmentImpl,
270 "sync/atomic.LoadInt64": checkAtomicAlignmentImpl,
271 "sync/atomic.LoadUint64": checkAtomicAlignmentImpl,
272 "sync/atomic.StoreInt64": checkAtomicAlignmentImpl,
273 "sync/atomic.StoreUint64": checkAtomicAlignmentImpl,
274 "sync/atomic.SwapInt64": checkAtomicAlignmentImpl,
275 "sync/atomic.SwapUint64": checkAtomicAlignmentImpl,
278 // TODO(dh): detect printf wrappers
279 checkPrintfRules = map[string]CallCheck{
280 "fmt.Errorf": func(call *Call) { checkPrintfCall(call, 0, 1) },
281 "fmt.Printf": func(call *Call) { checkPrintfCall(call, 0, 1) },
282 "fmt.Sprintf": func(call *Call) { checkPrintfCall(call, 0, 1) },
283 "fmt.Fprintf": func(call *Call) { checkPrintfCall(call, 1, 2) },
284 "golang.org/x/xerrors.Errorf": func(call *Call) { checkPrintfCall(call, 0, 1) },
287 checkSortSliceRules = map[string]CallCheck{
288 "sort.Slice": checkSortSlice,
289 "sort.SliceIsSorted": checkSortSlice,
290 "sort.SliceStable": checkSortSlice,
293 checkWithValueKeyRules = map[string]CallCheck{
294 "context.WithValue": checkWithValueKey,
298 func checkPrintfCall(call *Call, fIdx, vIdx int) {
301 switch v := call.Args[vIdx].Value.Value.(type) {
304 args, ok = irutil.Vararg(v)
306 // We don't know what the actual arguments to the function are
310 // nil, i.e. no arguments
312 // We don't know what the actual arguments to the function are
315 checkPrintfCallImpl(f, f.Value.Value, args)
321 isInt verbFlag = 1 << iota
326 // Verbs that accept "pseudo pointers" will sometimes dereference
327 // non-nil pointers. For example, %x on a non-nil *struct will print the
328 // individual fields, but on a nil pointer it will print the address.
335 var verbs = [...]verbFlag{
336 'b': isPseudoPointer | isInt | isFP,
338 'd': isPseudoPointer | isInt,
345 'o': isPseudoPointer | isInt,
346 'O': isPseudoPointer | isInt,
347 'p': isSlice | isPointer | noRecurse,
348 'q': isInt | isString,
354 'X': isPseudoPointer | isInt | isFP | isString,
355 'x': isPseudoPointer | isInt | isFP | isString,
358 func checkPrintfCallImpl(carg *Argument, f ir.Value, args []ir.Value) {
359 var msCache *gotypeutil.MethodSetCache
360 if f.Parent() != nil {
361 msCache = &f.Parent().Prog.MethodSets
364 elem := func(T types.Type, verb rune) ([]types.Type, bool) {
365 if verbs[verb]&noRecurse != 0 {
366 return []types.Type{T}, false
368 switch T := T.(type) {
370 if verbs[verb]&isSlice != 0 {
371 return []types.Type{T}, false
373 if verbs[verb]&isString != 0 && typeutil.IsType(T.Elem().Underlying(), "byte") {
374 return []types.Type{T}, false
376 return []types.Type{T.Elem()}, true
380 return []types.Type{key, val}, true
382 out := make([]types.Type, 0, T.NumFields())
383 for i := 0; i < T.NumFields(); i++ {
384 out = append(out, T.Field(i).Type())
388 return []types.Type{T.Elem()}, true
390 return []types.Type{T}, false
393 isInfo := func(T types.Type, info types.BasicInfo) bool {
394 basic, ok := T.Underlying().(*types.Basic)
395 return ok && basic.Info()&info != 0
398 isStringer := func(T types.Type, ms *types.MethodSet) bool {
399 sel := ms.Lookup(nil, "String")
403 fn, ok := sel.Obj().(*types.Func)
405 // should be unreachable
408 sig := fn.Type().(*types.Signature)
409 if sig.Params().Len() != 0 {
412 if sig.Results().Len() != 1 {
415 if !typeutil.IsType(sig.Results().At(0).Type(), "string") {
420 isError := func(T types.Type, ms *types.MethodSet) bool {
421 sel := ms.Lookup(nil, "Error")
425 fn, ok := sel.Obj().(*types.Func)
427 // should be unreachable
430 sig := fn.Type().(*types.Signature)
431 if sig.Params().Len() != 0 {
434 if sig.Results().Len() != 1 {
437 if !typeutil.IsType(sig.Results().At(0).Type(), "string") {
443 isFormatter := func(T types.Type, ms *types.MethodSet) bool {
444 sel := ms.Lookup(nil, "Format")
448 fn, ok := sel.Obj().(*types.Func)
450 // should be unreachable
453 sig := fn.Type().(*types.Signature)
454 if sig.Params().Len() != 2 {
457 // TODO(dh): check the types of the arguments for more
459 if sig.Results().Len() != 0 {
465 seen := map[types.Type]bool{}
466 var checkType func(verb rune, T types.Type, top bool) bool
467 checkType = func(verb rune, T types.Type, top bool) bool {
469 for k := range seen {
477 if int(verb) >= len(verbs) {
488 ms := msCache.MethodSet(T)
489 if isFormatter(T, ms) {
490 // the value is responsible for formatting itself
494 if flags&isString != 0 && (isStringer(T, ms) || isError(T, ms)) {
495 // Check for stringer early because we're about to dereference
500 if flags&(isPointer|isPseudoPointer) == 0 && top {
501 T = typeutil.Dereference(T)
503 if flags&isPseudoPointer != 0 && top {
504 t := typeutil.Dereference(T)
505 if _, ok := t.Underlying().(*types.Struct); ok {
510 if _, ok := T.(*types.Interface); ok {
511 // We don't know what's in the interface
515 var info types.BasicInfo
516 if flags&isInt != 0 {
517 info |= types.IsInteger
519 if flags&isBool != 0 {
520 info |= types.IsBoolean
523 info |= types.IsFloat | types.IsComplex
525 if flags&isString != 0 {
526 info |= types.IsString
529 if info != 0 && isInfo(T, info) {
533 if flags&isString != 0 {
534 isStringyElem := func(typ types.Type) bool {
535 if typ, ok := typ.Underlying().(*types.Basic); ok {
536 return typ.Kind() == types.Byte
540 switch T := T.(type) {
542 if isStringyElem(T.Elem()) {
546 if isStringyElem(T.Elem()) {
550 if isStringer(T, ms) || isError(T, ms) {
555 if flags&isPointer != 0 && typeutil.IsPointerLike(T) {
558 if flags&isPseudoPointer != 0 {
559 switch U := T.Underlying().(type) {
565 if _, ok := U.Elem().Underlying().(*types.Struct); !ok {
566 // TODO(dh): can this condition ever be false? For
567 // *T, if T is a struct, we'll already have
568 // dereferenced it, meaning the *types.Pointer
569 // branch couldn't have been taken. For T that
570 // aren't structs, this condition will always
574 case *types.Chan, *types.Signature:
575 // Channels and functions are always treated as
576 // pointers and never recursed into.
579 if U.Kind() == types.UnsafePointer {
582 case *types.Interface:
583 // we will already have bailed if the type is an
587 // other pointer-like types, such as maps or slices,
588 // will be printed element-wise.
592 if flags&isSlice != 0 {
593 if _, ok := T.(*types.Slice); ok {
598 if flags&isAny != 0 {
602 elems, ok := elem(T.Underlying(), verb)
606 for _, elem := range elems {
607 if !checkType(verb, elem, false) {
615 k, ok := irutil.Flatten(f).(*ir.Const)
619 actions, err := printf.Parse(constant.StringVal(k.Value))
621 carg.Invalid("couldn't parse format string")
628 checkStar := func(verb printf.Verb, star printf.Argument) bool {
629 if star, ok := star.(printf.Star); ok {
631 if star.Index == -1 {
640 carg.Invalid(fmt.Sprintf("Printf format %s reads invalid arg 0; indices are 1-based", verb.Raw))
645 fmt.Sprintf("Printf format %s reads arg #%d, but call has only %d args",
646 verb.Raw, idx, len(args)))
649 if arg, ok := args[idx-1].(*ir.MakeInterface); ok {
650 if !isInfo(arg.X.Type(), types.IsInteger) {
651 carg.Invalid(fmt.Sprintf("Printf format %s reads non-int arg #%d as argument of *", verb.Raw, idx))
658 // We only report one problem per format string. Making a
659 // mistake with an index tends to invalidate all future
661 for _, action := range actions {
662 verb, ok := action.(printf.Verb)
667 if !checkStar(verb, verb.Width) || !checkStar(verb, verb.Precision) {
672 if verb.Value != -1 {
678 fmt.Sprintf("Printf format %s reads arg #%d, but call has only %d args",
679 verb.Raw, off, len(args)))
681 } else if verb.Value == 0 && verb.Letter != '%' {
682 carg.Invalid(fmt.Sprintf("Printf format %s reads invalid arg 0; indices are 1-based", verb.Raw))
685 arg, ok := args[off-1].(*ir.MakeInterface)
687 if !checkType(verb.Letter, arg.X.Type(), true) {
688 carg.Invalid(fmt.Sprintf("Printf format %s has arg #%d of wrong type %s",
689 verb.Raw, ptr, args[ptr-1].(*ir.MakeInterface).X.Type()))
697 // Consume next argument
700 // Don't consume any arguments
706 if !hasExplicit && ptr <= len(args) {
707 carg.Invalid(fmt.Sprintf("Printf call needs %d args but has %d args", ptr-1, len(args)))
711 func checkAtomicAlignmentImpl(call *Call) {
712 sizes := call.Pass.TypesSizes
713 if sizes.Sizeof(types.Typ[types.Uintptr]) != 4 {
714 // Not running on a 32-bit platform
717 v, ok := irutil.Flatten(call.Args[0].Value.Value).(*ir.FieldAddr)
719 // TODO(dh): also check indexing into arrays and slices
722 T := v.X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct)
723 fields := make([]*types.Var, 0, T.NumFields())
724 for i := 0; i < T.NumFields() && i <= v.Field; i++ {
725 fields = append(fields, T.Field(i))
728 off := sizes.Offsetsof(fields)[v.Field]
730 msg := fmt.Sprintf("address of non 64-bit aligned field %s passed to %s",
731 T.Field(v.Field).Name(),
732 irutil.CallName(call.Instr.Common()))
737 func checkNoopMarshalImpl(argN int, meths ...string) CallCheck {
738 return func(call *Call) {
739 if code.IsGenerated(call.Pass, call.Instr.Pos()) {
742 arg := call.Args[argN]
743 T := arg.Value.Value.Type()
744 Ts, ok := typeutil.Dereference(T).Underlying().(*types.Struct)
748 if Ts.NumFields() == 0 {
751 fields := typeutil.FlattenFields(Ts)
752 for _, field := range fields {
753 if field.Var.Exported() {
757 // OPT(dh): we could use a method set cache here
758 ms := call.Instr.Parent().Prog.MethodSets.MethodSet(T)
759 // TODO(dh): we're not checking the signature, which can cause false negatives.
760 // This isn't a huge problem, however, since vet complains about incorrect signatures.
761 for _, meth := range meths {
762 if ms.Lookup(nil, meth) != nil {
766 arg.Invalid("struct doesn't have any exported fields, nor custom marshaling")
770 func checkUnsupportedMarshalImpl(argN int, tag string, meths ...string) CallCheck {
771 // TODO(dh): flag slices and maps of unsupported types
772 return func(call *Call) {
773 msCache := &call.Instr.Parent().Prog.MethodSets
775 arg := call.Args[argN]
776 T := arg.Value.Value.Type()
777 Ts, ok := typeutil.Dereference(T).Underlying().(*types.Struct)
781 ms := msCache.MethodSet(T)
782 // TODO(dh): we're not checking the signature, which can cause false negatives.
783 // This isn't a huge problem, however, since vet complains about incorrect signatures.
784 for _, meth := range meths {
785 if ms.Lookup(nil, meth) != nil {
789 fields := typeutil.FlattenFields(Ts)
790 for _, field := range fields {
791 if !(field.Var.Exported()) {
794 if reflect.StructTag(field.Tag).Get(tag) == "-" {
797 ms := msCache.MethodSet(field.Var.Type())
798 // TODO(dh): we're not checking the signature, which can cause false negatives.
799 // This isn't a huge problem, however, since vet complains about incorrect signatures.
800 for _, meth := range meths {
801 if ms.Lookup(nil, meth) != nil {
805 switch field.Var.Type().Underlying().(type) {
806 case *types.Chan, *types.Signature:
807 arg.Invalid(fmt.Sprintf("trying to marshal chan or func value, field %s", fieldPath(T, field.Path)))
813 func fieldPath(start types.Type, indices []int) string {
815 for _, idx := range indices {
816 field := typeutil.Dereference(start).Underlying().(*types.Struct).Field(idx)
818 p += "." + field.Name()
823 func isInLoop(b *ir.BasicBlock) bool {
824 sets := irutil.FindLoops(b.Parent())
825 for _, set := range sets {
833 func CheckUntrappableSignal(pass *analysis.Pass) (interface{}, error) {
834 fn := func(node ast.Node) {
835 call := node.(*ast.CallExpr)
836 if !code.IsCallToAny(pass, call,
837 "os/signal.Ignore", "os/signal.Notify", "os/signal.Reset") {
842 for _, arg := range call.Args {
843 if conv, ok := arg.(*ast.CallExpr); ok && isName(pass, conv.Fun, "os.Signal") {
847 if isName(pass, arg, "syscall.SIGTERM") {
853 for i, arg := range call.Args {
854 if conv, ok := arg.(*ast.CallExpr); ok && isName(pass, conv.Fun, "os.Signal") {
858 if isName(pass, arg, "os.Kill") || isName(pass, arg, "syscall.SIGKILL") {
859 var fixes []analysis.SuggestedFix
861 nargs := make([]ast.Expr, len(call.Args))
862 for j, a := range call.Args {
864 nargs[j] = edit.Selector("syscall", "SIGTERM")
871 fixes = append(fixes, edit.Fix(fmt.Sprintf("use syscall.SIGTERM instead of %s", report.Render(pass, arg)), edit.ReplaceWithNode(pass.Fset, call, &ncall)))
873 nargs := make([]ast.Expr, 0, len(call.Args))
874 for j, a := range call.Args {
878 nargs = append(nargs, a)
882 fixes = append(fixes, edit.Fix(fmt.Sprintf("remove %s from list of arguments", report.Render(pass, arg)), edit.ReplaceWithNode(pass.Fset, call, &ncall)))
883 report.Report(pass, arg, fmt.Sprintf("%s cannot be trapped (did you mean syscall.SIGTERM?)", report.Render(pass, arg)), report.Fixes(fixes...))
885 if isName(pass, arg, "syscall.SIGSTOP") {
886 nargs := make([]ast.Expr, 0, len(call.Args)-1)
887 for j, a := range call.Args {
891 nargs = append(nargs, a)
895 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))))
899 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
903 func CheckTemplate(pass *analysis.Pass) (interface{}, error) {
904 fn := func(node ast.Node) {
905 call := node.(*ast.CallExpr)
906 // OPT(dh): use integer for kind
908 switch code.CallName(pass, call) {
909 case "(*text/template.Template).Parse":
911 case "(*html/template.Template).Parse":
916 sel := call.Fun.(*ast.SelectorExpr)
917 if !code.IsCallToAny(pass, sel.X, "text/template.New", "html/template.New") {
918 // TODO(dh): this is a cheap workaround for templates with
919 // different delims. A better solution with less false
920 // negatives would use data flow analysis to see where the
921 // template comes from and where it has been
924 s, ok := code.ExprToString(pass, call.Args[knowledge.Arg("(*text/template.Template).Parse.text")])
931 _, err = texttemplate.New("").Parse(s)
933 _, err = htmltemplate.New("").Parse(s)
936 // TODO(dominikh): whitelist other parse errors, if any
937 if strings.Contains(err.Error(), "unexpected") {
938 report.Report(pass, call.Args[knowledge.Arg("(*text/template.Template).Parse.text")], err.Error())
942 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
947 checkTimeSleepConstantPatternRns = pattern.MustParse(`(BinaryExpr duration "*" (SelectorExpr (Ident "time") (Ident "Nanosecond")))`)
948 checkTimeSleepConstantPatternRs = pattern.MustParse(`(BinaryExpr duration "*" (SelectorExpr (Ident "time") (Ident "Second")))`)
951 func CheckTimeSleepConstant(pass *analysis.Pass) (interface{}, error) {
952 fn := func(node ast.Node) {
953 call := node.(*ast.CallExpr)
954 if !code.IsCallTo(pass, call, "time.Sleep") {
957 lit, ok := call.Args[knowledge.Arg("time.Sleep.d")].(*ast.BasicLit)
961 n, err := strconv.Atoi(lit.Value)
965 if n == 0 || n > 120 {
966 // time.Sleep(0) is a seldom used pattern in concurrency
967 // tests. >120 might be intentional. 120 was chosen
968 // because the user could've meant 2 minutes.
972 report.Report(pass, lit,
973 fmt.Sprintf("sleeping for %d nanoseconds is probably a bug; be explicit if it isn't", n), report.Fixes(
974 edit.Fix("explicitly use nanoseconds", edit.ReplaceWithPattern(pass, checkTimeSleepConstantPatternRns, pattern.State{"duration": lit}, lit)),
975 edit.Fix("use seconds", edit.ReplaceWithPattern(pass, checkTimeSleepConstantPatternRs, pattern.State{"duration": lit}, lit))))
977 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
981 var checkWaitgroupAddQ = pattern.MustParse(`
986 call@(CallExpr (Function "(*sync.WaitGroup).Add") _):_) _))`)
988 func CheckWaitgroupAdd(pass *analysis.Pass) (interface{}, error) {
989 fn := func(node ast.Node) {
990 if m, ok := code.Match(pass, checkWaitgroupAddQ, node); ok {
991 call := m.State["call"].(ast.Node)
992 report.Report(pass, call, fmt.Sprintf("should call %s before starting the goroutine to avoid a race", report.Render(pass, call)))
995 code.Preorder(pass, fn, (*ast.GoStmt)(nil))
999 func CheckInfiniteEmptyLoop(pass *analysis.Pass) (interface{}, error) {
1000 fn := func(node ast.Node) {
1001 loop := node.(*ast.ForStmt)
1002 if len(loop.Body.List) != 0 || loop.Post != nil {
1006 if loop.Init != nil {
1007 // TODO(dh): this isn't strictly necessary, it just makes
1008 // the check easier.
1011 // An empty loop is bad news in two cases: 1) The loop has no
1012 // condition. In that case, it's just a loop that spins
1013 // forever and as fast as it can, keeping a core busy. 2) The
1014 // loop condition only consists of variable or field reads and
1015 // operators on those. The only way those could change their
1016 // value is with unsynchronised access, which constitutes a
1019 // If the condition contains any function calls, its behaviour
1020 // is dynamic and the loop might terminate. Similarly for
1021 // channel receives.
1023 if loop.Cond != nil {
1024 if code.MayHaveSideEffects(pass, loop.Cond, nil) {
1027 if ident, ok := loop.Cond.(*ast.Ident); ok {
1028 if k, ok := pass.TypesInfo.ObjectOf(ident).(*types.Const); ok {
1029 if !constant.BoolVal(k.Val()) {
1030 // don't flag `for false {}` loops. They're a debug aid.
1035 report.Report(pass, loop, "loop condition never changes or has a race condition")
1037 report.Report(pass, loop, "this loop will spin, using 100%% CPU", report.ShortRange())
1039 code.Preorder(pass, fn, (*ast.ForStmt)(nil))
1043 func CheckDeferInInfiniteLoop(pass *analysis.Pass) (interface{}, error) {
1044 fn := func(node ast.Node) {
1046 var defers []ast.Stmt
1047 loop := node.(*ast.ForStmt)
1048 if loop.Cond != nil {
1051 fn2 := func(node ast.Node) bool {
1052 switch stmt := node.(type) {
1053 case *ast.ReturnStmt:
1056 case *ast.BranchStmt:
1057 // TODO(dominikh): if this sees a break in a switch or
1058 // select, it doesn't check if it breaks the loop or
1059 // just the select/switch. This causes some false
1061 if stmt.Tok == token.BREAK {
1065 case *ast.DeferStmt:
1066 defers = append(defers, stmt)
1068 // Don't look into function bodies
1073 ast.Inspect(loop.Body, fn2)
1077 for _, stmt := range defers {
1078 report.Report(pass, stmt, "defers in this infinite loop will never run")
1081 code.Preorder(pass, fn, (*ast.ForStmt)(nil))
1085 func CheckDubiousDeferInChannelRangeLoop(pass *analysis.Pass) (interface{}, error) {
1086 fn := func(node ast.Node) {
1087 loop := node.(*ast.RangeStmt)
1088 typ := pass.TypesInfo.TypeOf(loop.X)
1089 _, ok := typ.Underlying().(*types.Chan)
1093 fn2 := func(node ast.Node) bool {
1094 switch stmt := node.(type) {
1095 case *ast.DeferStmt:
1096 report.Report(pass, stmt, "defers in this range loop won't run unless the channel gets closed")
1098 // Don't look into function bodies
1103 ast.Inspect(loop.Body, fn2)
1105 code.Preorder(pass, fn, (*ast.RangeStmt)(nil))
1109 func CheckTestMainExit(pass *analysis.Pass) (interface{}, error) {
1110 if code.IsGoVersion(pass, 15) {
1111 // Beginning with Go 1.15, the test framework will call
1122 fn := func(node ast.Node, push bool) bool {
1124 if fnmain != nil && node == fnmain {
1125 if !callsExit && callsRun {
1126 report.Report(pass, fnmain, "TestMain should call os.Exit to set exit code")
1136 switch node := node.(type) {
1141 if !isTestMain(pass, node) {
1145 arg = pass.TypesInfo.ObjectOf(node.Type.Params.List[0].Names[0])
1148 if code.IsCallTo(pass, node, "os.Exit") {
1152 sel, ok := node.Fun.(*ast.SelectorExpr)
1156 ident, ok := sel.X.(*ast.Ident)
1160 if arg != pass.TypesInfo.ObjectOf(ident) {
1163 if sel.Sel.Name == "Run" {
1169 lint.ExhaustiveTypeSwitch(node)
1173 pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.FuncDecl)(nil), (*ast.CallExpr)(nil)}, fn)
1177 func isTestMain(pass *analysis.Pass, decl *ast.FuncDecl) bool {
1178 if decl.Name.Name != "TestMain" {
1181 if len(decl.Type.Params.List) != 1 {
1184 arg := decl.Type.Params.List[0]
1185 if len(arg.Names) != 1 {
1188 return code.IsOfType(pass, arg.Type, "*testing.M")
1191 func CheckExec(pass *analysis.Pass) (interface{}, error) {
1192 fn := func(node ast.Node) {
1193 call := node.(*ast.CallExpr)
1194 if !code.IsCallTo(pass, call, "os/exec.Command") {
1197 val, ok := code.ExprToString(pass, call.Args[knowledge.Arg("os/exec.Command.name")])
1201 if !strings.Contains(val, " ") || strings.Contains(val, `\`) || strings.Contains(val, "/") {
1204 report.Report(pass, call.Args[knowledge.Arg("os/exec.Command.name")],
1205 "first argument to exec.Command looks like a shell command, but a program name or path are expected")
1207 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
1211 func CheckLoopEmptyDefault(pass *analysis.Pass) (interface{}, error) {
1212 fn := func(node ast.Node) {
1213 loop := node.(*ast.ForStmt)
1214 if len(loop.Body.List) != 1 || loop.Cond != nil || loop.Init != nil {
1217 sel, ok := loop.Body.List[0].(*ast.SelectStmt)
1221 for _, c := range sel.Body.List {
1222 // FIXME this leaves behind an empty line, and possibly
1223 // comments in the default branch. We can't easily fix
1225 if comm, ok := c.(*ast.CommClause); ok && comm.Comm == nil && len(comm.Body) == 0 {
1226 report.Report(pass, comm, "should not have an empty default case in a for+select loop; the loop will spin",
1227 report.Fixes(edit.Fix("remove empty default branch", edit.Delete(comm))))
1228 // there can only be one default case
1233 code.Preorder(pass, fn, (*ast.ForStmt)(nil))
1237 func CheckLhsRhsIdentical(pass *analysis.Pass) (interface{}, error) {
1238 var isFloat func(T types.Type) bool
1239 isFloat = func(T types.Type) bool {
1240 switch T := T.Underlying().(type) {
1243 return kind == types.Float32 || kind == types.Float64
1245 return isFloat(T.Elem())
1247 for i := 0; i < T.NumFields(); i++ {
1248 if !isFloat(T.Field(i).Type()) {
1258 // TODO(dh): this check ignores the existence of side-effects and
1259 // happily flags fn() == fn() – so far, we've had nobody complain
1260 // about a false positive, and it's caught several bugs in real
1262 fn := func(node ast.Node) {
1263 op := node.(*ast.BinaryExpr)
1265 case token.EQL, token.NEQ:
1266 if isFloat(pass.TypesInfo.TypeOf(op.X)) {
1267 // f == f and f != f might be used to check for NaN
1270 case token.SUB, token.QUO, token.AND, token.REM, token.OR, token.XOR, token.AND_NOT,
1271 token.LAND, token.LOR, token.LSS, token.GTR, token.LEQ, token.GEQ:
1273 // For some ops, such as + and *, it can make sense to
1274 // have identical operands
1278 if reflect.TypeOf(op.X) != reflect.TypeOf(op.Y) {
1281 if report.Render(pass, op.X) != report.Render(pass, op.Y) {
1284 l1, ok1 := op.X.(*ast.BasicLit)
1285 l2, ok2 := op.Y.(*ast.BasicLit)
1286 if ok1 && ok2 && l1.Kind == token.INT && l2.Kind == l1.Kind && l1.Value == "0" && l2.Value == l1.Value && code.IsGenerated(pass, l1.Pos()) {
1287 // cgo generates the following function call:
1288 // _cgoCheckPointer(_cgoBase0, 0 == 0) – it uses 0 == 0
1289 // instead of true in case the user shadowed the
1290 // identifier. Ideally we'd restrict this exception to
1291 // calls of _cgoCheckPointer, but it's not worth the
1292 // hassle of keeping track of the stack. <lit> <op> <lit>
1293 // are very rare to begin with, and we're mostly checking
1294 // for them to catch typos such as 1 == 1 where the user
1295 // meant to type i == 1. The odds of a false negative for
1299 report.Report(pass, op, fmt.Sprintf("identical expressions on the left and right side of the '%s' operator", op.Op))
1301 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil))
1305 func CheckScopedBreak(pass *analysis.Pass) (interface{}, error) {
1306 fn := func(node ast.Node) {
1307 var body *ast.BlockStmt
1308 switch node := node.(type) {
1311 case *ast.RangeStmt:
1314 lint.ExhaustiveTypeSwitch(node)
1316 for _, stmt := range body.List {
1317 var blocks [][]ast.Stmt
1318 switch stmt := stmt.(type) {
1319 case *ast.SwitchStmt:
1320 for _, c := range stmt.Body.List {
1321 blocks = append(blocks, c.(*ast.CaseClause).Body)
1323 case *ast.SelectStmt:
1324 for _, c := range stmt.Body.List {
1325 blocks = append(blocks, c.(*ast.CommClause).Body)
1331 for _, body := range blocks {
1335 lasts := []ast.Stmt{body[len(body)-1]}
1336 // TODO(dh): unfold all levels of nested block
1337 // statements, not just a single level if statement
1338 if ifs, ok := lasts[0].(*ast.IfStmt); ok {
1339 if len(ifs.Body.List) == 0 {
1342 lasts[0] = ifs.Body.List[len(ifs.Body.List)-1]
1344 if block, ok := ifs.Else.(*ast.BlockStmt); ok {
1345 if len(block.List) != 0 {
1346 lasts = append(lasts, block.List[len(block.List)-1])
1350 for _, last := range lasts {
1351 branch, ok := last.(*ast.BranchStmt)
1352 if !ok || branch.Tok != token.BREAK || branch.Label != nil {
1355 report.Report(pass, branch, "ineffective break statement. Did you mean to break out of the outer loop?")
1360 code.Preorder(pass, fn, (*ast.ForStmt)(nil), (*ast.RangeStmt)(nil))
1364 func CheckUnsafePrintf(pass *analysis.Pass) (interface{}, error) {
1365 fn := func(node ast.Node) {
1366 call := node.(*ast.CallExpr)
1367 name := code.CallName(pass, call)
1371 case "fmt.Printf", "fmt.Sprintf", "log.Printf":
1372 arg = knowledge.Arg("fmt.Printf.format")
1374 arg = knowledge.Arg("fmt.Fprintf.format")
1378 if len(call.Args) != arg+1 {
1381 switch call.Args[arg].(type) {
1382 case *ast.CallExpr, *ast.Ident:
1387 alt := name[:len(name)-1]
1388 report.Report(pass, call,
1389 "printf-style function with dynamic format string and no further arguments should use print-style function instead",
1390 report.Fixes(edit.Fix(fmt.Sprintf("use %s instead of %s", alt, name), edit.ReplaceWithString(pass.Fset, call.Fun, alt))))
1392 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
1396 func CheckEarlyDefer(pass *analysis.Pass) (interface{}, error) {
1397 fn := func(node ast.Node) {
1398 block := node.(*ast.BlockStmt)
1399 if len(block.List) < 2 {
1402 for i, stmt := range block.List {
1403 if i == len(block.List)-1 {
1406 assign, ok := stmt.(*ast.AssignStmt)
1410 if len(assign.Rhs) != 1 {
1413 if len(assign.Lhs) < 2 {
1416 if lhs, ok := assign.Lhs[len(assign.Lhs)-1].(*ast.Ident); ok && lhs.Name == "_" {
1419 call, ok := assign.Rhs[0].(*ast.CallExpr)
1423 sig, ok := pass.TypesInfo.TypeOf(call.Fun).(*types.Signature)
1427 if sig.Results().Len() < 2 {
1430 last := sig.Results().At(sig.Results().Len() - 1)
1431 // FIXME(dh): check that it's error from universe, not
1432 // another type of the same name
1433 if last.Type().String() != "error" {
1436 lhs, ok := assign.Lhs[0].(*ast.Ident)
1440 def, ok := block.List[i+1].(*ast.DeferStmt)
1444 sel, ok := def.Call.Fun.(*ast.SelectorExpr)
1448 ident, ok := selectorX(sel).(*ast.Ident)
1452 if ident.Obj != lhs.Obj {
1455 if sel.Sel.Name != "Close" {
1458 report.Report(pass, def, fmt.Sprintf("should check returned error before deferring %s", report.Render(pass, def.Call)))
1461 code.Preorder(pass, fn, (*ast.BlockStmt)(nil))
1465 func selectorX(sel *ast.SelectorExpr) ast.Node {
1466 switch x := sel.X.(type) {
1467 case *ast.SelectorExpr:
1474 func CheckEmptyCriticalSection(pass *analysis.Pass) (interface{}, error) {
1475 if pass.Pkg.Path() == "sync_test" {
1476 // exception for the sync package's tests
1480 // Initially it might seem like this check would be easier to
1481 // implement using IR. After all, we're only checking for two
1482 // consecutive method calls. In reality, however, there may be any
1483 // number of other instructions between the lock and unlock, while
1484 // still constituting an empty critical section. For example,
1485 // given `m.x().Lock(); m.x().Unlock()`, there will be a call to
1486 // x(). In the AST-based approach, this has a tiny potential for a
1487 // false positive (the second call to x might be doing work that
1488 // is protected by the mutex). In an IR-based approach, however,
1489 // it would miss a lot of real bugs.
1491 mutexParams := func(s ast.Stmt) (x ast.Expr, funcName string, ok bool) {
1492 expr, ok := s.(*ast.ExprStmt)
1494 return nil, "", false
1496 call, ok := expr.X.(*ast.CallExpr)
1498 return nil, "", false
1500 sel, ok := call.Fun.(*ast.SelectorExpr)
1502 return nil, "", false
1505 fn, ok := pass.TypesInfo.ObjectOf(sel.Sel).(*types.Func)
1507 return nil, "", false
1509 sig := fn.Type().(*types.Signature)
1510 if sig.Params().Len() != 0 || sig.Results().Len() != 0 {
1511 return nil, "", false
1514 return sel.X, fn.Name(), true
1517 fn := func(node ast.Node) {
1518 block := node.(*ast.BlockStmt)
1519 if len(block.List) < 2 {
1522 for i := range block.List[:len(block.List)-1] {
1523 sel1, method1, ok1 := mutexParams(block.List[i])
1524 sel2, method2, ok2 := mutexParams(block.List[i+1])
1526 if !ok1 || !ok2 || report.Render(pass, sel1) != report.Render(pass, sel2) {
1529 if (method1 == "Lock" && method2 == "Unlock") ||
1530 (method1 == "RLock" && method2 == "RUnlock") {
1531 report.Report(pass, block.List[i+1], "empty critical section")
1535 code.Preorder(pass, fn, (*ast.BlockStmt)(nil))
1540 // cgo produces code like fn(&*_Cvar_kSomeCallbacks) which we don't
1542 cgoIdent = regexp.MustCompile(`^_C(func|var)_.+$`)
1543 checkIneffectiveCopyQ1 = pattern.MustParse(`(UnaryExpr "&" (StarExpr obj))`)
1544 checkIneffectiveCopyQ2 = pattern.MustParse(`(StarExpr (UnaryExpr "&" _))`)
1547 func CheckIneffectiveCopy(pass *analysis.Pass) (interface{}, error) {
1548 fn := func(node ast.Node) {
1549 if m, ok := code.Match(pass, checkIneffectiveCopyQ1, node); ok {
1550 if ident, ok := m.State["obj"].(*ast.Ident); !ok || !cgoIdent.MatchString(ident.Name) {
1551 report.Report(pass, node, "&*x will be simplified to x. It will not copy x.")
1553 } else if _, ok := code.Match(pass, checkIneffectiveCopyQ2, node); ok {
1554 report.Report(pass, node, "*&x will be simplified to x. It will not copy x.")
1557 code.Preorder(pass, fn, (*ast.UnaryExpr)(nil), (*ast.StarExpr)(nil))
1561 func CheckCanonicalHeaderKey(pass *analysis.Pass) (interface{}, error) {
1562 fn := func(node ast.Node, push bool) bool {
1566 if assign, ok := node.(*ast.AssignStmt); ok {
1567 // TODO(dh): This risks missing some Header reads, for
1568 // example in `h1["foo"] = h2["foo"]` – these edge
1569 // cases are probably rare enough to ignore for now.
1570 for _, expr := range assign.Lhs {
1571 op, ok := expr.(*ast.IndexExpr)
1575 if code.IsOfType(pass, op.X, "net/http.Header") {
1581 op, ok := node.(*ast.IndexExpr)
1585 if !code.IsOfType(pass, op.X, "net/http.Header") {
1588 s, ok := code.ExprToString(pass, op.Index)
1592 canonical := http.CanonicalHeaderKey(s)
1596 var fix analysis.SuggestedFix
1597 switch op.Index.(type) {
1599 fix = edit.Fix("canonicalize header key", edit.ReplaceWithString(pass.Fset, op.Index, strconv.Quote(canonical)))
1601 call := &ast.CallExpr{
1602 Fun: edit.Selector("http", "CanonicalHeaderKey"),
1603 Args: []ast.Expr{op.Index},
1605 fix = edit.Fix("wrap in http.CanonicalHeaderKey", edit.ReplaceWithNode(pass.Fset, op.Index, call))
1607 msg := fmt.Sprintf("keys in http.Header are canonicalized, %q is not canonical; fix the constant or use http.CanonicalHeaderKey", s)
1608 if fix.Message != "" {
1609 report.Report(pass, op, msg, report.Fixes(fix))
1611 report.Report(pass, op, msg)
1615 pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.AssignStmt)(nil), (*ast.IndexExpr)(nil)}, fn)
1619 func CheckBenchmarkN(pass *analysis.Pass) (interface{}, error) {
1620 fn := func(node ast.Node) {
1621 assign := node.(*ast.AssignStmt)
1622 if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 {
1625 sel, ok := assign.Lhs[0].(*ast.SelectorExpr)
1629 if sel.Sel.Name != "N" {
1632 if !code.IsOfType(pass, sel.X, "*testing.B") {
1635 report.Report(pass, assign, fmt.Sprintf("should not assign to %s", report.Render(pass, sel)))
1637 code.Preorder(pass, fn, (*ast.AssignStmt)(nil))
1641 func CheckUnreadVariableValues(pass *analysis.Pass) (interface{}, error) {
1642 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
1643 if irutil.IsExample(fn) {
1650 if gen, ok := code.Generator(pass, node.Pos()); ok && gen == facts.Goyacc {
1651 // Don't flag unused values in code generated by goyacc.
1652 // There may be hundreds of those due to the way the state
1653 // machine is constructed.
1657 switchTags := map[ir.Value]struct{}{}
1658 ast.Inspect(node, func(node ast.Node) bool {
1659 s, ok := node.(*ast.SwitchStmt)
1663 v, _ := fn.ValueForExpr(s.Tag)
1664 switchTags[v] = struct{}{}
1668 // OPT(dh): don't use a map, possibly use a bitset
1669 var hasUse func(v ir.Value, seen map[ir.Value]struct{}) bool
1670 hasUse = func(v ir.Value, seen map[ir.Value]struct{}) bool {
1671 if _, ok := seen[v]; ok {
1674 if _, ok := switchTags[v]; ok {
1677 refs := v.Referrers()
1679 // TODO investigate why refs can be nil
1682 for _, ref := range *refs {
1683 switch ref := ref.(type) {
1687 seen = map[ir.Value]struct{}{}
1689 seen[v] = struct{}{}
1690 if hasUse(ref, seen) {
1695 seen = map[ir.Value]struct{}{}
1697 seen[v] = struct{}{}
1698 if hasUse(ref, seen) {
1708 ast.Inspect(node, func(node ast.Node) bool {
1709 assign, ok := node.(*ast.AssignStmt)
1713 if len(assign.Lhs) > 1 && len(assign.Rhs) == 1 {
1714 // Either a function call with multiple return values,
1715 // or a comma-ok assignment
1717 val, _ := fn.ValueForExpr(assign.Rhs[0])
1721 refs := val.Referrers()
1725 for _, ref := range *refs {
1726 ex, ok := ref.(*ir.Extract)
1730 if !hasUse(ex, nil) {
1731 lhs := assign.Lhs[ex.Index]
1732 if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" {
1735 report.Report(pass, assign, fmt.Sprintf("this value of %s is never used", lhs))
1740 for i, lhs := range assign.Lhs {
1741 rhs := assign.Rhs[i]
1742 if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" {
1745 val, _ := fn.ValueForExpr(rhs)
1750 if _, ok := val.(*ir.Const); ok {
1751 // a zero-valued constant, for example in 'foo := []string(nil)'
1754 if !hasUse(val, nil) {
1755 report.Report(pass, assign, fmt.Sprintf("this value of %s is never used", lhs))
1764 func CheckPredeterminedBooleanExprs(pass *analysis.Pass) (interface{}, error) {
1765 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
1766 for _, block := range fn.Blocks {
1767 for _, ins := range block.Instrs {
1768 binop, ok := ins.(*ir.BinOp)
1773 case token.GTR, token.LSS, token.EQL, token.NEQ, token.LEQ, token.GEQ:
1778 xs, ok1 := consts(binop.X, nil, nil)
1779 ys, ok2 := consts(binop.Y, nil, nil)
1780 if !ok1 || !ok2 || len(xs) == 0 || len(ys) == 0 {
1785 for _, x := range xs {
1786 for _, y := range ys {
1793 if constant.Compare(x.Value, binop.Op, y.Value) {
1799 if trues == 0 || trues == len(xs)*len(ys) {
1800 report.Report(pass, binop, fmt.Sprintf("binary expression is always %t for all possible values (%s %s %s)", b, xs, binop.Op, ys))
1808 func CheckNilMaps(pass *analysis.Pass) (interface{}, error) {
1809 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
1810 for _, block := range fn.Blocks {
1811 for _, ins := range block.Instrs {
1812 mu, ok := ins.(*ir.MapUpdate)
1816 c, ok := irutil.Flatten(mu.Map).(*ir.Const)
1823 report.Report(pass, mu, "assignment to nil map")
1830 func CheckExtremeComparison(pass *analysis.Pass) (interface{}, error) {
1831 isobj := func(expr ast.Expr, name string) bool {
1832 sel, ok := expr.(*ast.SelectorExpr)
1836 return typeutil.IsObject(pass.TypesInfo.ObjectOf(sel.Sel), name)
1839 fn := func(node ast.Node) {
1840 expr := node.(*ast.BinaryExpr)
1841 tx := pass.TypesInfo.TypeOf(expr.X)
1842 basic, ok := tx.Underlying().(*types.Basic)
1850 switch basic.Kind() {
1852 max = "math.MaxUint8"
1854 max = "math.MaxUint16"
1856 max = "math.MaxUint32"
1858 max = "math.MaxUint64"
1860 max = "math.MaxUint64"
1863 min = "math.MinInt8"
1864 max = "math.MaxInt8"
1866 min = "math.MinInt16"
1867 max = "math.MaxInt16"
1869 min = "math.MinInt32"
1870 max = "math.MaxInt32"
1872 min = "math.MinInt64"
1873 max = "math.MaxInt64"
1875 min = "math.MinInt64"
1876 max = "math.MaxInt64"
1879 if (expr.Op == token.GTR || expr.Op == token.GEQ) && isobj(expr.Y, max) ||
1880 (expr.Op == token.LSS || expr.Op == token.LEQ) && isobj(expr.X, max) {
1881 report.Report(pass, expr, fmt.Sprintf("no value of type %s is greater than %s", basic, max))
1883 if expr.Op == token.LEQ && isobj(expr.Y, max) ||
1884 expr.Op == token.GEQ && isobj(expr.X, max) {
1885 report.Report(pass, expr, fmt.Sprintf("every value of type %s is <= %s", basic, max))
1888 if (basic.Info() & types.IsUnsigned) != 0 {
1889 if (expr.Op == token.LSS && astutil.IsIntLiteral(expr.Y, "0")) ||
1890 (expr.Op == token.GTR && astutil.IsIntLiteral(expr.X, "0")) {
1891 report.Report(pass, expr, fmt.Sprintf("no value of type %s is less than 0", basic))
1893 if expr.Op == token.GEQ && astutil.IsIntLiteral(expr.Y, "0") ||
1894 expr.Op == token.LEQ && astutil.IsIntLiteral(expr.X, "0") {
1895 report.Report(pass, expr, fmt.Sprintf("every value of type %s is >= 0", basic))
1898 if (expr.Op == token.LSS || expr.Op == token.LEQ) && isobj(expr.Y, min) ||
1899 (expr.Op == token.GTR || expr.Op == token.GEQ) && isobj(expr.X, min) {
1900 report.Report(pass, expr, fmt.Sprintf("no value of type %s is less than %s", basic, min))
1902 if expr.Op == token.GEQ && isobj(expr.Y, min) ||
1903 expr.Op == token.LEQ && isobj(expr.X, min) {
1904 report.Report(pass, expr, fmt.Sprintf("every value of type %s is >= %s", basic, min))
1909 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil))
1913 func consts(val ir.Value, out []*ir.Const, visitedPhis map[string]bool) ([]*ir.Const, bool) {
1914 if visitedPhis == nil {
1915 visitedPhis = map[string]bool{}
1918 switch val := val.(type) {
1920 if visitedPhis[val.Name()] {
1923 visitedPhis[val.Name()] = true
1924 vals := val.Operands(nil)
1925 for _, phival := range vals {
1926 out, ok = consts(*phival, out, visitedPhis)
1932 out = append(out, val)
1934 out, ok = consts(val.X, out, visitedPhis)
1944 uniq := []*ir.Const{out[0]}
1945 for _, val := range out[1:] {
1946 if val.Value == uniq[len(uniq)-1].Value {
1949 uniq = append(uniq, val)
1954 func CheckLoopCondition(pass *analysis.Pass) (interface{}, error) {
1955 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
1956 cb := func(node ast.Node) bool {
1957 loop, ok := node.(*ast.ForStmt)
1961 if loop.Init == nil || loop.Cond == nil || loop.Post == nil {
1964 init, ok := loop.Init.(*ast.AssignStmt)
1965 if !ok || len(init.Lhs) != 1 || len(init.Rhs) != 1 {
1968 cond, ok := loop.Cond.(*ast.BinaryExpr)
1972 x, ok := cond.X.(*ast.Ident)
1976 lhs, ok := init.Lhs[0].(*ast.Ident)
1980 if x.Obj != lhs.Obj {
1983 if _, ok := loop.Post.(*ast.IncDecStmt); !ok {
1987 v, isAddr := fn.ValueForExpr(cond.X)
1988 if v == nil || isAddr {
1991 switch v := v.(type) {
1993 ops := v.Operands(nil)
1997 _, ok := (*ops[0]).(*ir.Const)
2001 sigma, ok := (*ops[1]).(*ir.Sigma)
2011 report.Report(pass, cond, "variable in loop condition never changes")
2015 if source := fn.Source(); source != nil {
2016 ast.Inspect(source, cb)
2022 func CheckArgOverwritten(pass *analysis.Pass) (interface{}, error) {
2023 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2024 cb := func(node ast.Node) bool {
2025 var typ *ast.FuncType
2026 var body *ast.BlockStmt
2027 switch fn := node.(type) {
2038 if len(typ.Params.List) == 0 {
2041 for _, field := range typ.Params.List {
2042 for _, arg := range field.Names {
2043 obj := pass.TypesInfo.ObjectOf(arg)
2044 var irobj *ir.Parameter
2045 for _, param := range fn.Params {
2046 if param.Object() == obj {
2054 refs := irobj.Referrers()
2058 if len(irutil.FilterDebug(*refs)) != 0 {
2062 var assignment ast.Node
2063 ast.Inspect(body, func(node ast.Node) bool {
2064 if assignment != nil {
2067 assign, ok := node.(*ast.AssignStmt)
2071 for _, lhs := range assign.Lhs {
2072 ident, ok := lhs.(*ast.Ident)
2076 if pass.TypesInfo.ObjectOf(ident) == obj {
2083 if assignment != nil {
2084 report.Report(pass, arg, fmt.Sprintf("argument %s is overwritten before first use", arg),
2085 report.Related(assignment, fmt.Sprintf("assignment to %s", arg)))
2091 if source := fn.Source(); source != nil {
2092 ast.Inspect(source, cb)
2098 func CheckIneffectiveLoop(pass *analysis.Pass) (interface{}, error) {
2099 // This check detects some, but not all unconditional loop exits.
2100 // We give up in the following cases:
2102 // - a goto anywhere in the loop. The goto might skip over our
2103 // return, and we don't check that it doesn't.
2105 // - any nested, unlabelled continue, even if it is in another
2107 fn := func(node ast.Node) {
2108 var body *ast.BlockStmt
2109 switch fn := node.(type) {
2115 lint.ExhaustiveTypeSwitch(node)
2120 labels := map[*ast.Object]ast.Stmt{}
2121 ast.Inspect(body, func(node ast.Node) bool {
2122 label, ok := node.(*ast.LabeledStmt)
2126 labels[label.Label.Obj] = label.Stmt
2130 ast.Inspect(body, func(node ast.Node) bool {
2132 var body *ast.BlockStmt
2133 switch node := node.(type) {
2137 case *ast.RangeStmt:
2138 typ := pass.TypesInfo.TypeOf(node.X)
2139 if _, ok := typ.Underlying().(*types.Map); ok {
2140 // looping once over a map is a valid pattern for
2141 // getting an arbitrary element.
2149 if len(body.List) < 2 {
2150 // avoid flagging the somewhat common pattern of using
2151 // a range loop to get the first element in a slice,
2152 // or the first rune in a string.
2155 var unconditionalExit ast.Node
2156 hasBranching := false
2157 for _, stmt := range body.List {
2158 switch stmt := stmt.(type) {
2159 case *ast.BranchStmt:
2162 if stmt.Label == nil || labels[stmt.Label.Obj] == loop {
2163 unconditionalExit = stmt
2165 case token.CONTINUE:
2166 if stmt.Label == nil || labels[stmt.Label.Obj] == loop {
2167 unconditionalExit = nil
2171 case *ast.ReturnStmt:
2172 unconditionalExit = stmt
2173 case *ast.IfStmt, *ast.ForStmt, *ast.RangeStmt, *ast.SwitchStmt, *ast.SelectStmt:
2177 if unconditionalExit == nil || !hasBranching {
2180 ast.Inspect(body, func(node ast.Node) bool {
2181 if branch, ok := node.(*ast.BranchStmt); ok {
2185 unconditionalExit = nil
2187 case token.CONTINUE:
2188 if branch.Label != nil && labels[branch.Label.Obj] != loop {
2191 unconditionalExit = nil
2197 if unconditionalExit != nil {
2198 report.Report(pass, unconditionalExit, "the surrounding loop is unconditionally terminated")
2203 code.Preorder(pass, fn, (*ast.FuncDecl)(nil), (*ast.FuncLit)(nil))
2207 var checkNilContextQ = pattern.MustParse(`(CallExpr fun@(Function _) (Builtin "nil"):_)`)
2209 func CheckNilContext(pass *analysis.Pass) (interface{}, error) {
2210 todo := &ast.CallExpr{
2211 Fun: edit.Selector("context", "TODO"),
2213 bg := &ast.CallExpr{
2214 Fun: edit.Selector("context", "Background"),
2216 fn := func(node ast.Node) {
2217 m, ok := code.Match(pass, checkNilContextQ, node)
2222 call := node.(*ast.CallExpr)
2223 fun, ok := m.State["fun"].(*types.Func)
2225 // it might also be a builtin
2228 sig := fun.Type().(*types.Signature)
2229 if sig.Params().Len() == 0 {
2230 // Our CallExpr might've matched a method expression, like
2231 // (*T).Foo(nil) – here, nil isn't the first argument of
2232 // the Foo method, but the method receiver.
2235 if !typeutil.IsType(sig.Params().At(0).Type(), "context.Context") {
2238 report.Report(pass, call.Args[0],
2239 "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(
2240 edit.Fix("use context.TODO", edit.ReplaceWithNode(pass.Fset, call.Args[0], todo)),
2241 edit.Fix("use context.Background", edit.ReplaceWithNode(pass.Fset, call.Args[0], bg))))
2243 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
2248 checkSeekerQ = pattern.MustParse(`(CallExpr fun@(SelectorExpr _ (Ident "Seek")) [arg1@(SelectorExpr (Ident "io") (Ident (Or "SeekStart" "SeekCurrent" "SeekEnd"))) arg2])`)
2249 checkSeekerR = pattern.MustParse(`(CallExpr fun [arg2 arg1])`)
2252 func CheckSeeker(pass *analysis.Pass) (interface{}, error) {
2253 fn := func(node ast.Node) {
2254 if _, edits, ok := code.MatchAndEdit(pass, checkSeekerQ, checkSeekerR, node); ok {
2255 report.Report(pass, node, "the first argument of io.Seeker is the offset, but an io.Seek* constant is being used instead",
2256 report.Fixes(edit.Fix("swap arguments", edits...)))
2259 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
2263 func CheckIneffectiveAppend(pass *analysis.Pass) (interface{}, error) {
2264 isAppend := func(ins ir.Value) bool {
2265 call, ok := ins.(*ir.Call)
2269 if call.Call.IsInvoke() {
2272 if builtin, ok := call.Call.Value.(*ir.Builtin); !ok || builtin.Name() != "append" {
2278 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2279 for _, block := range fn.Blocks {
2280 for _, ins := range block.Instrs {
2281 val, ok := ins.(ir.Value)
2282 if !ok || !isAppend(val) {
2287 visited := map[ir.Instruction]bool{}
2288 var walkRefs func(refs []ir.Instruction)
2289 walkRefs = func(refs []ir.Instruction) {
2291 for _, ref := range refs {
2296 if _, ok := ref.(*ir.DebugRef); ok {
2299 switch ref := ref.(type) {
2301 walkRefs(*ref.Referrers())
2303 walkRefs(*ref.Referrers())
2308 walkRefs(*ref.Referrers())
2310 case ir.Instruction:
2317 refs := val.Referrers()
2324 report.Report(pass, ins, "this result of append is never used, except maybe in other appends")
2332 func CheckConcurrentTesting(pass *analysis.Pass) (interface{}, error) {
2333 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2334 for _, block := range fn.Blocks {
2335 for _, ins := range block.Instrs {
2336 gostmt, ok := ins.(*ir.Go)
2341 switch val := gostmt.Call.Value.(type) {
2344 case *ir.MakeClosure:
2345 fn = val.Fn.(*ir.Function)
2349 if fn.Blocks == nil {
2352 for _, block := range fn.Blocks {
2353 for _, ins := range block.Instrs {
2354 call, ok := ins.(*ir.Call)
2358 if call.Call.IsInvoke() {
2361 callee := call.Call.StaticCallee()
2365 recv := callee.Signature.Recv()
2369 if !typeutil.IsType(recv.Type(), "*testing.common") {
2372 fn, ok := call.Call.StaticCallee().Object().(*types.Func)
2378 case "FailNow", "Fatal", "Fatalf", "SkipNow", "Skip", "Skipf":
2382 // TODO(dh): don't report multiple diagnostics
2383 // for multiple calls to T.Fatal, but do
2384 // collect all of them as related information
2385 report.Report(pass, gostmt, fmt.Sprintf("the goroutine calls T.%s, which must be called in the same goroutine as the test", name),
2386 report.Related(call, fmt.Sprintf("call to T.%s", name)))
2395 func eachCall(fn *ir.Function, cb func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function)) {
2396 for _, b := range fn.Blocks {
2397 for _, instr := range b.Instrs {
2398 if site, ok := instr.(ir.CallInstruction); ok {
2399 if g := site.Common().StaticCallee(); g != nil {
2407 func CheckCyclicFinalizer(pass *analysis.Pass) (interface{}, error) {
2408 cb := func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function) {
2409 if callee.RelString(nil) != "runtime.SetFinalizer" {
2412 arg0 := site.Common().Args[knowledge.Arg("runtime.SetFinalizer.obj")]
2413 if iface, ok := arg0.(*ir.MakeInterface); ok {
2416 load, ok := arg0.(*ir.Load)
2420 v, ok := load.X.(*ir.Alloc)
2424 arg1 := site.Common().Args[knowledge.Arg("runtime.SetFinalizer.finalizer")]
2425 if iface, ok := arg1.(*ir.MakeInterface); ok {
2428 mc, ok := arg1.(*ir.MakeClosure)
2432 for _, b := range mc.Bindings {
2434 pos := report.DisplayPosition(pass.Fset, mc.Fn.Pos())
2435 report.Report(pass, site, fmt.Sprintf("the finalizer closes over the object, preventing the finalizer from ever running (at %s)", pos))
2439 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2446 func CheckSliceOutOfBounds(pass *analysis.Pass) (interface{}, error) {
2447 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2448 for _, block := range fn.Blocks {
2449 for _, ins := range block.Instrs {
2450 ia, ok := ins.(*ir.IndexAddr)
2454 if _, ok := ia.X.Type().Underlying().(*types.Slice); !ok {
2457 sr, ok1 := c.funcDescs.Get(fn).Ranges[ia.X].(vrp.SliceInterval)
2458 idxr, ok2 := c.funcDescs.Get(fn).Ranges[ia.Index].(vrp.IntInterval)
2459 if !ok1 || !ok2 || !sr.IsKnown() || !idxr.IsKnown() || sr.Length.Empty() || idxr.Empty() {
2462 if idxr.Lower.Cmp(sr.Length.Upper) >= 0 {
2463 report.Nodef(pass, ia, "index out of bounds")
2472 func CheckDeferLock(pass *analysis.Pass) (interface{}, error) {
2473 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2474 for _, block := range fn.Blocks {
2475 instrs := irutil.FilterDebug(block.Instrs)
2476 if len(instrs) < 2 {
2479 for i, ins := range instrs[:len(instrs)-1] {
2480 call, ok := ins.(*ir.Call)
2484 if !irutil.IsCallToAny(call.Common(), "(*sync.Mutex).Lock", "(*sync.RWMutex).RLock") {
2487 nins, ok := instrs[i+1].(*ir.Defer)
2491 if !irutil.IsCallToAny(&nins.Call, "(*sync.Mutex).Lock", "(*sync.RWMutex).RLock") {
2494 if call.Common().Args[0] != nins.Call.Args[0] {
2497 name := shortCallName(call.Common())
2505 report.Report(pass, nins, fmt.Sprintf("deferring %s right after having locked already; did you mean to defer %s?", name, alt))
2512 func CheckNaNComparison(pass *analysis.Pass) (interface{}, error) {
2513 isNaN := func(v ir.Value) bool {
2514 call, ok := v.(*ir.Call)
2518 return irutil.IsCallTo(call.Common(), "math.NaN")
2520 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2521 for _, block := range fn.Blocks {
2522 for _, ins := range block.Instrs {
2523 ins, ok := ins.(*ir.BinOp)
2527 if isNaN(irutil.Flatten(ins.X)) || isNaN(irutil.Flatten(ins.Y)) {
2528 report.Report(pass, ins, "no value is equal to NaN, not even NaN itself")
2536 func CheckInfiniteRecursion(pass *analysis.Pass) (interface{}, error) {
2537 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2538 eachCall(fn, func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function) {
2542 if _, ok := site.(*ir.Go); ok {
2543 // Recursively spawning goroutines doesn't consume
2544 // stack space infinitely, so don't flag it.
2548 block := site.Block()
2550 for _, b := range fn.Blocks {
2551 if block.Dominates(b) {
2554 if len(b.Instrs) == 0 {
2557 if _, ok := b.Control().(*ir.Return); ok {
2565 report.Report(pass, site, "infinite recursive call")
2571 func objectName(obj types.Object) string {
2576 if obj.Pkg() != nil && obj.Pkg().Scope().Lookup(obj.Name()) == obj {
2577 s := obj.Pkg().Path()
2586 func isName(pass *analysis.Pass, expr ast.Expr, name string) bool {
2587 var obj types.Object
2588 switch expr := expr.(type) {
2590 obj = pass.TypesInfo.ObjectOf(expr)
2591 case *ast.SelectorExpr:
2592 obj = pass.TypesInfo.ObjectOf(expr.Sel)
2594 return objectName(obj) == name
2597 func CheckLeakyTimeTick(pass *analysis.Pass) (interface{}, error) {
2598 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2599 if code.IsMainLike(pass) || code.IsInTest(pass, fn) {
2602 for _, block := range fn.Blocks {
2603 for _, ins := range block.Instrs {
2604 call, ok := ins.(*ir.Call)
2605 if !ok || !irutil.IsCallTo(call.Common(), "time.Tick") {
2608 if !irutil.Terminates(call.Parent()) {
2611 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")
2618 var checkDoubleNegationQ = pattern.MustParse(`(UnaryExpr "!" single@(UnaryExpr "!" x))`)
2620 func CheckDoubleNegation(pass *analysis.Pass) (interface{}, error) {
2621 fn := func(node ast.Node) {
2622 if m, ok := code.Match(pass, checkDoubleNegationQ, node); ok {
2623 report.Report(pass, node, "negating a boolean twice has no effect; is this a typo?", report.Fixes(
2624 edit.Fix("turn into single negation", edit.ReplaceWithNode(pass.Fset, node, m.State["single"].(ast.Node))),
2625 edit.Fix("remove double negation", edit.ReplaceWithNode(pass.Fset, node, m.State["x"].(ast.Node)))))
2628 code.Preorder(pass, fn, (*ast.UnaryExpr)(nil))
2632 func CheckRepeatedIfElse(pass *analysis.Pass) (interface{}, error) {
2633 seen := map[ast.Node]bool{}
2635 var collectConds func(ifstmt *ast.IfStmt, conds []ast.Expr) ([]ast.Expr, bool)
2636 collectConds = func(ifstmt *ast.IfStmt, conds []ast.Expr) ([]ast.Expr, bool) {
2638 // Bail if any if-statement has an Init statement or side effects in its condition
2639 if ifstmt.Init != nil {
2642 if code.MayHaveSideEffects(pass, ifstmt.Cond, nil) {
2646 conds = append(conds, ifstmt.Cond)
2647 if elsestmt, ok := ifstmt.Else.(*ast.IfStmt); ok {
2648 return collectConds(elsestmt, conds)
2652 fn := func(node ast.Node) {
2653 ifstmt := node.(*ast.IfStmt)
2655 // this if-statement is part of an if/else-if chain that we've already processed
2658 if ifstmt.Else == nil {
2659 // there can be at most one condition
2662 conds, ok := collectConds(ifstmt, nil)
2669 counts := map[string]int{}
2670 for _, cond := range conds {
2671 s := report.Render(pass, cond)
2674 report.Report(pass, cond, "this condition occurs multiple times in this if/else if chain")
2678 code.Preorder(pass, fn, (*ast.IfStmt)(nil))
2682 func CheckSillyBitwiseOps(pass *analysis.Pass) (interface{}, error) {
2683 // FIXME(dh): what happened here?
2685 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2686 for _, block := range fn.Blocks {
2687 for _, ins := range block.Instrs {
2688 ins, ok := ins.(*ir.BinOp)
2693 if c, ok := ins.Y.(*ir.Const); !ok || c.Value == nil || c.Value.Kind() != constant.Int || c.Uint64() != 0 {
2697 case token.AND, token.OR, token.XOR:
2699 // we do not flag shifts because too often, x<<0 is part
2700 // of a pattern, x<<0, x<<8, x<<16, ...
2703 path, _ := goastutil.PathEnclosingInterval(code.File(pass, ins), ins.Pos(), ins.Pos())
2708 if node, ok := path[0].(*ast.BinaryExpr); !ok || !astutil.IsIntLiteral(node.Y, "0") {
2714 report.Report(pass, ins, "x & 0 always equals 0")
2715 case token.OR, token.XOR:
2716 report.Report(pass, ins, fmt.Sprintf("x %s 0 always equals x", ins.Op))
2722 fn := func(node ast.Node) {
2723 binop := node.(*ast.BinaryExpr)
2724 b, ok := pass.TypesInfo.TypeOf(binop).Underlying().(*types.Basic)
2728 if (b.Info() & types.IsInteger) == 0 {
2732 case token.AND, token.OR, token.XOR:
2734 // we do not flag shifts because too often, x<<0 is part
2735 // of a pattern, x<<0, x<<8, x<<16, ...
2738 switch y := binop.Y.(type) {
2740 obj, ok := pass.TypesInfo.ObjectOf(y).(*types.Const)
2744 if obj.Pkg() != pass.Pkg {
2745 // identifier was dot-imported
2748 if v, _ := constant.Int64Val(obj.Val()); v != 0 {
2751 path, _ := goastutil.PathEnclosingInterval(code.File(pass, obj), obj.Pos(), obj.Pos())
2755 spec, ok := path[1].(*ast.ValueSpec)
2759 if len(spec.Names) != 1 || len(spec.Values) != 1 {
2760 // TODO(dh): we could support this
2763 ident, ok := spec.Values[0].(*ast.Ident)
2767 if !isIota(pass.TypesInfo.ObjectOf(ident)) {
2772 report.Report(pass, node,
2773 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)))
2774 case token.OR, token.XOR:
2775 report.Report(pass, node,
2776 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)))
2779 if !astutil.IsIntLiteral(binop.Y, "0") {
2784 report.Report(pass, node, fmt.Sprintf("%s always equals 0", report.Render(pass, binop)))
2785 case token.OR, token.XOR:
2786 report.Report(pass, node, fmt.Sprintf("%s always equals %s", report.Render(pass, binop), report.Render(pass, binop.X)))
2792 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil))
2796 func isIota(obj types.Object) bool {
2797 if obj.Name() != "iota" {
2800 c, ok := obj.(*types.Const)
2804 return c.Pkg() == nil
2807 func CheckNonOctalFileMode(pass *analysis.Pass) (interface{}, error) {
2808 fn := func(node ast.Node) {
2809 call := node.(*ast.CallExpr)
2810 sig, ok := pass.TypesInfo.TypeOf(call.Fun).(*types.Signature)
2814 n := sig.Params().Len()
2815 for i := 0; i < n; i++ {
2816 typ := sig.Params().At(i).Type()
2817 if !typeutil.IsType(typ, "os.FileMode") {
2821 lit, ok := call.Args[i].(*ast.BasicLit)
2825 if len(lit.Value) == 3 &&
2826 lit.Value[0] != '0' &&
2827 lit.Value[0] >= '0' && lit.Value[0] <= '7' &&
2828 lit.Value[1] >= '0' && lit.Value[1] <= '7' &&
2829 lit.Value[2] >= '0' && lit.Value[2] <= '7' {
2831 v, err := strconv.ParseInt(lit.Value, 10, 64)
2835 report.Report(pass, call.Args[i], fmt.Sprintf("file mode '%s' evaluates to %#o; did you mean '0%s'?", lit.Value, v, lit.Value),
2836 report.Fixes(edit.Fix("fix octal literal", edit.ReplaceWithString(pass.Fset, call.Args[i], "0"+lit.Value))))
2840 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
2844 func CheckPureFunctions(pass *analysis.Pass) (interface{}, error) {
2845 pure := pass.ResultOf[facts.Purity].(facts.PurityResult)
2848 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
2849 if code.IsInTest(pass, fn) {
2850 params := fn.Signature.Params()
2851 for i := 0; i < params.Len(); i++ {
2852 param := params.At(i)
2853 if typeutil.IsType(param.Type(), "*testing.B") {
2854 // Ignore discarded pure functions in code related
2855 // to benchmarks. Instead of matching BenchmarkFoo
2856 // functions, we match any function accepting a
2857 // *testing.B. Benchmarks sometimes call generic
2858 // functions for doing the actual work, and
2859 // checking for the parameter is a lot easier and
2860 // faster than analyzing call trees.
2866 for _, b := range fn.Blocks {
2867 for _, ins := range b.Instrs {
2868 ins, ok := ins.(*ir.Call)
2872 refs := ins.Referrers()
2873 if refs == nil || len(irutil.FilterDebug(*refs)) > 0 {
2877 callee := ins.Common().StaticCallee()
2881 if callee.Object() == nil {
2882 // TODO(dh): support anonymous functions
2885 if _, ok := pure[callee.Object().(*types.Func)]; ok {
2886 if pass.Pkg.Path() == "fmt_test" && callee.Object().(*types.Func).FullName() == "fmt.Sprintf" {
2887 // special case for benchmarks in the fmt package
2890 report.Report(pass, ins, fmt.Sprintf("%s is a pure function but its return value is ignored", callee.Name()))
2898 func CheckDeprecated(pass *analysis.Pass) (interface{}, error) {
2899 deprs := pass.ResultOf[facts.Deprecated].(facts.DeprecatedResult)
2901 // Selectors can appear outside of function literals, e.g. when
2902 // declaring package level variables.
2904 var tfn types.Object
2906 fn := func(node ast.Node, push bool) bool {
2915 if fn, ok := node.(*ast.FuncDecl); ok {
2916 tfn = pass.TypesInfo.ObjectOf(fn.Name)
2918 sel, ok := node.(*ast.SelectorExpr)
2923 obj := pass.TypesInfo.ObjectOf(sel.Sel)
2924 if obj.Pkg() == nil {
2927 if pass.Pkg == obj.Pkg() || obj.Pkg().Path()+"_test" == pass.Pkg.Path() {
2928 // Don't flag stuff in our own package
2931 if depr, ok := deprs.Objects[obj]; ok {
2932 std, ok := knowledge.StdlibDeprecations[code.SelectorName(pass, sel)]
2934 switch std.AlternativeAvailableSince {
2935 case knowledge.DeprecatedNeverUse:
2936 // This should never be used, regardless of the
2937 // targeted Go version. Examples include insecure
2938 // cryptography or inherently broken APIs.
2940 // We always want to flag these.
2941 case knowledge.DeprecatedUseNoLonger:
2942 // This should no longer be used. Using it with
2943 // older Go versions might still make sense.
2944 if !code.IsGoVersion(pass, std.DeprecatedSince) {
2948 if std.AlternativeAvailableSince < 0 {
2949 panic(fmt.Sprintf("unhandled case %d", std.AlternativeAvailableSince))
2951 // Look for the first available alternative, not the first
2952 // version something was deprecated in. If a function was
2953 // deprecated in Go 1.6, an alternative has been available
2954 // already in 1.0, and we're targeting 1.2, it still
2955 // makes sense to use the alternative from 1.0, to be
2957 if !code.IsGoVersion(pass, std.AlternativeAvailableSince) {
2962 if ok && !code.IsGoVersion(pass, std.AlternativeAvailableSince) {
2967 if _, ok := deprs.Objects[tfn]; ok {
2968 // functions that are deprecated may use deprecated
2975 if std.AlternativeAvailableSince == knowledge.DeprecatedNeverUse {
2976 report.Report(pass, sel, fmt.Sprintf("%s has been deprecated since Go 1.%d because it shouldn't be used: %s", report.Render(pass, sel), std.DeprecatedSince, depr.Msg))
2977 } else if std.AlternativeAvailableSince == std.DeprecatedSince || std.AlternativeAvailableSince == knowledge.DeprecatedUseNoLonger {
2978 report.Report(pass, sel, fmt.Sprintf("%s has been deprecated since Go 1.%d: %s", report.Render(pass, sel), std.DeprecatedSince, depr.Msg))
2980 report.Report(pass, sel, fmt.Sprintf("%s has been deprecated since Go 1.%d and an alternative has been available since Go 1.%d: %s", report.Render(pass, sel), std.DeprecatedSince, std.AlternativeAvailableSince, depr.Msg))
2983 report.Report(pass, sel, fmt.Sprintf("%s is deprecated: %s", report.Render(pass, sel), depr.Msg))
2990 fn2 := func(node ast.Node) {
2991 spec := node.(*ast.ImportSpec)
2992 var imp *types.Package
2993 if spec.Name != nil {
2994 imp = pass.TypesInfo.ObjectOf(spec.Name).(*types.PkgName).Imported()
2996 imp = pass.TypesInfo.Implicits[spec].(*types.PkgName).Imported()
2999 p := spec.Path.Value
3000 path := p[1 : len(p)-1]
3001 if depr, ok := deprs.Packages[imp]; ok {
3002 if path == "github.com/golang/protobuf/proto" {
3003 gen, ok := code.Generator(pass, spec.Path.Pos())
3004 if ok && gen == facts.ProtocGenGo {
3008 report.Report(pass, spec, fmt.Sprintf("package %s is deprecated: %s", path, depr.Msg))
3011 pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes(nil, fn)
3012 code.Preorder(pass, fn2, (*ast.ImportSpec)(nil))
3016 func callChecker(rules map[string]CallCheck) func(pass *analysis.Pass) (interface{}, error) {
3017 return func(pass *analysis.Pass) (interface{}, error) {
3018 return checkCalls(pass, rules)
3022 func checkCalls(pass *analysis.Pass, rules map[string]CallCheck) (interface{}, error) {
3023 cb := func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function) {
3024 obj, ok := callee.Object().(*types.Func)
3029 r, ok := rules[typeutil.FuncName(obj)]
3033 var args []*Argument
3034 irargs := site.Common().Args
3035 if callee.Signature.Recv() != nil {
3038 for _, arg := range irargs {
3039 if iarg, ok := arg.(*ir.MakeInterface); ok {
3042 args = append(args, &Argument{Value: Value{arg}})
3048 Parent: site.Parent(),
3052 var astcall *ast.CallExpr
3053 switch source := site.Source().(type) {
3056 case *ast.DeferStmt:
3057 astcall = source.Call
3059 astcall = source.Call
3061 // TODO(dh): I am not sure this can actually happen. If it
3062 // can't, we should remove this case, and also stop
3063 // checking for astcall == nil in the code that follows.
3065 panic(fmt.Sprintf("unhandled case %T", source))
3068 for idx, arg := range call.Args {
3069 for _, e := range arg.invalids {
3071 report.Report(pass, astcall.Args[idx], e)
3073 report.Report(pass, site, e)
3077 for _, e := range call.invalids {
3078 report.Report(pass, call.Instr, e)
3081 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
3087 func shortCallName(call *ir.CallCommon) string {
3088 if call.IsInvoke() {
3091 switch v := call.Value.(type) {
3093 fn, ok := v.Object().(*types.Func)
3104 func CheckWriterBufferModified(pass *analysis.Pass) (interface{}, error) {
3105 // TODO(dh): this might be a good candidate for taint analysis.
3106 // Taint the argument as MUST_NOT_MODIFY, then propagate that
3107 // through functions like bytes.Split
3109 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
3111 if fn.Name() != "Write" || sig.Recv() == nil || sig.Params().Len() != 1 || sig.Results().Len() != 2 {
3114 tArg, ok := sig.Params().At(0).Type().(*types.Slice)
3118 if basic, ok := tArg.Elem().(*types.Basic); !ok || basic.Kind() != types.Byte {
3121 if basic, ok := sig.Results().At(0).Type().(*types.Basic); !ok || basic.Kind() != types.Int {
3124 if named, ok := sig.Results().At(1).Type().(*types.Named); !ok || !typeutil.IsType(named, "error") {
3128 for _, block := range fn.Blocks {
3129 for _, ins := range block.Instrs {
3130 switch ins := ins.(type) {
3132 addr, ok := ins.Addr.(*ir.IndexAddr)
3136 if addr.X != fn.Params[1] {
3139 report.Report(pass, ins, "io.Writer.Write must not modify the provided buffer, not even temporarily")
3141 if !irutil.IsCallTo(ins.Common(), "append") {
3144 if ins.Common().Args[0] != fn.Params[1] {
3147 report.Report(pass, ins, "io.Writer.Write must not modify the provided buffer, not even temporarily")
3155 func loopedRegexp(name string) CallCheck {
3156 return func(call *Call) {
3157 if extractConst(call.Args[0].Value.Value) == nil {
3160 if !isInLoop(call.Instr.Block()) {
3163 call.Invalid(fmt.Sprintf("calling %s in a loop has poor performance, consider using regexp.Compile", name))
3167 func CheckEmptyBranch(pass *analysis.Pass) (interface{}, error) {
3168 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
3169 if fn.Source() == nil {
3172 if irutil.IsExample(fn) {
3175 cb := func(node ast.Node) bool {
3176 ifstmt, ok := node.(*ast.IfStmt)
3180 if ifstmt.Else != nil {
3181 b, ok := ifstmt.Else.(*ast.BlockStmt)
3182 if !ok || len(b.List) != 0 {
3185 report.Report(pass, ifstmt.Else, "empty branch", report.FilterGenerated(), report.ShortRange())
3187 if len(ifstmt.Body.List) != 0 {
3190 report.Report(pass, ifstmt, "empty branch", report.FilterGenerated(), report.ShortRange())
3193 if source := fn.Source(); source != nil {
3194 ast.Inspect(source, cb)
3200 func CheckMapBytesKey(pass *analysis.Pass) (interface{}, error) {
3201 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
3202 for _, b := range fn.Blocks {
3204 for _, ins := range b.Instrs {
3205 // find []byte -> string conversions
3206 conv, ok := ins.(*ir.Convert)
3207 if !ok || conv.Type() != types.Universe.Lookup("string").Type() {
3210 if s, ok := conv.X.Type().(*types.Slice); !ok || s.Elem() != types.Universe.Lookup("byte").Type() {
3213 refs := conv.Referrers()
3214 // need at least two (DebugRef) references: the
3215 // conversion and the *ast.Ident
3216 if refs == nil || len(*refs) < 2 {
3220 // skip first reference, that's the conversion itself
3221 for _, ref := range (*refs)[1:] {
3222 switch ref := ref.(type) {
3224 if _, ok := ref.Expr.(*ast.Ident); !ok {
3225 // the string seems to be used somewhere
3226 // unexpected; the default branch should
3227 // catch this already, but be safe
3234 // the string is used somewhere else than a
3240 // the result of the conversion wasn't assigned to an
3245 report.Report(pass, conv, "m[string(key)] would be more efficient than k := string(key); m[k]")
3252 func CheckRangeStringRunes(pass *analysis.Pass) (interface{}, error) {
3253 return sharedcheck.CheckRangeStringRunes(pass)
3256 func CheckSelfAssignment(pass *analysis.Pass) (interface{}, error) {
3257 pure := pass.ResultOf[facts.Purity].(facts.PurityResult)
3259 fn := func(node ast.Node) {
3260 assign := node.(*ast.AssignStmt)
3261 if assign.Tok != token.ASSIGN || len(assign.Lhs) != len(assign.Rhs) {
3264 for i, lhs := range assign.Lhs {
3265 rhs := assign.Rhs[i]
3266 if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) {
3269 if code.MayHaveSideEffects(pass, lhs, pure) || code.MayHaveSideEffects(pass, rhs, pure) {
3273 rlh := report.Render(pass, lhs)
3274 rrh := report.Render(pass, rhs)
3276 report.Report(pass, assign, fmt.Sprintf("self-assignment of %s to %s", rrh, rlh), report.FilterGenerated())
3280 code.Preorder(pass, fn, (*ast.AssignStmt)(nil))
3284 func buildTagsIdentical(s1, s2 []string) bool {
3285 if len(s1) != len(s2) {
3288 s1s := make([]string, len(s1))
3291 s2s := make([]string, len(s2))
3294 for i, s := range s1s {
3302 func CheckDuplicateBuildConstraints(pass *analysis.Pass) (interface{}, error) {
3303 for _, f := range pass.Files {
3304 constraints := buildTags(f)
3305 for i, constraint1 := range constraints {
3306 for j, constraint2 := range constraints {
3310 if buildTagsIdentical(constraint1, constraint2) {
3311 msg := fmt.Sprintf("identical build constraints %q and %q",
3312 strings.Join(constraint1, " "),
3313 strings.Join(constraint2, " "))
3314 report.Report(pass, f, msg, report.FilterGenerated(), report.ShortRange())
3322 func CheckSillyRegexp(pass *analysis.Pass) (interface{}, error) {
3323 // We could use the rule checking engine for this, but the
3324 // arguments aren't really invalid.
3325 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
3326 for _, b := range fn.Blocks {
3327 for _, ins := range b.Instrs {
3328 call, ok := ins.(*ir.Call)
3332 if !irutil.IsCallToAny(call.Common(), "regexp.MustCompile", "regexp.Compile", "regexp.Match", "regexp.MatchReader", "regexp.MatchString") {
3335 c, ok := call.Common().Args[0].(*ir.Const)
3339 s := constant.StringVal(c.Value)
3340 re, err := syntax.Parse(s, 0)
3344 if re.Op != syntax.OpLiteral && re.Op != syntax.OpEmptyMatch {
3347 report.Report(pass, call, "regular expression does not contain any meta characters")
3354 func CheckMissingEnumTypesInDeclaration(pass *analysis.Pass) (interface{}, error) {
3355 convertibleTo := func(V, T types.Type) bool {
3356 if types.ConvertibleTo(V, T) {
3359 // Go <1.16 returns false for untyped string to string conversion
3360 if V, ok := V.(*types.Basic); ok && V.Kind() == types.UntypedString {
3361 if T, ok := T.Underlying().(*types.Basic); ok && T.Kind() == types.String {
3367 fn := func(node ast.Node) {
3368 decl := node.(*ast.GenDecl)
3369 if !decl.Lparen.IsValid() {
3372 if decl.Tok != token.CONST {
3376 groups := astutil.GroupSpecs(pass.Fset, decl.Specs)
3378 for _, group := range groups {
3382 if group[0].(*ast.ValueSpec).Type == nil {
3383 // first constant doesn't have a type
3387 firstType := pass.TypesInfo.TypeOf(group[0].(*ast.ValueSpec).Values[0])
3388 for i, spec := range group {
3389 spec := spec.(*ast.ValueSpec)
3390 if i > 0 && spec.Type != nil {
3393 if len(spec.Names) != 1 || len(spec.Values) != 1 {
3397 if !convertibleTo(pass.TypesInfo.TypeOf(spec.Values[0]), firstType) {
3401 switch v := spec.Values[0].(type) {
3403 case *ast.UnaryExpr:
3404 if _, ok := v.X.(*ast.BasicLit); !ok {
3408 // if it's not a literal it might be typed, such as
3409 // time.Microsecond = 1000 * Nanosecond
3413 var edits []analysis.TextEdit
3414 typ := group[0].(*ast.ValueSpec).Type
3415 for _, spec := range group[1:] {
3416 nspec := *spec.(*ast.ValueSpec)
3418 // The position of `spec` node excludes comments (if any).
3419 // However, on generating the source back from the node, the comments are included. Setting `Comment` to nil ensures deduplication of comments.
3421 edits = append(edits, edit.ReplaceWithNode(pass.Fset, spec, &nspec))
3423 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...)))
3426 code.Preorder(pass, fn, (*ast.GenDecl)(nil))
3430 func CheckTimerResetReturnValue(pass *analysis.Pass) (interface{}, error) {
3431 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
3432 for _, block := range fn.Blocks {
3433 for _, ins := range block.Instrs {
3434 call, ok := ins.(*ir.Call)
3438 if !irutil.IsCallTo(call.Common(), "(*time.Timer).Reset") {
3441 refs := call.Referrers()
3445 for _, ref := range irutil.FilterDebug(*refs) {
3446 ifstmt, ok := ref.(*ir.If)
3452 for _, succ := range ifstmt.Block().Succs {
3453 if len(succ.Preds) != 1 {
3454 // Merge point, not a branch in the
3455 // syntactical sense.
3457 // FIXME(dh): this is broken for if
3458 // statements a la "if x || y"
3461 irutil.Walk(succ, func(b *ir.BasicBlock) bool {
3462 if !succ.Dominates(b) {
3463 // We've reached the end of the branch
3466 for _, ins := range b.Instrs {
3467 // TODO(dh): we should check that
3468 // we're receiving from the channel of
3469 // a time.Timer to further reduce
3470 // false positives. Not a key
3471 // priority, considering the rarity of
3472 // Reset and the tiny likeliness of a
3474 if ins, ok := ins.(*ir.Recv); ok && typeutil.IsType(ins.Chan.Type(), "<-chan time.Time") {
3484 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")
3494 checkToLowerToUpperComparisonQ = pattern.MustParse(`
3496 (CallExpr fun@(Function (Or "strings.ToLower" "strings.ToUpper")) [a])
3498 (CallExpr fun [b]))`)
3499 checkToLowerToUpperComparisonR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "strings") (Ident "EqualFold")) [a b])`)
3502 func CheckToLowerToUpperComparison(pass *analysis.Pass) (interface{}, error) {
3503 fn := func(node ast.Node) {
3504 m, ok := code.Match(pass, checkToLowerToUpperComparisonQ, node)
3508 rn := pattern.NodeToAST(checkToLowerToUpperComparisonR.Root, m.State).(ast.Expr)
3509 if m.State["tok"].(token.Token) == token.NEQ {
3510 rn = &ast.UnaryExpr{
3516 report.Report(pass, node, "should use strings.EqualFold instead", report.Fixes(edit.Fix("replace with strings.EqualFold", edit.ReplaceWithNode(pass.Fset, node, rn))))
3519 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil))
3523 func CheckUnreachableTypeCases(pass *analysis.Pass) (interface{}, error) {
3524 // 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.
3525 subsumes := func(T, V types.Type) bool {
3526 tIface, ok := T.Underlying().(*types.Interface)
3531 return types.Implements(V, tIface)
3534 subsumesAny := func(Ts, Vs []types.Type) (types.Type, types.Type, bool) {
3535 for _, T := range Ts {
3536 for _, V := range Vs {
3543 return nil, nil, false
3546 fn := func(node ast.Node) {
3547 tsStmt := node.(*ast.TypeSwitchStmt)
3549 type ccAndTypes struct {
3554 // All asserted types in the order of case clauses.
3555 ccs := make([]ccAndTypes, 0, len(tsStmt.Body.List))
3556 for _, stmt := range tsStmt.Body.List {
3557 cc, _ := stmt.(*ast.CaseClause)
3559 // Exclude the 'default' case.
3560 if len(cc.List) == 0 {
3564 Ts := make([]types.Type, 0, len(cc.List))
3565 for _, expr := range cc.List {
3566 // Exclude the 'nil' value from any 'case' statement (it is always reachable).
3567 if typ := pass.TypesInfo.TypeOf(expr); typ != types.Typ[types.UntypedNil] {
3568 Ts = append(Ts, typ)
3572 ccs = append(ccs, ccAndTypes{cc: cc, types: Ts})
3576 // Zero or one case clauses, nothing to check.
3580 // Check if case clauses following cc have types that are subsumed by cc.
3581 for i, cc := range ccs[:len(ccs)-1] {
3582 for _, next := range ccs[i+1:] {
3583 if T, V, yes := subsumesAny(cc.types, next.types); yes {
3584 report.Report(pass, next.cc, fmt.Sprintf("unreachable case clause: %s will always match before %s", T.String(), V.String()),
3585 report.ShortRange())
3591 code.Preorder(pass, fn, (*ast.TypeSwitchStmt)(nil))
3595 var checkSingleArgAppendQ = pattern.MustParse(`(CallExpr (Builtin "append") [_])`)
3597 func CheckSingleArgAppend(pass *analysis.Pass) (interface{}, error) {
3598 fn := func(node ast.Node) {
3599 _, ok := code.Match(pass, checkSingleArgAppendQ, node)
3603 report.Report(pass, node, "x = append(y) is equivalent to x = y", report.FilterGenerated())
3605 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
3609 func CheckStructTags(pass *analysis.Pass) (interface{}, error) {
3610 importsGoFlags := false
3612 // we use the AST instead of (*types.Package).Imports to work
3613 // around vendored packages in GOPATH mode. A vendored package's
3614 // path will include the vendoring subtree as a prefix.
3615 for _, f := range pass.Files {
3616 for _, imp := range f.Imports {
3618 if v[1:len(v)-1] == "github.com/jessevdk/go-flags" {
3619 importsGoFlags = true
3625 fn := func(node ast.Node) {
3626 for _, field := range node.(*ast.StructType).Fields.List {
3627 if field.Tag == nil {
3630 tags, err := parseStructTag(field.Tag.Value[1 : len(field.Tag.Value)-1])
3632 report.Report(pass, field.Tag, fmt.Sprintf("unparseable struct tag: %s", err))
3635 for k, v := range tags {
3637 isGoFlagsTag := importsGoFlags &&
3638 (k == "choice" || k == "optional-value" || k == "default")
3640 report.Report(pass, field.Tag, fmt.Sprintf("duplicate struct tag %q", k))
3646 checkJSONTag(pass, field, v[0])
3648 checkXMLTag(pass, field, v[0])
3653 code.Preorder(pass, fn, (*ast.StructType)(nil))
3657 func checkJSONTag(pass *analysis.Pass, field *ast.Field, tag string) {
3658 if pass.Pkg.Path() == "encoding/json" || pass.Pkg.Path() == "encoding/json_test" {
3659 // don't flag malformed JSON tags in the encoding/json
3660 // package; it knows what it is doing, and it is testing
3664 //lint:ignore SA9003 TODO(dh): should we flag empty tags?
3667 fields := strings.Split(tag, ",")
3668 for _, r := range fields[0] {
3669 if !unicode.IsLetter(r) && !unicode.IsDigit(r) && !strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", r) {
3670 report.Report(pass, field.Tag, fmt.Sprintf("invalid JSON field name %q", fields[0]))
3674 for _, s := range fields[1:] {
3679 // allow stuff like "-,"
3682 // only for string, floating point, integer and bool
3683 T := typeutil.Dereference(pass.TypesInfo.TypeOf(field.Type).Underlying()).Underlying()
3684 basic, ok := T.(*types.Basic)
3685 if !ok || (basic.Info()&(types.IsBoolean|types.IsInteger|types.IsFloat|types.IsString)) == 0 {
3686 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")
3691 report.Report(pass, field.Tag, fmt.Sprintf("unknown JSON option %q", s))
3695 report.Report(pass, field.Tag, `duplicate JSON option "omitempty"`)
3698 report.Report(pass, field.Tag, `duplicate JSON option "string"`)
3701 report.Report(pass, field.Tag, `duplicate JSON option "inline"`)
3705 func checkXMLTag(pass *analysis.Pass, field *ast.Field, tag string) {
3706 //lint:ignore SA9003 TODO(dh): should we flag empty tags?
3709 fields := strings.Split(tag, ",")
3710 counts := map[string]int{}
3711 var exclusives []string
3712 for _, s := range fields[1:] {
3714 case "attr", "chardata", "cdata", "innerxml", "comment":
3717 exclusives = append(exclusives, s)
3719 case "omitempty", "any":
3723 report.Report(pass, field.Tag, fmt.Sprintf("unknown XML option %q", s))
3726 for k, v := range counts {
3728 report.Report(pass, field.Tag, fmt.Sprintf("duplicate XML option %q", k))
3731 if len(exclusives) > 1 {
3732 report.Report(pass, field.Tag, fmt.Sprintf("XML options %s are mutually exclusive", strings.Join(exclusives, " and ")))
3736 func CheckImpossibleTypeAssertion(pass *analysis.Pass) (interface{}, error) {
3741 msc := &pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg.Prog.MethodSets
3742 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
3743 for _, b := range fn.Blocks {
3744 for _, instr := range b.Instrs {
3745 assert, ok := instr.(*ir.TypeAssert)
3750 left := assert.X.Type()
3751 right := assert.AssertedType
3752 righti, ok := right.Underlying().(*types.Interface)
3755 // We only care about interface->interface
3756 // assertions. The Go compiler already catches
3757 // impossible interface->concrete assertions.
3761 ms := msc.MethodSet(left)
3762 for i := 0; i < righti.NumMethods(); i++ {
3763 mr := righti.Method(i)
3764 sel := ms.Lookup(mr.Pkg(), mr.Name())
3768 ml := sel.Obj().(*types.Func)
3769 if types.AssignableTo(ml.Type(), mr.Type()) {
3773 wrong = append(wrong, entry{ml, mr})
3776 if len(wrong) != 0 {
3777 s := fmt.Sprintf("impossible type assertion; %s and %s contradict each other:",
3778 types.TypeString(left, types.RelativeTo(pass.Pkg)),
3779 types.TypeString(right, types.RelativeTo(pass.Pkg)))
3780 for _, e := range wrong {
3781 s += fmt.Sprintf("\n\twrong type for %s method", e.l.Name())
3782 s += fmt.Sprintf("\n\t\thave %s", e.l.Type())
3783 s += fmt.Sprintf("\n\t\twant %s", e.r.Type())
3785 report.Report(pass, assert, s)
3793 func checkWithValueKey(call *Call) {
3795 T := arg.Value.Value.Type()
3796 if T, ok := T.(*types.Basic); ok {
3798 fmt.Sprintf("should not use built-in type %s as key for value; define your own type to avoid collisions", T))
3800 if !types.Comparable(T) {
3801 arg.Invalid(fmt.Sprintf("keys used with context.WithValue must be comparable, but type %s is not comparable", T))
3805 func CheckMaybeNil(pass *analysis.Pass) (interface{}, error) {
3806 // This is an extremely trivial check that doesn't try to reason
3807 // about control flow. That is, phis and sigmas do not propagate
3808 // any information. As such, we can flag this:
3811 // if x == nil { return }
3813 // but we cannot flag this:
3815 // if x == nil { println(x) }
3818 // nor many other variations of conditional uses of or assignments to x.
3820 // However, even this trivial implementation finds plenty of
3821 // real-world bugs, such as dereference before nil pointer check,
3822 // or using t.Error instead of t.Fatal when encountering nil
3825 // On the flip side, our naive implementation avoids false positives in branches, such as
3827 // if x != nil { _ = *x }
3829 // due to the same lack of propagating information through sigma
3830 // nodes. x inside the branch will be independent of the x in the
3831 // nil pointer check.
3834 // We could implement a more powerful check, but then we'd be
3835 // getting false positives instead of false negatives because
3836 // we're incapable of deducing relationships between variables.
3837 // For example, a function might return a pointer and an error,
3838 // and the error being nil guarantees that the pointer is not nil.
3839 // Depending on the surrounding code, the pointer may still end up
3840 // being checked against nil in one place, and guarded by a check
3841 // on the error in another, which would lead to us marking some
3844 // Unfortunately, simply hard-coding the relationship between
3845 // return values wouldn't eliminate all false positives, either.
3846 // Many other more subtle relationships exist. An abridged example
3849 // if a == nil && b == nil { return }
3851 // if c != "" { _ = *a }
3853 // where `fn` is guaranteed to return a non-empty string if a
3856 // We choose to err on the side of false negatives.
3858 isNilConst := func(v ir.Value) bool {
3859 if typeutil.IsPointerLike(v.Type()) {
3860 if k, ok := v.(*ir.Const); ok {
3867 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
3868 maybeNil := map[ir.Value]ir.Instruction{}
3869 for _, b := range fn.Blocks {
3870 for _, instr := range b.Instrs {
3871 if instr, ok := instr.(*ir.BinOp); ok {
3873 if isNilConst(instr.X) {
3875 } else if isNilConst(instr.Y) {
3878 maybeNil[ptr] = instr
3883 for _, b := range fn.Blocks {
3884 for _, instr := range b.Instrs {
3886 switch instr := instr.(type) {
3898 case *ir.Alloc, *ir.FieldAddr, *ir.IndexAddr:
3899 // these cannot be nil
3902 if r, ok := maybeNil[ptr]; ok {
3903 report.Report(pass, instr, "possible nil pointer dereference",
3904 report.Related(r, "this check suggests that the pointer can be nil"))
3914 var checkAddressIsNilQ = pattern.MustParse(
3920 func CheckAddressIsNil(pass *analysis.Pass) (interface{}, error) {
3921 fn := func(node ast.Node) {
3922 _, ok := code.Match(pass, checkAddressIsNilQ, node)
3926 report.Report(pass, node, "the address of a variable cannot be nil")
3928 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil))
3933 checkFixedLengthTypeShiftQ = pattern.MustParse(`
3935 (AssignStmt _ (Or ">>=" "<<=") _)
3936 (BinaryExpr _ (Or ">>" "<<") _))
3940 func CheckStaticBitShift(pass *analysis.Pass) (interface{}, error) {
3941 isDubiousShift := func(x, y ast.Expr) (int64, int64, bool) {
3942 typ, ok := pass.TypesInfo.TypeOf(x).Underlying().(*types.Basic)
3947 case types.Int8, types.Int16, types.Int32, types.Int64,
3948 types.Uint8, types.Uint16, types.Uint32, types.Uint64:
3949 // We're only interested in fixed–size types.
3954 const bitsInByte = 8
3955 typeBits := pass.TypesSizes.Sizeof(typ) * bitsInByte
3957 shiftLength, ok := code.ExprToInt(pass, y)
3962 return typeBits, shiftLength, shiftLength >= typeBits
3965 fn := func(node ast.Node) {
3966 if _, ok := code.Match(pass, checkFixedLengthTypeShiftQ, node); !ok {
3970 switch e := node.(type) {
3971 case *ast.AssignStmt:
3972 if size, shift, yes := isDubiousShift(e.Lhs[0], e.Rhs[0]); yes {
3973 report.Report(pass, e, fmt.Sprintf("shifting %d-bit value by %d bits will always clear it", size, shift))
3975 case *ast.BinaryExpr:
3976 if size, shift, yes := isDubiousShift(e.X, e.Y); yes {
3977 report.Report(pass, e, fmt.Sprintf("shifting %d-bit value by %d bits will always clear it", size, shift))
3981 code.Preorder(pass, fn, (*ast.AssignStmt)(nil), (*ast.BinaryExpr)(nil))
3986 func findSliceLenChecks(pass *analysis.Pass) {
3987 // mark all function parameters that have to be of even length
3988 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
3989 for _, b := range fn.Blocks {
3990 // all paths go through this block
3991 if !b.Dominates(fn.Exit) {
3996 ifi, ok := b.Control().(*ir.If)
4000 cmp, ok := ifi.Cond.(*ir.BinOp)
4016 rem, ok1 := cmp.X.(*ir.BinOp)
4017 k, ok2 := cmp.Y.(*ir.Const)
4022 rem, ok1 = cmp.Y.(*ir.BinOp)
4023 k, ok2 = cmp.X.(*ir.Const)
4025 if !ok1 || !ok2 || rem.Op != token.REM || k.Value.Kind() != constant.Int || k.Uint64() != needle {
4028 k, ok = rem.Y.(*ir.Const)
4029 if !ok || k.Value.Kind() != constant.Int || k.Uint64() != 2 {
4033 // if len(foo) % 2 != 0
4034 call, ok := rem.X.(*ir.Call)
4035 if !ok || !irutil.IsCallTo(call.Common(), "len") {
4039 // we're checking the length of a parameter that is a slice
4040 // TODO(dh): support parameters that have flown through sigmas and phis
4041 param, ok := call.Call.Args[0].(*ir.Parameter)
4045 if _, ok := param.Type().Underlying().(*types.Slice); !ok {
4049 // if len(foo) % 2 != 0 then panic
4050 if _, ok := b.Succs[0].Control().(*ir.Panic); !ok {
4054 pass.ExportObjectFact(param.Object(), new(evenElements))
4059 func findIndirectSliceLenChecks(pass *analysis.Pass) {
4060 seen := map[*ir.Function]struct{}{}
4062 var doFunction func(fn *ir.Function)
4063 doFunction = func(fn *ir.Function) {
4064 if _, ok := seen[fn]; ok {
4067 seen[fn] = struct{}{}
4069 for _, b := range fn.Blocks {
4070 // all paths go through this block
4071 if !b.Dominates(fn.Exit) {
4075 for _, instr := range b.Instrs {
4076 call, ok := instr.(*ir.Call)
4080 callee := call.Call.StaticCallee()
4085 if callee.Pkg == fn.Pkg {
4086 // TODO(dh): are we missing interesting wrappers
4087 // because wrappers don't have Pkg set?
4091 for argi, arg := range call.Call.Args {
4092 if callee.Signature.Recv() != nil {
4099 // TODO(dh): support parameters that have flown through sigmas and phis
4100 param, ok := arg.(*ir.Parameter)
4104 if _, ok := param.Type().Underlying().(*types.Slice); !ok {
4108 // We can't use callee.Params to look up the
4109 // parameter, because Params is not populated for
4110 // external functions. In our modular analysis.
4111 // any function in any package that isn't the
4112 // current package is consided "external", as it
4113 // has been loaded from export data only.
4114 sigParams := callee.Signature.Params()
4116 if !pass.ImportObjectFact(sigParams.At(argi), new(evenElements)) {
4119 pass.ExportObjectFact(param.Object(), new(evenElements))
4125 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
4130 func findSliceLength(v ir.Value) int {
4131 // TODO(dh): VRP would help here
4133 v = irutil.Flatten(v)
4134 val := func(v ir.Value) int {
4135 if v, ok := v.(*ir.Const); ok {
4136 return int(v.Int64())
4140 switch v := v.(type) {
4150 switch vv := v.X.(type) {
4152 high = int(typeutil.Dereference(vv.Type()).Underlying().(*types.Array).Len())
4154 high = findSliceLength(vv)
4157 if low == -1 || high == -1 {
4166 type evenElements struct{}
4168 func (evenElements) AFact() {}
4170 func (evenElements) String() string { return "needs even elements" }
4172 func flagSliceLens(pass *analysis.Pass) {
4173 var tag evenElements
4175 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
4176 for _, b := range fn.Blocks {
4177 for _, instr := range b.Instrs {
4178 call, ok := instr.(ir.CallInstruction)
4182 callee := call.Common().StaticCallee()
4186 for argi, arg := range call.Common().Args {
4187 if callee.Signature.Recv() != nil {
4194 _, ok := arg.Type().Underlying().(*types.Slice)
4198 param := callee.Signature.Params().At(argi)
4199 if !pass.ImportObjectFact(param, &tag) {
4203 // TODO handle stubs
4205 // we know the argument has to have even length.
4206 // now let's try to find its length
4207 if n := findSliceLength(arg); n > -1 && n%2 != 0 {
4208 src := call.Source().(*ast.CallExpr).Args[argi]
4209 sig := call.Common().Signature()
4211 if argi == sig.Params().Len()-1 && sig.Variadic() {
4212 label = "variadic argument"
4216 // Note that param.Name() is guaranteed to not
4217 // be empty, otherwise the function couldn't
4218 // have enforced its length.
4219 report.Report(pass, src, fmt.Sprintf("%s %q is expected to have even number of elements, but has %d elements", label, param.Name(), n))
4227 func CheckEvenSliceLength(pass *analysis.Pass) (interface{}, error) {
4228 findSliceLenChecks(pass)
4229 findIndirectSliceLenChecks(pass)
4235 func CheckTypedNilInterface(pass *analysis.Pass) (interface{}, error) {
4236 // The comparison 'fn() == nil' can never be true if fn() returns
4237 // an interface value and only returns typed nils. This is usually
4238 // a mistake in the function itself, but all we can say for
4239 // certain is that the comparison is pointless.
4241 // Flag results if no untyped nils are being returned, but either
4242 // known typed nils, or typed unknown nilness are being returned.
4244 irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR)
4245 typedness := pass.ResultOf[typedness.Analysis].(*typedness.Result)
4246 nilness := pass.ResultOf[nilness.Analysis].(*nilness.Result)
4247 for _, fn := range irpkg.SrcFuncs {
4248 for _, b := range fn.Blocks {
4249 for _, instr := range b.Instrs {
4250 binop, ok := instr.(*ir.BinOp)
4251 if !ok || !(binop.Op == token.EQL || binop.Op == token.NEQ) {
4254 if _, ok := binop.X.Type().Underlying().(*types.Interface); !ok {
4255 // TODO support swapped X and Y
4259 k, ok := binop.Y.(*ir.Const)
4260 if !ok || !k.IsNil() {
4261 // if binop.X is an interface, then binop.Y can
4262 // only be a Const if its untyped. A typed nil
4263 // constant would first be passed to
4270 switch x := irutil.Flatten(binop.X).(type) {
4272 callee := x.Call.StaticCallee()
4276 obj, _ = callee.Object().(*types.Func)
4279 call, ok := irutil.Flatten(x.Tuple).(*ir.Call)
4283 callee := call.Call.StaticCallee()
4287 obj, _ = callee.Object().(*types.Func)
4289 case *ir.MakeInterface:
4290 var qualifier string
4295 qualifier = "always"
4297 panic("unreachable")
4299 report.Report(pass, binop, fmt.Sprintf("this comparison is %s true", qualifier),
4300 report.Related(x.X, "the lhs of the comparison gets its value from here and has a concrete type"))
4307 isNil, onlyGlobal := nilness.MayReturnNil(obj, idx)
4308 if typedness.MustReturnTyped(obj, idx) && isNil && !onlyGlobal && !code.IsInTest(pass, binop) {
4309 // Don't flag these comparisons in tests. Tests
4310 // may be explicitly enforcing the invariant that
4311 // a value isn't nil.
4313 var qualifier string
4318 qualifier = "always"
4320 panic("unreachable")
4322 report.Report(pass, binop, fmt.Sprintf("this comparison is %s true", qualifier),
4323 // TODO support swapped X and Y
4324 report.Related(binop.X, fmt.Sprintf("the lhs of the comparison is the %s return value of this function call", report.Ordinal(idx+1))),
4325 report.Related(obj, fmt.Sprintf("%s never returns a nil interface value", typeutil.FuncName(obj))))