@@ -22,6 +22,7 @@ import (
22
22
"golang.org/x/tools/go/ast/inspector"
23
23
"golang.org/x/tools/go/types/typeutil"
24
24
"golang.org/x/tools/internal/analysisinternal"
25
+ "golang.org/x/tools/internal/astutil"
25
26
"golang.org/x/tools/internal/fmtstr"
26
27
"golang.org/x/tools/internal/typeparams"
27
28
"golang.org/x/tools/internal/versions"
@@ -540,7 +541,7 @@ func checkPrintf(pass *analysis.Pass, fileVersion string, kind Kind, call *ast.C
540
541
firstArg := idx + 1 // Arguments are immediately after format string.
541
542
if ! strings .Contains (format , "%" ) {
542
543
if len (call .Args ) > firstArg {
543
- pass .Reportf (call .Lparen , "%s call has arguments but no formatting directives" , name )
544
+ pass .ReportRangef (call .Args [ firstArg ] , "%s call has arguments but no formatting directives" , name )
544
545
}
545
546
return
546
547
}
@@ -552,28 +553,29 @@ func checkPrintf(pass *analysis.Pass, fileVersion string, kind Kind, call *ast.C
552
553
if err != nil {
553
554
// All error messages are in predicate form ("call has a problem")
554
555
// so that they may be affixed into a subject ("log.Printf ").
555
- pass .ReportRangef (call . Args [ idx ] , "%s %s" , name , err )
556
+ pass .ReportRangef (formatArg , "%s %s" , name , err )
556
557
return
557
558
}
558
559
559
560
// index of the highest used index.
560
561
maxArgIndex := firstArg - 1
561
562
anyIndex := false
562
563
// Check formats against args.
563
- for _ , operation := range operations {
564
- if operation .Prec .Index != - 1 ||
565
- operation .Width .Index != - 1 ||
566
- operation .Verb .Index != - 1 {
564
+ for _ , op := range operations {
565
+ if op .Prec .Index != - 1 ||
566
+ op .Width .Index != - 1 ||
567
+ op .Verb .Index != - 1 {
567
568
anyIndex = true
568
569
}
569
- if ! okPrintfArg (pass , call , & maxArgIndex , firstArg , name , operation ) {
570
+ rng := opRange (formatArg , op )
571
+ if ! okPrintfArg (pass , call , rng , & maxArgIndex , firstArg , name , op ) {
570
572
// One error per format is enough.
571
573
return
572
574
}
573
- if operation .Verb .Verb == 'w' {
575
+ if op .Verb .Verb == 'w' {
574
576
switch kind {
575
577
case KindNone , KindPrint , KindPrintf :
576
- pass .Reportf ( call . Pos () , "%s does not support error-wrapping directive %%w" , name )
578
+ pass .ReportRangef ( rng , "%s does not support error-wrapping directive %%w" , name )
577
579
return
578
580
}
579
581
}
@@ -594,6 +596,18 @@ func checkPrintf(pass *analysis.Pass, fileVersion string, kind Kind, call *ast.C
594
596
}
595
597
}
596
598
599
+ // opRange returns the source range for the specified printf operation,
600
+ // such as the position of the %v substring of "...%v...".
601
+ func opRange (formatArg ast.Expr , op * fmtstr.Operation ) analysis.Range {
602
+ if lit , ok := formatArg .(* ast.BasicLit ); ok {
603
+ start , end , err := astutil .RangeInStringLiteral (lit , op .Range .Start , op .Range .End )
604
+ if err == nil {
605
+ return analysisinternal .Range (start , end ) // position of "%v"
606
+ }
607
+ }
608
+ return formatArg // entire format string
609
+ }
610
+
597
611
// printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask.
598
612
type printfArgType int
599
613
@@ -657,7 +671,7 @@ var printVerbs = []printVerb{
657
671
// okPrintfArg compares the operation to the arguments actually present,
658
672
// reporting any discrepancies it can discern, maxArgIndex was the index of the highest used index.
659
673
// If the final argument is ellipsissed, there's little it can do for that.
660
- func okPrintfArg (pass * analysis.Pass , call * ast.CallExpr , maxArgIndex * int , firstArg int , name string , operation * fmtstr.Operation ) (ok bool ) {
674
+ func okPrintfArg (pass * analysis.Pass , call * ast.CallExpr , rng analysis. Range , maxArgIndex * int , firstArg int , name string , operation * fmtstr.Operation ) (ok bool ) {
661
675
verb := operation .Verb .Verb
662
676
var v printVerb
663
677
found := false
@@ -680,7 +694,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
680
694
681
695
if ! formatter {
682
696
if ! found {
683
- pass .ReportRangef (call , "%s format %s has unknown verb %c" , name , operation .Text , verb )
697
+ pass .ReportRangef (rng , "%s format %s has unknown verb %c" , name , operation .Text , verb )
684
698
return false
685
699
}
686
700
for _ , flag := range operation .Flags {
@@ -690,7 +704,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
690
704
continue
691
705
}
692
706
if ! strings .ContainsRune (v .flags , rune (flag )) {
693
- pass .ReportRangef (call , "%s format %s has unrecognized flag %c" , name , operation .Text , flag )
707
+ pass .ReportRangef (rng , "%s format %s has unrecognized flag %c" , name , operation .Text , flag )
694
708
return false
695
709
}
696
710
}
@@ -707,7 +721,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
707
721
// If len(argIndexes)>0, we have something like %.*s and all
708
722
// indexes in argIndexes must be an integer.
709
723
for _ , argIndex := range argIndexes {
710
- if ! argCanBeChecked (pass , call , argIndex , firstArg , operation , name ) {
724
+ if ! argCanBeChecked (pass , call , rng , argIndex , firstArg , operation , name ) {
711
725
return
712
726
}
713
727
arg := call .Args [argIndex ]
@@ -716,7 +730,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
716
730
if reason != "" {
717
731
details = " (" + reason + ")"
718
732
}
719
- pass .ReportRangef (call , "%s format %s uses non-int %s%s as argument of *" , name , operation .Text , analysisinternal .Format (pass .Fset , arg ), details )
733
+ pass .ReportRangef (rng , "%s format %s uses non-int %s%s as argument of *" , name , operation .Text , analysisinternal .Format (pass .Fset , arg ), details )
720
734
return false
721
735
}
722
736
}
@@ -738,12 +752,12 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
738
752
739
753
// Now check verb's type.
740
754
verbArgIndex := operation .Verb .ArgIndex
741
- if ! argCanBeChecked (pass , call , verbArgIndex , firstArg , operation , name ) {
755
+ if ! argCanBeChecked (pass , call , rng , verbArgIndex , firstArg , operation , name ) {
742
756
return false
743
757
}
744
758
arg := call .Args [verbArgIndex ]
745
759
if isFunctionValue (pass , arg ) && verb != 'p' && verb != 'T' {
746
- pass .ReportRangef (call , "%s format %s arg %s is a func value, not called" , name , operation .Text , analysisinternal .Format (pass .Fset , arg ))
760
+ pass .ReportRangef (rng , "%s format %s arg %s is a func value, not called" , name , operation .Text , analysisinternal .Format (pass .Fset , arg ))
747
761
return false
748
762
}
749
763
if reason , ok := matchArgType (pass , v .typ , arg ); ! ok {
@@ -755,14 +769,14 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
755
769
if reason != "" {
756
770
details = " (" + reason + ")"
757
771
}
758
- pass .ReportRangef (call , "%s format %s has arg %s of wrong type %s%s" , name , operation .Text , analysisinternal .Format (pass .Fset , arg ), typeString , details )
772
+ pass .ReportRangef (rng , "%s format %s has arg %s of wrong type %s%s" , name , operation .Text , analysisinternal .Format (pass .Fset , arg ), typeString , details )
759
773
return false
760
774
}
761
775
// Detect recursive formatting via value's String/Error methods.
762
776
// The '#' flag suppresses the methods, except with %x, %X, and %q.
763
777
if v .typ & argString != 0 && v .verb != 'T' && (! strings .Contains (operation .Flags , "#" ) || strings .ContainsRune ("qxX" , v .verb )) {
764
778
if methodName , ok := recursiveStringer (pass , arg ); ok {
765
- pass .ReportRangef (call , "%s format %s with arg %s causes recursive %s method call" , name , operation .Text , analysisinternal .Format (pass .Fset , arg ), methodName )
779
+ pass .ReportRangef (rng , "%s format %s with arg %s causes recursive %s method call" , name , operation .Text , analysisinternal .Format (pass .Fset , arg ), methodName )
766
780
return false
767
781
}
768
782
}
@@ -846,7 +860,7 @@ func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool {
846
860
// argCanBeChecked reports whether the specified argument is statically present;
847
861
// it may be beyond the list of arguments or in a terminal slice... argument, which
848
862
// means we can't see it.
849
- func argCanBeChecked (pass * analysis.Pass , call * ast.CallExpr , argIndex , firstArg int , operation * fmtstr.Operation , name string ) bool {
863
+ func argCanBeChecked (pass * analysis.Pass , call * ast.CallExpr , rng analysis. Range , argIndex , firstArg int , operation * fmtstr.Operation , name string ) bool {
850
864
if argIndex <= 0 {
851
865
// Shouldn't happen, so catch it with prejudice.
852
866
panic ("negative argIndex" )
@@ -863,7 +877,7 @@ func argCanBeChecked(pass *analysis.Pass, call *ast.CallExpr, argIndex, firstArg
863
877
// There are bad indexes in the format or there are fewer arguments than the format needs.
864
878
// This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi".
865
879
arg := argIndex - firstArg + 1 // People think of arguments as 1-indexed.
866
- pass .ReportRangef (call , "%s format %s reads arg #%d, but call has %v" , name , operation .Text , arg , count (len (call .Args )- firstArg , "arg" ))
880
+ pass .ReportRangef (rng , "%s format %s reads arg #%d, but call has %v" , name , operation .Text , arg , count (len (call .Args )- firstArg , "arg" ))
867
881
return false
868
882
}
869
883
0 commit comments