Suggest replacing errors.New(fmt.Sprintf(...)) with fmt.Errorf(...).
diff --git a/lint.go b/lint.go
index 64e8049..d679f2f 100644
--- a/lint.go
+++ b/lint.go
@@ -70,6 +70,7 @@
 	f.lintVarDecls()
 	f.lintElses()
 	f.lintRanges()
+	f.lintErrorf()
 
 	return f.problems
 }
@@ -595,7 +596,26 @@
 		}
 
 		f.errorf(rs.Value, 1, "should omit 2nd value from range; this loop is equivalent to `for %s %s range ...`", f.render(rs.Key), rs.Tok)
+		return true
+	})
+}
 
+// lintErrorf examines errors.New calls. It complains if its only argument is an fmt.Sprintf invocation.
+func (f *file) lintErrorf() {
+	f.walk(func(node ast.Node) bool {
+		ce, ok := node.(*ast.CallExpr)
+		if !ok {
+			return true
+		}
+		if !isPkgDot(ce.Fun, "errors", "New") || len(ce.Args) != 1 {
+			return true
+		}
+		arg := ce.Args[0]
+		ce, ok = arg.(*ast.CallExpr)
+		if !ok || !isPkgDot(ce.Fun, "fmt", "Sprintf") {
+			return true
+		}
+		f.errorf(node, 1, "should replace errors.New(fmt.Sprintf(...)) with fmt.Errorf(...)")
 		return true
 	})
 }
@@ -646,6 +666,11 @@
 	return ok && id.Name == ident
 }
 
+func isPkgDot(expr ast.Expr, pkg, name string) bool {
+	sel, ok := expr.(*ast.SelectorExpr)
+	return ok && isIdent(sel.X, pkg) && isIdent(sel.Sel, name)
+}
+
 func isIntLiteral(expr ast.Expr) bool {
 	// Either a BasicLit with Kind token.INT,
 	// or some combination of a UnaryExpr with Op token.SUB (for "-<lit>")
diff --git a/testdata/errorf.go b/testdata/errorf.go
new file mode 100644
index 0000000..768fb8c
--- /dev/null
+++ b/testdata/errorf.go
@@ -0,0 +1,22 @@
+// Test for not using fmt.Errorf.
+
+// Package foo ...
+package foo
+
+import (
+	"errors"
+	"fmt"
+)
+
+func f(x int) error {
+	if x > 10 {
+		return errors.New(fmt.Sprintf("something %d", x)) // MATCH /should replace.*errors\.New\(fmt\.Sprintf\(\.\.\.\)\).*fmt\.Errorf\(\.\.\.\)/
+	}
+	if x > 5 {
+		return errors.New(g("blah")) // ok
+	}
+	if x > 4 {
+		return errors.New("something else") // ok
+	}
+	return nil
+}