code: func (f *file) lintErrorf() {
  	f.walk(func(node ast.Node) bool {
  		ce, ok := node.(*ast.CallExpr)
  		if !ok || len(ce.Args) != 1 {
  			return true
  		}
  		isErrorsNew := isPkgDot(ce.Fun, "errors", "New")
  		var isTestingError bool
  		se, ok := ce.Fun.(*ast.SelectorExpr)
  		if ok && se.Sel.Name == "Error" {
  			if typ := f.pkg.typeOf(se.X); typ != nil {
  				isTestingError = typ.String() == "*testing.T"
  			}
  		}
  		if !isErrorsNew && !isTestingError {
  			return true
  		}
  		if !f.imports("errors") {
  			return true
  		}
  		arg := ce.Args[0]
  		ce, ok = arg.(*ast.CallExpr)
  		if !ok || !isPkgDot(ce.Fun, "fmt", "Sprintf") {
  			return true
  		}
  		errorfPrefix := "fmt"
  		if isTestingError {
  			errorfPrefix = f.render(se.X)
  		}
  		p := f.errorf(node, 1, category("errors"), "should replace %s(fmt.Sprintf(...)) with %s.Errorf(...)", f.render(se), errorfPrefix)
  
  		m := f.srcLineWithMatch(ce, `^(.*)`+f.render(se)+`\(fmt\.Sprintf\((.*)\)\)(.*)$`)
  		if m != nil {
  			p.ReplacementLine = m[1] + errorfPrefix + ".Errorf(" + m[2] + ")" + m[3]
  		}
  
  		return true
  	})
  }
docs: // lintErrorf examines errors.New and testing.Error calls. It complains if its only argument is an fmt.Sprintf invocation.