Skip to content

Commit

Permalink
go/analysis/passes/printf: return Result with IsPrint helper
Browse files Browse the repository at this point in the history
printf.Result has IsPrint function telling the caller if a function is
a Print/Printf function or a wrapper of one.

This aids in developing Ananlyzer's applying checks on Print/Printf
functions.

Implements @alandonovan suggestion from
golang/go#29616 (comment)

Change-Id: I57833d5c65c417f2e4a761d9187fc133937f39fb
  • Loading branch information
aaronbee committed Jul 15, 2019
1 parent 9b2cb0e commit 5e5351b
Showing 1 changed file with 73 additions and 27 deletions.
100 changes: 73 additions & 27 deletions go/analysis/passes/printf/printf.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"go/constant"
"go/token"
"go/types"
"reflect"
"regexp"
"sort"
"strconv"
Expand All @@ -31,11 +32,12 @@ func init() {
}

var Analyzer = &analysis.Analyzer{
Name: "printf",
Doc: doc,
Requires: []*analysis.Analyzer{inspect.Analyzer},
Run: run,
FactTypes: []analysis.Fact{new(isWrapper)},
Name: "printf",
Doc: doc,
Requires: []*analysis.Analyzer{inspect.Analyzer},
Run: run,
ResultType: reflect.TypeOf((*Result)(nil)),
FactTypes: []analysis.Fact{new(isWrapper)},
}

const doc = `check consistency of Printf format strings and arguments
Expand Down Expand Up @@ -66,6 +68,51 @@ argument list. Otherwise it is assumed to be Print-like, taking a list
of arguments with no format string.
`

type PrintKind int

const (
// Not a print or printf func or wrapper
KindNo PrintKind = iota
// Print or Print wrapper
KindPrint
// Printf or Printf wrapper
KindPrintf
)

func (w PrintKind) String() string {
switch w {
case KindPrint:
return "printWrapper"
case KindPrintf:
return "printfWrapper"
}
return ""
}

// Result is the printf analyzer's result type. It allows checking if
// a function is a print or printf wrapper.
type Result struct {
set map[*types.Func]PrintKind
}

// IsPrint returns
func (r *Result) IsPrint(fn *types.Func) PrintKind {
_, ok := isPrint[fn.FullName()]
if !ok {
// Next look up just "printf", for use with -printf.funcs.
_, ok = isPrint[strings.ToLower(fn.Name())]
}
if ok {
if strings.HasSuffix(fn.Name(), "f") {
return KindPrintf
} else {
return KindPrint
}
}

return r.set[fn]
}

// isWrapper is a fact indicating that a function is a print or printf wrapper.
type isWrapper struct{ Printf bool }

Expand All @@ -80,9 +127,12 @@ func (f *isWrapper) String() string {
}

func run(pass *analysis.Pass) (interface{}, error) {
findPrintfLike(pass)
res := &Result{
set: make(map[*types.Func]PrintKind),
}
findPrintfLike(pass, res)
checkCall(pass)
return nil, nil
return res, nil
}

type printfWrapper struct {
Expand Down Expand Up @@ -145,7 +195,7 @@ func maybePrintfWrapper(info *types.Info, decl ast.Decl) *printfWrapper {
}

// findPrintfLike scans the entire package to find printf-like functions.
func findPrintfLike(pass *analysis.Pass) (interface{}, error) {
func findPrintfLike(pass *analysis.Pass, res *Result) (interface{}, error) {
// Gather potential wrappers and call graph between them.
byObj := make(map[*types.Func]*printfWrapper)
var wrappers []*printfWrapper
Expand Down Expand Up @@ -200,7 +250,7 @@ func findPrintfLike(pass *analysis.Pass) (interface{}, error) {

fn, kind := printfNameAndKind(pass, call)
if kind != 0 {
checkPrintfFwd(pass, w, call, kind)
checkPrintfFwd(pass, w, call, kind, res)
return true
}

Expand All @@ -223,16 +273,11 @@ func match(info *types.Info, arg ast.Expr, param *types.Var) bool {
return ok && info.ObjectOf(id) == param
}

const (
kindPrintf = 1
kindPrint = 2
)

// checkPrintfFwd checks that a printf-forwarding wrapper is forwarding correctly.
// It diagnoses writing fmt.Printf(format, args) instead of fmt.Printf(format, args...).
func checkPrintfFwd(pass *analysis.Pass, w *printfWrapper, call *ast.CallExpr, kind int) {
matched := kind == kindPrint ||
kind == kindPrintf && len(call.Args) >= 2 && match(pass.TypesInfo, call.Args[len(call.Args)-2], w.format)
func checkPrintfFwd(pass *analysis.Pass, w *printfWrapper, call *ast.CallExpr, kind PrintKind, res *Result) {
matched := kind == KindPrint ||
kind == KindPrintf && len(call.Args) >= 2 && match(pass.TypesInfo, call.Args[len(call.Args)-2], w.format)
if !matched {
return
}
Expand All @@ -253,7 +298,7 @@ func checkPrintfFwd(pass *analysis.Pass, w *printfWrapper, call *ast.CallExpr, k
return
}
desc := "printf"
if kind == kindPrint {
if kind == KindPrint {
desc = "print"
}
pass.Reportf(call.Pos(), "missing ... in args forwarded to %s-like function", desc)
Expand All @@ -262,10 +307,11 @@ func checkPrintfFwd(pass *analysis.Pass, w *printfWrapper, call *ast.CallExpr, k
fn := w.obj
var fact isWrapper
if !pass.ImportObjectFact(fn, &fact) {
fact.Printf = kind == kindPrintf
fact.Printf = kind == KindPrintf
pass.ExportObjectFact(fn, &fact)
res.set[fn] = kind
for _, caller := range w.callers {
checkPrintfFwd(pass, caller.w, caller.call, kind)
checkPrintfFwd(pass, caller.w, caller.call, kind, res)
}
}
}
Expand Down Expand Up @@ -414,15 +460,15 @@ func checkCall(pass *analysis.Pass) {
call := n.(*ast.CallExpr)
fn, kind := printfNameAndKind(pass, call)
switch kind {
case kindPrintf:
case KindPrintf:
checkPrintf(pass, call, fn)
case kindPrint:
case KindPrint:
checkPrint(pass, call, fn)
}
})
}

func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func, kind int) {
func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func, kind PrintKind) {
fn, _ = typeutil.Callee(pass.TypesInfo, call).(*types.Func)
if fn == nil {
return nil, 0
Expand All @@ -431,9 +477,9 @@ func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func,
var fact isWrapper
if pass.ImportObjectFact(fn, &fact) {
if fact.Printf {
return fn, kindPrintf
return fn, KindPrintf
} else {
return fn, kindPrint
return fn, KindPrint
}
}

Expand All @@ -444,9 +490,9 @@ func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func,
}
if ok {
if strings.HasSuffix(fn.Name(), "f") {
kind = kindPrintf
kind = KindPrintf
} else {
kind = kindPrint
kind = KindPrint
}
}
return fn, kind
Expand Down

0 comments on commit 5e5351b

Please sign in to comment.