github.com/panjjo/go@v0.0.0-20161104043856-d62b31386338/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 "bytes" 11 "flag" 12 "go/ast" 13 "go/constant" 14 "go/token" 15 "go/types" 16 "strconv" 17 "strings" 18 "unicode/utf8" 19 ) 20 21 var printfuncs = flag.String("printfuncs", "", "comma-separated list of print function names to check") 22 23 func init() { 24 register("printf", 25 "check printf-like invocations", 26 checkFmtPrintfCall, 27 funcDecl, callExpr) 28 } 29 30 func initPrintFlags() { 31 if *printfuncs == "" { 32 return 33 } 34 for _, name := range strings.Split(*printfuncs, ",") { 35 if len(name) == 0 { 36 flag.Usage() 37 } 38 39 // Backwards compatibility: skip optional first argument 40 // index after the colon. 41 if colon := strings.LastIndex(name, ":"); colon > 0 { 42 name = name[:colon] 43 } 44 45 name = strings.ToLower(name) 46 if name[len(name)-1] == 'f' { 47 isFormattedPrint[name] = true 48 } else { 49 isPrint[name] = true 50 } 51 } 52 } 53 54 // isFormattedPrint records the formatted-print functions. Names are 55 // lower-cased so the lookup is case insensitive. 56 var isFormattedPrint = map[string]bool{ 57 "errorf": true, 58 "fatalf": true, 59 "fprintf": true, 60 "logf": true, 61 "panicf": true, 62 "printf": true, 63 "sprintf": true, 64 } 65 66 // isPrint records the unformatted-print functions. Names are lower-cased 67 // so the lookup is case insensitive. 68 var isPrint = map[string]bool{ 69 "error": true, 70 "fatal": true, 71 "fprint": true, 72 "fprintln": true, 73 "log": true, 74 "panic": true, 75 "panicln": true, 76 "print": true, 77 "println": true, 78 "sprint": true, 79 "sprintln": true, 80 } 81 82 // formatString returns the format string argument and its index within 83 // the given printf-like call expression. 84 // 85 // The last parameter before variadic arguments is assumed to be 86 // a format string. 87 // 88 // The first string literal or string constant is assumed to be a format string 89 // if the call's signature cannot be determined. 90 // 91 // If it cannot find any format string parameter, it returns ("", -1). 92 func formatString(f *File, call *ast.CallExpr) (string, int) { 93 typ := f.pkg.types[call.Fun].Type 94 if typ != nil { 95 if sig, ok := typ.(*types.Signature); ok { 96 if !sig.Variadic() { 97 // Skip checking non-variadic functions. 98 return "", -1 99 } 100 idx := sig.Params().Len() - 2 101 if idx < 0 { 102 // Skip checking variadic functions without 103 // fixed arguments. 104 return "", -1 105 } 106 s, ok := stringConstantArg(f, call, idx) 107 if !ok { 108 // The last argument before variadic args isn't a string. 109 return "", -1 110 } 111 return s, idx 112 } 113 } 114 115 // Cannot determine call's signature. Fall back to scanning for the first 116 // string constant in the call. 117 for idx := range call.Args { 118 if s, ok := stringConstantArg(f, call, idx); ok { 119 return s, idx 120 } 121 if f.pkg.types[call.Args[idx]].Type == types.Typ[types.String] { 122 // Skip checking a call with a non-constant format 123 // string argument, since its contents are unavailable 124 // for validation. 125 return "", -1 126 } 127 } 128 return "", -1 129 } 130 131 // stringConstantArg returns call's string constant argument at the index idx. 132 // 133 // ("", false) is returned if call's argument at the index idx isn't a string 134 // constant. 135 func stringConstantArg(f *File, call *ast.CallExpr, idx int) (string, bool) { 136 if idx >= len(call.Args) { 137 return "", false 138 } 139 arg := call.Args[idx] 140 lit := f.pkg.types[arg].Value 141 if lit != nil && lit.Kind() == constant.String { 142 return constant.StringVal(lit), true 143 } 144 return "", false 145 } 146 147 // checkCall triggers the print-specific checks if the call invokes a print function. 148 func checkFmtPrintfCall(f *File, node ast.Node) { 149 if d, ok := node.(*ast.FuncDecl); ok && isStringer(f, d) { 150 // Remember we saw this. 151 if f.stringers == nil { 152 f.stringers = make(map[*ast.Object]bool) 153 } 154 if l := d.Recv.List; len(l) == 1 { 155 if n := l[0].Names; len(n) == 1 { 156 f.stringers[n[0].Obj] = true 157 } 158 } 159 return 160 } 161 162 call, ok := node.(*ast.CallExpr) 163 if !ok { 164 return 165 } 166 var Name string 167 switch x := call.Fun.(type) { 168 case *ast.Ident: 169 Name = x.Name 170 case *ast.SelectorExpr: 171 Name = x.Sel.Name 172 default: 173 return 174 } 175 176 name := strings.ToLower(Name) 177 if _, ok := isFormattedPrint[name]; ok { 178 f.checkPrintf(call, Name) 179 return 180 } 181 if _, ok := isPrint[name]; ok { 182 f.checkPrint(call, Name) 183 return 184 } 185 } 186 187 // isStringer returns true if the provided declaration is a "String() string" 188 // method, an implementation of fmt.Stringer. 189 func isStringer(f *File, d *ast.FuncDecl) bool { 190 return d.Recv != nil && d.Name.Name == "String" && d.Type.Results != nil && 191 len(d.Type.Params.List) == 0 && len(d.Type.Results.List) == 1 && 192 f.pkg.types[d.Type.Results.List[0].Type].Type == types.Typ[types.String] 193 } 194 195 // formatState holds the parsed representation of a printf directive such as "%3.*[4]d". 196 // It is constructed by parsePrintfVerb. 197 type formatState struct { 198 verb rune // the format verb: 'd' for "%d" 199 format string // the full format directive from % through verb, "%.3d". 200 name string // Printf, Sprintf etc. 201 flags []byte // the list of # + etc. 202 argNums []int // the successive argument numbers that are consumed, adjusted to refer to actual arg in call 203 firstArg int // Index of first argument after the format in the Printf call. 204 // Used only during parse. 205 file *File 206 call *ast.CallExpr 207 argNum int // Which argument we're expecting to format now. 208 indexPending bool // Whether we have an indexed argument that has not resolved. 209 nbytes int // number of bytes of the format string consumed. 210 } 211 212 // checkPrintf checks a call to a formatted print routine such as Printf. 213 func (f *File) checkPrintf(call *ast.CallExpr, name string) { 214 format, idx := formatString(f, call) 215 if idx < 0 { 216 if *verbose { 217 f.Warn(call.Pos(), "can't check non-constant format in call to", name) 218 } 219 return 220 } 221 222 firstArg := idx + 1 // Arguments are immediately after format string. 223 if !strings.Contains(format, "%") { 224 if len(call.Args) > firstArg { 225 f.Badf(call.Pos(), "no formatting directive in %s call", name) 226 } 227 return 228 } 229 // Hard part: check formats against args. 230 argNum := firstArg 231 maxArgNum := firstArg 232 for i, w := 0, 0; i < len(format); i += w { 233 w = 1 234 if format[i] == '%' { 235 state := f.parsePrintfVerb(call, name, format[i:], firstArg, argNum) 236 if state == nil { 237 return 238 } 239 w = len(state.format) 240 if !f.okPrintfArg(call, state) { // One error per format is enough. 241 return 242 } 243 if len(state.argNums) > 0 { 244 // Continue with the next sequential argument. 245 argNum = state.argNums[len(state.argNums)-1] + 1 246 } 247 for _, n := range state.argNums { 248 if n >= maxArgNum { 249 maxArgNum = n + 1 250 } 251 } 252 } 253 } 254 // Dotdotdot is hard. 255 if call.Ellipsis.IsValid() && maxArgNum >= len(call.Args)-1 { 256 return 257 } 258 // There should be no leftover arguments. 259 if maxArgNum != len(call.Args) { 260 expect := maxArgNum - firstArg 261 numArgs := len(call.Args) - firstArg 262 f.Badf(call.Pos(), "wrong number of args for format in %s call: %d needed but %d args", name, expect, numArgs) 263 } 264 } 265 266 // parseFlags accepts any printf flags. 267 func (s *formatState) parseFlags() { 268 for s.nbytes < len(s.format) { 269 switch c := s.format[s.nbytes]; c { 270 case '#', '0', '+', '-', ' ': 271 s.flags = append(s.flags, c) 272 s.nbytes++ 273 default: 274 return 275 } 276 } 277 } 278 279 // scanNum advances through a decimal number if present. 280 func (s *formatState) scanNum() { 281 for ; s.nbytes < len(s.format); s.nbytes++ { 282 c := s.format[s.nbytes] 283 if c < '0' || '9' < c { 284 return 285 } 286 } 287 } 288 289 // parseIndex scans an index expression. It returns false if there is a syntax error. 290 func (s *formatState) parseIndex() bool { 291 if s.nbytes == len(s.format) || s.format[s.nbytes] != '[' { 292 return true 293 } 294 // Argument index present. 295 s.nbytes++ // skip '[' 296 start := s.nbytes 297 s.scanNum() 298 if s.nbytes == len(s.format) || s.nbytes == start || s.format[s.nbytes] != ']' { 299 end := strings.Index(s.format, "]") 300 if end < 0 { 301 end = len(s.format) 302 } 303 s.file.Badf(s.call.Pos(), "bad syntax for printf argument index: [%s]", s.format[start:end]) 304 return false 305 } 306 arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32) 307 if err != nil { 308 s.file.Badf(s.call.Pos(), "bad syntax for printf argument index: %s", err) 309 return false 310 } 311 s.nbytes++ // skip ']' 312 arg := int(arg32) 313 arg += s.firstArg - 1 // We want to zero-index the actual arguments. 314 s.argNum = arg 315 s.indexPending = true 316 return true 317 } 318 319 // parseNum scans a width or precision (or *). It returns false if there's a bad index expression. 320 func (s *formatState) parseNum() bool { 321 if s.nbytes < len(s.format) && s.format[s.nbytes] == '*' { 322 if s.indexPending { // Absorb it. 323 s.indexPending = false 324 } 325 s.nbytes++ 326 s.argNums = append(s.argNums, s.argNum) 327 s.argNum++ 328 } else { 329 s.scanNum() 330 } 331 return true 332 } 333 334 // parsePrecision scans for a precision. It returns false if there's a bad index expression. 335 func (s *formatState) parsePrecision() bool { 336 // If there's a period, there may be a precision. 337 if s.nbytes < len(s.format) && s.format[s.nbytes] == '.' { 338 s.flags = append(s.flags, '.') // Treat precision as a flag. 339 s.nbytes++ 340 if !s.parseIndex() { 341 return false 342 } 343 if !s.parseNum() { 344 return false 345 } 346 } 347 return true 348 } 349 350 // parsePrintfVerb looks the formatting directive that begins the format string 351 // and returns a formatState that encodes what the directive wants, without looking 352 // at the actual arguments present in the call. The result is nil if there is an error. 353 func (f *File) parsePrintfVerb(call *ast.CallExpr, name, format string, firstArg, argNum int) *formatState { 354 state := &formatState{ 355 format: format, 356 name: name, 357 flags: make([]byte, 0, 5), 358 argNum: argNum, 359 argNums: make([]int, 0, 1), 360 nbytes: 1, // There's guaranteed to be a percent sign. 361 firstArg: firstArg, 362 file: f, 363 call: call, 364 } 365 // There may be flags. 366 state.parseFlags() 367 // There may be an index. 368 if !state.parseIndex() { 369 return nil 370 } 371 // There may be a width. 372 if !state.parseNum() { 373 return nil 374 } 375 // There may be a precision. 376 if !state.parsePrecision() { 377 return nil 378 } 379 // Now a verb, possibly prefixed by an index (which we may already have). 380 if !state.indexPending && !state.parseIndex() { 381 return nil 382 } 383 if state.nbytes == len(state.format) { 384 f.Badf(call.Pos(), "missing verb at end of format string in %s call", name) 385 return nil 386 } 387 verb, w := utf8.DecodeRuneInString(state.format[state.nbytes:]) 388 state.verb = verb 389 state.nbytes += w 390 if verb != '%' { 391 state.argNums = append(state.argNums, state.argNum) 392 } 393 state.format = state.format[:state.nbytes] 394 return state 395 } 396 397 // printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask. 398 type printfArgType int 399 400 const ( 401 argBool printfArgType = 1 << iota 402 argInt 403 argRune 404 argString 405 argFloat 406 argComplex 407 argPointer 408 anyType printfArgType = ^0 409 ) 410 411 type printVerb struct { 412 verb rune // User may provide verb through Formatter; could be a rune. 413 flags string // known flags are all ASCII 414 typ printfArgType 415 } 416 417 // Common flag sets for printf verbs. 418 const ( 419 noFlag = "" 420 numFlag = " -+.0" 421 sharpNumFlag = " -+.0#" 422 allFlags = " -+.0#" 423 ) 424 425 // printVerbs identifies which flags are known to printf for each verb. 426 // TODO: A type that implements Formatter may do what it wants, and vet 427 // will complain incorrectly. 428 var printVerbs = []printVerb{ 429 // '-' is a width modifier, always valid. 430 // '.' is a precision for float, max width for strings. 431 // '+' is required sign for numbers, Go format for %v. 432 // '#' is alternate format for several verbs. 433 // ' ' is spacer for numbers 434 {'%', noFlag, 0}, 435 {'b', numFlag, argInt | argFloat | argComplex}, 436 {'c', "-", argRune | argInt}, 437 {'d', numFlag, argInt}, 438 {'e', numFlag, argFloat | argComplex}, 439 {'E', numFlag, argFloat | argComplex}, 440 {'f', numFlag, argFloat | argComplex}, 441 {'F', numFlag, argFloat | argComplex}, 442 {'g', numFlag, argFloat | argComplex}, 443 {'G', numFlag, argFloat | argComplex}, 444 {'o', sharpNumFlag, argInt}, 445 {'p', "-#", argPointer}, 446 {'q', " -+.0#", argRune | argInt | argString}, 447 {'s', " -+.0", argString}, 448 {'t', "-", argBool}, 449 {'T', "-", anyType}, 450 {'U', "-#", argRune | argInt}, 451 {'v', allFlags, anyType}, 452 {'x', sharpNumFlag, argRune | argInt | argString}, 453 {'X', sharpNumFlag, argRune | argInt | argString}, 454 } 455 456 // okPrintfArg compares the formatState to the arguments actually present, 457 // reporting any discrepancies it can discern. If the final argument is ellipsissed, 458 // there's little it can do for that. 459 func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) { 460 var v printVerb 461 found := false 462 // Linear scan is fast enough for a small list. 463 for _, v = range printVerbs { 464 if v.verb == state.verb { 465 found = true 466 break 467 } 468 } 469 if !found { 470 f.Badf(call.Pos(), "unrecognized printf verb %q", state.verb) 471 return false 472 } 473 for _, flag := range state.flags { 474 if !strings.ContainsRune(v.flags, rune(flag)) { 475 f.Badf(call.Pos(), "unrecognized printf flag for verb %q: %q", state.verb, flag) 476 return false 477 } 478 } 479 // Verb is good. If len(state.argNums)>trueArgs, we have something like %.*s and all 480 // but the final arg must be an integer. 481 trueArgs := 1 482 if state.verb == '%' { 483 trueArgs = 0 484 } 485 nargs := len(state.argNums) 486 for i := 0; i < nargs-trueArgs; i++ { 487 argNum := state.argNums[i] 488 if !f.argCanBeChecked(call, i, true, state) { 489 return 490 } 491 arg := call.Args[argNum] 492 if !f.matchArgType(argInt, nil, arg) { 493 f.Badf(call.Pos(), "arg %s for * in printf format not of type int", f.gofmt(arg)) 494 return false 495 } 496 } 497 if state.verb == '%' { 498 return true 499 } 500 argNum := state.argNums[len(state.argNums)-1] 501 if !f.argCanBeChecked(call, len(state.argNums)-1, false, state) { 502 return false 503 } 504 arg := call.Args[argNum] 505 if f.isFunctionValue(arg) && state.verb != 'p' && state.verb != 'T' { 506 f.Badf(call.Pos(), "arg %s in printf call is a function value, not a function call", f.gofmt(arg)) 507 return false 508 } 509 if !f.matchArgType(v.typ, nil, arg) { 510 typeString := "" 511 if typ := f.pkg.types[arg].Type; typ != nil { 512 typeString = typ.String() 513 } 514 f.Badf(call.Pos(), "arg %s for printf verb %%%c of wrong type: %s", f.gofmt(arg), state.verb, typeString) 515 return false 516 } 517 if v.typ&argString != 0 && v.verb != 'T' && !bytes.Contains(state.flags, []byte{'#'}) && f.recursiveStringer(arg) { 518 f.Badf(call.Pos(), "arg %s for printf causes recursive call to String method", f.gofmt(arg)) 519 return false 520 } 521 return true 522 } 523 524 // recursiveStringer reports whether the provided argument is r or &r for the 525 // fmt.Stringer receiver identifier r. 526 func (f *File) recursiveStringer(e ast.Expr) bool { 527 if len(f.stringers) == 0 { 528 return false 529 } 530 var obj *ast.Object 531 switch e := e.(type) { 532 case *ast.Ident: 533 obj = e.Obj 534 case *ast.UnaryExpr: 535 if id, ok := e.X.(*ast.Ident); ok && e.Op == token.AND { 536 obj = id.Obj 537 } 538 } 539 540 // It's unlikely to be a recursive stringer if it has a Format method. 541 if typ := f.pkg.types[e].Type; typ != nil { 542 // Not a perfect match; see issue 6259. 543 if f.hasMethod(typ, "Format") { 544 return false 545 } 546 } 547 548 // We compare the underlying Object, which checks that the identifier 549 // is the one we declared as the receiver for the String method in 550 // which this printf appears. 551 return f.stringers[obj] 552 } 553 554 // isFunctionValue reports whether the expression is a function as opposed to a function call. 555 // It is almost always a mistake to print a function value. 556 func (f *File) isFunctionValue(e ast.Expr) bool { 557 if typ := f.pkg.types[e].Type; typ != nil { 558 _, ok := typ.(*types.Signature) 559 return ok 560 } 561 return false 562 } 563 564 // argCanBeChecked reports whether the specified argument is statically present; 565 // it may be beyond the list of arguments or in a terminal slice... argument, which 566 // means we can't see it. 567 func (f *File) argCanBeChecked(call *ast.CallExpr, formatArg int, isStar bool, state *formatState) bool { 568 argNum := state.argNums[formatArg] 569 if argNum < 0 { 570 // Shouldn't happen, so catch it with prejudice. 571 panic("negative arg num") 572 } 573 if argNum == 0 { 574 f.Badf(call.Pos(), `index value [0] for %s("%s"); indexes start at 1`, state.name, state.format) 575 return false 576 } 577 if argNum < len(call.Args)-1 { 578 return true // Always OK. 579 } 580 if call.Ellipsis.IsValid() { 581 return false // We just can't tell; there could be many more arguments. 582 } 583 if argNum < len(call.Args) { 584 return true 585 } 586 // There are bad indexes in the format or there are fewer arguments than the format needs. 587 // This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi". 588 arg := argNum - state.firstArg + 1 // People think of arguments as 1-indexed. 589 f.Badf(call.Pos(), `missing argument for %s("%s"): format reads arg %d, have only %d args`, state.name, state.format, arg, len(call.Args)-state.firstArg) 590 return false 591 } 592 593 // checkPrint checks a call to an unformatted print routine such as Println. 594 func (f *File) checkPrint(call *ast.CallExpr, name string) { 595 firstArg := 0 596 typ := f.pkg.types[call.Fun].Type 597 if typ == nil { 598 // Skip checking functions with unknown type. 599 return 600 } 601 if sig, ok := typ.(*types.Signature); ok { 602 if !sig.Variadic() { 603 // Skip checking non-variadic functions. 604 return 605 } 606 params := sig.Params() 607 firstArg = params.Len() - 1 608 609 typ := params.At(firstArg).Type() 610 typ = typ.(*types.Slice).Elem() 611 it, ok := typ.(*types.Interface) 612 if !ok || !it.Empty() { 613 // Skip variadic functions accepting non-interface{} args. 614 return 615 } 616 } 617 args := call.Args 618 if len(args) <= firstArg { 619 // Skip calls without variadic args. 620 return 621 } 622 args = args[firstArg:] 623 624 // check for Println(os.Stderr, ...) 625 if firstArg == 0 { 626 if sel, ok := args[0].(*ast.SelectorExpr); ok { 627 if x, ok := sel.X.(*ast.Ident); ok { 628 if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") { 629 f.Badf(call.Pos(), "first argument to %s is %s.%s", name, x.Name, sel.Sel.Name) 630 } 631 } 632 } 633 } 634 arg := args[0] 635 if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { 636 // Ignore trailing % character in lit.Value. 637 // The % in "abc 0.0%" couldn't be a formatting directive. 638 s := strings.TrimSuffix(lit.Value, `%"`) 639 if strings.Contains(s, "%") { 640 f.Badf(call.Pos(), "possible formatting directive in %s call", name) 641 } 642 } 643 if strings.HasSuffix(name, "ln") { 644 // The last item, if a string, should not have a newline. 645 arg = args[len(args)-1] 646 if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { 647 if strings.HasSuffix(lit.Value, `\n"`) { 648 f.Badf(call.Pos(), "%s call ends with newline", name) 649 } 650 } 651 } 652 for _, arg := range args { 653 if f.isFunctionValue(arg) { 654 f.Badf(call.Pos(), "arg %s in %s call is a function value, not a function call", f.gofmt(arg), name) 655 } 656 if f.recursiveStringer(arg) { 657 f.Badf(call.Pos(), "arg %s in %s call causes recursive call to String method", f.gofmt(arg), name) 658 } 659 } 660 }