mirror of
https://github.com/mgechev/revive.git
synced 2025-03-21 21:17:06 +02:00
String of int (#342)
* string-of-int: working version * adds doc of string-to-int
This commit is contained in:
parent
9c5528e4f7
commit
a24e8e7b81
@ -347,6 +347,7 @@ List of all available rules. The rules ported from `golint` are left unchanged a
|
||||
| [`unused-receiver`](./RULES_DESCRIPTIONS.md#unused-receiver) | n/a | Suggests to rename or remove unused method receivers | no | no |
|
||||
| [`unhandled-error`](./RULES_DESCRIPTIONS.md#unhandled-error) | []string | Warns on unhandled errors returned by funcion calls | no | yes |
|
||||
| [`cognitive-complexity`](./RULES_DESCRIPTIONS.md#cognitive-complexity) | int | Sets restriction for maximum Cognitive complexity. | no | no |
|
||||
| [`string-of-int`](./RULES_DESCRIPTIONS.md#string-of-int) | n/a | Warns on suspicious casts from int to string | no | yes |
|
||||
|
||||
## Configurable rules
|
||||
|
||||
|
@ -46,6 +46,7 @@ List of all available rules.
|
||||
- [range-val-in-closure](#range-val-in-closure)
|
||||
- [receiver-naming](#receiver-naming)
|
||||
- [redefines-builtin-id](#redefines-builtin-id)
|
||||
- [string-of-int](#string-of-int)
|
||||
- [struct-tag](#struct-tag)
|
||||
- [superfluous-else](#superfluous-else)
|
||||
- [time-naming](#time-naming)
|
||||
@ -406,6 +407,11 @@ Even if possible, redefining these built in names can lead to bugs very difficul
|
||||
|
||||
_Configuration_: N/A
|
||||
|
||||
## string-of-int
|
||||
_Description_: explicit type conversion `string(i)` where `i` has an integer type other than `rune` might behave not as expected by the developer (e.g. `string(42)` is not `"42"`). This rule spot that kind of suspicious conversions.
|
||||
|
||||
_Configuration_: N/A
|
||||
|
||||
## struct-tag
|
||||
|
||||
_Description_: Struct tags are not checked at compile time.
|
||||
|
@ -81,6 +81,7 @@ var allRules = append([]lint.Rule{
|
||||
&rule.UnusedReceiverRule{},
|
||||
&rule.UnhandledErrorRule{},
|
||||
&rule.CognitiveComplexityRule{},
|
||||
&rule.StringOfIntRule{},
|
||||
}, defaultRules...)
|
||||
|
||||
var allFormatters = []lint.Formatter{
|
||||
|
30
fixtures/string-of-int.go
Normal file
30
fixtures/string-of-int.go
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
package fixtures
|
||||
|
||||
type A string
|
||||
type B = string
|
||||
type C int
|
||||
type D = uintptr
|
||||
|
||||
func StringTest() {
|
||||
var (
|
||||
i int
|
||||
j rune
|
||||
k byte
|
||||
l C
|
||||
m D
|
||||
n = []int{0, 1, 2}
|
||||
o struct{ x int }
|
||||
)
|
||||
const p = 0
|
||||
_ = string(i) // MATCH /dubious convertion of an integer into a string, use strconv.Itoa/
|
||||
_ = string(j)
|
||||
_ = string(k)
|
||||
_ = string(p) // MATCH /dubious convertion of an integer into a string, use strconv.Itoa/
|
||||
_ = A(l) // MATCH /dubious convertion of an integer into a string, use strconv.Itoa/
|
||||
_ = B(m) // MATCH /dubious convertion of an integer into a string, use strconv.Itoa/
|
||||
_ = string(n[1]) // MATCH /dubious convertion of an integer into a string, use strconv.Itoa/
|
||||
_ = string(o.x) // MATCH /dubious convertion of an integer into a string, use strconv.Itoa/
|
||||
}
|
95
rule/string-of-int.go
Normal file
95
rule/string-of-int.go
Normal file
@ -0,0 +1,95 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"github.com/mgechev/revive/lint"
|
||||
)
|
||||
|
||||
// StringOfIntRule warns when logic expressions contains Boolean literals.
|
||||
type StringOfIntRule struct{}
|
||||
|
||||
// Apply applies the rule to given file.
|
||||
func (r *StringOfIntRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
||||
var failures []lint.Failure
|
||||
|
||||
onFailure := func(failure lint.Failure) {
|
||||
failures = append(failures, failure)
|
||||
}
|
||||
|
||||
astFile := file.AST
|
||||
file.Pkg.TypeCheck()
|
||||
|
||||
w := &lintStringInt{file, onFailure}
|
||||
ast.Walk(w, astFile)
|
||||
|
||||
return failures
|
||||
}
|
||||
|
||||
// Name returns the rule name.
|
||||
func (r *StringOfIntRule) Name() string {
|
||||
return "string-of-int"
|
||||
}
|
||||
|
||||
type lintStringInt struct {
|
||||
file *lint.File
|
||||
onFailure func(lint.Failure)
|
||||
}
|
||||
|
||||
func (w *lintStringInt) Visit(node ast.Node) ast.Visitor {
|
||||
ce, ok := node.(*ast.CallExpr)
|
||||
if !ok {
|
||||
return w
|
||||
}
|
||||
|
||||
if !w.isCallStringCast(ce.Fun) {
|
||||
return w
|
||||
}
|
||||
|
||||
if !w.isIntExpression(ce.Args) {
|
||||
return w
|
||||
}
|
||||
|
||||
w.onFailure(lint.Failure{
|
||||
Confidence: 1,
|
||||
Node: ce,
|
||||
Failure: "dubious convertion of an integer into a string, use strconv.Itoa",
|
||||
})
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *lintStringInt) isCallStringCast(e ast.Expr) bool {
|
||||
t := w.file.Pkg.TypeOf(e)
|
||||
if t == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
tb, _ := t.Underlying().(*types.Basic)
|
||||
|
||||
return tb != nil && tb.Kind() == types.String
|
||||
}
|
||||
|
||||
func (w *lintStringInt) isIntExpression(es []ast.Expr) bool {
|
||||
if len(es) != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
t := w.file.Pkg.TypeOf(es[0])
|
||||
if t == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
ut, _ := t.Underlying().(*types.Basic)
|
||||
if ut == nil || ut.Info()&types.IsInteger == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
switch ut.Kind() {
|
||||
case types.Byte, types.Rune, types.UntypedRune:
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
12
test/string-of-int.go
Normal file
12
test/string-of-int.go
Normal file
@ -0,0 +1,12 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mgechev/revive/rule"
|
||||
)
|
||||
|
||||
// String-of-int rule.
|
||||
func TestStringOfInt(t *testing.T) {
|
||||
testRule(t, "string-of-int", &rule.StringOfIntRule{})
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user