blob: 9998ddae074aa7ff3663e273b397c86ed49e9127 [file] [log] [blame]
Robert Griesemer1b8b2c12015-06-04 12:54:58 -07001// 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
7package main
8
9import (
10 "bytes"
11 "flag"
12 "go/ast"
Robert Griesemera7d2d482015-06-04 13:07:22 -070013 "go/constant"
Robert Griesemer1b8b2c12015-06-04 12:54:58 -070014 "go/token"
Robert Griesemera7d2d482015-06-04 13:07:22 -070015 "go/types"
Robert Griesemer1b8b2c12015-06-04 12:54:58 -070016 "strconv"
17 "strings"
18 "unicode/utf8"
Robert Griesemer1b8b2c12015-06-04 12:54:58 -070019)
20
21var printfuncs = flag.String("printfuncs", "", "comma-separated list of print function names to check")
22
23func init() {
24 register("printf",
25 "check printf-like invocations",
26 checkFmtPrintfCall,
27 funcDecl, callExpr)
28}
29
30func initPrintFlags() {
31 if *printfuncs == "" {
32 return
33 }
34 for _, name := range strings.Split(*printfuncs, ",") {
35 if len(name) == 0 {
36 flag.Usage()
37 }
Aliaksandr Valialkind7ddee72016-04-05 18:42:07 +030038
39 // Backwards compatibility: skip optional first argument
40 // index after the colon.
Robert Griesemer1b8b2c12015-06-04 12:54:58 -070041 if colon := strings.LastIndex(name, ":"); colon > 0 {
Robert Griesemer1b8b2c12015-06-04 12:54:58 -070042 name = name[:colon]
43 }
Aliaksandr Valialkind7ddee72016-04-05 18:42:07 +030044
Robert Griesemer1b8b2c12015-06-04 12:54:58 -070045 name = strings.ToLower(name)
46 if name[len(name)-1] == 'f' {
Spencer Nelson867910e2016-03-02 15:29:30 -050047 isFormattedPrint[name] = true
Robert Griesemer1b8b2c12015-06-04 12:54:58 -070048 } else {
Aliaksandr Valialkind7ddee72016-04-05 18:42:07 +030049 isPrint[name] = true
Robert Griesemer1b8b2c12015-06-04 12:54:58 -070050 }
51 }
52}
53
Spencer Nelson867910e2016-03-02 15:29:30 -050054// isFormattedPrint records the formatted-print functions. Names are
55// lower-cased so the lookup is case insensitive.
56var 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,
Robert Griesemer1b8b2c12015-06-04 12:54:58 -070064}
65
Aliaksandr Valialkind7ddee72016-04-05 18:42:07 +030066// isPrint records the unformatted-print functions. Names are lower-cased
67// so the lookup is case insensitive.
68var 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,
Robert Griesemer1b8b2c12015-06-04 12:54:58 -070080}
81
Aliaksandr Valialkinee1b90a2016-03-22 15:38:21 +020082// 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).
92func formatString(f *File, call *ast.CallExpr) (string, int) {
Spencer Nelson867910e2016-03-02 15:29:30 -050093 typ := f.pkg.types[call.Fun].Type
Aliaksandr Valialkinee1b90a2016-03-22 15:38:21 +020094 if typ != nil {
95 if sig, ok := typ.(*types.Signature); ok {
96 if !sig.Variadic() {
Aliaksandr Valialkinee8ec422016-09-10 17:04:41 +030097 // Skip checking non-variadic functions.
Aliaksandr Valialkinee1b90a2016-03-22 15:38:21 +020098 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 }
Aliaksandr Valialkinee8ec422016-09-10 17:04:41 +0300106 s, ok := stringConstantArg(f, call, idx)
Aliaksandr Valialkinee1b90a2016-03-22 15:38:21 +0200107 if !ok {
Aliaksandr Valialkinee8ec422016-09-10 17:04:41 +0300108 // The last argument before variadic args isn't a string.
Aliaksandr Valialkinee1b90a2016-03-22 15:38:21 +0200109 return "", -1
110 }
111 return s, idx
Spencer Nelson867910e2016-03-02 15:29:30 -0500112 }
113 }
Aliaksandr Valialkinee1b90a2016-03-22 15:38:21 +0200114
Aliaksandr Valialkinee8ec422016-09-10 17:04:41 +0300115 // Cannot determine call's signature. Fall back to scanning for the first
116 // string constant in the call.
Aliaksandr Valialkinee1b90a2016-03-22 15:38:21 +0200117 for idx := range call.Args {
Aliaksandr Valialkinee8ec422016-09-10 17:04:41 +0300118 if s, ok := stringConstantArg(f, call, idx); ok {
Aliaksandr Valialkinee1b90a2016-03-22 15:38:21 +0200119 return s, idx
120 }
Aliaksandr Valialkinee8ec422016-09-10 17:04:41 +0300121 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 }
Aliaksandr Valialkinee1b90a2016-03-22 15:38:21 +0200127 }
128 return "", -1
129}
130
Aliaksandr Valialkinee8ec422016-09-10 17:04:41 +0300131// stringConstantArg returns call's string constant argument at the index idx.
Aliaksandr Valialkinee1b90a2016-03-22 15:38:21 +0200132//
133// ("", false) is returned if call's argument at the index idx isn't a string
Aliaksandr Valialkinee8ec422016-09-10 17:04:41 +0300134// constant.
135func stringConstantArg(f *File, call *ast.CallExpr, idx int) (string, bool) {
Aliaksandr Valialkinee1b90a2016-03-22 15:38:21 +0200136 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
Spencer Nelson867910e2016-03-02 15:29:30 -0500145}
146
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700147// checkCall triggers the print-specific checks if the call invokes a print function.
148func 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)
Spencer Nelson867910e2016-03-02 15:29:30 -0500177 if _, ok := isFormattedPrint[name]; ok {
178 f.checkPrintf(call, Name)
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700179 return
180 }
Aliaksandr Valialkind7ddee72016-04-05 18:42:07 +0300181 if _, ok := isPrint[name]; ok {
182 f.checkPrint(call, Name)
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700183 return
184 }
185}
186
187// isStringer returns true if the provided declaration is a "String() string"
188// method, an implementation of fmt.Stringer.
189func 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
Dhananjay Nakrani662d2532016-11-06 19:56:14 -0800195// isFormatter reports whether t satisfies fmt.Formatter.
196// Unlike fmt.Stringer, it's impossible to satisfy fmt.Formatter without importing fmt.
197func (f *File) isFormatter(t types.Type) bool {
198 return formatterType != nil && types.Implements(t, formatterType)
199}
200
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700201// formatState holds the parsed representation of a printf directive such as "%3.*[4]d".
202// It is constructed by parsePrintfVerb.
203type 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
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700209 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.
Spencer Nelson867910e2016-03-02 15:29:30 -0500219func (f *File) checkPrintf(call *ast.CallExpr, name string) {
Aliaksandr Valialkinee1b90a2016-03-22 15:38:21 +0200220 format, idx := formatString(f, call)
Spencer Nelson867910e2016-03-02 15:29:30 -0500221 if idx < 0 {
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700222 if *verbose {
223 f.Warn(call.Pos(), "can't check non-constant format in call to", name)
224 }
225 return
226 }
Aliaksandr Valialkinee1b90a2016-03-22 15:38:21 +0200227
Spencer Nelson867910e2016-03-02 15:29:30 -0500228 firstArg := idx + 1 // Arguments are immediately after format string.
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700229 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
Aliaksandr Valialkinc9fbe0f2016-06-23 14:59:26 +0300237 maxArgNum := firstArg
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700238 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)
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700246 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 }
Aliaksandr Valialkinc9fbe0f2016-06-23 14:59:26 +0300253 for _, n := range state.argNums {
254 if n >= maxArgNum {
255 maxArgNum = n + 1
256 }
257 }
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700258 }
259 }
260 // Dotdotdot is hard.
Aliaksandr Valialkinc9fbe0f2016-06-23 14:59:26 +0300261 if call.Ellipsis.IsValid() && maxArgNum >= len(call.Args)-1 {
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700262 return
263 }
Aliaksandr Valialkinc9fbe0f2016-06-23 14:59:26 +0300264 // There should be no leftover arguments.
265 if maxArgNum != len(call.Args) {
266 expect := maxArgNum - firstArg
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700267 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.
273func (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.
286func (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.
296func (s *formatState) parseIndex() bool {
297 if s.nbytes == len(s.format) || s.format[s.nbytes] != '[' {
298 return true
299 }
300 // Argument index present.
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700301 s.nbytes++ // skip '['
302 start := s.nbytes
303 s.scanNum()
304 if s.nbytes == len(s.format) || s.nbytes == start || s.format[s.nbytes] != ']' {
Aliaksandr Valialkinc9fbe0f2016-06-23 14:59:26 +0300305 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])
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700310 return false
311 }
312 arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32)
313 if err != nil {
Aliaksandr Valialkinc9fbe0f2016-06-23 14:59:26 +0300314 s.file.Badf(s.call.Pos(), "bad syntax for printf argument index: %s", err)
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700315 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.
326func (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.
341func (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.
359func (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.
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700367 firstArg: firstArg,
368 file: f,
369 call: call,
370 }
371 // There may be flags.
372 state.parseFlags()
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700373 // 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).
Aliaksandr Valialkinc9fbe0f2016-06-23 14:59:26 +0300386 if !state.indexPending && !state.parseIndex() {
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700387 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.
404type printfArgType int
405
406const (
407 argBool printfArgType = 1 << iota
408 argInt
409 argRune
410 argString
411 argFloat
412 argComplex
413 argPointer
414 anyType printfArgType = ^0
415)
416
417type 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.
424const (
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.
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700432var 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.
463func (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 }
Dhananjay Nakrani662d2532016-11-06 19:56:14 -0800473
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 {
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700483 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 }
Dhananjay Nakrani662d2532016-11-06 19:56:14 -0800510 if state.verb == '%' || formatter {
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700511 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]
Russ Cox0f89efa2016-01-29 10:16:24 -0500518 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 }
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700522 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.
539func (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
Rob Pike43a7a9c2015-08-31 14:36:36 -0700567// 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.
569func (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
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700577// 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.
580func (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.
Aliaksandr Valialkind7ddee72016-04-05 18:42:07 +0300607func (f *File) checkPrint(call *ast.CallExpr, name string) {
608 firstArg := 0
609 typ := f.pkg.types[call.Fun].Type
Aliaksandr Valialkindcc42c72016-05-24 13:53:44 +0300610 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
Aliaksandr Valialkind7ddee72016-04-05 18:42:07 +0300621
Aliaksandr Valialkindcc42c72016-05-24 13:53:44 +0300622 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
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700628 }
629 }
Aliaksandr Valialkind7ddee72016-04-05 18:42:07 +0300630 args := call.Args
631 if len(args) <= firstArg {
632 // Skip calls without variadic args.
633 return
634 }
635 args = args[firstArg:]
636
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700637 // check for Println(os.Stderr, ...)
Aliaksandr Valialkind7ddee72016-04-05 18:42:07 +0300638 if firstArg == 0 {
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700639 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 }
Aliaksandr Valialkind7ddee72016-04-05 18:42:07 +0300647 arg := args[0]
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700648 if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
Josh Bleecher Snyder6ad76712016-07-07 17:40:37 -0700649 // 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, "%") {
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700653 f.Badf(call.Pos(), "possible formatting directive in %s call", name)
654 }
655 }
Aliaksandr Valialkind7ddee72016-04-05 18:42:07 +0300656 if strings.HasSuffix(name, "ln") {
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700657 // The last item, if a string, should not have a newline.
Aliaksandr Valialkind7ddee72016-04-05 18:42:07 +0300658 arg = args[len(args)-1]
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700659 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 {
Rob Pike43a7a9c2015-08-31 14:36:36 -0700666 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 }
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700669 if f.recursiveStringer(arg) {
Rob Pike43a7a9c2015-08-31 14:36:36 -0700670 f.Badf(call.Pos(), "arg %s in %s call causes recursive call to String method", f.gofmt(arg), name)
Robert Griesemer1b8b2c12015-06-04 12:54:58 -0700671 }
672 }
673}