1
2
3
4
5
6
7 package printf
8
9 import (
10 "bytes"
11 "fmt"
12 "go/ast"
13 "go/constant"
14 "go/token"
15 "go/types"
16 "reflect"
17 "regexp"
18 "sort"
19 "strconv"
20 "strings"
21 "unicode/utf8"
22
23 "golang.org/x/tools/go/analysis"
24 "golang.org/x/tools/go/analysis/passes/inspect"
25 "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
26 "golang.org/x/tools/go/ast/inspector"
27 "golang.org/x/tools/go/types/typeutil"
28 "golang.org/x/tools/internal/typeparams"
29 )
30
31 func init() {
32 Analyzer.Flags.Var(isPrint, "funcs", "comma-separated list of print function names to check")
33 }
34
35 var Analyzer = &analysis.Analyzer{
36 Name: "printf",
37 Doc: Doc,
38 Requires: []*analysis.Analyzer{inspect.Analyzer},
39 Run: run,
40 ResultType: reflect.TypeOf((*Result)(nil)),
41 FactTypes: []analysis.Fact{new(isWrapper)},
42 }
43
44 const Doc = `check consistency of Printf format strings and arguments
45
46 The check applies to known functions (for example, those in package fmt)
47 as well as any detected wrappers of known functions.
48
49 A function that wants to avail itself of printf checking but is not
50 found by this analyzer's heuristics (for example, due to use of
51 dynamic calls) can insert a bogus call:
52
53 if false {
54 _ = fmt.Sprintf(format, args...) // enable printf checking
55 }
56
57 The -funcs flag specifies a comma-separated list of names of additional
58 known formatting functions or methods. If the name contains a period,
59 it must denote a specific function using one of the following forms:
60
61 dir/pkg.Function
62 dir/pkg.Type.Method
63 (*dir/pkg.Type).Method
64
65 Otherwise the name is interpreted as a case-insensitive unqualified
66 identifier such as "errorf". Either way, if a listed name ends in f, the
67 function is assumed to be Printf-like, taking a format string before the
68 argument list. Otherwise it is assumed to be Print-like, taking a list
69 of arguments with no format string.
70 `
71
72
73 type Kind int
74
75 const (
76 KindNone Kind = iota
77 KindPrint
78 KindPrintf
79 KindErrorf
80 )
81
82 func (kind Kind) String() string {
83 switch kind {
84 case KindPrint:
85 return "print"
86 case KindPrintf:
87 return "printf"
88 case KindErrorf:
89 return "errorf"
90 }
91 return ""
92 }
93
94
95
96 type Result struct {
97 funcs map[*types.Func]Kind
98 }
99
100
101 func (r *Result) Kind(fn *types.Func) Kind {
102 _, ok := isPrint[fn.FullName()]
103 if !ok {
104
105 _, ok = isPrint[strings.ToLower(fn.Name())]
106 }
107 if ok {
108 if strings.HasSuffix(fn.Name(), "f") {
109 return KindPrintf
110 } else {
111 return KindPrint
112 }
113 }
114
115 return r.funcs[fn]
116 }
117
118
119 type isWrapper struct{ Kind Kind }
120
121 func (f *isWrapper) AFact() {}
122
123 func (f *isWrapper) String() string {
124 switch f.Kind {
125 case KindPrintf:
126 return "printfWrapper"
127 case KindPrint:
128 return "printWrapper"
129 case KindErrorf:
130 return "errorfWrapper"
131 default:
132 return "unknownWrapper"
133 }
134 }
135
136 func run(pass *analysis.Pass) (interface{}, error) {
137 res := &Result{
138 funcs: make(map[*types.Func]Kind),
139 }
140 findPrintfLike(pass, res)
141 checkCall(pass)
142 return res, nil
143 }
144
145 type printfWrapper struct {
146 obj *types.Func
147 fdecl *ast.FuncDecl
148 format *types.Var
149 args *types.Var
150 callers []printfCaller
151 failed bool
152 }
153
154 type printfCaller struct {
155 w *printfWrapper
156 call *ast.CallExpr
157 }
158
159
160
161
162
163
164
165
166 func maybePrintfWrapper(info *types.Info, decl ast.Decl) *printfWrapper {
167
168 fdecl, ok := decl.(*ast.FuncDecl)
169 if !ok || fdecl.Body == nil {
170 return nil
171 }
172 fn, ok := info.Defs[fdecl.Name].(*types.Func)
173
174 if !ok {
175 return nil
176 }
177
178 sig := fn.Type().(*types.Signature)
179 if !sig.Variadic() {
180 return nil
181 }
182
183 params := sig.Params()
184 nparams := params.Len()
185
186 args := params.At(nparams - 1)
187 iface, ok := args.Type().(*types.Slice).Elem().(*types.Interface)
188 if !ok || !iface.Empty() {
189 return nil
190 }
191
192
193 var format *types.Var
194 if nparams >= 2 {
195 if p := params.At(nparams - 2); p.Type() == types.Typ[types.String] {
196 format = p
197 }
198 }
199
200 return &printfWrapper{
201 obj: fn,
202 fdecl: fdecl,
203 format: format,
204 args: args,
205 }
206 }
207
208
209 func findPrintfLike(pass *analysis.Pass, res *Result) (interface{}, error) {
210
211 byObj := make(map[*types.Func]*printfWrapper)
212 var wrappers []*printfWrapper
213 for _, file := range pass.Files {
214 for _, decl := range file.Decls {
215 w := maybePrintfWrapper(pass.TypesInfo, decl)
216 if w == nil {
217 continue
218 }
219 byObj[w.obj] = w
220 wrappers = append(wrappers, w)
221 }
222 }
223
224
225 for _, w := range wrappers {
226
227 ast.Inspect(w.fdecl.Body, func(n ast.Node) bool {
228 if w.failed {
229 return false
230 }
231
232
233 if assign, ok := n.(*ast.AssignStmt); ok {
234 for _, lhs := range assign.Lhs {
235 if match(pass.TypesInfo, lhs, w.format) ||
236 match(pass.TypesInfo, lhs, w.args) {
237
238
239
240
241 w.failed = true
242 return false
243 }
244 }
245 }
246 if un, ok := n.(*ast.UnaryExpr); ok && un.Op == token.AND {
247 if match(pass.TypesInfo, un.X, w.format) ||
248 match(pass.TypesInfo, un.X, w.args) {
249
250
251
252 w.failed = true
253 return false
254 }
255 }
256
257 call, ok := n.(*ast.CallExpr)
258 if !ok || len(call.Args) == 0 || !match(pass.TypesInfo, call.Args[len(call.Args)-1], w.args) {
259 return true
260 }
261
262 fn, kind := printfNameAndKind(pass, call)
263 if kind != 0 {
264 checkPrintfFwd(pass, w, call, kind, res)
265 return true
266 }
267
268
269
270
271 if fn != nil && fn.Pkg() == pass.Pkg && byObj[fn] != nil {
272 callee := byObj[fn]
273 callee.callers = append(callee.callers, printfCaller{w, call})
274 }
275
276 return true
277 })
278 }
279 return nil, nil
280 }
281
282 func match(info *types.Info, arg ast.Expr, param *types.Var) bool {
283 id, ok := arg.(*ast.Ident)
284 return ok && info.ObjectOf(id) == param
285 }
286
287
288
289 func checkPrintfFwd(pass *analysis.Pass, w *printfWrapper, call *ast.CallExpr, kind Kind, res *Result) {
290 matched := kind == KindPrint ||
291 kind != KindNone && len(call.Args) >= 2 && match(pass.TypesInfo, call.Args[len(call.Args)-2], w.format)
292 if !matched {
293 return
294 }
295
296 if !call.Ellipsis.IsValid() {
297 typ, ok := pass.TypesInfo.Types[call.Fun].Type.(*types.Signature)
298 if !ok {
299 return
300 }
301 if len(call.Args) > typ.Params().Len() {
302
303
304
305
306
307
308
309 return
310 }
311 desc := "printf"
312 if kind == KindPrint {
313 desc = "print"
314 }
315 pass.ReportRangef(call, "missing ... in args forwarded to %s-like function", desc)
316 return
317 }
318 fn := w.obj
319 var fact isWrapper
320 if !pass.ImportObjectFact(fn, &fact) {
321 fact.Kind = kind
322 pass.ExportObjectFact(fn, &fact)
323 res.funcs[fn] = kind
324 for _, caller := range w.callers {
325 checkPrintfFwd(pass, caller.w, caller.call, kind, res)
326 }
327 }
328 }
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346 var isPrint = stringSet{
347 "fmt.Errorf": true,
348 "fmt.Fprint": true,
349 "fmt.Fprintf": true,
350 "fmt.Fprintln": true,
351 "fmt.Print": true,
352 "fmt.Printf": true,
353 "fmt.Println": true,
354 "fmt.Sprint": true,
355 "fmt.Sprintf": true,
356 "fmt.Sprintln": true,
357
358 "runtime/trace.Logf": true,
359
360 "log.Print": true,
361 "log.Printf": true,
362 "log.Println": true,
363 "log.Fatal": true,
364 "log.Fatalf": true,
365 "log.Fatalln": true,
366 "log.Panic": true,
367 "log.Panicf": true,
368 "log.Panicln": true,
369 "(*log.Logger).Fatal": true,
370 "(*log.Logger).Fatalf": true,
371 "(*log.Logger).Fatalln": true,
372 "(*log.Logger).Panic": true,
373 "(*log.Logger).Panicf": true,
374 "(*log.Logger).Panicln": true,
375 "(*log.Logger).Print": true,
376 "(*log.Logger).Printf": true,
377 "(*log.Logger).Println": true,
378
379 "(*testing.common).Error": true,
380 "(*testing.common).Errorf": true,
381 "(*testing.common).Fatal": true,
382 "(*testing.common).Fatalf": true,
383 "(*testing.common).Log": true,
384 "(*testing.common).Logf": true,
385 "(*testing.common).Skip": true,
386 "(*testing.common).Skipf": true,
387
388
389 "(testing.TB).Error": true,
390 "(testing.TB).Errorf": true,
391 "(testing.TB).Fatal": true,
392 "(testing.TB).Fatalf": true,
393 "(testing.TB).Log": true,
394 "(testing.TB).Logf": true,
395 "(testing.TB).Skip": true,
396 "(testing.TB).Skipf": true,
397 }
398
399
400
401
402
403
404
405
406
407
408
409 func formatString(pass *analysis.Pass, call *ast.CallExpr) (format string, idx int) {
410 typ := pass.TypesInfo.Types[call.Fun].Type
411 if typ != nil {
412 if sig, ok := typ.(*types.Signature); ok {
413 if !sig.Variadic() {
414
415 return "", -1
416 }
417 idx := sig.Params().Len() - 2
418 if idx < 0 {
419
420
421 return "", -1
422 }
423 s, ok := stringConstantArg(pass, call, idx)
424 if !ok {
425
426 return "", -1
427 }
428 return s, idx
429 }
430 }
431
432
433
434 for idx := range call.Args {
435 if s, ok := stringConstantArg(pass, call, idx); ok {
436 return s, idx
437 }
438 if pass.TypesInfo.Types[call.Args[idx]].Type == types.Typ[types.String] {
439
440
441
442 return "", -1
443 }
444 }
445 return "", -1
446 }
447
448
449
450
451
452 func stringConstantArg(pass *analysis.Pass, call *ast.CallExpr, idx int) (string, bool) {
453 if idx >= len(call.Args) {
454 return "", false
455 }
456 return stringConstantExpr(pass, call.Args[idx])
457 }
458
459
460
461
462
463 func stringConstantExpr(pass *analysis.Pass, expr ast.Expr) (string, bool) {
464 lit := pass.TypesInfo.Types[expr].Value
465 if lit != nil && lit.Kind() == constant.String {
466 return constant.StringVal(lit), true
467 }
468 return "", false
469 }
470
471
472 func checkCall(pass *analysis.Pass) {
473 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
474 nodeFilter := []ast.Node{
475 (*ast.CallExpr)(nil),
476 }
477 inspect.Preorder(nodeFilter, func(n ast.Node) {
478 call := n.(*ast.CallExpr)
479 fn, kind := printfNameAndKind(pass, call)
480 switch kind {
481 case KindPrintf, KindErrorf:
482 checkPrintf(pass, kind, call, fn)
483 case KindPrint:
484 checkPrint(pass, call, fn)
485 }
486 })
487 }
488
489 func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func, kind Kind) {
490 fn, _ = typeutil.Callee(pass.TypesInfo, call).(*types.Func)
491 if fn == nil {
492 return nil, 0
493 }
494
495 _, ok := isPrint[fn.FullName()]
496 if !ok {
497
498 _, ok = isPrint[strings.ToLower(fn.Name())]
499 }
500 if ok {
501 if fn.FullName() == "fmt.Errorf" {
502 kind = KindErrorf
503 } else if strings.HasSuffix(fn.Name(), "f") {
504 kind = KindPrintf
505 } else {
506 kind = KindPrint
507 }
508 return fn, kind
509 }
510
511 var fact isWrapper
512 if pass.ImportObjectFact(fn, &fact) {
513 return fn, fact.Kind
514 }
515
516 return fn, KindNone
517 }
518
519
520
521 func isFormatter(typ types.Type) bool {
522
523 if _, ok := typ.Underlying().(*types.Interface); ok {
524
525
526
527 if !typeparams.IsTypeParam(typ) {
528 return true
529 }
530 }
531 obj, _, _ := types.LookupFieldOrMethod(typ, false, nil, "Format")
532 fn, ok := obj.(*types.Func)
533 if !ok {
534 return false
535 }
536 sig := fn.Type().(*types.Signature)
537 return sig.Params().Len() == 2 &&
538 sig.Results().Len() == 0 &&
539 isNamed(sig.Params().At(0).Type(), "fmt", "State") &&
540 types.Identical(sig.Params().At(1).Type(), types.Typ[types.Rune])
541 }
542
543 func isNamed(T types.Type, pkgpath, name string) bool {
544 named, ok := T.(*types.Named)
545 return ok && named.Obj().Pkg().Path() == pkgpath && named.Obj().Name() == name
546 }
547
548
549
550 type formatState struct {
551 verb rune
552 format string
553 name string
554 flags []byte
555 argNums []int
556 firstArg int
557
558 pass *analysis.Pass
559 call *ast.CallExpr
560 argNum int
561 hasIndex bool
562 indexPending bool
563 nbytes int
564 }
565
566
567 func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.Func) {
568 format, idx := formatString(pass, call)
569 if idx < 0 {
570 if false {
571 pass.Reportf(call.Lparen, "can't check non-constant format in call to %s", fn.FullName())
572 }
573 return
574 }
575
576 firstArg := idx + 1
577 if !strings.Contains(format, "%") {
578 if len(call.Args) > firstArg {
579 pass.Reportf(call.Lparen, "%s call has arguments but no formatting directives", fn.FullName())
580 }
581 return
582 }
583
584 argNum := firstArg
585 maxArgNum := firstArg
586 anyIndex := false
587 anyW := false
588 for i, w := 0, 0; i < len(format); i += w {
589 w = 1
590 if format[i] != '%' {
591 continue
592 }
593 state := parsePrintfVerb(pass, call, fn.FullName(), format[i:], firstArg, argNum)
594 if state == nil {
595 return
596 }
597 w = len(state.format)
598 if !okPrintfArg(pass, call, state) {
599 return
600 }
601 if state.hasIndex {
602 anyIndex = true
603 }
604 if state.verb == 'w' {
605 switch kind {
606 case KindNone, KindPrint, KindPrintf:
607 pass.Reportf(call.Pos(), "%s does not support error-wrapping directive %%w", state.name)
608 return
609 }
610 if anyW {
611 pass.Reportf(call.Pos(), "%s call has more than one error-wrapping directive %%w", state.name)
612 return
613 }
614 anyW = true
615 }
616 if len(state.argNums) > 0 {
617
618 argNum = state.argNums[len(state.argNums)-1] + 1
619 }
620 for _, n := range state.argNums {
621 if n >= maxArgNum {
622 maxArgNum = n + 1
623 }
624 }
625 }
626
627 if call.Ellipsis.IsValid() && maxArgNum >= len(call.Args)-1 {
628 return
629 }
630
631 if anyIndex {
632 return
633 }
634
635 if maxArgNum != len(call.Args) {
636 expect := maxArgNum - firstArg
637 numArgs := len(call.Args) - firstArg
638 pass.ReportRangef(call, "%s call needs %v but has %v", fn.FullName(), count(expect, "arg"), count(numArgs, "arg"))
639 }
640 }
641
642
643 func (s *formatState) parseFlags() {
644 for s.nbytes < len(s.format) {
645 switch c := s.format[s.nbytes]; c {
646 case '#', '0', '+', '-', ' ':
647 s.flags = append(s.flags, c)
648 s.nbytes++
649 default:
650 return
651 }
652 }
653 }
654
655
656 func (s *formatState) scanNum() {
657 for ; s.nbytes < len(s.format); s.nbytes++ {
658 c := s.format[s.nbytes]
659 if c < '0' || '9' < c {
660 return
661 }
662 }
663 }
664
665
666 func (s *formatState) parseIndex() bool {
667 if s.nbytes == len(s.format) || s.format[s.nbytes] != '[' {
668 return true
669 }
670
671 s.nbytes++
672 start := s.nbytes
673 s.scanNum()
674 ok := true
675 if s.nbytes == len(s.format) || s.nbytes == start || s.format[s.nbytes] != ']' {
676 ok = false
677 s.nbytes = strings.Index(s.format, "]")
678 if s.nbytes < 0 {
679 s.pass.ReportRangef(s.call, "%s format %s is missing closing ]", s.name, s.format)
680 return false
681 }
682 }
683 arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32)
684 if err != nil || !ok || arg32 <= 0 || arg32 > int64(len(s.call.Args)-s.firstArg) {
685 s.pass.ReportRangef(s.call, "%s format has invalid argument index [%s]", s.name, s.format[start:s.nbytes])
686 return false
687 }
688 s.nbytes++
689 arg := int(arg32)
690 arg += s.firstArg - 1
691 s.argNum = arg
692 s.hasIndex = true
693 s.indexPending = true
694 return true
695 }
696
697
698 func (s *formatState) parseNum() bool {
699 if s.nbytes < len(s.format) && s.format[s.nbytes] == '*' {
700 if s.indexPending {
701 s.indexPending = false
702 }
703 s.nbytes++
704 s.argNums = append(s.argNums, s.argNum)
705 s.argNum++
706 } else {
707 s.scanNum()
708 }
709 return true
710 }
711
712
713 func (s *formatState) parsePrecision() bool {
714
715 if s.nbytes < len(s.format) && s.format[s.nbytes] == '.' {
716 s.flags = append(s.flags, '.')
717 s.nbytes++
718 if !s.parseIndex() {
719 return false
720 }
721 if !s.parseNum() {
722 return false
723 }
724 }
725 return true
726 }
727
728
729
730
731 func parsePrintfVerb(pass *analysis.Pass, call *ast.CallExpr, name, format string, firstArg, argNum int) *formatState {
732 state := &formatState{
733 format: format,
734 name: name,
735 flags: make([]byte, 0, 5),
736 argNum: argNum,
737 argNums: make([]int, 0, 1),
738 nbytes: 1,
739 firstArg: firstArg,
740 pass: pass,
741 call: call,
742 }
743
744 state.parseFlags()
745
746 if !state.parseIndex() {
747 return nil
748 }
749
750 if !state.parseNum() {
751 return nil
752 }
753
754 if !state.parsePrecision() {
755 return nil
756 }
757
758 if !state.indexPending && !state.parseIndex() {
759 return nil
760 }
761 if state.nbytes == len(state.format) {
762 pass.ReportRangef(call.Fun, "%s format %s is missing verb at end of string", name, state.format)
763 return nil
764 }
765 verb, w := utf8.DecodeRuneInString(state.format[state.nbytes:])
766 state.verb = verb
767 state.nbytes += w
768 if verb != '%' {
769 state.argNums = append(state.argNums, state.argNum)
770 }
771 state.format = state.format[:state.nbytes]
772 return state
773 }
774
775
776 type printfArgType int
777
778 const (
779 argBool printfArgType = 1 << iota
780 argInt
781 argRune
782 argString
783 argFloat
784 argComplex
785 argPointer
786 argError
787 anyType printfArgType = ^0
788 )
789
790 type printVerb struct {
791 verb rune
792 flags string
793 typ printfArgType
794 }
795
796
797 const (
798 noFlag = ""
799 numFlag = " -+.0"
800 sharpNumFlag = " -+.0#"
801 allFlags = " -+.0#"
802 )
803
804
805 var printVerbs = []printVerb{
806
807
808
809
810
811 {'%', noFlag, 0},
812 {'b', sharpNumFlag, argInt | argFloat | argComplex | argPointer},
813 {'c', "-", argRune | argInt},
814 {'d', numFlag, argInt | argPointer},
815 {'e', sharpNumFlag, argFloat | argComplex},
816 {'E', sharpNumFlag, argFloat | argComplex},
817 {'f', sharpNumFlag, argFloat | argComplex},
818 {'F', sharpNumFlag, argFloat | argComplex},
819 {'g', sharpNumFlag, argFloat | argComplex},
820 {'G', sharpNumFlag, argFloat | argComplex},
821 {'o', sharpNumFlag, argInt | argPointer},
822 {'O', sharpNumFlag, argInt | argPointer},
823 {'p', "-#", argPointer},
824 {'q', " -+.0#", argRune | argInt | argString},
825 {'s', " -+.0", argString},
826 {'t', "-", argBool},
827 {'T', "-", anyType},
828 {'U', "-#", argRune | argInt},
829 {'v', allFlags, anyType},
830 {'w', allFlags, argError},
831 {'x', sharpNumFlag, argRune | argInt | argString | argPointer | argFloat | argComplex},
832 {'X', sharpNumFlag, argRune | argInt | argString | argPointer | argFloat | argComplex},
833 }
834
835
836
837
838 func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (ok bool) {
839 var v printVerb
840 found := false
841
842 for _, v = range printVerbs {
843 if v.verb == state.verb {
844 found = true
845 break
846 }
847 }
848
849
850
851 formatter := false
852 if v.typ != argError && state.argNum < len(call.Args) {
853 if tv, ok := pass.TypesInfo.Types[call.Args[state.argNum]]; ok {
854 formatter = isFormatter(tv.Type)
855 }
856 }
857
858 if !formatter {
859 if !found {
860 pass.ReportRangef(call, "%s format %s has unknown verb %c", state.name, state.format, state.verb)
861 return false
862 }
863 for _, flag := range state.flags {
864
865
866 if flag == '0' {
867 continue
868 }
869 if !strings.ContainsRune(v.flags, rune(flag)) {
870 pass.ReportRangef(call, "%s format %s has unrecognized flag %c", state.name, state.format, flag)
871 return false
872 }
873 }
874 }
875
876
877 trueArgs := 1
878 if state.verb == '%' {
879 trueArgs = 0
880 }
881 nargs := len(state.argNums)
882 for i := 0; i < nargs-trueArgs; i++ {
883 argNum := state.argNums[i]
884 if !argCanBeChecked(pass, call, i, state) {
885 return
886 }
887 arg := call.Args[argNum]
888 if reason, ok := matchArgType(pass, argInt, arg); !ok {
889 details := ""
890 if reason != "" {
891 details = " (" + reason + ")"
892 }
893 pass.ReportRangef(call, "%s format %s uses non-int %s%s as argument of *", state.name, state.format, analysisutil.Format(pass.Fset, arg), details)
894 return false
895 }
896 }
897
898 if state.verb == '%' || formatter {
899 return true
900 }
901 argNum := state.argNums[len(state.argNums)-1]
902 if !argCanBeChecked(pass, call, len(state.argNums)-1, state) {
903 return false
904 }
905 arg := call.Args[argNum]
906 if isFunctionValue(pass, arg) && state.verb != 'p' && state.verb != 'T' {
907 pass.ReportRangef(call, "%s format %s arg %s is a func value, not called", state.name, state.format, analysisutil.Format(pass.Fset, arg))
908 return false
909 }
910 if reason, ok := matchArgType(pass, v.typ, arg); !ok {
911 typeString := ""
912 if typ := pass.TypesInfo.Types[arg].Type; typ != nil {
913 typeString = typ.String()
914 }
915 details := ""
916 if reason != "" {
917 details = " (" + reason + ")"
918 }
919 pass.ReportRangef(call, "%s format %s has arg %s of wrong type %s%s", state.name, state.format, analysisutil.Format(pass.Fset, arg), typeString, details)
920 return false
921 }
922 if v.typ&argString != 0 && v.verb != 'T' && !bytes.Contains(state.flags, []byte{'#'}) {
923 if methodName, ok := recursiveStringer(pass, arg); ok {
924 pass.ReportRangef(call, "%s format %s with arg %s causes recursive %s method call", state.name, state.format, analysisutil.Format(pass.Fset, arg), methodName)
925 return false
926 }
927 }
928 return true
929 }
930
931
932
933
934
935
936
937 func recursiveStringer(pass *analysis.Pass, e ast.Expr) (string, bool) {
938 typ := pass.TypesInfo.Types[e].Type
939
940
941 if isFormatter(typ) {
942 return "", false
943 }
944
945
946 strObj, _, _ := types.LookupFieldOrMethod(typ, false, pass.Pkg, "String")
947 strMethod, strOk := strObj.(*types.Func)
948 errObj, _, _ := types.LookupFieldOrMethod(typ, false, pass.Pkg, "Error")
949 errMethod, errOk := errObj.(*types.Func)
950 if !strOk && !errOk {
951 return "", false
952 }
953
954
955 var method *types.Func
956 if strOk && strMethod.Pkg() == pass.Pkg && strMethod.Scope().Contains(e.Pos()) {
957 method = strMethod
958 } else if errOk && errMethod.Pkg() == pass.Pkg && errMethod.Scope().Contains(e.Pos()) {
959 method = errMethod
960 } else {
961 return "", false
962 }
963
964 sig := method.Type().(*types.Signature)
965 if !isStringer(sig) {
966 return "", false
967 }
968
969
970 if u, ok := e.(*ast.UnaryExpr); ok && u.Op == token.AND {
971 e = u.X
972 }
973 if id, ok := e.(*ast.Ident); ok {
974 if pass.TypesInfo.Uses[id] == sig.Recv() {
975 return method.FullName(), true
976 }
977 }
978 return "", false
979 }
980
981
982 func isStringer(sig *types.Signature) bool {
983 return sig.Params().Len() == 0 &&
984 sig.Results().Len() == 1 &&
985 sig.Results().At(0).Type() == types.Typ[types.String]
986 }
987
988
989
990 func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool {
991 if typ := pass.TypesInfo.Types[e].Type; typ != nil {
992 _, ok := typ.(*types.Signature)
993 return ok
994 }
995 return false
996 }
997
998
999
1000
1001 func argCanBeChecked(pass *analysis.Pass, call *ast.CallExpr, formatArg int, state *formatState) bool {
1002 argNum := state.argNums[formatArg]
1003 if argNum <= 0 {
1004
1005 panic("negative arg num")
1006 }
1007 if argNum < len(call.Args)-1 {
1008 return true
1009 }
1010 if call.Ellipsis.IsValid() {
1011 return false
1012 }
1013 if argNum < len(call.Args) {
1014 return true
1015 }
1016
1017
1018 arg := argNum - state.firstArg + 1
1019 pass.ReportRangef(call, "%s format %s reads arg #%d, but call has %v", state.name, state.format, arg, count(len(call.Args)-state.firstArg, "arg"))
1020 return false
1021 }
1022
1023
1024
1025
1026 var printFormatRE = regexp.MustCompile(`%` + flagsRE + numOptRE + `\.?` + numOptRE + indexOptRE + verbRE)
1027
1028 const (
1029 flagsRE = `[+\-#]*`
1030 indexOptRE = `(\[[0-9]+\])?`
1031 numOptRE = `([0-9]+|` + indexOptRE + `\*)?`
1032 verbRE = `[bcdefgopqstvxEFGTUX]`
1033 )
1034
1035
1036 func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) {
1037 firstArg := 0
1038 typ := pass.TypesInfo.Types[call.Fun].Type
1039 if typ == nil {
1040
1041 return
1042 }
1043 if sig, ok := typ.(*types.Signature); ok {
1044 if !sig.Variadic() {
1045
1046 return
1047 }
1048 params := sig.Params()
1049 firstArg = params.Len() - 1
1050
1051 typ := params.At(firstArg).Type()
1052 typ = typ.(*types.Slice).Elem()
1053 it, ok := typ.(*types.Interface)
1054 if !ok || !it.Empty() {
1055
1056 return
1057 }
1058 }
1059 args := call.Args
1060 if len(args) <= firstArg {
1061
1062 return
1063 }
1064 args = args[firstArg:]
1065
1066 if firstArg == 0 {
1067 if sel, ok := call.Args[0].(*ast.SelectorExpr); ok {
1068 if x, ok := sel.X.(*ast.Ident); ok {
1069 if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") {
1070 pass.ReportRangef(call, "%s does not take io.Writer but has first arg %s", fn.FullName(), analysisutil.Format(pass.Fset, call.Args[0]))
1071 }
1072 }
1073 }
1074 }
1075
1076 arg := args[0]
1077 if s, ok := stringConstantExpr(pass, arg); ok {
1078
1079
1080 s = strings.TrimSuffix(s, "%")
1081 if strings.Contains(s, "%") {
1082 m := printFormatRE.FindStringSubmatch(s)
1083 if m != nil {
1084 pass.ReportRangef(call, "%s call has possible formatting directive %s", fn.FullName(), m[0])
1085 }
1086 }
1087 }
1088 if strings.HasSuffix(fn.Name(), "ln") {
1089
1090 arg = args[len(args)-1]
1091 if s, ok := stringConstantExpr(pass, arg); ok {
1092 if strings.HasSuffix(s, "\n") {
1093 pass.ReportRangef(call, "%s arg list ends with redundant newline", fn.FullName())
1094 }
1095 }
1096 }
1097 for _, arg := range args {
1098 if isFunctionValue(pass, arg) {
1099 pass.ReportRangef(call, "%s arg %s is a func value, not called", fn.FullName(), analysisutil.Format(pass.Fset, arg))
1100 }
1101 if methodName, ok := recursiveStringer(pass, arg); ok {
1102 pass.ReportRangef(call, "%s arg %s causes recursive call to %s method", fn.FullName(), analysisutil.Format(pass.Fset, arg), methodName)
1103 }
1104 }
1105 }
1106
1107
1108
1109 func count(n int, what string) string {
1110 if n == 1 {
1111 return "1 " + what
1112 }
1113 return fmt.Sprintf("%d %ss", n, what)
1114 }
1115
1116
1117
1118 type stringSet map[string]bool
1119
1120 func (ss stringSet) String() string {
1121 var list []string
1122 for name := range ss {
1123 list = append(list, name)
1124 }
1125 sort.Strings(list)
1126 return strings.Join(list, ",")
1127 }
1128
1129 func (ss stringSet) Set(flag string) error {
1130 for _, name := range strings.Split(flag, ",") {
1131 if len(name) == 0 {
1132 return fmt.Errorf("empty string")
1133 }
1134 if !strings.Contains(name, ".") {
1135 name = strings.ToLower(name)
1136 }
1137 ss[name] = true
1138 }
1139 return nil
1140 }
1141
View as plain text