github.com/mattn/go@v0.0.0-20171011075504-07f7db3ea99f/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 // isFormatter reports whether t satisfies fmt.Formatter. 196 // Unlike fmt.Stringer, it's impossible to satisfy fmt.Formatter without importing fmt. 197 func (f *File) isFormatter(t types.Type) bool { 198 return formatterType != nil && types.Implements(t, formatterType) 199 } 200 201 // formatState holds the parsed representation of a printf directive such as "%3.*[4]d". 202 // It is constructed by parsePrintfVerb. 203 type formatState struct { 204 verb rune // the format verb: 'd' for "%d" 205 format string // the full format directive from % through verb, "%.3d". 206 name string // Printf, Sprintf etc. 207 flags []byte // the list of # + etc. 208 argNums []int // the successive argument numbers that are consumed, adjusted to refer to actual arg in call 209 firstArg int // Index of first argument after the format in the Printf call. 210 // Used only during parse. 211 file *File 212 call *ast.CallExpr 213 argNum int // Which argument we're expecting to format now. 214 indexPending bool // Whether we have an indexed argument that has not resolved. 215 nbytes int // number of bytes of the format string consumed. 216 } 217 218 // checkPrintf checks a call to a formatted print routine such as Printf. 219 func (f *File) checkPrintf(call *ast.CallExpr, name string) { 220 format, idx := formatString(f, call) 221 if idx < 0 { 222 if *verbose { 223 f.Warn(call.Pos(), "can't check non-constant format in call to", name) 224 } 225 return 226 } 227 228 firstArg := idx + 1 // Arguments are immediately after format string. 229 if !strings.Contains(format, "%") { 230 if len(call.Args) > firstArg { 231 f.Badf(call.Pos(), "no formatting directive in %s call", name) 232 } 233 return 234 } 235 // Hard part: check formats against args. 236 argNum := firstArg 237 maxArgNum := firstArg 238 for i, w := 0, 0; i < len(format); i += w { 239 w = 1 240 if format[i] != '%' { 241 continue 242 } 243 state := f.parsePrintfVerb(call, name, format[i:], firstArg, argNum) 244 if state == nil { 245 return 246 } 247 w = len(state.format) 248 if !f.okPrintfArg(call, state) { // One error per format is enough. 249 return 250 } 251 if len(state.argNums) > 0 { 252 // Continue with the next sequential argument. 253 argNum = state.argNums[len(state.argNums)-1] + 1 254 } 255 for _, n := range state.argNums { 256 if n >= maxArgNum { 257 maxArgNum = n + 1 258 } 259 } 260 } 261 // Dotdotdot is hard. 262 if call.Ellipsis.IsValid() && maxArgNum >= len(call.Args)-1 { 263 return 264 } 265 // There should be no leftover arguments. 266 if maxArgNum != len(call.Args) { 267 expect := maxArgNum - firstArg 268 numArgs := len(call.Args) - firstArg 269 f.Badf(call.Pos(), "wrong number of args for format in %s call: %d needed but %d args", name, expect, numArgs) 270 } 271 } 272 273 // parseFlags accepts any printf flags. 274 func (s *formatState) parseFlags() { 275 for s.nbytes < len(s.format) { 276 switch c := s.format[s.nbytes]; c { 277 case '#', '0', '+', '-', ' ': 278 s.flags = append(s.flags, c) 279 s.nbytes++ 280 default: 281 return 282 } 283 } 284 } 285 286 // scanNum advances through a decimal number if present. 287 func (s *formatState) scanNum() { 288 for ; s.nbytes < len(s.format); s.nbytes++ { 289 c := s.format[s.nbytes] 290 if c < '0' || '9' < c { 291 return 292 } 293 } 294 } 295 296 // parseIndex scans an index expression. It returns false if there is a syntax error. 297 func (s *formatState) parseIndex() bool { 298 if s.nbytes == len(s.format) || s.format[s.nbytes] != '[' { 299 return true 300 } 301 // Argument index present. 302 s.nbytes++ // skip '[' 303 start := s.nbytes 304 s.scanNum() 305 if s.nbytes == len(s.format) || s.nbytes == start || s.format[s.nbytes] != ']' { 306 end := strings.Index(s.format, "]") 307 if end < 0 { 308 end = len(s.format) 309 } 310 s.file.Badf(s.call.Pos(), "bad syntax for printf argument index: [%s]", s.format[start:end]) 311 return false 312 } 313 arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32) 314 if err != nil { 315 s.file.Badf(s.call.Pos(), "bad syntax for printf argument index: %s", err) 316 return false 317 } 318 s.nbytes++ // skip ']' 319 arg := int(arg32) 320 arg += s.firstArg - 1 // We want to zero-index the actual arguments. 321 s.argNum = arg 322 s.indexPending = true 323 return true 324 } 325 326 // parseNum scans a width or precision (or *). It returns false if there's a bad index expression. 327 func (s *formatState) parseNum() bool { 328 if s.nbytes < len(s.format) && s.format[s.nbytes] == '*' { 329 if s.indexPending { // Absorb it. 330 s.indexPending = false 331 } 332 s.nbytes++ 333 s.argNums = append(s.argNums, s.argNum) 334 s.argNum++ 335 } else { 336 s.scanNum() 337 } 338 return true 339 } 340 341 // parsePrecision scans for a precision. It returns false if there's a bad index expression. 342 func (s *formatState) parsePrecision() bool { 343 // If there's a period, there may be a precision. 344 if s.nbytes < len(s.format) && s.format[s.nbytes] == '.' { 345 s.flags = append(s.flags, '.') // Treat precision as a flag. 346 s.nbytes++ 347 if !s.parseIndex() { 348 return false 349 } 350 if !s.parseNum() { 351 return false 352 } 353 } 354 return true 355 } 356 357 // parsePrintfVerb looks the formatting directive that begins the format string 358 // and returns a formatState that encodes what the directive wants, without looking 359 // at the actual arguments present in the call. The result is nil if there is an error. 360 func (f *File) parsePrintfVerb(call *ast.CallExpr, name, format string, firstArg, argNum int) *formatState { 361 state := &formatState{ 362 format: format, 363 name: name, 364 flags: make([]byte, 0, 5), 365 argNum: argNum, 366 argNums: make([]int, 0, 1), 367 nbytes: 1, // There's guaranteed to be a percent sign. 368 firstArg: firstArg, 369 file: f, 370 call: call, 371 } 372 // There may be flags. 373 state.parseFlags() 374 // There may be an index. 375 if !state.parseIndex() { 376 return nil 377 } 378 // There may be a width. 379 if !state.parseNum() { 380 return nil 381 } 382 // There may be a precision. 383 if !state.parsePrecision() { 384 return nil 385 } 386 // Now a verb, possibly prefixed by an index (which we may already have). 387 if !state.indexPending && !state.parseIndex() { 388 return nil 389 } 390 if state.nbytes == len(state.format) { 391 f.Badf(call.Pos(), "missing verb at end of format string in %s call", name) 392 return nil 393 } 394 verb, w := utf8.DecodeRuneInString(state.format[state.nbytes:]) 395 state.verb = verb 396 state.nbytes += w 397 if verb != '%' { 398 state.argNums = append(state.argNums, state.argNum) 399 } 400 state.format = state.format[:state.nbytes] 401 return state 402 } 403 404 // printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask. 405 type printfArgType int 406 407 const ( 408 argBool printfArgType = 1 << iota 409 argInt 410 argRune 411 argString 412 argFloat 413 argComplex 414 argPointer 415 anyType printfArgType = ^0 416 ) 417 418 type printVerb struct { 419 verb rune // User may provide verb through Formatter; could be a rune. 420 flags string // known flags are all ASCII 421 typ printfArgType 422 } 423 424 // Common flag sets for printf verbs. 425 const ( 426 noFlag = "" 427 numFlag = " -+.0" 428 sharpNumFlag = " -+.0#" 429 allFlags = " -+.0#" 430 ) 431 432 // printVerbs identifies which flags are known to printf for each verb. 433 var printVerbs = []printVerb{ 434 // '-' is a width modifier, always valid. 435 // '.' is a precision for float, max width for strings. 436 // '+' is required sign for numbers, Go format for %v. 437 // '#' is alternate format for several verbs. 438 // ' ' is spacer for numbers 439 {'%', noFlag, 0}, 440 {'b', numFlag, argInt | argFloat | argComplex}, 441 {'c', "-", argRune | argInt}, 442 {'d', numFlag, argInt}, 443 {'e', sharpNumFlag, argFloat | argComplex}, 444 {'E', sharpNumFlag, argFloat | argComplex}, 445 {'f', sharpNumFlag, argFloat | argComplex}, 446 {'F', sharpNumFlag, argFloat | argComplex}, 447 {'g', sharpNumFlag, argFloat | argComplex}, 448 {'G', sharpNumFlag, argFloat | argComplex}, 449 {'o', sharpNumFlag, argInt}, 450 {'p', "-#", argPointer}, 451 {'q', " -+.0#", argRune | argInt | argString}, 452 {'s', " -+.0", argString}, 453 {'t', "-", argBool}, 454 {'T', "-", anyType}, 455 {'U', "-#", argRune | argInt}, 456 {'v', allFlags, anyType}, 457 {'x', sharpNumFlag, argRune | argInt | argString}, 458 {'X', sharpNumFlag, argRune | argInt | argString}, 459 } 460 461 // okPrintfArg compares the formatState to the arguments actually present, 462 // reporting any discrepancies it can discern. If the final argument is ellipsissed, 463 // there's little it can do for that. 464 func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) { 465 var v printVerb 466 found := false 467 // Linear scan is fast enough for a small list. 468 for _, v = range printVerbs { 469 if v.verb == state.verb { 470 found = true 471 break 472 } 473 } 474 475 // Does current arg implement fmt.Formatter? 476 formatter := false 477 if state.argNum < len(call.Args) { 478 if tv, ok := f.pkg.types[call.Args[state.argNum]]; ok { 479 formatter = f.isFormatter(tv.Type) 480 } 481 } 482 483 if !found && !formatter { 484 f.Badf(call.Pos(), "unrecognized printf verb %q", state.verb) 485 return false 486 } 487 for _, flag := range state.flags { 488 if !strings.ContainsRune(v.flags, rune(flag)) { 489 f.Badf(call.Pos(), "unrecognized printf flag for verb %q: %q", state.verb, flag) 490 return false 491 } 492 } 493 // Verb is good. If len(state.argNums)>trueArgs, we have something like %.*s and all 494 // but the final arg must be an integer. 495 trueArgs := 1 496 if state.verb == '%' { 497 trueArgs = 0 498 } 499 nargs := len(state.argNums) 500 for i := 0; i < nargs-trueArgs; i++ { 501 argNum := state.argNums[i] 502 if !f.argCanBeChecked(call, i, state) { 503 return 504 } 505 arg := call.Args[argNum] 506 if !f.matchArgType(argInt, nil, arg) { 507 f.Badf(call.Pos(), "arg %s for * in printf format not of type int", f.gofmt(arg)) 508 return false 509 } 510 } 511 if state.verb == '%' || formatter { 512 return true 513 } 514 argNum := state.argNums[len(state.argNums)-1] 515 if !f.argCanBeChecked(call, len(state.argNums)-1, state) { 516 return false 517 } 518 arg := call.Args[argNum] 519 if f.isFunctionValue(arg) && state.verb != 'p' && state.verb != 'T' { 520 f.Badf(call.Pos(), "arg %s in printf call is a function value, not a function call", f.gofmt(arg)) 521 return false 522 } 523 if !f.matchArgType(v.typ, nil, arg) { 524 typeString := "" 525 if typ := f.pkg.types[arg].Type; typ != nil { 526 typeString = typ.String() 527 } 528 f.Badf(call.Pos(), "arg %s for printf verb %%%c of wrong type: %s", f.gofmt(arg), state.verb, typeString) 529 return false 530 } 531 if v.typ&argString != 0 && v.verb != 'T' && !bytes.Contains(state.flags, []byte{'#'}) && f.recursiveStringer(arg) { 532 f.Badf(call.Pos(), "arg %s for printf causes recursive call to String method", f.gofmt(arg)) 533 return false 534 } 535 return true 536 } 537 538 // recursiveStringer reports whether the provided argument is r or &r for the 539 // fmt.Stringer receiver identifier r. 540 func (f *File) recursiveStringer(e ast.Expr) bool { 541 if len(f.stringers) == 0 { 542 return false 543 } 544 var obj *ast.Object 545 switch e := e.(type) { 546 case *ast.Ident: 547 obj = e.Obj 548 case *ast.UnaryExpr: 549 if id, ok := e.X.(*ast.Ident); ok && e.Op == token.AND { 550 obj = id.Obj 551 } 552 } 553 554 // It's unlikely to be a recursive stringer if it has a Format method. 555 if typ := f.pkg.types[e].Type; typ != nil { 556 // Not a perfect match; see issue 6259. 557 if f.hasMethod(typ, "Format") { 558 return false 559 } 560 } 561 562 // We compare the underlying Object, which checks that the identifier 563 // is the one we declared as the receiver for the String method in 564 // which this printf appears. 565 return f.stringers[obj] 566 } 567 568 // isFunctionValue reports whether the expression is a function as opposed to a function call. 569 // It is almost always a mistake to print a function value. 570 func (f *File) isFunctionValue(e ast.Expr) bool { 571 if typ := f.pkg.types[e].Type; typ != nil { 572 _, ok := typ.(*types.Signature) 573 return ok 574 } 575 return false 576 } 577 578 // argCanBeChecked reports whether the specified argument is statically present; 579 // it may be beyond the list of arguments or in a terminal slice... argument, which 580 // means we can't see it. 581 func (f *File) argCanBeChecked(call *ast.CallExpr, formatArg int, state *formatState) bool { 582 argNum := state.argNums[formatArg] 583 if argNum < 0 { 584 // Shouldn't happen, so catch it with prejudice. 585 panic("negative arg num") 586 } 587 if argNum == 0 { 588 f.Badf(call.Pos(), `index value [0] for %s("%s"); indexes start at 1`, state.name, state.format) 589 return false 590 } 591 if argNum < len(call.Args)-1 { 592 return true // Always OK. 593 } 594 if call.Ellipsis.IsValid() { 595 return false // We just can't tell; there could be many more arguments. 596 } 597 if argNum < len(call.Args) { 598 return true 599 } 600 // There are bad indexes in the format or there are fewer arguments than the format needs. 601 // This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi". 602 arg := argNum - state.firstArg + 1 // People think of arguments as 1-indexed. 603 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) 604 return false 605 } 606 607 // checkPrint checks a call to an unformatted print routine such as Println. 608 func (f *File) checkPrint(call *ast.CallExpr, name string) { 609 firstArg := 0 610 typ := f.pkg.types[call.Fun].Type 611 if typ == nil { 612 // Skip checking functions with unknown type. 613 return 614 } 615 if sig, ok := typ.(*types.Signature); ok { 616 if !sig.Variadic() { 617 // Skip checking non-variadic functions. 618 return 619 } 620 params := sig.Params() 621 firstArg = params.Len() - 1 622 623 typ := params.At(firstArg).Type() 624 typ = typ.(*types.Slice).Elem() 625 it, ok := typ.(*types.Interface) 626 if !ok || !it.Empty() { 627 // Skip variadic functions accepting non-interface{} args. 628 return 629 } 630 } 631 args := call.Args 632 if len(args) <= firstArg { 633 // Skip calls without variadic args. 634 return 635 } 636 args = args[firstArg:] 637 638 // check for Println(os.Stderr, ...) 639 if firstArg == 0 { 640 if sel, ok := args[0].(*ast.SelectorExpr); ok { 641 if x, ok := sel.X.(*ast.Ident); ok { 642 if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") { 643 f.Badf(call.Pos(), "first argument to %s is %s.%s", name, x.Name, sel.Sel.Name) 644 } 645 } 646 } 647 } 648 arg := args[0] 649 if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { 650 // Ignore trailing % character in lit.Value. 651 // The % in "abc 0.0%" couldn't be a formatting directive. 652 s := strings.TrimSuffix(lit.Value, `%"`) 653 if strings.Contains(s, "%") { 654 f.Badf(call.Pos(), "possible formatting directive in %s call", name) 655 } 656 } 657 if strings.HasSuffix(name, "ln") { 658 // The last item, if a string, should not have a newline. 659 arg = args[len(args)-1] 660 if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { 661 if strings.HasSuffix(lit.Value, `\n"`) { 662 f.Badf(call.Pos(), "%s call ends with newline", name) 663 } 664 } 665 } 666 for _, arg := range args { 667 if f.isFunctionValue(arg) { 668 f.Badf(call.Pos(), "arg %s in %s call is a function value, not a function call", f.gofmt(arg), name) 669 } 670 if f.recursiveStringer(arg) { 671 f.Badf(call.Pos(), "arg %s in %s call causes recursive call to String method", f.gofmt(arg), name) 672 } 673 } 674 }