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 var isPrint = stringSet{
346 "fmt.Errorf": true,
347 "fmt.Fprint": true,
348 "fmt.Fprintf": true,
349 "fmt.Fprintln": true,
350 "fmt.Print": true,
351 "fmt.Printf": true,
352 "fmt.Println": true,
353 "fmt.Sprint": true,
354 "fmt.Sprintf": true,
355 "fmt.Sprintln": true,
356
357 "runtime/trace.Logf": true,
358
359 "log.Print": true,
360 "log.Printf": true,
361 "log.Println": true,
362 "log.Fatal": true,
363 "log.Fatalf": true,
364 "log.Fatalln": true,
365 "log.Panic": true,
366 "log.Panicf": true,
367 "log.Panicln": true,
368 "(*log.Logger).Fatal": true,
369 "(*log.Logger).Fatalf": true,
370 "(*log.Logger).Fatalln": true,
371 "(*log.Logger).Panic": true,
372 "(*log.Logger).Panicf": true,
373 "(*log.Logger).Panicln": true,
374 "(*log.Logger).Print": true,
375 "(*log.Logger).Printf": true,
376 "(*log.Logger).Println": true,
377
378 "(*testing.common).Error": true,
379 "(*testing.common).Errorf": true,
380 "(*testing.common).Fatal": true,
381 "(*testing.common).Fatalf": true,
382 "(*testing.common).Log": true,
383 "(*testing.common).Logf": true,
384 "(*testing.common).Skip": true,
385 "(*testing.common).Skipf": true,
386
387
388 "(testing.TB).Error": true,
389 "(testing.TB).Errorf": true,
390 "(testing.TB).Fatal": true,
391 "(testing.TB).Fatalf": true,
392 "(testing.TB).Log": true,
393 "(testing.TB).Logf": true,
394 "(testing.TB).Skip": true,
395 "(testing.TB).Skipf": true,
396 }
397
398
399
400
401
402
403
404
405
406
407
408 func formatString(pass *analysis.Pass, call *ast.CallExpr) (format string, idx int) {
409 typ := pass.TypesInfo.Types[call.Fun].Type
410 if typ != nil {
411 if sig, ok := typ.(*types.Signature); ok {
412 if !sig.Variadic() {
413
414 return "", -1
415 }
416 idx := sig.Params().Len() - 2
417 if idx < 0 {
418
419
420 return "", -1
421 }
422 s, ok := stringConstantArg(pass, call, idx)
423 if !ok {
424
425 return "", -1
426 }
427 return s, idx
428 }
429 }
430
431
432
433 for idx := range call.Args {
434 if s, ok := stringConstantArg(pass, call, idx); ok {
435 return s, idx
436 }
437 if pass.TypesInfo.Types[call.Args[idx]].Type == types.Typ[types.String] {
438
439
440
441 return "", -1
442 }
443 }
444 return "", -1
445 }
446
447
448
449
450
451 func stringConstantArg(pass *analysis.Pass, call *ast.CallExpr, idx int) (string, bool) {
452 if idx >= len(call.Args) {
453 return "", false
454 }
455 return stringConstantExpr(pass, call.Args[idx])
456 }
457
458
459
460
461
462 func stringConstantExpr(pass *analysis.Pass, expr ast.Expr) (string, bool) {
463 lit := pass.TypesInfo.Types[expr].Value
464 if lit != nil && lit.Kind() == constant.String {
465 return constant.StringVal(lit), true
466 }
467 return "", false
468 }
469
470
471 func checkCall(pass *analysis.Pass) {
472 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
473 nodeFilter := []ast.Node{
474 (*ast.CallExpr)(nil),
475 }
476 inspect.Preorder(nodeFilter, func(n ast.Node) {
477 call := n.(*ast.CallExpr)
478 fn, kind := printfNameAndKind(pass, call)
479 switch kind {
480 case KindPrintf, KindErrorf:
481 checkPrintf(pass, kind, call, fn)
482 case KindPrint:
483 checkPrint(pass, call, fn)
484 }
485 })
486 }
487
488 func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func, kind Kind) {
489 fn, _ = typeutil.Callee(pass.TypesInfo, call).(*types.Func)
490 if fn == nil {
491 return nil, 0
492 }
493
494 _, ok := isPrint[fn.FullName()]
495 if !ok {
496
497 _, ok = isPrint[strings.ToLower(fn.Name())]
498 }
499 if ok {
500 if fn.FullName() == "fmt.Errorf" {
501 kind = KindErrorf
502 } else if strings.HasSuffix(fn.Name(), "f") {
503 kind = KindPrintf
504 } else {
505 kind = KindPrint
506 }
507 return fn, kind
508 }
509
510 var fact isWrapper
511 if pass.ImportObjectFact(fn, &fact) {
512 return fn, fact.Kind
513 }
514
515 return fn, KindNone
516 }
517
518
519
520 func isFormatter(typ types.Type) bool {
521
522 if _, ok := typ.Underlying().(*types.Interface); ok {
523
524
525
526 if !typeparams.IsTypeParam(typ) {
527 return true
528 }
529 }
530 obj, _, _ := types.LookupFieldOrMethod(typ, false, nil, "Format")
531 fn, ok := obj.(*types.Func)
532 if !ok {
533 return false
534 }
535 sig := fn.Type().(*types.Signature)
536 return sig.Params().Len() == 2 &&
537 sig.Results().Len() == 0 &&
538 isNamed(sig.Params().At(0).Type(), "fmt", "State") &&
539 types.Identical(sig.Params().At(1).Type(), types.Typ[types.Rune])
540 }
541
542 func isNamed(T types.Type, pkgpath, name string) bool {
543 named, ok := T.(*types.Named)
544 return ok && named.Obj().Pkg().Path() == pkgpath && named.Obj().Name() == name
545 }
546
547
548
549 type formatState struct {
550 verb rune
551 format string
552 name string
553 flags []byte
554 argNums []int
555 firstArg int
556
557 pass *analysis.Pass
558 call *ast.CallExpr
559 argNum int
560 hasIndex bool
561 indexPending bool
562 nbytes int
563 }
564
565
566 func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.Func) {
567 format, idx := formatString(pass, call)
568 if idx < 0 {
569 if false {
570 pass.Reportf(call.Lparen, "can't check non-constant format in call to %s", fn.FullName())
571 }
572 return
573 }
574
575 firstArg := idx + 1
576 if !strings.Contains(format, "%") {
577 if len(call.Args) > firstArg {
578 pass.Reportf(call.Lparen, "%s call has arguments but no formatting directives", fn.FullName())
579 }
580 return
581 }
582
583 argNum := firstArg
584 maxArgNum := firstArg
585 anyIndex := false
586 for i, w := 0, 0; i < len(format); i += w {
587 w = 1
588 if format[i] != '%' {
589 continue
590 }
591 state := parsePrintfVerb(pass, call, fn.FullName(), format[i:], firstArg, argNum)
592 if state == nil {
593 return
594 }
595 w = len(state.format)
596 if !okPrintfArg(pass, call, state) {
597 return
598 }
599 if state.hasIndex {
600 anyIndex = true
601 }
602 if state.verb == 'w' {
603 switch kind {
604 case KindNone, KindPrint, KindPrintf:
605 pass.Reportf(call.Pos(), "%s does not support error-wrapping directive %%w", state.name)
606 return
607 }
608 }
609 if len(state.argNums) > 0 {
610
611 argNum = state.argNums[len(state.argNums)-1] + 1
612 }
613 for _, n := range state.argNums {
614 if n >= maxArgNum {
615 maxArgNum = n + 1
616 }
617 }
618 }
619
620 if call.Ellipsis.IsValid() && maxArgNum >= len(call.Args)-1 {
621 return
622 }
623
624 if anyIndex {
625 return
626 }
627
628 if maxArgNum != len(call.Args) {
629 expect := maxArgNum - firstArg
630 numArgs := len(call.Args) - firstArg
631 pass.ReportRangef(call, "%s call needs %v but has %v", fn.FullName(), count(expect, "arg"), count(numArgs, "arg"))
632 }
633 }
634
635
636 func (s *formatState) parseFlags() {
637 for s.nbytes < len(s.format) {
638 switch c := s.format[s.nbytes]; c {
639 case '#', '0', '+', '-', ' ':
640 s.flags = append(s.flags, c)
641 s.nbytes++
642 default:
643 return
644 }
645 }
646 }
647
648
649 func (s *formatState) scanNum() {
650 for ; s.nbytes < len(s.format); s.nbytes++ {
651 c := s.format[s.nbytes]
652 if c < '0' || '9' < c {
653 return
654 }
655 }
656 }
657
658
659 func (s *formatState) parseIndex() bool {
660 if s.nbytes == len(s.format) || s.format[s.nbytes] != '[' {
661 return true
662 }
663
664 s.nbytes++
665 start := s.nbytes
666 s.scanNum()
667 ok := true
668 if s.nbytes == len(s.format) || s.nbytes == start || s.format[s.nbytes] != ']' {
669 ok = false
670 s.nbytes = strings.Index(s.format[start:], "]")
671 if s.nbytes < 0 {
672 s.pass.ReportRangef(s.call, "%s format %s is missing closing ]", s.name, s.format)
673 return false
674 }
675 s.nbytes = s.nbytes + start
676 }
677 arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32)
678 if err != nil || !ok || arg32 <= 0 || arg32 > int64(len(s.call.Args)-s.firstArg) {
679 s.pass.ReportRangef(s.call, "%s format has invalid argument index [%s]", s.name, s.format[start:s.nbytes])
680 return false
681 }
682 s.nbytes++
683 arg := int(arg32)
684 arg += s.firstArg - 1
685 s.argNum = arg
686 s.hasIndex = true
687 s.indexPending = true
688 return true
689 }
690
691
692 func (s *formatState) parseNum() bool {
693 if s.nbytes < len(s.format) && s.format[s.nbytes] == '*' {
694 if s.indexPending {
695 s.indexPending = false
696 }
697 s.nbytes++
698 s.argNums = append(s.argNums, s.argNum)
699 s.argNum++
700 } else {
701 s.scanNum()
702 }
703 return true
704 }
705
706
707 func (s *formatState) parsePrecision() bool {
708
709 if s.nbytes < len(s.format) && s.format[s.nbytes] == '.' {
710 s.flags = append(s.flags, '.')
711 s.nbytes++
712 if !s.parseIndex() {
713 return false
714 }
715 if !s.parseNum() {
716 return false
717 }
718 }
719 return true
720 }
721
722
723
724
725 func parsePrintfVerb(pass *analysis.Pass, call *ast.CallExpr, name, format string, firstArg, argNum int) *formatState {
726 state := &formatState{
727 format: format,
728 name: name,
729 flags: make([]byte, 0, 5),
730 argNum: argNum,
731 argNums: make([]int, 0, 1),
732 nbytes: 1,
733 firstArg: firstArg,
734 pass: pass,
735 call: call,
736 }
737
738 state.parseFlags()
739
740 if !state.parseIndex() {
741 return nil
742 }
743
744 if !state.parseNum() {
745 return nil
746 }
747
748 if !state.parsePrecision() {
749 return nil
750 }
751
752 if !state.indexPending && !state.parseIndex() {
753 return nil
754 }
755 if state.nbytes == len(state.format) {
756 pass.ReportRangef(call.Fun, "%s format %s is missing verb at end of string", name, state.format)
757 return nil
758 }
759 verb, w := utf8.DecodeRuneInString(state.format[state.nbytes:])
760 state.verb = verb
761 state.nbytes += w
762 if verb != '%' {
763 state.argNums = append(state.argNums, state.argNum)
764 }
765 state.format = state.format[:state.nbytes]
766 return state
767 }
768
769
770 type printfArgType int
771
772 const (
773 argBool printfArgType = 1 << iota
774 argInt
775 argRune
776 argString
777 argFloat
778 argComplex
779 argPointer
780 argError
781 anyType printfArgType = ^0
782 )
783
784 type printVerb struct {
785 verb rune
786 flags string
787 typ printfArgType
788 }
789
790
791 const (
792 noFlag = ""
793 numFlag = " -+.0"
794 sharpNumFlag = " -+.0#"
795 allFlags = " -+.0#"
796 )
797
798
799 var printVerbs = []printVerb{
800
801
802
803
804
805 {'%', noFlag, 0},
806 {'b', sharpNumFlag, argInt | argFloat | argComplex | argPointer},
807 {'c', "-", argRune | argInt},
808 {'d', numFlag, argInt | argPointer},
809 {'e', sharpNumFlag, argFloat | argComplex},
810 {'E', sharpNumFlag, argFloat | argComplex},
811 {'f', sharpNumFlag, argFloat | argComplex},
812 {'F', sharpNumFlag, argFloat | argComplex},
813 {'g', sharpNumFlag, argFloat | argComplex},
814 {'G', sharpNumFlag, argFloat | argComplex},
815 {'o', sharpNumFlag, argInt | argPointer},
816 {'O', sharpNumFlag, argInt | argPointer},
817 {'p', "-#", argPointer},
818 {'q', " -+.0#", argRune | argInt | argString},
819 {'s', " -+.0", argString},
820 {'t', "-", argBool},
821 {'T', "-", anyType},
822 {'U', "-#", argRune | argInt},
823 {'v', allFlags, anyType},
824 {'w', allFlags, argError},
825 {'x', sharpNumFlag, argRune | argInt | argString | argPointer | argFloat | argComplex},
826 {'X', sharpNumFlag, argRune | argInt | argString | argPointer | argFloat | argComplex},
827 }
828
829
830
831
832 func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (ok bool) {
833 var v printVerb
834 found := false
835
836 for _, v = range printVerbs {
837 if v.verb == state.verb {
838 found = true
839 break
840 }
841 }
842
843
844
845 formatter := false
846 if v.typ != argError && state.argNum < len(call.Args) {
847 if tv, ok := pass.TypesInfo.Types[call.Args[state.argNum]]; ok {
848 formatter = isFormatter(tv.Type)
849 }
850 }
851
852 if !formatter {
853 if !found {
854 pass.ReportRangef(call, "%s format %s has unknown verb %c", state.name, state.format, state.verb)
855 return false
856 }
857 for _, flag := range state.flags {
858
859
860 if flag == '0' {
861 continue
862 }
863 if !strings.ContainsRune(v.flags, rune(flag)) {
864 pass.ReportRangef(call, "%s format %s has unrecognized flag %c", state.name, state.format, flag)
865 return false
866 }
867 }
868 }
869
870
871 trueArgs := 1
872 if state.verb == '%' {
873 trueArgs = 0
874 }
875 nargs := len(state.argNums)
876 for i := 0; i < nargs-trueArgs; i++ {
877 argNum := state.argNums[i]
878 if !argCanBeChecked(pass, call, i, state) {
879 return
880 }
881 arg := call.Args[argNum]
882 if reason, ok := matchArgType(pass, argInt, arg); !ok {
883 details := ""
884 if reason != "" {
885 details = " (" + reason + ")"
886 }
887 pass.ReportRangef(call, "%s format %s uses non-int %s%s as argument of *", state.name, state.format, analysisutil.Format(pass.Fset, arg), details)
888 return false
889 }
890 }
891
892 if state.verb == '%' || formatter {
893 return true
894 }
895 argNum := state.argNums[len(state.argNums)-1]
896 if !argCanBeChecked(pass, call, len(state.argNums)-1, state) {
897 return false
898 }
899 arg := call.Args[argNum]
900 if isFunctionValue(pass, arg) && state.verb != 'p' && state.verb != 'T' {
901 pass.ReportRangef(call, "%s format %s arg %s is a func value, not called", state.name, state.format, analysisutil.Format(pass.Fset, arg))
902 return false
903 }
904 if reason, ok := matchArgType(pass, v.typ, arg); !ok {
905 typeString := ""
906 if typ := pass.TypesInfo.Types[arg].Type; typ != nil {
907 typeString = typ.String()
908 }
909 details := ""
910 if reason != "" {
911 details = " (" + reason + ")"
912 }
913 pass.ReportRangef(call, "%s format %s has arg %s of wrong type %s%s, see also https://pkg.go.dev/fmt#hdr-Printing", state.name, state.format, analysisutil.Format(pass.Fset, arg), typeString, details)
914 return false
915 }
916 if v.typ&argString != 0 && v.verb != 'T' && !bytes.Contains(state.flags, []byte{'#'}) {
917 if methodName, ok := recursiveStringer(pass, arg); ok {
918 pass.ReportRangef(call, "%s format %s with arg %s causes recursive %s method call", state.name, state.format, analysisutil.Format(pass.Fset, arg), methodName)
919 return false
920 }
921 }
922 return true
923 }
924
925
926
927
928
929
930
931 func recursiveStringer(pass *analysis.Pass, e ast.Expr) (string, bool) {
932 typ := pass.TypesInfo.Types[e].Type
933
934
935 if isFormatter(typ) {
936 return "", false
937 }
938
939
940 strObj, _, _ := types.LookupFieldOrMethod(typ, false, pass.Pkg, "String")
941 strMethod, strOk := strObj.(*types.Func)
942 errObj, _, _ := types.LookupFieldOrMethod(typ, false, pass.Pkg, "Error")
943 errMethod, errOk := errObj.(*types.Func)
944 if !strOk && !errOk {
945 return "", false
946 }
947
948
949 inScope := func(e ast.Expr, f *types.Func) bool {
950 return f.Scope() != nil && f.Scope().Contains(e.Pos())
951 }
952
953
954 var method *types.Func
955 if strOk && strMethod.Pkg() == pass.Pkg && inScope(e, strMethod) {
956 method = strMethod
957 } else if errOk && errMethod.Pkg() == pass.Pkg && inScope(e, errMethod) {
958 method = errMethod
959 } else {
960 return "", false
961 }
962
963 sig := method.Type().(*types.Signature)
964 if !isStringer(sig) {
965 return "", false
966 }
967
968
969 if u, ok := e.(*ast.UnaryExpr); ok && u.Op == token.AND {
970 e = u.X
971 }
972 if id, ok := e.(*ast.Ident); ok {
973 if pass.TypesInfo.Uses[id] == sig.Recv() {
974 return method.FullName(), true
975 }
976 }
977 return "", false
978 }
979
980
981 func isStringer(sig *types.Signature) bool {
982 return sig.Params().Len() == 0 &&
983 sig.Results().Len() == 1 &&
984 sig.Results().At(0).Type() == types.Typ[types.String]
985 }
986
987
988
989 func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool {
990 if typ := pass.TypesInfo.Types[e].Type; typ != nil {
991 _, ok := typ.(*types.Signature)
992 return ok
993 }
994 return false
995 }
996
997
998
999
1000 func argCanBeChecked(pass *analysis.Pass, call *ast.CallExpr, formatArg int, state *formatState) bool {
1001 argNum := state.argNums[formatArg]
1002 if argNum <= 0 {
1003
1004 panic("negative arg num")
1005 }
1006 if argNum < len(call.Args)-1 {
1007 return true
1008 }
1009 if call.Ellipsis.IsValid() {
1010 return false
1011 }
1012 if argNum < len(call.Args) {
1013 return true
1014 }
1015
1016
1017 arg := argNum - state.firstArg + 1
1018 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"))
1019 return false
1020 }
1021
1022
1023
1024
1025 var printFormatRE = regexp.MustCompile(`%` + flagsRE + numOptRE + `\.?` + numOptRE + indexOptRE + verbRE)
1026
1027 const (
1028 flagsRE = `[+\-#]*`
1029 indexOptRE = `(\[[0-9]+\])?`
1030 numOptRE = `([0-9]+|` + indexOptRE + `\*)?`
1031 verbRE = `[bcdefgopqstvxEFGTUX]`
1032 )
1033
1034
1035 func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) {
1036 firstArg := 0
1037 typ := pass.TypesInfo.Types[call.Fun].Type
1038 if typ == nil {
1039
1040 return
1041 }
1042 if sig, ok := typ.(*types.Signature); ok {
1043 if !sig.Variadic() {
1044
1045 return
1046 }
1047 params := sig.Params()
1048 firstArg = params.Len() - 1
1049
1050 typ := params.At(firstArg).Type()
1051 typ = typ.(*types.Slice).Elem()
1052 it, ok := typ.(*types.Interface)
1053 if !ok || !it.Empty() {
1054
1055 return
1056 }
1057 }
1058 args := call.Args
1059 if len(args) <= firstArg {
1060
1061 return
1062 }
1063 args = args[firstArg:]
1064
1065 if firstArg == 0 {
1066 if sel, ok := call.Args[0].(*ast.SelectorExpr); ok {
1067 if x, ok := sel.X.(*ast.Ident); ok {
1068 if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") {
1069 pass.ReportRangef(call, "%s does not take io.Writer but has first arg %s", fn.FullName(), analysisutil.Format(pass.Fset, call.Args[0]))
1070 }
1071 }
1072 }
1073 }
1074
1075 arg := args[0]
1076 if s, ok := stringConstantExpr(pass, arg); ok {
1077
1078
1079 s = strings.TrimSuffix(s, "%")
1080 if strings.Contains(s, "%") {
1081 m := printFormatRE.FindStringSubmatch(s)
1082 if m != nil {
1083 pass.ReportRangef(call, "%s call has possible formatting directive %s", fn.FullName(), m[0])
1084 }
1085 }
1086 }
1087 if strings.HasSuffix(fn.Name(), "ln") {
1088
1089 arg = args[len(args)-1]
1090 if s, ok := stringConstantExpr(pass, arg); ok {
1091 if strings.HasSuffix(s, "\n") {
1092 pass.ReportRangef(call, "%s arg list ends with redundant newline", fn.FullName())
1093 }
1094 }
1095 }
1096 for _, arg := range args {
1097 if isFunctionValue(pass, arg) {
1098 pass.ReportRangef(call, "%s arg %s is a func value, not called", fn.FullName(), analysisutil.Format(pass.Fset, arg))
1099 }
1100 if methodName, ok := recursiveStringer(pass, arg); ok {
1101 pass.ReportRangef(call, "%s arg %s causes recursive call to %s method", fn.FullName(), analysisutil.Format(pass.Fset, arg), methodName)
1102 }
1103 }
1104 }
1105
1106
1107
1108 func count(n int, what string) string {
1109 if n == 1 {
1110 return "1 " + what
1111 }
1112 return fmt.Sprintf("%d %ss", n, what)
1113 }
1114
1115
1116
1117 type stringSet map[string]bool
1118
1119 func (ss stringSet) String() string {
1120 var list []string
1121 for name := range ss {
1122 list = append(list, name)
1123 }
1124 sort.Strings(list)
1125 return strings.Join(list, ",")
1126 }
1127
1128 func (ss stringSet) Set(flag string) error {
1129 for _, name := range strings.Split(flag, ",") {
1130 if len(name) == 0 {
1131 return fmt.Errorf("empty string")
1132 }
1133 if !strings.Contains(name, ".") {
1134 name = strings.ToLower(name)
1135 }
1136 ss[name] = true
1137 }
1138 return nil
1139 }
1140
View as plain text