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