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