@@ -492,16 +492,16 @@ func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, name string
492
492
return
493
493
}
494
494
495
- maxArgNum := firstArg
495
+ maxArgIndex := firstArg
496
496
anyIndex := false
497
497
// Check formats against args.
498
498
for _ , directive := range directives {
499
499
if (directive .Prec != nil && directive .Prec .Index != - 1 ) ||
500
500
(directive .Width != nil && directive .Width .Index != - 1 ) ||
501
- (directive .Verb != nil && directive . Verb .Index != - 1 ) {
501
+ (directive .Verb .Index != - 1 ) {
502
502
anyIndex = true
503
503
}
504
- if ! okPrintfArg (pass , call , & maxArgNum , name , directive ) { // One error per format is enough.
504
+ if ! okPrintfArg (pass , call , & maxArgIndex , firstArg , name , directive ) { // One error per format is enough.
505
505
return
506
506
}
507
507
if directive .Verb .Verb == 'w' {
@@ -513,16 +513,16 @@ func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, name string
513
513
}
514
514
}
515
515
// Dotdotdot is hard.
516
- if call .Ellipsis .IsValid () && maxArgNum >= len (call .Args )- 1 {
516
+ if call .Ellipsis .IsValid () && maxArgIndex >= len (call .Args )- 1 {
517
517
return
518
518
}
519
519
// If any formats are indexed, extra arguments are ignored.
520
520
if anyIndex {
521
521
return
522
522
}
523
523
// There should be no leftover arguments.
524
- if maxArgNum != len (call .Args ) {
525
- expect := maxArgNum - firstArg
524
+ if maxArgIndex != len (call .Args ) {
525
+ expect := maxArgIndex - firstArg
526
526
numArgs := len (call .Args ) - firstArg
527
527
pass .ReportRangef (call , "%s call needs %v but has %v" , name , count (expect , "arg" ), count (numArgs , "arg" ))
528
528
}
@@ -591,7 +591,7 @@ var printVerbs = []printVerb{
591
591
// okPrintfArg compares the FormatDirective to the arguments actually present,
592
592
// reporting any discrepancies it can discern. If the final argument is ellipsissed,
593
593
// there's little it can do for that.
594
- func okPrintfArg (pass * analysis.Pass , call * ast.CallExpr , maxArgNum * int , name string , directive * fmtstr.FormatDirective ) (ok bool ) {
594
+ func okPrintfArg (pass * analysis.Pass , call * ast.CallExpr , maxArgIndex * int , firstArg int , name string , directive * fmtstr.FormatDirective ) (ok bool ) {
595
595
verb := directive .Verb .Verb
596
596
var v printVerb
597
597
found := false
@@ -606,8 +606,8 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgNum *int, name s
606
606
// Could verb's arg implement fmt.Formatter?
607
607
// Skip check for the %w verb, which requires an error.
608
608
formatter := false
609
- if v .typ != argError && directive .Verb .ArgNum < len (call .Args ) {
610
- if tv , ok := pass .TypesInfo .Types [call .Args [directive .Verb .ArgNum ]]; ok {
609
+ if v .typ != argError && directive .Verb .ArgIndex < len (call .Args ) {
610
+ if tv , ok := pass .TypesInfo .Types [call .Args [directive .Verb .ArgIndex ]]; ok {
611
611
formatter = isFormatter (tv .Type )
612
612
}
613
613
}
@@ -629,22 +629,23 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgNum *int, name s
629
629
}
630
630
}
631
631
}
632
- var argNums []int
633
- if directive .Width != nil && directive .Width .ArgNum != - 1 {
634
- argNums = append (argNums , directive .Width .ArgNum )
632
+
633
+ var argIndexes []int
634
+ // First check for *.
635
+ if directive .Width != nil && directive .Width .ArgIndex != - 1 {
636
+ argIndexes = append (argIndexes , directive .Width .ArgIndex )
635
637
}
636
- if directive .Prec != nil && directive .Prec .ArgNum != - 1 {
637
- argNums = append (argNums , directive .Prec .ArgNum )
638
+ if directive .Prec != nil && directive .Prec .ArgIndex != - 1 {
639
+ argIndexes = append (argIndexes , directive .Prec .ArgIndex )
638
640
}
639
-
640
- // Verb is good. If len(argNums)>0, we have something like %.*s and all
641
- // args in argNums must be an integer.
642
- for i := 0 ; i < len (argNums ); i ++ {
643
- argNum := argNums [i ]
644
- if ! argCanBeChecked (pass , call , argNums [i ], directive , name ) {
641
+ // If len(argIndexes)>0, we have something like %.*s and all
642
+ // indexes in argIndexes must be an integer.
643
+ for i := 0 ; i < len (argIndexes ); i ++ {
644
+ argIndex := argIndexes [i ]
645
+ if ! argCanBeChecked (pass , call , argIndex , firstArg , directive , name ) {
645
646
return
646
647
}
647
- arg := call .Args [argNum ]
648
+ arg := call .Args [argIndex ]
648
649
if reason , ok := matchArgType (pass , argInt , arg ); ! ok {
649
650
details := ""
650
651
if reason != "" {
@@ -656,25 +657,28 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgNum *int, name s
656
657
}
657
658
658
659
// Collect to update maxArgNum in one loop.
659
- if directive .Verb != nil && directive . Verb . ArgNum != - 1 && verb != '%' {
660
- argNums = append (argNums , directive .Verb .ArgNum )
660
+ if directive .Verb . ArgIndex != - 1 && verb != '%' {
661
+ argIndexes = append (argIndexes , directive .Verb .ArgIndex )
661
662
}
662
- for _ , n := range argNums {
663
- if n >= * maxArgNum {
664
- * maxArgNum = n + 1
663
+ for _ , index := range argIndexes {
664
+ if index >= * maxArgIndex {
665
+ * maxArgIndex = index + 1
665
666
}
666
667
}
667
668
669
+ // Special case for '%', go will print "fmt.Printf("%10.2%%dhello", 4)"
670
+ // as "%4hello", discard any runes between the two '%'s, and treat the verb '%'
671
+ // as an ordinary rune, so early return to skip the type check.
668
672
if verb == '%' || formatter {
669
673
return true
670
674
}
671
675
672
676
// Now check verb's type.
673
- argNum := argNums [ len ( argNums ) - 1 ]
674
- if ! argCanBeChecked (pass , call , argNums [ len ( argNums ) - 1 ] , directive , name ) {
677
+ verbArgIndex := directive . Verb . ArgIndex
678
+ if ! argCanBeChecked (pass , call , verbArgIndex , firstArg , directive , name ) {
675
679
return false
676
680
}
677
- arg := call .Args [argNum ]
681
+ arg := call .Args [verbArgIndex ]
678
682
if isFunctionValue (pass , arg ) && verb != 'p' && verb != 'T' {
679
683
pass .ReportRangef (call , "%s format %s arg %s is a func value, not called" , name , directive .Format , analysisutil .Format (pass .Fset , arg ))
680
684
return false
@@ -777,24 +781,24 @@ func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool {
777
781
// argCanBeChecked reports whether the specified argument is statically present;
778
782
// it may be beyond the list of arguments or in a terminal slice... argument, which
779
783
// means we can't see it.
780
- func argCanBeChecked (pass * analysis.Pass , call * ast.CallExpr , argNum int , directive * fmtstr.FormatDirective , name string ) bool {
781
- if argNum <= 0 {
784
+ func argCanBeChecked (pass * analysis.Pass , call * ast.CallExpr , argIndex , firstArg int , directive * fmtstr.FormatDirective , name string ) bool {
785
+ if argIndex <= 0 {
782
786
// Shouldn't happen, so catch it with prejudice.
783
- panic ("negative arg num " )
787
+ panic ("negative argIndex " )
784
788
}
785
- if argNum < len (call .Args )- 1 {
789
+ if argIndex < len (call .Args )- 1 {
786
790
return true // Always OK.
787
791
}
788
792
if call .Ellipsis .IsValid () {
789
793
return false // We just can't tell; there could be many more arguments.
790
794
}
791
- if argNum < len (call .Args ) {
795
+ if argIndex < len (call .Args ) {
792
796
return true
793
797
}
794
798
// There are bad indexes in the format or there are fewer arguments than the format needs.
795
799
// This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi".
796
- arg := argNum - directive . FirstArg + 1 // People think of arguments as 1-indexed.
797
- pass .ReportRangef (call , "%s format %s reads arg #%d, but call has %v" , name , directive .Format , arg , count (len (call .Args )- directive . FirstArg , "arg" ))
800
+ arg := argIndex - firstArg + 1 // People think of arguments as 1-indexed.
801
+ pass .ReportRangef (call , "%s format %s reads arg #%d, but call has %v" , name , directive .Format , arg , count (len (call .Args )- firstArg , "arg" ))
798
802
return false
799
803
}
800
804
0 commit comments