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