Expand suggestions to cope with sub-line replacements.
This changes the Errorf check to provide a suggestion.
diff --git a/lint.go b/lint.go
index eff0bbd..58b01d7 100644
--- a/lint.go
+++ b/lint.go
@@ -1042,12 +1042,7 @@
newRS := *rs // shallow copy
newRS.Value = nil
- line := f.render(&newRS)
- if i := strings.Index(line, "\n"); i >= 0 {
- line = line[:i]
- }
- line = f.indentOf(rs) + line
- p.ReplacementLine = line
+ p.ReplacementLine = f.firstLineOf(&newRS, rs)
return true
})
@@ -1080,7 +1075,13 @@
if isTestingError {
errorfPrefix = f.render(se.X)
}
- f.errorf(node, 1, category("errors"), "should replace %s(fmt.Sprintf(...)) with %s.Errorf(...)", f.render(se), errorfPrefix)
+ 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
})
}
@@ -1488,6 +1489,16 @@
return "", false
}
+// firstLineOf renders the given node and returns its first line.
+// It will also match the indentation of another node.
+func (f *file) firstLineOf(node, match ast.Node) string {
+ line := f.render(node)
+ if i := strings.Index(line, "\n"); i >= 0 {
+ line = line[:i]
+ }
+ return f.indentOf(match) + line
+}
+
func (f *file) indentOf(node ast.Node) string {
line := srcLine(f.src, f.fset.Position(node.Pos()))
for i, r := range line {
@@ -1500,6 +1511,13 @@
return line // unusual or empty line
}
+func (f *file) srcLineWithMatch(node ast.Node, pattern string) (m []string) {
+ line := srcLine(f.src, f.fset.Position(node.Pos()))
+ line = strings.TrimSuffix(line, "\n")
+ rx := regexp.MustCompile(pattern)
+ return rx.FindStringSubmatch(line)
+}
+
// srcLine returns the complete line at p, including the terminating newline.
func srcLine(src []byte, p token.Position) string {
// Run to end of line in both directions if not at line start/end.
diff --git a/lint_test.go b/lint_test.go
index e6b25de..d4539b2 100644
--- a/lint_test.go
+++ b/lint_test.go
@@ -71,8 +71,15 @@
}
if in.Match.MatchString(p.Text) {
// check replacement if we are expecting one
- if in.Replacement != "" && p.ReplacementLine != in.Replacement {
- t.Errorf("Lint failed at %s:%d; got replacement %q, want %q", fi.Name(), in.Line, p.ReplacementLine, in.Replacement)
+ if in.Replacement != "" {
+ // ignore any inline comments, since that would be recursive
+ r := p.ReplacementLine
+ if i := strings.Index(r, " //"); i >= 0 {
+ r = r[:i]
+ }
+ if r != in.Replacement {
+ t.Errorf("Lint failed at %s:%d; got replacement %q, want %q", fi.Name(), in.Line, r, in.Replacement)
+ }
}
// remove this problem from ps
diff --git a/testdata/errorf.go b/testdata/errorf.go
index 5a7efeb..72161f2 100644
--- a/testdata/errorf.go
+++ b/testdata/errorf.go
@@ -11,7 +11,7 @@
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\(\.\.\.\)/
+ return errors.New(fmt.Sprintf("something %d", x)) // MATCH /should replace.*errors\.New\(fmt\.Sprintf\(\.\.\.\)\).*fmt\.Errorf\(\.\.\.\)/ -> ` return fmt.Errorf("something %d", x)`
}
if x > 5 {
return errors.New(g("blah")) // ok