mirror of
https://github.com/mgechev/revive.git
synced 2024-11-24 08:32:22 +02:00
Var declarations
This commit is contained in:
parent
e20e8e338a
commit
ce81530c03
@ -3,6 +3,7 @@ package defaultrule
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
@ -101,3 +102,29 @@ var commonInitialisms = map[string]bool{
|
||||
}
|
||||
|
||||
var allCapsRE = regexp.MustCompile(`^[A-Z0-9_]+$`)
|
||||
|
||||
func isIdent(expr ast.Expr, ident string) bool {
|
||||
id, ok := expr.(*ast.Ident)
|
||||
return ok && id.Name == ident
|
||||
}
|
||||
|
||||
var zeroLiteral = map[string]bool{
|
||||
"false": true, // bool
|
||||
// runes
|
||||
`'\x00'`: true,
|
||||
`'\000'`: true,
|
||||
// strings
|
||||
`""`: true,
|
||||
"``": true,
|
||||
// numerics
|
||||
"0": true,
|
||||
"0.": true,
|
||||
"0.0": true,
|
||||
"0i": true,
|
||||
}
|
||||
|
||||
func validType(T types.Type) bool {
|
||||
return T != nil &&
|
||||
T != types.Typ[types.Invalid] &&
|
||||
!strings.Contains(T.String(), "invalid type") // good but not foolproof
|
||||
}
|
||||
|
93
defaultrule/var-declarations.go
Normal file
93
defaultrule/var-declarations.go
Normal file
@ -0,0 +1,93 @@
|
||||
package defaultrule
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
|
||||
"github.com/mgechev/revive/file"
|
||||
"github.com/mgechev/revive/rule"
|
||||
)
|
||||
|
||||
// VarDeclarationsRule lints given else constructs.
|
||||
type VarDeclarationsRule struct{}
|
||||
|
||||
// Apply applies the rule to given file.
|
||||
func (r *VarDeclarationsRule) Apply(file *file.File, arguments rule.Arguments) []rule.Failure {
|
||||
var failures []rule.Failure
|
||||
|
||||
fileAst := file.GetAST()
|
||||
walker := &lintVarDeclarations{
|
||||
file: file,
|
||||
fileAst: fileAst,
|
||||
onFailure: func(failure rule.Failure) {
|
||||
failures = append(failures, failure)
|
||||
},
|
||||
}
|
||||
|
||||
ast.Walk(walker, fileAst)
|
||||
|
||||
return failures
|
||||
}
|
||||
|
||||
// Name returns the rule name.
|
||||
func (r *VarDeclarationsRule) Name() string {
|
||||
return "blank-imports"
|
||||
}
|
||||
|
||||
type lintVarDeclarations struct {
|
||||
fileAst *ast.File
|
||||
file *file.File
|
||||
lastGen *ast.GenDecl
|
||||
onFailure func(rule.Failure)
|
||||
}
|
||||
|
||||
func (w *lintVarDeclarations) Visit(node ast.Node) ast.Visitor {
|
||||
switch v := node.(type) {
|
||||
case *ast.GenDecl:
|
||||
if v.Tok != token.CONST && v.Tok != token.VAR {
|
||||
return nil
|
||||
}
|
||||
w.lastGen = v
|
||||
return w
|
||||
case *ast.ValueSpec:
|
||||
if w.lastGen.Tok == token.CONST {
|
||||
return nil
|
||||
}
|
||||
if len(v.Names) > 1 || v.Type == nil || len(v.Values) == 0 {
|
||||
return nil
|
||||
}
|
||||
rhs := v.Values[0]
|
||||
// An underscore var appears in a common idiom for compile-time interface satisfaction,
|
||||
// as in "var _ Interface = (*Concrete)(nil)".
|
||||
if isIdent(v.Names[0], "_") {
|
||||
return nil
|
||||
}
|
||||
// If the RHS is a zero value, suggest dropping it.
|
||||
zero := false
|
||||
if lit, ok := rhs.(*ast.BasicLit); ok {
|
||||
zero = zeroLiteral[lit.Value]
|
||||
} else if isIdent(rhs, "nil") {
|
||||
zero = true
|
||||
}
|
||||
if zero {
|
||||
w.onFailure(rule.Failure{
|
||||
Confidence: 0.9,
|
||||
Node: rhs,
|
||||
Failure: fmt.Sprintf("should drop = %s from declaration of var %s; it is the zero value", render(rhs), v.Names[0]),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
func render(x interface{}) string {
|
||||
var buf bytes.Buffer
|
||||
if err := printer.Fprint(&buf, token.NewFileSet(), x); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
Loading…
Reference in New Issue
Block a user