github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/cmd/vet/print.go (about)

     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  // This file contains the printf-checker.
     6  
     7  package main
     8  
     9  import (
    10  	"flag"
    11  	"go/ast"
    12  	"go/token"
    13  	"strconv"
    14  	"strings"
    15  	"unicode/utf8"
    16  )
    17  
    18  var printfuncs = flag.String("printfuncs", "", "comma-separated list of print function names to check")
    19  
    20  // printfList records the formatted-print functions. The value is the location
    21  // of the format parameter. Names are lower-cased so the lookup is
    22  // case insensitive.
    23  var printfList = map[string]int{
    24  	"errorf":  0,
    25  	"fatalf":  0,
    26  	"fprintf": 1,
    27  	"panicf":  0,
    28  	"printf":  0,
    29  	"sprintf": 0,
    30  }
    31  
    32  // printList records the unformatted-print functions. The value is the location
    33  // of the first parameter to be printed.  Names are lower-cased so the lookup is
    34  // case insensitive.
    35  var printList = map[string]int{
    36  	"error":  0,
    37  	"fatal":  0,
    38  	"fprint": 1, "fprintln": 1,
    39  	"panic": 0, "panicln": 0,
    40  	"print": 0, "println": 0,
    41  	"sprint": 0, "sprintln": 0,
    42  }
    43  
    44  // checkCall triggers the print-specific checks if the call invokes a print function.
    45  func (f *File) checkFmtPrintfCall(call *ast.CallExpr, Name string) {
    46  	if !vet("printf") {
    47  		return
    48  	}
    49  	name := strings.ToLower(Name)
    50  	if skip, ok := printfList[name]; ok {
    51  		f.checkPrintf(call, Name, skip)
    52  		return
    53  	}
    54  	if skip, ok := printList[name]; ok {
    55  		f.checkPrint(call, Name, skip)
    56  		return
    57  	}
    58  }
    59  
    60  // literal returns the literal value represented by the expression, or nil if it is not a literal.
    61  func (f *File) literal(value ast.Expr) *ast.BasicLit {
    62  	switch v := value.(type) {
    63  	case *ast.BasicLit:
    64  		return v
    65  	case *ast.ParenExpr:
    66  		return f.literal(v.X)
    67  	case *ast.BinaryExpr:
    68  		if v.Op != token.ADD {
    69  			break
    70  		}
    71  		litX := f.literal(v.X)
    72  		litY := f.literal(v.Y)
    73  		if litX != nil && litY != nil {
    74  			lit := *litX
    75  			x, errX := strconv.Unquote(litX.Value)
    76  			y, errY := strconv.Unquote(litY.Value)
    77  			if errX == nil && errY == nil {
    78  				return &ast.BasicLit{
    79  					ValuePos: lit.ValuePos,
    80  					Kind:     lit.Kind,
    81  					Value:    strconv.Quote(x + y),
    82  				}
    83  			}
    84  		}
    85  	case *ast.Ident:
    86  		// See if it's a constant or initial value (we can't tell the difference).
    87  		if v.Obj == nil || v.Obj.Decl == nil {
    88  			return nil
    89  		}
    90  		valueSpec, ok := v.Obj.Decl.(*ast.ValueSpec)
    91  		if ok && len(valueSpec.Names) == len(valueSpec.Values) {
    92  			// Find the index in the list of names
    93  			var i int
    94  			for i = 0; i < len(valueSpec.Names); i++ {
    95  				if valueSpec.Names[i].Name == v.Name {
    96  					if lit, ok := valueSpec.Values[i].(*ast.BasicLit); ok {
    97  						return lit
    98  					}
    99  					return nil
   100  				}
   101  			}
   102  		}
   103  	}
   104  	return nil
   105  }
   106  
   107  // checkPrintf checks a call to a formatted print routine such as Printf.
   108  // call.Args[formatIndex] is (well, should be) the format argument.
   109  func (f *File) checkPrintf(call *ast.CallExpr, name string, formatIndex int) {
   110  	if formatIndex >= len(call.Args) {
   111  		return
   112  	}
   113  	lit := f.literal(call.Args[formatIndex])
   114  	if lit == nil {
   115  		if *verbose {
   116  			f.Warn(call.Pos(), "can't check non-literal format in call to", name)
   117  		}
   118  		return
   119  	}
   120  	if lit.Kind != token.STRING {
   121  		f.Badf(call.Pos(), "literal %v not a string in call to", lit.Value, name)
   122  	}
   123  	format, err := strconv.Unquote(lit.Value)
   124  	if err != nil {
   125  		// Shouldn't happen if parser returned no errors, but be safe.
   126  		f.Badf(call.Pos(), "invalid quoted string literal")
   127  	}
   128  	firstArg := formatIndex + 1 // Arguments are immediately after format string.
   129  	if !strings.Contains(format, "%") {
   130  		if len(call.Args) > firstArg {
   131  			f.Badf(call.Pos(), "no formatting directive in %s call", name)
   132  		}
   133  		return
   134  	}
   135  	// Hard part: check formats against args.
   136  	argNum := firstArg
   137  	for i, w := 0, 0; i < len(format); i += w {
   138  		w = 1
   139  		if format[i] == '%' {
   140  			verb, flags, nbytes, nargs := f.parsePrintfVerb(call, format[i:])
   141  			w = nbytes
   142  			if verb == '%' { // "%%" does nothing interesting.
   143  				continue
   144  			}
   145  			// If we've run out of args, print after loop will pick that up.
   146  			if argNum+nargs <= len(call.Args) {
   147  				f.checkPrintfArg(call, verb, flags, argNum, nargs)
   148  			}
   149  			argNum += nargs
   150  		}
   151  	}
   152  	// TODO: Dotdotdot is hard.
   153  	if call.Ellipsis.IsValid() && argNum != len(call.Args) {
   154  		return
   155  	}
   156  	if argNum != len(call.Args) {
   157  		expect := argNum - firstArg
   158  		numArgs := len(call.Args) - firstArg
   159  		f.Badf(call.Pos(), "wrong number of args for format in %s call: %d needed but %d args", name, expect, numArgs)
   160  	}
   161  }
   162  
   163  // parsePrintfVerb returns the verb that begins the format string, along with its flags,
   164  // the number of bytes to advance the format to step past the verb, and number of
   165  // arguments it consumes.
   166  func (f *File) parsePrintfVerb(call *ast.CallExpr, format string) (verb rune, flags []byte, nbytes, nargs int) {
   167  	// There's guaranteed a percent sign.
   168  	flags = make([]byte, 0, 5)
   169  	nbytes = 1
   170  	end := len(format)
   171  	// There may be flags.
   172  FlagLoop:
   173  	for nbytes < end {
   174  		switch format[nbytes] {
   175  		case '#', '0', '+', '-', ' ':
   176  			flags = append(flags, format[nbytes])
   177  			nbytes++
   178  		default:
   179  			break FlagLoop
   180  		}
   181  	}
   182  	getNum := func() {
   183  		if nbytes < end && format[nbytes] == '*' {
   184  			nbytes++
   185  			nargs++
   186  		} else {
   187  			for nbytes < end && '0' <= format[nbytes] && format[nbytes] <= '9' {
   188  				nbytes++
   189  			}
   190  		}
   191  	}
   192  	// There may be a width.
   193  	getNum()
   194  	// If there's a period, there may be a precision.
   195  	if nbytes < end && format[nbytes] == '.' {
   196  		flags = append(flags, '.') // Treat precision as a flag.
   197  		nbytes++
   198  		getNum()
   199  	}
   200  	// Now a verb.
   201  	c, w := utf8.DecodeRuneInString(format[nbytes:])
   202  	nbytes += w
   203  	verb = c
   204  	if c != '%' {
   205  		nargs++
   206  	}
   207  	return
   208  }
   209  
   210  // printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask.
   211  type printfArgType int
   212  
   213  const (
   214  	argBool printfArgType = 1 << iota
   215  	argInt
   216  	argRune
   217  	argString
   218  	argFloat
   219  	argPointer
   220  	anyType printfArgType = ^0
   221  )
   222  
   223  type printVerb struct {
   224  	verb  rune
   225  	flags string // known flags are all ASCII
   226  	typ   printfArgType
   227  }
   228  
   229  // Common flag sets for printf verbs.
   230  const (
   231  	numFlag      = " -+.0"
   232  	sharpNumFlag = " -+.0#"
   233  	allFlags     = " -+.0#"
   234  )
   235  
   236  // printVerbs identifies which flags are known to printf for each verb.
   237  // TODO: A type that implements Formatter may do what it wants, and vet
   238  // will complain incorrectly.
   239  var printVerbs = []printVerb{
   240  	// '-' is a width modifier, always valid.
   241  	// '.' is a precision for float, max width for strings.
   242  	// '+' is required sign for numbers, Go format for %v.
   243  	// '#' is alternate format for several verbs.
   244  	// ' ' is spacer for numbers
   245  	{'b', numFlag, argInt | argFloat},
   246  	{'c', "-", argRune | argInt},
   247  	{'d', numFlag, argInt},
   248  	{'e', numFlag, argFloat},
   249  	{'E', numFlag, argFloat},
   250  	{'f', numFlag, argFloat},
   251  	{'F', numFlag, argFloat},
   252  	{'g', numFlag, argFloat},
   253  	{'G', numFlag, argFloat},
   254  	{'o', sharpNumFlag, argInt},
   255  	{'p', "-#", argPointer},
   256  	{'q', " -+.0#", argRune | argInt | argString},
   257  	{'s', " -+.0", argString},
   258  	{'t', "-", argBool},
   259  	{'T', "-", anyType},
   260  	{'U', "-#", argRune | argInt},
   261  	{'v', allFlags, anyType},
   262  	{'x', sharpNumFlag, argRune | argInt | argString},
   263  	{'X', sharpNumFlag, argRune | argInt | argString},
   264  }
   265  
   266  const printfVerbs = "bcdeEfFgGopqstTvxUX"
   267  
   268  func (f *File) checkPrintfArg(call *ast.CallExpr, verb rune, flags []byte, argNum, nargs int) {
   269  	// Linear scan is fast enough for a small list.
   270  	for _, v := range printVerbs {
   271  		if v.verb == verb {
   272  			for _, flag := range flags {
   273  				if !strings.ContainsRune(v.flags, rune(flag)) {
   274  					f.Badf(call.Pos(), "unrecognized printf flag for verb %q: %q", verb, flag)
   275  					return
   276  				}
   277  			}
   278  			// Verb is good. If nargs>1, we have something like %.*s and all but the final
   279  			// arg must be integer.
   280  			for i := 0; i < nargs-1; i++ {
   281  				if !f.matchArgType(argInt, call.Args[argNum+i]) {
   282  					f.Badf(call.Pos(), "arg %s for * in printf format not of type int", f.gofmt(call.Args[argNum+i]))
   283  				}
   284  			}
   285  			for _, v := range printVerbs {
   286  				if v.verb == verb {
   287  					arg := call.Args[argNum+nargs-1]
   288  					if !f.matchArgType(v.typ, arg) {
   289  						typeString := ""
   290  						if typ := f.pkg.types[arg]; typ != nil {
   291  							typeString = typ.String()
   292  						}
   293  						f.Badf(call.Pos(), "arg %s for printf verb %%%c of wrong type: %s", f.gofmt(arg), verb, typeString)
   294  					}
   295  					break
   296  				}
   297  			}
   298  			return
   299  		}
   300  	}
   301  	f.Badf(call.Pos(), "unrecognized printf verb %q", verb)
   302  }
   303  
   304  // checkPrint checks a call to an unformatted print routine such as Println.
   305  // call.Args[firstArg] is the first argument to be printed.
   306  func (f *File) checkPrint(call *ast.CallExpr, name string, firstArg int) {
   307  	isLn := strings.HasSuffix(name, "ln")
   308  	isF := strings.HasPrefix(name, "F")
   309  	args := call.Args
   310  	// check for Println(os.Stderr, ...)
   311  	if firstArg == 0 && !isF && len(args) > 0 {
   312  		if sel, ok := args[0].(*ast.SelectorExpr); ok {
   313  			if x, ok := sel.X.(*ast.Ident); ok {
   314  				if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") {
   315  					f.Badf(call.Pos(), "first argument to %s is %s.%s", name, x.Name, sel.Sel.Name)
   316  				}
   317  			}
   318  		}
   319  	}
   320  	if len(args) <= firstArg {
   321  		// If we have a call to a method called Error that satisfies the Error interface,
   322  		// then it's ok. Otherwise it's something like (*T).Error from the testing package
   323  		// and we need to check it.
   324  		if name == "Error" && f.isErrorMethodCall(call) {
   325  			return
   326  		}
   327  		// If it's an Error call now, it's probably for printing errors.
   328  		if !isLn {
   329  			// Check the signature to be sure: there are niladic functions called "error".
   330  			if firstArg != 0 || f.numArgsInSignature(call) != firstArg {
   331  				f.Badf(call.Pos(), "no args in %s call", name)
   332  			}
   333  		}
   334  		return
   335  	}
   336  	arg := args[firstArg]
   337  	if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
   338  		if strings.Contains(lit.Value, "%") {
   339  			f.Badf(call.Pos(), "possible formatting directive in %s call", name)
   340  		}
   341  	}
   342  	if isLn {
   343  		// The last item, if a string, should not have a newline.
   344  		arg = args[len(call.Args)-1]
   345  		if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
   346  			if strings.HasSuffix(lit.Value, `\n"`) {
   347  				f.Badf(call.Pos(), "%s call ends with newline", name)
   348  			}
   349  		}
   350  	}
   351  }