1
0
mirror of https://github.com/mgechev/revive.git synced 2025-01-22 03:38:47 +02:00

Update error-strings rule (#608) (#609)

Co-authored-by: SalvadorC <salvadorcavadini+github@gmail.com>
This commit is contained in:
doniacld 2021-10-28 21:26:02 +02:00 committed by GitHub
parent 2c895fb33f
commit 0fada9d76c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 96 additions and 11 deletions

View File

@ -17,10 +17,25 @@ type ErrorStringsRule struct{}
func (r *ErrorStringsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
var failures []lint.Failure
var errorFunctions = map[string]map[string]struct{}{
"fmt": {
"Errorf": {},
},
"errors": {
"Errorf": {},
"WithMessage": {},
"Wrap": {},
"New": {},
"WithMessagef": {},
"Wrapf": {},
},
}
fileAst := file.AST
walker := lintErrorStrings{
file: file,
fileAst: fileAst,
file: file,
fileAst: fileAst,
errorFunctions: errorFunctions,
onFailure: func(failure lint.Failure) {
failures = append(failures, failure)
},
@ -37,24 +52,31 @@ func (r *ErrorStringsRule) Name() string {
}
type lintErrorStrings struct {
file *lint.File
fileAst *ast.File
onFailure func(lint.Failure)
file *lint.File
fileAst *ast.File
errorFunctions map[string]map[string]struct{}
onFailure func(lint.Failure)
}
// Visit browses the AST
func (w lintErrorStrings) Visit(n ast.Node) ast.Visitor {
ce, ok := n.(*ast.CallExpr)
if !ok {
return w
}
if !isPkgDot(ce.Fun, "errors", "New") && !isPkgDot(ce.Fun, "fmt", "Errorf") {
return w
}
if len(ce.Args) < 1 {
return w
}
str, ok := ce.Args[0].(*ast.BasicLit)
if !ok || str.Kind != token.STRING {
// expression matches the known pkg.function
ok = w.match(ce)
if !ok {
return w
}
str, ok := w.getMessage(ce)
if !ok {
return w
}
s, _ := strconv.Unquote(str.Value) // can assume well-formed Go
@ -65,7 +87,6 @@ func (w lintErrorStrings) Visit(n ast.Node) ast.Visitor {
if clean {
return w
}
w.onFailure(lint.Failure{
Node: str,
Confidence: conf,
@ -75,6 +96,52 @@ func (w lintErrorStrings) Visit(n ast.Node) ast.Visitor {
return w
}
// match returns true if the expression corresponds to the known pkg.function
// i.e.: errors.Wrap
func (w lintErrorStrings) match(expr *ast.CallExpr) bool {
sel, ok := expr.Fun.(*ast.SelectorExpr)
if !ok {
return false
}
// retrieve the package
id, ok := sel.X.(*ast.Ident)
functions, ok := w.errorFunctions[id.Name]
if !ok {
return false
}
// retrieve the function
_, ok = functions[sel.Sel.Name]
return ok
}
// getMessage returns the message depending on its position
// returns false if the cast is unsuccessful
func (w lintErrorStrings) getMessage(expr *ast.CallExpr) (s *ast.BasicLit, success bool) {
str, ok := w.checkArg(expr, 0)
if ok {
return str, true
}
if len(expr.Args) < 2 {
return s, false
}
str, ok = w.checkArg(expr, 1)
if !ok {
return s, false
}
return str, true
}
func (lintErrorStrings) checkArg(expr *ast.CallExpr, arg int) (s *ast.BasicLit, success bool) {
str, ok := expr.Args[arg].(*ast.BasicLit)
if !ok {
return s, false
}
if str.Kind != token.STRING {
return s, false
}
return str, true
}
func lintErrorString(s string) (isClean bool, conf float64) {
const basicConfidence = 0.8
const capConfidence = basicConfidence - 0.2

View File

@ -0,0 +1,18 @@
// Package foo ...
package foo
import (
"github.com/pkg/errors"
)
// Check for the error strings themselves.
func errorsStrings(x int) error {
var err error
err = errors.Wrap(err, "This %d is too low") // MATCH /error strings should not be capitalized or end with punctuation or a newline/
err = errors.New("This %d is too low") // MATCH /error strings should not be capitalized or end with punctuation or a newline/
err = errors.Wrapf(err, "This %d is too low", x) // MATCH /error strings should not be capitalized or end with punctuation or a newline/
err = errors.WithMessage(err, "This %d is too low") // MATCH /error strings should not be capitalized or end with punctuation or a newline/
err = errors.WithMessagef(err, "This %d is too low", x) // MATCH /error strings should not be capitalized or end with punctuation or a newline/
return err
}