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