5
5
package printf
6
6
7
7
import (
8
- "bytes"
9
8
_ "embed"
10
9
"fmt"
11
10
"go/ast"
@@ -371,6 +370,18 @@ var isPrint = stringSet{
371
370
"(testing.TB).Skipf" : true ,
372
371
}
373
372
373
+ // stringConstantExpr returns expression's string constant value.
374
+ //
375
+ // ("", false) is returned if expression isn't a string
376
+ // constant.
377
+ func stringConstantExpr (pass * analysis.Pass , expr ast.Expr ) (string , bool ) {
378
+ lit := pass .TypesInfo .Types [expr ].Value
379
+ if lit != nil && lit .Kind () == constant .String {
380
+ return constant .StringVal (lit ), true
381
+ }
382
+ return "" , false
383
+ }
384
+
374
385
// checkCall triggers the print-specific checks if the call invokes a print function.
375
386
func checkCall (pass * analysis.Pass ) {
376
387
inspect := pass .ResultOf [inspect .Analyzer ].(* inspector.Inspector )
@@ -446,10 +457,10 @@ func isFormatter(typ types.Type) bool {
446
457
types .Identical (sig .Params ().At (1 ).Type (), types .Typ [types .Rune ])
447
458
}
448
459
449
- // FormatStringIndex returns the index of the format string (the last
460
+ // formatStringIndex returns the index of the format string (the last
450
461
// non-variadic parameter) within the given printf-like call
451
462
// expression, or -1 if unknown.
452
- func FormatStringIndex (info * types.Info , call * ast.CallExpr ) int {
463
+ func formatStringIndex (info * types.Info , call * ast.CallExpr ) int {
453
464
typ := info .Types [call .Fun ].Type
454
465
if typ == nil {
455
466
return - 1 // missing type
@@ -473,12 +484,12 @@ func FormatStringIndex(info *types.Info, call *ast.CallExpr) int {
473
484
474
485
// checkPrintf checks a call to a formatted print routine such as Printf.
475
486
func checkPrintf (pass * analysis.Pass , kind Kind , call * ast.CallExpr , name string ) {
476
- idx := FormatStringIndex (pass .TypesInfo , call )
487
+ idx := formatStringIndex (pass .TypesInfo , call )
477
488
if idx < 0 || idx >= len (call .Args ) {
478
489
return
479
490
}
480
491
formatArg := call .Args [idx ]
481
- format , ok := stringConstantExpr (pass . TypesInfo , formatArg )
492
+ format , ok := stringConstantExpr (pass , formatArg )
482
493
if ! ok {
483
494
// Format string argument is non-constant.
484
495
@@ -518,20 +529,25 @@ func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, name string
518
529
// "fmt.Sprintf call needs 1 arg but has 2 args".
519
530
operations , err := fmtstr .Parse (format , idx )
520
531
if err != nil {
532
+ // All error messages are in predicate form ("call has a problem")
533
+ // so that they may be affixed into a subject ("log.Printf ").
534
+ // See https://go-review.googlesource.com/c/tools/+/632598/comment/9d980373_e6460abf/
521
535
pass .ReportRangef (call .Args [idx ], "%s %s" , name , err )
522
536
return
523
537
}
524
538
525
- maxArgIndex := firstArg
539
+ // index of the highest used index.
540
+ maxArgIndex := firstArg - 1
526
541
anyIndex := false
527
542
// Check formats against args.
528
543
for _ , operation := range operations {
529
- if operation .Prec != nil && operation . Prec .Index != - 1 ||
530
- operation .Width != nil && operation . Width .Index != - 1 ||
544
+ if operation .Prec .Index != - 1 ||
545
+ operation .Width .Index != - 1 ||
531
546
operation .Verb .Index != - 1 {
532
547
anyIndex = true
533
548
}
534
- if ! okPrintfArg (pass , call , & maxArgIndex , firstArg , name , operation ) { // One error per format is enough.
549
+ if ! okPrintfArg (pass , call , & maxArgIndex , firstArg , name , operation ) {
550
+ // One error per format is enough.
535
551
return
536
552
}
537
553
if operation .Verb .Verb == 'w' {
@@ -543,16 +559,16 @@ func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, name string
543
559
}
544
560
}
545
561
// Dotdotdot is hard.
546
- if call .Ellipsis .IsValid () && maxArgIndex >= len (call .Args )- 1 {
562
+ if call .Ellipsis .IsValid () && maxArgIndex >= len (call .Args )- 2 {
547
563
return
548
564
}
549
565
// If any formats are indexed, extra arguments are ignored.
550
566
if anyIndex {
551
567
return
552
568
}
553
569
// There should be no leftover arguments.
554
- if maxArgIndex != len (call .Args ) {
555
- expect := maxArgIndex - firstArg
570
+ if maxArgIndex + 1 < len (call .Args ) {
571
+ expect := maxArgIndex + 1 - firstArg
556
572
numArgs := len (call .Args ) - firstArg
557
573
pass .ReportRangef (call , "%s call needs %v but has %v" , name , count (expect , "arg" ), count (numArgs , "arg" ))
558
574
}
@@ -619,8 +635,8 @@ var printVerbs = []printVerb{
619
635
}
620
636
621
637
// okPrintfArg compares the operation to the arguments actually present,
622
- // reporting any discrepancies it can discern. If the final argument is ellipsissed,
623
- // there's little it can do for that.
638
+ // reporting any discrepancies it can discern, maxArgIndex was the index of the highest used index.
639
+ // If the final argument is ellipsissed, there's little it can do for that.
624
640
func okPrintfArg (pass * analysis.Pass , call * ast.CallExpr , maxArgIndex * int , firstArg int , name string , operation * fmtstr.Operation ) (ok bool ) {
625
641
verb := operation .Verb .Verb
626
642
var v printVerb
@@ -662,16 +678,15 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
662
678
663
679
var argIndexes []int
664
680
// First check for *.
665
- if operation .Width != nil && operation . Width .Dynamic != - 1 {
681
+ if operation .Width .Dynamic != - 1 {
666
682
argIndexes = append (argIndexes , operation .Width .Dynamic )
667
683
}
668
- if operation .Prec != nil && operation . Prec . Dynamic > 0 {
684
+ if operation .Prec . Dynamic != - 1 {
669
685
argIndexes = append (argIndexes , operation .Prec .Dynamic )
670
686
}
671
687
// If len(argIndexes)>0, we have something like %.*s and all
672
688
// indexes in argIndexes must be an integer.
673
- for i := 0 ; i < len (argIndexes ); i ++ {
674
- argIndex := argIndexes [i ]
689
+ for _ , argIndex := range argIndexes {
675
690
if ! argCanBeChecked (pass , call , argIndex , firstArg , operation , name ) {
676
691
return
677
692
}
@@ -691,9 +706,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
691
706
argIndexes = append (argIndexes , operation .Verb .ArgIndex )
692
707
}
693
708
for _ , index := range argIndexes {
694
- if index >= * maxArgIndex {
695
- * maxArgIndex = index + 1
696
- }
709
+ * maxArgIndex = max (* maxArgIndex , index )
697
710
}
698
711
699
712
// Special case for '%', go will print "fmt.Printf("%10.2%%dhello", 4)"
@@ -725,7 +738,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
725
738
pass .ReportRangef (call , "%s format %s has arg %s of wrong type %s%s" , name , operation .Text , analysisutil .Format (pass .Fset , arg ), typeString , details )
726
739
return false
727
740
}
728
- if v .typ & argString != 0 && v .verb != 'T' && ! bytes .Contains (operation .Flags , [] byte { '#' } ) {
741
+ if v .typ & argString != 0 && v .verb != 'T' && ! strings .Contains (operation .Flags , "#" ) {
729
742
if methodName , ok := recursiveStringer (pass , arg ); ok {
730
743
pass .ReportRangef (call , "%s format %s with arg %s causes recursive %s method call" , name , operation .Text , analysisutil .Format (pass .Fset , arg ), methodName )
731
744
return false
@@ -886,7 +899,7 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, name string) {
886
899
}
887
900
888
901
arg := args [0 ]
889
- if s , ok := stringConstantExpr (pass . TypesInfo , arg ); ok {
902
+ if s , ok := stringConstantExpr (pass , arg ); ok {
890
903
// Ignore trailing % character
891
904
// The % in "abc 0.0%" couldn't be a formatting directive.
892
905
s = strings .TrimSuffix (s , "%" )
@@ -900,7 +913,7 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, name string) {
900
913
if strings .HasSuffix (name , "ln" ) {
901
914
// The last item, if a string, should not have a newline.
902
915
arg = args [len (args )- 1 ]
903
- if s , ok := stringConstantExpr (pass . TypesInfo , arg ); ok {
916
+ if s , ok := stringConstantExpr (pass , arg ); ok {
904
917
if strings .HasSuffix (s , "\n " ) {
905
918
pass .ReportRangef (call , "%s arg list ends with redundant newline" , name )
906
919
}
@@ -916,17 +929,6 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, name string) {
916
929
}
917
930
}
918
931
919
- // StringConstantExpr returns expression's string constant value.
920
- //
921
- // ("", false) is returned if expression isn't a string constant.
922
- func stringConstantExpr (info * types.Info , expr ast.Expr ) (string , bool ) {
923
- lit := info .Types [expr ].Value
924
- if lit != nil && lit .Kind () == constant .String {
925
- return constant .StringVal (lit ), true
926
- }
927
- return "" , false
928
- }
929
-
930
932
// count(n, what) returns "1 what" or "N whats"
931
933
// (assuming the plural of what is whats).
932
934
func count (n int , what string ) string {
0 commit comments