github.com/Johnny2210/revive@v1.0.8-0.20210625134200-febf37ccd0f5/rule/error-strings.go (about) 1 package rule 2 3 import ( 4 "go/ast" 5 "go/token" 6 "strconv" 7 "unicode" 8 "unicode/utf8" 9 10 "github.com/mgechev/revive/lint" 11 ) 12 13 // ErrorStringsRule lints given else constructs. 14 type ErrorStringsRule struct{} 15 16 // Apply applies the rule to given file. 17 func (r *ErrorStringsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { 18 var failures []lint.Failure 19 20 fileAst := file.AST 21 walker := lintErrorStrings{ 22 file: file, 23 fileAst: fileAst, 24 onFailure: func(failure lint.Failure) { 25 failures = append(failures, failure) 26 }, 27 } 28 29 ast.Walk(walker, fileAst) 30 31 return failures 32 } 33 34 // Name returns the rule name. 35 func (r *ErrorStringsRule) Name() string { 36 return "error-strings" 37 } 38 39 type lintErrorStrings struct { 40 file *lint.File 41 fileAst *ast.File 42 onFailure func(lint.Failure) 43 } 44 45 func (w lintErrorStrings) Visit(n ast.Node) ast.Visitor { 46 ce, ok := n.(*ast.CallExpr) 47 if !ok { 48 return w 49 } 50 if !isPkgDot(ce.Fun, "errors", "New") && !isPkgDot(ce.Fun, "fmt", "Errorf") { 51 return w 52 } 53 if len(ce.Args) < 1 { 54 return w 55 } 56 str, ok := ce.Args[0].(*ast.BasicLit) 57 if !ok || str.Kind != token.STRING { 58 return w 59 } 60 s, _ := strconv.Unquote(str.Value) // can assume well-formed Go 61 if s == "" { 62 return w 63 } 64 clean, conf := lintErrorString(s) 65 if clean { 66 return w 67 } 68 69 w.onFailure(lint.Failure{ 70 Node: str, 71 Confidence: conf, 72 Category: "errors", 73 Failure: "error strings should not be capitalized or end with punctuation or a newline", 74 }) 75 return w 76 } 77 78 func lintErrorString(s string) (isClean bool, conf float64) { 79 const basicConfidence = 0.8 80 const capConfidence = basicConfidence - 0.2 81 first, firstN := utf8.DecodeRuneInString(s) 82 last, _ := utf8.DecodeLastRuneInString(s) 83 if last == '.' || last == ':' || last == '!' || last == '\n' { 84 return false, basicConfidence 85 } 86 if unicode.IsUpper(first) { 87 // People use proper nouns and exported Go identifiers in error strings, 88 // so decrease the confidence of warnings for capitalization. 89 if len(s) <= firstN { 90 return false, capConfidence 91 } 92 // Flag strings starting with something that doesn't look like an initialism. 93 if second, _ := utf8.DecodeRuneInString(s[firstN:]); !unicode.IsUpper(second) { 94 return false, capConfidence 95 } 96 } 97 return true, 0 98 }