@@ -22,6 +22,7 @@ import (
2222	"golang.org/x/tools/go/ast/inspector" 
2323	"golang.org/x/tools/go/types/typeutil" 
2424	"golang.org/x/tools/internal/analysisinternal" 
25+ 	"golang.org/x/tools/internal/astutil" 
2526	"golang.org/x/tools/internal/fmtstr" 
2627	"golang.org/x/tools/internal/typeparams" 
2728	"golang.org/x/tools/internal/versions" 
@@ -540,7 +541,7 @@ func checkPrintf(pass *analysis.Pass, fileVersion string, kind Kind, call *ast.C
540541	firstArg  :=  idx  +  1  // Arguments are immediately after format string. 
541542	if  ! strings .Contains (format , "%" ) {
542543		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 )
544545		}
545546		return 
546547	}
@@ -552,28 +553,29 @@ func checkPrintf(pass *analysis.Pass, fileVersion string, kind Kind, call *ast.C
552553	if  err  !=  nil  {
553554		// All error messages are in predicate form ("call has a problem") 
554555		// 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 )
556557		return 
557558	}
558559
559560	// index of the highest used index. 
560561	maxArgIndex  :=  firstArg  -  1 
561562	anyIndex  :=  false 
562563	// 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  {
567568			anyIndex  =  true 
568569		}
569- 		if  ! okPrintfArg (pass , call , & maxArgIndex , firstArg , name , operation ) {
570+ 		rng  :=  opRange (formatArg , op )
571+ 		if  ! okPrintfArg (pass , call , rng , & maxArgIndex , firstArg , name , op ) {
570572			// One error per format is enough. 
571573			return 
572574		}
573- 		if  operation .Verb .Verb  ==  'w'  {
575+ 		if  op .Verb .Verb  ==  'w'  {
574576			switch  kind  {
575577			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 )
577579				return 
578580			}
579581		}
@@ -594,6 +596,18 @@ func checkPrintf(pass *analysis.Pass, fileVersion string, kind Kind, call *ast.C
594596	}
595597}
596598
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+ 
597611// printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask. 
598612type  printfArgType  int 
599613
@@ -657,7 +671,7 @@ var printVerbs = []printVerb{
657671// okPrintfArg compares the operation to the arguments actually present, 
658672// reporting any discrepancies it can discern, maxArgIndex was the index of the highest used index. 
659673// 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 ) {
661675	verb  :=  operation .Verb .Verb 
662676	var  v  printVerb 
663677	found  :=  false 
@@ -680,7 +694,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
680694
681695	if  ! formatter  {
682696		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 )
684698			return  false 
685699		}
686700		for  _ , flag  :=  range  operation .Flags  {
@@ -690,7 +704,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
690704				continue 
691705			}
692706			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 )
694708				return  false 
695709			}
696710		}
@@ -707,7 +721,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
707721	// If len(argIndexes)>0, we have something like %.*s and all 
708722	// indexes in argIndexes must be an integer. 
709723	for  _ , argIndex  :=  range  argIndexes  {
710- 		if  ! argCanBeChecked (pass , call , argIndex , firstArg , operation , name ) {
724+ 		if  ! argCanBeChecked (pass , call , rng ,  argIndex , firstArg , operation , name ) {
711725			return 
712726		}
713727		arg  :=  call .Args [argIndex ]
@@ -716,7 +730,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
716730			if  reason  !=  ""  {
717731				details  =  " ("  +  reason  +  ")" 
718732			}
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 )
720734			return  false 
721735		}
722736	}
@@ -738,12 +752,12 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
738752
739753	// Now check verb's type. 
740754	verbArgIndex  :=  operation .Verb .ArgIndex 
741- 	if  ! argCanBeChecked (pass , call , verbArgIndex , firstArg , operation , name ) {
755+ 	if  ! argCanBeChecked (pass , call , rng ,  verbArgIndex , firstArg , operation , name ) {
742756		return  false 
743757	}
744758	arg  :=  call .Args [verbArgIndex ]
745759	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 ))
747761		return  false 
748762	}
749763	if  reason , ok  :=  matchArgType (pass , v .typ , arg ); ! ok  {
@@ -755,14 +769,14 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
755769		if  reason  !=  ""  {
756770			details  =  " ("  +  reason  +  ")" 
757771		}
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 )
759773		return  false 
760774	}
761775	// Detect recursive formatting via value's String/Error methods. 
762776	// The '#' flag suppresses the methods, except with %x, %X, and %q. 
763777	if  v .typ & argString  !=  0  &&  v .verb  !=  'T'  &&  (! strings .Contains (operation .Flags , "#" ) ||  strings .ContainsRune ("qxX" , v .verb )) {
764778		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 )
766780			return  false 
767781		}
768782	}
@@ -846,7 +860,7 @@ func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool {
846860// argCanBeChecked reports whether the specified argument is statically present; 
847861// it may be beyond the list of arguments or in a terminal slice... argument, which 
848862// 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  {
850864	if  argIndex  <=  0  {
851865		// Shouldn't happen, so catch it with prejudice. 
852866		panic ("negative argIndex" )
@@ -863,7 +877,7 @@ func argCanBeChecked(pass *analysis.Pass, call *ast.CallExpr, argIndex, firstArg
863877	// There are bad indexes in the format or there are fewer arguments than the format needs. 
864878	// This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi". 
865879	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" ))
867881	return  false 
868882}
869883
0 commit comments