Source file src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go

     1  // Copyright 2010 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package printf
     6  
     7  import (
     8  	_ "embed"
     9  	"fmt"
    10  	"go/ast"
    11  	"go/constant"
    12  	"go/token"
    13  	"go/types"
    14  	"reflect"
    15  	"regexp"
    16  	"sort"
    17  	"strings"
    18  
    19  	"golang.org/x/tools/go/analysis"
    20  	"golang.org/x/tools/go/analysis/passes/inspect"
    21  	"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
    22  	"golang.org/x/tools/go/ast/inspector"
    23  	"golang.org/x/tools/go/types/typeutil"
    24  	"golang.org/x/tools/internal/analysisinternal"
    25  	"golang.org/x/tools/internal/astutil"
    26  	"golang.org/x/tools/internal/fmtstr"
    27  	"golang.org/x/tools/internal/typeparams"
    28  	"golang.org/x/tools/internal/versions"
    29  )
    30  
    31  func init() {
    32  	Analyzer.Flags.Var(isPrint, "funcs", "comma-separated list of print function names to check")
    33  }
    34  
    35  //go:embed doc.go
    36  var doc string
    37  
    38  var Analyzer = &analysis.Analyzer{
    39  	Name:       "printf",
    40  	Doc:        analysisutil.MustExtractDoc(doc, "printf"),
    41  	URL:        "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/printf",
    42  	Requires:   []*analysis.Analyzer{inspect.Analyzer},
    43  	Run:        run,
    44  	ResultType: reflect.TypeOf((*Result)(nil)),
    45  	FactTypes:  []analysis.Fact{new(isWrapper)},
    46  }
    47  
    48  // Kind is a kind of fmt function behavior.
    49  type Kind int
    50  
    51  const (
    52  	KindNone   Kind = iota // not a fmt wrapper function
    53  	KindPrint              // function behaves like fmt.Print
    54  	KindPrintf             // function behaves like fmt.Printf
    55  	KindErrorf             // function behaves like fmt.Errorf
    56  )
    57  
    58  func (kind Kind) String() string {
    59  	switch kind {
    60  	case KindPrint:
    61  		return "print"
    62  	case KindPrintf:
    63  		return "printf"
    64  	case KindErrorf:
    65  		return "errorf"
    66  	}
    67  	return ""
    68  }
    69  
    70  // Result is the printf analyzer's result type. Clients may query the result
    71  // to learn whether a function behaves like fmt.Print or fmt.Printf.
    72  type Result struct {
    73  	funcs map[*types.Func]Kind
    74  }
    75  
    76  // Kind reports whether fn behaves like fmt.Print or fmt.Printf.
    77  func (r *Result) Kind(fn *types.Func) Kind {
    78  	_, ok := isPrint[fn.FullName()]
    79  	if !ok {
    80  		// Next look up just "printf", for use with -printf.funcs.
    81  		_, ok = isPrint[strings.ToLower(fn.Name())]
    82  	}
    83  	if ok {
    84  		if strings.HasSuffix(fn.Name(), "f") {
    85  			return KindPrintf
    86  		} else {
    87  			return KindPrint
    88  		}
    89  	}
    90  
    91  	return r.funcs[fn]
    92  }
    93  
    94  // isWrapper is a fact indicating that a function is a print or printf wrapper.
    95  type isWrapper struct{ Kind Kind }
    96  
    97  func (f *isWrapper) AFact() {}
    98  
    99  func (f *isWrapper) String() string {
   100  	switch f.Kind {
   101  	case KindPrintf:
   102  		return "printfWrapper"
   103  	case KindPrint:
   104  		return "printWrapper"
   105  	case KindErrorf:
   106  		return "errorfWrapper"
   107  	default:
   108  		return "unknownWrapper"
   109  	}
   110  }
   111  
   112  func run(pass *analysis.Pass) (any, error) {
   113  	res := &Result{
   114  		funcs: make(map[*types.Func]Kind),
   115  	}
   116  	findPrintfLike(pass, res)
   117  	checkCalls(pass)
   118  	return res, nil
   119  }
   120  
   121  type printfWrapper struct {
   122  	obj     *types.Func
   123  	fdecl   *ast.FuncDecl
   124  	format  *types.Var
   125  	args    *types.Var
   126  	callers []printfCaller
   127  	failed  bool // if true, not a printf wrapper
   128  }
   129  
   130  type printfCaller struct {
   131  	w    *printfWrapper
   132  	call *ast.CallExpr
   133  }
   134  
   135  // maybePrintfWrapper decides whether decl (a declared function) may be a wrapper
   136  // around a fmt.Printf or fmt.Print function. If so it returns a printfWrapper
   137  // function describing the declaration. Later processing will analyze the
   138  // graph of potential printf wrappers to pick out the ones that are true wrappers.
   139  // A function may be a Printf or Print wrapper if its last argument is ...interface{}.
   140  // If the next-to-last argument is a string, then this may be a Printf wrapper.
   141  // Otherwise it may be a Print wrapper.
   142  func maybePrintfWrapper(info *types.Info, decl ast.Decl) *printfWrapper {
   143  	// Look for functions with final argument type ...interface{}.
   144  	fdecl, ok := decl.(*ast.FuncDecl)
   145  	if !ok || fdecl.Body == nil {
   146  		return nil
   147  	}
   148  	fn, ok := info.Defs[fdecl.Name].(*types.Func)
   149  	// Type information may be incomplete.
   150  	if !ok {
   151  		return nil
   152  	}
   153  
   154  	sig := fn.Type().(*types.Signature)
   155  	if !sig.Variadic() {
   156  		return nil // not variadic
   157  	}
   158  
   159  	params := sig.Params()
   160  	nparams := params.Len() // variadic => nonzero
   161  
   162  	// Check final parameter is "args ...interface{}".
   163  	args := params.At(nparams - 1)
   164  	iface, ok := types.Unalias(args.Type().(*types.Slice).Elem()).(*types.Interface)
   165  	if !ok || !iface.Empty() {
   166  		return nil
   167  	}
   168  
   169  	// Is second last param 'format string'?
   170  	var format *types.Var
   171  	if nparams >= 2 {
   172  		if p := params.At(nparams - 2); p.Type() == types.Typ[types.String] {
   173  			format = p
   174  		}
   175  	}
   176  
   177  	return &printfWrapper{
   178  		obj:    fn,
   179  		fdecl:  fdecl,
   180  		format: format,
   181  		args:   args,
   182  	}
   183  }
   184  
   185  // findPrintfLike scans the entire package to find printf-like functions.
   186  func findPrintfLike(pass *analysis.Pass, res *Result) (any, error) {
   187  	// Gather potential wrappers and call graph between them.
   188  	byObj := make(map[*types.Func]*printfWrapper)
   189  	var wrappers []*printfWrapper
   190  	for _, file := range pass.Files {
   191  		for _, decl := range file.Decls {
   192  			w := maybePrintfWrapper(pass.TypesInfo, decl)
   193  			if w == nil {
   194  				continue
   195  			}
   196  			byObj[w.obj] = w
   197  			wrappers = append(wrappers, w)
   198  		}
   199  	}
   200  
   201  	// Walk the graph to figure out which are really printf wrappers.
   202  	for _, w := range wrappers {
   203  		// Scan function for calls that could be to other printf-like functions.
   204  		ast.Inspect(w.fdecl.Body, func(n ast.Node) bool {
   205  			if w.failed {
   206  				return false
   207  			}
   208  
   209  			// TODO: Relax these checks; issue 26555.
   210  			if assign, ok := n.(*ast.AssignStmt); ok {
   211  				for _, lhs := range assign.Lhs {
   212  					if match(pass.TypesInfo, lhs, w.format) ||
   213  						match(pass.TypesInfo, lhs, w.args) {
   214  						// Modifies the format
   215  						// string or args in
   216  						// some way, so not a
   217  						// simple wrapper.
   218  						w.failed = true
   219  						return false
   220  					}
   221  				}
   222  			}
   223  			if un, ok := n.(*ast.UnaryExpr); ok && un.Op == token.AND {
   224  				if match(pass.TypesInfo, un.X, w.format) ||
   225  					match(pass.TypesInfo, un.X, w.args) {
   226  					// Taking the address of the
   227  					// format string or args,
   228  					// so not a simple wrapper.
   229  					w.failed = true
   230  					return false
   231  				}
   232  			}
   233  
   234  			call, ok := n.(*ast.CallExpr)
   235  			if !ok || len(call.Args) == 0 || !match(pass.TypesInfo, call.Args[len(call.Args)-1], w.args) {
   236  				return true
   237  			}
   238  
   239  			fn, kind := printfNameAndKind(pass, call)
   240  			if kind != 0 {
   241  				checkPrintfFwd(pass, w, call, kind, res)
   242  				return true
   243  			}
   244  
   245  			// If the call is to another function in this package,
   246  			// maybe we will find out it is printf-like later.
   247  			// Remember this call for later checking.
   248  			if fn != nil && fn.Pkg() == pass.Pkg && byObj[fn] != nil {
   249  				callee := byObj[fn]
   250  				callee.callers = append(callee.callers, printfCaller{w, call})
   251  			}
   252  
   253  			return true
   254  		})
   255  	}
   256  	return nil, nil
   257  }
   258  
   259  func match(info *types.Info, arg ast.Expr, param *types.Var) bool {
   260  	id, ok := arg.(*ast.Ident)
   261  	return ok && info.ObjectOf(id) == param
   262  }
   263  
   264  // checkPrintfFwd checks that a printf-forwarding wrapper is forwarding correctly.
   265  // It diagnoses writing fmt.Printf(format, args) instead of fmt.Printf(format, args...).
   266  func checkPrintfFwd(pass *analysis.Pass, w *printfWrapper, call *ast.CallExpr, kind Kind, res *Result) {
   267  	matched := kind == KindPrint ||
   268  		kind != KindNone && len(call.Args) >= 2 && match(pass.TypesInfo, call.Args[len(call.Args)-2], w.format)
   269  	if !matched {
   270  		return
   271  	}
   272  
   273  	if !call.Ellipsis.IsValid() {
   274  		typ, ok := pass.TypesInfo.Types[call.Fun].Type.(*types.Signature)
   275  		if !ok {
   276  			return
   277  		}
   278  		if len(call.Args) > typ.Params().Len() {
   279  			// If we're passing more arguments than what the
   280  			// print/printf function can take, adding an ellipsis
   281  			// would break the program. For example:
   282  			//
   283  			//   func foo(arg1 string, arg2 ...interface{}) {
   284  			//       fmt.Printf("%s %v", arg1, arg2)
   285  			//   }
   286  			return
   287  		}
   288  		desc := "printf"
   289  		if kind == KindPrint {
   290  			desc = "print"
   291  		}
   292  		pass.ReportRangef(call, "missing ... in args forwarded to %s-like function", desc)
   293  		return
   294  	}
   295  	fn := w.obj
   296  	var fact isWrapper
   297  	if !pass.ImportObjectFact(fn, &fact) {
   298  		fact.Kind = kind
   299  		pass.ExportObjectFact(fn, &fact)
   300  		res.funcs[fn] = kind
   301  		for _, caller := range w.callers {
   302  			checkPrintfFwd(pass, caller.w, caller.call, kind, res)
   303  		}
   304  	}
   305  }
   306  
   307  // isPrint records the print functions.
   308  // If a key ends in 'f' then it is assumed to be a formatted print.
   309  //
   310  // Keys are either values returned by (*types.Func).FullName,
   311  // or case-insensitive identifiers such as "errorf".
   312  //
   313  // The -funcs flag adds to this set.
   314  //
   315  // The set below includes facts for many important standard library
   316  // functions, even though the analysis is capable of deducing that, for
   317  // example, fmt.Printf forwards to fmt.Fprintf. We avoid relying on the
   318  // driver applying analyzers to standard packages because "go vet" does
   319  // not do so with gccgo, and nor do some other build systems.
   320  var isPrint = stringSet{
   321  	"fmt.Appendf":  true,
   322  	"fmt.Append":   true,
   323  	"fmt.Appendln": true,
   324  	"fmt.Errorf":   true,
   325  	"fmt.Fprint":   true,
   326  	"fmt.Fprintf":  true,
   327  	"fmt.Fprintln": true,
   328  	"fmt.Print":    true,
   329  	"fmt.Printf":   true,
   330  	"fmt.Println":  true,
   331  	"fmt.Sprint":   true,
   332  	"fmt.Sprintf":  true,
   333  	"fmt.Sprintln": true,
   334  
   335  	"runtime/trace.Logf": true,
   336  
   337  	"log.Print":             true,
   338  	"log.Printf":            true,
   339  	"log.Println":           true,
   340  	"log.Fatal":             true,
   341  	"log.Fatalf":            true,
   342  	"log.Fatalln":           true,
   343  	"log.Panic":             true,
   344  	"log.Panicf":            true,
   345  	"log.Panicln":           true,
   346  	"(*log.Logger).Fatal":   true,
   347  	"(*log.Logger).Fatalf":  true,
   348  	"(*log.Logger).Fatalln": true,
   349  	"(*log.Logger).Panic":   true,
   350  	"(*log.Logger).Panicf":  true,
   351  	"(*log.Logger).Panicln": true,
   352  	"(*log.Logger).Print":   true,
   353  	"(*log.Logger).Printf":  true,
   354  	"(*log.Logger).Println": true,
   355  
   356  	"(*testing.common).Error":  true,
   357  	"(*testing.common).Errorf": true,
   358  	"(*testing.common).Fatal":  true,
   359  	"(*testing.common).Fatalf": true,
   360  	"(*testing.common).Log":    true,
   361  	"(*testing.common).Logf":   true,
   362  	"(*testing.common).Skip":   true,
   363  	"(*testing.common).Skipf":  true,
   364  	// *testing.T and B are detected by induction, but testing.TB is
   365  	// an interface and the inference can't follow dynamic calls.
   366  	"(testing.TB).Error":  true,
   367  	"(testing.TB).Errorf": true,
   368  	"(testing.TB).Fatal":  true,
   369  	"(testing.TB).Fatalf": true,
   370  	"(testing.TB).Log":    true,
   371  	"(testing.TB).Logf":   true,
   372  	"(testing.TB).Skip":   true,
   373  	"(testing.TB).Skipf":  true,
   374  }
   375  
   376  // formatStringIndex returns the index of the format string (the last
   377  // non-variadic parameter) within the given printf-like call
   378  // expression, or -1 if unknown.
   379  func formatStringIndex(pass *analysis.Pass, call *ast.CallExpr) int {
   380  	typ := pass.TypesInfo.Types[call.Fun].Type
   381  	if typ == nil {
   382  		return -1 // missing type
   383  	}
   384  	sig, ok := typ.(*types.Signature)
   385  	if !ok {
   386  		return -1 // ill-typed
   387  	}
   388  	if !sig.Variadic() {
   389  		// Skip checking non-variadic functions.
   390  		return -1
   391  	}
   392  	idx := sig.Params().Len() - 2
   393  	if idx < 0 {
   394  		// Skip checking variadic functions without
   395  		// fixed arguments.
   396  		return -1
   397  	}
   398  	return idx
   399  }
   400  
   401  // stringConstantExpr returns expression's string constant value.
   402  //
   403  // ("", false) is returned if expression isn't a string
   404  // constant.
   405  func stringConstantExpr(pass *analysis.Pass, expr ast.Expr) (string, bool) {
   406  	lit := pass.TypesInfo.Types[expr].Value
   407  	if lit != nil && lit.Kind() == constant.String {
   408  		return constant.StringVal(lit), true
   409  	}
   410  	return "", false
   411  }
   412  
   413  // checkCalls triggers the print-specific checks for calls that invoke a print
   414  // function.
   415  func checkCalls(pass *analysis.Pass) {
   416  	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
   417  	nodeFilter := []ast.Node{
   418  		(*ast.File)(nil),
   419  		(*ast.CallExpr)(nil),
   420  	}
   421  
   422  	var fileVersion string // for selectively suppressing checks; "" if unknown.
   423  	inspect.Preorder(nodeFilter, func(n ast.Node) {
   424  		switch n := n.(type) {
   425  		case *ast.File:
   426  			fileVersion = versions.Lang(versions.FileVersion(pass.TypesInfo, n))
   427  
   428  		case *ast.CallExpr:
   429  			fn, kind := printfNameAndKind(pass, n)
   430  			switch kind {
   431  			case KindPrintf, KindErrorf:
   432  				checkPrintf(pass, fileVersion, kind, n, fn.FullName())
   433  			case KindPrint:
   434  				checkPrint(pass, n, fn.FullName())
   435  			}
   436  		}
   437  	})
   438  }
   439  
   440  func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func, kind Kind) {
   441  	fn, _ = typeutil.Callee(pass.TypesInfo, call).(*types.Func)
   442  	if fn == nil {
   443  		return nil, 0
   444  	}
   445  
   446  	// Facts are associated with generic declarations, not instantiations.
   447  	fn = fn.Origin()
   448  
   449  	_, ok := isPrint[fn.FullName()]
   450  	if !ok {
   451  		// Next look up just "printf", for use with -printf.funcs.
   452  		_, ok = isPrint[strings.ToLower(fn.Name())]
   453  	}
   454  	if ok {
   455  		if fn.FullName() == "fmt.Errorf" {
   456  			kind = KindErrorf
   457  		} else if strings.HasSuffix(fn.Name(), "f") {
   458  			kind = KindPrintf
   459  		} else {
   460  			kind = KindPrint
   461  		}
   462  		return fn, kind
   463  	}
   464  
   465  	var fact isWrapper
   466  	if pass.ImportObjectFact(fn, &fact) {
   467  		return fn, fact.Kind
   468  	}
   469  
   470  	return fn, KindNone
   471  }
   472  
   473  // isFormatter reports whether t could satisfy fmt.Formatter.
   474  // The only interface method to look for is "Format(State, rune)".
   475  func isFormatter(typ types.Type) bool {
   476  	// If the type is an interface, the value it holds might satisfy fmt.Formatter.
   477  	if _, ok := typ.Underlying().(*types.Interface); ok {
   478  		// Don't assume type parameters could be formatters. With the greater
   479  		// expressiveness of constraint interface syntax we expect more type safety
   480  		// when using type parameters.
   481  		if !typeparams.IsTypeParam(typ) {
   482  			return true
   483  		}
   484  	}
   485  	obj, _, _ := types.LookupFieldOrMethod(typ, false, nil, "Format")
   486  	fn, ok := obj.(*types.Func)
   487  	if !ok {
   488  		return false
   489  	}
   490  	sig := fn.Type().(*types.Signature)
   491  	return sig.Params().Len() == 2 &&
   492  		sig.Results().Len() == 0 &&
   493  		analysisinternal.IsTypeNamed(sig.Params().At(0).Type(), "fmt", "State") &&
   494  		types.Identical(sig.Params().At(1).Type(), types.Typ[types.Rune])
   495  }
   496  
   497  // checkPrintf checks a call to a formatted print routine such as Printf.
   498  func checkPrintf(pass *analysis.Pass, fileVersion string, kind Kind, call *ast.CallExpr, name string) {
   499  	idx := formatStringIndex(pass, call)
   500  	if idx < 0 || idx >= len(call.Args) {
   501  		return
   502  	}
   503  	formatArg := call.Args[idx]
   504  	format, ok := stringConstantExpr(pass, formatArg)
   505  	if !ok {
   506  		// Format string argument is non-constant.
   507  
   508  		// It is a common mistake to call fmt.Printf(msg) with a
   509  		// non-constant format string and no arguments:
   510  		// if msg contains "%", misformatting occurs.
   511  		// Report the problem and suggest a fix: fmt.Printf("%s", msg).
   512  		//
   513  		// However, as described in golang/go#71485, this analysis can produce a
   514  		// significant number of diagnostics in existing code, and the bugs it
   515  		// finds are sometimes unlikely or inconsequential, and may not be worth
   516  		// fixing for some users. Gating on language version allows us to avoid
   517  		// breaking existing tests and CI scripts.
   518  		if !suppressNonconstants &&
   519  			idx == len(call.Args)-1 &&
   520  			fileVersion != "" && // fail open
   521  			versions.AtLeast(fileVersion, "go1.24") {
   522  
   523  			pass.Report(analysis.Diagnostic{
   524  				Pos: formatArg.Pos(),
   525  				End: formatArg.End(),
   526  				Message: fmt.Sprintf("non-constant format string in call to %s",
   527  					name),
   528  				SuggestedFixes: []analysis.SuggestedFix{{
   529  					Message: `Insert "%s" format string`,
   530  					TextEdits: []analysis.TextEdit{{
   531  						Pos:     formatArg.Pos(),
   532  						End:     formatArg.Pos(),
   533  						NewText: []byte(`"%s", `),
   534  					}},
   535  				}},
   536  			})
   537  		}
   538  		return
   539  	}
   540  
   541  	firstArg := idx + 1 // Arguments are immediately after format string.
   542  	if !strings.Contains(format, "%") {
   543  		if len(call.Args) > firstArg {
   544  			pass.ReportRangef(call.Args[firstArg], "%s call has arguments but no formatting directives", name)
   545  		}
   546  		return
   547  	}
   548  
   549  	// Pass the string constant value so
   550  	// fmt.Sprintf("%"+("s"), "hi", 3) can be reported as
   551  	// "fmt.Sprintf call needs 1 arg but has 2 args".
   552  	operations, err := fmtstr.Parse(format, idx)
   553  	if err != nil {
   554  		// All error messages are in predicate form ("call has a problem")
   555  		// so that they may be affixed into a subject ("log.Printf ").
   556  		pass.ReportRangef(formatArg, "%s %s", name, err)
   557  		return
   558  	}
   559  
   560  	// index of the highest used index.
   561  	maxArgIndex := firstArg - 1
   562  	anyIndex := false
   563  	// Check formats against args.
   564  	for _, op := range operations {
   565  		if op.Prec.Index != -1 ||
   566  			op.Width.Index != -1 ||
   567  			op.Verb.Index != -1 {
   568  			anyIndex = true
   569  		}
   570  		rng := opRange(formatArg, op)
   571  		if !okPrintfArg(pass, call, rng, &maxArgIndex, firstArg, name, op) {
   572  			// One error per format is enough.
   573  			return
   574  		}
   575  		if op.Verb.Verb == 'w' {
   576  			switch kind {
   577  			case KindNone, KindPrint, KindPrintf:
   578  				pass.ReportRangef(rng, "%s does not support error-wrapping directive %%w", name)
   579  				return
   580  			}
   581  		}
   582  	}
   583  	// Dotdotdot is hard.
   584  	if call.Ellipsis.IsValid() && maxArgIndex >= len(call.Args)-2 {
   585  		return
   586  	}
   587  	// If any formats are indexed, extra arguments are ignored.
   588  	if anyIndex {
   589  		return
   590  	}
   591  	// There should be no leftover arguments.
   592  	if maxArgIndex+1 < len(call.Args) {
   593  		expect := maxArgIndex + 1 - firstArg
   594  		numArgs := len(call.Args) - firstArg
   595  		pass.ReportRangef(call, "%s call needs %v but has %v", name, count(expect, "arg"), count(numArgs, "arg"))
   596  	}
   597  }
   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  
   611  // printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask.
   612  type printfArgType int
   613  
   614  const (
   615  	argBool printfArgType = 1 << iota
   616  	argInt
   617  	argRune
   618  	argString
   619  	argFloat
   620  	argComplex
   621  	argPointer
   622  	argError
   623  	anyType printfArgType = ^0
   624  )
   625  
   626  type printVerb struct {
   627  	verb  rune   // User may provide verb through Formatter; could be a rune.
   628  	flags string // known flags are all ASCII
   629  	typ   printfArgType
   630  }
   631  
   632  // Common flag sets for printf verbs.
   633  const (
   634  	noFlag       = ""
   635  	numFlag      = " -+.0"
   636  	sharpNumFlag = " -+.0#"
   637  	allFlags     = " -+.0#"
   638  )
   639  
   640  // printVerbs identifies which flags are known to printf for each verb.
   641  var printVerbs = []printVerb{
   642  	// '-' is a width modifier, always valid.
   643  	// '.' is a precision for float, max width for strings.
   644  	// '+' is required sign for numbers, Go format for %v.
   645  	// '#' is alternate format for several verbs.
   646  	// ' ' is spacer for numbers
   647  	{'%', noFlag, 0},
   648  	{'b', sharpNumFlag, argInt | argFloat | argComplex | argPointer},
   649  	{'c', "-", argRune | argInt},
   650  	{'d', numFlag, argInt | argPointer},
   651  	{'e', sharpNumFlag, argFloat | argComplex},
   652  	{'E', sharpNumFlag, argFloat | argComplex},
   653  	{'f', sharpNumFlag, argFloat | argComplex},
   654  	{'F', sharpNumFlag, argFloat | argComplex},
   655  	{'g', sharpNumFlag, argFloat | argComplex},
   656  	{'G', sharpNumFlag, argFloat | argComplex},
   657  	{'o', sharpNumFlag, argInt | argPointer},
   658  	{'O', sharpNumFlag, argInt | argPointer},
   659  	{'p', "-#", argPointer},
   660  	{'q', " -+.0#", argRune | argInt | argString},
   661  	{'s', " -+.0", argString},
   662  	{'t', "-", argBool},
   663  	{'T', "-", anyType},
   664  	{'U', "-#", argRune | argInt},
   665  	{'v', allFlags, anyType},
   666  	{'w', allFlags, argError},
   667  	{'x', sharpNumFlag, argRune | argInt | argString | argPointer | argFloat | argComplex},
   668  	{'X', sharpNumFlag, argRune | argInt | argString | argPointer | argFloat | argComplex},
   669  }
   670  
   671  // okPrintfArg compares the operation to the arguments actually present,
   672  // reporting any discrepancies it can discern, maxArgIndex was the index of the highest used index.
   673  // If the final argument is ellipsissed, there's little it can do for that.
   674  func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, rng analysis.Range, maxArgIndex *int, firstArg int, name string, operation *fmtstr.Operation) (ok bool) {
   675  	verb := operation.Verb.Verb
   676  	var v printVerb
   677  	found := false
   678  	// Linear scan is fast enough for a small list.
   679  	for _, v = range printVerbs {
   680  		if v.verb == verb {
   681  			found = true
   682  			break
   683  		}
   684  	}
   685  
   686  	// Could verb's arg implement fmt.Formatter?
   687  	// Skip check for the %w verb, which requires an error.
   688  	formatter := false
   689  	if v.typ != argError && operation.Verb.ArgIndex < len(call.Args) {
   690  		if tv, ok := pass.TypesInfo.Types[call.Args[operation.Verb.ArgIndex]]; ok {
   691  			formatter = isFormatter(tv.Type)
   692  		}
   693  	}
   694  
   695  	if !formatter {
   696  		if !found {
   697  			pass.ReportRangef(rng, "%s format %s has unknown verb %c", name, operation.Text, verb)
   698  			return false
   699  		}
   700  		for _, flag := range operation.Flags {
   701  			// TODO: Disable complaint about '0' for Go 1.10. To be fixed properly in 1.11.
   702  			// See issues 23598 and 23605.
   703  			if flag == '0' {
   704  				continue
   705  			}
   706  			if !strings.ContainsRune(v.flags, rune(flag)) {
   707  				pass.ReportRangef(rng, "%s format %s has unrecognized flag %c", name, operation.Text, flag)
   708  				return false
   709  			}
   710  		}
   711  	}
   712  
   713  	var argIndexes []int
   714  	// First check for *.
   715  	if operation.Width.Dynamic != -1 {
   716  		argIndexes = append(argIndexes, operation.Width.Dynamic)
   717  	}
   718  	if operation.Prec.Dynamic != -1 {
   719  		argIndexes = append(argIndexes, operation.Prec.Dynamic)
   720  	}
   721  	// If len(argIndexes)>0, we have something like %.*s and all
   722  	// indexes in argIndexes must be an integer.
   723  	for _, argIndex := range argIndexes {
   724  		if !argCanBeChecked(pass, call, rng, argIndex, firstArg, operation, name) {
   725  			return
   726  		}
   727  		arg := call.Args[argIndex]
   728  		if reason, ok := matchArgType(pass, argInt, arg); !ok {
   729  			details := ""
   730  			if reason != "" {
   731  				details = " (" + reason + ")"
   732  			}
   733  			pass.ReportRangef(rng, "%s format %s uses non-int %s%s as argument of *", name, operation.Text, analysisinternal.Format(pass.Fset, arg), details)
   734  			return false
   735  		}
   736  	}
   737  
   738  	// Collect to update maxArgNum in one loop.
   739  	if operation.Verb.ArgIndex != -1 && verb != '%' {
   740  		argIndexes = append(argIndexes, operation.Verb.ArgIndex)
   741  	}
   742  	for _, index := range argIndexes {
   743  		*maxArgIndex = max(*maxArgIndex, index)
   744  	}
   745  
   746  	// Special case for '%', go will print "fmt.Printf("%10.2%%dhello", 4)"
   747  	// as "%4hello", discard any runes between the two '%'s, and treat the verb '%'
   748  	// as an ordinary rune, so early return to skip the type check.
   749  	if verb == '%' || formatter {
   750  		return true
   751  	}
   752  
   753  	// Now check verb's type.
   754  	verbArgIndex := operation.Verb.ArgIndex
   755  	if !argCanBeChecked(pass, call, rng, verbArgIndex, firstArg, operation, name) {
   756  		return false
   757  	}
   758  	arg := call.Args[verbArgIndex]
   759  	if isFunctionValue(pass, arg) && verb != 'p' && verb != 'T' {
   760  		pass.ReportRangef(rng, "%s format %s arg %s is a func value, not called", name, operation.Text, analysisinternal.Format(pass.Fset, arg))
   761  		return false
   762  	}
   763  	if reason, ok := matchArgType(pass, v.typ, arg); !ok {
   764  		typeString := ""
   765  		if typ := pass.TypesInfo.Types[arg].Type; typ != nil {
   766  			typeString = typ.String()
   767  		}
   768  		details := ""
   769  		if reason != "" {
   770  			details = " (" + reason + ")"
   771  		}
   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)
   773  		return false
   774  	}
   775  	// Detect recursive formatting via value's String/Error methods.
   776  	// The '#' flag suppresses the methods, except with %x, %X, and %q.
   777  	if v.typ&argString != 0 && v.verb != 'T' && (!strings.Contains(operation.Flags, "#") || strings.ContainsRune("qxX", v.verb)) {
   778  		if methodName, ok := recursiveStringer(pass, arg); ok {
   779  			pass.ReportRangef(rng, "%s format %s with arg %s causes recursive %s method call", name, operation.Text, analysisinternal.Format(pass.Fset, arg), methodName)
   780  			return false
   781  		}
   782  	}
   783  	return true
   784  }
   785  
   786  // recursiveStringer reports whether the argument e is a potential
   787  // recursive call to stringer or is an error, such as t and &t in these examples:
   788  //
   789  //	func (t *T) String() string { printf("%s",  t) }
   790  //	func (t  T) Error() string { printf("%s",  t) }
   791  //	func (t  T) String() string { printf("%s", &t) }
   792  func recursiveStringer(pass *analysis.Pass, e ast.Expr) (string, bool) {
   793  	typ := pass.TypesInfo.Types[e].Type
   794  
   795  	// It's unlikely to be a recursive stringer if it has a Format method.
   796  	if isFormatter(typ) {
   797  		return "", false
   798  	}
   799  
   800  	// Does e allow e.String() or e.Error()?
   801  	strObj, _, _ := types.LookupFieldOrMethod(typ, false, pass.Pkg, "String")
   802  	strMethod, strOk := strObj.(*types.Func)
   803  	errObj, _, _ := types.LookupFieldOrMethod(typ, false, pass.Pkg, "Error")
   804  	errMethod, errOk := errObj.(*types.Func)
   805  	if !strOk && !errOk {
   806  		return "", false
   807  	}
   808  
   809  	// inScope returns true if e is in the scope of f.
   810  	inScope := func(e ast.Expr, f *types.Func) bool {
   811  		return f.Scope() != nil && f.Scope().Contains(e.Pos())
   812  	}
   813  
   814  	// Is the expression e within the body of that String or Error method?
   815  	var method *types.Func
   816  	if strOk && strMethod.Pkg() == pass.Pkg && inScope(e, strMethod) {
   817  		method = strMethod
   818  	} else if errOk && errMethod.Pkg() == pass.Pkg && inScope(e, errMethod) {
   819  		method = errMethod
   820  	} else {
   821  		return "", false
   822  	}
   823  
   824  	sig := method.Type().(*types.Signature)
   825  	if !isStringer(sig) {
   826  		return "", false
   827  	}
   828  
   829  	// Is it the receiver r, or &r?
   830  	if u, ok := e.(*ast.UnaryExpr); ok && u.Op == token.AND {
   831  		e = u.X // strip off & from &r
   832  	}
   833  	if id, ok := e.(*ast.Ident); ok {
   834  		if pass.TypesInfo.Uses[id] == sig.Recv() {
   835  			return method.FullName(), true
   836  		}
   837  	}
   838  	return "", false
   839  }
   840  
   841  // isStringer reports whether the method signature matches the String() definition in fmt.Stringer.
   842  func isStringer(sig *types.Signature) bool {
   843  	return sig.Params().Len() == 0 &&
   844  		sig.Results().Len() == 1 &&
   845  		sig.Results().At(0).Type() == types.Typ[types.String]
   846  }
   847  
   848  // isFunctionValue reports whether the expression is a function as opposed to a function call.
   849  // It is almost always a mistake to print a function value.
   850  func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool {
   851  	if typ := pass.TypesInfo.Types[e].Type; typ != nil {
   852  		// Don't call Underlying: a named func type with a String method is ok.
   853  		// TODO(adonovan): it would be more precise to check isStringer.
   854  		_, ok := typ.(*types.Signature)
   855  		return ok
   856  	}
   857  	return false
   858  }
   859  
   860  // argCanBeChecked reports whether the specified argument is statically present;
   861  // it may be beyond the list of arguments or in a terminal slice... argument, which
   862  // means we can't see it.
   863  func argCanBeChecked(pass *analysis.Pass, call *ast.CallExpr, rng analysis.Range, argIndex, firstArg int, operation *fmtstr.Operation, name string) bool {
   864  	if argIndex <= 0 {
   865  		// Shouldn't happen, so catch it with prejudice.
   866  		panic("negative argIndex")
   867  	}
   868  	if argIndex < len(call.Args)-1 {
   869  		return true // Always OK.
   870  	}
   871  	if call.Ellipsis.IsValid() {
   872  		return false // We just can't tell; there could be many more arguments.
   873  	}
   874  	if argIndex < len(call.Args) {
   875  		return true
   876  	}
   877  	// There are bad indexes in the format or there are fewer arguments than the format needs.
   878  	// This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi".
   879  	arg := argIndex - firstArg + 1 // People think of arguments as 1-indexed.
   880  	pass.ReportRangef(rng, "%s format %s reads arg #%d, but call has %v", name, operation.Text, arg, count(len(call.Args)-firstArg, "arg"))
   881  	return false
   882  }
   883  
   884  // printFormatRE is the regexp we match and report as a possible format string
   885  // in the first argument to unformatted prints like fmt.Print.
   886  // We exclude the space flag, so that printing a string like "x % y" is not reported as a format.
   887  var printFormatRE = regexp.MustCompile(`%` + flagsRE + numOptRE + `\.?` + numOptRE + indexOptRE + verbRE)
   888  
   889  const (
   890  	flagsRE    = `[+\-#]*`
   891  	indexOptRE = `(\[[0-9]+\])?`
   892  	numOptRE   = `([0-9]+|` + indexOptRE + `\*)?`
   893  	verbRE     = `[bcdefgopqstvxEFGTUX]`
   894  )
   895  
   896  // checkPrint checks a call to an unformatted print routine such as Println.
   897  func checkPrint(pass *analysis.Pass, call *ast.CallExpr, name string) {
   898  	firstArg := 0
   899  	typ := pass.TypesInfo.Types[call.Fun].Type
   900  	if typ == nil {
   901  		// Skip checking functions with unknown type.
   902  		return
   903  	}
   904  	if sig, ok := typ.Underlying().(*types.Signature); ok {
   905  		if !sig.Variadic() {
   906  			// Skip checking non-variadic functions.
   907  			return
   908  		}
   909  		params := sig.Params()
   910  		firstArg = params.Len() - 1
   911  
   912  		typ := params.At(firstArg).Type()
   913  		typ = typ.(*types.Slice).Elem()
   914  		it, ok := types.Unalias(typ).(*types.Interface)
   915  		if !ok || !it.Empty() {
   916  			// Skip variadic functions accepting non-interface{} args.
   917  			return
   918  		}
   919  	}
   920  	args := call.Args
   921  	if len(args) <= firstArg {
   922  		// Skip calls without variadic args.
   923  		return
   924  	}
   925  	args = args[firstArg:]
   926  
   927  	if firstArg == 0 {
   928  		if sel, ok := call.Args[0].(*ast.SelectorExpr); ok {
   929  			if x, ok := sel.X.(*ast.Ident); ok {
   930  				if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") {
   931  					pass.ReportRangef(call, "%s does not take io.Writer but has first arg %s", name, analysisinternal.Format(pass.Fset, call.Args[0]))
   932  				}
   933  			}
   934  		}
   935  	}
   936  
   937  	arg := args[0]
   938  	if s, ok := stringConstantExpr(pass, arg); ok {
   939  		// Ignore trailing % character
   940  		// The % in "abc 0.0%" couldn't be a formatting directive.
   941  		s = strings.TrimSuffix(s, "%")
   942  		if strings.Contains(s, "%") {
   943  			for _, m := range printFormatRE.FindAllString(s, -1) {
   944  				// Allow %XX where XX are hex digits,
   945  				// as this is common in URLs.
   946  				if len(m) >= 3 && isHex(m[1]) && isHex(m[2]) {
   947  					continue
   948  				}
   949  				pass.ReportRangef(call, "%s call has possible Printf formatting directive %s", name, m)
   950  				break // report only the first one
   951  			}
   952  		}
   953  	}
   954  	if strings.HasSuffix(name, "ln") {
   955  		// The last item, if a string, should not have a newline.
   956  		arg = args[len(args)-1]
   957  		if s, ok := stringConstantExpr(pass, arg); ok {
   958  			if strings.HasSuffix(s, "\n") {
   959  				pass.ReportRangef(call, "%s arg list ends with redundant newline", name)
   960  			}
   961  		}
   962  	}
   963  	for _, arg := range args {
   964  		if isFunctionValue(pass, arg) {
   965  			pass.ReportRangef(call, "%s arg %s is a func value, not called", name, analysisinternal.Format(pass.Fset, arg))
   966  		}
   967  		if methodName, ok := recursiveStringer(pass, arg); ok {
   968  			pass.ReportRangef(call, "%s arg %s causes recursive call to %s method", name, analysisinternal.Format(pass.Fset, arg), methodName)
   969  		}
   970  	}
   971  }
   972  
   973  // count(n, what) returns "1 what" or "N whats"
   974  // (assuming the plural of what is whats).
   975  func count(n int, what string) string {
   976  	if n == 1 {
   977  		return "1 " + what
   978  	}
   979  	return fmt.Sprintf("%d %ss", n, what)
   980  }
   981  
   982  // stringSet is a set-of-nonempty-strings-valued flag.
   983  // Note: elements without a '.' get lower-cased.
   984  type stringSet map[string]bool
   985  
   986  func (ss stringSet) String() string {
   987  	var list []string
   988  	for name := range ss {
   989  		list = append(list, name)
   990  	}
   991  	sort.Strings(list)
   992  	return strings.Join(list, ",")
   993  }
   994  
   995  func (ss stringSet) Set(flag string) error {
   996  	for _, name := range strings.Split(flag, ",") {
   997  		if len(name) == 0 {
   998  			return fmt.Errorf("empty string")
   999  		}
  1000  		if !strings.Contains(name, ".") {
  1001  			name = strings.ToLower(name)
  1002  		}
  1003  		ss[name] = true
  1004  	}
  1005  	return nil
  1006  }
  1007  
  1008  // suppressNonconstants suppresses reporting printf calls with
  1009  // non-constant formatting strings (proposal #60529) when true.
  1010  //
  1011  // This variable is to allow for staging the transition to newer
  1012  // versions of x/tools by vendoring.
  1013  //
  1014  // Remove this after the 1.24 release.
  1015  var suppressNonconstants bool
  1016  
  1017  // isHex reports whether b is a hex digit.
  1018  func isHex(b byte) bool {
  1019  	return '0' <= b && b <= '9' ||
  1020  		'A' <= b && b <= 'F' ||
  1021  		'a' <= b && b <= 'f'
  1022  }
  1023  

View as plain text